From 11295055526308ee71d82dc97f0a9ca2dd61c3b9 Mon Sep 17 00:00:00 2001 From: Laurence Oberman Date: Thu, 1 Nov 2018 09:30:18 -0400 Subject: [PATCH 0001/4277] watchdog/core: Add watchdog_thresh command line parameter The hard and soft lockup detector threshold has a default value of 10 seconds which can only be changed via sysctl. During early boot lockup detection can trigger when noisy debugging emits a large amount of messages to the console, but there is no way to set a larger threshold on the kernel command line. The detector can only be completely disabled. Add a new watchdog_thresh= command line parameter to allow boot time control over the threshold. It works in the same way as the sysctl and affects both the soft and the hard lockup detectors. Signed-off-by: Laurence Oberman Signed-off-by: Thomas Gleixner Cc: rdunlap@infradead.org Cc: prarit@redhat.com Link: https://lkml.kernel.org/r/1541079018-13953-1-git-send-email-loberman@redhat.com --- Documentation/admin-guide/kernel-parameters.txt | 8 ++++++++ kernel/watchdog.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b90fe3b6bc6c..79b5b473001b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4957,6 +4957,14 @@ or other driver-specific files in the Documentation/watchdog/ directory. + watchdog_thresh= + [KNL] + Set the hard lockup detector stall duration + threshold in seconds. The soft lockup detector + threshold is set to twice the value. A value of 0 + disables both lockup detectors. Default is 10 + seconds. + workqueue.watchdog_thresh= If CONFIG_WQ_WATCHDOG is configured, workqueue can warn stall conditions and dump internal state to diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 977918d5d350..8fbfda94a67b 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -199,6 +199,13 @@ static int __init nosoftlockup_setup(char *str) } __setup("nosoftlockup", nosoftlockup_setup); +static int __init watchdog_thresh_setup(char *str) +{ + get_option(&str, &watchdog_thresh); + return 1; +} +__setup("watchdog_thresh=", watchdog_thresh_setup); + #ifdef CONFIG_SMP int __read_mostly sysctl_softlockup_all_cpu_backtrace; From 43aa09fee2f08c8d90a4f35d4c8c711362afcaee Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Mon, 12 Nov 2018 11:59:12 +0100 Subject: [PATCH 0002/4277] apparmor: Fix warning about unused function apparmor_ipv6_postroute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when compiled without CONFIG_IPV6: security/apparmor/lsm.c:1601:21: warning: ‘apparmor_ipv6_postroute’ defined but not used [-Wunused-function] static unsigned int apparmor_ipv6_postroute(void *priv, ^~~~~~~~~~~~~~~~~~~~~~~ Reported-by: Jordan Glover Tested-by: Jordan Glover Signed-off-by: Petr Vorel Signed-off-by: John Johansen --- security/apparmor/lsm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 42446a216f3b..7d1eeb084968 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1598,12 +1598,14 @@ static unsigned int apparmor_ipv4_postroute(void *priv, return apparmor_ip_postroute(priv, skb, state); } +#if IS_ENABLED(CONFIG_IPV6) static unsigned int apparmor_ipv6_postroute(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { return apparmor_ip_postroute(priv, skb, state); } +#endif static const struct nf_hook_ops apparmor_nf_ops[] = { { From adf040ddd00152bda99aa9bc2afef9075ac88b34 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:25 -0600 Subject: [PATCH 0003/4277] ide: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. Cc: "David S. Miller" Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: linux-ide@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Rob Herring Signed-off-by: David S. Miller --- drivers/ide/pmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c index 203ed4adc04a..92f840365718 100644 --- a/drivers/ide/pmac.c +++ b/drivers/ide/pmac.c @@ -1046,7 +1046,7 @@ static int pmac_ide_setup_device(pmac_ide_hwif_t *pmif, struct ide_hw *hw) d.port_ops = &pmac_ide_ata4_port_ops; d.udma_mask = ATA_UDMA5; } else if (of_device_is_compatible(np, "keylargo-ata")) { - if (strcmp(np->name, "ata-4") == 0) { + if (of_node_name_eq(np, "ata-4")) { pmif->kind = controller_kl_ata4; d.port_ops = &pmac_ide_ata4_port_ops; d.udma_mask = ATA_UDMA4; From 15ff2069cb7f967dae6a8f8c176ba51447c75f00 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 18 Dec 2018 06:05:04 +0900 Subject: [PATCH 0004/4277] printk: Add caller information to printk() output. Sometimes we want to print a series of printk() messages to consoles without being disturbed by concurrent printk() from interrupts and/or other threads. But we can't enforce printk() callers to use their local buffers because we need to ask them to make too much changes. Also, even buffering up to one line inside printk() might cause failing to emit an important clue under critical situation. Therefore, instead of trying to help buffering, let's try to help reconstructing messages by saving caller information as of calling log_store() and adding it as "[T$thread_id]" or "[C$processor_id]" upon printing to consoles. Some examples for console output: [ 1.222773][ T1] x86: Booting SMP configuration: [ 2.779635][ T1] pci 0000:00:01.0: PCI bridge to [bus 01] [ 5.069193][ T268] Fusion MPT base driver 3.04.20 [ 9.316504][ C2] random: fast init done [ 13.413336][ T3355] Initialized host personality Some examples for /dev/kmsg output: 6,496,1222773,-,caller=T1;x86: Booting SMP configuration: 6,968,2779635,-,caller=T1;pci 0000:00:01.0: PCI bridge to [bus 01] SUBSYSTEM=pci DEVICE=+pci:0000:00:01.0 6,1353,5069193,-,caller=T268;Fusion MPT base driver 3.04.20 5,1526,9316504,-,caller=C2;random: fast init done 6,1575,13413336,-,caller=T3355;Initialized host personality Note that this patch changes max length of messages which can be printed by printk() or written to /dev/kmsg interface from 992 bytes to 976 bytes, based on an assumption that userspace won't try to write messages hitting that border line to /dev/kmsg interface. Link: http://lkml.kernel.org/r/93f19e57-5051-c67d-9af4-b17624062d44@i-love.sakura.ne.jp Cc: Dmitry Vyukov Cc: Sergey Senozhatsky Cc: Steven Rostedt Cc: Linus Torvalds Cc: Andrew Morton Cc: LKML Cc: syzkaller Signed-off-by: Tetsuo Handa Acked-by: Sergey Senozhatsky Signed-off-by: Petr Mladek --- kernel/printk/printk.c | 55 +++++++++++++++++++++++++++++++++++++++--- lib/Kconfig.debug | 17 +++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 91db332ccf4d..e3d977bbc7ab 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -357,6 +357,9 @@ struct printk_log { u8 facility; /* syslog facility */ u8 flags:5; /* internal record flags */ u8 level:3; /* syslog level */ +#ifdef CONFIG_PRINTK_CALLER + u32 caller_id; /* thread id or processor id */ +#endif } #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS __packed __aligned(4) @@ -423,7 +426,11 @@ static u64 exclusive_console_stop_seq; static u64 clear_seq; static u32 clear_idx; +#ifdef CONFIG_PRINTK_CALLER +#define PREFIX_MAX 48 +#else #define PREFIX_MAX 32 +#endif #define LOG_LINE_MAX (1024 - PREFIX_MAX) #define LOG_LEVEL(v) ((v) & 0x07) @@ -626,6 +633,12 @@ static int log_store(int facility, int level, msg->ts_nsec = ts_nsec; else msg->ts_nsec = local_clock(); +#ifdef CONFIG_PRINTK_CALLER + if (in_task()) + msg->caller_id = task_pid_nr(current); + else + msg->caller_id = 0x80000000 + raw_smp_processor_id(); +#endif memset(log_dict(msg) + dict_len, 0, pad_len); msg->len = size; @@ -689,12 +702,21 @@ static ssize_t msg_print_ext_header(char *buf, size_t size, struct printk_log *msg, u64 seq) { u64 ts_usec = msg->ts_nsec; + char caller[20]; +#ifdef CONFIG_PRINTK_CALLER + u32 id = msg->caller_id; + + snprintf(caller, sizeof(caller), ",caller=%c%u", + id & 0x80000000 ? 'C' : 'T', id & ~0x80000000); +#else + caller[0] = '\0'; +#endif do_div(ts_usec, 1000); - return scnprintf(buf, size, "%u,%llu,%llu,%c;", - (msg->facility << 3) | msg->level, seq, ts_usec, - msg->flags & LOG_CONT ? 'c' : '-'); + return scnprintf(buf, size, "%u,%llu,%llu,%c%s;", + (msg->facility << 3) | msg->level, seq, ts_usec, + msg->flags & LOG_CONT ? 'c' : '-', caller); } static ssize_t msg_print_ext_body(char *buf, size_t size, @@ -1039,6 +1061,9 @@ void log_buf_vmcoreinfo_setup(void) VMCOREINFO_OFFSET(printk_log, len); VMCOREINFO_OFFSET(printk_log, text_len); VMCOREINFO_OFFSET(printk_log, dict_len); +#ifdef CONFIG_PRINTK_CALLER + VMCOREINFO_OFFSET(printk_log, caller_id); +#endif } #endif @@ -1237,10 +1262,23 @@ static size_t print_time(u64 ts, char *buf) { unsigned long rem_nsec = do_div(ts, 1000000000); - return sprintf(buf, "[%5lu.%06lu] ", + return sprintf(buf, "[%5lu.%06lu]", (unsigned long)ts, rem_nsec / 1000); } +#ifdef CONFIG_PRINTK_CALLER +static size_t print_caller(u32 id, char *buf) +{ + char caller[12]; + + snprintf(caller, sizeof(caller), "%c%u", + id & 0x80000000 ? 'C' : 'T', id & ~0x80000000); + return sprintf(buf, "[%6s]", caller); +} +#else +#define print_caller(id, buf) 0 +#endif + static size_t print_prefix(const struct printk_log *msg, bool syslog, bool time, char *buf) { @@ -1248,8 +1286,17 @@ static size_t print_prefix(const struct printk_log *msg, bool syslog, if (syslog) len = print_syslog((msg->facility << 3) | msg->level, buf); + if (time) len += print_time(msg->ts_nsec, buf + len); + + len += print_caller(msg->caller_id, buf + len); + + if (IS_ENABLED(CONFIG_PRINTK_CALLER) || time) { + buf[len++] = ' '; + buf[len] = '\0'; + } + return len; } diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8d24f4ed66fd..89df6750b365 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -17,6 +17,23 @@ config PRINTK_TIME The behavior is also controlled by the kernel command line parameter printk.time=1. See Documentation/admin-guide/kernel-parameters.rst +config PRINTK_CALLER + bool "Show caller information on printks" + depends on PRINTK + help + Selecting this option causes printk() to add a caller "thread id" (if + in task context) or a caller "processor id" (if not in task context) + to every message. + + This option is intended for environments where multiple threads + concurrently call printk() for many times, for it is difficult to + interpret without knowing where these lines (or sometimes individual + line which was divided into multiple lines due to race) came from. + + Since toggling after boot makes the code racy, currently there is + no option to enable/disable at the kernel command line parameter or + sysfs interface. + config CONSOLE_LOGLEVEL_DEFAULT int "Default console loglevel (1-15)" range 1 15 From 5eb8ba90958de1285120dae5d3a5d2b1a360b3b4 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:10:01 -0500 Subject: [PATCH 0005/4277] clk: highbank: fix refcount leak in hb_clk_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 26cae166cff9 ("ARM: highbank: remove custom .init_time hook") Signed-off-by: Stephen Boyd --- drivers/clk/clk-highbank.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 727ed8e1bb72..8e4581004695 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -293,6 +293,7 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk /* Map system registers */ srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs"); hb_clk->reg = of_iomap(srnp, 0); + of_node_put(srnp); BUG_ON(!hb_clk->reg); hb_clk->reg += reg; From 70af6c5b5270e8101f318c4b69cc98a726edfab9 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:14:42 -0500 Subject: [PATCH 0006/4277] clk: qoriq: fix refcount leak in clockgen_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 0dfc86b3173f ("clk: qoriq: Move chip-specific knowledge into driver") Signed-off-by: Stephen Boyd --- drivers/clk/clk-qoriq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 4c30b6e799ed..97a0029a1cca 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -1389,6 +1389,7 @@ static void __init clockgen_init(struct device_node *np) pr_err("%s: Couldn't map %pOF regs\n", __func__, guts); } + of_node_put(guts); } } From 2274d8001fbb5e1942fbcab5ad2eb15553b09ed2 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:21:31 -0500 Subject: [PATCH 0007/4277] clk: ti: fix refcount leak in ti_dt_clocks_register() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 5b385a45e001 ("clk: ti: add support for clkctrl aliases") Signed-off-by: Stephen Boyd --- drivers/clk/ti/clk.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index d0cd58534781..5174f9a91679 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -191,9 +191,13 @@ void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) clkdev_add(&c->lk); } else { if (num_args && !has_clkctrl_data) { - if (of_find_compatible_node(NULL, NULL, - "ti,clkctrl")) { + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, + "ti,clkctrl"); + if (np) { has_clkctrl_data = true; + of_node_put(np); } else { clkctrl_nodes_missing = true; From 7f9705beeb3759e69165e7aff588f6488ff6c1ac Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:29:02 -0500 Subject: [PATCH 0008/4277] clk: socfpga: fix refcount leak The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 5343325ff3dd ("clk: socfpga: add a clock driver for the Arria 10 platform") Fixes: a30d27ed739b ("clk: socfpga: fix clock driver for 3.15") Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-pll-a10.c | 1 + drivers/clk/socfpga/clk-pll.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c index 35fabe1a32c3..269467e8e07e 100644 --- a/drivers/clk/socfpga/clk-pll-a10.c +++ b/drivers/clk/socfpga/clk-pll-a10.c @@ -95,6 +95,7 @@ static struct clk * __init __socfpga_pll_init(struct device_node *node, clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); + of_node_put(clkmgr_np); BUG_ON(!clk_mgr_a10_base_addr); pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index c7f463172e4b..b4b44e9b5901 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -100,6 +100,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); clk_mgr_base_addr = of_iomap(clkmgr_np, 0); + of_node_put(clkmgr_np); BUG_ON(!clk_mgr_base_addr); pll_clk->hw.reg = clk_mgr_base_addr + reg; From cee82eb9532090cd1dc953e845d71f9b1445c84e Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:32:15 -0500 Subject: [PATCH 0009/4277] clk: samsung: exynos4: fix refcount leak in exynos4_get_xom() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: e062b571777f ("clk: exynos4: register clocks using common clock framework") Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk-exynos4.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 59d4d46667ce..54066e6508d3 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1028,6 +1028,7 @@ static unsigned long __init exynos4_get_xom(void) xom = readl(chipid_base + 8); iounmap(chipid_base); + of_node_put(np); } return xom; From c9ec1d8fef31b5fc9e90e99f9bd685db5caa7c5e Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:53:00 -0500 Subject: [PATCH 0010/4277] clk: imx6q: fix refcount leak in imx6q_clocks_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 2acd1b6f889c ("ARM: i.MX6: implement clocks using common clock framework") Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6q.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index bbe0c60f4d09..53617e0632a9 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -424,6 +424,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) { From 1731e14fb30212dd8c1e9f8fc1af061e56498c55 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:55:10 -0500 Subject: [PATCH 0011/4277] clk: imx6sx: fix refcount leak in imx6sx_clocks_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: d55135689019 ("ARM: imx: add clock driver for imx6sx") Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6sx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index 18527a335ace..91558b09bf9e 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -151,6 +151,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop"); base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); clks[IMX6SX_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); clks[IMX6SX_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); From 5f8c183a996b76bb09748073c856e4246fd4ce95 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:57:16 -0500 Subject: [PATCH 0012/4277] clk: imx7d: fix refcount leak in imx7d_clocks_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 8f6d8094b215 ("ARM: imx: add imx7d clk tree support") Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx7d.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index adb08f64c691..12e2e85bbe1c 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -404,6 +404,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop"); base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); clks[IMX7D_PLL_ARM_MAIN_SRC] = imx_clk_mux("pll_arm_main_src", base + 0x60, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); clks[IMX7D_PLL_DRAM_MAIN_SRC] = imx_clk_mux("pll_dram_main_src", base + 0x70, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); From 567177024e0313e4f0dcba7ba10c0732e50e655d Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:59:36 -0500 Subject: [PATCH 0013/4277] clk: vf610: fix refcount leak in vf610_clocks_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Fixes: 1f2c5fd5f048 ("ARM: imx: add VF610 clock support") Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-vf610.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 6dae54325a91..a334667c450a 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -203,6 +203,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); anatop_base = of_iomap(np, 0); BUG_ON(!anatop_base); + of_node_put(np); np = ccm_node; ccm_base = of_iomap(np, 0); From a3c24050bdf70c958a8d98c2823b66ea761e6a31 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:36:58 -0500 Subject: [PATCH 0014/4277] clk: armada-370: fix refcount leak in a370_clk_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Reviewed-by: Gregory CLEMENT Fixes: 07ad6836fa21 ("clk: mvebu: armada-370: maintain clock init order") Signed-off-by: Stephen Boyd --- drivers/clk/mvebu/armada-370.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 7dedfaa6e152..5c6bbee396b3 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -175,8 +175,10 @@ static void __init a370_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &a370_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, a370_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init); From e7beeab9c61591cd0e690d8733d534c3f4278ff8 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:40:19 -0500 Subject: [PATCH 0015/4277] clk: kirkwood: fix refcount leak in kirkwood_clk_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Reviewed-by: Gregory CLEMENT Fixes: 58d516ae95cb ("clk: mvebu: kirkwood: maintain clock init order") Signed-off-by: Stephen Boyd --- drivers/clk/mvebu/kirkwood.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index 6f784167bda4..35af3aa18f1c 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -331,6 +331,8 @@ static void __init kirkwood_clk_init(struct device_node *np) if (cgnp) { mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); + + of_node_put(cgnp); } } CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", From db20a90a4b6745dad62753f8bd2f66afdd5abc84 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:42:26 -0500 Subject: [PATCH 0016/4277] clk: armada-xp: fix refcount leak in axp_clk_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Reviewed-by: Gregory CLEMENT Fixes: 0a11a6ae9437 ("clk: mvebu: armada-xp: maintain clock init order") Signed-off-by: Stephen Boyd --- drivers/clk/mvebu/armada-xp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index e8f03293ec83..fa1568279c23 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -226,7 +226,9 @@ static void __init axp_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &axp_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, axp_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init); From 9b4eedf627045ae5ddcff60a484200cdd554c413 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:48:05 -0500 Subject: [PATCH 0017/4277] clk: mv98dx3236: fix refcount leak in mv98dx3236_clk_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Reviewed-by: Gregory CLEMENT Fixes: 337072604224 ("clk: mvebu: Expand mv98dx3236-core-clock support") Signed-off-by: Stephen Boyd --- drivers/clk/mvebu/mv98dx3236.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/mvebu/mv98dx3236.c b/drivers/clk/mvebu/mv98dx3236.c index 0a74cf7a7725..1c8ab4f834ba 100644 --- a/drivers/clk/mvebu/mv98dx3236.c +++ b/drivers/clk/mvebu/mv98dx3236.c @@ -172,7 +172,9 @@ static void __init mv98dx3236_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &mv98dx3236_core_clocks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock", mv98dx3236_clk_init); From 8d726c5128298386b907963033be93407b0c4275 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 08:50:13 -0500 Subject: [PATCH 0018/4277] clk: dove: fix refcount leak in dove_clk_init() The of_find_compatible_node() returns a node pointer with refcount incremented, but there is the lack of use of the of_node_put() when done. Add the missing of_node_put() to release the refcount. Signed-off-by: Yangtao Li Reviewed-by: Gregory CLEMENT Fixes: 8f7fc5450b64 ("clk: mvebu: dove: maintain clock init order") Fixes: 63b8d92c793f ("clk: add Dove PLL divider support for GPU, VMeta and AXI clocks") Signed-off-by: Stephen Boyd --- drivers/clk/mvebu/dove.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c index e0dd99f36bf4..0bd09d33f9cf 100644 --- a/drivers/clk/mvebu/dove.c +++ b/drivers/clk/mvebu/dove.c @@ -188,10 +188,14 @@ static void __init dove_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &dove_coreclks); - if (ddnp) + if (ddnp) { dove_divider_clk_init(ddnp); + of_node_put(ddnp); + } - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, dove_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(dove_clk, "marvell,dove-core-clock", dove_clk_init); From 39be9b6d0455ed6dc6e9ac7252f56a498db5314e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 3 Jan 2019 18:09:39 -0800 Subject: [PATCH 0019/4277] Input: soc_button_array - add usage-page 0x01 usage-id 0xca mapping The ACPI0011 _DSD button descriptor on a CHT based Intel Compute Sticks contains a mapping for usage-page 0x01 usage-id 0xca. As described in hutrr52_system_display_rotation_lock_controls_0.pdf this should be mapped as a "System Display Rotation Lock Slider Switch", this commit adds support for this, silencing the following warning: soc_button_array ACPI0011:00: Unknown button index 4 upage 01 usage ca, ignoring Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/misc/soc_button_array.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 23520df7650f..f53923b1593b 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -185,6 +185,10 @@ static int soc_button_parse_btn_desc(struct device *dev, info->name = "power"; info->event_code = KEY_POWER; info->wakeup = true; + } else if (upage == 0x01 && usage == 0xca) { + info->name = "rotation lock switch"; + info->event_type = EV_SW; + info->event_code = SW_ROTATE_LOCK; } else if (upage == 0x07 && usage == 0xe3) { info->name = "home"; info->event_code = KEY_LEFTMETA; From e9eb788f9442d1b5d93efdb30c3be071ce8a22b1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 3 Jan 2019 18:10:45 -0800 Subject: [PATCH 0020/4277] Input: soc_button_array - fix mapping of the 5th GPIO in a PNP0C40 device The Microsoft documenation for the PNP0C40 device aka the "Windows-compatible button array" describes the 5th GpioInt listed in the resources as: '5. Interrupt corresponding to the "Rotation Lock" button, if supported'. Notice this describes the 5th entry as a button while we sofar have been mapping it to EV_SW, SW_ROTATE_LOCK. On my Point of View TAB P1006W-232 which actually comes with a rotation-lock button, the button indeed is a button and not a slider/switch. An image search for other Windows tablets has found 2 more models with a rotation-lock button and on both of those it too is a push-button and not a slider/switch. Further evidence can be found in the HUT extension HUTRR52 from Microsoft which adds rotation lock support to the HUT, which describes 2 different usages: "0xC9 System Display Rotation Lock Button" and "0xCA System Display Rotation Lock Slider Switch" note that switch is seen as a separate thing here and the non switch wording is an exact match for the "Windows-compatible button array" spec wording. TL;DR: our current mapping of the 5th GPIO to SW_ROTATE_LOCK is wrong because the 5th GPIO is for a push-button not a switch. This commit fixes this by maping the 5th GPIO to KEY_ROTATE_LOCK_TOGGLE. Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- drivers/input/misc/soc_button_array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index f53923b1593b..bb458beecb43 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -377,7 +377,7 @@ static struct soc_button_info soc_button_PNP0C40[] = { { "home", 1, EV_KEY, KEY_LEFTMETA, false, true }, { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, - { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false }, + { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false }, { } }; From e7987a0659421c2bfe5bcf3bc689876ba9be5f3f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Dec 2018 09:57:02 +0100 Subject: [PATCH 0021/4277] dmaengine: at_hdmac: drop useless LIST_HEAD Drop LIST_HEAD where the variable it declares is never used. tmp_list has been declared since the introduction of the driver and has never been used. The two declarations of list were introduced with the containing functions but were also not used. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier x; @@ - LIST_HEAD(x); ... when != x // Fixes: dc78baa2b90b ("dmaengine: at_hdmac: new driver for the Atmel AHB DMA Controller") Fixes: 4facfe7f09f2b ("dmaengine: hdmac: Split device_control") Acked-by: Ludovic Desroches Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 01d936c9fe89..a0a9cd76c1d4 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -134,7 +134,6 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) struct at_desc *ret = NULL; unsigned long flags; unsigned int i = 0; - LIST_HEAD(tmp_list); spin_lock_irqsave(&atchan->lock, flags); list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) { @@ -1387,8 +1386,6 @@ static int atc_pause(struct dma_chan *chan) int chan_id = atchan->chan_common.chan_id; unsigned long flags; - LIST_HEAD(list); - dev_vdbg(chan2dev(chan), "%s\n", __func__); spin_lock_irqsave(&atchan->lock, flags); @@ -1408,8 +1405,6 @@ static int atc_resume(struct dma_chan *chan) int chan_id = atchan->chan_common.chan_id; unsigned long flags; - LIST_HEAD(list); - dev_vdbg(chan2dev(chan), "%s\n", __func__); if (!atc_chan_is_paused(atchan)) From 1bd09f869f9b294b5645e2ac153ee59604ef959e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Dec 2018 09:57:07 +0100 Subject: [PATCH 0022/4277] dmaengine: dw: drop useless LIST_HEAD Drop LIST_HEAD where the variable it declares is never used. Commit ab703f818ac3 ("dmaengine: dw: lazy allocation of dma descriptors") removed the uses, but not the declaration. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier x; @@ - LIST_HEAD(x); ... when != x // Fixes: ab703f818ac3 ("dmaengine: dw: lazy allocation of dma descriptors") Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index dc053e62f894..290d21be4d4c 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1150,7 +1150,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma *dw = to_dw_dma(chan->device); unsigned long flags; - LIST_HEAD(list); dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, dwc->descs_allocated); From 3c763b388897a7398ef797b93cbf6580b6ae49e1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Dec 2018 09:57:09 +0100 Subject: [PATCH 0023/4277] dmaengine: st_fdma: drop useless LIST_HEAD Drop LIST_HEAD where the variable it declares is never used. The declarations were introduced with the file, but the declared variables were not used. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier x; @@ - LIST_HEAD(x); ... when != x // Fixes: 6b4cd727eaf1 ("dmaengine: st_fdma: Add STMicroelectronics FDMA engine driver support") Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/st_fdma.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c index 07c20aa2e955..954f7cf62bb1 100644 --- a/drivers/dma/st_fdma.c +++ b/drivers/dma/st_fdma.c @@ -294,8 +294,6 @@ static void st_fdma_free_chan_res(struct dma_chan *chan) struct rproc *rproc = fchan->fdev->slim_rproc->rproc; unsigned long flags; - LIST_HEAD(head); - dev_dbg(fchan->fdev->dev, "%s: freeing chan:%d\n", __func__, fchan->vchan.chan.chan_id); @@ -626,7 +624,6 @@ static void st_fdma_issue_pending(struct dma_chan *chan) static int st_fdma_pause(struct dma_chan *chan) { unsigned long flags; - LIST_HEAD(head); struct st_fdma_chan *fchan = to_st_fdma_chan(chan); int ch_id = fchan->vchan.chan.chan_id; unsigned long cmd = FDMA_CMD_PAUSE(ch_id); From 4f194969c3b01ab080d38114feb09be05b16d7c3 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Dec 2018 09:57:12 +0100 Subject: [PATCH 0024/4277] dmaengine: pl330: drop useless LIST_HEAD Drop LIST_HEAD where the variable it declares is never used. The variable has not been used since the function was introduced in 740aa95703c5 ("dmaengine: pl330: Split device_control"). The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier x; @@ - LIST_HEAD(x); ... when != x // Fixes: 740aa95703c5 ("dmaengine: pl330: Split device_control") Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index cff1b143fff5..eec79fdf27a5 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2267,7 +2267,6 @@ static int pl330_terminate_all(struct dma_chan *chan) struct dma_pl330_desc *desc; unsigned long flags; struct pl330_dmac *pl330 = pch->dmac; - LIST_HEAD(list); bool power_down = false; pm_runtime_get_sync(pl330->ddma.dev); From de2c45314d9bb8839f226dcf989b2582591921f5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Dec 2018 09:57:13 +0100 Subject: [PATCH 0025/4277] dmaengine: sa11x0: drop useless LIST_HEAD Drop LIST_HEAD where the variable it declares has never been used. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier x; @@ - LIST_HEAD(x); ... when != x // Fixes: 4a533218fccf ("dmaengine: sa11x0: Split device_control") Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/sa11x0-dma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index 784d5f1a473b..3fae23768b47 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -705,7 +705,6 @@ static int sa11x0_dma_device_pause(struct dma_chan *chan) struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; - LIST_HEAD(head); unsigned long flags; dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); @@ -732,7 +731,6 @@ static int sa11x0_dma_device_resume(struct dma_chan *chan) struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; - LIST_HEAD(head); unsigned long flags; dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); From a7359e762fdd6eea432fefd9d38851540192e7e3 Mon Sep 17 00:00:00 2001 From: Wen He Date: Tue, 30 Oct 2018 10:35:58 +0800 Subject: [PATCH 0026/4277] dmaengine: fsldma: Replace DMA_IN/OUT by FSL_DMA_IN/OUT This patch implement a standard macro call functions is used to NXP dma drivers. Signed-off-by: Wen He Signed-off-by: Peng Ma Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 16 ++++++++-------- drivers/dma/fsldma.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 9d360a3fbae3..1e38e6b94006 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -53,42 +53,42 @@ static const char msg_ld_oom[] = "No free memory for link descriptor"; static void set_sr(struct fsldma_chan *chan, u32 val) { - DMA_OUT(chan, &chan->regs->sr, val, 32); + FSL_DMA_OUT(chan, &chan->regs->sr, val, 32); } static u32 get_sr(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->sr, 32); + return FSL_DMA_IN(chan, &chan->regs->sr, 32); } static void set_mr(struct fsldma_chan *chan, u32 val) { - DMA_OUT(chan, &chan->regs->mr, val, 32); + FSL_DMA_OUT(chan, &chan->regs->mr, val, 32); } static u32 get_mr(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->mr, 32); + return FSL_DMA_IN(chan, &chan->regs->mr, 32); } static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr) { - DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); + FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); } static dma_addr_t get_cdar(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; + return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; } static void set_bcr(struct fsldma_chan *chan, u32 val) { - DMA_OUT(chan, &chan->regs->bcr, val, 32); + FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32); } static u32 get_bcr(struct fsldma_chan *chan) { - return DMA_IN(chan, &chan->regs->bcr, 32); + return FSL_DMA_IN(chan, &chan->regs->bcr, 32); } /* diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 4787d485dd76..982845b830c7 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -223,10 +223,10 @@ static void out_le64(u64 __iomem *addr, u64 val) } #endif -#define DMA_IN(fsl_chan, addr, width) \ +#define FSL_DMA_IN(fsl_chan, addr, width) \ (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ in_be##width(addr) : in_le##width(addr)) -#define DMA_OUT(fsl_chan, addr, val, width) \ +#define FSL_DMA_OUT(fsl_chan, addr, val, width) \ (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ out_be##width(addr, val) : out_le##width(addr, val)) From a1ff82a9c165ba26f0e2f4771d0490c513056428 Mon Sep 17 00:00:00 2001 From: Peng Ma Date: Tue, 30 Oct 2018 10:35:59 +0800 Subject: [PATCH 0027/4277] dmaengine: fsldma: Adding macro FSL_DMA_IN/OUT implement for ARM platform This patch add the macro FSL_DMA_IN/OUT implement for ARM platform. Signed-off-by: Wen He Signed-off-by: Peng Ma Signed-off-by: Vinod Koul --- drivers/dma/fsldma.h | 69 +++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 982845b830c7..88db939c04a1 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -196,39 +196,62 @@ struct fsldma_chan { #define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node) #define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx) +#ifdef CONFIG_PPC +#define fsl_ioread32(p) in_le32(p) +#define fsl_ioread32be(p) in_be32(p) +#define fsl_iowrite32(v, p) out_le32(p, v) +#define fsl_iowrite32be(v, p) out_be32(p, v) + #ifndef __powerpc64__ -static u64 in_be64(const u64 __iomem *addr) +static u64 fsl_ioread64(const u64 __iomem *addr) { - return ((u64)in_be32((u32 __iomem *)addr) << 32) | - (in_be32((u32 __iomem *)addr + 1)); + u32 fsl_addr = lower_32_bits(addr); + u64 fsl_addr_hi = (u64)in_le32((u32 *)(fsl_addr + 1)) << 32; + + return fsl_addr_hi | in_le32((u32 *)fsl_addr); } -static void out_be64(u64 __iomem *addr, u64 val) -{ - out_be32((u32 __iomem *)addr, val >> 32); - out_be32((u32 __iomem *)addr + 1, (u32)val); -} - -/* There is no asm instructions for 64 bits reverse loads and stores */ -static u64 in_le64(const u64 __iomem *addr) -{ - return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) | - (in_le32((u32 __iomem *)addr)); -} - -static void out_le64(u64 __iomem *addr, u64 val) +static void fsl_iowrite64(u64 val, u64 __iomem *addr) { out_le32((u32 __iomem *)addr + 1, val >> 32); out_le32((u32 __iomem *)addr, (u32)val); } + +static u64 fsl_ioread64be(const u64 __iomem *addr) +{ + u32 fsl_addr = lower_32_bits(addr); + u64 fsl_addr_hi = (u64)in_be32((u32 *)fsl_addr) << 32; + + return fsl_addr_hi | in_be32((u32 *)(fsl_addr + 1)); +} + +static void fsl_iowrite64be(u64 val, u64 __iomem *addr) +{ + out_be32((u32 __iomem *)addr, val >> 32); + out_be32((u32 __iomem *)addr + 1, (u32)val); +} +#endif #endif -#define FSL_DMA_IN(fsl_chan, addr, width) \ - (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ - in_be##width(addr) : in_le##width(addr)) -#define FSL_DMA_OUT(fsl_chan, addr, val, width) \ - (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ - out_be##width(addr, val) : out_le##width(addr, val)) +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM) +#define fsl_ioread32(p) ioread32(p) +#define fsl_ioread32be(p) ioread32be(p) +#define fsl_iowrite32(v, p) iowrite32(v, p) +#define fsl_iowrite32be(v, p) iowrite32be(v, p) +#define fsl_ioread64(p) ioread64(p) +#define fsl_ioread64be(p) ioread64be(p) +#define fsl_iowrite64(v, p) iowrite64(v, p) +#define fsl_iowrite64be(v, p) iowrite64be(v, p) +#endif + +#define FSL_DMA_IN(fsl_dma, addr, width) \ + (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \ + fsl_ioread##width##be(addr) : fsl_ioread##width(addr)) + +#define FSL_DMA_OUT(fsl_dma, addr, val, width) \ + (((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ? \ + fsl_iowrite##width##be(val, addr) : fsl_iowrite \ + ##width(val, addr)) #define DMA_TO_CPU(fsl_chan, d, width) \ (((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ? \ From b092529e0aa09829a6404424ce167bf3ce3235e2 Mon Sep 17 00:00:00 2001 From: Peng Ma Date: Tue, 30 Oct 2018 10:36:00 +0800 Subject: [PATCH 0028/4277] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel virtuallization by allowing DMA jobs to be enqueued into different command queues. Signed-off-by: Wen He Signed-off-by: Jiaheng Fan Signed-off-by: Peng Ma Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 14 + drivers/dma/Makefile | 1 + drivers/dma/fsl-qdma.c | 1258 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1273 insertions(+) create mode 100644 drivers/dma/fsl-qdma.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d2286c7f7222..0b1dfb5bf2d9 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -218,6 +218,20 @@ config FSL_EDMA multiplexing capability for DMA request sources(slot). This module can be found on Freescale Vybrid and LS-1 SoCs. +config FSL_QDMA + tristate "NXP Layerscape qDMA engine support" + depends on ARM || ARM64 + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + select DMA_ENGINE_RAID + select ASYNC_TX_ENABLE_CHANNEL_SWITCH + help + Support the NXP Layerscape qDMA engine with command queue and legacy mode. + Channel virtualization is supported through enqueuing of DMA jobs to, + or dequeuing DMA jobs from, different work queues. + This module can be found on NXP Layerscape SoCs. + The qdma driver only work on SoCs with a DPAA hardware block. + config FSL_RAID tristate "Freescale RAID engine Support" depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 09571a81353d..6126e1c3a875 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o +obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_HSU_DMA) += hsu/ obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c new file mode 100644 index 000000000000..c18e3492090b --- /dev/null +++ b/drivers/dma/fsl-qdma.c @@ -0,0 +1,1258 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2014-2015 Freescale +// Copyright 2018 NXP + +/* + * Driver for NXP Layerscape Queue Direct Memory Access Controller + * + * Author: + * Wen He + * Jiaheng Fan + * + */ + +#include +#include +#include +#include +#include +#include + +#include "virt-dma.h" +#include "fsldma.h" + +/* Register related definition */ +#define FSL_QDMA_DMR 0x0 +#define FSL_QDMA_DSR 0x4 +#define FSL_QDMA_DEIER 0xe00 +#define FSL_QDMA_DEDR 0xe04 +#define FSL_QDMA_DECFDW0R 0xe10 +#define FSL_QDMA_DECFDW1R 0xe14 +#define FSL_QDMA_DECFDW2R 0xe18 +#define FSL_QDMA_DECFDW3R 0xe1c +#define FSL_QDMA_DECFQIDR 0xe30 +#define FSL_QDMA_DECBR 0xe34 + +#define FSL_QDMA_BCQMR(x) (0xc0 + 0x100 * (x)) +#define FSL_QDMA_BCQSR(x) (0xc4 + 0x100 * (x)) +#define FSL_QDMA_BCQEDPA_SADDR(x) (0xc8 + 0x100 * (x)) +#define FSL_QDMA_BCQDPA_SADDR(x) (0xcc + 0x100 * (x)) +#define FSL_QDMA_BCQEEPA_SADDR(x) (0xd0 + 0x100 * (x)) +#define FSL_QDMA_BCQEPA_SADDR(x) (0xd4 + 0x100 * (x)) +#define FSL_QDMA_BCQIER(x) (0xe0 + 0x100 * (x)) +#define FSL_QDMA_BCQIDR(x) (0xe4 + 0x100 * (x)) + +#define FSL_QDMA_SQDPAR 0x80c +#define FSL_QDMA_SQEPAR 0x814 +#define FSL_QDMA_BSQMR 0x800 +#define FSL_QDMA_BSQSR 0x804 +#define FSL_QDMA_BSQICR 0x828 +#define FSL_QDMA_CQMR 0xa00 +#define FSL_QDMA_CQDSCR1 0xa08 +#define FSL_QDMA_CQDSCR2 0xa0c +#define FSL_QDMA_CQIER 0xa10 +#define FSL_QDMA_CQEDR 0xa14 +#define FSL_QDMA_SQCCMR 0xa20 + +/* Registers for bit and genmask */ +#define FSL_QDMA_CQIDR_SQT BIT(15) +#define QDMA_CCDF_FOTMAT BIT(29) +#define QDMA_CCDF_SER BIT(30) +#define QDMA_SG_FIN BIT(30) +#define QDMA_SG_LEN_MASK GENMASK(29, 0) +#define QDMA_CCDF_MASK GENMASK(28, 20) + +#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0) +#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0) +#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0) + +#define FSL_QDMA_BCQIER_CQTIE BIT(15) +#define FSL_QDMA_BCQIER_CQPEIE BIT(23) +#define FSL_QDMA_BSQICR_ICEN BIT(31) + +#define FSL_QDMA_BSQICR_ICST(x) ((x) << 16) +#define FSL_QDMA_CQIER_MEIE BIT(31) +#define FSL_QDMA_CQIER_TEIE BIT(0) +#define FSL_QDMA_SQCCMR_ENTER_WM BIT(21) + +#define FSL_QDMA_BCQMR_EN BIT(31) +#define FSL_QDMA_BCQMR_EI BIT(30) +#define FSL_QDMA_BCQMR_CD_THLD(x) ((x) << 20) +#define FSL_QDMA_BCQMR_CQ_SIZE(x) ((x) << 16) + +#define FSL_QDMA_BCQSR_QF BIT(16) +#define FSL_QDMA_BCQSR_XOFF BIT(0) + +#define FSL_QDMA_BSQMR_EN BIT(31) +#define FSL_QDMA_BSQMR_DI BIT(30) +#define FSL_QDMA_BSQMR_CQ_SIZE(x) ((x) << 16) + +#define FSL_QDMA_BSQSR_QE BIT(17) + +#define FSL_QDMA_DMR_DQD BIT(30) +#define FSL_QDMA_DSR_DB BIT(31) + +/* Size related definition */ +#define FSL_QDMA_QUEUE_MAX 8 +#define FSL_QDMA_COMMAND_BUFFER_SIZE 64 +#define FSL_QDMA_DESCRIPTOR_BUFFER_SIZE 32 +#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN 64 +#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX 16384 +#define FSL_QDMA_QUEUE_NUM_MAX 8 + +/* Field definition for CMD */ +#define FSL_QDMA_CMD_RWTTYPE 0x4 +#define FSL_QDMA_CMD_LWC 0x2 +#define FSL_QDMA_CMD_RWTTYPE_OFFSET 28 +#define FSL_QDMA_CMD_NS_OFFSET 27 +#define FSL_QDMA_CMD_DQOS_OFFSET 24 +#define FSL_QDMA_CMD_WTHROTL_OFFSET 20 +#define FSL_QDMA_CMD_DSEN_OFFSET 19 +#define FSL_QDMA_CMD_LWC_OFFSET 16 + +/* Field definition for Descriptor offset */ +#define QDMA_CCDF_STATUS 20 +#define QDMA_CCDF_OFFSET 20 + +/* Field definition for safe loop count*/ +#define FSL_QDMA_HALT_COUNT 1500 +#define FSL_QDMA_MAX_SIZE 16385 +#define FSL_QDMA_COMP_TIMEOUT 1000 +#define FSL_COMMAND_QUEUE_OVERFLLOW 10 + +#define FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma_engine, x) \ + (((fsl_qdma_engine)->block_offset) * (x)) + +/** + * struct fsl_qdma_format - This is the struct holding describing compound + * descriptor format with qDMA. + * @status: Command status and enqueue status notification. + * @cfg: Frame offset and frame format. + * @addr_lo: Holding the compound descriptor of the lower + * 32-bits address in memory 40-bit address. + * @addr_hi: Same as above member, but point high 8-bits in + * memory 40-bit address. + * @__reserved1: Reserved field. + * @cfg8b_w1: Compound descriptor command queue origin produced + * by qDMA and dynamic debug field. + * @data Pointer to the memory 40-bit address, describes DMA + * source information and DMA destination information. + */ +struct fsl_qdma_format { + __le32 status; + __le32 cfg; + union { + struct { + __le32 addr_lo; + u8 addr_hi; + u8 __reserved1[2]; + u8 cfg8b_w1; + } __packed; + __le64 data; + }; +} __packed; + +/* qDMA status notification pre information */ +struct fsl_pre_status { + u64 addr; + u8 queue; +}; + +static DEFINE_PER_CPU(struct fsl_pre_status, pre); + +struct fsl_qdma_chan { + struct virt_dma_chan vchan; + struct virt_dma_desc vdesc; + enum dma_status status; + struct fsl_qdma_engine *qdma; + struct fsl_qdma_queue *queue; +}; + +struct fsl_qdma_queue { + struct fsl_qdma_format *virt_head; + struct fsl_qdma_format *virt_tail; + struct list_head comp_used; + struct list_head comp_free; + struct dma_pool *comp_pool; + struct dma_pool *desc_pool; + spinlock_t queue_lock; + dma_addr_t bus_addr; + u32 n_cq; + u32 id; + struct fsl_qdma_format *cq; + void __iomem *block_base; +}; + +struct fsl_qdma_comp { + dma_addr_t bus_addr; + dma_addr_t desc_bus_addr; + struct fsl_qdma_format *virt_addr; + struct fsl_qdma_format *desc_virt_addr; + struct fsl_qdma_chan *qchan; + struct virt_dma_desc vdesc; + struct list_head list; +}; + +struct fsl_qdma_engine { + struct dma_device dma_dev; + void __iomem *ctrl_base; + void __iomem *status_base; + void __iomem *block_base; + u32 n_chans; + u32 n_queues; + struct mutex fsl_qdma_mutex; + int error_irq; + int *queue_irq; + u32 feature; + struct fsl_qdma_queue *queue; + struct fsl_qdma_queue **status; + struct fsl_qdma_chan *chans; + int block_number; + int block_offset; + int irq_base; + int desc_allocated; + +}; + +static inline u64 +qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf) +{ + return le64_to_cpu(ccdf->data) & (U64_MAX >> 24); +} + +static inline void +qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr) +{ + ccdf->addr_hi = upper_32_bits(addr); + ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr)); +} + +static inline u8 +qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf) +{ + return ccdf->cfg8b_w1 & U8_MAX; +} + +static inline int +qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf) +{ + return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >> QDMA_CCDF_OFFSET; +} + +static inline void +qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset) +{ + ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset); +} + +static inline int +qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf) +{ + return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS; +} + +static inline void +qdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status) +{ + ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status); +} + +static inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int len) +{ + csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK); +} + +static inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int len) +{ + csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len & QDMA_SG_LEN_MASK)); +} + +static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr) +{ + return FSL_DMA_IN(qdma, addr, 32); +} + +static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val, + void __iomem *addr) +{ + FSL_DMA_OUT(qdma, addr, val, 32); +} + +static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct fsl_qdma_chan, vchan.chan); +} + +static struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd) +{ + return container_of(vd, struct fsl_qdma_comp, vdesc); +} + +static void fsl_qdma_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma; + struct fsl_qdma_comp *comp_temp, *_comp_temp; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + + if (!fsl_queue->comp_pool && !fsl_queue->comp_pool) + return; + + list_for_each_entry_safe(comp_temp, _comp_temp, + &fsl_queue->comp_used, list) { + dma_pool_free(fsl_queue->comp_pool, + comp_temp->virt_addr, + comp_temp->bus_addr); + dma_pool_free(fsl_queue->desc_pool, + comp_temp->desc_virt_addr, + comp_temp->desc_bus_addr); + list_del(&comp_temp->list); + kfree(comp_temp); + } + + list_for_each_entry_safe(comp_temp, _comp_temp, + &fsl_queue->comp_free, list) { + dma_pool_free(fsl_queue->comp_pool, + comp_temp->virt_addr, + comp_temp->bus_addr); + dma_pool_free(fsl_queue->desc_pool, + comp_temp->desc_virt_addr, + comp_temp->desc_bus_addr); + list_del(&comp_temp->list); + kfree(comp_temp); + } + + dma_pool_destroy(fsl_queue->comp_pool); + dma_pool_destroy(fsl_queue->desc_pool); + + fsl_qdma->desc_allocated--; + fsl_queue->comp_pool = NULL; + fsl_queue->desc_pool = NULL; +} + +static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp, + dma_addr_t dst, dma_addr_t src, u32 len) +{ + struct fsl_qdma_format *sdf, *ddf; + struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest; + + ccdf = fsl_comp->virt_addr; + csgf_desc = fsl_comp->virt_addr + 1; + csgf_src = fsl_comp->virt_addr + 2; + csgf_dest = fsl_comp->virt_addr + 3; + sdf = fsl_comp->desc_virt_addr; + ddf = fsl_comp->desc_virt_addr + 1; + + memset(fsl_comp->virt_addr, 0, FSL_QDMA_COMMAND_BUFFER_SIZE); + memset(fsl_comp->desc_virt_addr, 0, FSL_QDMA_DESCRIPTOR_BUFFER_SIZE); + /* Head Command Descriptor(Frame Descriptor) */ + qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16); + qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf)); + qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf)); + /* Status notification is enqueued to status queue. */ + /* Compound Command Descriptor(Frame List Table) */ + qdma_desc_addr_set64(csgf_desc, fsl_comp->desc_bus_addr); + /* It must be 32 as Compound S/G Descriptor */ + qdma_csgf_set_len(csgf_desc, 32); + qdma_desc_addr_set64(csgf_src, src); + qdma_csgf_set_len(csgf_src, len); + qdma_desc_addr_set64(csgf_dest, dst); + qdma_csgf_set_len(csgf_dest, len); + /* This entry is the last entry. */ + qdma_csgf_set_f(csgf_dest, len); + /* Descriptor Buffer */ + sdf->data = + cpu_to_le64(FSL_QDMA_CMD_RWTTYPE << + FSL_QDMA_CMD_RWTTYPE_OFFSET); + ddf->data = + cpu_to_le64(FSL_QDMA_CMD_RWTTYPE << + FSL_QDMA_CMD_RWTTYPE_OFFSET); + ddf->data |= + cpu_to_le64(FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET); +} + +/* + * Pre-request full command descriptor for enqueue. + */ +static int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue *queue) +{ + int i; + struct fsl_qdma_comp *comp_temp, *_comp_temp; + + for (i = 0; i < queue->n_cq + FSL_COMMAND_QUEUE_OVERFLLOW; i++) { + comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL); + if (!comp_temp) + goto err_alloc; + comp_temp->virt_addr = + dma_pool_alloc(queue->comp_pool, GFP_KERNEL, + &comp_temp->bus_addr); + if (!comp_temp->virt_addr) + goto err_dma_alloc; + + comp_temp->desc_virt_addr = + dma_pool_alloc(queue->desc_pool, GFP_KERNEL, + &comp_temp->desc_bus_addr); + if (!comp_temp->desc_virt_addr) + goto err_desc_dma_alloc; + + list_add_tail(&comp_temp->list, &queue->comp_free); + } + + return 0; + +err_desc_dma_alloc: + dma_pool_free(queue->comp_pool, comp_temp->virt_addr, + comp_temp->bus_addr); + +err_dma_alloc: + kfree(comp_temp); + +err_alloc: + list_for_each_entry_safe(comp_temp, _comp_temp, + &queue->comp_free, list) { + if (comp_temp->virt_addr) + dma_pool_free(queue->comp_pool, + comp_temp->virt_addr, + comp_temp->bus_addr); + if (comp_temp->desc_virt_addr) + dma_pool_free(queue->desc_pool, + comp_temp->desc_virt_addr, + comp_temp->desc_bus_addr); + + list_del(&comp_temp->list); + kfree(comp_temp); + } + + return -ENOMEM; +} + +/* + * Request a command descriptor for enqueue. + */ +static struct fsl_qdma_comp +*fsl_qdma_request_enqueue_desc(struct fsl_qdma_chan *fsl_chan) +{ + unsigned long flags; + struct fsl_qdma_comp *comp_temp; + int timeout = FSL_QDMA_COMP_TIMEOUT; + struct fsl_qdma_queue *queue = fsl_chan->queue; + + while (timeout--) { + spin_lock_irqsave(&queue->queue_lock, flags); + if (!list_empty(&queue->comp_free)) { + comp_temp = list_first_entry(&queue->comp_free, + struct fsl_qdma_comp, + list); + list_del(&comp_temp->list); + + spin_unlock_irqrestore(&queue->queue_lock, flags); + comp_temp->qchan = fsl_chan; + return comp_temp; + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + udelay(1); + } + + return NULL; +} + +static struct fsl_qdma_queue +*fsl_qdma_alloc_queue_resources(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int ret, len, i, j; + int queue_num, block_number; + unsigned int queue_size[FSL_QDMA_QUEUE_MAX]; + struct fsl_qdma_queue *queue_head, *queue_temp; + + queue_num = fsl_qdma->n_queues; + block_number = fsl_qdma->block_number; + + if (queue_num > FSL_QDMA_QUEUE_MAX) + queue_num = FSL_QDMA_QUEUE_MAX; + len = sizeof(*queue_head) * queue_num * block_number; + queue_head = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!queue_head) + return NULL; + + ret = device_property_read_u32_array(&pdev->dev, "queue-sizes", + queue_size, queue_num); + if (ret) { + dev_err(&pdev->dev, "Can't get queue-sizes.\n"); + return NULL; + } + for (j = 0; j < block_number; j++) { + for (i = 0; i < queue_num; i++) { + if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX || + queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) { + dev_err(&pdev->dev, + "Get wrong queue-sizes.\n"); + return NULL; + } + queue_temp = queue_head + i + (j * queue_num); + + queue_temp->cq = + dma_alloc_coherent(&pdev->dev, + sizeof(struct fsl_qdma_format) * + queue_size[i], + &queue_temp->bus_addr, + GFP_KERNEL); + if (!queue_temp->cq) + return NULL; + queue_temp->block_base = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + queue_temp->n_cq = queue_size[i]; + queue_temp->id = i; + queue_temp->virt_head = queue_temp->cq; + queue_temp->virt_tail = queue_temp->cq; + /* + * List for queue command buffer + */ + INIT_LIST_HEAD(&queue_temp->comp_used); + spin_lock_init(&queue_temp->queue_lock); + } + } + return queue_head; +} + +static struct fsl_qdma_queue +*fsl_qdma_prep_status_queue(struct platform_device *pdev) +{ + int ret; + unsigned int status_size; + struct fsl_qdma_queue *status_head; + struct device_node *np = pdev->dev.of_node; + + ret = of_property_read_u32(np, "status-sizes", &status_size); + if (ret) { + dev_err(&pdev->dev, "Can't get status-sizes.\n"); + return NULL; + } + if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX || + status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) { + dev_err(&pdev->dev, "Get wrong status_size.\n"); + return NULL; + } + status_head = devm_kzalloc(&pdev->dev, + sizeof(*status_head), GFP_KERNEL); + if (!status_head) + return NULL; + + /* + * Buffer for queue command + */ + status_head->cq = dma_alloc_coherent(&pdev->dev, + sizeof(struct fsl_qdma_format) * + status_size, + &status_head->bus_addr, + GFP_KERNEL); + if (!status_head->cq) { + devm_kfree(&pdev->dev, status_head); + return NULL; + } + status_head->n_cq = status_size; + status_head->virt_head = status_head->cq; + status_head->virt_tail = status_head->cq; + status_head->comp_pool = NULL; + + return status_head; +} + +static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma) +{ + u32 reg; + int i, j, count = FSL_QDMA_HALT_COUNT; + void __iomem *block, *ctrl = fsl_qdma->ctrl_base; + + /* Disable the command queue and wait for idle state. */ + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); + reg |= FSL_QDMA_DMR_DQD; + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); + for (j = 0; j < fsl_qdma->block_number; j++) { + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++) + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i)); + } + while (1) { + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR); + if (!(reg & FSL_QDMA_DSR_DB)) + break; + if (count-- < 0) + return -EBUSY; + udelay(100); + } + + for (j = 0; j < fsl_qdma->block_number; j++) { + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + + /* Disable status queue. */ + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR); + + /* + * clear the command queue interrupt detect register for + * all queues. + */ + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, + block + FSL_QDMA_BCQIDR(0)); + } + + return 0; +} + +static int +fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma, + void *block, + int id) +{ + bool duplicate; + u32 reg, i, count; + struct fsl_qdma_queue *temp_queue; + struct fsl_qdma_format *status_addr; + struct fsl_qdma_comp *fsl_comp = NULL; + struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue; + struct fsl_qdma_queue *fsl_status = fsl_qdma->status[id]; + + count = FSL_QDMA_MAX_SIZE; + + while (count--) { + duplicate = 0; + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR); + if (reg & FSL_QDMA_BSQSR_QE) + return 0; + + status_addr = fsl_status->virt_head; + + if (qdma_ccdf_get_queue(status_addr) == + __this_cpu_read(pre.queue) && + qdma_ccdf_addr_get64(status_addr) == + __this_cpu_read(pre.addr)) + duplicate = 1; + i = qdma_ccdf_get_queue(status_addr) + + id * fsl_qdma->n_queues; + __this_cpu_write(pre.addr, qdma_ccdf_addr_get64(status_addr)); + __this_cpu_write(pre.queue, qdma_ccdf_get_queue(status_addr)); + temp_queue = fsl_queue + i; + + spin_lock(&temp_queue->queue_lock); + if (list_empty(&temp_queue->comp_used)) { + if (!duplicate) { + spin_unlock(&temp_queue->queue_lock); + return -EAGAIN; + } + } else { + fsl_comp = list_first_entry(&temp_queue->comp_used, + struct fsl_qdma_comp, list); + if (fsl_comp->bus_addr + 16 != + __this_cpu_read(pre.addr)) { + if (!duplicate) { + spin_unlock(&temp_queue->queue_lock); + return -EAGAIN; + } + } + } + + if (duplicate) { + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); + reg |= FSL_QDMA_BSQMR_DI; + qdma_desc_addr_set64(status_addr, 0x0); + fsl_status->virt_head++; + if (fsl_status->virt_head == fsl_status->cq + + fsl_status->n_cq) + fsl_status->virt_head = fsl_status->cq; + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); + spin_unlock(&temp_queue->queue_lock); + continue; + } + list_del(&fsl_comp->list); + + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); + reg |= FSL_QDMA_BSQMR_DI; + qdma_desc_addr_set64(status_addr, 0x0); + fsl_status->virt_head++; + if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq) + fsl_status->virt_head = fsl_status->cq; + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); + spin_unlock(&temp_queue->queue_lock); + + spin_lock(&fsl_comp->qchan->vchan.lock); + vchan_cookie_complete(&fsl_comp->vdesc); + fsl_comp->qchan->status = DMA_COMPLETE; + spin_unlock(&fsl_comp->qchan->vchan.lock); + } + + return 0; +} + +static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id) +{ + unsigned int intr; + struct fsl_qdma_engine *fsl_qdma = dev_id; + void __iomem *status = fsl_qdma->status_base; + + intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR); + + if (intr) { + dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n"); + return IRQ_NONE; + } + + qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); + return IRQ_HANDLED; +} + +static irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id) +{ + int id; + unsigned int intr, reg; + struct fsl_qdma_engine *fsl_qdma = dev_id; + void __iomem *block, *ctrl = fsl_qdma->ctrl_base; + + id = irq - fsl_qdma->irq_base; + if (id < 0 && id > fsl_qdma->block_number) { + dev_err(fsl_qdma->dma_dev.dev, + "irq %d is wrong irq_base is %d\n", + irq, fsl_qdma->irq_base); + } + + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, id); + + intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0)); + + if ((intr & FSL_QDMA_CQIDR_SQT) != 0) + intr = fsl_qdma_queue_transfer_complete(fsl_qdma, block, id); + + if (intr != 0) { + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); + reg |= FSL_QDMA_DMR_DQD; + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); + qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0)); + dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n"); + } + + /* Clear all detected events and interrupts. */ + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, + block + FSL_QDMA_BCQIDR(0)); + + return IRQ_HANDLED; +} + +static int +fsl_qdma_irq_init(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int i; + int cpu; + int ret; + char irq_name[20]; + + fsl_qdma->error_irq = + platform_get_irq_byname(pdev, "qdma-error"); + if (fsl_qdma->error_irq < 0) { + dev_err(&pdev->dev, "Can't get qdma controller irq.\n"); + return fsl_qdma->error_irq; + } + + ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq, + fsl_qdma_error_handler, 0, + "qDMA error", fsl_qdma); + if (ret) { + dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n"); + return ret; + } + + for (i = 0; i < fsl_qdma->block_number; i++) { + sprintf(irq_name, "qdma-queue%d", i); + fsl_qdma->queue_irq[i] = + platform_get_irq_byname(pdev, irq_name); + + if (fsl_qdma->queue_irq[i] < 0) { + dev_err(&pdev->dev, + "Can't get qdma queue %d irq.\n", i); + return fsl_qdma->queue_irq[i]; + } + + ret = devm_request_irq(&pdev->dev, + fsl_qdma->queue_irq[i], + fsl_qdma_queue_handler, + 0, + "qDMA queue", + fsl_qdma); + if (ret) { + dev_err(&pdev->dev, + "Can't register qDMA queue IRQ.\n"); + return ret; + } + + cpu = i % num_online_cpus(); + ret = irq_set_affinity_hint(fsl_qdma->queue_irq[i], + get_cpu_mask(cpu)); + if (ret) { + dev_err(&pdev->dev, + "Can't set cpu %d affinity to IRQ %d.\n", + cpu, + fsl_qdma->queue_irq[i]); + return ret; + } + } + + return 0; +} + +static void fsl_qdma_irq_exit(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int i; + + devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma); + for (i = 0; i < fsl_qdma->block_number; i++) + devm_free_irq(&pdev->dev, fsl_qdma->queue_irq[i], fsl_qdma); +} + +static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma) +{ + u32 reg; + int i, j, ret; + struct fsl_qdma_queue *temp; + void __iomem *status = fsl_qdma->status_base; + void __iomem *block, *ctrl = fsl_qdma->ctrl_base; + struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue; + + /* Try to halt the qDMA engine first. */ + ret = fsl_qdma_halt(fsl_qdma); + if (ret) { + dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!"); + return ret; + } + + for (i = 0; i < fsl_qdma->block_number; i++) { + /* + * Clear the command queue interrupt detect register for + * all queues. + */ + + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, i); + qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, + block + FSL_QDMA_BCQIDR(0)); + } + + for (j = 0; j < fsl_qdma->block_number; j++) { + block = fsl_qdma->block_base + + FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); + for (i = 0; i < fsl_qdma->n_queues; i++) { + temp = fsl_queue + i + (j * fsl_qdma->n_queues); + /* + * Initialize Command Queue registers to + * point to the first + * command descriptor in memory. + * Dequeue Pointer Address Registers + * Enqueue Pointer Address Registers + */ + + qdma_writel(fsl_qdma, temp->bus_addr, + block + FSL_QDMA_BCQDPA_SADDR(i)); + qdma_writel(fsl_qdma, temp->bus_addr, + block + FSL_QDMA_BCQEPA_SADDR(i)); + + /* Initialize the queue mode. */ + reg = FSL_QDMA_BCQMR_EN; + reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4); + reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6); + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i)); + } + + /* + * Workaround for erratum: ERR010812. + * We must enable XOFF to avoid the enqueue rejection occurs. + * Setting SQCCMR ENTER_WM to 0x20. + */ + + qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM, + block + FSL_QDMA_SQCCMR); + + /* + * Initialize status queue registers to point to the first + * command descriptor in memory. + * Dequeue Pointer Address Registers + * Enqueue Pointer Address Registers + */ + + qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr, + block + FSL_QDMA_SQEPAR); + qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr, + block + FSL_QDMA_SQDPAR); + /* Initialize status queue interrupt. */ + qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE, + block + FSL_QDMA_BCQIER(0)); + qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN | + FSL_QDMA_BSQICR_ICST(5) | 0x8000, + block + FSL_QDMA_BSQICR); + qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE | + FSL_QDMA_CQIER_TEIE, + block + FSL_QDMA_CQIER); + + /* Initialize the status queue mode. */ + reg = FSL_QDMA_BSQMR_EN; + reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2 + (fsl_qdma->status[j]->n_cq) - 6); + + qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); + } + + /* Initialize controller interrupt register. */ + qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); + qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status + FSL_QDMA_DEIER); + + reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); + reg &= ~FSL_QDMA_DMR_DQD; + qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); + + return 0; +} + +static struct dma_async_tx_descriptor * +fsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +{ + struct fsl_qdma_comp *fsl_comp; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan); + + if (!fsl_comp) + return NULL; + + fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len); + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags); +} + +static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan) +{ + u32 reg; + struct virt_dma_desc *vdesc; + struct fsl_qdma_comp *fsl_comp; + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + void __iomem *block = fsl_queue->block_base; + + reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQSR(fsl_queue->id)); + if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF)) + return; + vdesc = vchan_next_desc(&fsl_chan->vchan); + if (!vdesc) + return; + list_del(&vdesc->node); + fsl_comp = to_fsl_qdma_comp(vdesc); + + memcpy(fsl_queue->virt_head++, + fsl_comp->virt_addr, sizeof(struct fsl_qdma_format)); + if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq) + fsl_queue->virt_head = fsl_queue->cq; + + list_add_tail(&fsl_comp->list, &fsl_queue->comp_used); + barrier(); + reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQMR(fsl_queue->id)); + reg |= FSL_QDMA_BCQMR_EI; + qdma_writel(fsl_chan->qdma, reg, block + FSL_QDMA_BCQMR(fsl_queue->id)); + fsl_chan->status = DMA_IN_PROGRESS; +} + +static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc) +{ + unsigned long flags; + struct fsl_qdma_comp *fsl_comp; + struct fsl_qdma_queue *fsl_queue; + + fsl_comp = to_fsl_qdma_comp(vdesc); + fsl_queue = fsl_comp->qchan->queue; + + spin_lock_irqsave(&fsl_queue->queue_lock, flags); + list_add_tail(&fsl_comp->list, &fsl_queue->comp_free); + spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); +} + +static void fsl_qdma_issue_pending(struct dma_chan *chan) +{ + unsigned long flags; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + + spin_lock_irqsave(&fsl_queue->queue_lock, flags); + spin_lock(&fsl_chan->vchan.lock); + if (vchan_issue_pending(&fsl_chan->vchan)) + fsl_qdma_enqueue_desc(fsl_chan); + spin_unlock(&fsl_chan->vchan.lock); + spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); +} + +static void fsl_qdma_synchronize(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + vchan_synchronize(&fsl_chan->vchan); +} + +static int fsl_qdma_terminate_all(struct dma_chan *chan) +{ + LIST_HEAD(head); + unsigned long flags; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + return 0; +} + +static int fsl_qdma_alloc_chan_resources(struct dma_chan *chan) +{ + int ret; + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma; + struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; + + if (fsl_queue->comp_pool && fsl_queue->desc_pool) + return fsl_qdma->desc_allocated; + + INIT_LIST_HEAD(&fsl_queue->comp_free); + + /* + * The dma pool for queue command buffer + */ + fsl_queue->comp_pool = + dma_pool_create("comp_pool", + chan->device->dev, + FSL_QDMA_COMMAND_BUFFER_SIZE, + 64, 0); + if (!fsl_queue->comp_pool) + return -ENOMEM; + + /* + * The dma pool for Descriptor(SD/DD) buffer + */ + fsl_queue->desc_pool = + dma_pool_create("desc_pool", + chan->device->dev, + FSL_QDMA_DESCRIPTOR_BUFFER_SIZE, + 32, 0); + if (!fsl_queue->desc_pool) + goto err_desc_pool; + + ret = fsl_qdma_pre_request_enqueue_desc(fsl_queue); + if (ret) { + dev_err(chan->device->dev, + "failed to alloc dma buffer for S/G descriptor\n"); + goto err_mem; + } + + fsl_qdma->desc_allocated++; + return fsl_qdma->desc_allocated; + +err_mem: + dma_pool_destroy(fsl_queue->desc_pool); +err_desc_pool: + dma_pool_destroy(fsl_queue->comp_pool); + return -ENOMEM; +} + +static int fsl_qdma_probe(struct platform_device *pdev) +{ + int ret, i; + int blk_num, blk_off; + u32 len, chans, queues; + struct resource *res; + struct fsl_qdma_chan *fsl_chan; + struct fsl_qdma_engine *fsl_qdma; + struct device_node *np = pdev->dev.of_node; + + ret = of_property_read_u32(np, "dma-channels", &chans); + if (ret) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + ret = of_property_read_u32(np, "block-offset", &blk_off); + if (ret) { + dev_err(&pdev->dev, "Can't get block-offset.\n"); + return ret; + } + + ret = of_property_read_u32(np, "block-number", &blk_num); + if (ret) { + dev_err(&pdev->dev, "Can't get block-number.\n"); + return ret; + } + + blk_num = min_t(int, blk_num, num_online_cpus()); + + len = sizeof(*fsl_qdma); + fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma) + return -ENOMEM; + + len = sizeof(*fsl_chan) * chans; + fsl_qdma->chans = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma->chans) + return -ENOMEM; + + len = sizeof(struct fsl_qdma_queue *) * blk_num; + fsl_qdma->status = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma->status) + return -ENOMEM; + + len = sizeof(int) * blk_num; + fsl_qdma->queue_irq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma->queue_irq) + return -ENOMEM; + + ret = of_property_read_u32(np, "fsl,dma-queues", &queues); + if (ret) { + dev_err(&pdev->dev, "Can't get queues.\n"); + return ret; + } + + fsl_qdma->desc_allocated = 0; + fsl_qdma->n_chans = chans; + fsl_qdma->n_queues = queues; + fsl_qdma->block_number = blk_num; + fsl_qdma->block_offset = blk_off; + + mutex_init(&fsl_qdma->fsl_qdma_mutex); + + for (i = 0; i < fsl_qdma->block_number; i++) { + fsl_qdma->status[i] = fsl_qdma_prep_status_queue(pdev); + if (!fsl_qdma->status[i]) + return -ENOMEM; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->ctrl_base)) + return PTR_ERR(fsl_qdma->ctrl_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->status_base)) + return PTR_ERR(fsl_qdma->status_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->block_base)) + return PTR_ERR(fsl_qdma->block_base); + fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, fsl_qdma); + if (!fsl_qdma->queue) + return -ENOMEM; + + ret = fsl_qdma_irq_init(pdev, fsl_qdma); + if (ret) + return ret; + + fsl_qdma->irq_base = platform_get_irq_byname(pdev, "qdma-queue0"); + fsl_qdma->feature = of_property_read_bool(np, "big-endian"); + INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels); + + for (i = 0; i < fsl_qdma->n_chans; i++) { + struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i]; + + fsl_chan->qdma = fsl_qdma; + fsl_chan->queue = fsl_qdma->queue + i % (fsl_qdma->n_queues * + fsl_qdma->block_number); + fsl_chan->vchan.desc_free = fsl_qdma_free_desc; + vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev); + } + + dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask); + + fsl_qdma->dma_dev.dev = &pdev->dev; + fsl_qdma->dma_dev.device_free_chan_resources = + fsl_qdma_free_chan_resources; + fsl_qdma->dma_dev.device_alloc_chan_resources = + fsl_qdma_alloc_chan_resources; + fsl_qdma->dma_dev.device_tx_status = dma_cookie_status; + fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy; + fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending; + fsl_qdma->dma_dev.device_synchronize = fsl_qdma_synchronize; + fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all; + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + + platform_set_drvdata(pdev, fsl_qdma); + + ret = dma_async_device_register(&fsl_qdma->dma_dev); + if (ret) { + dev_err(&pdev->dev, + "Can't register NXP Layerscape qDMA engine.\n"); + return ret; + } + + ret = fsl_qdma_reg_init(fsl_qdma); + if (ret) { + dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n"); + return ret; + } + + return 0; +} + +static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev) +{ + struct fsl_qdma_chan *chan, *_chan; + + list_for_each_entry_safe(chan, _chan, + &dmadev->channels, vchan.chan.device_node) { + list_del(&chan->vchan.chan.device_node); + tasklet_kill(&chan->vchan.task); + } +} + +static int fsl_qdma_remove(struct platform_device *pdev) +{ + int i; + struct fsl_qdma_queue *status; + struct device_node *np = pdev->dev.of_node; + struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev); + + fsl_qdma_irq_exit(pdev, fsl_qdma); + fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev); + of_dma_controller_free(np); + dma_async_device_unregister(&fsl_qdma->dma_dev); + + for (i = 0; i < fsl_qdma->block_number; i++) { + status = fsl_qdma->status[i]; + dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) * + status->n_cq, status->cq, status->bus_addr); + } + return 0; +} + +static const struct of_device_id fsl_qdma_dt_ids[] = { + { .compatible = "fsl,ls1021a-qdma", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids); + +static struct platform_driver fsl_qdma_driver = { + .driver = { + .name = "fsl-qdma", + .of_match_table = fsl_qdma_dt_ids, + }, + .probe = fsl_qdma_probe, + .remove = fsl_qdma_remove, +}; + +module_platform_driver(fsl_qdma_driver); + +MODULE_ALIAS("platform:fsl-qdma"); +MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver"); From 9e2b9d3490a684415fc7cc062dfdb2783045b544 Mon Sep 17 00:00:00 2001 From: Peng Ma Date: Tue, 30 Oct 2018 10:36:04 +0800 Subject: [PATCH 0029/4277] dt-bindings: fsl-qdma: Add NXP Layerscpae qDMA controller bindings Document the devicetree bindings for NXP Layerscape qDMA controller which could be found on NXP QorIQ Layerscape SoCs. Signed-off-by: Wen He Signed-off-by: Peng Ma Reviewed-by: Rob Herring Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/fsl-qdma.txt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/fsl-qdma.txt diff --git a/Documentation/devicetree/bindings/dma/fsl-qdma.txt b/Documentation/devicetree/bindings/dma/fsl-qdma.txt new file mode 100644 index 000000000000..6a0ff9059e72 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/fsl-qdma.txt @@ -0,0 +1,57 @@ +NXP Layerscape SoC qDMA Controller +================================== + +This device follows the generic DMA bindings defined in dma/dma.txt. + +Required properties: + +- compatible: Must be one of + "fsl,ls1021a-qdma": for LS1021A Board + "fsl,ls1043a-qdma": for ls1043A Board + "fsl,ls1046a-qdma": for ls1046A Board +- reg: Should contain the register's base address and length. +- interrupts: Should contain a reference to the interrupt used by this + device. +- interrupt-names: Should contain interrupt names: + "qdma-queue0": the block0 interrupt + "qdma-queue1": the block1 interrupt + "qdma-queue2": the block2 interrupt + "qdma-queue3": the block3 interrupt + "qdma-error": the error interrupt +- fsl,dma-queues: Should contain number of queues supported. +- dma-channels: Number of DMA channels supported +- block-number: the virtual block number +- block-offset: the offset of different virtual block +- status-sizes: status queue size of per virtual block +- queue-sizes: command queue size of per virtual block, the size number + based on queues + +Optional properties: + +- dma-channels: Number of DMA channels supported by the controller. +- big-endian: If present registers and hardware scatter/gather descriptors + of the qDMA are implemented in big endian mode, otherwise in little + mode. + +Examples: + + qdma: dma-controller@8390000 { + compatible = "fsl,ls1021a-qdma"; + reg = <0x0 0x8388000 0x0 0x1000>, /* Controller regs */ + <0x0 0x8389000 0x0 0x1000>, /* Status regs */ + <0x0 0x838a000 0x0 0x2000>; /* Block regs */ + interrupts = , + , + ; + interrupt-names = "qdma-error", + "qdma-queue0", "qdma-queue1"; + dma-channels = <8>; + block-number = <2>; + block-offset = <0x1000>; + fsl,dma-queues = <2>; + status-sizes = <64>; + queue-sizes = <64 64>; + big-endian; + }; + +DMA clients must use the format described in dma/dma.txt file. From 279cc97c01a20477fe33b6705abff8a186761a8a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 21:55:02 +0100 Subject: [PATCH 0030/4277] dmaengine: fsl-qdma: add MODULE_LICENSE The newly added driver lacks a MODULE_LICENSE tag, which now produces a warning: WARNING: modpost: missing MODULE_LICENSE() in drivers/dma/fsl-qdma.o Add the license according to the SPDX specifier. Fixes: 75628c149b0d ("dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs") Signed-off-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/fsl-qdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index c18e3492090b..aa1d0ae3d207 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -1255,4 +1255,5 @@ static struct platform_driver fsl_qdma_driver = { module_platform_driver(fsl_qdma_driver); MODULE_ALIAS("platform:fsl-qdma"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver"); From 7c97381e7a9a5ec359007c0d491a143e3d9f787c Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Mon, 24 Dec 2018 11:41:54 -0600 Subject: [PATCH 0031/4277] dmaengine: mv_xor: Fix a missing check in mv_xor_channel_add dma_async_device_register() may fail and return an error. The capabilities checked in mv_xor_channel_add() are not complete. The fix handles the error by freeing the resources. Signed-off-by: Aditya Pakki Signed-off-by: Vinod Koul --- drivers/dma/mv_xor.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 7f595355fb79..e733c5eeaabc 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1153,7 +1153,10 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); - dma_async_device_register(dma_dev); + ret = dma_async_device_register(dma_dev); + if (ret) + goto err_free_irq; + return mv_chan; err_free_irq: From a474b3f0428d6b02a538aa10b3c3b722751cb382 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 28 Dec 2018 14:11:19 -0600 Subject: [PATCH 0032/4277] dmaengine: qcom_hidma: Check for driver register failure While initializing the driver, the function platform_driver_register can fail and return an error. Consistent with other invocations, this patch returns the error upstream. Signed-off-by: Aditya Pakki Acked-by: Sinan Kaya Signed-off-by: Vinod Koul --- drivers/dma/qcom/hidma_mgmt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c index d64edeb6771a..681de12f4c67 100644 --- a/drivers/dma/qcom/hidma_mgmt.c +++ b/drivers/dma/qcom/hidma_mgmt.c @@ -423,9 +423,8 @@ static int __init hidma_mgmt_init(void) hidma_mgmt_of_populate_channels(child); } #endif - platform_driver_register(&hidma_mgmt_driver); + return platform_driver_register(&hidma_mgmt_driver); - return 0; } module_init(hidma_mgmt_init); MODULE_LICENSE("GPL v2"); From 906b40b246b0acb54c4dc97e815cf734761c9820 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 28 Dec 2018 13:26:41 -0600 Subject: [PATCH 0033/4277] dmaengine: stm32-mdma: Add a check on read_u32_array In stm32_mdma_probe, after reading the property "st,ahb-addr-masks", the second call is not checked for failure. This time of check to time of use case of "count" error is sent upstream. Signed-off-by: Aditya Pakki Acked-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-mdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index 390e4cae0e1a..485dea177704 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -1579,9 +1579,11 @@ static int stm32_mdma_probe(struct platform_device *pdev) dmadev->nr_channels = nr_channels; dmadev->nr_requests = nr_requests; - device_property_read_u32_array(&pdev->dev, "st,ahb-addr-masks", + ret = device_property_read_u32_array(&pdev->dev, "st,ahb-addr-masks", dmadev->ahb_addr_masks, count); + if (ret) + return ret; dmadev->nr_ahb_addr_masks = count; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); From ca4c72c01e349ff6313ced02e8e66f8e691530a9 Mon Sep 17 00:00:00 2001 From: Pierre-Yves MORDRET Date: Thu, 3 Jan 2019 11:17:29 +0100 Subject: [PATCH 0034/4277] dmaengine: stm32-dma: check FIFO error interrupt enable For avoiding false FIFO detection, check FIFO Error interrupt is enabled prior raising any errors. This will prevent having spurious FIFO error where it shouldn't. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 4903a408fc14..48f7c0fc1af9 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -641,12 +641,13 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) { struct stm32_dma_chan *chan = devid; struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); - u32 status, scr; + u32 status, scr, sfcr; spin_lock(&chan->vchan.lock); status = stm32_dma_irq_status(chan); scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id)); + sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id)); if (status & STM32_DMA_TCI) { stm32_dma_irq_clear(chan, STM32_DMA_TCI); @@ -661,10 +662,12 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) if (status & STM32_DMA_FEI) { stm32_dma_irq_clear(chan, STM32_DMA_FEI); status &= ~STM32_DMA_FEI; - if (!(scr & STM32_DMA_SCR_EN)) - dev_err(chan2dev(chan), "FIFO Error\n"); - else - dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); + if (sfcr & STM32_DMA_SFCR_FEIE) { + if (!(scr & STM32_DMA_SCR_EN)) + dev_err(chan2dev(chan), "FIFO Error\n"); + else + dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); + } } if (status) { stm32_dma_irq_clear(chan, status); From 48bc73ba14bcd4080d971ed7049e81f2a4f4302e Mon Sep 17 00:00:00 2001 From: Pierre-Yves MORDRET Date: Thu, 3 Jan 2019 11:17:08 +0100 Subject: [PATCH 0035/4277] dmaengine: stm32-dma: Add PM Runtime support Use pm_runtime engine for clock management purpose. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 58 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 48f7c0fc1af9..ba239b529fa9 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1115,15 +1116,14 @@ static int stm32_dma_alloc_chan_resources(struct dma_chan *c) int ret; chan->config_init = false; - ret = clk_prepare_enable(dmadev->clk); - if (ret < 0) { - dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret); + + ret = pm_runtime_get_sync(dmadev->ddev.dev); + if (ret < 0) return ret; - } ret = stm32_dma_disable_chan(chan); if (ret < 0) - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); return ret; } @@ -1143,7 +1143,7 @@ static void stm32_dma_free_chan_resources(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); vchan_free_chan_resources(to_virt_chan(c)); } @@ -1243,6 +1243,12 @@ static int stm32_dma_probe(struct platform_device *pdev) return PTR_ERR(dmadev->clk); } + ret = clk_prepare_enable(dmadev->clk); + if (ret < 0) { + dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); + return ret; + } + dmadev->mem2mem = of_property_read_bool(pdev->dev.of_node, "st,mem2mem"); @@ -1292,7 +1298,7 @@ static int stm32_dma_probe(struct platform_device *pdev) ret = dma_async_device_register(dd); if (ret) - return ret; + goto clk_free; for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) { chan = &dmadev->chan[i]; @@ -1324,20 +1330,58 @@ static int stm32_dma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dmadev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_put(&pdev->dev); + dev_info(&pdev->dev, "STM32 DMA driver registered\n"); return 0; err_unregister: dma_async_device_unregister(dd); +clk_free: + clk_disable_unprepare(dmadev->clk); return ret; } +#ifdef CONFIG_PM +static int stm32_dma_runtime_suspend(struct device *dev) +{ + struct stm32_dma_device *dmadev = dev_get_drvdata(dev); + + clk_disable_unprepare(dmadev->clk); + + return 0; +} + +static int stm32_dma_runtime_resume(struct device *dev) +{ + struct stm32_dma_device *dmadev = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dmadev->clk); + if (ret) { + dev_err(dev, "failed to prepare_enable clock\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops stm32_dma_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_dma_runtime_suspend, + stm32_dma_runtime_resume, NULL) +}; + static struct platform_driver stm32_dma_driver = { .driver = { .name = "stm32-dma", .of_match_table = stm32_dma_of_match, + .pm = &stm32_dma_pm_ops, }, }; From 4f3ceca254e0fa75ccb9c716480f9b30e1f6c0e9 Mon Sep 17 00:00:00 2001 From: Pierre-Yves MORDRET Date: Thu, 3 Jan 2019 11:17:09 +0100 Subject: [PATCH 0036/4277] dmaengine: stm32-dmamux: Add PM Runtime support Use pm_runtime engine for clock management purpose. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dmamux.c | 58 ++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/drivers/dma/stm32-dmamux.c b/drivers/dma/stm32-dmamux.c index b922db90939a..a67119199c45 100644 --- a/drivers/dma/stm32-dmamux.c +++ b/drivers/dma/stm32-dmamux.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -79,8 +80,7 @@ static void stm32_dmamux_free(struct device *dev, void *route_data) stm32_dmamux_write(dmamux->iomem, STM32_DMAMUX_CCR(mux->chan_id), 0); clear_bit(mux->chan_id, dmamux->dma_inuse); - if (!IS_ERR(dmamux->clk)) - clk_disable(dmamux->clk); + pm_runtime_put_sync(dev); spin_unlock_irqrestore(&dmamux->lock, flags); @@ -146,13 +146,10 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec, /* Set dma request */ spin_lock_irqsave(&dmamux->lock, flags); - if (!IS_ERR(dmamux->clk)) { - ret = clk_enable(dmamux->clk); - if (ret < 0) { - spin_unlock_irqrestore(&dmamux->lock, flags); - dev_err(&pdev->dev, "clk_prep_enable issue: %d\n", ret); - goto error; - } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + spin_unlock_irqrestore(&dmamux->lock, flags); + goto error; } spin_unlock_irqrestore(&dmamux->lock, flags); @@ -254,6 +251,7 @@ static int stm32_dmamux_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "DMAMUX defaulting on %u requests\n", stm32_dmamux->dmamux_requests); } + pm_runtime_get_noresume(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = devm_ioremap_resource(&pdev->dev, res); @@ -282,6 +280,8 @@ static int stm32_dmamux_probe(struct platform_device *pdev) stm32_dmamux->dmarouter.route_free = stm32_dmamux_free; platform_set_drvdata(pdev, stm32_dmamux); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); if (!IS_ERR(stm32_dmamux->clk)) { ret = clk_prepare_enable(stm32_dmamux->clk); @@ -291,17 +291,52 @@ static int stm32_dmamux_probe(struct platform_device *pdev) } } + pm_runtime_get_noresume(&pdev->dev); + /* Reset the dmamux */ for (i = 0; i < stm32_dmamux->dma_requests; i++) stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), 0); - if (!IS_ERR(stm32_dmamux->clk)) - clk_disable(stm32_dmamux->clk); + pm_runtime_put(&pdev->dev); return of_dma_router_register(node, stm32_dmamux_route_allocate, &stm32_dmamux->dmarouter); } +#ifdef CONFIG_PM +static int stm32_dmamux_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); + + clk_disable_unprepare(stm32_dmamux->clk); + + return 0; +} + +static int stm32_dmamux_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(stm32_dmamux->clk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare_enable clock\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops stm32_dmamux_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_dmamux_runtime_suspend, + stm32_dmamux_runtime_resume, NULL) +}; + static const struct of_device_id stm32_dmamux_match[] = { { .compatible = "st,stm32h7-dmamux" }, {}, @@ -312,6 +347,7 @@ static struct platform_driver stm32_dmamux_driver = { .driver = { .name = "stm32-dmamux", .of_match_table = stm32_dmamux_match, + .pm = &stm32_dmamux_pm_ops, }, }; From 89e987e3b4d6b87b2b12c3bdfc90bfef16e68b4e Mon Sep 17 00:00:00 2001 From: Pierre-Yves MORDRET Date: Thu, 3 Jan 2019 11:17:10 +0100 Subject: [PATCH 0037/4277] dmaengine: stm32-mdma: Add PM Runtime support Use pm_runtime engine for clock management purpose Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-mdma.c | 52 +++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index 485dea177704..4e0eede599a8 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -1456,15 +1457,13 @@ static int stm32_mdma_alloc_chan_resources(struct dma_chan *c) return -ENOMEM; } - ret = clk_prepare_enable(dmadev->clk); - if (ret < 0) { - dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret); + ret = pm_runtime_get_sync(dmadev->ddev.dev); + if (ret < 0) return ret; - } ret = stm32_mdma_disable_chan(chan); if (ret < 0) - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); return ret; } @@ -1484,7 +1483,7 @@ static void stm32_mdma_free_chan_resources(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } - clk_disable_unprepare(dmadev->clk); + pm_runtime_put(dmadev->ddev.dev); vchan_free_chan_resources(to_virt_chan(c)); dmam_pool_destroy(chan->desc_pool); chan->desc_pool = NULL; @@ -1599,6 +1598,12 @@ static int stm32_mdma_probe(struct platform_device *pdev) return ret; } + ret = clk_prepare_enable(dmadev->clk); + if (ret < 0) { + dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); + return ret; + } + dmadev->rst = devm_reset_control_get(&pdev->dev, NULL); if (!IS_ERR(dmadev->rst)) { reset_control_assert(dmadev->rst); @@ -1670,6 +1675,10 @@ static int stm32_mdma_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, dmadev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_put(&pdev->dev); dev_info(&pdev->dev, "STM32 MDMA driver registered\n"); @@ -1679,11 +1688,42 @@ err_unregister: return ret; } +#ifdef CONFIG_PM +static int stm32_mdma_runtime_suspend(struct device *dev) +{ + struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); + + clk_disable_unprepare(dmadev->clk); + + return 0; +} + +static int stm32_mdma_runtime_resume(struct device *dev) +{ + struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dmadev->clk); + if (ret) { + dev_err(dev, "failed to prepare_enable clock\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops stm32_mdma_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_mdma_runtime_suspend, + stm32_mdma_runtime_resume, NULL) +}; + static struct platform_driver stm32_mdma_driver = { .probe = stm32_mdma_probe, .driver = { .name = "stm32-mdma", .of_match_table = stm32_mdma_of_match, + .pm = &stm32_mdma_pm_ops, }, }; From e486df39305864604b7e25f2a95d51039517ac57 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 21 Nov 2018 16:13:19 +0000 Subject: [PATCH 0038/4277] dmaengine: tegra: avoid overflow of byte tracking The dma_desc->bytes_transferred counter tracks the number of bytes moved by the DMA channel. This is then used to calculate the information passed back in the in the tegra_dma_tx_status callback, which is usually fine. When the DMA channel is configured as continous, then the bytes_transferred counter will increase over time and eventually overflow to become negative so the residue count will become invalid and the ALSA sound-dma code will report invalid hardware pointer values to the application. This results in some users becoming confused about the playout position and putting audio data in the wrong place. To fix this issue, always ensure the bytes_transferred field is modulo the size of the request. We only do this for the case of the cyclic transfer done ISR as anyone attempting to move 2GiB of DMA data in one transfer is unlikely. Note, we don't fix the issue that we should /never/ transfer a negative number of bytes so we could make those fields unsigned. Reviewed-by: Dmitry Osipenko Signed-off-by: Ben Dooks Acked-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 9a558e30c461..8219ab88a507 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -636,7 +636,10 @@ static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc, sgreq = list_first_entry(&tdc->pending_sg_req, typeof(*sgreq), node); dma_desc = sgreq->dma_desc; - dma_desc->bytes_transferred += sgreq->req_len; + /* if we dma for long enough the transfer count will wrap */ + dma_desc->bytes_transferred = + (dma_desc->bytes_transferred + sgreq->req_len) % + dma_desc->bytes_requested; /* Callback need to be call */ if (!dma_desc->cb_count) From 216a1d7da0660dd7d5f79c48e117de8d8d2da468 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 21 Nov 2018 16:13:20 +0000 Subject: [PATCH 0039/4277] dmaengine: tegra: make byte counters unsigned int The buffer byte request length and counter are declared as signed integers but the values should never be below zero, so make these unsigned integers instead. Reviewed-by: Dmitry Osipenko Signed-off-by: Ben Dooks Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 8219ab88a507..adfd918baedc 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -155,7 +155,7 @@ struct tegra_dma_channel_regs { */ struct tegra_dma_sg_req { struct tegra_dma_channel_regs ch_regs; - int req_len; + unsigned int req_len; bool configured; bool last_sg; struct list_head node; @@ -169,8 +169,8 @@ struct tegra_dma_sg_req { */ struct tegra_dma_desc { struct dma_async_tx_descriptor txd; - int bytes_requested; - int bytes_transferred; + unsigned int bytes_requested; + unsigned int bytes_transferred; enum dma_status dma_status; struct list_head node; struct list_head tx_list; From 547b311cf7ecf778adc1979b06eda854429946b6 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 21 Nov 2018 16:13:21 +0000 Subject: [PATCH 0040/4277] dmaengine: tegra: fix incorrect case of DMA The use of Dma is annoying, since it is an acronym so should be all upper case. Fix this throughout the driver. Reviewed-by: Dmitry Osipenko Signed-off-by: Ben Dooks Acked-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index adfd918baedc..ec8938a2ecab 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -146,7 +146,7 @@ struct tegra_dma_channel_regs { }; /* - * tegra_dma_sg_req: Dma request details to configure hardware. This + * tegra_dma_sg_req: DMA request details to configure hardware. This * contains the details for one transfer to configure DMA hw. * The client's request for data transfer can be broken into multiple * sub-transfer as per requester details and hw support. @@ -574,7 +574,7 @@ static bool handle_continuous_head_request(struct tegra_dma_channel *tdc, struct tegra_dma_sg_req *hsgreq = NULL; if (list_empty(&tdc->pending_sg_req)) { - dev_err(tdc2dev(tdc), "Dma is running without req\n"); + dev_err(tdc2dev(tdc), "DMA is running without req\n"); tegra_dma_stop(tdc); return false; } @@ -587,7 +587,7 @@ static bool handle_continuous_head_request(struct tegra_dma_channel *tdc, hsgreq = list_first_entry(&tdc->pending_sg_req, typeof(*hsgreq), node); if (!hsgreq->configured) { tegra_dma_stop(tdc); - dev_err(tdc2dev(tdc), "Error in dma transfer, aborting dma\n"); + dev_err(tdc2dev(tdc), "Error in DMA transfer, aborting DMA\n"); tegra_dma_abort_all(tdc); return false; } @@ -922,7 +922,7 @@ static int get_transfer_param(struct tegra_dma_channel *tdc, return 0; default: - dev_err(tdc2dev(tdc), "Dma direction is not supported\n"); + dev_err(tdc2dev(tdc), "DMA direction is not supported\n"); return -EINVAL; } return -EINVAL; @@ -955,7 +955,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( enum dma_slave_buswidth slave_bw; if (!tdc->config_init) { - dev_err(tdc2dev(tdc), "dma channel is not configured\n"); + dev_err(tdc2dev(tdc), "DMA channel is not configured\n"); return NULL; } if (sg_len < 1) { @@ -988,7 +988,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( dma_desc = tegra_dma_desc_get(tdc); if (!dma_desc) { - dev_err(tdc2dev(tdc), "Dma descriptors not available\n"); + dev_err(tdc2dev(tdc), "DMA descriptors not available\n"); return NULL; } INIT_LIST_HEAD(&dma_desc->tx_list); @@ -1008,14 +1008,14 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( if ((len & 3) || (mem & 3) || (len > tdc->tdma->chip_data->max_dma_count)) { dev_err(tdc2dev(tdc), - "Dma length/memory address is not supported\n"); + "DMA length/memory address is not supported\n"); tegra_dma_desc_put(tdc, dma_desc); return NULL; } sg_req = tegra_dma_sg_req_get(tdc); if (!sg_req) { - dev_err(tdc2dev(tdc), "Dma sg-req not available\n"); + dev_err(tdc2dev(tdc), "DMA sg-req not available\n"); tegra_dma_desc_put(tdc, dma_desc); return NULL; } @@ -1090,7 +1090,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( * terminating the DMA. */ if (tdc->busy) { - dev_err(tdc2dev(tdc), "Request not allowed when dma running\n"); + dev_err(tdc2dev(tdc), "Request not allowed when DMA running\n"); return NULL; } @@ -1147,7 +1147,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( while (remain_len) { sg_req = tegra_dma_sg_req_get(tdc); if (!sg_req) { - dev_err(tdc2dev(tdc), "Dma sg-req not available\n"); + dev_err(tdc2dev(tdc), "DMA sg-req not available\n"); tegra_dma_desc_put(tdc, dma_desc); return NULL; } From 65c383c78000f73a96869505554f57c364308204 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 21 Nov 2018 16:13:22 +0000 Subject: [PATCH 0041/4277] dmaengine: tegra: reduce channel name field size The name field is used for "apbdma.%d" which is rarely going to be more than 10 bytes, so reduce the size from 30 to 12. This is only being used by the interrupt registration, so is not critical to the operation of the driver either. Signed-off-by: Ben Dooks Acked-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index ec8938a2ecab..c60c3f7cad5f 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -186,7 +186,7 @@ typedef void (*dma_isr_handler)(struct tegra_dma_channel *tdc, /* tegra_dma_channel: Channel specific information */ struct tegra_dma_channel { struct dma_chan dma_chan; - char name[30]; + char name[12]; bool config_init; int id; int irq; From 95f295f9fe08166307d94b641fba0b0e924d5d31 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 21 Nov 2018 16:13:23 +0000 Subject: [PATCH 0042/4277] dmaengine: tegra: add tracepoints to driver Add some trace-points to the driver to allow for debuging via the trace pipe. Signed-off-by: Ben Dooks Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 7 ++++ include/trace/events/tegra_apb_dma.h | 61 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 include/trace/events/tegra_apb_dma.h diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index c60c3f7cad5f..659b49bf6fdb 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -38,6 +38,9 @@ #include "dmaengine.h" +#define CREATE_TRACE_POINTS +#include + #define TEGRA_APBDMA_GENERAL 0x0 #define TEGRA_APBDMA_GENERAL_ENABLE BIT(31) @@ -672,6 +675,8 @@ static void tegra_dma_tasklet(unsigned long data) dmaengine_desc_get_callback(&dma_desc->txd, &cb); cb_count = dma_desc->cb_count; dma_desc->cb_count = 0; + trace_tegra_dma_complete_cb(&tdc->dma_chan, cb_count, + cb.callback); spin_unlock_irqrestore(&tdc->lock, flags); while (cb_count--) dmaengine_desc_callback_invoke(&cb, NULL); @@ -688,6 +693,7 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id) spin_lock_irqsave(&tdc->lock, flags); + trace_tegra_dma_isr(&tdc->dma_chan, irq); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status); @@ -846,6 +852,7 @@ found: dma_set_residue(txstate, residual); } + trace_tegra_dma_tx_status(&tdc->dma_chan, cookie, txstate); spin_unlock_irqrestore(&tdc->lock, flags); return ret; } diff --git a/include/trace/events/tegra_apb_dma.h b/include/trace/events/tegra_apb_dma.h new file mode 100644 index 000000000000..0818f6286110 --- /dev/null +++ b/include/trace/events/tegra_apb_dma.h @@ -0,0 +1,61 @@ +#if !defined(_TRACE_TEGRA_APB_DMA_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_TEGRA_APM_DMA_H + +#include +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM tegra_apb_dma + +TRACE_EVENT(tegra_dma_tx_status, + TP_PROTO(struct dma_chan *dc, dma_cookie_t cookie, struct dma_tx_state *state), + TP_ARGS(dc, cookie, state), + TP_STRUCT__entry( + __string(chan, dev_name(&dc->dev->device)) + __field(dma_cookie_t, cookie) + __field(__u32, residue) + ), + TP_fast_assign( + __assign_str(chan, dev_name(&dc->dev->device)); + __entry->cookie = cookie; + __entry->residue = state ? state->residue : (u32)-1; + ), + TP_printk("channel %s: dma cookie %d, residue %u", + __get_str(chan), __entry->cookie, __entry->residue) +); + +TRACE_EVENT(tegra_dma_complete_cb, + TP_PROTO(struct dma_chan *dc, int count, void *ptr), + TP_ARGS(dc, count, ptr), + TP_STRUCT__entry( + __string(chan, dev_name(&dc->dev->device)) + __field(int, count) + __field(void *, ptr) + ), + TP_fast_assign( + __assign_str(chan, dev_name(&dc->dev->device)); + __entry->count = count; + __entry->ptr = ptr; + ), + TP_printk("channel %s: done %d, ptr %p", + __get_str(chan), __entry->count, __entry->ptr) +); + +TRACE_EVENT(tegra_dma_isr, + TP_PROTO(struct dma_chan *dc, int irq), + TP_ARGS(dc, irq), + TP_STRUCT__entry( + __string(chan, dev_name(&dc->dev->device)) + __field(int, irq) + ), + TP_fast_assign( + __assign_str(chan, dev_name(&dc->dev->device)); + __entry->irq = irq; + ), + TP_printk("%s: irq %d\n", __get_str(chan), __entry->irq) +); + +#endif /* _TRACE_TEGRADMA_H */ + +/* This part must be outside protection */ +#include From 616f0f81d857e248a72b5af45ab185196556ae2e Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 20 Nov 2018 16:31:45 +0100 Subject: [PATCH 0043/4277] dmaengine: xilinx_dma: commonize DMA copy size calculation This patch removes a bit of duplicated code by introducing a new function that implements calculations for DMA copy size, and prepares for changes to the copy size calculation that will happen in following patches. Suggested-by: Vinod Koul Signed-off-by: Andrea Merello Reviewed-by: Radhey Shyam Pandey Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 39 ++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 02880963092f..fd9f37bafab0 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -425,6 +425,7 @@ struct xilinx_dma_config { * @rxs_clk: DMA s2mm stream clock * @nr_channels: Number of channels DMA device supports * @chan_id: DMA channel identifier + * @max_buffer_len: Max buffer length */ struct xilinx_dma_device { void __iomem *regs; @@ -444,6 +445,7 @@ struct xilinx_dma_device { struct clk *rxs_clk; u32 nr_channels; u32 chan_id; + u32 max_buffer_len; }; /* Macros */ @@ -959,6 +961,25 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan) return 0; } +/** + * xilinx_dma_calc_copysize - Calculate the amount of data to copy + * @chan: Driver specific DMA channel + * @size: Total data that needs to be copied + * @done: Amount of data that has been already copied + * + * Return: Amount of data that has to be copied + */ +static int xilinx_dma_calc_copysize(struct xilinx_dma_chan *chan, + int size, int done) +{ + size_t copy; + + copy = min_t(size_t, size - done, + chan->xdev->max_buffer_len); + + return copy; +} + /** * xilinx_dma_tx_status - Get DMA transaction status * @dchan: DMA channel @@ -992,7 +1013,7 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan, list_for_each_entry(segment, &desc->segments, node) { hw = &segment->hw; residue += (hw->control - hw->status) & - XILINX_DMA_MAX_TRANS_LEN; + chan->xdev->max_buffer_len; } } spin_unlock_irqrestore(&chan->lock, flags); @@ -1254,7 +1275,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan) /* Start the transfer */ dma_ctrl_write(chan, XILINX_DMA_REG_BTT, - hw->control & XILINX_DMA_MAX_TRANS_LEN); + hw->control & chan->xdev->max_buffer_len); } list_splice_tail_init(&chan->pending_list, &chan->active_list); @@ -1357,7 +1378,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) /* Start the transfer */ dma_ctrl_write(chan, XILINX_DMA_REG_BTT, - hw->control & XILINX_DMA_MAX_TRANS_LEN); + hw->control & chan->xdev->max_buffer_len); } list_splice_tail_init(&chan->pending_list, &chan->active_list); @@ -1718,7 +1739,7 @@ xilinx_cdma_prep_memcpy(struct dma_chan *dchan, dma_addr_t dma_dst, struct xilinx_cdma_tx_segment *segment; struct xilinx_cdma_desc_hw *hw; - if (!len || len > XILINX_DMA_MAX_TRANS_LEN) + if (!len || len > chan->xdev->max_buffer_len) return NULL; desc = xilinx_dma_alloc_tx_descriptor(chan); @@ -1808,8 +1829,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg( * Calculate the maximum number of bytes to transfer, * making sure it is less than the hw limit */ - copy = min_t(size_t, sg_dma_len(sg) - sg_used, - XILINX_DMA_MAX_TRANS_LEN); + copy = xilinx_dma_calc_copysize(chan, sg_dma_len(sg), + sg_used); hw = &segment->hw; /* Fill in the descriptor */ @@ -1913,8 +1934,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic( * Calculate the maximum number of bytes to transfer, * making sure it is less than the hw limit */ - copy = min_t(size_t, period_len - sg_used, - XILINX_DMA_MAX_TRANS_LEN); + copy = xilinx_dma_calc_copysize(chan, period_len, + sg_used); hw = &segment->hw; xilinx_axidma_buf(chan, hw, buf_addr, sg_used, period_len * i); @@ -2628,6 +2649,8 @@ static int xilinx_dma_probe(struct platform_device *pdev) /* Retrieve the DMA engine properties from the device tree */ xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg"); + xdev->max_buffer_len = XILINX_DMA_MAX_TRANS_LEN; + if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) xdev->mcdma = of_property_read_bool(node, "xlnx,mcdma"); From 5c094d4cac5ba78139f4d7169145b57af7f07981 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 20 Nov 2018 16:31:46 +0100 Subject: [PATCH 0044/4277] dmaengine: xilinx_dma: in axidma slave_sg and dma_cyclic mode align split descriptors Whenever a single or cyclic transaction is prepared, the driver could eventually split it over several SG descriptors in order to deal with the HW maximum transfer length. This could end up in DMA operations starting from a misaligned address. This seems fatal for the HW if DRE (Data Realignment Engine) is not enabled. This patch eventually adjusts the transfer size in order to make sure all operations start from an aligned address. Cc: Radhey Shyam Pandey Signed-off-by: Andrea Merello Reviewed-by: Radhey Shyam Pandey Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index fd9f37bafab0..93435f7002ab 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -977,6 +977,15 @@ static int xilinx_dma_calc_copysize(struct xilinx_dma_chan *chan, copy = min_t(size_t, size - done, chan->xdev->max_buffer_len); + if ((copy + done < size) && + chan->xdev->common.copy_align) { + /* + * If this is not the last descriptor, make sure + * the next one will be properly aligned + */ + copy = rounddown(copy, + (1 << chan->xdev->common.copy_align)); + } return copy; } From 7df54dbeb055229f6689161aa90bf00bf4af077e Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 20 Nov 2018 16:31:47 +0100 Subject: [PATCH 0045/4277] dt-bindings: dmaengine: xilinx_dma: add optional xlnx,sg-length-width property The width of the "length register" cannot be autodetected, and it is now specified with a DT property. Add documentation for it. Cc: Rob Herring Cc: Mark Rutland Cc: devicetree@vger.kernel.org Cc: Radhey Shyam Pandey Signed-off-by: Andrea Merello Reviewed-by: Radhey Shyam Pandey Reviewed-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt index 174af2c45e77..2fce9fb4b270 100644 --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt @@ -41,6 +41,10 @@ Optional properties: - xlnx,include-sg: Tells configured for Scatter-mode in the hardware. Optional properties for AXI DMA: +- xlnx,sg-length-width: Should be set to the width in bits of the length + register as configured in h/w. Takes values {8...26}. If the property + is missing or invalid then the default value 23 is used. This is the + maximum value that is supported by all IP versions. - xlnx,mcdma: Tells whether configured for multi-channel mode in the hardware. Optional properties for VDMA: - xlnx,flush-fsync: Tells which channel to Flush on Frame sync. From ae809690b46a71dc56cda5b3b8884c8c41a0df15 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Tue, 20 Nov 2018 16:31:48 +0100 Subject: [PATCH 0046/4277] dmaengine: xilinx_dma: program hardware supported buffer length AXI-DMA IP supports configurable (c_sg_length_width) buffer length register width, hence read buffer length (xlnx,sg-length-width) DT property and ensure that driver doesn't program buffer length exceeding the supported limit. For VDMA and CDMA there is no change. Cc: Rob Herring Cc: Mark Rutland Cc: devicetree@vger.kernel.org Signed-off-by: Radhey Shyam Pandey Signed-off-by: Michal Simek Signed-off-by: Andrea Merello [rebase, reword] Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 93435f7002ab..d59af2b33a99 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -161,7 +161,9 @@ #define XILINX_DMA_REG_BTT 0x28 /* AXI DMA Specific Masks/Bit fields */ -#define XILINX_DMA_MAX_TRANS_LEN GENMASK(22, 0) +#define XILINX_DMA_MAX_TRANS_LEN_MIN 8 +#define XILINX_DMA_MAX_TRANS_LEN_MAX 23 +#define XILINX_DMA_V2_MAX_TRANS_LEN_MAX 26 #define XILINX_DMA_CR_COALESCE_MAX GENMASK(23, 16) #define XILINX_DMA_CR_CYCLIC_BD_EN_MASK BIT(4) #define XILINX_DMA_CR_COALESCE_SHIFT 16 @@ -2626,7 +2628,7 @@ static int xilinx_dma_probe(struct platform_device *pdev) struct xilinx_dma_device *xdev; struct device_node *child, *np = pdev->dev.of_node; struct resource *io; - u32 num_frames, addr_width; + u32 num_frames, addr_width, len_width; int i, err; /* Allocate and initialize the DMA engine structure */ @@ -2658,10 +2660,24 @@ static int xilinx_dma_probe(struct platform_device *pdev) /* Retrieve the DMA engine properties from the device tree */ xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg"); - xdev->max_buffer_len = XILINX_DMA_MAX_TRANS_LEN; + xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0); - if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) + if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { xdev->mcdma = of_property_read_bool(node, "xlnx,mcdma"); + if (!of_property_read_u32(node, "xlnx,sg-length-width", + &len_width)) { + if (len_width < XILINX_DMA_MAX_TRANS_LEN_MIN || + len_width > XILINX_DMA_V2_MAX_TRANS_LEN_MAX) { + dev_warn(xdev->dev, + "invalid xlnx,sg-length-width property value. Using default width\n"); + } else { + if (len_width > XILINX_DMA_MAX_TRANS_LEN_MAX) + dev_warn(xdev->dev, "Please ensure that IP supports buffer length > 23 bits\n"); + xdev->max_buffer_len = + GENMASK(len_width - 1, 0); + } + } + } if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { err = of_property_read_u32(node, "xlnx,num-fstores", From 05f7ea7f6ef611a077fcbb983fb8d6a99e91e1f6 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 20 Nov 2018 16:31:49 +0100 Subject: [PATCH 0047/4277] dmaengine: xilinx_dma: autodetect whether the HW supports scatter-gather The AXIDMA and CDMA HW can be either direct-access or scatter-gather version. These are SW incompatible. The driver can handle both versions: a DT property was used to tell the driver whether to assume the HW is in scatter-gather mode. This patch makes the driver to autodetect this information. The DT property is not required anymore. No changes for VDMA. Cc: Rob Herring Cc: Mark Rutland Cc: devicetree@vger.kernel.org Cc: Radhey Shyam Pandey Signed-off-by: Andrea Merello Reviewed-by: Radhey Shyam Pandey Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index d59af2b33a99..b559efe06adb 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -86,6 +86,7 @@ #define XILINX_DMA_DMASR_DMA_DEC_ERR BIT(6) #define XILINX_DMA_DMASR_DMA_SLAVE_ERR BIT(5) #define XILINX_DMA_DMASR_DMA_INT_ERR BIT(4) +#define XILINX_DMA_DMASR_SG_MASK BIT(3) #define XILINX_DMA_DMASR_IDLE BIT(1) #define XILINX_DMA_DMASR_HALTED BIT(0) #define XILINX_DMA_DMASR_DELAY_MASK GENMASK(31, 24) @@ -414,7 +415,6 @@ struct xilinx_dma_config { * @dev: Device Structure * @common: DMA device structure * @chan: Driver specific DMA channel - * @has_sg: Specifies whether Scatter-Gather is present or not * @mcdma: Specifies whether Multi-Channel is present or not * @flush_on_fsync: Flush on frame sync * @ext_addr: Indicates 64 bit addressing is supported by dma device @@ -434,7 +434,6 @@ struct xilinx_dma_device { struct device *dev; struct dma_device common; struct xilinx_dma_chan *chan[XILINX_DMA_MAX_CHANS_PER_DEVICE]; - bool has_sg; bool mcdma; u32 flush_on_fsync; bool ext_addr; @@ -2421,7 +2420,6 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, chan->dev = xdev->dev; chan->xdev = xdev; - chan->has_sg = xdev->has_sg; chan->desc_pendingcount = 0x0; chan->ext_addr = xdev->ext_addr; /* This variable ensures that descriptors are not @@ -2521,6 +2519,15 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, chan->stop_transfer = xilinx_dma_stop_transfer; } + /* check if SG is enabled (only for AXIDMA and CDMA) */ + if (xdev->dma_config->dmatype != XDMA_TYPE_VDMA) { + if (dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) & + XILINX_DMA_DMASR_SG_MASK) + chan->has_sg = true; + dev_dbg(chan->dev, "ch %d: SG %s\n", chan->id, + chan->has_sg ? "enabled" : "disabled"); + } + /* Initialize the tasklet */ tasklet_init(&chan->tasklet, xilinx_dma_do_tasklet, (unsigned long)chan); @@ -2659,7 +2666,6 @@ static int xilinx_dma_probe(struct platform_device *pdev) return PTR_ERR(xdev->regs); /* Retrieve the DMA engine properties from the device tree */ - xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg"); xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0); if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { From 29b9ee4a0c0edf32667e2d95690f6ed810aad6ec Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 20 Nov 2018 16:31:50 +0100 Subject: [PATCH 0048/4277] dt-bindings: dmaengine: xilinx_dma: drop include-sg property This property is not needed anymore, because the driver now autodetects it. Delete references in documentation. Cc: Rob Herring Cc: Mark Rutland Cc: devicetree@vger.kernel.org Cc: Radhey Shyam Pandey Signed-off-by: Andrea Merello Reviewed-by: Radhey Shyam Pandey Reviewed-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt index 2fce9fb4b270..93b6d961dd4f 100644 --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt @@ -37,9 +37,6 @@ Required properties: Required properties for VDMA: - xlnx,num-fstores: Should be the number of framebuffers as configured in h/w. -Optional properties: -- xlnx,include-sg: Tells configured for Scatter-mode in - the hardware. Optional properties for AXI DMA: - xlnx,sg-length-width: Should be set to the width in bits of the length register as configured in h/w. Takes values {8...26}. If the property From b8349172b4001ea4e8b38a243275aecd90aa7573 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Tue, 20 Nov 2018 16:31:51 +0100 Subject: [PATCH 0049/4277] dmaengine: xilinx_dma: Drop SG support for VDMA IP xilinx_vdma_start_transfer() is used only for VDMA IP, still it contains conditional code on has_sg variable. has_sg is set only whenever the HW does support SG mode, that is never true for VDMA IP. This patch drops the never-taken branches. Signed-off-by: Andrea Merello Reviewed-by: Radhey Shyam Pandey Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 84 +++++++++++++-------------------- 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index b559efe06adb..d9431afa031b 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -1102,6 +1102,8 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) struct xilinx_dma_tx_descriptor *desc, *tail_desc; u32 reg, j; struct xilinx_vdma_tx_segment *tail_segment; + struct xilinx_vdma_tx_segment *segment, *last = NULL; + int i = 0; /* This function was invoked with lock held */ if (chan->err) @@ -1121,14 +1123,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) tail_segment = list_last_entry(&tail_desc->segments, struct xilinx_vdma_tx_segment, node); - /* - * If hardware is idle, then all descriptors on the running lists are - * done, start new transfers - */ - if (chan->has_sg) - dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC, - desc->async_tx.phys); - /* Configure the hardware using info in the config structure */ if (chan->has_vflip) { reg = dma_read(chan, XILINX_VDMA_REG_ENABLE_VERTICAL_FLIP); @@ -1145,15 +1139,11 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) else reg &= ~XILINX_DMA_DMACR_FRAMECNT_EN; - /* - * With SG, start with circular mode, so that BDs can be fetched. - * In direct register mode, if not parking, enable circular mode - */ - if (chan->has_sg || !config->park) - reg |= XILINX_DMA_DMACR_CIRC_EN; - + /* If not parking, enable circular mode */ if (config->park) reg &= ~XILINX_DMA_DMACR_CIRC_EN; + else + reg |= XILINX_DMA_DMACR_CIRC_EN; dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); @@ -1175,49 +1165,39 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) return; /* Start the transfer */ - if (chan->has_sg) { - dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC, - tail_segment->phys); - list_splice_tail_init(&chan->pending_list, &chan->active_list); - chan->desc_pendingcount = 0; - } else { - struct xilinx_vdma_tx_segment *segment, *last = NULL; - int i = 0; + if (chan->desc_submitcount < chan->num_frms) + i = chan->desc_submitcount; - if (chan->desc_submitcount < chan->num_frms) - i = chan->desc_submitcount; - - list_for_each_entry(segment, &desc->segments, node) { - if (chan->ext_addr) - vdma_desc_write_64(chan, - XILINX_VDMA_REG_START_ADDRESS_64(i++), - segment->hw.buf_addr, - segment->hw.buf_addr_msb); - else - vdma_desc_write(chan, + list_for_each_entry(segment, &desc->segments, node) { + if (chan->ext_addr) + vdma_desc_write_64(chan, + XILINX_VDMA_REG_START_ADDRESS_64(i++), + segment->hw.buf_addr, + segment->hw.buf_addr_msb); + else + vdma_desc_write(chan, XILINX_VDMA_REG_START_ADDRESS(i++), segment->hw.buf_addr); - last = segment; - } - - if (!last) - return; - - /* HW expects these parameters to be same for one transaction */ - vdma_desc_write(chan, XILINX_DMA_REG_HSIZE, last->hw.hsize); - vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE, - last->hw.stride); - vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize); - - chan->desc_submitcount++; - chan->desc_pendingcount--; - list_del(&desc->node); - list_add_tail(&desc->node, &chan->active_list); - if (chan->desc_submitcount == chan->num_frms) - chan->desc_submitcount = 0; + last = segment; } + if (!last) + return; + + /* HW expects these parameters to be same for one transaction */ + vdma_desc_write(chan, XILINX_DMA_REG_HSIZE, last->hw.hsize); + vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE, + last->hw.stride); + vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize); + + chan->desc_submitcount++; + chan->desc_pendingcount--; + list_del(&desc->node); + list_add_tail(&desc->node, &chan->active_list); + if (chan->desc_submitcount == chan->num_frms) + chan->desc_submitcount = 0; + chan->idle = false; } From 21b9e979501fdb5f6797193d70428a2b00bd5247 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 19 Jul 2017 14:55:26 -0700 Subject: [PATCH 0050/4277] device-dax: Kill dax_region ida Commit bbb3be170ac2 "device-dax: fix sysfs duplicate warnings" arranged for passing a dax instance-id to devm_create_dax_dev(), rather than generating one internally. Remove the dax_region ida and related code. Signed-off-by: Dan Williams --- drivers/dax/dax-private.h | 3 --- drivers/dax/device.c | 24 +++--------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index b6fc4f04636d..d1b36a42132f 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -28,7 +28,6 @@ */ struct dax_region { int id; - struct ida ida; void *base; struct kref kref; struct device *dev; @@ -42,7 +41,6 @@ struct dax_region { * @region - parent region * @dax_dev - core dax functionality * @dev - device core - * @id - child id in the region * @num_resources - number of physical address extents in this device * @res - array of physical address ranges */ @@ -50,7 +48,6 @@ struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; struct device dev; - int id; int num_resources; struct resource res[0]; }; diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 948806e57cee..a5a670c1cd58 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -128,7 +128,6 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, dax_region->pfn_flags = pfn_flags; kref_init(&dax_region->kref); dax_region->id = region_id; - ida_init(&dax_region->ida); dax_region->align = align; dax_region->dev = parent; dax_region->base = addr; @@ -582,8 +581,6 @@ static void dev_dax_release(struct device *dev) struct dax_region *dax_region = dev_dax->region; struct dax_device *dax_dev = dev_dax->dax_dev; - if (dev_dax->id >= 0) - ida_simple_remove(&dax_region->ida, dev_dax->id); dax_region_put(dax_region); put_dax(dax_dev); kfree(dev_dax); @@ -642,19 +639,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, } if (i < count) - goto err_id; - - if (id < 0) { - id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL); - dev_dax->id = id; - if (id < 0) { - rc = id; - goto err_id; - } - } else { - /* region provider owns @id lifetime */ - dev_dax->id = -1; - } + goto err; /* * No 'host' or dax_operations since there is no access to this @@ -663,7 +648,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, dax_dev = alloc_dax(dev_dax, NULL, NULL); if (!dax_dev) { rc = -ENOMEM; - goto err_dax; + goto err; } /* from here on we're committed to teardown via dax_dev_release() */ @@ -700,10 +685,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, return dev_dax; - err_dax: - if (dev_dax->id >= 0) - ida_simple_remove(&dax_region->ida, dev_dax->id); - err_id: + err: kfree(dev_dax); return ERR_PTR(rc); From 93694f9630b0ed29cda61df58e480dcb34ef52fd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 19 Jul 2017 15:57:44 -0700 Subject: [PATCH 0051/4277] device-dax: Kill dax_region base Nothing consumes this attribute of a region and devres otherwise remembers the value for de-allocation purposes. Signed-off-by: Dan Williams --- drivers/dax/dax-private.h | 2 -- drivers/dax/device-dax.h | 5 ++--- drivers/dax/device.c | 3 +-- drivers/dax/pmem.c | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index d1b36a42132f..9b393c218fe4 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -19,7 +19,6 @@ /** * struct dax_region - mapping infrastructure for dax devices * @id: kernel-wide unique region for a memory range - * @base: linear address corresponding to @res * @kref: to pin while other agents have a need to do lookups * @dev: parent device backing this region * @align: allocation and mapping alignment for child dax devices @@ -28,7 +27,6 @@ */ struct dax_region { int id; - void *base; struct kref kref; struct device *dev; unsigned int align; diff --git a/drivers/dax/device-dax.h b/drivers/dax/device-dax.h index 688b051750bd..4f1c69e1b3a2 100644 --- a/drivers/dax/device-dax.h +++ b/drivers/dax/device-dax.h @@ -17,9 +17,8 @@ struct dev_dax; struct resource; struct dax_region; void dax_region_put(struct dax_region *dax_region); -struct dax_region *alloc_dax_region(struct device *parent, - int region_id, struct resource *res, unsigned int align, - void *addr, unsigned long flags); +struct dax_region *alloc_dax_region(struct device *parent, int region_id, + struct resource *res, unsigned int align, unsigned long flags); struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, struct resource *res, int count); #endif /* __DEVICE_DAX_H__ */ diff --git a/drivers/dax/device.c b/drivers/dax/device.c index a5a670c1cd58..811c1015194c 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -100,7 +100,7 @@ static void dax_region_unregister(void *region) } struct dax_region *alloc_dax_region(struct device *parent, int region_id, - struct resource *res, unsigned int align, void *addr, + struct resource *res, unsigned int align, unsigned long pfn_flags) { struct dax_region *dax_region; @@ -130,7 +130,6 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, dax_region->id = region_id; dax_region->align = align; dax_region->dev = parent; - dax_region->base = addr; if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { kfree(dax_region); return NULL; diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index 2c1f459c0c63..72a76105eb02 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -125,7 +125,7 @@ static int dax_pmem_probe(struct device *dev) return -EINVAL; dax_region = alloc_dax_region(dev, region_id, &res, - le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP); + le32_to_cpu(pfn_sb->align), PFN_DEV|PFN_MAP); if (!dax_region) return -ENOMEM; From 753a0850e707e9a8c5861356222f9b9e4eba7945 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 14 Jul 2017 13:54:50 -0700 Subject: [PATCH 0052/4277] device-dax: Remove multi-resource infrastructure The multi-resource implementation anticipated discontiguous sub-division support. That has not yet materialized, delete the infrastructure and related code. Signed-off-by: Dan Williams --- drivers/dax/dax-private.h | 4 --- drivers/dax/device-dax.h | 3 +-- drivers/dax/device.c | 49 ++++++---------------------------- drivers/dax/pmem.c | 3 +-- tools/testing/nvdimm/dax-dev.c | 16 +++-------- 5 files changed, 13 insertions(+), 62 deletions(-) diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index 9b393c218fe4..dbd077653b5c 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -39,14 +39,10 @@ struct dax_region { * @region - parent region * @dax_dev - core dax functionality * @dev - device core - * @num_resources - number of physical address extents in this device - * @res - array of physical address ranges */ struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; struct device dev; - int num_resources; - struct resource res[0]; }; #endif diff --git a/drivers/dax/device-dax.h b/drivers/dax/device-dax.h index 4f1c69e1b3a2..e9be99584b92 100644 --- a/drivers/dax/device-dax.h +++ b/drivers/dax/device-dax.h @@ -19,6 +19,5 @@ struct dax_region; void dax_region_put(struct dax_region *dax_region); struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, unsigned long flags); -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, - int id, struct resource *res, int count); +struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id); #endif /* __DEVICE_DAX_H__ */ diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 811c1015194c..db12e24b8005 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -151,11 +151,7 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dev_dax *dev_dax = to_dev_dax(dev); - unsigned long long size = 0; - int i; - - for (i = 0; i < dev_dax->num_resources; i++) - size += resource_size(&dev_dax->res[i]); + unsigned long long size = resource_size(&dev_dax->region->res); return sprintf(buf, "%llu\n", size); } @@ -224,21 +220,11 @@ static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, unsigned long size) { - struct resource *res; - /* gcc-4.6.3-nolibc for i386 complains that this is uninitialized */ - phys_addr_t uninitialized_var(phys); - int i; + struct resource *res = &dev_dax->region->res; + phys_addr_t phys; - for (i = 0; i < dev_dax->num_resources; i++) { - res = &dev_dax->res[i]; - phys = pgoff * PAGE_SIZE + res->start; - if (phys >= res->start && phys <= res->end) - break; - pgoff -= PHYS_PFN(resource_size(res)); - } - - if (i < dev_dax->num_resources) { - res = &dev_dax->res[i]; + phys = pgoff * PAGE_SIZE + res->start; + if (phys >= res->start && phys <= res->end) { if (phys + size - 1 <= res->end) return phys; } @@ -608,8 +594,7 @@ static void unregister_dev_dax(void *dev) put_device(dev); } -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, - int id, struct resource *res, int count) +struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id) { struct device *parent = dax_region->dev; struct dax_device *dax_dev; @@ -617,29 +602,12 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, struct inode *inode; struct device *dev; struct cdev *cdev; - int rc, i; + int rc; - if (!count) - return ERR_PTR(-EINVAL); - - dev_dax = kzalloc(struct_size(dev_dax, res, count), GFP_KERNEL); + dev_dax = kzalloc(sizeof(*dev_dax), GFP_KERNEL); if (!dev_dax) return ERR_PTR(-ENOMEM); - for (i = 0; i < count; i++) { - if (!IS_ALIGNED(res[i].start, dax_region->align) - || !IS_ALIGNED(resource_size(&res[i]), - dax_region->align)) { - rc = -EINVAL; - break; - } - dev_dax->res[i].start = res[i].start; - dev_dax->res[i].end = res[i].end; - } - - if (i < count) - goto err; - /* * No 'host' or dax_operations since there is no access to this * device outside of mmap of the resulting character device. @@ -659,7 +627,6 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, cdev_init(cdev, &dax_fops); cdev->owner = parent->driver->owner; - dev_dax->num_resources = count; dev_dax->dax_dev = dax_dev; dev_dax->region = dax_region; kref_get(&dax_region->kref); diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index 72a76105eb02..6c61a019f997 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -129,8 +129,7 @@ static int dax_pmem_probe(struct device *dev) if (!dax_region) return -ENOMEM; - /* TODO: support for subdividing a dax region... */ - dev_dax = devm_create_dev_dax(dax_region, id, &res, 1); + dev_dax = devm_create_dev_dax(dax_region, id); /* child dev_dax instances now own the lifetime of the dax_region */ dax_region_put(dax_region); diff --git a/tools/testing/nvdimm/dax-dev.c b/tools/testing/nvdimm/dax-dev.c index 36ee3d8797c3..f36e708265b8 100644 --- a/tools/testing/nvdimm/dax-dev.c +++ b/tools/testing/nvdimm/dax-dev.c @@ -17,20 +17,11 @@ phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, unsigned long size) { - struct resource *res; + struct resource *res = &dev_dax->region->res; phys_addr_t addr; - int i; - for (i = 0; i < dev_dax->num_resources; i++) { - res = &dev_dax->res[i]; - addr = pgoff * PAGE_SIZE + res->start; - if (addr >= res->start && addr <= res->end) - break; - pgoff -= PHYS_PFN(resource_size(res)); - } - - if (i < dev_dax->num_resources) { - res = &dev_dax->res[i]; + addr = pgoff * PAGE_SIZE + res->start; + if (addr >= res->start && addr <= res->end) { if (addr + size - 1 <= res->end) { if (get_nfit_res(addr)) { struct page *page; @@ -44,6 +35,5 @@ phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, return addr; } } - return -1; } From 51cf784c42d07fbd62cb604836a9270cf3361509 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 12 Jul 2017 17:58:21 -0700 Subject: [PATCH 0053/4277] device-dax: Start defining a dax bus model Towards eliminating the dax_class, move the dax-device-attribute enabling to a new bus.c file in the core. The amount of code thrash of sub-sequent patches is reduced as no logic changes are made, just pure code movement. A temporary export of unregister_dex_dax() and dax_attribute_groups is needed to preserve compilation, but those symbols become static again in a follow-on patch. Signed-off-by: Dan Williams --- drivers/dax/Makefile | 1 + drivers/dax/bus.c | 174 +++++++++++++++++++++++++++++++++ drivers/dax/bus.h | 15 +++ drivers/dax/dax-private.h | 14 +++ drivers/dax/dax.h | 18 ---- drivers/dax/device-dax.h | 23 ----- drivers/dax/device.c | 185 +----------------------------------- drivers/dax/pmem.c | 2 +- drivers/dax/super.c | 1 + tools/testing/nvdimm/Kbuild | 1 + 10 files changed, 210 insertions(+), 224 deletions(-) create mode 100644 drivers/dax/bus.c create mode 100644 drivers/dax/bus.h delete mode 100644 drivers/dax/dax.h delete mode 100644 drivers/dax/device-dax.h diff --git a/drivers/dax/Makefile b/drivers/dax/Makefile index 574286fac87c..658e6b9b1d74 100644 --- a/drivers/dax/Makefile +++ b/drivers/dax/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o dax-y := super.o +dax-y += bus.o dax_pmem-y := pmem.o device_dax-y := device.o diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c new file mode 100644 index 000000000000..8a398e8e1956 --- /dev/null +++ b/drivers/dax/bus.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2017-2018 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include "dax-private.h" +#include "bus.h" + +/* + * Rely on the fact that drvdata is set before the attributes are + * registered, and that the attributes are unregistered before drvdata + * is cleared to assume that drvdata is always valid. + */ +static ssize_t id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dax_region *dax_region = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", dax_region->id); +} +static DEVICE_ATTR_RO(id); + +static ssize_t region_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dax_region *dax_region = dev_get_drvdata(dev); + + return sprintf(buf, "%llu\n", (unsigned long long) + resource_size(&dax_region->res)); +} +static struct device_attribute dev_attr_region_size = __ATTR(size, 0444, + region_size_show, NULL); + +static ssize_t align_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dax_region *dax_region = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", dax_region->align); +} +static DEVICE_ATTR_RO(align); + +static struct attribute *dax_region_attributes[] = { + &dev_attr_region_size.attr, + &dev_attr_align.attr, + &dev_attr_id.attr, + NULL, +}; + +static const struct attribute_group dax_region_attribute_group = { + .name = "dax_region", + .attrs = dax_region_attributes, +}; + +static const struct attribute_group *dax_region_attribute_groups[] = { + &dax_region_attribute_group, + NULL, +}; + +static void dax_region_free(struct kref *kref) +{ + struct dax_region *dax_region; + + dax_region = container_of(kref, struct dax_region, kref); + kfree(dax_region); +} + +void dax_region_put(struct dax_region *dax_region) +{ + kref_put(&dax_region->kref, dax_region_free); +} +EXPORT_SYMBOL_GPL(dax_region_put); + +static void dax_region_unregister(void *region) +{ + struct dax_region *dax_region = region; + + sysfs_remove_groups(&dax_region->dev->kobj, + dax_region_attribute_groups); + dax_region_put(dax_region); +} + +struct dax_region *alloc_dax_region(struct device *parent, int region_id, + struct resource *res, unsigned int align, + unsigned long pfn_flags) +{ + struct dax_region *dax_region; + + /* + * The DAX core assumes that it can store its private data in + * parent->driver_data. This WARN is a reminder / safeguard for + * developers of device-dax drivers. + */ + if (dev_get_drvdata(parent)) { + dev_WARN(parent, "dax core failed to setup private data\n"); + return NULL; + } + + if (!IS_ALIGNED(res->start, align) + || !IS_ALIGNED(resource_size(res), align)) + return NULL; + + dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL); + if (!dax_region) + return NULL; + + dev_set_drvdata(parent, dax_region); + memcpy(&dax_region->res, res, sizeof(*res)); + dax_region->pfn_flags = pfn_flags; + kref_init(&dax_region->kref); + dax_region->id = region_id; + dax_region->align = align; + dax_region->dev = parent; + if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { + kfree(dax_region); + return NULL; + } + + kref_get(&dax_region->kref); + if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) + return NULL; + return dax_region; +} +EXPORT_SYMBOL_GPL(alloc_dax_region); + +static ssize_t size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + unsigned long long size = resource_size(&dev_dax->region->res); + + return sprintf(buf, "%llu\n", size); +} +static DEVICE_ATTR_RO(size); + +static struct attribute *dev_dax_attributes[] = { + &dev_attr_size.attr, + NULL, +}; + +static const struct attribute_group dev_dax_attribute_group = { + .attrs = dev_dax_attributes, +}; + +const struct attribute_group *dax_attribute_groups[] = { + &dev_dax_attribute_group, + NULL, +}; +EXPORT_SYMBOL_GPL(dax_attribute_groups); + +void kill_dev_dax(struct dev_dax *dev_dax) +{ + struct dax_device *dax_dev = dev_dax->dax_dev; + struct inode *inode = dax_inode(dax_dev); + + kill_dax(dax_dev); + unmap_mapping_range(inode->i_mapping, 0, 0, 1); +} +EXPORT_SYMBOL_GPL(kill_dev_dax); + +void unregister_dev_dax(void *dev) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + struct dax_device *dax_dev = dev_dax->dax_dev; + struct inode *inode = dax_inode(dax_dev); + struct cdev *cdev = inode->i_cdev; + + dev_dbg(dev, "trace\n"); + + kill_dev_dax(dev_dax); + cdev_device_del(cdev, dev); + put_device(dev); +} +EXPORT_SYMBOL_GPL(unregister_dev_dax); diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h new file mode 100644 index 000000000000..840865aa69e8 --- /dev/null +++ b/drivers/dax/bus.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#ifndef __DAX_BUS_H__ +#define __DAX_BUS_H__ +struct device; +struct dev_dax; +struct resource; +struct dax_device; +struct dax_region; +void dax_region_put(struct dax_region *dax_region); +struct dax_region *alloc_dax_region(struct device *parent, int region_id, + struct resource *res, unsigned int align, unsigned long flags); +struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id); +void kill_dev_dax(struct dev_dax *dev_dax); +#endif /* __DAX_BUS_H__ */ diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index dbd077653b5c..620c3f4eefe7 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -16,6 +16,15 @@ #include #include +/* private routines between core files */ +struct dax_device; +struct dax_device *inode_dax(struct inode *inode); +struct inode *dax_inode(struct dax_device *dax_dev); + +/* temporary until devm_create_dax_dev moves to bus.c */ +extern const struct attribute_group *dax_attribute_groups[]; +void unregister_dev_dax(void *dev); + /** * struct dax_region - mapping infrastructure for dax devices * @id: kernel-wide unique region for a memory range @@ -45,4 +54,9 @@ struct dev_dax { struct dax_device *dax_dev; struct device dev; }; + +static inline struct dev_dax *to_dev_dax(struct device *dev) +{ + return container_of(dev, struct dev_dax, dev); +} #endif diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h deleted file mode 100644 index f9e5feea742c..000000000000 --- a/drivers/dax/dax.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright(c) 2016 - 2017 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#ifndef __DAX_H__ -#define __DAX_H__ -struct dax_device; -struct dax_device *inode_dax(struct inode *inode); -struct inode *dax_inode(struct dax_device *dax_dev); -#endif /* __DAX_H__ */ diff --git a/drivers/dax/device-dax.h b/drivers/dax/device-dax.h deleted file mode 100644 index e9be99584b92..000000000000 --- a/drivers/dax/device-dax.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#ifndef __DEVICE_DAX_H__ -#define __DEVICE_DAX_H__ -struct device; -struct dev_dax; -struct resource; -struct dax_region; -void dax_region_put(struct dax_region *dax_region); -struct dax_region *alloc_dax_region(struct device *parent, int region_id, - struct resource *res, unsigned int align, unsigned long flags); -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id); -#endif /* __DEVICE_DAX_H__ */ diff --git a/drivers/dax/device.c b/drivers/dax/device.c index db12e24b8005..1fc375783e0b 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -1,15 +1,5 @@ -/* - * Copyright(c) 2016 - 2017 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-2018 Intel Corporation. All rights reserved. */ #include #include #include @@ -21,156 +11,10 @@ #include #include #include "dax-private.h" -#include "dax.h" +#include "bus.h" static struct class *dax_class; -/* - * Rely on the fact that drvdata is set before the attributes are - * registered, and that the attributes are unregistered before drvdata - * is cleared to assume that drvdata is always valid. - */ -static ssize_t id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dax_region *dax_region = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", dax_region->id); -} -static DEVICE_ATTR_RO(id); - -static ssize_t region_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dax_region *dax_region = dev_get_drvdata(dev); - - return sprintf(buf, "%llu\n", (unsigned long long) - resource_size(&dax_region->res)); -} -static struct device_attribute dev_attr_region_size = __ATTR(size, 0444, - region_size_show, NULL); - -static ssize_t align_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dax_region *dax_region = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", dax_region->align); -} -static DEVICE_ATTR_RO(align); - -static struct attribute *dax_region_attributes[] = { - &dev_attr_region_size.attr, - &dev_attr_align.attr, - &dev_attr_id.attr, - NULL, -}; - -static const struct attribute_group dax_region_attribute_group = { - .name = "dax_region", - .attrs = dax_region_attributes, -}; - -static const struct attribute_group *dax_region_attribute_groups[] = { - &dax_region_attribute_group, - NULL, -}; - -static void dax_region_free(struct kref *kref) -{ - struct dax_region *dax_region; - - dax_region = container_of(kref, struct dax_region, kref); - kfree(dax_region); -} - -void dax_region_put(struct dax_region *dax_region) -{ - kref_put(&dax_region->kref, dax_region_free); -} -EXPORT_SYMBOL_GPL(dax_region_put); - -static void dax_region_unregister(void *region) -{ - struct dax_region *dax_region = region; - - sysfs_remove_groups(&dax_region->dev->kobj, - dax_region_attribute_groups); - dax_region_put(dax_region); -} - -struct dax_region *alloc_dax_region(struct device *parent, int region_id, - struct resource *res, unsigned int align, - unsigned long pfn_flags) -{ - struct dax_region *dax_region; - - /* - * The DAX core assumes that it can store its private data in - * parent->driver_data. This WARN is a reminder / safeguard for - * developers of device-dax drivers. - */ - if (dev_get_drvdata(parent)) { - dev_WARN(parent, "dax core failed to setup private data\n"); - return NULL; - } - - if (!IS_ALIGNED(res->start, align) - || !IS_ALIGNED(resource_size(res), align)) - return NULL; - - dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL); - if (!dax_region) - return NULL; - - dev_set_drvdata(parent, dax_region); - memcpy(&dax_region->res, res, sizeof(*res)); - dax_region->pfn_flags = pfn_flags; - kref_init(&dax_region->kref); - dax_region->id = region_id; - dax_region->align = align; - dax_region->dev = parent; - if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { - kfree(dax_region); - return NULL; - } - - kref_get(&dax_region->kref); - if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) - return NULL; - return dax_region; -} -EXPORT_SYMBOL_GPL(alloc_dax_region); - -static struct dev_dax *to_dev_dax(struct device *dev) -{ - return container_of(dev, struct dev_dax, dev); -} - -static ssize_t size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct dev_dax *dev_dax = to_dev_dax(dev); - unsigned long long size = resource_size(&dev_dax->region->res); - - return sprintf(buf, "%llu\n", size); -} -static DEVICE_ATTR_RO(size); - -static struct attribute *dev_dax_attributes[] = { - &dev_attr_size.attr, - NULL, -}; - -static const struct attribute_group dev_dax_attribute_group = { - .attrs = dev_dax_attributes, -}; - -static const struct attribute_group *dax_attribute_groups[] = { - &dev_dax_attribute_group, - NULL, -}; - static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, const char *func) { @@ -571,29 +415,6 @@ static void dev_dax_release(struct device *dev) kfree(dev_dax); } -static void kill_dev_dax(struct dev_dax *dev_dax) -{ - struct dax_device *dax_dev = dev_dax->dax_dev; - struct inode *inode = dax_inode(dax_dev); - - kill_dax(dax_dev); - unmap_mapping_range(inode->i_mapping, 0, 0, 1); -} - -static void unregister_dev_dax(void *dev) -{ - struct dev_dax *dev_dax = to_dev_dax(dev); - struct dax_device *dax_dev = dev_dax->dax_dev; - struct inode *inode = dax_inode(dax_dev); - struct cdev *cdev = inode->i_cdev; - - dev_dbg(dev, "trace\n"); - - kill_dev_dax(dev_dax); - cdev_device_del(cdev, dev); - put_device(dev); -} - struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id) { struct device *parent = dax_region->dev; diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index 6c61a019f997..e1b50d8fd1f8 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -16,7 +16,7 @@ #include #include "../nvdimm/pfn.h" #include "../nvdimm/nd.h" -#include "device-dax.h" +#include "bus.h" struct dax_pmem { struct device *dev; diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 6e928f37d084..0ecc1a2cf1cc 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -22,6 +22,7 @@ #include #include #include +#include "dax-private.h" static dev_t dax_devt; DEFINE_STATIC_SRCU(dax_srcu); diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index 10ddf223055b..c9b500a652d5 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -57,6 +57,7 @@ nd_e820-y := $(NVDIMM_SRC)/e820.o nd_e820-y += config_check.o dax-y := $(DAX_SRC)/super.o +dax-y += $(DAX_SRC)/bus.o dax-y += config_check.o device_dax-y := $(DAX_SRC)/device.o From 9567da0b408a2553d32ca83cba4f1fc5a8aad459 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 12 Jul 2017 17:58:21 -0700 Subject: [PATCH 0054/4277] device-dax: Introduce bus + driver model In support of multiple device-dax instances per device-dax-region and allowing the 'kmem' driver to attach to dax-instances instead of the current device-node access, convert the dax sub-system from a class to a bus. Recall that the kmem driver takes reserved / special purpose memories and assigns them to be managed by the core-mm. Aside from the fact the device-dax instances are registered and probed on a bus, two other lifetime-management changes are made: 1/ Delay attaching a cdev until driver probe time 2/ A new run_dax() helper is introduced to allow restoring dax-operation after a kill_dax() event. So, at driver ->probe() time we run_dax() and at ->remove() time we kill_dax() and invalidate all mappings. Signed-off-by: Dan Williams --- drivers/dax/bus.c | 133 +++++++++++++++++++++++++++++++++++--- drivers/dax/bus.h | 16 +++++ drivers/dax/dax-private.h | 6 +- drivers/dax/device.c | 99 ++++++++++------------------ drivers/dax/super.c | 40 ++++++++---- 5 files changed, 205 insertions(+), 89 deletions(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 8a398e8e1956..0cff32102c4c 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -6,6 +6,33 @@ #include "dax-private.h" #include "bus.h" +static int dax_bus_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* + * We only ever expect to handle device-dax instances, i.e. the + * @type argument to MODULE_ALIAS_DAX_DEVICE() is always zero + */ + return add_uevent_var(env, "MODALIAS=" DAX_DEVICE_MODALIAS_FMT, 0); +} + +static int dax_bus_match(struct device *dev, struct device_driver *drv); + +static struct bus_type dax_bus_type = { + .name = "dax", + .uevent = dax_bus_uevent, + .match = dax_bus_match, +}; + +static int dax_bus_match(struct device *dev, struct device_driver *drv) +{ + /* + * The drivers that can register on the 'dax' bus are private to + * drivers/dax/ so any device and driver on the bus always + * match. + */ + return 1; +} + /* * Rely on the fact that drvdata is set before the attributes are * registered, and that the attributes are unregistered before drvdata @@ -142,11 +169,10 @@ static const struct attribute_group dev_dax_attribute_group = { .attrs = dev_dax_attributes, }; -const struct attribute_group *dax_attribute_groups[] = { +static const struct attribute_group *dax_attribute_groups[] = { &dev_dax_attribute_group, NULL, }; -EXPORT_SYMBOL_GPL(dax_attribute_groups); void kill_dev_dax(struct dev_dax *dev_dax) { @@ -158,17 +184,108 @@ void kill_dev_dax(struct dev_dax *dev_dax) } EXPORT_SYMBOL_GPL(kill_dev_dax); -void unregister_dev_dax(void *dev) +static void dev_dax_release(struct device *dev) { struct dev_dax *dev_dax = to_dev_dax(dev); + struct dax_region *dax_region = dev_dax->region; struct dax_device *dax_dev = dev_dax->dax_dev; - struct inode *inode = dax_inode(dax_dev); - struct cdev *cdev = inode->i_cdev; - dev_dbg(dev, "trace\n"); + dax_region_put(dax_region); + put_dax(dax_dev); + kfree(dev_dax); +} + +static void unregister_dev_dax(void *dev) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + + dev_dbg(dev, "%s\n", __func__); kill_dev_dax(dev_dax); - cdev_device_del(cdev, dev); + device_del(dev); put_device(dev); } -EXPORT_SYMBOL_GPL(unregister_dev_dax); + +struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id) +{ + struct device *parent = dax_region->dev; + struct dax_device *dax_dev; + struct dev_dax *dev_dax; + struct inode *inode; + struct device *dev; + int rc = -ENOMEM; + + if (id < 0) + return ERR_PTR(-EINVAL); + + dev_dax = kzalloc(sizeof(*dev_dax), GFP_KERNEL); + if (!dev_dax) + return ERR_PTR(-ENOMEM); + + /* + * No 'host' or dax_operations since there is no access to this + * device outside of mmap of the resulting character device. + */ + dax_dev = alloc_dax(dev_dax, NULL, NULL); + if (!dax_dev) + goto err; + + /* a device_dax instance is dead while the driver is not attached */ + kill_dax(dax_dev); + + /* from here on we're committed to teardown via dax_dev_release() */ + dev = &dev_dax->dev; + device_initialize(dev); + + dev_dax->dax_dev = dax_dev; + dev_dax->region = dax_region; + kref_get(&dax_region->kref); + + inode = dax_inode(dax_dev); + dev->devt = inode->i_rdev; + dev->bus = &dax_bus_type; + dev->parent = parent; + dev->groups = dax_attribute_groups; + dev->release = dev_dax_release; + dev_set_name(dev, "dax%d.%d", dax_region->id, id); + + rc = device_add(dev); + if (rc) { + kill_dev_dax(dev_dax); + put_device(dev); + return ERR_PTR(rc); + } + + rc = devm_add_action_or_reset(dax_region->dev, unregister_dev_dax, dev); + if (rc) + return ERR_PTR(rc); + + return dev_dax; + + err: + kfree(dev_dax); + + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(devm_create_dev_dax); + +int __dax_driver_register(struct device_driver *drv, + struct module *module, const char *mod_name) +{ + drv->owner = module; + drv->name = mod_name; + drv->mod_name = mod_name; + drv->bus = &dax_bus_type; + return driver_register(drv); +} +EXPORT_SYMBOL_GPL(__dax_driver_register); + +int __init dax_bus_init(void) +{ + return bus_register(&dax_bus_type); +} + +void __exit dax_bus_exit(void) +{ + bus_unregister(&dax_bus_type); +} diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h index 840865aa69e8..ea509504df3a 100644 --- a/drivers/dax/bus.h +++ b/drivers/dax/bus.h @@ -11,5 +11,21 @@ void dax_region_put(struct dax_region *dax_region); struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, unsigned long flags); struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id); +int __dax_driver_register(struct device_driver *drv, + struct module *module, const char *mod_name); +#define dax_driver_register(driver) \ + __dax_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) void kill_dev_dax(struct dev_dax *dev_dax); + +/* + * While run_dax() is potentially a generic operation that could be + * defined in include/linux/dax.h we don't want to grow any users + * outside of drivers/dax/ + */ +void run_dax(struct dax_device *dax_dev); + +#define MODULE_ALIAS_DAX_DEVICE(type) \ + MODULE_ALIAS("dax:t" __stringify(type) "*") +#define DAX_DEVICE_MODALIAS_FMT "dax:t%d" + #endif /* __DAX_BUS_H__ */ diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index 620c3f4eefe7..c3a121700837 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -20,10 +20,8 @@ struct dax_device; struct dax_device *inode_dax(struct inode *inode); struct inode *dax_inode(struct dax_device *dax_dev); - -/* temporary until devm_create_dax_dev moves to bus.c */ -extern const struct attribute_group *dax_attribute_groups[]; -void unregister_dev_dax(void *dev); +int dax_bus_init(void); +void dax_bus_exit(void); /** * struct dax_region - mapping infrastructure for dax devices diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 1fc375783e0b..f55829404a24 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -13,8 +13,6 @@ #include "dax-private.h" #include "bus.h" -static struct class *dax_class; - static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, const char *func) { @@ -404,93 +402,64 @@ static const struct file_operations dax_fops = { .mmap_supported_flags = MAP_SYNC, }; -static void dev_dax_release(struct device *dev) +static void dev_dax_cdev_del(void *cdev) { - struct dev_dax *dev_dax = to_dev_dax(dev); - struct dax_region *dax_region = dev_dax->region; - struct dax_device *dax_dev = dev_dax->dax_dev; - - dax_region_put(dax_region); - put_dax(dax_dev); - kfree(dev_dax); + cdev_del(cdev); } -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id) +static void dev_dax_kill(void *dev_dax) { - struct device *parent = dax_region->dev; - struct dax_device *dax_dev; - struct dev_dax *dev_dax; + kill_dev_dax(dev_dax); +} + +static int dev_dax_probe(struct device *dev) +{ + struct dev_dax *dev_dax = to_dev_dax(dev); + struct dax_device *dax_dev = dev_dax->dax_dev; struct inode *inode; - struct device *dev; struct cdev *cdev; int rc; - dev_dax = kzalloc(sizeof(*dev_dax), GFP_KERNEL); - if (!dev_dax) - return ERR_PTR(-ENOMEM); - - /* - * No 'host' or dax_operations since there is no access to this - * device outside of mmap of the resulting character device. - */ - dax_dev = alloc_dax(dev_dax, NULL, NULL); - if (!dax_dev) { - rc = -ENOMEM; - goto err; - } - - /* from here on we're committed to teardown via dax_dev_release() */ - dev = &dev_dax->dev; - device_initialize(dev); - inode = dax_inode(dax_dev); cdev = inode->i_cdev; cdev_init(cdev, &dax_fops); - cdev->owner = parent->driver->owner; - - dev_dax->dax_dev = dax_dev; - dev_dax->region = dax_region; - kref_get(&dax_region->kref); - - dev->devt = inode->i_rdev; - dev->class = dax_class; - dev->parent = parent; - dev->groups = dax_attribute_groups; - dev->release = dev_dax_release; - dev_set_name(dev, "dax%d.%d", dax_region->id, id); - - rc = cdev_device_add(cdev, dev); - if (rc) { - kill_dev_dax(dev_dax); - put_device(dev); - return ERR_PTR(rc); - } - - rc = devm_add_action_or_reset(dax_region->dev, unregister_dev_dax, dev); + cdev->owner = dev->driver->owner; + cdev_set_parent(cdev, &dev->kobj); + rc = cdev_add(cdev, dev->devt, 1); if (rc) - return ERR_PTR(rc); + return rc; - return dev_dax; + rc = devm_add_action_or_reset(dev, dev_dax_cdev_del, cdev); + if (rc) + return rc; - err: - kfree(dev_dax); - - return ERR_PTR(rc); + run_dax(dax_dev); + return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax); } -EXPORT_SYMBOL_GPL(devm_create_dev_dax); + +static int dev_dax_remove(struct device *dev) +{ + /* all probe actions are unwound by devm */ + return 0; +} + +static struct device_driver device_dax_driver = { + .probe = dev_dax_probe, + .remove = dev_dax_remove, +}; static int __init dax_init(void) { - dax_class = class_create(THIS_MODULE, "dax"); - return PTR_ERR_OR_ZERO(dax_class); + return dax_driver_register(&device_dax_driver); } static void __exit dax_exit(void) { - class_destroy(dax_class); + driver_unregister(&device_dax_driver); } MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); -subsys_initcall(dax_init); +module_init(dax_init); module_exit(dax_exit); +MODULE_ALIAS_DAX_DEVICE(0); diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 0ecc1a2cf1cc..ccb22d8db3a2 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -366,11 +366,15 @@ void kill_dax(struct dax_device *dax_dev) spin_lock(&dax_host_lock); hlist_del_init(&dax_dev->list); spin_unlock(&dax_host_lock); - - dax_dev->private = NULL; } EXPORT_SYMBOL_GPL(kill_dax); +void run_dax(struct dax_device *dax_dev) +{ + set_bit(DAXDEV_ALIVE, &dax_dev->flags); +} +EXPORT_SYMBOL_GPL(run_dax); + static struct inode *dax_alloc_inode(struct super_block *sb) { struct dax_device *dax_dev; @@ -585,6 +589,8 @@ EXPORT_SYMBOL_GPL(dax_inode); void *dax_get_private(struct dax_device *dax_dev) { + if (!test_bit(DAXDEV_ALIVE, &dax_dev->flags)) + return NULL; return dax_dev->private; } EXPORT_SYMBOL_GPL(dax_get_private); @@ -598,7 +604,7 @@ static void init_once(void *_dax_dev) inode_init_once(inode); } -static int __dax_fs_init(void) +static int dax_fs_init(void) { int rc; @@ -630,35 +636,45 @@ static int __dax_fs_init(void) return rc; } -static void __dax_fs_exit(void) +static void dax_fs_exit(void) { kern_unmount(dax_mnt); unregister_filesystem(&dax_fs_type); kmem_cache_destroy(dax_cache); } -static int __init dax_fs_init(void) +static int __init dax_core_init(void) { int rc; - rc = __dax_fs_init(); + rc = dax_fs_init(); if (rc) return rc; rc = alloc_chrdev_region(&dax_devt, 0, MINORMASK+1, "dax"); if (rc) - __dax_fs_exit(); - return rc; + goto err_chrdev; + + rc = dax_bus_init(); + if (rc) + goto err_bus; + return 0; + +err_bus: + unregister_chrdev_region(dax_devt, MINORMASK+1); +err_chrdev: + dax_fs_exit(); + return 0; } -static void __exit dax_fs_exit(void) +static void __exit dax_core_exit(void) { unregister_chrdev_region(dax_devt, MINORMASK+1); ida_destroy(&dax_minor_ida); - __dax_fs_exit(); + dax_fs_exit(); } MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); -subsys_initcall(dax_fs_init); -module_exit(dax_fs_exit); +subsys_initcall(dax_core_init); +module_exit(dax_core_exit); From 89ec9f2cfa36cc5fca2fb445ed221bb9add7b536 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 29 Oct 2018 15:52:42 -0700 Subject: [PATCH 0055/4277] device-dax: Move resource pinning+mapping into the common driver Move the responsibility of calling devm_request_resource() and devm_memremap_pages() into the common device-dax driver. This is another preparatory step to allowing an alternate personality driver for a device-dax range. Signed-off-by: Dan Williams --- drivers/dax/bus.c | 6 ++- drivers/dax/bus.h | 3 +- drivers/dax/dax-private.h | 9 +++- drivers/dax/device.c | 61 ++++++++++++++++++++++++++ drivers/dax/pmem.c | 90 ++++++--------------------------------- 5 files changed, 90 insertions(+), 79 deletions(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 0cff32102c4c..69aae2cbd45f 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017-2018 Intel Corporation. All rights reserved. */ +#include #include #include #include @@ -206,7 +207,8 @@ static void unregister_dev_dax(void *dev) put_device(dev); } -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id) +struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, + struct dev_pagemap *pgmap) { struct device *parent = dax_region->dev; struct dax_device *dax_dev; @@ -222,6 +224,8 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id) if (!dev_dax) return ERR_PTR(-ENOMEM); + memcpy(&dev_dax->pgmap, pgmap, sizeof(*pgmap)); + /* * No 'host' or dax_operations since there is no access to this * device outside of mmap of the resulting character device. diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h index ea509504df3a..e08e0c394983 100644 --- a/drivers/dax/bus.h +++ b/drivers/dax/bus.h @@ -10,7 +10,8 @@ struct dax_region; void dax_region_put(struct dax_region *dax_region); struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, unsigned long flags); -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id); +struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, + struct dev_pagemap *pgmap); int __dax_driver_register(struct device_driver *drv, struct module *module, const char *mod_name); #define dax_driver_register(driver) \ diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index c3a121700837..a82ce48f5884 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -42,15 +42,22 @@ struct dax_region { }; /** - * struct dev_dax - instance data for a subdivision of a dax region + * struct dev_dax - instance data for a subdivision of a dax region, and + * data while the device is activated in the driver. * @region - parent region * @dax_dev - core dax functionality * @dev - device core + * @pgmap - pgmap for memmap setup / lifetime (driver owned) + * @ref: pgmap reference count (driver owned) + * @cmp: @ref final put completion (driver owned) */ struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; struct device dev; + struct dev_pagemap pgmap; + struct percpu_ref ref; + struct completion cmp; }; static inline struct dev_dax *to_dev_dax(struct device *dev) diff --git a/drivers/dax/device.c b/drivers/dax/device.c index f55829404a24..6ad964d7b077 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2016-2018 Intel Corporation. All rights reserved. */ +#include #include #include #include @@ -13,6 +14,38 @@ #include "dax-private.h" #include "bus.h" +static struct dev_dax *ref_to_dev_dax(struct percpu_ref *ref) +{ + return container_of(ref, struct dev_dax, ref); +} + +static void dev_dax_percpu_release(struct percpu_ref *ref) +{ + struct dev_dax *dev_dax = ref_to_dev_dax(ref); + + dev_dbg(&dev_dax->dev, "%s\n", __func__); + complete(&dev_dax->cmp); +} + +static void dev_dax_percpu_exit(void *data) +{ + struct percpu_ref *ref = data; + struct dev_dax *dev_dax = ref_to_dev_dax(ref); + + dev_dbg(&dev_dax->dev, "%s\n", __func__); + wait_for_completion(&dev_dax->cmp); + percpu_ref_exit(ref); +} + +static void dev_dax_percpu_kill(struct percpu_ref *data) +{ + struct percpu_ref *ref = data; + struct dev_dax *dev_dax = ref_to_dev_dax(ref); + + dev_dbg(&dev_dax->dev, "%s\n", __func__); + percpu_ref_kill(ref); +} + static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, const char *func) { @@ -416,10 +449,38 @@ static int dev_dax_probe(struct device *dev) { struct dev_dax *dev_dax = to_dev_dax(dev); struct dax_device *dax_dev = dev_dax->dax_dev; + struct resource *res = &dev_dax->region->res; struct inode *inode; struct cdev *cdev; + void *addr; int rc; + /* 1:1 map region resource range to device-dax instance range */ + if (!devm_request_mem_region(dev, res->start, resource_size(res), + dev_name(dev))) { + dev_warn(dev, "could not reserve region %pR\n", res); + return -EBUSY; + } + + init_completion(&dev_dax->cmp); + rc = percpu_ref_init(&dev_dax->ref, dev_dax_percpu_release, 0, + GFP_KERNEL); + if (rc) + return rc; + + rc = devm_add_action_or_reset(dev, dev_dax_percpu_exit, &dev_dax->ref); + if (rc) + return rc; + + dev_dax->pgmap.ref = &dev_dax->ref; + dev_dax->pgmap.kill = dev_dax_percpu_kill; + addr = devm_memremap_pages(dev, &dev_dax->pgmap); + if (IS_ERR(addr)) { + devm_remove_action(dev, dev_dax_percpu_exit, &dev_dax->ref); + percpu_ref_exit(&dev_dax->ref); + return PTR_ERR(addr); + } + inode = dax_inode(dax_dev); cdev = inode->i_cdev; cdev_init(cdev, &dax_fops); diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index e1b50d8fd1f8..d3cefa7868ac 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -18,54 +18,16 @@ #include "../nvdimm/nd.h" #include "bus.h" -struct dax_pmem { - struct device *dev; - struct percpu_ref ref; - struct dev_pagemap pgmap; - struct completion cmp; -}; - -static struct dax_pmem *to_dax_pmem(struct percpu_ref *ref) -{ - return container_of(ref, struct dax_pmem, ref); -} - -static void dax_pmem_percpu_release(struct percpu_ref *ref) -{ - struct dax_pmem *dax_pmem = to_dax_pmem(ref); - - dev_dbg(dax_pmem->dev, "trace\n"); - complete(&dax_pmem->cmp); -} - -static void dax_pmem_percpu_exit(void *data) -{ - struct percpu_ref *ref = data; - struct dax_pmem *dax_pmem = to_dax_pmem(ref); - - dev_dbg(dax_pmem->dev, "trace\n"); - wait_for_completion(&dax_pmem->cmp); - percpu_ref_exit(ref); -} - -static void dax_pmem_percpu_kill(struct percpu_ref *ref) -{ - struct dax_pmem *dax_pmem = to_dax_pmem(ref); - - dev_dbg(dax_pmem->dev, "trace\n"); - percpu_ref_kill(ref); -} - static int dax_pmem_probe(struct device *dev) { - void *addr; struct resource res; int rc, id, region_id; + resource_size_t offset; struct nd_pfn_sb *pfn_sb; struct dev_dax *dev_dax; - struct dax_pmem *dax_pmem; struct nd_namespace_io *nsio; struct dax_region *dax_region; + struct dev_pagemap pgmap = { 0 }; struct nd_namespace_common *ndns; struct nd_dax *nd_dax = to_nd_dax(dev); struct nd_pfn *nd_pfn = &nd_dax->nd_pfn; @@ -75,61 +37,37 @@ static int dax_pmem_probe(struct device *dev) return PTR_ERR(ndns); nsio = to_nd_namespace_io(&ndns->dev); - dax_pmem = devm_kzalloc(dev, sizeof(*dax_pmem), GFP_KERNEL); - if (!dax_pmem) - return -ENOMEM; - /* parse the 'pfn' info block via ->rw_bytes */ rc = devm_nsio_enable(dev, nsio); if (rc) return rc; - rc = nvdimm_setup_pfn(nd_pfn, &dax_pmem->pgmap); + rc = nvdimm_setup_pfn(nd_pfn, &pgmap); if (rc) return rc; devm_nsio_disable(dev, nsio); - pfn_sb = nd_pfn->pfn_sb; - - if (!devm_request_mem_region(dev, nsio->res.start, - resource_size(&nsio->res), + /* reserve the metadata area, device-dax will reserve the data */ + pfn_sb = nd_pfn->pfn_sb; + offset = le64_to_cpu(pfn_sb->dataoff); + if (!devm_request_mem_region(dev, nsio->res.start, offset, dev_name(&ndns->dev))) { - dev_warn(dev, "could not reserve region %pR\n", &nsio->res); - return -EBUSY; - } - - dax_pmem->dev = dev; - init_completion(&dax_pmem->cmp); - rc = percpu_ref_init(&dax_pmem->ref, dax_pmem_percpu_release, 0, - GFP_KERNEL); - if (rc) - return rc; - - rc = devm_add_action(dev, dax_pmem_percpu_exit, &dax_pmem->ref); - if (rc) { - percpu_ref_exit(&dax_pmem->ref); - return rc; - } - - dax_pmem->pgmap.ref = &dax_pmem->ref; - dax_pmem->pgmap.kill = dax_pmem_percpu_kill; - addr = devm_memremap_pages(dev, &dax_pmem->pgmap); - if (IS_ERR(addr)) - return PTR_ERR(addr); - - /* adjust the dax_region resource to the start of data */ - memcpy(&res, &dax_pmem->pgmap.res, sizeof(res)); - res.start += le64_to_cpu(pfn_sb->dataoff); + dev_warn(dev, "could not reserve metadata\n"); + return -EBUSY; + } rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", ®ion_id, &id); if (rc != 2) return -EINVAL; + /* adjust the dax_region resource to the start of data */ + memcpy(&res, &pgmap.res, sizeof(res)); + res.start += offset; dax_region = alloc_dax_region(dev, region_id, &res, le32_to_cpu(pfn_sb->align), PFN_DEV|PFN_MAP); if (!dax_region) return -ENOMEM; - dev_dax = devm_create_dev_dax(dax_region, id); + dev_dax = devm_create_dev_dax(dax_region, id, &pgmap); /* child dev_dax instances now own the lifetime of the dax_region */ dax_region_put(dax_region); From d200781ef237a354d918ceff5cee350d88a93d42 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 7 Nov 2018 15:31:23 -0800 Subject: [PATCH 0056/4277] device-dax: Add support for a dax override driver Introduce the 'new_id' concept for enabling a custom device-driver attach policy for dax-bus drivers. The intended use is to have a mechanism for hot-plugging device-dax ranges into the page allocator on-demand. With this in place the default policy of using device-dax for performance differentiated memory can be overridden by user-space policy that can arrange for the memory range to be managed as 'System RAM' with user-defined NUMA and other performance attributes. Signed-off-by: Dan Williams --- drivers/dax/bus.c | 145 +++++++++++++++++++++++++++++++++++++++++-- drivers/dax/bus.h | 10 ++- drivers/dax/device.c | 11 ++-- 3 files changed, 156 insertions(+), 10 deletions(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 69aae2cbd45f..17af6fbc3be5 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -2,11 +2,21 @@ /* Copyright(c) 2017-2018 Intel Corporation. All rights reserved. */ #include #include +#include +#include #include #include #include "dax-private.h" #include "bus.h" +static DEFINE_MUTEX(dax_bus_lock); + +#define DAX_NAME_LEN 30 +struct dax_id { + struct list_head list; + char dev_name[DAX_NAME_LEN]; +}; + static int dax_bus_uevent(struct device *dev, struct kobj_uevent_env *env) { /* @@ -16,22 +26,115 @@ static int dax_bus_uevent(struct device *dev, struct kobj_uevent_env *env) return add_uevent_var(env, "MODALIAS=" DAX_DEVICE_MODALIAS_FMT, 0); } +static struct dax_device_driver *to_dax_drv(struct device_driver *drv) +{ + return container_of(drv, struct dax_device_driver, drv); +} + +static struct dax_id *__dax_match_id(struct dax_device_driver *dax_drv, + const char *dev_name) +{ + struct dax_id *dax_id; + + lockdep_assert_held(&dax_bus_lock); + + list_for_each_entry(dax_id, &dax_drv->ids, list) + if (sysfs_streq(dax_id->dev_name, dev_name)) + return dax_id; + return NULL; +} + +static int dax_match_id(struct dax_device_driver *dax_drv, struct device *dev) +{ + int match; + + mutex_lock(&dax_bus_lock); + match = !!__dax_match_id(dax_drv, dev_name(dev)); + mutex_unlock(&dax_bus_lock); + + return match; +} + +static ssize_t do_id_store(struct device_driver *drv, const char *buf, + size_t count, bool add) +{ + struct dax_device_driver *dax_drv = to_dax_drv(drv); + unsigned int region_id, id; + char devname[DAX_NAME_LEN]; + struct dax_id *dax_id; + ssize_t rc = count; + int fields; + + fields = sscanf(buf, "dax%d.%d", ®ion_id, &id); + if (fields != 2) + return -EINVAL; + sprintf(devname, "dax%d.%d", region_id, id); + if (!sysfs_streq(buf, devname)) + return -EINVAL; + + mutex_lock(&dax_bus_lock); + dax_id = __dax_match_id(dax_drv, buf); + if (!dax_id) { + if (add) { + dax_id = kzalloc(sizeof(*dax_id), GFP_KERNEL); + if (dax_id) { + strncpy(dax_id->dev_name, buf, DAX_NAME_LEN); + list_add(&dax_id->list, &dax_drv->ids); + } else + rc = -ENOMEM; + } else + /* nothing to remove */; + } else if (!add) { + list_del(&dax_id->list); + kfree(dax_id); + } else + /* dax_id already added */; + mutex_unlock(&dax_bus_lock); + return rc; +} + +static ssize_t new_id_store(struct device_driver *drv, const char *buf, + size_t count) +{ + return do_id_store(drv, buf, count, true); +} +static DRIVER_ATTR_WO(new_id); + +static ssize_t remove_id_store(struct device_driver *drv, const char *buf, + size_t count) +{ + return do_id_store(drv, buf, count, false); +} +static DRIVER_ATTR_WO(remove_id); + +static struct attribute *dax_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(dax_drv); + static int dax_bus_match(struct device *dev, struct device_driver *drv); static struct bus_type dax_bus_type = { .name = "dax", .uevent = dax_bus_uevent, .match = dax_bus_match, + .drv_groups = dax_drv_groups, }; static int dax_bus_match(struct device *dev, struct device_driver *drv) { + struct dax_device_driver *dax_drv = to_dax_drv(drv); + /* - * The drivers that can register on the 'dax' bus are private to - * drivers/dax/ so any device and driver on the bus always - * match. + * All but the 'device-dax' driver, which has 'match_always' + * set, requires an exact id match. */ - return 1; + if (dax_drv->match_always) + return 1; + + return dax_match_id(dax_drv, dev); } /* @@ -273,17 +376,49 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, } EXPORT_SYMBOL_GPL(devm_create_dev_dax); -int __dax_driver_register(struct device_driver *drv, +static int match_always_count; + +int __dax_driver_register(struct dax_device_driver *dax_drv, struct module *module, const char *mod_name) { + struct device_driver *drv = &dax_drv->drv; + int rc = 0; + + INIT_LIST_HEAD(&dax_drv->ids); drv->owner = module; drv->name = mod_name; drv->mod_name = mod_name; drv->bus = &dax_bus_type; + + /* there can only be one default driver */ + mutex_lock(&dax_bus_lock); + match_always_count += dax_drv->match_always; + if (match_always_count > 1) { + match_always_count--; + WARN_ON(1); + rc = -EINVAL; + } + mutex_unlock(&dax_bus_lock); + if (rc) + return rc; return driver_register(drv); } EXPORT_SYMBOL_GPL(__dax_driver_register); +void dax_driver_unregister(struct dax_device_driver *dax_drv) +{ + struct dax_id *dax_id, *_id; + + mutex_lock(&dax_bus_lock); + match_always_count -= dax_drv->match_always; + list_for_each_entry_safe(dax_id, _id, &dax_drv->ids, list) { + list_del(&dax_id->list); + kfree(dax_id); + } + mutex_unlock(&dax_bus_lock); +} +EXPORT_SYMBOL_GPL(dax_driver_unregister); + int __init dax_bus_init(void) { return bus_register(&dax_bus_type); diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h index e08e0c394983..395ab812367c 100644 --- a/drivers/dax/bus.h +++ b/drivers/dax/bus.h @@ -12,10 +12,18 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, unsigned long flags); struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, struct dev_pagemap *pgmap); -int __dax_driver_register(struct device_driver *drv, + +struct dax_device_driver { + struct device_driver drv; + struct list_head ids; + int match_always; +}; + +int __dax_driver_register(struct dax_device_driver *dax_drv, struct module *module, const char *mod_name); #define dax_driver_register(driver) \ __dax_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) +void dax_driver_unregister(struct dax_device_driver *dax_drv); void kill_dev_dax(struct dev_dax *dev_dax); /* diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 6ad964d7b077..ad3120395f7a 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -504,9 +504,12 @@ static int dev_dax_remove(struct device *dev) return 0; } -static struct device_driver device_dax_driver = { - .probe = dev_dax_probe, - .remove = dev_dax_remove, +static struct dax_device_driver device_dax_driver = { + .drv = { + .probe = dev_dax_probe, + .remove = dev_dax_remove, + }, + .match_always = 1, }; static int __init dax_init(void) @@ -516,7 +519,7 @@ static int __init dax_init(void) static void __exit dax_exit(void) { - driver_unregister(&device_dax_driver); + dax_driver_unregister(&device_dax_driver); } MODULE_AUTHOR("Intel Corporation"); From 730926c3b0998943654019f00296cf8e3b02277e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 16 Jul 2017 13:51:53 -0700 Subject: [PATCH 0057/4277] device-dax: Add /sys/class/dax backwards compatibility On the expectation that some environments may not upgrade libdaxctl (userspace component that depends on the /sys/class/dax hierarchy), provide a default / legacy dax_pmem_compat driver. The dax_pmem_compat driver implements the original /sys/class/dax sysfs layout rather than /sys/bus/dax. When userspace is upgraded it can blacklist this module and switch to the dax_pmem driver going forward. CONFIG_DEV_DAX_PMEM_COMPAT and supporting code will be deleted according to the dax_pmem entry in Documentation/ABI/obsolete/. Signed-off-by: Dan Williams --- Documentation/ABI/obsolete/sysfs-class-dax | 22 +++++++ drivers/dax/Kconfig | 12 +++- drivers/dax/Makefile | 4 +- drivers/dax/bus.c | 29 +++++++-- drivers/dax/bus.h | 26 +++++++- drivers/dax/device.c | 9 ++- drivers/dax/pmem/Makefile | 7 +++ drivers/dax/pmem/compat.c | 73 ++++++++++++++++++++++ drivers/dax/{pmem.c => pmem/core.c} | 51 +++++---------- drivers/dax/pmem/pmem.c | 40 ++++++++++++ tools/testing/nvdimm/Kbuild | 6 +- 11 files changed, 229 insertions(+), 50 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-class-dax create mode 100644 drivers/dax/pmem/Makefile create mode 100644 drivers/dax/pmem/compat.c rename drivers/dax/{pmem.c => pmem/core.c} (57%) create mode 100644 drivers/dax/pmem/pmem.c diff --git a/Documentation/ABI/obsolete/sysfs-class-dax b/Documentation/ABI/obsolete/sysfs-class-dax new file mode 100644 index 000000000000..2cb9fc5e8bd1 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-class-dax @@ -0,0 +1,22 @@ +What: /sys/class/dax/ +Date: May, 2016 +KernelVersion: v4.7 +Contact: linux-nvdimm@lists.01.org +Description: Device DAX is the device-centric analogue of Filesystem + DAX (CONFIG_FS_DAX). It allows memory ranges to be + allocated and mapped without need of an intervening file + system. Device DAX is strict, precise and predictable. + Specifically this interface: + + 1/ Guarantees fault granularity with respect to a given + page size (pte, pmd, or pud) set at configuration time. + + 2/ Enforces deterministic behavior by being strict about + what fault scenarios are supported. + + The /sys/class/dax/ interface enumerates all the + device-dax instances in the system. The ABI is + deprecated and will be removed after 2020. It is + replaced with the DAX bus interface /sys/bus/dax/ where + device-dax instances can be found under + /sys/bus/dax/devices/ diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig index e0700bf4893a..6fc96f03920e 100644 --- a/drivers/dax/Kconfig +++ b/drivers/dax/Kconfig @@ -23,12 +23,22 @@ config DEV_DAX config DEV_DAX_PMEM tristate "PMEM DAX: direct access to persistent memory" depends on LIBNVDIMM && NVDIMM_DAX && DEV_DAX + depends on m # until we can kill DEV_DAX_PMEM_COMPAT default DEV_DAX help Support raw access to persistent memory. Note that this driver consumes memory ranges allocated and exported by the libnvdimm sub-system. - Say Y if unsure + Say M if unsure + +config DEV_DAX_PMEM_COMPAT + tristate "PMEM DAX: support the deprecated /sys/class/dax interface" + depends on DEV_DAX_PMEM + default DEV_DAX_PMEM + help + Older versions of the libdaxctl library expect to find all + device-dax instances under /sys/class/dax. If libdaxctl in + your distribution is older than v58 say M, otherwise say N. endif diff --git a/drivers/dax/Makefile b/drivers/dax/Makefile index 658e6b9b1d74..233bbffccbe6 100644 --- a/drivers/dax/Makefile +++ b/drivers/dax/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DAX) += dax.o obj-$(CONFIG_DEV_DAX) += device_dax.o -obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o dax-y := super.o dax-y += bus.o -dax_pmem-y := pmem.o device_dax-y := device.o + +obj-y += pmem/ diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 17af6fbc3be5..568168500217 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -9,6 +9,8 @@ #include "dax-private.h" #include "bus.h" +static struct class *dax_class; + static DEFINE_MUTEX(dax_bus_lock); #define DAX_NAME_LEN 30 @@ -310,8 +312,8 @@ static void unregister_dev_dax(void *dev) put_device(dev); } -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, - struct dev_pagemap *pgmap) +struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, + struct dev_pagemap *pgmap, enum dev_dax_subsys subsys) { struct device *parent = dax_region->dev; struct dax_device *dax_dev; @@ -350,7 +352,10 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, inode = dax_inode(dax_dev); dev->devt = inode->i_rdev; - dev->bus = &dax_bus_type; + if (subsys == DEV_DAX_BUS) + dev->bus = &dax_bus_type; + else + dev->class = dax_class; dev->parent = parent; dev->groups = dax_attribute_groups; dev->release = dev_dax_release; @@ -374,7 +379,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, return ERR_PTR(rc); } -EXPORT_SYMBOL_GPL(devm_create_dev_dax); +EXPORT_SYMBOL_GPL(__devm_create_dev_dax); static int match_always_count; @@ -407,6 +412,7 @@ EXPORT_SYMBOL_GPL(__dax_driver_register); void dax_driver_unregister(struct dax_device_driver *dax_drv) { + struct device_driver *drv = &dax_drv->drv; struct dax_id *dax_id, *_id; mutex_lock(&dax_bus_lock); @@ -416,15 +422,28 @@ void dax_driver_unregister(struct dax_device_driver *dax_drv) kfree(dax_id); } mutex_unlock(&dax_bus_lock); + driver_unregister(drv); } EXPORT_SYMBOL_GPL(dax_driver_unregister); int __init dax_bus_init(void) { - return bus_register(&dax_bus_type); + int rc; + + if (IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT)) { + dax_class = class_create(THIS_MODULE, "dax"); + if (IS_ERR(dax_class)) + return PTR_ERR(dax_class); + } + + rc = bus_register(&dax_bus_type); + if (rc) + class_destroy(dax_class); + return rc; } void __exit dax_bus_exit(void) { bus_unregister(&dax_bus_type); + class_destroy(dax_class); } diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h index 395ab812367c..ce977552ffb5 100644 --- a/drivers/dax/bus.h +++ b/drivers/dax/bus.h @@ -2,7 +2,8 @@ /* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ #ifndef __DAX_BUS_H__ #define __DAX_BUS_H__ -struct device; +#include + struct dev_dax; struct resource; struct dax_device; @@ -10,8 +11,23 @@ struct dax_region; void dax_region_put(struct dax_region *dax_region); struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, unsigned long flags); -struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, int id, - struct dev_pagemap *pgmap); + +enum dev_dax_subsys { + DEV_DAX_BUS, + DEV_DAX_CLASS, +}; + +struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, + struct dev_pagemap *pgmap, enum dev_dax_subsys subsys); + +static inline struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, + int id, struct dev_pagemap *pgmap) +{ + return __devm_create_dev_dax(dax_region, id, pgmap, DEV_DAX_BUS); +} + +/* to be deleted when DEV_DAX_CLASS is removed */ +struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys); struct dax_device_driver { struct device_driver drv; @@ -26,6 +42,10 @@ int __dax_driver_register(struct dax_device_driver *dax_drv, void dax_driver_unregister(struct dax_device_driver *dax_drv); void kill_dev_dax(struct dev_dax *dev_dax); +#if IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT) +int dev_dax_probe(struct device *dev); +#endif + /* * While run_dax() is potentially a generic operation that could be * defined in include/linux/dax.h we don't want to grow any users diff --git a/drivers/dax/device.c b/drivers/dax/device.c index ad3120395f7a..e428468ab661 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -445,7 +445,7 @@ static void dev_dax_kill(void *dev_dax) kill_dev_dax(dev_dax); } -static int dev_dax_probe(struct device *dev) +int dev_dax_probe(struct device *dev) { struct dev_dax *dev_dax = to_dev_dax(dev); struct dax_device *dax_dev = dev_dax->dax_dev; @@ -484,7 +484,11 @@ static int dev_dax_probe(struct device *dev) inode = dax_inode(dax_dev); cdev = inode->i_cdev; cdev_init(cdev, &dax_fops); - cdev->owner = dev->driver->owner; + if (dev->class) { + /* for the CONFIG_DEV_DAX_PMEM_COMPAT case */ + cdev->owner = dev->parent->driver->owner; + } else + cdev->owner = dev->driver->owner; cdev_set_parent(cdev, &dev->kobj); rc = cdev_add(cdev, dev->devt, 1); if (rc) @@ -497,6 +501,7 @@ static int dev_dax_probe(struct device *dev) run_dax(dax_dev); return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax); } +EXPORT_SYMBOL_GPL(dev_dax_probe); static int dev_dax_remove(struct device *dev) { diff --git a/drivers/dax/pmem/Makefile b/drivers/dax/pmem/Makefile new file mode 100644 index 000000000000..e2e79bd3fdcf --- /dev/null +++ b/drivers/dax/pmem/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o +obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o +obj-$(CONFIG_DEV_DAX_PMEM_COMPAT) += dax_pmem_compat.o + +dax_pmem-y := pmem.o +dax_pmem_core-y := core.o +dax_pmem_compat-y := compat.o diff --git a/drivers/dax/pmem/compat.c b/drivers/dax/pmem/compat.c new file mode 100644 index 000000000000..d7b15e6f30c5 --- /dev/null +++ b/drivers/dax/pmem/compat.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include +#include +#include "../bus.h" + +/* we need the private definitions to implement compat suport */ +#include "../dax-private.h" + +static int dax_pmem_compat_probe(struct device *dev) +{ + struct dev_dax *dev_dax = __dax_pmem_probe(dev, DEV_DAX_CLASS); + int rc; + + if (IS_ERR(dev_dax)) + return PTR_ERR(dev_dax); + + if (!devres_open_group(&dev_dax->dev, dev_dax, GFP_KERNEL)) + return -ENOMEM; + + device_lock(&dev_dax->dev); + rc = dev_dax_probe(&dev_dax->dev); + device_unlock(&dev_dax->dev); + + devres_close_group(&dev_dax->dev, dev_dax); + if (rc) + devres_release_group(&dev_dax->dev, dev_dax); + + return rc; +} + +static int dax_pmem_compat_release(struct device *dev, void *data) +{ + device_lock(dev); + devres_release_group(dev, to_dev_dax(dev)); + device_unlock(dev); + + return 0; +} + +static int dax_pmem_compat_remove(struct device *dev) +{ + device_for_each_child(dev, NULL, dax_pmem_compat_release); + return 0; +} + +static struct nd_device_driver dax_pmem_compat_driver = { + .probe = dax_pmem_compat_probe, + .remove = dax_pmem_compat_remove, + .drv = { + .name = "dax_pmem_compat", + }, + .type = ND_DRIVER_DAX_PMEM, +}; + +static int __init dax_pmem_compat_init(void) +{ + return nd_driver_register(&dax_pmem_compat_driver); +} +module_init(dax_pmem_compat_init); + +static void __exit dax_pmem_compat_exit(void) +{ + driver_unregister(&dax_pmem_compat_driver.drv); +} +module_exit(dax_pmem_compat_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM); diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem/core.c similarity index 57% rename from drivers/dax/pmem.c rename to drivers/dax/pmem/core.c index d3cefa7868ac..bdcff1b14e95 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem/core.c @@ -1,24 +1,13 @@ -/* - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#include +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ #include #include #include -#include "../nvdimm/pfn.h" -#include "../nvdimm/nd.h" -#include "bus.h" +#include "../../nvdimm/pfn.h" +#include "../../nvdimm/nd.h" +#include "../bus.h" -static int dax_pmem_probe(struct device *dev) +struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys) { struct resource res; int rc, id, region_id; @@ -34,16 +23,16 @@ static int dax_pmem_probe(struct device *dev) ndns = nvdimm_namespace_common_probe(dev); if (IS_ERR(ndns)) - return PTR_ERR(ndns); + return ERR_CAST(ndns); nsio = to_nd_namespace_io(&ndns->dev); /* parse the 'pfn' info block via ->rw_bytes */ rc = devm_nsio_enable(dev, nsio); if (rc) - return rc; + return ERR_PTR(rc); rc = nvdimm_setup_pfn(nd_pfn, &pgmap); if (rc) - return rc; + return ERR_PTR(rc); devm_nsio_disable(dev, nsio); /* reserve the metadata area, device-dax will reserve the data */ @@ -52,12 +41,12 @@ static int dax_pmem_probe(struct device *dev) if (!devm_request_mem_region(dev, nsio->res.start, offset, dev_name(&ndns->dev))) { dev_warn(dev, "could not reserve metadata\n"); - return -EBUSY; + return ERR_PTR(-EBUSY); } rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", ®ion_id, &id); if (rc != 2) - return -EINVAL; + return ERR_PTR(-EINVAL); /* adjust the dax_region resource to the start of data */ memcpy(&res, &pgmap.res, sizeof(res)); @@ -65,26 +54,16 @@ static int dax_pmem_probe(struct device *dev) dax_region = alloc_dax_region(dev, region_id, &res, le32_to_cpu(pfn_sb->align), PFN_DEV|PFN_MAP); if (!dax_region) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - dev_dax = devm_create_dev_dax(dax_region, id, &pgmap); + dev_dax = __devm_create_dev_dax(dax_region, id, &pgmap, subsys); /* child dev_dax instances now own the lifetime of the dax_region */ dax_region_put(dax_region); - return PTR_ERR_OR_ZERO(dev_dax); + return dev_dax; } - -static struct nd_device_driver dax_pmem_driver = { - .probe = dax_pmem_probe, - .drv = { - .name = "dax_pmem", - }, - .type = ND_DRIVER_DAX_PMEM, -}; - -module_nd_driver(dax_pmem_driver); +EXPORT_SYMBOL_GPL(__dax_pmem_probe); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Intel Corporation"); -MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM); diff --git a/drivers/dax/pmem/pmem.c b/drivers/dax/pmem/pmem.c new file mode 100644 index 000000000000..0ae4238a0ef8 --- /dev/null +++ b/drivers/dax/pmem/pmem.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include +#include +#include "../bus.h" + +static int dax_pmem_probe(struct device *dev) +{ + return PTR_ERR_OR_ZERO(__dax_pmem_probe(dev, DEV_DAX_BUS)); +} + +static struct nd_device_driver dax_pmem_driver = { + .probe = dax_pmem_probe, + .drv = { + .name = "dax_pmem", + }, + .type = ND_DRIVER_DAX_PMEM, +}; + +static int __init dax_pmem_init(void) +{ + return nd_driver_register(&dax_pmem_driver); +} +module_init(dax_pmem_init); + +static void __exit dax_pmem_exit(void) +{ + driver_unregister(&dax_pmem_driver.drv); +} +module_exit(dax_pmem_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Intel Corporation"); +#if !IS_ENABLED(CONFIG_DEV_DAX_PMEM_COMPAT) +/* For compat builds, don't load this module by default */ +MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM); +#endif diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index c9b500a652d5..e1286d2cdfbf 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -35,6 +35,8 @@ obj-$(CONFIG_DAX) += dax.o endif obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o +obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem_core.o +obj-$(CONFIG_DEV_DAX_PMEM_COMPAT) += dax_pmem_compat.o nfit-y := $(ACPI_SRC)/core.o nfit-y += $(ACPI_SRC)/intel.o @@ -65,7 +67,9 @@ device_dax-y += dax-dev.o device_dax-y += device_dax_test.o device_dax-y += config_check.o -dax_pmem-y := $(DAX_SRC)/pmem.o +dax_pmem-y := $(DAX_SRC)/pmem/pmem.o +dax_pmem_core-y := $(DAX_SRC)/pmem/core.o +dax_pmem_compat-y := $(DAX_SRC)/pmem/compat.o dax_pmem-y += config_check.o libnvdimm-y := $(NVDIMM_SRC)/core.o From 8fc5c73554db0ac18c0c6ac5b2099ab917f83bdf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 9 Nov 2018 12:43:07 -0800 Subject: [PATCH 0058/4277] acpi/nfit, device-dax: Identify differentiated memory with a unique numa-node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Persistent memory, as described by the ACPI NFIT (NVDIMM Firmware Interface Table), is the first known instance of a memory range described by a unique "target" proximity domain. Where "initiator" and "target" proximity domains is an approach that the ACPI HMAT (Heterogeneous Memory Attributes Table) uses to described the unique performance properties of a memory range relative to a given initiator (e.g. CPU or DMA device). Currently the numa-node for a /dev/pmemX block-device or /dev/daxX.Y char-device follows the traditional notion of 'numa-node' where the attribute conveys the closest online numa-node. That numa-node attribute is useful for cpu-binding and memory-binding processes *near* the device. However, when the memory range backing a 'pmem', or 'dax' device is onlined (memory hot-add) the memory-only-numa-node representing that address needs to be differentiated from the set of online nodes. In other words, the numa-node association of the device depends on whether you can bind processes *near* the cpu-numa-node in the offline device-case, or bind process *on* the memory-range directly after the backing address range is onlined. Allow for the case that platform firmware describes persistent memory with a unique proximity domain, i.e. when it is distinct from the proximity of DRAM and CPUs that are on the same socket. Plumb the Linux numa-node translation of that proximity through the libnvdimm region device to namespaces that are in device-dax mode. With this in place the proposed kmem driver [1] can optionally discover a unique numa-node number for the address range as it transitions the memory from an offline state managed by a device-driver to an online memory range managed by the core-mm. [1]: https://lore.kernel.org/lkml/20181022201317.8558C1D8@viggo.jf.intel.com Reported-by: Fan Du Cc: Michael Ellerman Cc: "Oliver O'Halloran" Cc: Dave Hansen Cc: Jérôme Glisse Reviewed-by: Yang Shi Signed-off-by: Dan Williams --- arch/powerpc/platforms/pseries/papr_scm.c | 1 + drivers/acpi/nfit/core.c | 8 ++++++-- drivers/acpi/numa.c | 1 + drivers/dax/bus.c | 4 +++- drivers/dax/bus.h | 3 ++- drivers/dax/dax-private.h | 4 ++++ drivers/dax/pmem/core.c | 4 +++- drivers/nvdimm/e820.c | 1 + drivers/nvdimm/nd.h | 2 +- drivers/nvdimm/of_pmem.c | 1 + drivers/nvdimm/region_devs.c | 1 + include/linux/acpi.h | 5 +++++ include/linux/libnvdimm.h | 1 + 13 files changed, 30 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 7d6457ab5d34..8806ac822627 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -236,6 +236,7 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p) memset(&ndr_desc, 0, sizeof(ndr_desc)); ndr_desc.attr_groups = region_attr_groups; ndr_desc.numa_node = dev_to_node(&p->pdev->dev); + ndr_desc.target_node = ndr_desc.numa_node; ndr_desc.res = &p->res; ndr_desc.of_node = p->dn; ndr_desc.provider_data = p; diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 011d3db19c80..475899974c70 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2869,11 +2869,15 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, ndr_desc->res = &res; ndr_desc->provider_data = nfit_spa; ndr_desc->attr_groups = acpi_nfit_region_attribute_groups; - if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) + if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) { ndr_desc->numa_node = acpi_map_pxm_to_online_node( spa->proximity_domain); - else + ndr_desc->target_node = acpi_map_pxm_to_node( + spa->proximity_domain); + } else { ndr_desc->numa_node = NUMA_NO_NODE; + ndr_desc->target_node = NUMA_NO_NODE; + } /* * Persistence domain bits are hierarchical, if diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 274699463b4f..b9d86babb13a 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -84,6 +84,7 @@ int acpi_map_pxm_to_node(int pxm) return node; } +EXPORT_SYMBOL(acpi_map_pxm_to_node); /** * acpi_map_pxm_to_online_node - Map proximity ID to online node diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 568168500217..c620ad52d7e5 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -214,7 +214,7 @@ static void dax_region_unregister(void *region) } struct dax_region *alloc_dax_region(struct device *parent, int region_id, - struct resource *res, unsigned int align, + struct resource *res, int target_node, unsigned int align, unsigned long pfn_flags) { struct dax_region *dax_region; @@ -244,6 +244,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, dax_region->id = region_id; dax_region->align = align; dax_region->dev = parent; + dax_region->target_node = target_node; if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { kfree(dax_region); return NULL; @@ -348,6 +349,7 @@ struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, dev_dax->dax_dev = dax_dev; dev_dax->region = dax_region; + dev_dax->target_node = dax_region->target_node; kref_get(&dax_region->kref); inode = dax_inode(dax_dev); diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h index ce977552ffb5..8619e3299943 100644 --- a/drivers/dax/bus.h +++ b/drivers/dax/bus.h @@ -10,7 +10,8 @@ struct dax_device; struct dax_region; void dax_region_put(struct dax_region *dax_region); struct dax_region *alloc_dax_region(struct device *parent, int region_id, - struct resource *res, unsigned int align, unsigned long flags); + struct resource *res, int target_node, unsigned int align, + unsigned long flags); enum dev_dax_subsys { DEV_DAX_BUS, diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index a82ce48f5884..a45612148ca0 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -26,6 +26,7 @@ void dax_bus_exit(void); /** * struct dax_region - mapping infrastructure for dax devices * @id: kernel-wide unique region for a memory range + * @target_node: effective numa node if this memory range is onlined * @kref: to pin while other agents have a need to do lookups * @dev: parent device backing this region * @align: allocation and mapping alignment for child dax devices @@ -34,6 +35,7 @@ void dax_bus_exit(void); */ struct dax_region { int id; + int target_node; struct kref kref; struct device *dev; unsigned int align; @@ -46,6 +48,7 @@ struct dax_region { * data while the device is activated in the driver. * @region - parent region * @dax_dev - core dax functionality + * @target_node: effective numa node if dev_dax memory range is onlined * @dev - device core * @pgmap - pgmap for memmap setup / lifetime (driver owned) * @ref: pgmap reference count (driver owned) @@ -54,6 +57,7 @@ struct dax_region { struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; + int target_node; struct device dev; struct dev_pagemap pgmap; struct percpu_ref ref; diff --git a/drivers/dax/pmem/core.c b/drivers/dax/pmem/core.c index bdcff1b14e95..f71019ce0647 100644 --- a/drivers/dax/pmem/core.c +++ b/drivers/dax/pmem/core.c @@ -20,6 +20,7 @@ struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys) struct nd_namespace_common *ndns; struct nd_dax *nd_dax = to_nd_dax(dev); struct nd_pfn *nd_pfn = &nd_dax->nd_pfn; + struct nd_region *nd_region = to_nd_region(dev->parent); ndns = nvdimm_namespace_common_probe(dev); if (IS_ERR(ndns)) @@ -52,7 +53,8 @@ struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys) memcpy(&res, &pgmap.res, sizeof(res)); res.start += offset; dax_region = alloc_dax_region(dev, region_id, &res, - le32_to_cpu(pfn_sb->align), PFN_DEV|PFN_MAP); + nd_region->target_node, le32_to_cpu(pfn_sb->align), + PFN_DEV|PFN_MAP); if (!dax_region) return ERR_PTR(-ENOMEM); diff --git a/drivers/nvdimm/e820.c b/drivers/nvdimm/e820.c index 521eaf53a52a..36be9b619187 100644 --- a/drivers/nvdimm/e820.c +++ b/drivers/nvdimm/e820.c @@ -47,6 +47,7 @@ static int e820_register_one(struct resource *res, void *data) ndr_desc.res = res; ndr_desc.attr_groups = e820_pmem_region_attribute_groups; ndr_desc.numa_node = e820_range_to_nid(res->start); + ndr_desc.target_node = ndr_desc.numa_node; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc)) return -ENXIO; diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index cfde992684e7..0b3d7595b3cb 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -153,7 +153,7 @@ struct nd_region { u16 ndr_mappings; u64 ndr_size; u64 ndr_start; - int id, num_lanes, ro, numa_node; + int id, num_lanes, ro, numa_node, target_node; void *provider_data; struct kernfs_node *bb_state; struct badblocks bb; diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c index 0a701837dfc0..ecaaa27438e2 100644 --- a/drivers/nvdimm/of_pmem.c +++ b/drivers/nvdimm/of_pmem.c @@ -68,6 +68,7 @@ static int of_pmem_region_probe(struct platform_device *pdev) memset(&ndr_desc, 0, sizeof(ndr_desc)); ndr_desc.attr_groups = region_attr_groups; ndr_desc.numa_node = dev_to_node(&pdev->dev); + ndr_desc.target_node = ndr_desc.numa_node; ndr_desc.res = &pdev->resource[i]; ndr_desc.of_node = np; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index e2818f94f292..caf2f3129ccd 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -1065,6 +1065,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, nd_region->flags = ndr_desc->flags; nd_region->ro = ro; nd_region->numa_node = ndr_desc->numa_node; + nd_region->target_node = ndr_desc->target_node; ida_init(&nd_region->ns_ida); ida_init(&nd_region->btt_ida); ida_init(&nd_region->pfn_ida); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 87715f20b69a..eddf2736e5a6 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -400,12 +400,17 @@ extern bool acpi_osi_is_win8(void); #ifdef CONFIG_ACPI_NUMA int acpi_map_pxm_to_online_node(int pxm); +int acpi_map_pxm_to_node(int pxm); int acpi_get_node(acpi_handle handle); #else static inline int acpi_map_pxm_to_online_node(int pxm) { return 0; } +static inline int acpi_map_pxm_to_node(int pxm) +{ + return 0; +} static inline int acpi_get_node(acpi_handle handle) { return 0; diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 5440f11b0907..56bc545ad3b2 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -128,6 +128,7 @@ struct nd_region_desc { void *provider_data; int num_lanes; int numa_node; + int target_node; unsigned long flags; struct device_node *of_node; }; From 60042a22be4e8e565db0a82acb84ee4b348b17c9 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Wed, 7 Nov 2018 23:26:01 +0800 Subject: [PATCH 0059/4277] remoteproc: debug: add crash debugfs node so we can trigger the crash manully which could: 1.test the crash handling code path more easily 2.update the firmware without reboot kernel Signed-off-by: Xiang Xiao Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_debugfs.c | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index e90135c64af0..f330a9ad014f 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -155,6 +155,30 @@ static const struct file_operations rproc_recovery_ops = { .llseek = generic_file_llseek, }; +/* expose the crash trigger via debugfs */ +static ssize_t +rproc_crash_write(struct file *filp, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + unsigned int type; + int ret; + + ret = kstrtouint_from_user(user_buf, count, 0, &type); + if (ret < 0) + return ret; + + rproc_report_crash(rproc, type); + + return count; +} + +static const struct file_operations rproc_crash_ops = { + .write = rproc_crash_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + /* Expose resource table content via debugfs */ static int rproc_rsc_table_show(struct seq_file *seq, void *p) { @@ -325,6 +349,8 @@ void rproc_create_debug_dir(struct rproc *rproc) rproc, &rproc_name_ops); debugfs_create_file("recovery", 0400, rproc->dbg_dir, rproc, &rproc_recovery_ops); + debugfs_create_file("crash", 0200, rproc->dbg_dir, + rproc, &rproc_crash_ops); debugfs_create_file("resource_table", 0400, rproc->dbg_dir, rproc, &rproc_rsc_table_ops); debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, From ebc40be2b8eec093abbbd87658a6726ff84a61f5 Mon Sep 17 00:00:00 2001 From: Fabien Dessenne Date: Wed, 7 Nov 2018 11:18:34 +0100 Subject: [PATCH 0060/4277] remoteproc: fix kernel-doc comment for parse_fw Fix the kernel-doc comment for "parse_fw" and fix a typo. Signed-off-by: Fabien Dessenne Signed-off-by: Bjorn Andersson --- include/linux/remoteproc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 507a2b524208..68e72f33c705 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -345,9 +345,9 @@ struct firmware; * @stop: power off the device * @kick: kick a virtqueue (virtqueue id given as a parameter) * @da_to_va: optional platform hook to perform address translations - * @load_rsc_table: load resource table from firmware image + * @parse_fw: parse firmware to extract information (e.g. resource table) * @find_loaded_rsc_table: find the loaded resouce table - * @load: load firmeware to memory, where the remote processor + * @load: load firmware to memory, where the remote processor * expects to find it * @sanity_check: sanity check the fw image * @get_boot_addr: get boot address to entry point specified in firmware From fff7fca51d400e67403efacf25612d8690e939c1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 26 Nov 2018 14:27:35 +0000 Subject: [PATCH 0061/4277] remoteproc: qcom: fix spelling mistake "Peripherial" -> "Peripheral" There is a spelling mistake in the module description text, fix it. Signed-off-by: Colin Ian King Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_wcnss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index b0e07e9f42d5..1152da49a18a 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -622,5 +622,5 @@ static void __exit wcnss_exit(void) } module_exit(wcnss_exit); -MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem"); +MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Wireless Subsystem"); MODULE_LICENSE("GPL v2"); From 2808cd5e798d22e3f25ddfaddcde9b507c840d1e Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 29 Dec 2018 00:23:01 +0530 Subject: [PATCH 0062/4277] dt-bindings: remoteproc: qcom: Add missing clocks for SDM845 Add missing clock bindings for Q6V5 MSS on SDM845 SoCs. Fixes: fb22022ff63d ("dt-bindings: remoteproc: Add Q6v5 Modem PIL binding for SDM845") Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/remoteproc/qcom,q6v5.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 9ff5b0309417..d645c8db29fd 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -39,13 +39,21 @@ on the Qualcomm Hexagon core. - clocks: Usage: required Value type: - Definition: reference to the iface, bus and mem clocks to be held on - behalf of the booting of the Hexagon core + Definition: reference to the clocks that match clock-names - clock-names: Usage: required Value type: - Definition: must be "iface", "bus", "mem" + Definition: The clocks needed depend on the compatible string: + qcom,ipq8074-wcss-pil: + no clock names required + qcom,q6v5-pil: + qcom,msm8916-mss-pil: + qcom,msm8974-mss-pil: + must be "iface", "bus", "mem", "xo" + qcom,sdm845-mss-pil: + must be "iface", "bus", "mem", "xo", "gpll0_mss", + "snoc_axi", "mnoc_axi", "prng" - resets: Usage: required From 0f97dbbc1f3498f8e672aedffbec69b113490369 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 29 Dec 2018 00:23:02 +0530 Subject: [PATCH 0063/4277] dt-bindings: remoteproc: qcom: Add missing clocks for MSM8996 Add missing clock bindings for Q6V5 MSS on MSM8996 SoCs. Fixes: 9f058fa2efb1 ("remoteproc: qcom: Add support for mss remoteproc on msm8996") Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index d645c8db29fd..bba3d6be71c0 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -51,6 +51,9 @@ on the Qualcomm Hexagon core. qcom,msm8916-mss-pil: qcom,msm8974-mss-pil: must be "iface", "bus", "mem", "xo" + qcom,msm8996-mss-pil: + must be "iface", "bus", "mem", "xo", "gpll0_mss", + "snoc_axi", "mnoc_axi", "pnoc", "qdss" qcom,sdm845-mss-pil: must be "iface", "bus", "mem", "xo", "gpll0_mss", "snoc_axi", "mnoc_axi", "prng" From 80ec419c3404106c563aaf56aa6b516a59c4cdfb Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 29 Dec 2018 00:23:03 +0530 Subject: [PATCH 0064/4277] remoteproc: qcom: q6v5-mss: Add missing clocks for MSM8996 Proxy vote for QDSS clock and remove vote on handover interrupt to provide MSS PBL with access to STM hardware registers during boot. Add "snoc_axi" and "mnoc_axi" to the active clock list. Rename "gpll0_mss_clk" to "gpll0_mss" for consistency across SoCs. Fixes: 9f058fa2efb1 ("remoteproc: qcom: Add support for mss remoteproc on msm8996") Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 01be7314e176..cb5f0d3ac6a3 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1398,13 +1398,16 @@ static const struct rproc_hexagon_res msm8996_mss = { .proxy_clk_names = (char*[]){ "xo", "pnoc", + "qdss", NULL }, .active_clk_names = (char*[]){ "iface", "bus", "mem", - "gpll0_mss_clk", + "gpll0_mss", + "snoc_axi", + "mnoc_axi", NULL }, .need_mem_protection = true, From 688c4375b853759120169e220a0ef648ad9f0970 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 29 Dec 2018 00:23:04 +0530 Subject: [PATCH 0065/4277] dt-bindings: remoteproc: qcom: Fixup regulator dependencies Fixup regulator supply dependencies for Q6V5 MSS on MSM996 SoCs. Fixes: 9f058fa2efb1 ("remoteproc: qcom: Add support for mss remoteproc on msm8996") Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- .../bindings/remoteproc/qcom,q6v5.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index bba3d6be71c0..9db371da4897 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -76,6 +76,19 @@ on the Qualcomm Hexagon core. must be "mss_restart", "pdc_reset" for the modem sub-system on SDM845 SoCs +For the compatible strings below the following supplies are required: + "qcom,q6v5-pil" + "qcom,msm8916-mss-pil", +- cx-supply: +- mx-supply: +- pll-supply: + Usage: required + Value type: + Definition: reference to the regulators to be held on behalf of the + booting of the Hexagon core + +For the compatible string below the following supplies are required: + "qcom,msm8974-mss-pil" - cx-supply: - mss-supply: - mx-supply: @@ -85,6 +98,14 @@ on the Qualcomm Hexagon core. Definition: reference to the regulators to be held on behalf of the booting of the Hexagon core +For the compatible string below the following supplies are required: + "qcom,msm8996-mss-pil" +- pll-supply: + Usage: required + Value type: + Definition: reference to the regulators to be held on behalf of the + booting of the Hexagon core + - qcom,smem-states: Usage: required Value type: From 47b874748d500020026ee43b386b5598e20f3a68 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 29 Dec 2018 00:23:05 +0530 Subject: [PATCH 0066/4277] remoteproc: qcom: q6v5-mss: Add missing regulator for MSM8996 Add proxy vote for pll supply on MSM8996 SoC. Fixes: 9f058fa2efb1 ("remoteproc: qcom: Add support for mss remoteproc on msm8996") Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index cb5f0d3ac6a3..c86dc40cfb8c 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1395,6 +1395,13 @@ static const struct rproc_hexagon_res sdm845_mss = { static const struct rproc_hexagon_res msm8996_mss = { .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, .proxy_clk_names = (char*[]){ "xo", "pnoc", From f2951bb0022526e2c8884e4c5dfc50087d799eb9 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 29 Dec 2018 00:23:06 +0530 Subject: [PATCH 0067/4277] dt-bindings: remoteproc: qcom: Add power-domain bindings for Q6V5 Add power-domain bindings for Q6V5 MSS on MSM8996 and SDM845 SoCs. Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- .../bindings/remoteproc/qcom,q6v5.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 9db371da4897..36e91c9d76e0 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -106,6 +106,25 @@ For the compatible string below the following supplies are required: Definition: reference to the regulators to be held on behalf of the booting of the Hexagon core +- power-domains: + Usage: required + Value type: + Definition: reference to power-domains that match power-domain-names + +- power-domain-names: + Usage: required + Value type: + Definition: The power-domains needed depend on the compatible string: + qcom,q6v5-pil: + qcom,ipq8074-wcss-pil: + qcom,msm8916-mss-pil: + qcom,msm8974-mss-pil: + no power-domain names required + qcom,msm8996-mss-pil: + must be "cx", "mx" + qcom,sdm845-mss-pil: + must be "cx", "mx", "mss", "load_state" + - qcom,smem-states: Usage: required Value type: From a5cb1ee8f241e6595ef1538b3572b98ee3fe6396 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Fri, 28 Dec 2018 10:18:18 +0530 Subject: [PATCH 0068/4277] dt-bindings: remoteproc: qcom: Add firmware bindings for Q6V5 Add optional "firmware-name" bindings for Q6V5 MSS and PAS based remoteprocs. For Q6V5 MSS/PAS the two/one relative firmware paths/path are to be listed respectively. Fallback to the default images for mba/modem for Q6V5 MSS or the default Hexagon image for Q6V5 PAS if the "firmware-name" binding is not present. Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt | 6 ++++++ Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt index 9c0cff3a5ed8..60ee0f73071a 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt @@ -27,6 +27,12 @@ on the Qualcomm ADSP Hexagon core. Value type: Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" +- firmware-name: + Usage: optional + Value type: + Definition: must list the relative firmware image path for the + Hexagon Core. + - clocks: Usage: required Value type: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 36e91c9d76e0..401e49ebee39 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -36,6 +36,13 @@ on the Qualcomm Hexagon core. Value type: Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" +- firmware-name: + Usage: optional + Value type: + Definition: must list the relative firmware image paths for mba and + modem. They are used for booting and authenticating the + Hexagon core. + - clocks: Usage: required Value type: From a0e447b0c50240a90ab84b7126b3c06b0bab4adc Mon Sep 17 00:00:00 2001 From: Katsuhiro Suzuki Date: Sun, 23 Dec 2018 01:42:49 +0900 Subject: [PATCH 0069/4277] clk: rockchip: fix frac settings of GPLL clock for rk3328 This patch fixes settings of GPLL frequency in fractional mode for rk3328. In this mode, FOUTVCO is calcurated by following formula: FOUTVCO = FREF * FBDIV / REFDIV + ((FREF * FRAC / REFDIV) >> 24) The problem is in FREF * FRAC >> 24 term. This result always lacks one from target value is specified by rate member. For example first itme of rk3328_pll_frac_rate originally has - rate : 1016064000 - refdiv: 3 - fbdiv : 127 - frac : 134217 - FREF * FBDIV / REFDIV = 1016000000 - (FREF * FRAC / REFDIV) >> 24 = 63999 Thus calculated rate is 1016063999. It seems wrong. If frac has 134218 (it is increased 1 from original value), second term is 64000. All other items have same situation. So this patch adds 1 to frac member in all items of rk3328_pll_frac_rate. Signed-off-by: Katsuhiro Suzuki Acked-by: Elaine Zhang Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index faa94adb2a37..65ab5c2f48b0 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -78,17 +78,17 @@ static struct rockchip_pll_rate_table rk3328_pll_rates[] = { static struct rockchip_pll_rate_table rk3328_pll_frac_rates[] = { /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ - RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134217), + RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134218), /* vco = 1016064000 */ - RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671088), + RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671089), /* vco = 983040000 */ - RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671088), + RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671089), /* vco = 983040000 */ - RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671088), + RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671089), /* vco = 860156000 */ - RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797894), + RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797895), /* vco = 903168000 */ - RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066329), + RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066330), /* vco = 819200000 */ { /* sentinel */ }, }; From 491b00ff699356a8dab10eb517a1b44205514c9e Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Sat, 29 Dec 2018 14:33:13 +0100 Subject: [PATCH 0070/4277] clk: rockchip: add CLK_SET_RATE_PARENT for rk3066 lcdc dclks Add CLK_SET_RATE_PARENT for both rk3066 lcdc dclk. Signed-off-by: Finley Xiao Signed-off-by: Johan Jonker Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3188.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 7ea20341e870..5ecf28854876 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -586,12 +586,12 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { COMPOSITE(0, "dclk_lcdc0_src", mux_pll_src_cpll_gpll_p, 0, RK2928_CLKSEL_CON(27), 0, 1, MFLAGS, 8, 8, DFLAGS, RK2928_CLKGATE_CON(3), 1, GFLAGS), - MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, 0, + MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, CLK_SET_RATE_PARENT, RK2928_CLKSEL_CON(27), 4, 1, MFLAGS), COMPOSITE(0, "dclk_lcdc1_src", mux_pll_src_cpll_gpll_p, 0, RK2928_CLKSEL_CON(28), 0, 1, MFLAGS, 8, 8, DFLAGS, RK2928_CLKGATE_CON(3), 2, GFLAGS), - MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, 0, + MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, CLK_SET_RATE_PARENT, RK2928_CLKSEL_CON(28), 4, 1, MFLAGS), COMPOSITE_NOMUX(0, "cif1_pre", "cif_src", 0, From 87fe9ae84d7ba61df1f272e26df174e73b15cd74 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:35 +0200 Subject: [PATCH 0071/4277] dmaengine: dw: Add missed multi-block support for iDMA 32-bit Intel integrated DMA 32-bit support multi-block transfers. Add missed setting to the platform data. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 7778ed705a1a..313ba10c6224 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -25,6 +25,7 @@ static struct dw_dma_platform_data mrfld_pdata = { .block_size = 131071, .nr_masters = 1, .data_width = {4}, + .multi_block = {1, 1, 1, 1, 1, 1, 1, 1}, }; static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) From d7dba6be0f31ae61f5f3296aa130f45d57d30f74 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:36 +0200 Subject: [PATCH 0072/4277] dmaengine: dw: Remove misleading is_private property The commit a9ddb575d6d6 ("dmaengine: dw_dmac: Enhance device tree support") introduces is_private property in uncertain understanding what does it mean. First of all, documentation defines DMA_PRIVATE capability as Documentation/crypto/async-tx-api.txt: The DMA_PRIVATE capability flag is used to tag dma devices that should not be used by the general-purpose allocator. It can be set at initialization time if it is known that a channel will always be private. Alternatively, it is set when dma_request_channel() finds an unused "public" channel. A couple caveats to note when implementing a driver and consumer: 1/ Once a channel has been privately allocated it will no longer be considered by the general-purpose allocator even after a call to dma_release_channel(). 2/ Since capabilities are specified at the device level a dma_device with multiple channels will either have all channels public, or all channels private. Documentation/driver-api/dmaengine/provider.rst: - DMA_PRIVATE The devices only supports slave transfers, and as such isn't available for async transfers. The capability had been introduced by the commit 59b5ec21446b ("dmaengine: introduce dma_request_channel and private channels") and some code didn't changed from that times ever. Taking into consideration above and the fact that on all known platforms Synopsys DesignWare DMA engine is attached to serve slave transfers, the DMA_PRIVATE capability must be enabled for this device unconditionally. Otherwise, as rightfully noticed in drivers/dma/at_xdmac.c: /* * Without DMA_PRIVATE the driver is not able to allocate more than * one channel, second allocation fails in private_candidate. */ because of of a caveats mentioned in above documentation excerpts. So, remove conditional around DMA_PRIVATE followed by removal leftovers. If someone wonders, DMA_PRIVATE can be not used if and only if the all channels of the DMA controller are supposed to serve memory-to-memory like operations. For example, EP93xx has two controllers, one of which can only perform memory-to-memory transfers Note, this change doesn't affect dmatest to be able to test such controllers. Cc: Greg Kroah-Hartman (maintainer:SERIAL DRIVERS) Cc: Dan Williams Signed-off-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps-dma.txt | 2 -- drivers/dma/dw/core.c | 4 +--- drivers/dma/dw/pci.c | 1 - drivers/dma/dw/platform.c | 3 --- drivers/tty/serial/8250/8250_lpss.c | 1 - include/linux/platform_data/dma-dw.h | 3 --- 6 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index db757df7057d..0bedceed1963 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -23,8 +23,6 @@ Deprecated properties: Optional properties: -- is_private: The device channels should be marked as private and not for by the - general purpose DMA channel allocator. False if not passed. - multi-block: Multi block transfers supported by hardware. Array property with one cell per channel. 0: not supported, 1 (default): supported. - snps,dma-protection-control: AHB HPROT[3:1] protection setting. diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index dc053e62f894..e25503986680 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1227,7 +1227,6 @@ int dw_dma_probe(struct dw_dma_chip *chip) pdata->block_size = dma_readl(dw, MAX_BLK_SIZE); /* Fill platform data with the default values */ - pdata->is_private = true; pdata->is_memcpy = true; pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; pdata->chan_priority = CHAN_PRIORITY_ASCENDING; @@ -1340,8 +1339,7 @@ int dw_dma_probe(struct dw_dma_chip *chip) /* Set capabilities */ dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); - if (pdata->is_private) - dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); + dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); if (pdata->is_memcpy) dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 313ba10c6224..570498faadc3 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -17,7 +17,6 @@ static struct dw_dma_platform_data mrfld_pdata = { .nr_channels = 8, - .is_private = true, .is_memcpy = true, .is_idma32 = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 31ff8113c3de..6dd8cd1820c1 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -128,9 +128,6 @@ dw_dma_parse_dt(struct platform_device *pdev) pdata->nr_masters = nr_masters; pdata->nr_channels = nr_channels; - if (of_property_read_bool(np, "is_private")) - pdata->is_private = true; - /* * All known devices, which use DT for configuration, support * memory-to-memory transfers. So enable it by default. diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 98dbc796353f..53ca9ba6ab4b 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -153,7 +153,6 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) #ifdef CONFIG_SERIAL_8250_DMA static const struct dw_dma_platform_data qrk_serial_dma_pdata = { .nr_channels = 2, - .is_private = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, .block_size = 4095, diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 1a1d58ebffbf..d443025c5c72 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -38,8 +38,6 @@ struct dw_dma_slave { /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) - * @is_private: The device channels should be marked as private and not for - * by the general purpose DMA channel allocator. * @is_memcpy: The device channels do support memory-to-memory transfers. * @is_idma32: The type of the DMA controller is iDMA32 * @chan_allocation_order: Allocate channels starting from 0 or 7 @@ -53,7 +51,6 @@ struct dw_dma_slave { */ struct dw_dma_platform_data { unsigned int nr_channels; - bool is_private; bool is_memcpy; bool is_idma32; #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ From 078165779608873e7b6eae1316a39c73af9f3edc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:37 +0200 Subject: [PATCH 0073/4277] dmaengine: dw: Remove unused internal property All known devices, which use DT for configuration, support memory-to-memory transfers. So enable it by default. The rest two cases, i.e. Intel Quark and PPC460ex, instantiate DMA driver and use its channels exclusively for hardware, which means there is no available channel for any other purposes anyway. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 4 +--- drivers/dma/dw/pci.c | 1 - drivers/dma/dw/platform.c | 6 ------ include/linux/platform_data/dma-dw.h | 2 -- 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index e25503986680..4982e443869c 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1227,7 +1227,6 @@ int dw_dma_probe(struct dw_dma_chip *chip) pdata->block_size = dma_readl(dw, MAX_BLK_SIZE); /* Fill platform data with the default values */ - pdata->is_memcpy = true; pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; pdata->chan_priority = CHAN_PRIORITY_ASCENDING; } else if (chip->pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) { @@ -1340,8 +1339,7 @@ int dw_dma_probe(struct dw_dma_chip *chip) /* Set capabilities */ dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); - if (pdata->is_memcpy) - dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); dw->dma.dev = chip->dev; dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 570498faadc3..66d98d7ccad0 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -17,7 +17,6 @@ static struct dw_dma_platform_data mrfld_pdata = { .nr_channels = 8, - .is_memcpy = true, .is_idma32 = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 6dd8cd1820c1..58fc1ba02a1e 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -128,12 +128,6 @@ dw_dma_parse_dt(struct platform_device *pdev) pdata->nr_masters = nr_masters; pdata->nr_channels = nr_channels; - /* - * All known devices, which use DT for configuration, support - * memory-to-memory transfers. So enable it by default. - */ - pdata->is_memcpy = true; - if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) pdata->chan_allocation_order = (unsigned char)tmp; diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index d443025c5c72..1c85eeee4171 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -38,7 +38,6 @@ struct dw_dma_slave { /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) - * @is_memcpy: The device channels do support memory-to-memory transfers. * @is_idma32: The type of the DMA controller is iDMA32 * @chan_allocation_order: Allocate channels starting from 0 or 7 * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. @@ -51,7 +50,6 @@ struct dw_dma_slave { */ struct dw_dma_platform_data { unsigned int nr_channels; - bool is_memcpy; bool is_idma32; #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ From 69da8be90d5e85e60b5377c47384154b9dabf592 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:38 +0200 Subject: [PATCH 0074/4277] dmaengine: dw: Split DW and iDMA 32-bit operations Here is a kinda big refactoring that should have been done in the first place, when Intel iDMA 32-bit support appeared. It splits operations which are different to Synopsys DesignWare and Intel iDMA 32-bit controllers. No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/Makefile | 2 +- drivers/dma/dw/core.c | 190 +++++---------------------- drivers/dma/dw/dw.c | 112 ++++++++++++++++ drivers/dma/dw/idma32.c | 138 +++++++++++++++++++ drivers/dma/dw/internal.h | 10 +- drivers/dma/dw/pci.c | 45 ++++--- drivers/dma/dw/platform.c | 8 +- drivers/dma/dw/regs.h | 13 ++ include/linux/dma/dw.h | 4 + include/linux/platform_data/dma-dw.h | 2 - 10 files changed, 343 insertions(+), 181 deletions(-) create mode 100644 drivers/dma/dw/dw.c create mode 100644 drivers/dma/dw/idma32.c diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index 2b949c2e4504..63ed895c09aa 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o -dw_dmac_core-objs := core.o +dw_dmac_core-objs := core.o dw.o idma32.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o dw_dmac-objs := platform.o diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 4982e443869c..8a581d86ea8d 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -138,44 +138,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) dwc->descs_allocated--; } -static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc) -{ - u32 cfghi = 0; - u32 cfglo = 0; - - /* Set default burst alignment */ - cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; - - /* Low 4 bits of the request lines */ - cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); - cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); - - /* Request line extension (2 bits) */ - cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); - cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); -} - -static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 cfghi = DWC_CFGH_FIFO_MODE; - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); - bool hs_polarity = dwc->dws.hs_polarity; - - cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); - cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); - cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl); - - /* Set polarity of handshake interface */ - cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); -} - static void dwc_initialize(struct dw_dma_chan *dwc) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); @@ -183,10 +145,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc) if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) return; - if (dw->pdata->is_idma32) - dwc_initialize_chan_idma32(dwc); - else - dwc_initialize_chan_dw(dwc); + dw->initialize_chan(dwc); /* Enable interrupts */ channel_set_bit(dw, MASK.XFER, dwc->mask); @@ -215,37 +174,6 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) cpu_relax(); } -static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes, - unsigned int width, size_t *len) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 block; - - /* Always in bytes for iDMA 32-bit */ - if (dw->pdata->is_idma32) - width = 0; - - if ((bytes >> width) > dwc->block_size) { - block = dwc->block_size; - *len = block << width; - } else { - block = bytes >> width; - *len = bytes; - } - - return block; -} - -static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - - if (dw->pdata->is_idma32) - return IDMA32C_CTLH_BLOCK_TS(block); - - return DWC_CTLH_BLOCK_TS(block) << width; -} - /*----------------------------------------------------------------------*/ /* Perform single block transfer */ @@ -391,10 +319,11 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) /* Returns how many bytes were already received from source */ static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) { + struct dw_dma *dw = to_dw_dma(dwc->chan.device); u32 ctlhi = channel_readl(dwc, CTL_HI); u32 ctllo = channel_readl(dwc, CTL_LO); - return block2bytes(dwc, ctlhi, ctllo >> 4 & 7); + return dw->block2bytes(dwc, ctlhi, ctllo >> 4 & 7); } static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) @@ -651,7 +580,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, unsigned int src_width; unsigned int dst_width; unsigned int data_width = dw->pdata->data_width[m_master]; - u32 ctllo; + u32 ctllo, ctlhi; u8 lms = DWC_LLP_LMS(m_master); dev_vdbg(chan2dev(chan), @@ -680,10 +609,12 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len - offset, src_width, &xfer_count); + lli_write(desc, sar, src + offset); lli_write(desc, dar, dest + offset); lli_write(desc, ctllo, ctllo); - lli_write(desc, ctlhi, bytes2block(dwc, len - offset, src_width, &xfer_count)); + lli_write(desc, ctlhi, ctlhi); desc->len = xfer_count; if (!first) { @@ -721,7 +652,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; - u32 ctllo; + u32 ctllo, ctlhi; u8 m_master = dwc->dws.m_master; u8 lms = DWC_LLP_LMS(m_master); dma_addr_t reg; @@ -768,9 +699,11 @@ slave_sg_todev_fill_desc: if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len, mem_width, &dlen); + lli_write(desc, sar, mem); lli_write(desc, dar, reg); - lli_write(desc, ctlhi, bytes2block(dwc, len, mem_width, &dlen)); + lli_write(desc, ctlhi, ctlhi); lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width)); desc->len = dlen; @@ -814,9 +747,11 @@ slave_sg_fromdev_fill_desc: if (!desc) goto err_desc_get; + ctlhi = dw->bytes2block(dwc, len, reg_width, &dlen); + lli_write(desc, sar, reg); lli_write(desc, dar, mem); - lli_write(desc, ctlhi, bytes2block(dwc, len, reg_width, &dlen)); + lli_write(desc, ctlhi, ctlhi); mem_width = __ffs(data_width | mem | dlen); lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width)); desc->len = dlen; @@ -876,22 +811,12 @@ EXPORT_SYMBOL_GPL(dw_dma_filter); static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dma_slave_config *sc = &dwc->dma_sconfig; struct dw_dma *dw = to_dw_dma(chan->device); - /* - * Fix sconfig's burst size according to dw_dmac. We need to convert - * them as: - * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. - * - * NOTE: burst size 2 is not supported by DesignWare controller. - * iDMA 32-bit supports it. - */ - u32 s = dw->pdata->is_idma32 ? 1 : 2; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); - sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0; - sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0; + dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst); + dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst); return 0; } @@ -900,16 +825,9 @@ static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); unsigned int count = 20; /* timeout iterations */ - u32 cfglo; - cfglo = channel_readl(dwc, CFG_LO); - if (dw->pdata->is_idma32) { - if (drain) - cfglo |= IDMA32C_CFGL_CH_DRAIN; - else - cfglo &= ~IDMA32C_CFGL_CH_DRAIN; - } - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + dw->suspend_chan(dwc, drain); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) udelay(2); @@ -1058,33 +976,7 @@ static void dwc_issue_pending(struct dma_chan *chan) /*----------------------------------------------------------------------*/ -/* - * Program FIFO size of channels. - * - * By default full FIFO (512 bytes) is assigned to channel 0. Here we - * slice FIFO on equal parts between channels. - */ -static void idma32_fifo_partition(struct dw_dma *dw) -{ - u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | - IDMA32C_FP_UPDATE; - u64 fifo_partition = 0; - - if (!dw->pdata->is_idma32) - return; - - /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ - fifo_partition |= value << 0; - - /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ - fifo_partition |= value << 32; - - /* Program FIFO Partition registers - 64 bytes per channel */ - idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); - idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); -} - -static void dw_dma_off(struct dw_dma *dw) +void do_dw_dma_off(struct dw_dma *dw) { unsigned int i; @@ -1103,7 +995,7 @@ static void dw_dma_off(struct dw_dma *dw) clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags); } -static void dw_dma_on(struct dw_dma *dw) +void do_dw_dma_on(struct dw_dma *dw) { dma_writel(dw, CFG, DW_CFG_DMA_EN); } @@ -1139,7 +1031,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) /* Enable controller here if needed */ if (!dw->in_use) - dw_dma_on(dw); + do_dw_dma_on(dw); dw->in_use |= dwc->mask; return 0; @@ -1177,30 +1069,25 @@ static void dwc_free_chan_resources(struct dma_chan *chan) /* Disable controller in case it was a last user */ dw->in_use &= ~dwc->mask; if (!dw->in_use) - dw_dma_off(dw); + do_dw_dma_off(dw); dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } -int dw_dma_probe(struct dw_dma_chip *chip) +int do_dma_probe(struct dw_dma_chip *chip) { + struct dw_dma *dw = chip->dw; struct dw_dma_platform_data *pdata; - struct dw_dma *dw; bool autocfg = false; unsigned int dw_params; unsigned int i; int err; - dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); - if (!dw) - return -ENOMEM; - dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL); if (!dw->pdata) return -ENOMEM; dw->regs = chip->regs; - chip->dw = dw; pm_runtime_get_sync(chip->dev); @@ -1250,15 +1137,10 @@ int dw_dma_probe(struct dw_dma_chip *chip) dw->all_chan_mask = (1 << pdata->nr_channels) - 1; /* Force dma off, just in case */ - dw_dma_off(dw); - - idma32_fifo_partition(dw); + dw->disable(dw); /* Device and instance ID for IRQ and DMA pool */ - if (pdata->is_idma32) - snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", chip->id); - else - snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id); + dw->set_device_name(dw, chip->id); /* Create a pool of consistent memory blocks for hardware descriptors */ dw->desc_pool = dmam_pool_create(dw->name, chip->dev, @@ -1380,16 +1262,15 @@ err_pdata: pm_runtime_put_sync_suspend(chip->dev); return err; } -EXPORT_SYMBOL_GPL(dw_dma_probe); -int dw_dma_remove(struct dw_dma_chip *chip) +int do_dma_remove(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; struct dw_dma_chan *dwc, *_dwc; pm_runtime_get_sync(chip->dev); - dw_dma_off(dw); + do_dw_dma_off(dw); dma_async_device_unregister(&dw->dma); free_irq(chip->irq, dw); @@ -1404,27 +1285,24 @@ int dw_dma_remove(struct dw_dma_chip *chip) pm_runtime_put_sync_suspend(chip->dev); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_remove); -int dw_dma_disable(struct dw_dma_chip *chip) +int do_dw_dma_disable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - dw_dma_off(dw); + dw->disable(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_disable); +EXPORT_SYMBOL_GPL(do_dw_dma_disable); -int dw_dma_enable(struct dw_dma_chip *chip) +int do_dw_dma_enable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - idma32_fifo_partition(dw); - - dw_dma_on(dw); + dw->enable(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_enable); +EXPORT_SYMBOL_GPL(do_dw_dma_enable); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver"); diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c new file mode 100644 index 000000000000..977aa28bf81d --- /dev/null +++ b/drivers/dma/dw/dw.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2007-2008 Atmel Corporation +// Copyright (C) 2010-2011 ST Microelectronics +// Copyright (C) 2013,2018 Intel Corporation + +#include +#include +#include +#include + +#include "internal.h" + +static void dw_dma_initialize_chan(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + u32 cfghi = DWC_CFGH_FIFO_MODE; + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); + bool hs_polarity = dwc->dws.hs_polarity; + + cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); + cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); + cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl); + + /* Set polarity of handshake interface */ + cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); +} + +static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc, + size_t bytes, unsigned int width, size_t *len) +{ + u32 block; + + if ((bytes >> width) > dwc->block_size) { + block = dwc->block_size; + *len = dwc->block_size << width; + } else { + block = bytes >> width; + *len = bytes; + } + + return block; +} + +static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) +{ + return DWC_CTLH_BLOCK_TS(block) << width; +} + +static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) +{ + /* + * Fix burst size according to dw_dmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + */ + *maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0; +} + +static void dw_dma_set_device_name(struct dw_dma *dw, int id) +{ + snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", id); +} + +static void dw_dma_disable(struct dw_dma *dw) +{ + do_dw_dma_off(dw); +} + +static void dw_dma_enable(struct dw_dma *dw) +{ + do_dw_dma_on(dw); +} + +int dw_dma_probe(struct dw_dma_chip *chip) +{ + struct dw_dma *dw; + + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + /* Channel operations */ + dw->initialize_chan = dw_dma_initialize_chan; + dw->suspend_chan = dw_dma_suspend_chan; + dw->encode_maxburst = dw_dma_encode_maxburst; + dw->bytes2block = dw_dma_bytes2block; + dw->block2bytes = dw_dma_block2bytes; + + /* Device operations */ + dw->set_device_name = dw_dma_set_device_name; + dw->disable = dw_dma_disable; + dw->enable = dw_dma_enable; + + chip->dw = dw; + return do_dma_probe(chip); +} +EXPORT_SYMBOL_GPL(dw_dma_probe); + +int dw_dma_remove(struct dw_dma_chip *chip) +{ + return do_dma_remove(chip); +} +EXPORT_SYMBOL_GPL(dw_dma_remove); diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c new file mode 100644 index 000000000000..8707830f39ad --- /dev/null +++ b/drivers/dma/dw/idma32.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013,2018 Intel Corporation + +#include +#include +#include +#include + +#include "internal.h" + +static void idma32_initialize_chan(struct dw_dma_chan *dwc) +{ + u32 cfghi = 0; + u32 cfglo = 0; + + /* Set default burst alignment */ + cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; + + /* Low 4 bits of the request lines */ + cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); + cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); + + /* Request line extension (2 bits) */ + cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); + cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void idma32_suspend_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + if (drain) + cfglo |= IDMA32C_CFGL_CH_DRAIN; + else + cfglo &= ~IDMA32C_CFGL_CH_DRAIN; + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); +} + +static u32 idma32_bytes2block(struct dw_dma_chan *dwc, + size_t bytes, unsigned int width, size_t *len) +{ + u32 block; + + if (bytes > dwc->block_size) { + block = dwc->block_size; + *len = dwc->block_size; + } else { + block = bytes; + *len = bytes; + } + + return block; +} + +static size_t idma32_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) +{ + return IDMA32C_CTLH_BLOCK_TS(block); +} + +static void idma32_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) +{ + *maxburst = *maxburst > 1 ? fls(*maxburst) - 1 : 0; +} + +static void idma32_set_device_name(struct dw_dma *dw, int id) +{ + snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", id); +} + +/* + * Program FIFO size of channels. + * + * By default full FIFO (512 bytes) is assigned to channel 0. Here we + * slice FIFO on equal parts between channels. + */ +static void idma32_fifo_partition(struct dw_dma *dw) +{ + u64 value = IDMA32C_FP_PSIZE_CH0(64) | IDMA32C_FP_PSIZE_CH1(64) | + IDMA32C_FP_UPDATE; + u64 fifo_partition = 0; + + /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ + fifo_partition |= value << 0; + + /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ + fifo_partition |= value << 32; + + /* Program FIFO Partition registers - 64 bytes per channel */ + idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); + idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); +} + +static void idma32_disable(struct dw_dma *dw) +{ + do_dw_dma_off(dw); + idma32_fifo_partition(dw); +} + +static void idma32_enable(struct dw_dma *dw) +{ + idma32_fifo_partition(dw); + do_dw_dma_on(dw); +} + +int idma32_dma_probe(struct dw_dma_chip *chip) +{ + struct dw_dma *dw; + + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + /* Channel operations */ + dw->initialize_chan = idma32_initialize_chan; + dw->suspend_chan = idma32_suspend_chan; + dw->encode_maxburst = idma32_encode_maxburst; + dw->bytes2block = idma32_bytes2block; + dw->block2bytes = idma32_block2bytes; + + /* Device operations */ + dw->set_device_name = idma32_set_device_name; + dw->disable = idma32_disable; + dw->enable = idma32_enable; + + chip->dw = dw; + return do_dma_probe(chip); +} +EXPORT_SYMBOL_GPL(idma32_dma_probe); + +int idma32_dma_remove(struct dw_dma_chip *chip) +{ + return do_dma_remove(chip); +} +EXPORT_SYMBOL_GPL(idma32_dma_remove); diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h index 41439732ff6b..fdcac21ea665 100644 --- a/drivers/dma/dw/internal.h +++ b/drivers/dma/dw/internal.h @@ -15,8 +15,14 @@ #include "regs.h" -int dw_dma_disable(struct dw_dma_chip *chip); -int dw_dma_enable(struct dw_dma_chip *chip); +int do_dma_probe(struct dw_dma_chip *chip); +int do_dma_remove(struct dw_dma_chip *chip); + +void do_dw_dma_on(struct dw_dma *dw); +void do_dw_dma_off(struct dw_dma *dw); + +int do_dw_dma_disable(struct dw_dma_chip *chip); +int do_dw_dma_enable(struct dw_dma_chip *chip); extern bool dw_dma_filter(struct dma_chan *chan, void *param); diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 66d98d7ccad0..e9ba25b4f950 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -15,9 +15,17 @@ #include "internal.h" -static struct dw_dma_platform_data mrfld_pdata = { +struct dw_dma_pci_data { + const struct dw_dma_platform_data *pdata; + int (*probe)(struct dw_dma_chip *chip); +}; + +static const struct dw_dma_pci_data dw_pci_data = { + .probe = dw_dma_probe, +}; + +static const struct dw_dma_platform_data idma32_pdata = { .nr_channels = 8, - .is_idma32 = true, .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, .chan_priority = CHAN_PRIORITY_ASCENDING, .block_size = 131071, @@ -26,9 +34,14 @@ static struct dw_dma_platform_data mrfld_pdata = { .multi_block = {1, 1, 1, 1, 1, 1, 1, 1}, }; +static const struct dw_dma_pci_data idma32_pci_data = { + .pdata = &idma32_pdata, + .probe = idma32_dma_probe, +}; + static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { - const struct dw_dma_platform_data *pdata = (void *)pid->driver_data; + const struct dw_dma_pci_data *data = (void *)pid->driver_data; struct dw_dma_chip *chip; int ret; @@ -61,9 +74,9 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) chip->id = pdev->devfn; chip->regs = pcim_iomap_table(pdev)[0]; chip->irq = pdev->irq; - chip->pdata = pdata; + chip->pdata = data->pdata; - ret = dw_dma_probe(chip); + ret = data->probe(chip); if (ret) return ret; @@ -89,7 +102,7 @@ static int dw_pci_suspend_late(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_disable(chip); + return do_dw_dma_disable(chip); }; static int dw_pci_resume_early(struct device *dev) @@ -97,7 +110,7 @@ static int dw_pci_resume_early(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_enable(chip); + return do_dw_dma_enable(chip); }; #endif /* CONFIG_PM_SLEEP */ @@ -108,24 +121,24 @@ static const struct dev_pm_ops dw_pci_dev_pm_ops = { static const struct pci_device_id dw_pci_id_table[] = { /* Medfield (GPDMA) */ - { PCI_VDEVICE(INTEL, 0x0827) }, + { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_data }, /* BayTrail */ - { PCI_VDEVICE(INTEL, 0x0f06) }, - { PCI_VDEVICE(INTEL, 0x0f40) }, + { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_data }, + { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_data }, - /* Merrifield iDMA 32-bit (GPDMA) */ - { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&mrfld_pdata }, + /* Merrifield */ + { PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&idma32_pci_data }, /* Braswell */ - { PCI_VDEVICE(INTEL, 0x2286) }, - { PCI_VDEVICE(INTEL, 0x22c0) }, + { PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_pci_data }, + { PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_pci_data }, /* Haswell */ - { PCI_VDEVICE(INTEL, 0x9c60) }, + { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_data }, /* Broadwell */ - { PCI_VDEVICE(INTEL, 0x9ce0) }, + { PCI_VDEVICE(INTEL, 0x9ce0), (kernel_ulong_t)&dw_pci_data }, { } }; diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 58fc1ba02a1e..d5196c97e4f4 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -255,7 +255,7 @@ static void dw_shutdown(struct platform_device *pdev) struct dw_dma_chip *chip = platform_get_drvdata(pdev); /* - * We have to call dw_dma_disable() to stop any ongoing transfer. On + * We have to call do_dw_dma_disable() to stop any ongoing transfer. On * some platforms we can't do that since DMA device is powered off. * Moreover we have no possibility to check if the platform is affected * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() @@ -264,7 +264,7 @@ static void dw_shutdown(struct platform_device *pdev) * used by the driver. */ pm_runtime_get_sync(chip->dev); - dw_dma_disable(chip); + do_dw_dma_disable(chip); pm_runtime_put_sync_suspend(chip->dev); clk_disable_unprepare(chip->clk); @@ -294,7 +294,7 @@ static int dw_suspend_late(struct device *dev) { struct dw_dma_chip *chip = dev_get_drvdata(dev); - dw_dma_disable(chip); + do_dw_dma_disable(chip); clk_disable_unprepare(chip->clk); return 0; @@ -309,7 +309,7 @@ static int dw_resume_early(struct device *dev) if (ret) return ret; - return dw_dma_enable(chip); + return do_dw_dma_enable(chip); } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 646c9c960c07..66aa8b227248 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -312,6 +312,19 @@ struct dw_dma { u8 all_chan_mask; u8 in_use; + /* Channel operations */ + void (*initialize_chan)(struct dw_dma_chan *dwc); + void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain); + void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst); + u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes, + unsigned int width, size_t *len); + size_t (*block2bytes)(struct dw_dma_chan *dwc, u32 block, u32 width); + + /* Device operations */ + void (*set_device_name)(struct dw_dma *dw, int id); + void (*disable)(struct dw_dma *dw); + void (*enable)(struct dw_dma *dw); + /* platform data */ struct dw_dma_platform_data *pdata; }; diff --git a/include/linux/dma/dw.h b/include/linux/dma/dw.h index e166cac8e870..d643d331c20e 100644 --- a/include/linux/dma/dw.h +++ b/include/linux/dma/dw.h @@ -45,9 +45,13 @@ struct dw_dma_chip { #if IS_ENABLED(CONFIG_DW_DMAC_CORE) int dw_dma_probe(struct dw_dma_chip *chip); int dw_dma_remove(struct dw_dma_chip *chip); +int idma32_dma_probe(struct dw_dma_chip *chip); +int idma32_dma_remove(struct dw_dma_chip *chip); #else static inline int dw_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } static inline int dw_dma_remove(struct dw_dma_chip *chip) { return 0; } +static inline int idma32_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } +static inline int idma32_dma_remove(struct dw_dma_chip *chip) { return 0; } #endif /* CONFIG_DW_DMAC_CORE */ #endif /* _DMA_DW_H */ diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 1c85eeee4171..576048433809 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -38,7 +38,6 @@ struct dw_dma_slave { /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) - * @is_idma32: The type of the DMA controller is iDMA32 * @chan_allocation_order: Allocate channels starting from 0 or 7 * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller @@ -50,7 +49,6 @@ struct dw_dma_slave { */ struct dw_dma_platform_data { unsigned int nr_channels; - bool is_idma32; #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ unsigned char chan_allocation_order; From 91f0ff883e9ae654503fc62c588eefc8558e457f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:39 +0200 Subject: [PATCH 0075/4277] dmaengine: dw: Reset DRAIN bit when resume the channel For Intel iDMA 32-bit the channel can be drained on a suspend. We need to reset the bit on the resume to return a status quo. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 10 +++++----- drivers/dma/dw/dw.c | 8 ++++++++ drivers/dma/dw/idma32.c | 13 +++++++++++-- drivers/dma/dw/regs.h | 1 + 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 8a581d86ea8d..6a23203e601d 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -846,11 +846,11 @@ static int dwc_pause(struct dma_chan *chan) return 0; } -static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +static inline void dwc_chan_resume(struct dw_dma_chan *dwc, bool drain) { - u32 cfglo = channel_readl(dwc, CFG_LO); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + dw->resume_chan(dwc, drain); clear_bit(DW_DMA_IS_PAUSED, &dwc->flags); } @@ -863,7 +863,7 @@ static int dwc_resume(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags)) - dwc_chan_resume(dwc); + dwc_chan_resume(dwc, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -886,7 +886,7 @@ static int dwc_terminate_all(struct dma_chan *chan) dwc_chan_disable(dw, dwc); - dwc_chan_resume(dwc); + dwc_chan_resume(dwc, true); /* active_list entries will end up before queued entries */ list_splice_init(&dwc->queue, &list); diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c index 977aa28bf81d..156088f768f2 100644 --- a/drivers/dma/dw/dw.c +++ b/drivers/dma/dw/dw.c @@ -35,6 +35,13 @@ static void dw_dma_suspend_chan(struct dw_dma_chan *dwc, bool drain) channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); } +static void dw_dma_resume_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); +} + static u32 dw_dma_bytes2block(struct dw_dma_chan *dwc, size_t bytes, unsigned int width, size_t *len) { @@ -91,6 +98,7 @@ int dw_dma_probe(struct dw_dma_chip *chip) /* Channel operations */ dw->initialize_chan = dw_dma_initialize_chan; dw->suspend_chan = dw_dma_suspend_chan; + dw->resume_chan = dw_dma_resume_chan; dw->encode_maxburst = dw_dma_encode_maxburst; dw->bytes2block = dw_dma_bytes2block; dw->block2bytes = dw_dma_block2bytes; diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c index 8707830f39ad..cd23b9238333 100644 --- a/drivers/dma/dw/idma32.c +++ b/drivers/dma/dw/idma32.c @@ -34,12 +34,20 @@ static void idma32_suspend_chan(struct dw_dma_chan *dwc, bool drain) if (drain) cfglo |= IDMA32C_CFGL_CH_DRAIN; - else - cfglo &= ~IDMA32C_CFGL_CH_DRAIN; channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); } +static void idma32_resume_chan(struct dw_dma_chan *dwc, bool drain) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + if (drain) + cfglo &= ~IDMA32C_CFGL_CH_DRAIN; + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); +} + static u32 idma32_bytes2block(struct dw_dma_chan *dwc, size_t bytes, unsigned int width, size_t *len) { @@ -117,6 +125,7 @@ int idma32_dma_probe(struct dw_dma_chip *chip) /* Channel operations */ dw->initialize_chan = idma32_initialize_chan; dw->suspend_chan = idma32_suspend_chan; + dw->resume_chan = idma32_resume_chan; dw->encode_maxburst = idma32_encode_maxburst; dw->bytes2block = idma32_bytes2block; dw->block2bytes = idma32_block2bytes; diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 66aa8b227248..6cf299facf45 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -315,6 +315,7 @@ struct dw_dma { /* Channel operations */ void (*initialize_chan)(struct dw_dma_chan *dwc); void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain); + void (*resume_chan)(struct dw_dma_chan *dwc, bool drain); void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst); u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes, unsigned int width, size_t *len); From 934891b0a16c55716de9cdd6cefc82cc2b75eb4a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:40 +0200 Subject: [PATCH 0076/4277] dmaengine: dw: Don't pollute CTL_LO on iDMA 32-bit Intel iDMA 32-bit doesn't have a concept of bus masters and thus there is no need to setup any kind of masters in the CTL_LO register. Moreover, the burst size for memory-to-memory transfer is not what is says, we need to have a corrected list of possible sizes. Note, that the size of 8 items, each of that up to 4 bytes, is chosen because of maximum of 1/2 FIFO, which is 64 bytes on Intel Merrifield. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 31 +++++-------------------------- drivers/dma/dw/dw.c | 18 ++++++++++++++++++ drivers/dma/dw/idma32.c | 13 +++++++++++++ drivers/dma/dw/regs.h | 11 +++++++++++ 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 6a23203e601d..b7e4dab28f8a 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -37,27 +37,6 @@ * support descriptor writeback. */ -#define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ - struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ - bool _is_slave = is_slave_direction(_dwc->direction); \ - u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ - DW_DMA_MSIZE_16; \ - u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ - DW_DMA_MSIZE_16; \ - u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \ - _dwc->dws.p_master : _dwc->dws.m_master; \ - u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \ - _dwc->dws.p_master : _dwc->dws.m_master; \ - \ - (DWC_CTLL_DST_MSIZE(_dmsize) \ - | DWC_CTLL_SRC_MSIZE(_smsize) \ - | DWC_CTLL_LLP_D_EN \ - | DWC_CTLL_LLP_S_EN \ - | DWC_CTLL_DMS(_dms) \ - | DWC_CTLL_SMS(_sms)); \ - }) - /* The set of bus widths supported by the DMA controller */ #define DW_DMA_BUSWIDTHS \ BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ @@ -596,7 +575,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, src_width = dst_width = __ffs(data_width | src | dest | len); - ctllo = DWC_DEFAULT_CTLLO(chan) + ctllo = dw->prepare_ctllo(dwc) | DWC_CTLL_DST_WIDTH(dst_width) | DWC_CTLL_SRC_WIDTH(src_width) | DWC_CTLL_DST_INC @@ -676,10 +655,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, case DMA_MEM_TO_DEV: reg_width = __ffs(sconfig->dst_addr_width); reg = sconfig->dst_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) + ctllo = dw->prepare_ctllo(dwc) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC); + | DWC_CTLL_SRC_INC; ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); @@ -726,10 +705,10 @@ slave_sg_todev_fill_desc: case DMA_DEV_TO_MEM: reg_width = __ffs(sconfig->src_addr_width); reg = sconfig->src_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) + ctllo = dw->prepare_ctllo(dwc) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX); + | DWC_CTLL_SRC_FIX; ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); diff --git a/drivers/dma/dw/dw.c b/drivers/dma/dw/dw.c index 156088f768f2..7a085b3c1854 100644 --- a/drivers/dma/dw/dw.c +++ b/drivers/dma/dw/dw.c @@ -4,6 +4,7 @@ // Copyright (C) 2013,2018 Intel Corporation #include +#include #include #include #include @@ -63,6 +64,22 @@ static size_t dw_dma_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) return DWC_CTLH_BLOCK_TS(block) << width; } +static u32 dw_dma_prepare_ctllo(struct dw_dma_chan *dwc) +{ + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + bool is_slave = is_slave_direction(dwc->direction); + u8 smsize = is_slave ? sconfig->src_maxburst : DW_DMA_MSIZE_16; + u8 dmsize = is_slave ? sconfig->dst_maxburst : DW_DMA_MSIZE_16; + u8 p_master = dwc->dws.p_master; + u8 m_master = dwc->dws.m_master; + u8 dms = (dwc->direction == DMA_MEM_TO_DEV) ? p_master : m_master; + u8 sms = (dwc->direction == DMA_DEV_TO_MEM) ? p_master : m_master; + + return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN | + DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize) | + DWC_CTLL_DMS(dms) | DWC_CTLL_SMS(sms); +} + static void dw_dma_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) { /* @@ -99,6 +116,7 @@ int dw_dma_probe(struct dw_dma_chip *chip) dw->initialize_chan = dw_dma_initialize_chan; dw->suspend_chan = dw_dma_suspend_chan; dw->resume_chan = dw_dma_resume_chan; + dw->prepare_ctllo = dw_dma_prepare_ctllo; dw->encode_maxburst = dw_dma_encode_maxburst; dw->bytes2block = dw_dma_bytes2block; dw->block2bytes = dw_dma_block2bytes; diff --git a/drivers/dma/dw/idma32.c b/drivers/dma/dw/idma32.c index cd23b9238333..f00657308811 100644 --- a/drivers/dma/dw/idma32.c +++ b/drivers/dma/dw/idma32.c @@ -2,6 +2,7 @@ // Copyright (C) 2013,2018 Intel Corporation #include +#include #include #include #include @@ -69,6 +70,17 @@ static size_t idma32_block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) return IDMA32C_CTLH_BLOCK_TS(block); } +static u32 idma32_prepare_ctllo(struct dw_dma_chan *dwc) +{ + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + bool is_slave = is_slave_direction(dwc->direction); + u8 smsize = is_slave ? sconfig->src_maxburst : IDMA32_MSIZE_8; + u8 dmsize = is_slave ? sconfig->dst_maxburst : IDMA32_MSIZE_8; + + return DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN | + DWC_CTLL_DST_MSIZE(dmsize) | DWC_CTLL_SRC_MSIZE(smsize); +} + static void idma32_encode_maxburst(struct dw_dma_chan *dwc, u32 *maxburst) { *maxburst = *maxburst > 1 ? fls(*maxburst) - 1 : 0; @@ -126,6 +138,7 @@ int idma32_dma_probe(struct dw_dma_chip *chip) dw->initialize_chan = idma32_initialize_chan; dw->suspend_chan = idma32_suspend_chan; dw->resume_chan = idma32_resume_chan; + dw->prepare_ctllo = idma32_prepare_ctllo; dw->encode_maxburst = idma32_encode_maxburst; dw->bytes2block = idma32_bytes2block; dw->block2bytes = idma32_block2bytes; diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 6cf299facf45..07f91325e559 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -222,6 +222,16 @@ enum dw_dma_msize { /* iDMA 32-bit support */ +/* bursts size */ +enum idma32_msize { + IDMA32_MSIZE_1, + IDMA32_MSIZE_2, + IDMA32_MSIZE_4, + IDMA32_MSIZE_8, + IDMA32_MSIZE_16, + IDMA32_MSIZE_32, +}; + /* Bitfields in CTL_HI */ #define IDMA32C_CTLH_BLOCK_TS_MASK GENMASK(16, 0) #define IDMA32C_CTLH_BLOCK_TS(x) ((x) & IDMA32C_CTLH_BLOCK_TS_MASK) @@ -316,6 +326,7 @@ struct dw_dma { void (*initialize_chan)(struct dw_dma_chan *dwc); void (*suspend_chan)(struct dw_dma_chan *dwc, bool drain); void (*resume_chan)(struct dw_dma_chan *dwc, bool drain); + u32 (*prepare_ctllo)(struct dw_dma_chan *dwc); void (*encode_maxburst)(struct dw_dma_chan *dwc, u32 *maxburst); u32 (*bytes2block)(struct dw_dma_chan *dwc, size_t bytes, unsigned int width, size_t *len); From b466a37fbcc99ef79ea59e40ef6aa8391430b0d8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2019 13:07:41 +0200 Subject: [PATCH 0077/4277] dmaengine: dw: convert to SPDX identifiers This patch updates license to use SPDX-License-Identifier instead of verbose license text. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/Kconfig | 2 ++ drivers/dma/dw/core.c | 5 +---- drivers/dma/dw/internal.h | 5 +---- drivers/dma/dw/pci.c | 5 +---- drivers/dma/dw/platform.c | 5 +---- drivers/dma/dw/regs.h | 5 +---- include/linux/dma/dw.h | 5 +---- include/linux/platform_data/dma-dw.h | 5 +---- 8 files changed, 9 insertions(+), 28 deletions(-) diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index 04b9728c1d26..e5162690de8f 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + # # DMA engine configuration for dw # diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index b7e4dab28f8a..3a1ab52ffae0 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Core driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics * Copyright (C) 2013 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h index fdcac21ea665..1dd7a4e6dd23 100644 --- a/drivers/dma/dw/internal.h +++ b/drivers/dma/dw/internal.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2013 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _DMA_DW_INTERNAL_H diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index e9ba25b4f950..e79a75db0852 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PCI driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2013 Intel Corporation * Author: Andy Shevchenko - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index d5196c97e4f4..382dfd9e9600 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Platform driver for the Synopsys DesignWare DMA Controller * @@ -6,10 +7,6 @@ * Copyright (C) 2013 Intel Corporation * * Some parts of this driver are derived from the original dw_dmac. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 07f91325e559..3fce66ecee7a 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the Synopsys DesignWare AHB DMA Controller * * Copyright (C) 2005-2007 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics * Copyright (C) 2016 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/include/linux/dma/dw.h b/include/linux/dma/dw.h index d643d331c20e..9752f3745f76 100644 --- a/include/linux/dma/dw.h +++ b/include/linux/dma/dw.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics * Copyright (C) 2014 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _DMA_DW_H #define _DMA_DW_H diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 576048433809..f3eaf9ec00a1 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _PLATFORM_DATA_DMA_DW_H #define _PLATFORM_DATA_DMA_DW_H From 5fde600537db4c61621783ae9d1c18f35e5e342c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 12:30:34 -0600 Subject: [PATCH 0078/4277] dmaengine: bcm2835: Use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 1a44c8086d77..7beec403c2c9 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -312,8 +312,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain( return NULL; /* allocate and setup the descriptor. */ - d = kzalloc(sizeof(*d) + frames * sizeof(struct bcm2835_cb_entry), - gfp); + d = kzalloc(struct_size(d, cb_list, frames), gfp); if (!d) return NULL; From ed414d580310c3ec95abcef508035040e21a629d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 24 Dec 2018 00:52:17 -0600 Subject: [PATCH 0079/4277] dmaengine: dma-jz4780: Use struct_size() in devm_kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = devm_kzalloc(dev, sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = devm_kzalloc(dev, struct_size(instance, entry, count), GFP_KERNEL); This issue was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index a8b6225faa12..9ce0a386225b 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -838,9 +838,8 @@ static int jz4780_dma_probe(struct platform_device *pdev) if (!soc_data) return -EINVAL; - jzdma = devm_kzalloc(dev, sizeof(*jzdma) - + sizeof(*jzdma->chan) * soc_data->nb_channels, - GFP_KERNEL); + jzdma = devm_kzalloc(dev, struct_size(jzdma, chan, + soc_data->nb_channels), GFP_KERNEL); if (!jzdma) return -ENOMEM; From 55f53b9c1746bba0e1b3a8d82ff3abe7ad3b96ce Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 12:43:22 -0600 Subject: [PATCH 0080/4277] dmaengine: st_fdma: use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Acked-by: Patrice Chotard Signed-off-by: Vinod Koul --- drivers/dma/st_fdma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c index 954f7cf62bb1..bc7a1de3f29b 100644 --- a/drivers/dma/st_fdma.c +++ b/drivers/dma/st_fdma.c @@ -243,8 +243,7 @@ static struct st_fdma_desc *st_fdma_alloc_desc(struct st_fdma_chan *fchan, struct st_fdma_desc *fdesc; int i; - fdesc = kzalloc(sizeof(*fdesc) + - sizeof(struct st_fdma_sw_node) * sg_len, GFP_NOWAIT); + fdesc = kzalloc(struct_size(fdesc, node, sg_len), GFP_NOWAIT); if (!fdesc) return NULL; From edd3c389999678ddff93e72d04368ed576bf36f8 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 11:32:15 -0600 Subject: [PATCH 0081/4277] dmaengine: qcom: bam_dma: use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/qcom/bam_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index 1617715aa6e0..cb860cb53c27 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -636,8 +636,8 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_FIFO_SIZE); /* allocate enough room to accomodate the number of entries */ - async_desc = kzalloc(sizeof(*async_desc) + - (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); + async_desc = kzalloc(struct_size(async_desc, desc, num_alloc), + GFP_NOWAIT); if (!async_desc) goto err_out; From d3d70373f6fee9ed41bc4e9f1acdbc80c5b3a684 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 15:16:12 -0600 Subject: [PATCH 0082/4277] dmaengine: tegra-apb: Use struct_size() in devm_kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = devm_kzalloc(dev, sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = devm_kzalloc(dev, struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 659b49bf6fdb..cf462b1abc0b 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1329,8 +1329,9 @@ static int tegra_dma_probe(struct platform_device *pdev) return -ENODEV; } - tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels * - sizeof(struct tegra_dma_channel), GFP_KERNEL); + tdma = devm_kzalloc(&pdev->dev, + struct_size(tdma, channels, cdata->nr_channels), + GFP_KERNEL); if (!tdma) return -ENOMEM; From de1fa4f61be71725581c8e30f3665aaa20d1610e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 15:25:45 -0600 Subject: [PATCH 0083/4277] dmaengine: fsl-edma: use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Tested-by: Angelo Dureghello Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma-common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 8876c4c1bb2c..fe529100674f 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -339,9 +339,7 @@ static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, struct fsl_edma_desc *fsl_desc; int i; - fsl_desc = kzalloc(sizeof(*fsl_desc) + - sizeof(struct fsl_edma_sw_tcd) * - sg_len, GFP_NOWAIT); + fsl_desc = kzalloc(struct_size(fsl_desc, tcd, sg_len), GFP_NOWAIT); if (!fsl_desc) return NULL; From 83d0ea237bac5fb6a7509c69a8939e999693d311 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 21 Dec 2018 17:02:35 +0100 Subject: [PATCH 0084/4277] dt-bindings: clk: meson: add ao slow clock path ids Add the CLKIDs for the slow clock generation path Reviewed-by: Rob Herring Signed-off-by: Jerome Brunet Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181221160239.26265-2-jbrunet@baylibre.com --- include/dt-bindings/clock/axg-aoclkc.h | 7 ++++++- include/dt-bindings/clock/gxbb-aoclkc.h | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/clock/axg-aoclkc.h b/include/dt-bindings/clock/axg-aoclkc.h index 61955016a55b..8ec4a269c7a6 100644 --- a/include/dt-bindings/clock/axg-aoclkc.h +++ b/include/dt-bindings/clock/axg-aoclkc.h @@ -21,6 +21,11 @@ #define CLKID_AO_SAR_ADC_SEL 8 #define CLKID_AO_SAR_ADC_DIV 9 #define CLKID_AO_SAR_ADC_CLK 10 -#define CLKID_AO_ALT_XTAL 11 +#define CLKID_AO_CTS_OSCIN 11 +#define CLKID_AO_32K_PRE 12 +#define CLKID_AO_32K_DIV 13 +#define CLKID_AO_32K_SEL 14 +#define CLKID_AO_32K 15 +#define CLKID_AO_CTS_RTC_OSCIN 16 #endif diff --git a/include/dt-bindings/clock/gxbb-aoclkc.h b/include/dt-bindings/clock/gxbb-aoclkc.h index 9d15e2221fdb..ec3b26319fc4 100644 --- a/include/dt-bindings/clock/gxbb-aoclkc.h +++ b/include/dt-bindings/clock/gxbb-aoclkc.h @@ -63,5 +63,12 @@ #define CLKID_AO_UART2 4 #define CLKID_AO_IR_BLASTER 5 #define CLKID_AO_CEC_32K 6 +#define CLKID_AO_CTS_OSCIN 7 +#define CLKID_AO_32K_PRE 8 +#define CLKID_AO_32K_DIV 9 +#define CLKID_AO_32K_SEL 10 +#define CLKID_AO_32K 11 +#define CLKID_AO_CTS_RTC_OSCIN 12 +#define CLKID_AO_CLK81 13 #endif From 8d9981efbcab066d17af4d3c85c169200f6f78df Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 21 Dec 2018 17:02:36 +0100 Subject: [PATCH 0085/4277] clk: meson: clean-up clock registration Order, ids and size between the table of regmap clocks and the onecell data table could be different. Set regmap pointer in all the regmap clocks before starting the registration using the onecell data, to make sure we don't get into an incoherent situation. Signed-off-by: Jerome Brunet Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181221160239.26265-3-jbrunet@baylibre.com --- drivers/clk/meson/meson-aoclk.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index f965845917e3..258c8d259ea1 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -65,15 +65,20 @@ int meson_aoclkc_probe(struct platform_device *pdev) return ret; } - /* - * Populate regmap and register all clks - */ - for (clkid = 0; clkid < data->num_clks; clkid++) { + /* Populate regmap */ + for (clkid = 0; clkid < data->num_clks; clkid++) data->clks[clkid]->map = regmap; + /* Register all clks */ + for (clkid = 0; clkid < data->hw_data->num; clkid++) { + if (!data->hw_data->hws[clkid]) + continue; + ret = devm_clk_hw_register(dev, data->hw_data->hws[clkid]); - if (ret) + if (ret) { + dev_err(dev, "Clock registration failed\n"); return ret; + } } return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, From a8d552a63857c73c6f0ea28dc8a70df10764b820 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 21 Dec 2018 17:02:37 +0100 Subject: [PATCH 0086/4277] clk: meson: add dual divider clock driver Add the dual divider driver. This special divider make a weighted average between 2 dividers to reach fractional divider values. Signed-off-by: Jerome Brunet Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181221160239.26265-4-jbrunet@baylibre.com --- drivers/clk/meson/Makefile | 2 +- drivers/clk/meson/clk-dualdiv.c | 130 ++++++++++++++++++++++++++++++++ drivers/clk/meson/clkc.h | 19 +++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/meson/clk-dualdiv.c diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index a849aa809825..f1fcafc046d5 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o +obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o clk-dualdiv.o obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o diff --git a/drivers/clk/meson/clk-dualdiv.c b/drivers/clk/meson/clk-dualdiv.c new file mode 100644 index 000000000000..4d9e161de627 --- /dev/null +++ b/drivers/clk/meson/clk-dualdiv.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong + * Author: Jerome Brunet + */ + +/* + * The AO Domain embeds a dual/divider to generate a more precise + * 32,768KHz clock for low-power suspend mode and CEC. + * ______ ______ + * | | | | + * | Div1 |-| Cnt1 | + * /|______| |______|\ + * -| ______ ______ X--> Out + * \| | | |/ + * | Div2 |-| Cnt2 | + * |______| |______| + * + * The dividing can be switched to single or dual, with a counter + * for each divider to set when the switching is done. + */ + +#include +#include "clkc.h" + +static inline struct meson_clk_dualdiv_data * +meson_clk_dualdiv_data(struct clk_regmap *clk) +{ + return (struct meson_clk_dualdiv_data *)clk->data; +} + +static unsigned long +__dualdiv_param_to_rate(unsigned long parent_rate, + const struct meson_clk_dualdiv_param *p) +{ + if (!p->dual) + return DIV_ROUND_CLOSEST(parent_rate, p->n1); + + return DIV_ROUND_CLOSEST(parent_rate * (p->m1 + p->m2), + p->n1 * p->m1 + p->n2 * p->m2); +} + +static unsigned long meson_clk_dualdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk); + struct meson_clk_dualdiv_param setting; + + setting.dual = meson_parm_read(clk->map, &dualdiv->dual); + setting.n1 = meson_parm_read(clk->map, &dualdiv->n1) + 1; + setting.m1 = meson_parm_read(clk->map, &dualdiv->m1) + 1; + setting.n2 = meson_parm_read(clk->map, &dualdiv->n2) + 1; + setting.m2 = meson_parm_read(clk->map, &dualdiv->m2) + 1; + + return __dualdiv_param_to_rate(parent_rate, &setting); +} + +static const struct meson_clk_dualdiv_param * +__dualdiv_get_setting(unsigned long rate, unsigned long parent_rate, + struct meson_clk_dualdiv_data *dualdiv) +{ + const struct meson_clk_dualdiv_param *table = dualdiv->table; + unsigned long best = 0, now = 0; + unsigned int i, best_i = 0; + + if (!table) + return NULL; + + for (i = 0; table[i].n1; i++) { + now = __dualdiv_param_to_rate(parent_rate, &table[i]); + + /* If we get an exact match, don't bother any further */ + if (now == rate) { + return &table[i]; + } else if (abs(now - rate) < abs(best - rate)) { + best = now; + best_i = i; + } + } + + return (struct meson_clk_dualdiv_param *)&table[best_i]; +} + +static long meson_clk_dualdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk); + const struct meson_clk_dualdiv_param *setting = + __dualdiv_get_setting(rate, *parent_rate, dualdiv); + + if (!setting) + return meson_clk_dualdiv_recalc_rate(hw, *parent_rate); + + return __dualdiv_param_to_rate(*parent_rate, setting); +} + +static int meson_clk_dualdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_dualdiv_data *dualdiv = meson_clk_dualdiv_data(clk); + const struct meson_clk_dualdiv_param *setting = + __dualdiv_get_setting(rate, parent_rate, dualdiv); + + if (!setting) + return -EINVAL; + + meson_parm_write(clk->map, &dualdiv->dual, setting->dual); + meson_parm_write(clk->map, &dualdiv->n1, setting->n1 - 1); + meson_parm_write(clk->map, &dualdiv->m1, setting->m1 - 1); + meson_parm_write(clk->map, &dualdiv->n2, setting->n2 - 1); + meson_parm_write(clk->map, &dualdiv->m2, setting->m2 - 1); + + return 0; +} + +const struct clk_ops meson_clk_dualdiv_ops = { + .recalc_rate = meson_clk_dualdiv_recalc_rate, + .round_rate = meson_clk_dualdiv_round_rate, + .set_rate = meson_clk_dualdiv_set_rate, +}; +EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ops); + +const struct clk_ops meson_clk_dualdiv_ro_ops = { + .recalc_rate = meson_clk_dualdiv_recalc_rate, +}; +EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops); diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 6183b22c4bf2..e3cd442db739 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -110,6 +110,23 @@ struct clk_regmap _name = { \ }, \ }; +struct meson_clk_dualdiv_param { + unsigned int n1; + unsigned int n2; + unsigned int m1; + unsigned int m2; + unsigned int dual; +}; + +struct meson_clk_dualdiv_data { + struct parm n1; + struct parm n2; + struct parm m1; + struct parm m2; + struct parm dual; + const struct meson_clk_dualdiv_param *table; +}; + /* clk_ops */ extern const struct clk_ops meson_clk_pll_ro_ops; extern const struct clk_ops meson_clk_pll_ops; @@ -118,6 +135,8 @@ extern const struct clk_ops meson_clk_mpll_ro_ops; extern const struct clk_ops meson_clk_mpll_ops; extern const struct clk_ops meson_clk_phase_ops; extern const struct clk_ops meson_vid_pll_div_ro_ops; +extern const struct clk_ops meson_clk_dualdiv_ops; +extern const struct clk_ops meson_clk_dualdiv_ro_ops; struct clk_hw *meson_clk_hw_register_input(struct device *dev, const char *of_name, From b249623fd1472225232e8784864af55aec1fd81b Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 21 Dec 2018 17:02:38 +0100 Subject: [PATCH 0087/4277] clk: meson: gxbb-ao: replace cec-32k with the dual divider Replace the cec-32k clock of gxbb-ao with the simpler dual divider driver. The dual divider implements only the dividing part. All the other bits are now exposed using simple elements, such as gates and muxes Signed-off-by: Jerome Brunet Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181221160239.26265-5-jbrunet@baylibre.com --- drivers/clk/meson/Makefile | 2 +- drivers/clk/meson/gxbb-aoclk-32k.c | 193 ---------------------- drivers/clk/meson/gxbb-aoclk.c | 251 +++++++++++++++++++++++------ drivers/clk/meson/gxbb-aoclk.h | 20 +-- 4 files changed, 204 insertions(+), 262 deletions(-) delete mode 100644 drivers/clk/meson/gxbb-aoclk-32k.c diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index f1fcafc046d5..8234e92eea38 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o clk-dualdiv.o obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c deleted file mode 100644 index 680467141a1d..000000000000 --- a/drivers/clk/meson/gxbb-aoclk-32k.c +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2017 BayLibre, SAS. - * Author: Neil Armstrong - */ - -#include -#include -#include -#include "gxbb-aoclk.h" - -/* - * The AO Domain embeds a dual/divider to generate a more precise - * 32,768KHz clock for low-power suspend mode and CEC. - * ______ ______ - * | | | | - * ______ | Div1 |-| Cnt1 | ______ - * | | /|______| |______|\ | | - * Xtal-->| Gate |---| ______ ______ X-X--| Gate |--> - * |______| | \| | | |/ | |______| - * | | Div2 |-| Cnt2 | | - * | |______| |______| | - * |_______________________| - * - * The dividing can be switched to single or dual, with a counter - * for each divider to set when the switching is done. - * The entire dividing mechanism can be also bypassed. - */ - -#define CLK_CNTL0_N1_MASK GENMASK(11, 0) -#define CLK_CNTL0_N2_MASK GENMASK(23, 12) -#define CLK_CNTL0_DUALDIV_EN BIT(28) -#define CLK_CNTL0_OUT_GATE_EN BIT(30) -#define CLK_CNTL0_IN_GATE_EN BIT(31) - -#define CLK_CNTL1_M1_MASK GENMASK(11, 0) -#define CLK_CNTL1_M2_MASK GENMASK(23, 12) -#define CLK_CNTL1_BYPASS_EN BIT(24) -#define CLK_CNTL1_SELECT_OSC BIT(27) - -#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10) - -struct cec_32k_freq_table { - unsigned long parent_rate; - unsigned long target_rate; - bool dualdiv; - unsigned int n1; - unsigned int n2; - unsigned int m1; - unsigned int m2; -}; - -static const struct cec_32k_freq_table aoclk_cec_32k_table[] = { - [0] = { - .parent_rate = 24000000, - .target_rate = 32768, - .dualdiv = true, - .n1 = 733, - .n2 = 732, - .m1 = 8, - .m2 = 11, - }, -}; - -/* - * If CLK_CNTL0_DUALDIV_EN == 0 - * - will use N1 divider only - * If CLK_CNTL0_DUALDIV_EN == 1 - * - hold M1 cycles of N1 divider then changes to N2 - * - hold M2 cycles of N2 divider then changes to N1 - * Then we can get more accurate division. - */ -static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); - unsigned long n1; - u32 reg0, reg1; - - regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, ®0); - regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, ®1); - - if (reg1 & CLK_CNTL1_BYPASS_EN) - return parent_rate; - - if (reg0 & CLK_CNTL0_DUALDIV_EN) { - unsigned long n2, m1, m2, f1, f2, p1, p2; - - n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; - n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1; - - m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1; - m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1; - - f1 = DIV_ROUND_CLOSEST(parent_rate, n1); - f2 = DIV_ROUND_CLOSEST(parent_rate, n2); - - p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2)); - p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2)); - - return DIV_ROUND_UP(100000000, p1 + p2); - } - - n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; - - return DIV_ROUND_CLOSEST(parent_rate, n1); -} - -static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate, - unsigned long prate) -{ - int i; - - for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i) - if (aoclk_cec_32k_table[i].parent_rate == prate && - aoclk_cec_32k_table[i].target_rate == rate) - return &aoclk_cec_32k_table[i]; - - return NULL; -} - -static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, - *prate); - - /* If invalid return first one */ - if (!freq) - return aoclk_cec_32k_table[0].target_rate; - - return freq->target_rate; -} - -/* - * From the Amlogic init procedure, the IN and OUT gates needs to be handled - * in the init procedure to avoid any glitches. - */ - -static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, - parent_rate); - struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); - u32 reg = 0; - - if (!freq) - return -EINVAL; - - /* Disable clock */ - regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, - CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0); - - reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1); - if (freq->dualdiv) - reg |= CLK_CNTL0_DUALDIV_EN | - FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1); - - regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg); - - reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1); - if (freq->dualdiv) - reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1); - - regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg); - - /* Enable clock */ - regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, - CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN); - - udelay(200); - - regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, - CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN); - - regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1, - CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC); - - /* Select 32k from XTAL */ - regmap_update_bits(cec_32k->regmap, - AO_RTI_PWR_CNTL_REG0, - PWR_CNTL_ALT_32K_SEL, - FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4)); - - return 0; -} - -const struct clk_ops meson_aoclk_cec_32k_ops = { - .recalc_rate = aoclk_cec_32k_recalc_rate, - .round_rate = aoclk_cec_32k_round_rate, - .set_rate = aoclk_cec_32k_set_rate, -}; diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index 42ed61d3c3fb..5fa57b623b8f 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -5,10 +5,19 @@ */ #include #include -#include "clk-regmap.h" +#include "clkc.h" #include "meson-aoclk.h" #include "gxbb-aoclk.h" +/* AO Configuration Clock registers offsets */ +#define AO_RTI_PWR_CNTL_REG1 0x0c +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + #define GXBB_AO_GATE(_name, _bit) \ static struct clk_regmap _name##_ao = { \ .data = &(struct clk_regmap_gate_data) { \ @@ -31,13 +40,174 @@ GXBB_AO_GATE(uart1, 3); GXBB_AO_GATE(uart2, 5); GXBB_AO_GATE(ir_blaster, 6); -static struct aoclk_cec_32k cec_32k_ao = { - .hw.init = &(struct clk_init_data) { - .name = "cec_32k_ao", - .ops = &meson_aoclk_cec_32k_ops, +static struct clk_regmap ao_cts_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTI_PWR_CNTL_REG0, + .bit_idx = 6, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_cts_oscin", + .ops = &clk_regmap_gate_ro_ops, .parent_names = (const char *[]){ "xtal" }, .num_parents = 1, - .flags = CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap ao_32k_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "ao_cts_oscin" }, + .num_parents = 1, + }, +}; + +static const struct meson_clk_dualdiv_param gxbb_32k_div_table[] = { + { + .dual = 1, + .n1 = 733, + .m1 = 8, + .n2 = 732, + .m2 = 11, + }, {} +}; + +static struct clk_regmap ao_32k_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 28, + .width = 1, + }, + .table = gxbb_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "ao_32k_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap ao_32k_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTC_ALT_CLK_CNTL1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "ao_32k_div", + "ao_32k_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_32k = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_32k", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "ao_32k_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_cts_rtc_oscin = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x7, + .shift = 10, + .table = (u32[]){ 1, 2, 3, 4 }, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_cts_rtc_oscin", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "ext_32k_0", + "ext_32k_1", + "ext_32k_2", + "ao_32k" }, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_clk81 = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 0, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_clk81", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = (const char *[]){ "clk81", + "ao_cts_rtc_oscin" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap ao_cts_cec = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_CRT_CLK_CNTL1, + .mask = 0x1, + .shift = 27, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "ao_cts_cec", + .ops = &clk_regmap_mux_ops, + /* + * FIXME: The 'fixme' parent obviously does not exist. + * + * ATM, CCF won't call get_parent() if num_parents is 1. It + * does not allow NULL as a parent name either. + * + * On this particular mux, we only know the input #1 parent + * but, on boot, unknown input #0 is set, so it is critical + * to call .get_parent() on it + * + * Until CCF gets fixed, adding this fake parent that won't + * ever be registered should work around the problem + */ + .parent_names = (const char *[]){ "fixme", + "ao_cts_rtc_oscin" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -50,13 +220,21 @@ static const unsigned int gxbb_aoclk_reset[] = { [RESET_AO_IR_BLASTER] = 23, }; -static struct clk_regmap *gxbb_aoclk_gate[] = { - [CLKID_AO_REMOTE] = &remote_ao, - [CLKID_AO_I2C_MASTER] = &i2c_master_ao, - [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao, - [CLKID_AO_UART1] = &uart1_ao, - [CLKID_AO_UART2] = &uart2_ao, - [CLKID_AO_IR_BLASTER] = &ir_blaster_ao, +static struct clk_regmap *gxbb_aoclk[] = { + &remote_ao, + &i2c_master_ao, + &i2c_slave_ao, + &uart1_ao, + &uart2_ao, + &ir_blaster_ao, + &ao_cts_oscin, + &ao_32k_pre, + &ao_32k_div, + &ao_32k_sel, + &ao_32k, + &ao_cts_rtc_oscin, + &ao_clk81, + &ao_cts_cec, }; static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { @@ -67,52 +245,27 @@ static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { [CLKID_AO_UART1] = &uart1_ao.hw, [CLKID_AO_UART2] = &uart2_ao.hw, [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw, - [CLKID_AO_CEC_32K] = &cec_32k_ao.hw, + [CLKID_AO_CEC_32K] = &ao_cts_cec.hw, + [CLKID_AO_CTS_OSCIN] = &ao_cts_oscin.hw, + [CLKID_AO_32K_PRE] = &ao_32k_pre.hw, + [CLKID_AO_32K_DIV] = &ao_32k_div.hw, + [CLKID_AO_32K_SEL] = &ao_32k_sel.hw, + [CLKID_AO_32K] = &ao_32k.hw, + [CLKID_AO_CTS_RTC_OSCIN] = &ao_cts_rtc_oscin.hw, + [CLKID_AO_CLK81] = &ao_clk81.hw, }, .num = NR_CLKS, }; -static int gxbb_register_cec_ao_32k(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct regmap *regmap; - int ret; - - regmap = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(regmap)) { - dev_err(dev, "failed to get regmap\n"); - return PTR_ERR(regmap); - } - - /* Specific clocks */ - cec_32k_ao.regmap = regmap; - ret = devm_clk_hw_register(dev, &cec_32k_ao.hw); - if (ret) { - dev_err(&pdev->dev, "clk cec_32k_ao register failed.\n"); - return ret; - } - - return 0; -} - static const struct meson_aoclk_data gxbb_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(gxbb_aoclk_reset), .reset = gxbb_aoclk_reset, - .num_clks = ARRAY_SIZE(gxbb_aoclk_gate), - .clks = gxbb_aoclk_gate, + .num_clks = ARRAY_SIZE(gxbb_aoclk), + .clks = gxbb_aoclk, .hw_data = &gxbb_aoclk_onecell_data, }; -static int gxbb_aoclkc_probe(struct platform_device *pdev) -{ - int ret = gxbb_register_cec_ao_32k(pdev); - if (ret) - return ret; - - return meson_aoclkc_probe(pdev); -} - static const struct of_device_id gxbb_aoclkc_match_table[] = { { .compatible = "amlogic,meson-gx-aoclkc", @@ -122,7 +275,7 @@ static const struct of_device_id gxbb_aoclkc_match_table[] = { }; static struct platform_driver gxbb_aoclkc_driver = { - .probe = gxbb_aoclkc_probe, + .probe = meson_aoclkc_probe, .driver = { .name = "gxbb-aoclkc", .of_match_table = gxbb_aoclkc_match_table, diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h index c514493d989a..1db16f9b37d4 100644 --- a/drivers/clk/meson/gxbb-aoclk.h +++ b/drivers/clk/meson/gxbb-aoclk.h @@ -7,25 +7,7 @@ #ifndef __GXBB_AOCLKC_H #define __GXBB_AOCLKC_H -#define NR_CLKS 7 - -/* AO Configuration Clock registers offsets */ -#define AO_RTI_PWR_CNTL_REG1 0x0c -#define AO_RTI_PWR_CNTL_REG0 0x10 -#define AO_RTI_GEN_CNTL_REG0 0x40 -#define AO_OSCIN_CNTL 0x58 -#define AO_CRT_CLK_CNTL1 0x68 -#define AO_RTC_ALT_CLK_CNTL0 0x94 -#define AO_RTC_ALT_CLK_CNTL1 0x98 - -struct aoclk_cec_32k { - struct clk_hw hw; - struct regmap *regmap; -}; - -#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw) - -extern const struct clk_ops meson_aoclk_cec_32k_ops; +#define NR_CLKS 14 #include #include From 172e95346d5e3b364d71e3980c966cf323e0fdd7 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 21 Dec 2018 17:02:39 +0100 Subject: [PATCH 0088/4277] clk: meson: axg-ao: add 32k generation subtree Add the clock subtree generating the 32k clock in amlogic axg ao block. Signed-off-by: Jerome Brunet Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181221160239.26265-6-jbrunet@baylibre.com --- drivers/clk/meson/axg-aoclk.c | 175 +++++++++++++++++++++++++++++++--- drivers/clk/meson/axg-aoclk.h | 13 +-- 2 files changed, 163 insertions(+), 25 deletions(-) diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c index 29e088542387..5f518be144ce 100644 --- a/drivers/clk/meson/axg-aoclk.c +++ b/drivers/clk/meson/axg-aoclk.c @@ -12,10 +12,23 @@ #include #include #include -#include "clk-regmap.h" +#include "clkc.h" #include "meson-aoclk.h" #include "axg-aoclk.h" +/* + * AO Configuration Clock registers offsets + * Register offsets from the data sheet must be multiplied by 4. + */ +#define AO_RTI_PWR_CNTL_REG1 0x0C +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_SAR_CLK 0x90 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + #define AXG_AO_GATE(_name, _bit) \ static struct clk_regmap axg_aoclk_##_name = { \ .data = &(struct clk_regmap_gate_data) { \ @@ -39,17 +52,141 @@ AXG_AO_GATE(uart2, 5); AXG_AO_GATE(ir_blaster, 6); AXG_AO_GATE(saradc, 7); +static struct clk_regmap axg_aoclk_cts_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTI_PWR_CNTL_REG0, + .bit_idx = 14, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_oscin", + .ops = &clk_regmap_gate_ro_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap axg_aoclk_32k_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_oscin" }, + .num_parents = 1, + }, +}; + +static const struct meson_clk_dualdiv_param axg_32k_div_table[] = { + { + .dual = 1, + .n1 = 733, + .m1 = 8, + .n2 = 732, + .m2 = 11, + }, {} +}; + +static struct clk_regmap axg_aoclk_32k_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 28, + .width = 1, + }, + .table = axg_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "axg_ao_32k_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap axg_aoclk_32k_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTC_ALT_CLK_CNTL1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "axg_ao_32k_div", + "axg_ao_32k_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_aoclk_32k = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_32k", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "axg_ao_32k_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_aoclk_cts_rtc_oscin = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 10, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "axg_ao_cts_rtc_oscin", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "axg_ao_32k", + "axg_ext_32k" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + static struct clk_regmap axg_aoclk_clk81 = { .data = &(struct clk_regmap_mux_data) { .offset = AO_RTI_PWR_CNTL_REG0, .mask = 0x1, .shift = 8, + .flags = CLK_MUX_ROUND_CLOSEST, }, .hw.init = &(struct clk_init_data){ .name = "axg_ao_clk81", .ops = &clk_regmap_mux_ro_ops, - .parent_names = (const char *[]){ "clk81", "ao_alt_xtal"}, + .parent_names = (const char *[]){ "clk81", + "axg_ao_cts_rtc_oscin"}, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -106,17 +243,23 @@ static const unsigned int axg_aoclk_reset[] = { }; static struct clk_regmap *axg_aoclk_regmap[] = { - [CLKID_AO_REMOTE] = &axg_aoclk_remote, - [CLKID_AO_I2C_MASTER] = &axg_aoclk_i2c_master, - [CLKID_AO_I2C_SLAVE] = &axg_aoclk_i2c_slave, - [CLKID_AO_UART1] = &axg_aoclk_uart1, - [CLKID_AO_UART2] = &axg_aoclk_uart2, - [CLKID_AO_IR_BLASTER] = &axg_aoclk_ir_blaster, - [CLKID_AO_SAR_ADC] = &axg_aoclk_saradc, - [CLKID_AO_CLK81] = &axg_aoclk_clk81, - [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux, - [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div, - [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate, + &axg_aoclk_remote, + &axg_aoclk_i2c_master, + &axg_aoclk_i2c_slave, + &axg_aoclk_uart1, + &axg_aoclk_uart2, + &axg_aoclk_ir_blaster, + &axg_aoclk_saradc, + &axg_aoclk_cts_oscin, + &axg_aoclk_32k_pre, + &axg_aoclk_32k_div, + &axg_aoclk_32k_sel, + &axg_aoclk_32k, + &axg_aoclk_cts_rtc_oscin, + &axg_aoclk_clk81, + &axg_aoclk_saradc_mux, + &axg_aoclk_saradc_div, + &axg_aoclk_saradc_gate, }; static const struct clk_hw_onecell_data axg_aoclk_onecell_data = { @@ -132,6 +275,12 @@ static const struct clk_hw_onecell_data axg_aoclk_onecell_data = { [CLKID_AO_SAR_ADC_SEL] = &axg_aoclk_saradc_mux.hw, [CLKID_AO_SAR_ADC_DIV] = &axg_aoclk_saradc_div.hw, [CLKID_AO_SAR_ADC_CLK] = &axg_aoclk_saradc_gate.hw, + [CLKID_AO_CTS_OSCIN] = &axg_aoclk_cts_oscin.hw, + [CLKID_AO_32K_PRE] = &axg_aoclk_32k_pre.hw, + [CLKID_AO_32K_DIV] = &axg_aoclk_32k_div.hw, + [CLKID_AO_32K_SEL] = &axg_aoclk_32k_sel.hw, + [CLKID_AO_32K] = &axg_aoclk_32k.hw, + [CLKID_AO_CTS_RTC_OSCIN] = &axg_aoclk_cts_rtc_oscin.hw, }, .num = NR_CLKS, }; diff --git a/drivers/clk/meson/axg-aoclk.h b/drivers/clk/meson/axg-aoclk.h index 91384d8dd844..3cc27e85170f 100644 --- a/drivers/clk/meson/axg-aoclk.h +++ b/drivers/clk/meson/axg-aoclk.h @@ -10,18 +10,7 @@ #ifndef __AXG_AOCLKC_H #define __AXG_AOCLKC_H -#define NR_CLKS 11 -/* AO Configuration Clock registers offsets - * Register offsets from the data sheet must be multiplied by 4. - */ -#define AO_RTI_PWR_CNTL_REG1 0x0C -#define AO_RTI_PWR_CNTL_REG0 0x10 -#define AO_RTI_GEN_CNTL_REG0 0x40 -#define AO_OSCIN_CNTL 0x58 -#define AO_CRT_CLK_CNTL1 0x68 -#define AO_SAR_CLK 0x90 -#define AO_RTC_ALT_CLK_CNTL0 0x94 -#define AO_RTC_ALT_CLK_CNTL1 0x98 +#define NR_CLKS 17 #include #include From cce433e6bc53b3984274c61c78d0dadb87483646 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 8 Dec 2018 18:12:44 +0100 Subject: [PATCH 0089/4277] clk: meson: meson8b: use a separate clock table for Meson8 The Meson8 SoC is slightly different compared to Meson8b and Meson8m2 because it does not have the glitch-free Mali GPU clock mux. For Meson8b and Meson8m2 there are currently no known differences. Add a separate clk_hw_onecell_data table for Meson8 so these differences can be implemented. For now meson8_hw_onecell_data is a clone of our existing meson8b_hw_onecell_data. Signed-off-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181208171247.22238-3-martin.blumenstingl@googlemail.com --- drivers/clk/meson/meson8b.c | 203 ++++++++++++++++++++++++++++++++++-- 1 file changed, 197 insertions(+), 6 deletions(-) diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 950d0e548c75..0b9353d8d4fd 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -1659,6 +1659,185 @@ static MESON_GATE(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1); static MESON_GATE(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2); static MESON_GATE(meson8b_ao_iface, HHI_GCLK_AO, 3); +static struct clk_hw_onecell_data meson8_hw_onecell_data = { + .hws = { + [CLKID_XTAL] = &meson8b_xtal.hw, + [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw, + [CLKID_PLL_VID] = &meson8b_vid_pll.hw, + [CLKID_PLL_SYS] = &meson8b_sys_pll.hw, + [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw, + [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw, + [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw, + [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw, + [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw, + [CLKID_CPUCLK] = &meson8b_cpu_clk.hw, + [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw, + [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw, + [CLKID_CLK81] = &meson8b_clk81.hw, + [CLKID_DDR] = &meson8b_ddr.hw, + [CLKID_DOS] = &meson8b_dos.hw, + [CLKID_ISA] = &meson8b_isa.hw, + [CLKID_PL301] = &meson8b_pl301.hw, + [CLKID_PERIPHS] = &meson8b_periphs.hw, + [CLKID_SPICC] = &meson8b_spicc.hw, + [CLKID_I2C] = &meson8b_i2c.hw, + [CLKID_SAR_ADC] = &meson8b_sar_adc.hw, + [CLKID_SMART_CARD] = &meson8b_smart_card.hw, + [CLKID_RNG0] = &meson8b_rng0.hw, + [CLKID_UART0] = &meson8b_uart0.hw, + [CLKID_SDHC] = &meson8b_sdhc.hw, + [CLKID_STREAM] = &meson8b_stream.hw, + [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw, + [CLKID_SDIO] = &meson8b_sdio.hw, + [CLKID_ABUF] = &meson8b_abuf.hw, + [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw, + [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw, + [CLKID_SPI] = &meson8b_spi.hw, + [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw, + [CLKID_ETH] = &meson8b_eth.hw, + [CLKID_DEMUX] = &meson8b_demux.hw, + [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw, + [CLKID_IEC958] = &meson8b_iec958.hw, + [CLKID_I2S_OUT] = &meson8b_i2s_out.hw, + [CLKID_AMCLK] = &meson8b_amclk.hw, + [CLKID_AIFIFO2] = &meson8b_aififo2.hw, + [CLKID_MIXER] = &meson8b_mixer.hw, + [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw, + [CLKID_ADC] = &meson8b_adc.hw, + [CLKID_BLKMV] = &meson8b_blkmv.hw, + [CLKID_AIU] = &meson8b_aiu.hw, + [CLKID_UART1] = &meson8b_uart1.hw, + [CLKID_G2D] = &meson8b_g2d.hw, + [CLKID_USB0] = &meson8b_usb0.hw, + [CLKID_USB1] = &meson8b_usb1.hw, + [CLKID_RESET] = &meson8b_reset.hw, + [CLKID_NAND] = &meson8b_nand.hw, + [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw, + [CLKID_USB] = &meson8b_usb.hw, + [CLKID_VDIN1] = &meson8b_vdin1.hw, + [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw, + [CLKID_EFUSE] = &meson8b_efuse.hw, + [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw, + [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw, + [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw, + [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw, + [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw, + [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw, + [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw, + [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw, + [CLKID_DVIN] = &meson8b_dvin.hw, + [CLKID_UART2] = &meson8b_uart2.hw, + [CLKID_SANA] = &meson8b_sana.hw, + [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw, + [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw, + [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw, + [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw, + [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw, + [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw, + [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw, + [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw, + [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw, + [CLKID_DAC_CLK] = &meson8b_dac_clk.hw, + [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw, + [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw, + [CLKID_ENC480P] = &meson8b_enc480p.hw, + [CLKID_RNG1] = &meson8b_rng1.hw, + [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw, + [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw, + [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw, + [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw, + [CLKID_EDP] = &meson8b_edp.hw, + [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw, + [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw, + [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw, + [CLKID_AO_IFACE] = &meson8b_ao_iface.hw, + [CLKID_MPLL0] = &meson8b_mpll0.hw, + [CLKID_MPLL1] = &meson8b_mpll1.hw, + [CLKID_MPLL2] = &meson8b_mpll2.hw, + [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw, + [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw, + [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw, + [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw, + [CLKID_CPU_IN_DIV2] = &meson8b_cpu_in_div2.hw, + [CLKID_CPU_IN_DIV3] = &meson8b_cpu_in_div3.hw, + [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw, + [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw, + [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw, + [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw, + [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw, + [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw, + [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw, + [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw, + [CLKID_NAND_SEL] = &meson8b_nand_clk_sel.hw, + [CLKID_NAND_DIV] = &meson8b_nand_clk_div.hw, + [CLKID_NAND_CLK] = &meson8b_nand_clk_gate.hw, + [CLKID_PLL_FIXED_DCO] = &meson8b_fixed_pll_dco.hw, + [CLKID_HDMI_PLL_DCO] = &meson8b_hdmi_pll_dco.hw, + [CLKID_PLL_SYS_DCO] = &meson8b_sys_pll_dco.hw, + [CLKID_CPU_CLK_DIV2] = &meson8b_cpu_clk_div2.hw, + [CLKID_CPU_CLK_DIV3] = &meson8b_cpu_clk_div3.hw, + [CLKID_CPU_CLK_DIV4] = &meson8b_cpu_clk_div4.hw, + [CLKID_CPU_CLK_DIV5] = &meson8b_cpu_clk_div5.hw, + [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw, + [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw, + [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw, + [CLKID_ABP_SEL] = &meson8b_abp_clk_sel.hw, + [CLKID_ABP] = &meson8b_abp_clk_gate.hw, + [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw, + [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw, + [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw, + [CLKID_AXI] = &meson8b_axi_clk_gate.hw, + [CLKID_L2_DRAM_SEL] = &meson8b_l2_dram_clk_sel.hw, + [CLKID_L2_DRAM] = &meson8b_l2_dram_clk_gate.hw, + [CLKID_HDMI_PLL_LVDS_OUT] = &meson8b_hdmi_pll_lvds_out.hw, + [CLKID_HDMI_PLL_HDMI_OUT] = &meson8b_hdmi_pll_hdmi_out.hw, + [CLKID_VID_PLL_IN_SEL] = &meson8b_vid_pll_in_sel.hw, + [CLKID_VID_PLL_IN_EN] = &meson8b_vid_pll_in_en.hw, + [CLKID_VID_PLL_PRE_DIV] = &meson8b_vid_pll_pre_div.hw, + [CLKID_VID_PLL_POST_DIV] = &meson8b_vid_pll_post_div.hw, + [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw, + [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw, + [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw, + [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw, + [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw, + [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw, + [CLKID_VCLK_DIV4_DIV] = &meson8b_vclk_div4_div.hw, + [CLKID_VCLK_DIV4] = &meson8b_vclk_div4_div_gate.hw, + [CLKID_VCLK_DIV6_DIV] = &meson8b_vclk_div6_div.hw, + [CLKID_VCLK_DIV6] = &meson8b_vclk_div6_div_gate.hw, + [CLKID_VCLK_DIV12_DIV] = &meson8b_vclk_div12_div.hw, + [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw, + [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw, + [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw, + [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw, + [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw, + [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw, + [CLKID_VCLK2_DIV4_DIV] = &meson8b_vclk2_div4_div.hw, + [CLKID_VCLK2_DIV4] = &meson8b_vclk2_div4_div_gate.hw, + [CLKID_VCLK2_DIV6_DIV] = &meson8b_vclk2_div6_div.hw, + [CLKID_VCLK2_DIV6] = &meson8b_vclk2_div6_div_gate.hw, + [CLKID_VCLK2_DIV12_DIV] = &meson8b_vclk2_div12_div.hw, + [CLKID_VCLK2_DIV12] = &meson8b_vclk2_div12_div_gate.hw, + [CLKID_CTS_ENCT_SEL] = &meson8b_cts_enct_sel.hw, + [CLKID_CTS_ENCT] = &meson8b_cts_enct.hw, + [CLKID_CTS_ENCP_SEL] = &meson8b_cts_encp_sel.hw, + [CLKID_CTS_ENCP] = &meson8b_cts_encp.hw, + [CLKID_CTS_ENCI_SEL] = &meson8b_cts_enci_sel.hw, + [CLKID_CTS_ENCI] = &meson8b_cts_enci.hw, + [CLKID_HDMI_TX_PIXEL_SEL] = &meson8b_hdmi_tx_pixel_sel.hw, + [CLKID_HDMI_TX_PIXEL] = &meson8b_hdmi_tx_pixel.hw, + [CLKID_CTS_ENCL_SEL] = &meson8b_cts_encl_sel.hw, + [CLKID_CTS_ENCL] = &meson8b_cts_encl.hw, + [CLKID_CTS_VDAC0_SEL] = &meson8b_cts_vdac0_sel.hw, + [CLKID_CTS_VDAC0] = &meson8b_cts_vdac0.hw, + [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw, + [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw, + [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw, + [CLK_NR_CLKS] = NULL, + }, + .num = CLK_NR_CLKS, +}; + static struct clk_hw_onecell_data meson8b_hw_onecell_data = { .hws = { [CLKID_XTAL] = &meson8b_xtal.hw, @@ -2132,7 +2311,6 @@ static int meson8b_cpu_clk_notifier_cb(struct notifier_block *nb, static struct meson8b_nb_data meson8b_cpu_nb_data = { .nb.notifier_call = meson8b_cpu_clk_notifier_cb, - .onecell_data = &meson8b_hw_onecell_data, }; static const struct regmap_config clkc_regmap_config = { @@ -2141,7 +2319,8 @@ static const struct regmap_config clkc_regmap_config = { .reg_stride = 4, }; -static void __init meson8b_clkc_init(struct device_node *np) +static void __init meson8b_clkc_init_common(struct device_node *np, + struct clk_hw_onecell_data *clk_hw_onecell_data) { struct meson8b_clk_reset *rstc; const char *notifier_clk_name; @@ -2192,14 +2371,16 @@ static void __init meson8b_clkc_init(struct device_node *np) */ for (i = CLKID_XTAL; i < CLK_NR_CLKS; i++) { /* array might be sparse */ - if (!meson8b_hw_onecell_data.hws[i]) + if (!clk_hw_onecell_data->hws[i]) continue; - ret = clk_hw_register(NULL, meson8b_hw_onecell_data.hws[i]); + ret = clk_hw_register(NULL, clk_hw_onecell_data->hws[i]); if (ret) return; } + meson8b_cpu_nb_data.onecell_data = clk_hw_onecell_data; + /* * FIXME we shouldn't program the muxes in notifier handlers. The * tricky programming sequence will be handled by the forthcoming @@ -2215,13 +2396,23 @@ static void __init meson8b_clkc_init(struct device_node *np) } ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, - &meson8b_hw_onecell_data); + clk_hw_onecell_data); if (ret) pr_err("%s: failed to register clock provider\n", __func__); } +static void __init meson8_clkc_init(struct device_node *np) +{ + return meson8b_clkc_init_common(np, &meson8_hw_onecell_data); +} + +static void __init meson8b_clkc_init(struct device_node *np) +{ + return meson8b_clkc_init_common(np, &meson8b_hw_onecell_data); +} + CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc", - meson8b_clkc_init); + meson8_clkc_init); CLK_OF_DECLARE_DRIVER(meson8b_clkc, "amlogic,meson8b-clkc", meson8b_clkc_init); CLK_OF_DECLARE_DRIVER(meson8m2_clkc, "amlogic,meson8m2-clkc", From 74e1f2521f16ffe38f1e49681a96588082f4406b Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 8 Dec 2018 18:12:45 +0100 Subject: [PATCH 0090/4277] clk: meson: meson8b: add the GPU clock tree Add the GPU clock tree on Meson8, Meson8b and Meson8m2. The GPU clock tree on Meson8b and Meson8m2 is almost identical to the one one GXBB: - there's a glitch-free mux at HHI_MALI_CLK_CNTL[31] - there are two identical parents for this mux: mali_0 and mali_1, each with a gate, divider and mux - the parents of mali_0_sel and mali_1_sel are identical to GXBB except there's no GP0_PLL on these 32-bit SoCs Meson8 is different because it does not have the glitch-free mux. Instead if only has the mali_0 clock tree. The parents of mali_0_sel are identical to the ones on Meson8b and Meson8m2. Signed-off-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20181208171247.22238-4-martin.blumenstingl@googlemail.com --- drivers/clk/meson/meson8b.c | 146 ++++++++++++++++++++++++++++++++++++ drivers/clk/meson/meson8b.h | 9 ++- 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 0b9353d8d4fd..748552c5f6c8 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -1573,6 +1573,135 @@ static struct clk_regmap meson8b_hdmi_sys = { }, }; +/* + * The MALI IP is clocked by two identical clocks (mali_0 and mali_1) + * muxed by a glitch-free switch on Meson8b and Meson8m2. Meson8 only + * has mali_0 and no glitch-free mux. + */ +static const char * const meson8b_mali_0_1_parent_names[] = { + "xtal", "mpll2", "mpll1", "fclk_div7", "fclk_div4", "fclk_div3", + "fclk_div5" +}; + +static u32 meson8b_mali_0_1_mux_table[] = { 0, 2, 3, 4, 5, 6, 7 }; + +static struct clk_regmap meson8b_mali_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 9, + .table = meson8b_mali_0_1_mux_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = meson8b_mali_0_1_parent_names, + .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_names), + /* + * Don't propagate rate changes up because the only changeable + * parents are mpll1 and mpll2 but we need those for audio and + * RGMII (Ethernet). We don't want to change the audio or + * Ethernet clocks when setting the GPU frequency. + */ + .flags = 0, + }, +}; + +static struct clk_regmap meson8b_mali_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 25, + .table = meson8b_mali_0_1_mux_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = meson8b_mali_0_1_parent_names, + .num_parents = ARRAY_SIZE(meson8b_mali_0_1_parent_names), + /* + * Don't propagate rate changes up because the only changeable + * parents are mpll1 and mpll2 but we need those for audio and + * RGMII (Ethernet). We don't want to change the audio or + * Ethernet clocks when setting the GPU frequency. + */ + .flags = 0, + }, +}; + +static struct clk_regmap meson8b_mali_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap meson8b_mali = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "mali_0", "mali_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(meson8b_ddr, HHI_GCLK_MPEG0, 0); @@ -1833,6 +1962,9 @@ static struct clk_hw_onecell_data meson8_hw_onecell_data = { [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw, [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw, [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw, + [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw, + [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw, + [CLKID_MALI] = &meson8b_mali_0.hw, [CLK_NR_CLKS] = NULL, }, .num = CLK_NR_CLKS, @@ -2012,6 +2144,13 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_HDMI_SYS_SEL] = &meson8b_hdmi_sys_sel.hw, [CLKID_HDMI_SYS_DIV] = &meson8b_hdmi_sys_div.hw, [CLKID_HDMI_SYS] = &meson8b_hdmi_sys.hw, + [CLKID_MALI_0_SEL] = &meson8b_mali_0_sel.hw, + [CLKID_MALI_0_DIV] = &meson8b_mali_0_div.hw, + [CLKID_MALI_0] = &meson8b_mali_0.hw, + [CLKID_MALI_1_SEL] = &meson8b_mali_1_sel.hw, + [CLKID_MALI_1_DIV] = &meson8b_mali_1_div.hw, + [CLKID_MALI_1] = &meson8b_mali_1.hw, + [CLKID_MALI] = &meson8b_mali.hw, [CLK_NR_CLKS] = NULL, }, .num = CLK_NR_CLKS, @@ -2167,6 +2306,13 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = { &meson8b_hdmi_sys_sel, &meson8b_hdmi_sys_div, &meson8b_hdmi_sys, + &meson8b_mali_0_sel, + &meson8b_mali_0_div, + &meson8b_mali_0, + &meson8b_mali_1_sel, + &meson8b_mali_1_div, + &meson8b_mali_1, + &meson8b_mali, }; static const struct meson8b_clk_reset_line { diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index 87fba739af81..f212e2304ff5 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -33,6 +33,7 @@ #define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ #define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */ #define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */ +#define HHI_MALI_CLK_CNTL 0x1b0 /* 0x6c offset in data sheet */ #define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */ #define HHI_NAND_CLK_CNTL 0x25c /* 0x97 offset in data sheet */ #define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ @@ -139,8 +140,14 @@ #define CLKID_HDMI_SYS_SEL 172 #define CLKID_HDMI_SYS_DIV 173 #define CLKID_HDMI_SYS 174 +#define CLKID_MALI_0_SEL 175 +#define CLKID_MALI_0_DIV 176 +#define CLKID_MALI_0 177 +#define CLKID_MALI_1_SEL 178 +#define CLKID_MALI_1_DIV 179 +#define CLKID_MALI_1 180 -#define CLK_NR_CLKS 175 +#define CLK_NR_CLKS 181 /* * include the CLKID and RESETID that have From f5fd9fd4000984f19db689282054953981a50534 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Jul 2018 18:00:18 +0300 Subject: [PATCH 0091/4277] drm/etnaviv: fix some off by one bugs The ->nr_signal is the supposed to be the number of elements in the ->signal array. There was one place where it was 5 but it was supposed to be 4. That looks like a copy and paste bug. There were also two checks that were off by one. Fixes: 9e2c2e273012 ("drm/etnaviv: add infrastructure to query perf counter") Signed-off-by: Dan Carpenter Reviewed-by: Christian Gmeiner Tested-by: Christian Gmeiner Signed-off-by: Lucas Stach --- drivers/gpu/drm/etnaviv/etnaviv_perfmon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c index 9980d81a26e3..4227a4006c34 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c @@ -113,7 +113,7 @@ static const struct etnaviv_pm_domain doms_3d[] = { .name = "PE", .profile_read = VIVS_MC_PROFILE_PE_READ, .profile_config = VIVS_MC_PROFILE_CONFIG0, - .nr_signals = 5, + .nr_signals = 4, .signal = (const struct etnaviv_pm_signal[]) { { "PIXEL_COUNT_KILLED_BY_COLOR_PIPE", @@ -435,7 +435,7 @@ int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu, dom = meta->domains + signal->domain; - if (signal->iter > dom->nr_signals) + if (signal->iter >= dom->nr_signals) return -EINVAL; sig = &dom->signal[signal->iter]; @@ -461,7 +461,7 @@ int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r, dom = meta->domains + r->domain; - if (r->signal > dom->nr_signals) + if (r->signal >= dom->nr_signals) return -EINVAL; return 0; From 72ac64b84b2ecb5c980354a189018ede14d61035 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 23 Nov 2018 16:26:04 +0100 Subject: [PATCH 0092/4277] drm/etnaviv: move job context pointer to etnaviv_gem_submit The context isn't really related to the cmdbuf, but is a property of the job. This has been missed when moving to a properly refcounted etnaviv_gem_submit. Signed-off-by: Lucas Stach Reviewed-by: Christian Gmeiner --- drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h | 2 -- drivers/gpu/drm/etnaviv/etnaviv_gem.h | 1 + drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_sched.c | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h index acb68c698363..4d5d1a77eb2a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h @@ -15,8 +15,6 @@ struct etnaviv_perfmon_request; struct etnaviv_cmdbuf { /* suballocator this cmdbuf is allocated from */ struct etnaviv_cmdbuf_suballoc *suballoc; - /* user context key, must be unique between all active users */ - struct etnaviv_file_private *ctx; /* cmdbuf properties */ int suballoc_offset; void *vaddr; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index 76079c2291f8..f0abb744ef95 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -95,6 +95,7 @@ struct etnaviv_gem_submit_bo { struct etnaviv_gem_submit { struct drm_sched_job sched_job; struct kref refcount; + struct etnaviv_file_private *ctx; struct etnaviv_gpu *gpu; struct dma_fence *out_fence, *in_fence; int out_fence_id; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 30875f8f2933..b2fe3446bfbc 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -506,7 +506,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, if (ret) goto err_submit_objects; - submit->cmdbuf.ctx = file->driver_priv; + submit->ctx = file->driver_priv; submit->exec_state = args->exec_state; submit->flags = args->flags; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 49a6763693f1..7e4f550dde5b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -150,7 +150,7 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, mutex_lock(&submit->gpu->fence_lock); ret = drm_sched_job_init(&submit->sched_job, sched_entity, - submit->cmdbuf.ctx); + submit->ctx); if (ret) goto out_unlock; From 5f5b4fa522e630cfa154c59ae7649b5568913f96 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 Jan 2019 13:08:30 -0500 Subject: [PATCH 0093/4277] ipu3: add missing #include Lots of warning due to non-static functions are generated because the headers with define them were not included. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3-css-params.c | 1 + drivers/staging/media/ipu3/ipu3-dmamap.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 776206ded83b..053edce54b71 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -6,6 +6,7 @@ #include "ipu3-css.h" #include "ipu3-css-fw.h" #include "ipu3-tables.h" +#include "ipu3-css-params.h" #define DIV_ROUND_CLOSEST_DOWN(a, b) (((a) + ((b) / 2) - 1) / (b)) #define roundclosest_down(a, b) (DIV_ROUND_CLOSEST_DOWN(a, b) * (b)) diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c index 93a393d4e15e..5bed01d5b8df 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.c +++ b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -12,6 +12,7 @@ #include "ipu3.h" #include "ipu3-css-pool.h" #include "ipu3-mmu.h" +#include "ipu3-dmamap.h" /* * Free a buffer allocated by ipu3_dmamap_alloc_buffer() From e2d8ffe2e76028457759988ba6216fd13eeea01b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 13 Nov 2018 03:52:18 -0500 Subject: [PATCH 0094/4277] media: v4l2-mem2mem: add v4l2_m2m_buf_copy_data helper function Memory-to-memory devices should copy various parts of struct v4l2_buffer from the output buffer to the capture buffer. Add a helper function that does that to simplify the driver code. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Reviewed-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 20 ++++++++++++++++++++ include/media/v4l2-mem2mem.h | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 5bbdec55b7d7..631f4e2aa942 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -975,6 +975,26 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); +void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags) +{ + u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + + if (copy_frame_flags) + mask |= V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME; + + cap_vb->vb2_buf.timestamp = out_vb->vb2_buf.timestamp; + + if (out_vb->flags & V4L2_BUF_FLAG_TIMECODE) + cap_vb->timecode = out_vb->timecode; + cap_vb->field = out_vb->field; + cap_vb->flags &= ~mask; + cap_vb->flags |= out_vb->flags & mask; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_data); + void v4l2_m2m_request_queue(struct media_request *req) { struct media_request_object *obj, *obj_safe; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 5467264771ec..43e447dcf69d 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -622,6 +622,26 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->cap_q_ctx, idx); } +/** + * v4l2_m2m_buf_copy_data() - copy buffer data from the output buffer to the + * capture buffer + * + * @out_vb: the output buffer that is the source of the data. + * @cap_vb: the capture buffer that will receive the data. + * @copy_frame_flags: copy the KEY/B/PFRAME flags as well. + * + * This helper function copies the timestamp, timecode (if the TIMECODE + * buffer flag was set), field and the TIMECODE, KEYFRAME, BFRAME, PFRAME + * and TSTAMP_SRC_MASK flags from @out_vb to @cap_vb. + * + * If @copy_frame_flags is false, then the KEYFRAME, BFRAME and PFRAME + * flags are not copied. This is typically needed for encoders that + * set this bits explicitly. + */ +void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags); + /* v4l2 request helper */ void v4l2_m2m_request_queue(struct media_request *req); From 7aca565ee3d0febeb980545a5ce72452b401ac90 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:51:20 -0400 Subject: [PATCH 0095/4277] media: vim2m: use v4l2_m2m_buf_copy_data Use the new v4l2_m2m_buf_copy_data() function in vim2m. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index d01821a6906a..33397d4a1402 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -241,17 +241,7 @@ static int device_process(struct vim2m_ctx *ctx, out_vb->sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; in_vb->sequence = q_data->sequence++; - out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp; - - if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE) - out_vb->timecode = in_vb->timecode; - out_vb->field = in_vb->field; - out_vb->flags = in_vb->flags & - (V4L2_BUF_FLAG_TIMECODE | - V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME | - V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + v4l2_m2m_buf_copy_data(out_vb, in_vb, true); switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: From 96bddd5ff590e11b47255cf06af5a0d5ccaa7807 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:51:30 -0400 Subject: [PATCH 0096/4277] media: vicodec: use v4l2_m2m_buf_copy_data Use the new v4l2_m2m_buf_copy_data() function in vicodec. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 0d7876f5acf0..2378582d9790 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -190,18 +190,8 @@ static int device_process(struct vicodec_ctx *ctx, } dst_vb->sequence = q_dst->sequence++; - dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; - - if (src_vb->flags & V4L2_BUF_FLAG_TIMECODE) - dst_vb->timecode = src_vb->timecode; - dst_vb->field = src_vb->field; dst_vb->flags &= ~V4L2_BUF_FLAG_LAST; - dst_vb->flags |= src_vb->flags & - (V4L2_BUF_FLAG_TIMECODE | - V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME | - V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + v4l2_m2m_buf_copy_data(src_vb, dst_vb, !ctx->is_enc); return 0; } From 0e9109ffded1cfa2aeb5617a9a683ca3467e6e99 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 13 Nov 2018 03:32:25 -0500 Subject: [PATCH 0097/4277] media: buffer.rst: clean up timecode documentation V4L2_BUF_FLAG_TIMECODE is not video capture specific, so drop that part. The 'Timecodes' section was a bit messy, so that's cleaned up. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Reviewed-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/buffer.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst index 86878bb0087f..81ffdcb89057 100644 --- a/Documentation/media/uapi/v4l/buffer.rst +++ b/Documentation/media/uapi/v4l/buffer.rst @@ -230,8 +230,7 @@ struct v4l2_buffer * - struct :c:type:`v4l2_timecode` - ``timecode`` - - - When ``type`` is ``V4L2_BUF_TYPE_VIDEO_CAPTURE`` and the - ``V4L2_BUF_FLAG_TIMECODE`` flag is set in ``flags``, this + - When the ``V4L2_BUF_FLAG_TIMECODE`` flag is set in ``flags``, this structure contains a frame timecode. In :c:type:`V4L2_FIELD_ALTERNATE ` mode the top and bottom field contain the same timecode. Timecodes are intended to @@ -714,10 +713,10 @@ enum v4l2_memory Timecodes ========= -The struct :c:type:`v4l2_timecode` structure is designed to hold a -:ref:`smpte12m` or similar timecode. (struct -struct :c:type:`timeval` timestamps are stored in struct -:c:type:`v4l2_buffer` field ``timestamp``.) +The :c:type:`v4l2_buffer_timecode` structure is designed to hold a +:ref:`smpte12m` or similar timecode. +(struct :c:type:`timeval` timestamps are stored in the struct +:c:type:`v4l2_buffer` ``timestamp`` field.) .. c:type:: v4l2_timecode From c2eb8effb265ac5cdd960d8e61ecb931e9c767cd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:50:34 -0400 Subject: [PATCH 0098/4277] media: videodev2.h: add v4l2_timeval_to_ns inline function We want to be able to uniquely identify buffers for stateless codecs. The internal timestamp (a u64) as stored internally in the kernel is a suitable candidate for that, but in struct v4l2_buffer it is represented as a struct timeval. Add a v4l2_timeval_to_ns() function that converts the struct timeval into a u64 in the same way that the kernel does. This makes it possible to use this u64 elsewhere as a unique identifier of the buffer. Since timestamps are also copied from the output buffer to the corresponding capture buffer(s) by M2M devices, the u64 can be used to refer to both output and capture buffers. The plan is that in the future we redesign struct v4l2_buffer and use u64 for the timestamp instead of a struct timeval (which has lots of problems with 32 vs 64 bit and y2038 layout changes), and then there is no more need to use this function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index b5671ce2724f..d6eed479c3a6 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -973,6 +973,18 @@ struct v4l2_buffer { }; }; +/** + * v4l2_timeval_to_ns - Convert timeval to nanoseconds + * @ts: pointer to the timeval variable to be converted + * + * Returns the scalar nanosecond representation of the timeval + * parameter. + */ +static inline __u64 v4l2_timeval_to_ns(const struct timeval *tv) +{ + return (__u64)tv->tv_sec * 1000000000ULL + tv->tv_usec * 1000; +} + /* Flags for 'flags' field */ /* Buffer is mapped (flag) */ #define V4L2_BUF_FLAG_MAPPED 0x00000001 From 245ede423b43a6e081e94e0e5d4e895bd1f31228 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:51:01 -0400 Subject: [PATCH 0099/4277] media: vb2: add vb2_find_timestamp() Use v4l2_timeval_to_ns instead of timeval_to_ns to ensure that both kernelspace and userspace will use the same conversion function. Next add a new vb2_find_timestamp() function to find buffers with a specific timestamp. This function will only look at DEQUEUED and DONE buffers, i.e. buffers that are already processed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-v4l2.c | 19 ++++++++++++++++++- include/media/videobuf2-v4l2.h | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 3a0ca2f9854f..75ea90e795d8 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -143,7 +143,7 @@ static void __copy_timestamp(struct vb2_buffer *vb, const void *pb) * and the timecode field and flag if needed. */ if (q->copy_timestamp) - vb->timestamp = timeval_to_ns(&b->timestamp); + vb->timestamp = v4l2_timeval_to_ns(&b->timestamp); vbuf->flags |= b->flags & V4L2_BUF_FLAG_TIMECODE; if (b->flags & V4L2_BUF_FLAG_TIMECODE) vbuf->timecode = b->timecode; @@ -589,6 +589,23 @@ static const struct vb2_buf_ops v4l2_buf_ops = { .copy_timestamp = __copy_timestamp, }; +int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, + unsigned int start_idx) +{ + unsigned int i; + + for (i = start_idx; i < q->num_buffers; i++) { + struct vb2_buffer *vb = q->bufs[i]; + + if ((vb->state == VB2_BUF_STATE_DEQUEUED || + vb->state == VB2_BUF_STATE_DONE) && + vb->timestamp == timestamp) + return i; + } + return -1; +} +EXPORT_SYMBOL_GPL(vb2_find_timestamp); + /* * vb2_querybuf() - query video buffer information * @q: videobuf queue diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 727855463838..a9961bc776dc 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -55,6 +55,23 @@ struct vb2_v4l2_buffer { #define to_vb2_v4l2_buffer(vb) \ container_of(vb, struct vb2_v4l2_buffer, vb2_buf) +/** + * vb2_find_timestamp() - Find buffer with given timestamp in the queue + * + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @timestamp: the timestamp to find. Only buffers in state DEQUEUED or DONE + * are considered. + * @start_idx: the start index (usually 0) in the buffer array to start + * searching from. Note that there may be multiple buffers + * with the same timestamp value, so you can restart the search + * by setting @start_idx to the previously found index + 1. + * + * Returns the buffer index of the buffer with the given @timestamp, or + * -1 if no buffer with @timestamp was found. + */ +int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, + unsigned int start_idx); + int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b); /** From d998e03e322fc497454f584adfc35743713a44c5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Nov 2018 04:16:21 -0500 Subject: [PATCH 0100/4277] media: cedrus: identify buffers by timestamp Use the new v4l2_m2m_buf_copy_data helper function and use timestamps to refer to reference frames instead of using buffer indices. Also remove the padding fields in the structs, that's a bad idea. Just use the right types to keep everything aligned. Signed-off-by: Hans Verkuil Tested-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 9 -------- drivers/staging/media/sunxi/cedrus/cedrus.h | 9 +++++--- .../staging/media/sunxi/cedrus/cedrus_dec.c | 2 ++ .../staging/media/sunxi/cedrus/cedrus_mpeg2.c | 23 +++++++++---------- include/media/mpeg2-ctrls.h | 14 ++++------- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5e3806feb5d7..e3bd441fa29a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1661,15 +1661,6 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return -EINVAL; } - if (p_mpeg2_slice_params->backward_ref_index >= VIDEO_MAX_FRAME || - p_mpeg2_slice_params->forward_ref_index >= VIDEO_MAX_FRAME) - return -EINVAL; - - if (p_mpeg2_slice_params->pad || - p_mpeg2_slice_params->picture.pad || - p_mpeg2_slice_params->sequence.pad) - return -EINVAL; - return 0; case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION: diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 3acfdcf83691..4aedd24a9848 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -140,11 +140,14 @@ static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf, } static inline dma_addr_t cedrus_dst_buf_addr(struct cedrus_ctx *ctx, - unsigned int index, - unsigned int plane) + int index, unsigned int plane) { - struct vb2_buffer *buf = ctx->dst_bufs[index]; + struct vb2_buffer *buf; + if (index < 0) + return 0; + + buf = ctx->dst_bufs[index]; return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 591d191d4286..443fb037e1cf 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -50,6 +50,8 @@ void cedrus_device_run(void *priv) break; } + v4l2_m2m_buf_copy_data(run.src, run.dst, true); + dev->dec_ops[ctx->current_codec]->setup(ctx, &run); /* Complete request(s) controls if needed. */ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 9abd39cae38c..cb45fda9aaeb 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -82,7 +82,10 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t fwd_luma_addr, fwd_chroma_addr; dma_addr_t bwd_luma_addr, bwd_chroma_addr; struct cedrus_dev *dev = ctx->dev; + struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; const u8 *matrix; + int forward_idx; + int backward_idx; unsigned int i; u32 reg; @@ -156,23 +159,19 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ + forward_idx = vb2_find_timestamp(cap_q, + slice_params->forward_ref_ts, 0); - fwd_luma_addr = cedrus_dst_buf_addr(ctx, - slice_params->forward_ref_index, - 0); - fwd_chroma_addr = cedrus_dst_buf_addr(ctx, - slice_params->forward_ref_index, - 1); + fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); + fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - bwd_luma_addr = cedrus_dst_buf_addr(ctx, - slice_params->backward_ref_index, - 0); - bwd_chroma_addr = cedrus_dst_buf_addr(ctx, - slice_params->backward_ref_index, - 1); + backward_idx = vb2_find_timestamp(cap_q, + slice_params->backward_ref_ts, 0); + bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); + bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_BWD_REF_LUMA_ADDR, bwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_BWD_REF_CHROMA_ADDR, bwd_chroma_addr); diff --git a/include/media/mpeg2-ctrls.h b/include/media/mpeg2-ctrls.h index d21f40edc09e..6601455b3d5e 100644 --- a/include/media/mpeg2-ctrls.h +++ b/include/media/mpeg2-ctrls.h @@ -30,10 +30,9 @@ struct v4l2_mpeg2_sequence { __u32 vbv_buffer_size; /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence extension */ - __u8 profile_and_level_indication; + __u16 profile_and_level_indication; __u8 progressive_sequence; __u8 chroma_format; - __u8 pad; }; struct v4l2_mpeg2_picture { @@ -51,23 +50,20 @@ struct v4l2_mpeg2_picture { __u8 intra_vlc_format; __u8 alternate_scan; __u8 repeat_first_field; - __u8 progressive_frame; - __u8 pad; + __u16 progressive_frame; }; struct v4l2_ctrl_mpeg2_slice_params { __u32 bit_size; __u32 data_bit_offset; + __u64 backward_ref_ts; + __u64 forward_ref_ts; struct v4l2_mpeg2_sequence sequence; struct v4l2_mpeg2_picture picture; /* ISO/IEC 13818-2, ITU-T Rec. H.262: Slice */ - __u8 quantiser_scale_code; - - __u8 backward_ref_index; - __u8 forward_ref_index; - __u8 pad; + __u32 quantiser_scale_code; }; struct v4l2_ctrl_mpeg2_quantization { From bb03641f8a0d7fc7efcdafb02a50aefe8ef1c17d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Dec 2018 05:30:55 -0500 Subject: [PATCH 0101/4277] media: extended-controls.rst: update the mpeg2 compound controls The layout of the compound controls has changed to fix 32/64 bit alignment issues and the use of timestamps instead of buffer indices to refer to buffers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/uapi/v4l/extended-controls.rst | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 286a2dd7ec36..af4273aa5e85 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1546,17 +1546,23 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - - ``picture`` - Structure with MPEG-2 picture metadata, merging relevant fields from the picture header and picture coding extension parts of the bitstream. - * - __u8 + * - __u64 + - ``backward_ref_ts`` + - Timestamp of the V4L2 capture buffer to use as backward reference, used + with B-coded and P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u64 + - ``forward_ref_ts`` + - Timestamp for the V4L2 capture buffer to use as forward reference, used + with B-coded frames. The timestamp refers to the ``timestamp`` field in + struct :c:type:`v4l2_buffer`. Use the :c:func:`v4l2_timeval_to_ns()` + function to convert the struct :c:type:`timeval` in struct + :c:type:`v4l2_buffer` to a __u64. + * - __u32 - ``quantiser_scale_code`` - Code used to determine the quantization scale to use for the IDCT. - * - __u8 - - ``backward_ref_index`` - - Index for the V4L2 buffer to use as backward reference, used with - B-coded and P-coded frames. - * - __u8 - - ``forward_ref_index`` - - Index for the V4L2 buffer to use as forward reference, used with - B-coded frames. .. c:type:: v4l2_mpeg2_sequence @@ -1577,7 +1583,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - - ``vbv_buffer_size`` - Used to calculate the required size of the video buffering verifier, defined (in bits) as: 16 * 1024 * vbv_buffer_size. - * - __u8 + * - __u16 - ``profile_and_level_indication`` - The current profile and level indication as extracted from the bitstream. @@ -1635,7 +1641,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - * - __u8 - ``repeat_first_field`` - This flag affects the decoding process of progressive frames. - * - __u8 + * - __u16 - ``progressive_frame`` - Indicates whether the current frame is progressive. From b5c61b968def698c21e1aa8ae402cca393fe491f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 7 Jan 2019 11:26:16 -0600 Subject: [PATCH 0102/4277] IB/cm: Use struct_size() in kmalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kmalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kmalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 37980c7564c0..b9416a6fca36 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -4052,8 +4052,7 @@ static void cm_recv_handler(struct ib_mad_agent *mad_agent, atomic_long_inc(&port->counter_group[CM_RECV]. counter[attr_id - CM_ATTR_ID_OFFSET]); - work = kmalloc(sizeof(*work) + sizeof(struct sa_path_rec) * paths, - GFP_KERNEL); + work = kmalloc(struct_size(work, path, paths), GFP_KERNEL); if (!work) { ib_free_recv_mad(mad_recv_wc); return; From 02fc1848414e116b948ebd70e865b7539e5d7b8d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 7 Jan 2019 11:39:54 -0600 Subject: [PATCH 0103/4277] IB/usnic: Use struct_size() in kmalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kmalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kmalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/usnic/usnic_uiom.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index 49275a548751..ce01a59fccc4 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -157,9 +157,8 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, off = 0; while (ret) { - chunk = kmalloc(sizeof(*chunk) + - sizeof(struct scatterlist) * - min_t(int, ret, USNIC_UIOM_PAGE_CHUNK), + chunk = kmalloc(struct_size(chunk, page_list, + min_t(int, ret, USNIC_UIOM_PAGE_CHUNK)), GFP_KERNEL); if (!chunk) { ret = -ENOMEM; From 1b4fd9de6ec7f3722c2b3e08cc5ad171c11f93be Mon Sep 17 00:00:00 2001 From: "French, Nicholas A" Date: Sun, 9 Dec 2018 02:11:18 -0500 Subject: [PATCH 0104/4277] media: lgdt330x: fix lock status reporting A typo in code cleanup commit db9c1007bc07 ("media: lgdt330x: do some cleanups at status logic") broke the FE_HAS_LOCK reporting for 3303 chips by inadvertently modifying the register mask. The broken lock status is critial as it prevents video capture cards from reporting signal strength, scanning for channels, and capturing video. Fix regression by reverting mask change. Cc: stable@vger.kernel.org # Kernel 4.17+ Fixes: db9c1007bc07 ("media: lgdt330x: do some cleanups at status logic") Signed-off-by: Nick French Reviewed-by: Laurent Pinchart Tested-by: Adam Stylinski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/lgdt330x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 96807e134886..8abb1a510a81 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -783,7 +783,7 @@ static int lgdt3303_read_status(struct dvb_frontend *fe, if ((buf[0] & 0x02) == 0x00) *status |= FE_HAS_SYNC; - if ((buf[0] & 0xfd) == 0x01) + if ((buf[0] & 0x01) == 0x01) *status |= FE_HAS_VITERBI | FE_HAS_LOCK; break; default: From d706f52120f69f5bca53db081f682cb56f5186d6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Dec 2018 07:27:54 -0500 Subject: [PATCH 0105/4277] media: Documentation: staging/ipu3-imgu: Fix reference file name The intel-ipu3.h intended-to-be-uAPI header is currently under drivers/staging/media/ipu3/include/, not include/uapi/linux. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/ipu3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index f89b51dafadd..3369aabf919e 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -358,7 +358,7 @@ The source can be located under hal/intel directory. References ========== -.. [#f5] include/uapi/linux/intel-ipu3.h +.. [#f5] drivers/staging/media/ipu3/include/intel-ipu3.h .. [#f1] https://github.com/intel/nvt From 3eefa620d08dc6aa9323e409f1a307783c528004 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Dec 2018 16:53:43 -0500 Subject: [PATCH 0106/4277] media: Documentation: staging/ipu3-imgu: Add license information The driver documentation is under GPL v2 and the uAPI documentation under GNU FDL 1.1+ (without invariant sections) or GPL v2. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../media/uapi/v4l/pixfmt-meta-intel-ipu3.rst | 25 ++++++++++++++++++- Documentation/media/v4l-drivers/ipu3.rst | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst index dc871006b41a..659e58aa9c93 100644 --- a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst +++ b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst @@ -1,4 +1,27 @@ -.. -*- coding: utf-8; mode: rst -*- +.. This file is dual-licensed: you can use it either under the terms +.. of the GPL 2.0 or the GFDL 1.1+ license, at your option. Note that this +.. dual licensing only applies to this file, and not this project as a +.. whole. +.. +.. a) This file is free software; you can redistribute it and/or +.. modify it under the terms of the GNU General Public License version +.. 2.0 as published by the Free Software Foundation. +.. +.. This file 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 version 2.0 for more details. +.. +.. Or, alternatively, +.. +.. b) Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections .. _v4l2-meta-fmt-params: .. _v4l2-meta-fmt-stat-3a: diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index 3369aabf919e..804f37300623 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0 + .. include:: =============================================================== From 07758747a2d7963593421b4e782b0f20a80f58d3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 17 Dec 2018 07:14:35 -0500 Subject: [PATCH 0107/4277] media: platform: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. This is detected by Coccinelle semantic patch. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index dfec813f50a9..692e08ef38c0 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1661,6 +1661,7 @@ static int aspeed_video_probe(struct platform_device *pdev) video->frame_rate = 30; video->dev = &pdev->dev; + spin_lock_init(&video->lock); mutex_init(&video->video_lock); init_waitqueue_head(&video->wait); INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); From 5aad26a7eac5a15f6a87ab83da8bebc3500c1915 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 12:15:58 -0600 Subject: [PATCH 0108/4277] IB/core: Use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/sa_query.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 97e6d7b69abf..7925e45ea88a 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -2342,9 +2342,7 @@ static void ib_sa_add_one(struct ib_device *device) s = rdma_start_port(device); e = rdma_end_port(device); - sa_dev = kzalloc(sizeof *sa_dev + - (e - s + 1) * sizeof (struct ib_sa_port), - GFP_KERNEL); + sa_dev = kzalloc(struct_size(sa_dev, port, e - s + 1), GFP_KERNEL); if (!sa_dev) return; From e6b7b7d8a9b999ffed814267b34443155fae1245 Mon Sep 17 00:00:00 2001 From: Potnuri Bharat Teja Date: Mon, 24 Dec 2018 20:54:51 +0530 Subject: [PATCH 0109/4277] iw_cxgb4: Check for send WR also while posting write with completion WR Inorder to optimize the NVMEoF read IOPs, iw_cxgb4 posts a FW Write with Completion WQE that combines an RDMA Write WR and the subsequent RDMA Send with Invalidate WR. This patch is an extension to it, where it posts a Write with completion for RDMA WRITE WR + RDMA SEND WR combination as well. Reviewed-by: Steve Wise Signed-off-by: Potnuri Bharat Teja Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/qp.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 981ff5cfb5d1..03f4c66c2659 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -632,7 +632,10 @@ static void build_rdma_write_cmpl(struct t4_sq *sq, wcwr->stag_sink = cpu_to_be32(rdma_wr(wr)->rkey); wcwr->to_sink = cpu_to_be64(rdma_wr(wr)->remote_addr); - wcwr->stag_inv = cpu_to_be32(wr->next->ex.invalidate_rkey); + if (wr->next->opcode == IB_WR_SEND) + wcwr->stag_inv = 0; + else + wcwr->stag_inv = cpu_to_be32(wr->next->ex.invalidate_rkey); wcwr->r2 = 0; wcwr->r3 = 0; @@ -726,7 +729,10 @@ static void post_write_cmpl(struct c4iw_qp *qhp, const struct ib_send_wr *wr) /* SEND_WITH_INV swsqe */ swsqe = &qhp->wq.sq.sw_sq[qhp->wq.sq.pidx]; - swsqe->opcode = FW_RI_SEND_WITH_INV; + if (wr->next->opcode == IB_WR_SEND) + swsqe->opcode = FW_RI_SEND; + else + swsqe->opcode = FW_RI_SEND_WITH_INV; swsqe->idx = qhp->wq.sq.pidx; swsqe->complete = 0; swsqe->signaled = send_signaled; @@ -1133,9 +1139,9 @@ int c4iw_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, /* * Fastpath for NVMe-oF target WRITE + SEND_WITH_INV wr chain which is * the response for small NVMEe-oF READ requests. If the chain is - * exactly a WRITE->SEND_WITH_INV and the sgl depths and lengths - * meet the requirements of the fw_ri_write_cmpl_wr work request, - * then build and post the write_cmpl WR. If any of the tests + * exactly a WRITE->SEND_WITH_INV or a WRITE->SEND and the sgl depths + * and lengths meet the requirements of the fw_ri_write_cmpl_wr work + * request, then build and post the write_cmpl WR. If any of the tests * below are not true, then we continue on with the tradtional WRITE * and SEND WRs. */ @@ -1145,7 +1151,8 @@ int c4iw_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, wr && wr->next && !wr->next->next && wr->opcode == IB_WR_RDMA_WRITE && wr->sg_list[0].length && wr->num_sge <= T4_WRITE_CMPL_MAX_SGL && - wr->next->opcode == IB_WR_SEND_WITH_INV && + (wr->next->opcode == IB_WR_SEND || + wr->next->opcode == IB_WR_SEND_WITH_INV) && wr->next->sg_list[0].length == T4_WRITE_CMPL_MAX_CQE && wr->next->num_sge == 1 && num_wrs >= 2) { post_write_cmpl(qhp, wr); From 9c6ccc035c209dda07685e8dba829a203ba17499 Mon Sep 17 00:00:00 2001 From: Lijun Ou Date: Wed, 12 Dec 2018 17:49:06 +0800 Subject: [PATCH 0110/4277] RDMA/hns: Fix the bug with updating rq head pointer when flush cqe When flush cqe with srq, the driver disable to update the rq head pointer into the hardware. Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 3a669451cf86..6c36e460b00b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -3789,13 +3789,16 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp, roce_set_field(qpc_mask->byte_160_sq_ci_pi, V2_QPC_BYTE_160_SQ_PRODUCER_IDX_M, V2_QPC_BYTE_160_SQ_PRODUCER_IDX_S, 0); - roce_set_field(context->byte_84_rq_ci_pi, + + if (!ibqp->srq) { + roce_set_field(context->byte_84_rq_ci_pi, V2_QPC_BYTE_84_RQ_PRODUCER_IDX_M, V2_QPC_BYTE_84_RQ_PRODUCER_IDX_S, hr_qp->rq.head); - roce_set_field(qpc_mask->byte_84_rq_ci_pi, + roce_set_field(qpc_mask->byte_84_rq_ci_pi, V2_QPC_BYTE_84_RQ_PRODUCER_IDX_M, V2_QPC_BYTE_84_RQ_PRODUCER_IDX_S, 0); + } } if (attr_mask & IB_QP_AV) { @@ -4281,7 +4284,8 @@ static void hns_roce_set_qps_to_err(struct hns_roce_dev *hr_dev, u32 qpn) if (hr_qp->ibqp.uobject) { if (hr_qp->sdb_en == 1) { hr_qp->sq.head = *(int *)(hr_qp->sdb.virt_addr); - hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr); + if (hr_qp->rdb_en == 1) + hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr); } else { dev_warn(hr_dev->dev, "flush cqe is unsupported in userspace!\n"); return; From 4d103905eb1e4f14cb62fcf962c9d35da7005dea Mon Sep 17 00:00:00 2001 From: Lijun Ou Date: Wed, 12 Dec 2018 17:49:07 +0800 Subject: [PATCH 0111/4277] RDMA/hns: Bugfix for the scene without receiver queue In some application scenario, the user could not have receive queue when run rdma write or read operation. Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_qp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 54031c5b53fa..b9ba140b50d4 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -526,7 +526,8 @@ static int hns_roce_qp_has_sq(struct ib_qp_init_attr *attr) static int hns_roce_qp_has_rq(struct ib_qp_init_attr *attr) { if (attr->qp_type == IB_QPT_XRC_INI || - attr->qp_type == IB_QPT_XRC_TGT || attr->srq) + attr->qp_type == IB_QPT_XRC_TGT || attr->srq || + !attr->cap.max_recv_wr) return 0; return 1; From 44754b95dd35ee07c462b5425ae9c4cde8c7e7c8 Mon Sep 17 00:00:00 2001 From: Lijun Ou Date: Wed, 12 Dec 2018 17:49:08 +0800 Subject: [PATCH 0112/4277] RDMA/hns: Add constraint on the setting of local ACK timeout According to IB protocol, local ACK timeout shall be a 5 bit value. Currently, hip08 could not support the possible max value 31. Fail the request in this case. Signed-off-by: Yixian Liu Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 6c36e460b00b..748dc2b44a3b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -3686,10 +3686,16 @@ static int modify_qp_rtr_to_rts(struct ib_qp *ibqp, V2_QPC_BYTE_212_LSN_S, 0); if (attr_mask & IB_QP_TIMEOUT) { - roce_set_field(context->byte_28_at_fl, V2_QPC_BYTE_28_AT_M, - V2_QPC_BYTE_28_AT_S, attr->timeout); - roce_set_field(qpc_mask->byte_28_at_fl, V2_QPC_BYTE_28_AT_M, - V2_QPC_BYTE_28_AT_S, 0); + if (attr->timeout < 31) { + roce_set_field(context->byte_28_at_fl, + V2_QPC_BYTE_28_AT_M, V2_QPC_BYTE_28_AT_S, + attr->timeout); + roce_set_field(qpc_mask->byte_28_at_fl, + V2_QPC_BYTE_28_AT_M, V2_QPC_BYTE_28_AT_S, + 0); + } else { + dev_warn(dev, "Local ACK timeout shall be 0 to 30.\n"); + } } roce_set_field(context->byte_172_sq_psn, V2_QPC_BYTE_172_SQ_CUR_PSN_M, From 91fb4d83b88a7b544ce564c44167aad29d4154f0 Mon Sep 17 00:00:00 2001 From: Lijun Ou Date: Wed, 12 Dec 2018 17:49:09 +0800 Subject: [PATCH 0113/4277] RDMA/hns: Modify the pbl ba page size for hip08 Modify the pbl ba page size to 16K for in order to support 4G MR size. Signed-off-by: Wei Hu (Xavier) Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 748dc2b44a3b..9ed7e71dc437 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -1366,7 +1366,7 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev) caps->mpt_ba_pg_sz = 0; caps->mpt_buf_pg_sz = 0; caps->mpt_hop_num = HNS_ROCE_CONTEXT_HOP_NUM; - caps->pbl_ba_pg_sz = 0; + caps->pbl_ba_pg_sz = 2; caps->pbl_buf_pg_sz = 0; caps->pbl_hop_num = HNS_ROCE_PBL_HOP_NUM; caps->mtt_ba_pg_sz = 0; From 1e0d0a5fd38192f23304ea2fc2b531fea7c74247 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 7 Jan 2019 07:04:14 -0500 Subject: [PATCH 0114/4277] media: s5p-mfc: fix incorrect bus assignment in virtual child device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Virtual MFC codec's child devices must not be assigned to platform bus, because they are allocated as raw 'struct device' and don't have the corresponding 'platform' part. This fixes NULL pointer access revealed recently by commit a66d972465d1 ("devres: Align data[] to ARCH_KMALLOC_MINALIGN"). Fixes: c79667dd93b0 ("media: s5p-mfc: replace custom reserved memory handling code with generic one") Reported-by: Paweł Chmiel Signed-off-by: Marek Szyprowski Tested-by: Paweł Chmiel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8a5ba3bec3af..0a9f59d89185 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1089,7 +1089,6 @@ static struct device *s5p_mfc_alloc_memdev(struct device *dev, device_initialize(child); dev_set_name(child, "%s:%s", dev_name(dev), name); child->parent = dev; - child->bus = dev->bus; child->coherent_dma_mask = dev->coherent_dma_mask; child->dma_mask = dev->dma_mask; child->release = s5p_mfc_memdev_release; From d6f66f618569b8f6cc65435b1cab4be7f0610f32 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 7 Jan 2019 11:09:26 -0800 Subject: [PATCH 0115/4277] Input: tm2-touchkey - add support for midas touchkey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The touchkey on midas boards is almost identical. The only real difference is that it uses the same register for both keycode and base. Signed-off-by: Simon Shields Signed-off-by: Paweł Chmiel Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/cypress,tm2-touchkey.txt | 4 ++- drivers/input/keyboard/tm2-touchkey.c | 34 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt index 635f62c756ee..3df82c4017ff 100644 --- a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt +++ b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt @@ -1,7 +1,9 @@ Samsung tm2-touchkey Required properties: -- compatible: must be "cypress,tm2-touchkey" +- compatible: + * "cypress,tm2-touchkey" - for the touchkey found on the tm2 board + * "cypress,midas-touchkey" - for the touchkey found on midas boards - reg: I2C address of the chip. - interrupt-parent: a phandle for the interrupt controller (see interrupt binding[0]). diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index abc266e40e17..5a1fe08bdd76 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -22,12 +22,12 @@ #include #include #include +#include #include #include #define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey" -#define TM2_TOUCHKEY_KEYCODE_REG 0x03 -#define TM2_TOUCHKEY_BASE_REG 0x00 + #define TM2_TOUCHKEY_CMD_LED_ON 0x10 #define TM2_TOUCHKEY_CMD_LED_OFF 0x20 #define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3) @@ -40,12 +40,28 @@ enum { TM2_TOUCHKEY_KEY_BACK, }; +struct touchkey_variant { + u8 keycode_reg; + u8 base_reg; +}; + struct tm2_touchkey_data { struct i2c_client *client; struct input_dev *input_dev; struct led_classdev led_dev; struct regulator *vdd; struct regulator_bulk_data regulators[2]; + const struct touchkey_variant *variant; +}; + +static const struct touchkey_variant tm2_touchkey_variant = { + .keycode_reg = 0x03, + .base_reg = 0x00, +}; + +static const struct touchkey_variant midas_touchkey_variant = { + .keycode_reg = 0x00, + .base_reg = 0x00, }; static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, @@ -66,7 +82,7 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, regulator_set_voltage(touchkey->vdd, volt, volt); i2c_smbus_write_byte_data(touchkey->client, - TM2_TOUCHKEY_BASE_REG, data); + touchkey->variant->base_reg, data); } static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey) @@ -99,7 +115,7 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) int key; data = i2c_smbus_read_byte_data(touchkey->client, - TM2_TOUCHKEY_KEYCODE_REG); + touchkey->variant->keycode_reg); if (data < 0) { dev_err(&touchkey->client->dev, "failed to read i2c data: %d\n", data); @@ -153,6 +169,8 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->client = client; i2c_set_clientdata(client, touchkey); + touchkey->variant = of_device_get_match_data(&client->dev); + touchkey->regulators[0].supply = "vcc"; touchkey->regulators[1].supply = "vdd"; error = devm_regulator_bulk_get(&client->dev, @@ -262,7 +280,13 @@ static const struct i2c_device_id tm2_touchkey_id_table[] = { MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table); static const struct of_device_id tm2_touchkey_of_match[] = { - { .compatible = "cypress,tm2-touchkey", }, + { + .compatible = "cypress,tm2-touchkey", + .data = &tm2_touchkey_variant, + }, { + .compatible = "cypress,midas-touchkey", + .data = &midas_touchkey_variant, + }, { }, }; MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match); From d5a158cec4f33fbab031fc97fc17bfe5371925be Mon Sep 17 00:00:00 2001 From: Jonathan Bakker Date: Mon, 7 Jan 2019 11:11:04 -0800 Subject: [PATCH 0116/4277] Input: tm2-touchkey - correct initial brightness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tm2-touchkey doesn't have brightness levels, but only on/off states, so replace LED_FULL with LED_ON. Signed-off-by: Jonathan Bakker Signed-off-by: Paweł Chmiel Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tm2-touchkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index 5a1fe08bdd76..0336789ab1bb 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -230,7 +230,7 @@ static int tm2_touchkey_probe(struct i2c_client *client, /* led device */ touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME; - touchkey->led_dev.brightness = LED_FULL; + touchkey->led_dev.brightness = LED_ON; touchkey->led_dev.max_brightness = LED_ON; touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set; From 07df1c55270c142b5027dd31787c42d2b01e2e92 Mon Sep 17 00:00:00 2001 From: Jonathan Bakker Date: Mon, 7 Jan 2019 11:11:55 -0800 Subject: [PATCH 0117/4277] Input: tm2-touchkey - allow specifying custom keycodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all devices use the same keycodes in the same order, so add possibility to define keycodes for buttons present on actual hardware. If keycodes property is not present, we assume that device has at least MENU and BACK keys. Signed-off-by: Jonathan Bakker Signed-off-by: Paweł Chmiel Signed-off-by: Dmitry Torokhov --- .../bindings/input/cypress,tm2-touchkey.txt | 4 ++ drivers/input/keyboard/tm2-touchkey.c | 49 ++++++++++--------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt index 3df82c4017ff..22466d8a750d 100644 --- a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt +++ b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt @@ -12,6 +12,9 @@ Required properties: - vcc-supply : internal regulator output. 1.8V - vdd-supply : power supply for IC 3.3V +Optional properties: +- linux,keycodes: array of keycodes (max 4), default KEY_PHONE and KEY_BACK + [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt Example: @@ -25,5 +28,6 @@ Example: interrupts = <2 IRQ_TYPE_EDGE_FALLING>; vcc-supply=<&ldo32_reg>; vdd-supply=<&ldo33_reg>; + linux,keycodes = ; }; }; diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index 0336789ab1bb..b55faf597d8a 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -35,11 +35,6 @@ #define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000 #define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000 -enum { - TM2_TOUCHKEY_KEY_MENU = 0x1, - TM2_TOUCHKEY_KEY_BACK, -}; - struct touchkey_variant { u8 keycode_reg; u8 base_reg; @@ -52,6 +47,8 @@ struct tm2_touchkey_data { struct regulator *vdd; struct regulator_bulk_data regulators[2]; const struct touchkey_variant *variant; + u32 keycodes[4]; + int num_keycodes; }; static const struct touchkey_variant tm2_touchkey_variant = { @@ -112,7 +109,8 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) { struct tm2_touchkey_data *touchkey = devid; int data; - int key; + int index; + int i; data = i2c_smbus_read_byte_data(touchkey->client, touchkey->variant->keycode_reg); @@ -122,26 +120,20 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) goto out; } - switch (data & TM2_TOUCHKEY_BIT_KEYCODE) { - case TM2_TOUCHKEY_KEY_MENU: - key = KEY_PHONE; - break; - - case TM2_TOUCHKEY_KEY_BACK: - key = KEY_BACK; - break; - - default: + index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1; + if (index < 0 || index >= touchkey->num_keycodes) { dev_warn(&touchkey->client->dev, - "unhandled keycode, data %#02x\n", data); + "invalid keycode index %d\n", index); goto out; } if (data & TM2_TOUCHKEY_BIT_PRESS_EV) { - input_report_key(touchkey->input_dev, KEY_PHONE, 0); - input_report_key(touchkey->input_dev, KEY_BACK, 0); + for (i = 0; i < touchkey->num_keycodes; i++) + input_report_key(touchkey->input_dev, + touchkey->keycodes[i], 0); } else { - input_report_key(touchkey->input_dev, key, 1); + input_report_key(touchkey->input_dev, + touchkey->keycodes[index], 1); } input_sync(touchkey->input_dev); @@ -153,8 +145,10 @@ out: static int tm2_touchkey_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device_node *np = client->dev.of_node; struct tm2_touchkey_data *touchkey; int error; + int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -184,6 +178,16 @@ static int tm2_touchkey_probe(struct i2c_client *client, /* Save VDD for easy access */ touchkey->vdd = touchkey->regulators[1].consumer; + touchkey->num_keycodes = of_property_read_variable_u32_array(np, + "linux,keycodes", touchkey->keycodes, 0, + ARRAY_SIZE(touchkey->keycodes)); + if (touchkey->num_keycodes <= 0) { + /* default keycodes */ + touchkey->keycodes[0] = KEY_PHONE; + touchkey->keycodes[1] = KEY_BACK; + touchkey->num_keycodes = 2; + } + error = tm2_touchkey_power_enable(touchkey); if (error) { dev_err(&client->dev, "failed to power up device: %d\n", error); @@ -208,8 +212,9 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME; touchkey->input_dev->id.bustype = BUS_I2C; - input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE); - input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK); + for (i = 0; i < touchkey->num_keycodes; i++) + input_set_capability(touchkey->input_dev, EV_KEY, + touchkey->keycodes[i]); error = input_register_device(touchkey->input_dev); if (error) { From 1cdbd3e57698fa22eddb522b2c7fa6048420da5f Mon Sep 17 00:00:00 2001 From: Jonathan Bakker Date: Mon, 7 Jan 2019 11:21:16 -0800 Subject: [PATCH 0118/4277] Input: tm2-touchkey - add support for aries touchkey variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The touchkey variant found on aries board is slighty different, it uses a fixed regulator and writes/read to the same place Signed-off-by: Jonathan Bakker Signed-off-by: Paweł Chmiel Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/cypress,tm2-touchkey.txt | 1 + drivers/input/keyboard/tm2-touchkey.c | 53 ++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt index 22466d8a750d..f517660a3204 100644 --- a/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt +++ b/Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt @@ -4,6 +4,7 @@ Required properties: - compatible: * "cypress,tm2-touchkey" - for the touchkey found on the tm2 board * "cypress,midas-touchkey" - for the touchkey found on midas boards + * "cypress,aries-touchkey" - for the touchkey found on aries boards - reg: I2C address of the chip. - interrupt-parent: a phandle for the interrupt controller (see interrupt binding[0]). diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index b55faf597d8a..7dbef96559d2 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -28,6 +28,8 @@ #define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey" +#define ARIES_TOUCHKEY_CMD_LED_ON 0x1 +#define ARIES_TOUCHKEY_CMD_LED_OFF 0x2 #define TM2_TOUCHKEY_CMD_LED_ON 0x10 #define TM2_TOUCHKEY_CMD_LED_OFF 0x20 #define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3) @@ -38,6 +40,10 @@ struct touchkey_variant { u8 keycode_reg; u8 base_reg; + u8 cmd_led_on; + u8 cmd_led_off; + bool no_reg; + bool fixed_regulator; }; struct tm2_touchkey_data { @@ -54,11 +60,22 @@ struct tm2_touchkey_data { static const struct touchkey_variant tm2_touchkey_variant = { .keycode_reg = 0x03, .base_reg = 0x00, + .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON, + .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF, }; static const struct touchkey_variant midas_touchkey_variant = { .keycode_reg = 0x00, .base_reg = 0x00, + .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON, + .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF, +}; + +static struct touchkey_variant aries_touchkey_variant = { + .no_reg = true, + .fixed_regulator = true, + .cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON, + .cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF, }; static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, @@ -71,15 +88,20 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, if (brightness == LED_OFF) { volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN; - data = TM2_TOUCHKEY_CMD_LED_OFF; + data = touchkey->variant->cmd_led_off; } else { volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX; - data = TM2_TOUCHKEY_CMD_LED_ON; + data = touchkey->variant->cmd_led_on; } - regulator_set_voltage(touchkey->vdd, volt, volt); - i2c_smbus_write_byte_data(touchkey->client, - touchkey->variant->base_reg, data); + if (!touchkey->variant->fixed_regulator) + regulator_set_voltage(touchkey->vdd, volt, volt); + + if (touchkey->variant->no_reg) + i2c_smbus_write_byte(touchkey->client, data); + else + i2c_smbus_write_byte_data(touchkey->client, + touchkey->variant->base_reg, data); } static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey) @@ -112,8 +134,11 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) int index; int i; - data = i2c_smbus_read_byte_data(touchkey->client, - touchkey->variant->keycode_reg); + if (touchkey->variant->no_reg) + data = i2c_smbus_read_byte(touchkey->client); + else + data = i2c_smbus_read_byte_data(touchkey->client, + touchkey->variant->keycode_reg); if (data < 0) { dev_err(&touchkey->client->dev, "failed to read i2c data: %d\n", data); @@ -139,6 +164,14 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) input_sync(touchkey->input_dev); out: + if (touchkey->variant->fixed_regulator && + data & TM2_TOUCHKEY_BIT_PRESS_EV) { + /* touch turns backlight on, so make sure we're in sync */ + if (touchkey->led_dev.brightness == LED_OFF) + tm2_touchkey_led_brightness_set(&touchkey->led_dev, + LED_OFF); + } + return IRQ_HANDLED; } @@ -246,6 +279,9 @@ static int tm2_touchkey_probe(struct i2c_client *client, return error; } + if (touchkey->variant->fixed_regulator) + tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON); + return 0; } @@ -291,6 +327,9 @@ static const struct of_device_id tm2_touchkey_of_match[] = { }, { .compatible = "cypress,midas-touchkey", .data = &midas_touchkey_variant, + }, { + .compatible = "cypress,aries-touchkey", + .data = &aries_touchkey_variant, }, { }, }; From e85bb0beb6498c0dffe18a2f1f16d575bc175c32 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Mon, 7 Jan 2019 11:53:59 -0800 Subject: [PATCH 0119/4277] Input: ad7879 - add check for read errors in interrupt regmap_bulk_read() can return a non zero value on failure. The fix checks if the function call succeeded before calling mod_timer. The issue was identified by a static analysis tool. Signed-off-by: Aditya Pakki Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 6fa714c587b4..3a016f43fb85 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -246,11 +246,14 @@ static void ad7879_timer(struct timer_list *t) static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; + int error; - regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS, - ts->conversion_data, AD7879_NR_SENSE); - - if (!ad7879_report(ts)) + error = regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS, + ts->conversion_data, AD7879_NR_SENSE); + if (error) + dev_err_ratelimited(ts->dev, "failed to read %#02x: %d\n", + AD7879_REG_XPLUS, error); + else if (!ad7879_report(ts)) mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; From 9f63df26beea78aae0500d1225a99bbd905dd157 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 6 Jan 2019 19:19:29 -0800 Subject: [PATCH 0120/4277] Documentation/filesystems: fix title underline lengths in path-lookup.rst Fix Sphinx warnings in path-lookup.rst: Documentation/filesystems/path-lookup.rst:347: WARNING: Title underline too short. Documentation/filesystems/path-lookup.rst:358: WARNING: Title underline too short. [...] Signed-off-by: Randy Dunlap Acked-by: NeilBrown Signed-off-by: Jonathan Corbet --- Documentation/filesystems/path-lookup.rst | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Documentation/filesystems/path-lookup.rst b/Documentation/filesystems/path-lookup.rst index 9d6b68853f5b..80e22eda4132 100644 --- a/Documentation/filesystems/path-lookup.rst +++ b/Documentation/filesystems/path-lookup.rst @@ -344,7 +344,7 @@ In particular it is held while scanning chains in the dcache hash table, and the mount point hash table. Bringing it together with ``struct nameidata`` --------------------------------------------- +---------------------------------------------- .. _First edition Unix: http://minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u2.s @@ -355,7 +355,7 @@ converts a "name" to an "inode". ``struct nameidata`` contains (among other fields): ``struct path path`` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ A ``path`` contains a ``struct vfsmount`` (which is embedded in a ``struct mount``) and a ``struct dentry``. Together these @@ -366,13 +366,13 @@ step. A reference through ``d_lockref`` and ``mnt_count`` is always held. ``struct qstr last`` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ This is a string together with a length (i.e. _not_ ``nul`` terminated) that is the "next" component in the pathname. ``int last_type`` -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT``, ``LAST_DOTDOT``, or ``LAST_BIND``. The ``last`` field is only valid if the type is @@ -381,7 +381,7 @@ components of the symlink have been processed yet. Others should be fairly self-explanatory. ``struct path root`` -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ This is used to hold a reference to the effective root of the filesystem. Often that reference won't be needed, so this field is @@ -510,7 +510,7 @@ potentially interesting things about these dentries corresponding to three different flags that might be set in ``dentry->d_flags``: ``DCACHE_MANAGE_TRANSIT`` -~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~ If this flag has been set, then the filesystem has requested that the ``d_manage()`` dentry operation be called before handling any possible @@ -529,7 +529,7 @@ filesystem, which will then give it a special pass through ``d_manage()`` by returning ``-EISDIR``. ``DCACHE_MOUNTED`` -~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ This flag is set on every dentry that is mounted on. As Linux supports multiple filesystem namespaces, it is possible that the @@ -542,7 +542,7 @@ If this flag is set, and ``d_manage()`` didn't return ``-EISDIR``, and a new ``dentry`` (both with counted references). ``DCACHE_NEED_AUTOMOUNT`` -~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~ If ``d_manage()`` allowed us to get this far, and ``lookup_mnt()`` didn't find a mount point, then this flag causes the ``d_automount()`` dentry @@ -698,7 +698,7 @@ With that little refresher on seqlocks out of the way we can look at the bigger picture of how RCU-walk uses seqlocks. ``mount_lock`` and ``nd->m_seq`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We already met the ``mount_lock`` seqlock when REF-walk used it to ensure that crossing a mount point is performed safely. RCU-walk uses @@ -727,7 +727,7 @@ results would have been the same. This ensures the invariant holds, at least for vfsmount structures. ``dentry->d_seq`` and ``nd->seq`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In place of taking a count or lock on ``d_reflock``, RCU-walk samples the per-dentry ``d_seq`` seqlock, and stores the sequence number in the @@ -774,7 +774,7 @@ getting a counted reference to the new dentry before dropping that for the old dentry which we saw in REF-walk. No ``inode->i_rwsem`` or even ``rename_lock`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A semaphore is a fairly heavyweight lock that can only be taken when it is permissible to sleep. As ``rcu_read_lock()`` forbids sleeping, @@ -796,7 +796,7 @@ locking. This neatly handles all cases, so adding extra checks on rename_lock would bring no significant value. ``unlazy walk()`` and ``complete_walk()`` -------------------------------------- +----------------------------------------- That "dropping down to REF-walk" typically involves a call to ``unlazy_walk()``, so named because "RCU-walk" is also sometimes From 1b23f5e9973abc2137ca615d770bf23d8e45b93c Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Sun, 6 Jan 2019 00:28:59 +0100 Subject: [PATCH 0121/4277] doc: networking: prepare offload documents for conversion into RST Add small number of markups which are sufficient for conversion into reStructuredText. Unfortunately there was necessary to restructure all sections in checksum-offloads.txt file and create paragraphs separated by newline. There also must not be a space at the beginning of paragpraph. There are no semantic changes. Signed-off-by: Otto Sabart Acked-by: David S. Miller Signed-off-by: Jonathan Corbet --- .../networking/checksum-offloads.txt | 177 ++++++++++-------- .../networking/segmentation-offloads.txt | 46 +++-- 2 files changed, 129 insertions(+), 94 deletions(-) diff --git a/Documentation/networking/checksum-offloads.txt b/Documentation/networking/checksum-offloads.txt index 27bc09cfcf6d..1a1cd94a3f6d 100644 --- a/Documentation/networking/checksum-offloads.txt +++ b/Documentation/networking/checksum-offloads.txt @@ -1,122 +1,143 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============================================== Checksum Offloads in the Linux Networking Stack +=============================================== Introduction ============ -This document describes a set of techniques in the Linux networking stack - to take advantage of checksum offload capabilities of various NICs. +This document describes a set of techniques in the Linux networking stack to +take advantage of checksum offload capabilities of various NICs. The following technologies are described: - * TX Checksum Offload - * LCO: Local Checksum Offload - * RCO: Remote Checksum Offload + +* TX Checksum Offload +* LCO: Local Checksum Offload +* RCO: Remote Checksum Offload Things that should be documented here but aren't yet: - * RX Checksum Offload - * CHECKSUM_UNNECESSARY conversion + +* RX Checksum Offload +* CHECKSUM_UNNECESSARY conversion TX Checksum Offload =================== -The interface for offloading a transmit checksum to a device is explained - in detail in comments near the top of include/linux/skbuff.h. +The interface for offloading a transmit checksum to a device is explained in +detail in comments near the top of include/linux/skbuff.h. + In brief, it allows to request the device fill in a single ones-complement - checksum defined by the sk_buff fields skb->csum_start and - skb->csum_offset. The device should compute the 16-bit ones-complement - checksum (i.e. the 'IP-style' checksum) from csum_start to the end of the - packet, and fill in the result at (csum_start + csum_offset). -Because csum_offset cannot be negative, this ensures that the previous - value of the checksum field is included in the checksum computation, thus - it can be used to supply any needed corrections to the checksum (such as - the sum of the pseudo-header for UDP or TCP). +checksum defined by the sk_buff fields skb->csum_start and skb->csum_offset. +The device should compute the 16-bit ones-complement checksum (i.e. the +'IP-style' checksum) from csum_start to the end of the packet, and fill in the +result at (csum_start + csum_offset). + +Because csum_offset cannot be negative, this ensures that the previous value of +the checksum field is included in the checksum computation, thus it can be used +to supply any needed corrections to the checksum (such as the sum of the +pseudo-header for UDP or TCP). + This interface only allows a single checksum to be offloaded. Where - encapsulation is used, the packet may have multiple checksum fields in - different header layers, and the rest will have to be handled by another - mechanism such as LCO or RCO. +encapsulation is used, the packet may have multiple checksum fields in +different header layers, and the rest will have to be handled by another +mechanism such as LCO or RCO. + CRC32c can also be offloaded using this interface, by means of filling - skb->csum_start and skb->csum_offset as described above, and setting - skb->csum_not_inet: see skbuff.h comment (section 'D') for more details. +skb->csum_start and skb->csum_offset as described above, and setting +skb->csum_not_inet: see skbuff.h comment (section 'D') for more details. + No offloading of the IP header checksum is performed; it is always done in - software. This is OK because when we build the IP header, we obviously - have it in cache, so summing it isn't expensive. It's also rather short. +software. This is OK because when we build the IP header, we obviously have it +in cache, so summing it isn't expensive. It's also rather short. + The requirements for GSO are more complicated, because when segmenting an - encapsulated packet both the inner and outer checksums may need to be - edited or recomputed for each resulting segment. See the skbuff.h comment - (section 'E') for more details. +encapsulated packet both the inner and outer checksums may need to be edited or +recomputed for each resulting segment. See the skbuff.h comment (section 'E') +for more details. A driver declares its offload capabilities in netdev->hw_features; see - Documentation/networking/netdev-features.txt for more. Note that a device - which only advertises NETIF_F_IP[V6]_CSUM must still obey the csum_start - and csum_offset given in the SKB; if it tries to deduce these itself in - hardware (as some NICs do) the driver should check that the values in the - SKB match those which the hardware will deduce, and if not, fall back to - checksumming in software instead (with skb_csum_hwoffload_help() or one of - the skb_checksum_help() / skb_crc32c_csum_help functions, as mentioned in - include/linux/skbuff.h). +Documentation/networking/netdev-features.txt for more. Note that a device +which only advertises NETIF_F_IP[V6]_CSUM must still obey the csum_start and +csum_offset given in the SKB; if it tries to deduce these itself in hardware +(as some NICs do) the driver should check that the values in the SKB match +those which the hardware will deduce, and if not, fall back to checksumming in +software instead (with skb_csum_hwoffload_help() or one of the +skb_checksum_help() / skb_crc32c_csum_help functions, as mentioned in +include/linux/skbuff.h). -The stack should, for the most part, assume that checksum offload is - supported by the underlying device. The only place that should check is - validate_xmit_skb(), and the functions it calls directly or indirectly. - That function compares the offload features requested by the SKB (which - may include other offloads besides TX Checksum Offload) and, if they are - not supported or enabled on the device (determined by netdev->features), - performs the corresponding offload in software. In the case of TX - Checksum Offload, that means calling skb_csum_hwoffload_help(skb, features). +The stack should, for the most part, assume that checksum offload is supported +by the underlying device. The only place that should check is +validate_xmit_skb(), and the functions it calls directly or indirectly. That +function compares the offload features requested by the SKB (which may include +other offloads besides TX Checksum Offload) and, if they are not supported or +enabled on the device (determined by netdev->features), performs the +corresponding offload in software. In the case of TX Checksum Offload, that +means calling skb_csum_hwoffload_help(skb, features). LCO: Local Checksum Offload =========================== LCO is a technique for efficiently computing the outer checksum of an - encapsulated datagram when the inner checksum is due to be offloaded. -The ones-complement sum of a correctly checksummed TCP or UDP packet is - equal to the complement of the sum of the pseudo header, because everything - else gets 'cancelled out' by the checksum field. This is because the sum was - complemented before being written to the checksum field. +encapsulated datagram when the inner checksum is due to be offloaded. + +The ones-complement sum of a correctly checksummed TCP or UDP packet is equal +to the complement of the sum of the pseudo header, because everything else gets +'cancelled out' by the checksum field. This is because the sum was +complemented before being written to the checksum field. + More generally, this holds in any case where the 'IP-style' ones complement - checksum is used, and thus any checksum that TX Checksum Offload supports. +checksum is used, and thus any checksum that TX Checksum Offload supports. + That is, if we have set up TX Checksum Offload with a start/offset pair, we - know that after the device has filled in that checksum, the ones - complement sum from csum_start to the end of the packet will be equal to - the complement of whatever value we put in the checksum field beforehand. - This allows us to compute the outer checksum without looking at the payload: - we simply stop summing when we get to csum_start, then add the complement of - the 16-bit word at (csum_start + csum_offset). +know that after the device has filled in that checksum, the ones complement sum +from csum_start to the end of the packet will be equal to the complement of +whatever value we put in the checksum field beforehand. This allows us to +compute the outer checksum without looking at the payload: we simply stop +summing when we get to csum_start, then add the complement of the 16-bit word +at (csum_start + csum_offset). + Then, when the true inner checksum is filled in (either by hardware or by - skb_checksum_help()), the outer checksum will become correct by virtue of - the arithmetic. +skb_checksum_help()), the outer checksum will become correct by virtue of the +arithmetic. LCO is performed by the stack when constructing an outer UDP header for an - encapsulation such as VXLAN or GENEVE, in udp_set_csum(). Similarly for - the IPv6 equivalents, in udp6_set_csum(). +encapsulation such as VXLAN or GENEVE, in udp_set_csum(). Similarly for the +IPv6 equivalents, in udp6_set_csum(). + It is also performed when constructing an IPv4 GRE header, in - net/ipv4/ip_gre.c:build_header(). It is *not* currently performed when - constructing an IPv6 GRE header; the GRE checksum is computed over the - whole packet in net/ipv6/ip6_gre.c:ip6gre_xmit2(), but it should be - possible to use LCO here as IPv6 GRE still uses an IP-style checksum. +net/ipv4/ip_gre.c:build_header(). It is *not* currently performed when +constructing an IPv6 GRE header; the GRE checksum is computed over the whole +packet in net/ipv6/ip6_gre.c:ip6gre_xmit2(), but it should be possible to use +LCO here as IPv6 GRE still uses an IP-style checksum. + All of the LCO implementations use a helper function lco_csum(), in - include/linux/skbuff.h. +include/linux/skbuff.h. LCO can safely be used for nested encapsulations; in this case, the outer - encapsulation layer will sum over both its own header and the 'middle' - header. This does mean that the 'middle' header will get summed multiple - times, but there doesn't seem to be a way to avoid that without incurring - bigger costs (e.g. in SKB bloat). +encapsulation layer will sum over both its own header and the 'middle' header. +This does mean that the 'middle' header will get summed multiple times, but +there doesn't seem to be a way to avoid that without incurring bigger costs +(e.g. in SKB bloat). RCO: Remote Checksum Offload ============================ -RCO is a technique for eliding the inner checksum of an encapsulated - datagram, allowing the outer checksum to be offloaded. It does, however, - involve a change to the encapsulation protocols, which the receiver must - also support. For this reason, it is disabled by default. +RCO is a technique for eliding the inner checksum of an encapsulated datagram, +allowing the outer checksum to be offloaded. It does, however, involve a +change to the encapsulation protocols, which the receiver must also support. +For this reason, it is disabled by default. + RCO is detailed in the following Internet-Drafts: -https://tools.ietf.org/html/draft-herbert-remotecsumoffload-00 -https://tools.ietf.org/html/draft-herbert-vxlan-rco-00 -In Linux, RCO is implemented individually in each encapsulation protocol, - and most tunnel types have flags controlling its use. For instance, VXLAN - has the flag VXLAN_F_REMCSUM_TX (per struct vxlan_rdst) to indicate that - RCO should be used when transmitting to a given remote destination. + +* https://tools.ietf.org/html/draft-herbert-remotecsumoffload-00 +* https://tools.ietf.org/html/draft-herbert-vxlan-rco-00 + +In Linux, RCO is implemented individually in each encapsulation protocol, and +most tunnel types have flags controlling its use. For instance, VXLAN has the +flag VXLAN_F_REMCSUM_TX (per struct vxlan_rdst) to indicate that RCO should be +used when transmitting to a given remote destination. diff --git a/Documentation/networking/segmentation-offloads.txt b/Documentation/networking/segmentation-offloads.txt index aca542ec125c..1794bfe98196 100644 --- a/Documentation/networking/segmentation-offloads.txt +++ b/Documentation/networking/segmentation-offloads.txt @@ -1,4 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================================== Segmentation Offloads in the Linux Networking Stack +=================================================== + Introduction ============ @@ -15,6 +20,7 @@ The following technologies are described: * Partial Generic Segmentation Offload - GSO_PARTIAL * SCTP accelleration with GSO - GSO_BY_FRAGS + TCP Segmentation Offload ======================== @@ -42,6 +48,7 @@ NETIF_F_TSO_MANGLEID set then the IP ID can be ignored when performing TSO and we will either increment the IP ID for all frames, or leave it at a static value based on driver preference. + UDP Fragmentation Offload ========================= @@ -54,6 +61,7 @@ UFO is deprecated: modern kernels will no longer generate UFO skbs, but can still receive them from tuntap and similar devices. Offload of UDP-based tunnel protocols is still supported. + IPIP, SIT, GRE, UDP Tunnel, and Remote Checksum Offloads ======================================================== @@ -71,17 +79,19 @@ refer to the tunnel headers as the outer headers, while the encapsulated data is normally referred to as the inner headers. Below is the list of calls to access the given headers: -IPIP/SIT Tunnel: - Outer Inner -MAC skb_mac_header -Network skb_network_header skb_inner_network_header -Transport skb_transport_header +IPIP/SIT Tunnel:: -UDP/GRE Tunnel: - Outer Inner -MAC skb_mac_header skb_inner_mac_header -Network skb_network_header skb_inner_network_header -Transport skb_transport_header skb_inner_transport_header + Outer Inner + MAC skb_mac_header + Network skb_network_header skb_inner_network_header + Transport skb_transport_header + +UDP/GRE Tunnel:: + + Outer Inner + MAC skb_mac_header skb_inner_mac_header + Network skb_network_header skb_inner_network_header + Transport skb_transport_header skb_inner_transport_header In addition to the above tunnel types there are also SKB_GSO_GRE_CSUM and SKB_GSO_UDP_TUNNEL_CSUM. These two additional tunnel types reflect the @@ -93,6 +103,7 @@ header has requested a remote checksum offload. In this case the inner headers will be left with a partial checksum and only the outer header checksum will be computed. + Generic Segmentation Offload ============================ @@ -106,6 +117,7 @@ Before enabling any hardware segmentation offload a corresponding software offload is required in GSO. Otherwise it becomes possible for a frame to be re-routed between devices and end up being unable to be transmitted. + Generic Receive Offload ======================= @@ -117,6 +129,7 @@ this is IPv4 ID in the case that the DF bit is set for a given IP header. If the value of the IPv4 ID is not sequentially incrementing it will be altered so that it is when a frame assembled via GRO is segmented via GSO. + Partial Generic Segmentation Offload ==================================== @@ -134,6 +147,7 @@ is the outer IPv4 ID field. It is up to the device drivers to guarantee that the IPv4 ID field is incremented in the case that a given header does not have the DF bit set. + SCTP accelleration with GSO =========================== @@ -157,14 +171,14 @@ appropriately. There are some helpers to make this easier: - - skb_is_gso(skb) && skb_is_gso_sctp(skb) is the best way to see if - an skb is an SCTP GSO skb. +- skb_is_gso(skb) && skb_is_gso_sctp(skb) is the best way to see if + an skb is an SCTP GSO skb. - - For size checks, the skb_gso_validate_*_len family of helpers correctly - considers GSO_BY_FRAGS. +- For size checks, the skb_gso_validate_*_len family of helpers correctly + considers GSO_BY_FRAGS. - - For manipulating packets, skb_increase_gso_size and skb_decrease_gso_size - will check for GSO_BY_FRAGS and WARN if asked to manipulate these skbs. +- For manipulating packets, skb_increase_gso_size and skb_decrease_gso_size + will check for GSO_BY_FRAGS and WARN if asked to manipulate these skbs. This also affects drivers with the NETIF_F_FRAGLIST & NETIF_F_GSO_SCTP bits set. Note also that NETIF_F_GSO_SCTP is included in NETIF_F_GSO_SOFTWARE. From d0dcde6426ce071ad447fb9d91c85ab649026114 Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Sun, 6 Jan 2019 00:29:15 +0100 Subject: [PATCH 0122/4277] doc: networking: convert offload files into RST and update references This patch renames offload files. This is necessary for Sphinx. Also update reference to checksum-offloads.rst file. Whole kernel code was grepped for references using: $ grep -r "\(segmentation\|checksum\)-offloads.txt" . There should be no other references to {segmentation,checksum}-offloads.txt files. Signed-off-by: Otto Sabart Acked-by: David S. Miller Signed-off-by: Jonathan Corbet --- .../networking/{checksum-offloads.txt => checksum-offloads.rst} | 0 .../{segmentation-offloads.txt => segmentation-offloads.rst} | 0 include/linux/skbuff.h | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename Documentation/networking/{checksum-offloads.txt => checksum-offloads.rst} (100%) rename Documentation/networking/{segmentation-offloads.txt => segmentation-offloads.rst} (100%) diff --git a/Documentation/networking/checksum-offloads.txt b/Documentation/networking/checksum-offloads.rst similarity index 100% rename from Documentation/networking/checksum-offloads.txt rename to Documentation/networking/checksum-offloads.rst diff --git a/Documentation/networking/segmentation-offloads.txt b/Documentation/networking/segmentation-offloads.rst similarity index 100% rename from Documentation/networking/segmentation-offloads.txt rename to Documentation/networking/segmentation-offloads.rst diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 93f56fddd92a..4e671b46e767 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -4296,7 +4296,7 @@ static inline bool skb_head_is_locked(const struct sk_buff *skb) /* Local Checksum Offload. * Compute outer checksum based on the assumption that the * inner checksum will be offloaded later. - * See Documentation/networking/checksum-offloads.txt for + * See Documentation/networking/checksum-offloads.rst for * explanation of how this works. * Fill in outer checksum adjustment (e.g. with sum of outer * pseudo-header) before calling. From b83eb68cb939e4486c7792f26cacab9ce7a83dc4 Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Sun, 6 Jan 2019 00:29:28 +0100 Subject: [PATCH 0123/4277] doc: networking: shorten the main title in offloads documents The titles do not look very nice in the table of contents generated by Sphinx. I also think it is obvious that the documents are describing offloads in the Linux Networking Stack. Signed-off-by: Otto Sabart Acked-by: David S. Miller Signed-off-by: Jonathan Corbet --- Documentation/networking/checksum-offloads.rst | 6 +++--- Documentation/networking/segmentation-offloads.rst | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/networking/checksum-offloads.rst b/Documentation/networking/checksum-offloads.rst index 1a1cd94a3f6d..905c8a84b103 100644 --- a/Documentation/networking/checksum-offloads.rst +++ b/Documentation/networking/checksum-offloads.rst @@ -1,8 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0 -=============================================== -Checksum Offloads in the Linux Networking Stack -=============================================== +================= +Checksum Offloads +================= Introduction diff --git a/Documentation/networking/segmentation-offloads.rst b/Documentation/networking/segmentation-offloads.rst index 1794bfe98196..89d1ee933e9f 100644 --- a/Documentation/networking/segmentation-offloads.rst +++ b/Documentation/networking/segmentation-offloads.rst @@ -1,8 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0 -=================================================== -Segmentation Offloads in the Linux Networking Stack -=================================================== +===================== +Segmentation Offloads +===================== Introduction From d96bedb2b2488519818f1c2654b31069b5136401 Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Sun, 6 Jan 2019 00:29:41 +0100 Subject: [PATCH 0124/4277] doc: networking: add offload documents into main index file This patch just adds references to offload documents into main table of contents in network documentation. Signed-off-by: Otto Sabart Acked-by: David S. Miller Signed-off-by: Jonathan Corbet --- Documentation/networking/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 6a47629ef8ed..7034214cf87e 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -32,6 +32,8 @@ Contents: alias bridge snmp_counter + checksum-offloads + segmentation-offloads .. only:: subproject From 2fec7b33094c1257ddb07b2b8318cfccba235d58 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 7 Jan 2019 10:20:19 -0800 Subject: [PATCH 0125/4277] Documentation/CodingStyle: Use directory-local variables for emacs settings In emacs 23.1 support for directory-local variables was added (see also https://lists.gnu.org/archive/html/info-gnu-emacs/2009-07/msg00000.html). Simplify the settings in coding-style.rst by using that feature. Additionally, do not inherit any settings from emacs' linux coding style to minimize dependencies on the version of emacs that is being used. I have verified with several large and nontrivial kernel source files that the new settings format code according to what checkpatch expects. Signed-off-by: Bart Van Assche Cc: Matthew Wilcox Cc: Jani Nikula Cc: Alison Chaiken Cc: Joe Perches Cc: Federico Vaga Cc: Geyslan G. Bem Cc: Tiago Natel de Moura Signed-off-by: Jonathan Corbet --- Documentation/process/coding-style.rst | 55 ++++++++++++------- .../it_IT/process/coding-style.rst | 55 ++++++++++++------- .../translations/zh_CN/coding-style.rst | 55 ++++++++++++------- 3 files changed, 108 insertions(+), 57 deletions(-) diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index b78dd680c038..afa11024279a 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -595,26 +595,43 @@ values. To do the latter, you can stick the following in your .emacs file: (* (max steps 1) c-basic-offset))) - (add-hook 'c-mode-common-hook - (lambda () - ;; Add kernel style - (c-add-style - "linux-tabs-only" - '("linux" (c-offsets-alist - (arglist-cont-nonempty - c-lineup-gcc-asm-reg - c-lineup-arglist-tabs-only)))))) + (dir-locals-set-class-variables + 'linux-kernel + '((c-mode . ( + (c-basic-offset . 8) + (c-label-minimum-indentation . 0) + (c-offsets-alist . ( + (arglist-close . c-lineup-arglist-tabs-only) + (arglist-cont-nonempty . + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (arglist-intro . +) + (brace-list-intro . +) + (c . c-lineup-C-comments) + (case-label . 0) + (comment-intro . c-lineup-comment) + (cpp-define-intro . +) + (cpp-macro . -1000) + (cpp-macro-cont . +) + (defun-block-intro . +) + (else-clause . 0) + (func-decl-cont . +) + (inclass . +) + (inher-cont . c-lineup-multi-inher) + (knr-argdecl-intro . 0) + (label . -1000) + (statement . 0) + (statement-block-intro . +) + (statement-case-intro . +) + (statement-cont . +) + (substatement . +) + )) + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + )))) - (add-hook 'c-mode-hook - (lambda () - (let ((filename (buffer-file-name))) - ;; Enable kernel mode for the appropriate files - (when (and filename - (string-match (expand-file-name "~/src/linux-trees") - filename)) - (setq indent-tabs-mode t) - (setq show-trailing-whitespace t) - (c-set-style "linux-tabs-only"))))) + (dir-locals-set-directory-class + (expand-file-name "~/src/linux-trees") + 'linux-kernel) This will make emacs go better with the kernel coding style for C files below ``~/src/linux-trees``. diff --git a/Documentation/translations/it_IT/process/coding-style.rst b/Documentation/translations/it_IT/process/coding-style.rst index b707bdbe178c..67c6d9ea7257 100644 --- a/Documentation/translations/it_IT/process/coding-style.rst +++ b/Documentation/translations/it_IT/process/coding-style.rst @@ -600,26 +600,43 @@ segue nel vostro file .emacs: (* (max steps 1) c-basic-offset))) - (add-hook 'c-mode-common-hook - (lambda () - ;; Add kernel style - (c-add-style - "linux-tabs-only" - '("linux" (c-offsets-alist - (arglist-cont-nonempty - c-lineup-gcc-asm-reg - c-lineup-arglist-tabs-only)))))) + (dir-locals-set-class-variables + 'linux-kernel + '((c-mode . ( + (c-basic-offset . 8) + (c-label-minimum-indentation . 0) + (c-offsets-alist . ( + (arglist-close . c-lineup-arglist-tabs-only) + (arglist-cont-nonempty . + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (arglist-intro . +) + (brace-list-intro . +) + (c . c-lineup-C-comments) + (case-label . 0) + (comment-intro . c-lineup-comment) + (cpp-define-intro . +) + (cpp-macro . -1000) + (cpp-macro-cont . +) + (defun-block-intro . +) + (else-clause . 0) + (func-decl-cont . +) + (inclass . +) + (inher-cont . c-lineup-multi-inher) + (knr-argdecl-intro . 0) + (label . -1000) + (statement . 0) + (statement-block-intro . +) + (statement-case-intro . +) + (statement-cont . +) + (substatement . +) + )) + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + )))) - (add-hook 'c-mode-hook - (lambda () - (let ((filename (buffer-file-name))) - ;; Enable kernel mode for the appropriate files - (when (and filename - (string-match (expand-file-name "~/src/linux-trees") - filename)) - (setq indent-tabs-mode t) - (setq show-trailing-whitespace t) - (c-set-style "linux-tabs-only"))))) + (dir-locals-set-directory-class + (expand-file-name "~/src/linux-trees") + 'linux-kernel) Questo farà funzionare meglio emacs con lo stile del kernel per i file che si trovano nella cartella ``~/src/linux-trees``. diff --git a/Documentation/translations/zh_CN/coding-style.rst b/Documentation/translations/zh_CN/coding-style.rst index 1466aa64b8b4..3cb09803e084 100644 --- a/Documentation/translations/zh_CN/coding-style.rst +++ b/Documentation/translations/zh_CN/coding-style.rst @@ -535,26 +535,43 @@ Documentation/doc-guide/ 和 scripts/kernel-doc 以获得详细信息。 (* (max steps 1) c-basic-offset))) - (add-hook 'c-mode-common-hook - (lambda () - ;; Add kernel style - (c-add-style - "linux-tabs-only" - '("linux" (c-offsets-alist - (arglist-cont-nonempty - c-lineup-gcc-asm-reg - c-lineup-arglist-tabs-only)))))) + (dir-locals-set-class-variables + 'linux-kernel + '((c-mode . ( + (c-basic-offset . 8) + (c-label-minimum-indentation . 0) + (c-offsets-alist . ( + (arglist-close . c-lineup-arglist-tabs-only) + (arglist-cont-nonempty . + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (arglist-intro . +) + (brace-list-intro . +) + (c . c-lineup-C-comments) + (case-label . 0) + (comment-intro . c-lineup-comment) + (cpp-define-intro . +) + (cpp-macro . -1000) + (cpp-macro-cont . +) + (defun-block-intro . +) + (else-clause . 0) + (func-decl-cont . +) + (inclass . +) + (inher-cont . c-lineup-multi-inher) + (knr-argdecl-intro . 0) + (label . -1000) + (statement . 0) + (statement-block-intro . +) + (statement-case-intro . +) + (statement-cont . +) + (substatement . +) + )) + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + )))) - (add-hook 'c-mode-hook - (lambda () - (let ((filename (buffer-file-name))) - ;; Enable kernel mode for the appropriate files - (when (and filename - (string-match (expand-file-name "~/src/linux-trees") - filename)) - (setq indent-tabs-mode t) - (setq show-trailing-whitespace t) - (c-set-style "linux-tabs-only"))))) + (dir-locals-set-directory-class + (expand-file-name "~/src/linux-trees") + 'linux-kernel) 这会让 emacs 在 ``~/src/linux-trees`` 下的 C 源文件获得更好的内核代码风格。 From 2d87948a19ac26bb0307bd2af2a5550e4c3a172d Mon Sep 17 00:00:00 2001 From: Laurent Gauthier Date: Sat, 5 Jan 2019 00:08:34 +0100 Subject: [PATCH 0126/4277] doc: fault-injection: fix macro name in example Signed-off-by: Laurent Gauthier Signed-off-by: Jonathan Corbet --- Documentation/fault-injection/fault-injection.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/fault-injection/fault-injection.txt b/Documentation/fault-injection/fault-injection.txt index 4d1b7b4ccfaf..a17517a083c3 100644 --- a/Documentation/fault-injection/fault-injection.txt +++ b/Documentation/fault-injection/fault-injection.txt @@ -195,7 +195,7 @@ o #include o define the fault attributes - DECLARE_FAULT_INJECTION(name); + DECLARE_FAULT_ATTR(name); Please see the definition of struct fault_attr in fault-inject.h for details. From 9ac963c98e2c2d0bc6faafa3e746c94bed12426a Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Sun, 23 Dec 2018 02:01:02 +0100 Subject: [PATCH 0127/4277] doc:it_IT: translation for process/submitting-patches It translats the document process/submitting-patches.rst. Signed-off-by: Federico Vaga Signed-off-by: Jonathan Corbet --- .../it_IT/process/submitting-patches.rst | 862 +++++++++++++++++- 1 file changed, 858 insertions(+), 4 deletions(-) diff --git a/Documentation/translations/it_IT/process/submitting-patches.rst b/Documentation/translations/it_IT/process/submitting-patches.rst index d633775ed556..b6368a45a8e4 100644 --- a/Documentation/translations/it_IT/process/submitting-patches.rst +++ b/Documentation/translations/it_IT/process/submitting-patches.rst @@ -1,13 +1,867 @@ .. include:: ../disclaimer-ita.rst :Original: :ref:`Documentation/process/submitting-patches.rst ` - +:Translator: Federico Vaga .. _it_submittingpatches: -Sottomettere modifiche: la guida essenziale per vedere il vostro codice nel kernel -================================================================================== +Inviare patch: la guida essenziale per vedere il vostro codice nel kernel +========================================================================= + +Una persona o un'azienda che volesse inviare una patch al kernel potrebbe +sentirsi scoraggiata dal processo di sottomissione, specialmente quando manca +una certa familiarità col "sistema". Questo testo è una raccolta di +suggerimenti che aumenteranno significativamente le probabilità di vedere le +vostre patch accettate. + +Questo documento contiene un vasto numero di suggerimenti concisi. Per +maggiori dettagli su come funziona il processo di sviluppo del kernel leggete +:ref:`Documentation/translations/it_IT/process `. +Leggete anche :ref:`Documentation/translations/it_IT/process/submit-checklist.rst ` +per una lista di punti da verificare prima di inviare del codice. Se state +inviando un driver, allora leggete anche :ref:`Documentation/translations/it_IT/process/submitting-drivers.rst `; +per delle patch relative alle associazioni per Device Tree leggete +Documentation/devicetree/bindings/submitting-patches.txt. + +Molti di questi passi descrivono il comportamento di base del sistema di +controllo di versione ``git``; se utilizzate ``git`` per preparare le vostre +patch molto del lavoro più ripetitivo lo troverete già fatto per voi, tuttavia +dovete preparare e documentare un certo numero di patch. Generalmente, l'uso +di ``git`` renderà la vostra vita di sviluppatore del kernel più facile. + +0) Ottenere i sorgenti attuali +------------------------------ + +Se non avete un repositorio coi sorgenti del kernel più recenti, allora usate +``git`` per ottenerli. Vorrete iniziare col repositorio principale che può +essere recuperato col comando:: + + git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +Notate, comunque, che potreste non voler sviluppare direttamente coi sorgenti +principali del kernel. La maggior parte dei manutentori hanno i propri +sorgenti e desiderano che le patch siano preparate basandosi su di essi. +Guardate l'elemento **T:** per un determinato sottosistema nel file MAINTANERS +che troverete nei sorgenti, o semplicemente chiedete al manutentore nel caso +in cui i sorgenti da usare non siano elencati il quel file. + +Esiste ancora la possibilità di scaricare un rilascio del kernel come archivio +tar (come descritto in una delle prossime sezioni), ma questa è la via più +complicata per sviluppare per il kernel. + +1) ``diff -up`` +--------------- + +Se dovete produrre le vostre patch a mano, usate ``diff -up`` o ``diff -uprN`` +per crearle. Git produce di base le patch in questo formato; se state +usando ``git``, potete saltare interamente questa sezione. + +Tutte le modifiche al kernel Linux avvengono mediate patch, come descritte +in :manpage:`diff(1)`. Quando create la vostra patch, assicuratevi di +crearla nel formato "unified diff", come l'argomento ``-u`` di +:manpage:`diff(1)`. +Inoltre, per favore usate l'argomento ``-p`` per mostrare la funzione C +alla quale si riferiscono le diverse modifiche - questo rende il risultato +di ``diff`` molto più facile da leggere. Le patch dovrebbero essere basate +sulla radice dei sorgenti del kernel, e non sulle sue sottocartelle. + +Per creare una patch per un singolo file, spesso è sufficiente fare:: + + SRCTREE= linux + MYFILE= drivers/net/mydriver.c + + cd $SRCTREE + cp $MYFILE $MYFILE.orig + vi $MYFILE # make your change + cd .. + diff -up $SRCTREE/$MYFILE{.orig,} > /tmp/patch + +Per creare una patch per molteplici file, dovreste spacchettare i sorgenti +"vergini", o comunque non modificati, e fare un ``diff`` coi vostri. +Per esempio:: + + MYSRC= /devel/linux + + tar xvfz linux-3.19.tar.gz + mv linux-3.19 linux-3.19-vanilla + diff -uprN -X linux-3.19-vanilla/Documentation/dontdiff \ + linux-3.19-vanilla $MYSRC > /tmp/patch + +``dontdiff`` è una lista di file che sono generati durante il processo di +compilazione del kernel; questi dovrebbero essere ignorati in qualsiasi +patch generata con :manpage:`diff(1)`. + +Assicuratevi che la vostra patch non includa file che non ne fanno veramente +parte. Al fine di verificarne la correttezza, assicuratevi anche di +revisionare la vostra patch -dopo- averla generata con :manpage:`diff(1)`. + +Se le vostre modifiche producono molte differenze, allora dovrete dividerle +in patch indipendenti che modificano le cose in passi logici; leggete +:ref:`split_changes`. Questo faciliterà la revisione da parte degli altri +sviluppatori, il che è molto importante se volete che la patch venga accettata. + +Se state utilizzando ``git``, ``git rebase -i`` può aiutarvi nel procedimento. +Se non usate ``git``, un'alternativa popolare è ``quilt`` +. + +.. _it_describe_changes: + +2) Descrivete le vostre modifiche +--------------------------------- + +Descrivete il vostro problema. Esiste sempre un problema che via ha spinto +ha fare il vostro lavoro, che sia la correzione di un baco da una riga o una +nuova funzionalità da 5000 righe di codice. Convincete i revisori che vale +la pena risolvere il vostro problema e che ha senso continuare a leggere oltre +al primo paragrafo. + +Descrivete ciò che sarà visibile agli utenti. Chiari incidenti nel sistema +e blocchi sono abbastanza convincenti, ma non tutti i bachi sono così evidenti. +Anche se il problema è stato scoperto durante la revisione del codice, +descrivete l'impatto che questo avrà sugli utenti. Tenete presente che +la maggior parte delle installazioni Linux usa un kernel che arriva dai +sorgenti stabili o dai sorgenti di una distribuzione particolare che prende +singolarmente le patch dai sorgenti principali; quindi, includete tutte +le informazioni che possono essere utili a capire le vostre modifiche: +le circostanze che causano il problema, estratti da dmesg, descrizioni di +un incidente di sistema, prestazioni di una regressione, picchi di latenza, +blocchi, eccetera. + +Quantificare le ottimizzazioni e i compromessi. Se affermate di aver +migliorato le prestazioni, il consumo di memoria, l'impatto sollo stack, +o la dimensione del file binario, includete dei numeri a supporto della +vostra dichiarazione. Ma ricordatevi di descrivere anche eventuali costi +che non sono ovvi. Solitamente le ottimizzazioni non sono gratuite, ma sono +un compromesso fra l'uso di CPU, la memoria e la leggibilità; o, quando si +parla di ipotesi euristiche, fra differenti carichi. Descrivete i lati +negativi che vi aspettate dall'ottimizzazione cosicché i revisori possano +valutare i costi e i benefici. + +Una volta che il problema è chiaro, descrivete come lo risolvete andando +nel dettaglio tecnico. È molto importante che descriviate la modifica +in un inglese semplice cosicché i revisori possano verificare che il codice si +comporti come descritto. + +I manutentori vi saranno grati se scrivete la descrizione della patch in un +formato che sia compatibile con il gestore dei sorgenti usato dal kernel, +``git``, come un "commit log". Leggete :ref:`it_explicit_in_reply_to`. + +Risolvete solo un problema per patch. Se la vostra descrizione inizia ad +essere lunga, potrebbe essere un segno che la vostra patch necessita d'essere +divisa. Leggete :ref:`split_changes`. + +Quando inviate o rinviate una patch o una serie, includete la descrizione +completa delle modifiche e la loro giustificazione. Non limitatevi a dire che +questa è la versione N della patch (o serie). Non aspettatevi che i +manutentori di un sottosistema vadano a cercare le versioni precedenti per +cercare la descrizione da aggiungere. In pratica, la patch (o serie) e la sua +descrizione devono essere un'unica cosa. Questo aiuta i manutentori e i +revisori. Probabilmente, alcuni revisori non hanno nemmeno ricevuto o visto +le versioni precedenti della patch. + +Descrivete le vostro modifiche usando l'imperativo, per esempio "make xyzzy +do frotz" piuttosto che "[This patch] makes xyzzy do frotz" or "[I] changed +xyzzy to do frotz", come se steste dando ordini al codice di cambiare il suo +comportamento. + +Se la patch corregge un baco conosciuto, fare riferimento a quel baco inserendo +il suo numero o il suo URL. Se la patch è la conseguenza di una discussione +su una lista di discussione, allora fornite l'URL all'archivio di quella +discussione; usate i collegamenti a https://lkml.kernel.org/ con il +``Message-Id``, in questo modo vi assicurerete che il collegamento non diventi +invalido nel tempo. + +Tuttavia, cercate di rendere la vostra spiegazione comprensibile anche senza +far riferimento a fonti esterne. In aggiunta ai collegamenti a bachi e liste +di discussione, riassumente i punti più importanti della discussione che hanno +portato alla creazione della patch. + +Se volete far riferimento a uno specifico commit, non usate solo +l'identificativo SHA-1. Per cortesia, aggiungete anche la breve riga +riassuntiva del commit per rendere la chiaro ai revisori l'oggetto. +Per esempio:: + + Commit e21d2170f36602ae2708 ("video: remove unnecessary + platform_set_drvdata()") removed the unnecessary + platform_set_drvdata(), but left the variable "dev" unused, + delete it. + +Dovreste anche assicurarvi di usare almeno i primi 12 caratteri +dell'identificativo SHA-1. Il repositorio del kernel ha *molti* oggetti e +questo rende possibile la collisione fra due identificativi con pochi +caratteri. Tenete ben presente che anche se oggi non ci sono collisioni con il +vostro identificativo a 6 caratteri, potrebbero essercene fra 5 anni da oggi. + +Se la vostra patch corregge un baco in un commit specifico, per esempio avete +trovato un problema usando ``git bisect``, per favore usate l'etichetta +'Fixes:' indicando i primi 12 caratteri dell'identificativo SHA-1 seguiti +dalla riga riassuntiva. Per esempio:: + + Fixes: e21d2170f366 ("video: remove unnecessary platform_set_drvdata()") + +La seguente configurazione di ``git config`` può essere usata per formattare +i risultati dei comandi ``git log`` o ``git show`` come nell'esempio +precedente:: + + [core] + abbrev = 12 + [pretty] + fixes = Fixes: %h (\"%s\") + +.. _it_split_changes: + +3) Separate le vostre modifiche +------------------------------- + +Separate ogni **cambiamento logico** in patch distinte. + +Per esempio, se i vostri cambiamenti per un singolo driver includono +sia delle correzioni di bachi che miglioramenti alle prestazioni, +allora separateli in due o più patch. Se i vostri cambiamenti includono +un aggiornamento dell'API e un nuovo driver che lo sfrutta, allora separateli +in due patch. + +D'altro canto, se fate una singola modifica su più file, raggruppate tutte +queste modifiche in una singola patch. Dunque, un singolo cambiamento logico +è contenuto in una sola patch. + +Il punto da ricordare è che ogni modifica dovrebbe fare delle modifiche +che siano facilmente comprensibili e che possano essere verificate dai revisori. +Ogni patch dovrebbe essere giustificabile di per sé. + +Se al fine di ottenere un cambiamento completo una patch dipende da un'altra, +va bene. Semplicemente scrivete una nota nella descrizione della patch per +farlo presente: **"this patch depends on patch X"**. + +Quando dividete i vostri cambiamenti in una serie di patch, prestate +particolare attenzione alla verifica di ogni patch della serie; per ognuna +il kernel deve compilare ed essere eseguito correttamente. Gli sviluppatori +che usano ``git bisect`` per scovare i problemi potrebbero finire nel mezzo +della vostra serie in un punto qualsiasi; non vi saranno grati se nel mezzo +avete introdotto dei bachi. + +Se non potete condensare la vostra serie di patch in una più piccola, allora +pubblicatene una quindicina alla volta e aspettate che vengano revisionate +ed integrate. + + +4) Verificate lo stile delle vostre modifiche +--------------------------------------------- + +Controllate che la vostra patch non violi lo stile del codice, maggiori +dettagli sono disponibili in :ref:`Documentation/translations/it_IT/process/coding-style.rst `. +Non farlo porta semplicemente a una perdita di tempo da parte dei revisori e +voi vedrete la vostra patch rifiutata, probabilmente senza nemmeno essere stata +letta. + +Un'eccezione importante si ha quando del codice viene spostato da un file +ad un altro -- in questo caso non dovreste modificare il codice spostato +per nessun motivo, almeno non nella patch che lo sposta. Questo separa +chiaramente l'azione di spostare il codice e il vostro cambiamento. +Questo aiuta enormemente la revisione delle vere differenze e permette agli +strumenti di tenere meglio la traccia della storia del codice. + +Prima di inviare una patch, verificatene lo stile usando l'apposito +verificatore (scripts/checkpatch.pl). Da notare, comunque, che il verificator +di stile dovrebbe essere visto come una guida, non come un sostituto al +giudizio umano. Se il vostro codice è migliore nonostante una violazione +dello stile, probabilmente è meglio lasciarlo com'è. + +Il verificatore ha tre diversi livelli di severità: + - ERROR: le cose sono molto probabilmente sbagliate + - WARNING: le cose necessitano d'essere revisionate con attenzione + - CHECK: le cose necessitano di un pensierino + +Dovreste essere in grado di giustificare tutte le eventuali violazioni rimaste +nella vostra patch. + + +5) Selezionate i destinatari della vostra patch +----------------------------------------------- + +Dovreste sempre inviare una copia della patch ai manutentori dei sottosistemi +interessati dalle modifiche; date un'occhiata al file MAINTAINERS e alla storia +delle revisioni per scoprire chi si occupa del codice. Lo script +scripts/get_maintainer.pl può esservi d'aiuto. Se non riuscite a trovare un +manutentore per il sottosistema su cui state lavorando, allora Andrew Morton +(akpm@linux-foundation.org) sarà la vostra ultima possibilità. + +Normalmente, dovreste anche scegliere una lista di discussione a cui inviare +la vostra serie di patch. La lista di discussione linux-kernel@vger.kernel.org +è proprio l'ultima spiaggia, il volume di email su questa lista fa si che +diversi sviluppatori non la seguano. Guardate nel file MAINTAINERS per trovare +la lista di discussione dedicata ad un sottosistema; probabilmente lì la vostra +patch riceverà molta più attenzione. Tuttavia, per favore, non spammate le +liste di discussione che non sono interessate al vostro lavoro. + +Molte delle liste di discussione relative al kernel vengono ospitate su +vger.kernel.org; potete trovare un loro elenco alla pagina +http://vger.kernel.org/vger-lists.html. Tuttavia, ci sono altre liste di +discussione ospitate altrove. + +Non inviate più di 15 patch alla volta sulle liste di discussione vger!!! + +L'ultimo giudizio sull'integrazione delle modifiche accettate spetta a +Linux Torvalds. Il suo indirizzo e-mail è . +Riceve moltissime e-mail, e, a questo punto, solo poche patch passano +direttamente attraverso il suo giudizio; quindi, dovreste fare del vostro +meglio per -evitare di- inviargli e-mail. + +Se avete una patch che corregge un baco di sicurezza che potrebbe essere +sfruttato, inviatela a security@kernel.org. Per bachi importanti, un breve +embargo potrebbe essere preso in considerazione per dare il tempo alle +distribuzioni di prendere la patch e renderla disponibile ai loro utenti; +in questo caso, ovviamente, la patch non dovrebbe essere inviata su alcuna +lista di discussione pubblica. + +Patch che correggono bachi importanti su un kernel già rilasciato, dovrebbero +essere inviate ai manutentori dei kernel stabili aggiungendo la seguente riga:: + + Cc: stable@vger.kernel.org + +nella vostra patch, nell'area dedicata alle firme (notate, NON come destinatario +delle e-mail). In aggiunta a questo file, dovreste leggere anche +:ref:`Documentation/translations/it_IT/process/stable-kernel-rules.rst ` + +Tuttavia, notate, che alcuni manutentori di sottosistema preferiscono avere +l'ultima parola su quali patch dovrebbero essere aggiunte ai kernel stabili. +La rete di manutentori, in particolare, non vorrebbe vedere i singoli +sviluppatori aggiungere alle loro patch delle righe come quella sopracitata. + +Se le modifiche hanno effetti sull'interfaccia con lo spazio utente, per favore +inviate una patch per le pagine man ai manutentori di suddette pagine (elencati +nel file MAINTAINERS), o almeno una notifica circa la vostra modifica, +cosicché l'informazione possa trovare la sua strada nel manuale. Le modifiche +all'API dello spazio utente dovrebbero essere inviate in copia anche a +linux-api@vger.kernel.org. + +Per le piccole patch potreste aggiungere in CC l'indirizzo +*Trivial Patch Monkey trivial@kernel.org* che ha lo scopo di raccogliere +le patch "banali". Date uno sguardo al file MAINTAINERS per vedere chi +è l'attuale amministratore. + +Le patch banali devono rientrare in una delle seguenti categorie: + +- errori grammaticali nella documentazione +- errori grammaticali negli errori che potrebbero rompere :manpage:`grep(1)` +- correzione di avvisi di compilazione (riempirsi di avvisi inutili è negativo) +- correzione di errori di compilazione (solo se correggono qualcosa sul serio) +- rimozione di funzioni/macro deprecate +- sostituzione di codice non potabile con uno portabile (anche in codice + specifico per un'architettura, dato che le persone copiano, fintanto che + la modifica sia banale) +- qualsiasi modifica dell'autore/manutentore di un file (in pratica + "patch monkey" in modalità ritrasmissione) + + +6) Niente: MIME, links, compressione, allegati. Solo puro testo +---------------------------------------------------------------- + +Linus e gli altri sviluppatori del kernel devono poter commentare +le modifiche che sottomettete. Per uno sviluppatore è importante +essere in grado di "citare" le vostre modifiche, usando normali +programmi di posta elettronica, cosicché sia possibile commentare +una porzione specifica del vostro codice. + +Per questa ragione tutte le patch devono essere inviate via e-mail +come testo. .. warning:: - TODO ancora da tradurre + Se decidete di copiare ed incollare la patch nel corpo dell'e-mail, state + attenti che il vostro programma non corrompa il contenuto con andate + a capo automatiche. + +La patch non deve essere un allegato MIME, compresso o meno. Molti +dei più popolari programmi di posta elettronica non trasmettono un allegato +MIME come puro testo, e questo rende impossibile commentare il vostro codice. +Inoltre, un allegato MIME rende l'attività di Linus più laboriosa, diminuendo +così la possibilità che il vostro allegato-MIME venga accettato. + +Eccezione: se il vostro servizio di posta storpia le patch, allora qualcuno +potrebbe chiedervi di rinviarle come allegato MIME. + +Leggete :ref:`Documentation/translations/it_IT/process/email-clients.rst ` +per dei suggerimenti sulla configurazione del programmi di posta elettronica +per l'invio di patch intatte. + +7) Dimensione delle e-mail +-------------------------- + +Le grosse modifiche non sono adatte ad una lista di discussione, e nemmeno +per alcuni manutentori. Se la vostra patch, non compressa, eccede i 300 kB +di spazio, allora caricatela in una spazio accessibile su internet fornendo +l'URL (collegamento) ad essa. Ma notate che se la vostra patch eccede i 300 kB +è quasi certo che necessiti comunque di essere spezzettata. + +8) Rispondere ai commenti di revisione +-------------------------------------- + +Quasi certamente i revisori vi invieranno dei commenti su come migliorare +la vostra patch. Dovete rispondere a questi commenti; ignorare i revisori +è un ottimo modo per essere ignorati. Riscontri o domande che non conducono +ad una modifica del codice quasi certamente dovrebbero portare ad un commento +nel changelog cosicché il prossimo revisore potrà meglio comprendere cosa stia +accadendo. + +Assicuratevi di dire ai revisori quali cambiamenti state facendo e di +ringraziarli per il loro tempo. Revisionare codice è un lavoro faticoso e che +richiede molto tempo, e a volte i revisori diventano burberi. Tuttavia, anche +in questo caso, rispondete con educazione e concentratevi sul problema che +hanno evidenziato. + +9) Non scoraggiatevi - o impazientitevi +--------------------------------------- + +Dopo che avete inviato le vostre modifiche, siate pazienti e aspettate. +I revisori sono persone occupate e potrebbero non ricevere la vostra patch +immediatamente. + +Un tempo, le patch erano solite scomparire nel vuoto senza alcun commento, +ma ora il processo di sviluppo funziona meglio. Dovreste ricevere commenti +in una settimana o poco più; se questo non dovesse accadere, assicuratevi di +aver inviato le patch correttamente. Aspettate almeno una settimana prima di +rinviare le modifiche o sollecitare i revisori - probabilmente anche di più +durante la finestra d'integrazione. + +10) Aggiungete PATCH nell'oggetto +--------------------------------- + +Dato l'alto volume di e-mail per Linus, e la lista linux-kernel, è prassi +prefiggere il vostro oggetto con [PATCH]. Questo permette a Linus e agli +altri sviluppatori del kernel di distinguere facilmente le patch dalle altre +discussioni. + + +11) Firmate il vostro lavoro - Il certificato d'origine dello sviluppatore +-------------------------------------------------------------------------- + +Per migliorare la tracciabilità su "chi ha fatto cosa", specialmente per +quelle patch che per raggiungere lo stadio finale passano attraverso +diversi livelli di manutentori, abbiamo introdotto la procedura di "firma" +delle patch che vengono inviate per e-mail. + +La firma è una semplice riga alla fine della descrizione della patch che +certifica che l'avete scritta voi o che avete il diritto di pubblicarla +come patch open-source. Le regole sono abbastanza semplici: se potete +certificare quanto segue: + +Il certificato d'origine dello sviluppatore 1.1 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Contribuendo a questo progetto, io certifico che: + + (a) Il contributo è stato creato interamente, o in parte, da me e che + ho il diritto di inviarlo in accordo con la licenza open-source + indicata nel file; oppure + + (b) Il contributo è basato su un lavoro precedente che, nei limiti + della mia conoscenza, è coperto da un'appropriata licenza + open-source che mi da il diritto di modificarlo e inviarlo, + le cui modifiche sono interamente o in parte mie, in accordo con + la licenza open-source (a meno che non abbia il permesso di usare + un'altra licenza) indicata nel file; oppure + + (c) Il contributo mi è stato fornito direttamente da qualcuno che + ha certificato (a), (b) o (c) e non l'ho modificata. + + (d) Capisco e concordo col fatto che questo progetto e i suoi + contributi sono pubblici e che un registro dei contributi (incluse + tutte le informazioni personali che invio con essi, inclusa la mia + firma) verrà mantenuto indefinitamente e che possa essere + ridistribuito in accordo con questo progetto o le licenze + open-source coinvolte. + +poi dovete solo aggiungere una riga che dice:: + + Signed-off-by: Random J Developer + +usando il vostro vero nome (spiacenti, non si accettano pseudonimi o +contributi anonimi). + +Alcune persone aggiungono delle etichette alla fine. Per ora queste verranno +ignorate, ma potete farlo per meglio identificare procedure aziendali interne o +per aggiungere dettagli circa la firma. + +Se siete un manutentore di un sottosistema o di un ramo, qualche volta dovrete +modificare leggermente le patch che avete ricevuto al fine di poterle +integrare; questo perché il codice non è esattamente lo stesso nei vostri +sorgenti e in quelli dei vostri contributori. Se rispettate rigidamente la +regola (c), dovreste chiedere al mittente di rifare la patch, ma questo è +controproducente e una totale perdita di tempo ed energia. La regola (b) +vi permette di correggere il codice, ma poi diventa davvero maleducato cambiare +la patch di qualcuno e addossargli la responsabilità per i vostri bachi. +Per risolvere questo problema dovreste aggiungere una riga, fra l'ultimo +Signed-off-by e il vostro, che spiega la vostra modifica. Nonostante non ci +sia nulla di obbligatorio, un modo efficace è quello di indicare il vostro +nome o indirizzo email fra parentesi quadre, seguito da una breve descrizione; +questo renderà abbastanza visibile chi è responsabile per le modifiche +dell'ultimo minuto. Per esempio:: + + Signed-off-by: Random J Developer + [lucky@maintainer.example.org: struct foo moved from foo.c to foo.h] + Signed-off-by: Lucky K Maintainer + +Questa pratica è particolarmente utile se siete i manutentori di un ramo +stabile ma al contempo volete dare credito agli autori, tracciare e integrare +le modifiche, e proteggere i mittenti dalle lamentele. Notate che in nessuna +circostanza è permessa la modifica dell'identità dell'autore (l'intestazione +From), dato che è quella che appare nei changelog. + +Un appunto speciale per chi porta il codice su vecchie versioni. Sembra che +sia comune l'utile pratica di inserire un'indicazione circa l'origine della +patch all'inizio del messaggio di commit (appena dopo la riga dell'oggetto) +al fine di migliorare la tracciabilità. Per esempio, questo è quello che si +vede nel rilascio stabile 3.x-stable:: + + Date: Tue Oct 7 07:26:38 2014 -0400 + + libata: Un-break ATA blacklist + + commit 1c40279960bcd7d52dbdf1d466b20d24b99176c8 upstream. + +E qui quello che potrebbe vedersi su un kernel più vecchio dove la patch è +stata applicata:: + + Date: Tue May 13 22:12:27 2008 +0200 + + wireless, airo: waitbusy() won't delay + + [backport of 2.6 commit b7acbdfbd1f277c1eb23f344f899cfa4cd0bf36a] + +Qualunque sia il formato, questa informazione fornisce un importante aiuto +alle persone che vogliono seguire i vostri sorgenti, e quelle che cercano +dei bachi. + + +12) Quando utilizzare Acked-by:, Cc:, e Co-Developed-by: +-------------------------------------------------------- + +L'etichetta Signed-off-by: indica che il firmatario è stato coinvolto nello +sviluppo della patch, o che era nel suo percorso di consegna. + +Se una persona non è direttamente coinvolta con la preparazione o gestione +della patch ma desidera firmare e mettere agli atti la loro approvazione, +allora queste persone possono chiedere di aggiungere al changelog della patch +una riga Acked-by:. + +Acked-by: viene spesso utilizzato dai manutentori del sottosistema in oggetto +quando quello stesso manutentore non ha contribuito né trasmesso la patch. + +Acked-by: non è formale come Signed-off-by:. Questo indica che la persona ha +revisionato la patch e l'ha trovata accettabile. Per cui, a volte, chi +integra le patch convertirà un "sì, mi sembra che vada bene" in un Acked-by: +(ma tenete presente che solitamente è meglio chiedere esplicitamente). + +Acked-by: non indica l'accettazione di un'intera patch. Per esempio, quando +una patch ha effetti su diversi sottosistemi e ha un Acked-by: da un +manutentore di uno di questi, significa che il manutentore accetta quella +parte di codice relativa al sottosistema che mantiene. Qui dovremmo essere +giudiziosi. Quando si hanno dei dubbi si dovrebbe far riferimento alla +discussione originale negli archivi della lista di discussione. + +Se una persona ha avuto l'opportunità di commentare la patch, ma non lo ha +fatto, potete aggiungere l'etichetta ``Cc:`` alla patch. Questa è l'unica +etichetta che può essere aggiunta senza che la persona in questione faccia +alcunché - ma dovrebbe indicare che la persona ha ricevuto una copia della +patch. Questa etichetta documenta che terzi potenzialmente interessati sono +stati inclusi nella discussione. + +L'etichetta Co-Developed-by: indica che la patch è stata scritta dall'autore in +collaborazione con un altro sviluppatore. Qualche volta questo è utile quando +più persone lavorano sulla stessa patch. Notate, questa persona deve avere +nella patch anche una riga Signed-off-by:. + + +13) Utilizzare Reported-by:, Tested-by:, Reviewed-by:, Suggested-by: e Fixes: +----------------------------------------------------------------------------- + +L'etichetta Reported-by da credito alle persone che trovano e riportano i bachi +e si spera che questo possa ispirarli ad aiutarci nuovamente in futuro. +Rammentate che se il baco è stato riportato in privato, dovrete chiedere il +permesso prima di poter utilizzare l'etichetta Reported-by. + +L'etichetta Tested-by: indica che la patch è stata verificata con successo +(su un qualche sistema) dalla persona citata. Questa etichetta informa i +manutentori che qualche verifica è stata fatta, fornisce un mezzo per trovare +persone che possano verificare il codice in futuro, e garantisce che queste +stesse persone ricevano credito per il loro lavoro. + +Reviewd-by:, invece, indica che la patch è stata revisionata ed è stata +considerata accettabile in accordo con la dichiarazione dei revisori: + +Dichiarazione di svista dei revisori +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Offrendo la mia etichetta Reviewed-by, dichiaro quanto segue: + + (a) Ho effettuato una revisione tecnica di questa patch per valutarne + l'adeguatezza ai fini dell'inclusione nel ramo principale del + kernel. + + (b) Tutti i problemi e le domande riguardanti la patch sono stati + comunicati al mittente. Sono soddisfatto dalle risposte + del mittente. + + (c) Nonostante ci potrebbero essere cose migliorabili in queste + sottomissione, credo che sia, in questo momento, (1) una modifica + di interesse per il kernel, e (2) libera da problemi che + potrebbero metterne in discussione l'integrazione. + + (d) Nonostante abbia revisionato la patch e creda che vada bene, + non garantisco (se non specificato altrimenti) che questa + otterrà quello che promette o funzionerà correttamente in tutte + le possibili situazioni. + +L'etichetta Reviewed-by è la dichiarazione di un parere sulla bontà di +una modifica che si ritiene appropriata e senza alcun problema tecnico +importante. Qualsiasi revisore interessato (quelli che lo hanno fatto) +possono offrire il proprio Reviewed-by per la patch. Questa etichetta serve +a dare credito ai revisori e a informare i manutentori sul livello di revisione +che è stato fatto sulla patch. L'etichetta Reviewd-by, quando fornita da +revisori conosciuti per la loro conoscenza sulla materia in oggetto e per la +loro serietà nella revisione, accrescerà le probabilità che la vostra patch +venga integrate nel kernel. + +L'etichetta Suggested-by: indica che l'idea della patch è stata suggerita +dalla persona nominata e le da credito. Tenete a mente che questa etichetta +non dovrebbe essere aggiunta senza un permesso esplicito, specialmente se +l'idea non è stata pubblicata in un forum pubblico. Detto ciò, dando credito +a chi ci fornisce delle idee, si spera di poterli ispirare ad aiutarci +nuovamente in futuro. + +L'etichetta Fixes: indica che la patch corregge un problema in un commit +precedente. Serve a chiarire l'origine di un baco, il che aiuta la revisione +del baco stesso. Questa etichetta è di aiuto anche per i manutentori dei +kernel stabili al fine di capire quale kernel deve ricevere la correzione. +Questo è il modo suggerito per indicare che un baco è stato corretto nella +patch. Per maggiori dettagli leggete :ref:`it_describe_changes` + + +14) Il formato canonico delle patch +----------------------------------- + +Questa sezione descrive il formato che dovrebbe essere usato per le patch. +Notate che se state usando un repositorio ``git`` per salvare le vostre patch +potere usare il comando ``git format-patch`` per ottenere patch nel formato +appropriato. Lo strumento non crea il testo necessario, per cui, leggete +le seguenti istruzioni. + +L'oggetto di una patch canonica è la riga:: + + Subject: [PATCH 001/123] subsystem: summary phrase + +Il corpo di una patch canonica contiene i seguenti elementi: + + - Una riga ``from`` che specifica l'autore della patch, seguita + da una riga vuota (necessaria soltanto se la persona che invia la + patch non ne è l'autore). + + - Il corpo della spiegazione, con linee non più lunghe di 75 caratteri, + che verrà copiato permanentemente nel changelog per descrivere la patch. + + - Una riga vuota + + - Le righe ``Signed-off-by:``, descritte in precedenza, che finiranno + anch'esse nel changelog. + + - Una linea di demarcazione contenente semplicemente ``---``. + + - Qualsiasi altro commento che non deve finire nel changelog. + + - Le effettive modifiche al codice (il prodotto di ``diff``). + +Il formato usato per l'oggetto permette ai programmi di posta di usarlo +per ordinare le patch alfabeticamente - tutti i programmi di posta hanno +questa funzionalità - dato che al numero sequenziale si antepongono degli zeri; +in questo modo l'ordine numerico ed alfabetico coincidono. + +Il ``subsystem`` nell'oggetto dell'email dovrebbe identificare l'area +o il sottosistema modificato dalla patch. + +La ``summary phrase`` nell'oggetto dell'email dovrebbe descrivere brevemente +il contenuto della patch. La ``summary phrase`` non dovrebbe essere un nome +di file. Non utilizzate la stessa ``summary phrase`` per tutte le patch in +una serie (dove una ``serie di patch`` è una sequenza ordinata di diverse +patch correlate). + +Ricordatevi che la ``summary phrase`` della vostra email diventerà un +identificatore globale ed unico per quella patch. Si propaga fino al +changelog ``git``. La ``summary phrase`` potrà essere usata in futuro +dagli sviluppatori per riferirsi a quella patch. Le persone vorranno +cercare la ``summary phrase`` su internet per leggere le discussioni che la +riguardano. Potrebbe anche essere l'unica cosa che le persone vedranno +quando, in due o tre mesi, riguarderanno centinaia di patch usando strumenti +come ``gitk`` o ``git log --oneline``. + +Per queste ragioni, dovrebbe essere lunga fra i 70 e i 75 caratteri, e deve +descrivere sia cosa viene modificato, sia il perché sia necessario. Essere +brevi e descrittivi è una bella sfida, ma questo è quello che fa un riassunto +ben scritto. + +La ``summary phrase`` può avere un'etichetta (*tag*) di prefisso racchiusa fra +le parentesi quadre "Subject: [PATCH ...] ". +Le etichette non verranno considerate come parte della frase riassuntiva, ma +indicano come la patch dovrebbe essere trattata. Fra le etichette più comuni +ci sono quelle di versione che vengono usate quando una patch è stata inviata +più volte (per esempio, "v1, v2, v3"); oppure "RFC" per indicare che si +attendono dei commenti (*Request For Comments*). Se ci sono quattro patch +nella serie, queste dovrebbero essere enumerate così: 1/4, 2/4, 3/4, 4/4. +Questo assicura che gli sviluppatori capiranno l'ordine in cui le patch +dovrebbero essere applicate, e per tracciare quelle che hanno revisionate o +che hanno applicato. + +Un paio di esempi di oggetti:: + + Subject: [PATCH 2/5] ext2: improve scalability of bitmap searching + Subject: [PATCH v2 01/27] x86: fix eflags tracking + +La riga ``from`` dev'essere la prima nel corpo del messaggio ed è nel +formato: + + From: Original Author + +La riga ``from`` indica chi verrà accreditato nel changelog permanente come +l'autore della patch. Se la riga ``from`` è mancante, allora per determinare +l'autore da inserire nel changelog verrà usata la riga ``From`` +nell'intestazione dell'email. + +Il corpo della spiegazione verrà incluso nel changelog permanente, per cui +deve aver senso per un lettore esperto che è ha dimenticato i dettagli della +discussione che hanno portato alla patch. L'inclusione di informazioni +sui problemi oggetto dalla patch (messaggi del kernel, messaggi di oops, +eccetera) è particolarmente utile per le persone che potrebbero cercare fra +i messaggi di log per la patch che li tratta. Se la patch corregge un errore +di compilazione, non sarà necessario includere proprio _tutto_ quello che +è uscito dal compilatore; aggiungete solo quello che è necessario per far si +che la vostra patch venga trovata. Come nella ``summary phrase``, è importante +essere sia brevi che descrittivi. + +La linea di demarcazione ``---`` serve essenzialmente a segnare dove finisce +il messaggio di changelog. + +Aggiungere il ``diffstat`` dopo ``---`` è un buon uso di questo spazio, per +mostrare i file che sono cambiati, e il numero di file aggiunto o rimossi. +Un ``diffstat`` è particolarmente utile per le patch grandi. Altri commenti +che sono importanti solo per i manutentori, quindi inadatti al changelog +permanente, dovrebbero essere messi qui. Un buon esempio per questo tipo +di commenti potrebbe essere quello di descrivere le differenze fra le versioni +della patch. + +Se includete un ``diffstat`` dopo ``---``, usate le opzioni ``-p 1 -w70`` +cosicché i nomi dei file elencati non occupino troppo spazio (facilmente +rientreranno negli 80 caratteri, magari con qualche indentazione). +(``git`` genera di base dei diffstat adatti). + +Maggiori dettagli sul formato delle patch nei riferimenti qui di seguito. + +.. _it_explicit_in_reply_to: + +15) Usare esplicitamente In-Reply-To nell'intestazione +------------------------------------------------------ + +Aggiungere manualmente In-Reply-To: nell'intestazione dell'e-mail +potrebbe essere d'aiuto per associare una patch ad una discussione +precedente, per esempio per collegare la correzione di un baco con l'e-mail +che lo riportava. Tuttavia, per serie di patch multiple è generalmente +sconsigliato l'uso di In-Reply-To: per collegare precedenti versioni. +In questo modo versioni multiple di una patch non diventeranno un'ingestibile +giungla di riferimenti all'interno dei programmi di posta. Se un collegamento +è utile, potete usare https://lkml.kernel.org/ per ottenere i collegamenti +ad una versione precedente di una serie di patch (per esempio, potete usarlo +per l'email introduttiva alla serie). + +16) Inviare richieste ``git pull`` +---------------------------------- + +Se avete una serie di patch, potrebbe essere più conveniente per un manutentore +tirarle dentro al repositorio del sottosistema attraverso l'operazione +``git pull``. Comunque, tenete presente che prendere patch da uno sviluppatore +in questo modo richiede un livello di fiducia più alto rispetto a prenderle da +una lista di discussione. Di conseguenza, molti manutentori sono riluttanti +ad accettare richieste di *pull*, specialmente dagli sviluppatori nuovi e +quindi sconosciuti. Se siete in dubbio, potete fare una richiesta di *pull* +come messaggio introduttivo ad una normale pubblicazione di patch, così +il manutentore avrà la possibilità di scegliere come integrarle. + +Una richiesta di *pull* dovrebbe avere nell'oggetto [GIT] o [PULL]. +La richiesta stessa dovrebbe includere il nome del repositorio e quello del +ramo su una singola riga; dovrebbe essere più o meno così:: + + Please pull from + + git://jdelvare.pck.nerim.net/jdelvare-2.6 i2c-for-linus + + to get these changes: + +Una richiesta di *pull* dovrebbe includere anche un messaggio generico +che dica cos'è incluso, una lista delle patch usando ``git shortlog``, e una +panoramica sugli effetti della serie di patch con ``diffstat``. Il modo più +semplice per ottenere tutte queste informazioni è, ovviamente, quello di +lasciar fare tutto a ``git`` con il comando ``git request-pull``. + +Alcuni manutentori (incluso Linus) vogliono vedere le richieste di *pull* +da commit firmati con GPG; questo fornisce una maggiore garanzia sul fatto +che siate stati proprio voi a fare la richiesta. In assenza di tale etichetta +firmata Linus, in particolare, non prenderà alcuna patch da siti pubblici come +GitHub. + +Il primo passo verso la creazione di questa etichetta firmata è quello di +creare una chiave GNUPG ed averla fatta firmare da uno o più sviluppatori +principali del kernel. Questo potrebbe essere difficile per i nuovi +sviluppatori, ma non ci sono altre vie. Andare alle conferenze potrebbe +essere un buon modo per trovare sviluppatori che possano firmare la vostra +chiave. + +Una volta che avete preparato la vostra serie di patch in ``git``, e volete che +qualcuno le prenda, create una etichetta firmata col comando ``git tag -s``. +Questo creerà una nuova etichetta che identifica l'ultimo commit della serie +contenente una firma creata con la vostra chiave privata. Avrete anche +l'opportunità di aggiungere un messaggio di changelog all'etichetta; questo è +il posto ideale per descrivere gli effetti della richiesta di *pull*. + +Se i sorgenti da cui il manutentore prenderà le patch non sono gli stessi del +repositorio su cui state lavorando, allora non dimenticatevi di caricare +l'etichetta firmata anche sui sorgenti pubblici. + +Quando generate una richiesta di *pull*, usate l'etichetta firmata come +obiettivo. Un comando come il seguente farà il suo dovere:: + + git request-pull master git://my.public.tree/linux.git my-signed-tag + + +Riferimenti +----------- + +Andrew Morton, "La patch perfetta" (tpp). + + +Jeff Garzik, "Formato per la sottomissione di patch per il kernel Linux" + + +Greg Kroah-Hartman, "Come scocciare un manutentore di un sottosistema" + + + + + + + + + + + + +No!!!! Basta gigantesche bombe patch alle persone sulla lista linux-kernel@vger.kernel.org! + + +Kernel Documentation/translations/it_IT/process/coding-style.rst: + :ref:`Documentation/translations/it_IT/process/coding-style.rst ` + +E-mail di Linus Torvalds sul formato canonico di una patch: + + +Andi Kleen, "Su come sottomettere patch del kernel" + Alcune strategie su come sottomettere modifiche toste o controverse. + + http://halobates.de/on-submitting-patches.pdf From 787d07ed8b2ca726194b461f27fe5785d10add7d Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Fri, 4 Jan 2019 22:27:22 +0800 Subject: [PATCH 0128/4277] doc: fix typo in Documentation/hwmon/f71882fg Just fix a typo in Documentation/hwmon/f71882fg. Signed-off-by: Chengguang Xu Signed-off-by: Jonathan Corbet --- Documentation/hwmon/f71882fg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/f71882fg b/Documentation/hwmon/f71882fg index de91c0db5846..4c3cb8377d74 100644 --- a/Documentation/hwmon/f71882fg +++ b/Documentation/hwmon/f71882fg @@ -94,7 +94,7 @@ Note that the lowest numbered temperature zone trip point corresponds to to the border between the highest and one but highest temperature zones, and vica versa. So the temperature zone trip points 1-4 (or 1-2) go from high temp to low temp! This is how things are implemented in the IC, and the driver -mimicks this. +mimics this. There are 2 modes to specify the speed of the fan, PWM duty cycle (or DC voltage) mode, where 0-100% duty cycle (0-100% of 12V) is specified. And RPM From ad0d92d7ba6aecbe2705907c38ff8d8be4da1e9c Mon Sep 17 00:00:00 2001 From: Robin Gong Date: Tue, 8 Jan 2019 12:00:16 +0000 Subject: [PATCH 0129/4277] dmaengine: imx-sdma: refine to load context only once The context loaded only one time before channel running,but currently sdma_config_channel() and dma_prep_* duplicated with sdma_load_context(), so refine it to load context only one time before channel running and reload after the channel terminated. Signed-off-by: Robin Gong Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index a2b0a0e71168..015c17a3b826 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -377,6 +377,7 @@ struct sdma_channel { unsigned long watermark_level; u32 shp_addr, per_addr; enum dma_status status; + bool context_loaded; struct imx_dma_data data; struct work_struct terminate_worker; }; @@ -970,6 +971,9 @@ static int sdma_load_context(struct sdma_channel *sdmac) int ret; unsigned long flags; + if (sdmac->context_loaded) + return 0; + if (sdmac->direction == DMA_DEV_TO_MEM) load_address = sdmac->pc_from_device; else if (sdmac->direction == DMA_DEV_TO_DEV) @@ -1012,6 +1016,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) spin_unlock_irqrestore(&sdma->channel_0_lock, flags); + sdmac->context_loaded = true; + return ret; } @@ -1051,6 +1057,7 @@ static void sdma_channel_terminate_work(struct work_struct *work) sdmac->desc = NULL; spin_unlock_irqrestore(&sdmac->vc.lock, flags); vchan_dma_desc_free_list(&sdmac->vc, &head); + sdmac->context_loaded = false; } static int sdma_disable_channel_async(struct dma_chan *chan) From bdcb2c5d5ed0f100ae21decd30f0b2556b738b72 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 8 Jan 2019 17:57:55 +0200 Subject: [PATCH 0130/4277] dmaengine: dw-axi-dmac: Fix trivia typo Field name ststus_hi should be spelled as status_hi. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index f8888dc0b8dc..18b6014cf9b4 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -75,7 +75,7 @@ struct __packed axi_dma_lli { __le32 sstat; __le32 dstat; __le32 status_lo; - __le32 ststus_hi; + __le32 status_hi; __le32 reserved_lo; __le32 reserved_hi; }; From 31867b23d7d1ee3535136c6a410a6cf56f666bfc Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 28 Dec 2018 11:00:38 -0800 Subject: [PATCH 0131/4277] f2fs: wait on atomic writes to count F2FS_CP_WB_DATA Otherwise, we can get wrong counts incurring checkpoint hang. IO_W (CP: -24, Data: 24, Flush: ( 0 0 1), Discard: ( 0 0)) Thread A Thread B - f2fs_write_data_pages - __write_data_page - f2fs_submit_page_write - inc_page_count(F2FS_WB_DATA) type is F2FS_WB_DATA due to file is non-atomic one - f2fs_ioc_start_atomic_write - set_inode_flag(FI_ATOMIC_FILE) - f2fs_write_end_io - dec_page_count(F2FS_WB_CP_DATA) type is F2FS_WB_DATA due to file becomes atomic one Cc: Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bba56b39dcc5..ae2b45e75847 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1750,10 +1750,12 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); - if (!get_dirty_pages(inode)) - goto skip_flush; - - f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, + /* + * Should wait end_io to count F2FS_WB_CP_DATA correctly by + * f2fs_is_atomic_file. + */ + if (get_dirty_pages(inode)) + f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, "Unexpected flush for atomic writes: ino=%lu, npages=%u", inode->i_ino, get_dirty_pages(inode)); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); @@ -1761,7 +1763,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); goto out; } -skip_flush: + set_inode_flag(inode, FI_ATOMIC_FILE); clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); From 7c77bf7de1574ac7a31a2b76f4927404307d13e7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 1 Jan 2019 00:11:30 -0800 Subject: [PATCH 0132/4277] f2fs: don't access node/meta inode mapping after iput This fixes wrong access of address spaces of node and meta inodes after iput. Fixes: 60aa4d5536ab ("f2fs: fix use-after-free issue when accessing sbi->stat_info") Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 19 ++++++++++++------- fs/f2fs/super.c | 5 +++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index ebcc121920ba..503fde8349e6 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -96,8 +96,10 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->free_secs = free_sections(sbi); si->prefree_count = prefree_segments(sbi); si->dirty_count = dirty_segments(sbi); - si->node_pages = NODE_MAPPING(sbi)->nrpages; - si->meta_pages = META_MAPPING(sbi)->nrpages; + if (sbi->node_inode) + si->node_pages = NODE_MAPPING(sbi)->nrpages; + if (sbi->meta_inode) + si->meta_pages = META_MAPPING(sbi)->nrpages; si->nats = NM_I(sbi)->nat_cnt; si->dirty_nats = NM_I(sbi)->dirty_nat_cnt; si->sits = MAIN_SEGS(sbi); @@ -175,7 +177,6 @@ static void update_sit_info(struct f2fs_sb_info *sbi) static void update_mem_info(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = F2FS_STAT(sbi); - unsigned npages; int i; if (si->base_mem) @@ -258,10 +259,14 @@ get_cache: sizeof(struct extent_node); si->page_mem = 0; - npages = NODE_MAPPING(sbi)->nrpages; - si->page_mem += (unsigned long long)npages << PAGE_SHIFT; - npages = META_MAPPING(sbi)->nrpages; - si->page_mem += (unsigned long long)npages << PAGE_SHIFT; + if (sbi->node_inode) { + unsigned npages = NODE_MAPPING(sbi)->nrpages; + si->page_mem += (unsigned long long)npages << PAGE_SHIFT; + } + if (sbi->meta_inode) { + unsigned npages = META_MAPPING(sbi)->nrpages; + si->page_mem += (unsigned long long)npages << PAGE_SHIFT; + } } static int stat_show(struct seq_file *s, void *v) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c46a1d4318d4..14f033e1ab42 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1075,7 +1075,10 @@ static void f2fs_put_super(struct super_block *sb) f2fs_bug_on(sbi, sbi->fsync_node_num); iput(sbi->node_inode); + sbi->node_inode = NULL; + iput(sbi->meta_inode); + sbi->meta_inode = NULL; /* * iput() can update stat information, if f2fs_write_checkpoint() @@ -3410,6 +3413,7 @@ free_node_inode: f2fs_release_ino_entry(sbi, true); truncate_inode_pages_final(NODE_MAPPING(sbi)); iput(sbi->node_inode); + sbi->node_inode = NULL; free_stats: f2fs_destroy_stats(sbi); free_nm: @@ -3422,6 +3426,7 @@ free_devices: free_meta_inode: make_bad_inode(sbi->meta_inode); iput(sbi->meta_inode); + sbi->meta_inode = NULL; free_io_dummy: mempool_destroy(sbi->write_io_dummy); free_percpu: From f365c6cc85b1d2348d73bf327258874fcc7ac161 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 1 Jan 2019 21:33:11 +0800 Subject: [PATCH 0133/4277] f2fs: change error code to -ENOMEM from -EINVAL The error case of failing allocating memory should return -ENOMEM. Signed-off-by: Chengguang Xu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 14f033e1ab42..bc32a1035f65 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -269,7 +269,7 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, if (!qname) { f2fs_msg(sb, KERN_ERR, "Not enough memory for storing quotafile name"); - return -EINVAL; + return -ENOMEM; } if (F2FS_OPTION(sbi).s_qf_names[qtype]) { if (strcmp(F2FS_OPTION(sbi).s_qf_names[qtype], qname) == 0) From 539cd352239f6acae4a7997bf31edca5c9a69c81 Mon Sep 17 00:00:00 2001 From: Zhikang Zhang Date: Thu, 3 Jan 2019 20:06:38 +0800 Subject: [PATCH 0134/4277] f2fs: fix compile warnings: 'struct *' declared inside parameter list We meet these compile warnings below, which caused by missing declare structs: struct f2fs_io_info, struct extent, struct f2fs_sb_info. warning: 'struct f2fs_io_info' declared inside parameter list warning: 'struct extent_info' declared inside parameter list warning: 'struct f2fs_sb_info' declared inside parameter list Signed-off-by: Zhikang Zhang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- include/trace/events/f2fs.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 3ec73f17ee2a..8a28a2b1be74 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -149,6 +149,9 @@ TRACE_DEFINE_ENUM(CP_TRIMMED); { CP_SPEC_LOG_NUM, "log type is 2" }, \ { CP_RECOVER_DIR, "dir needs recovery" }) +struct f2fs_sb_info; +struct f2fs_io_info; +struct extent_info; struct victim_sel_policy; struct f2fs_map_blocks; From 8e11403876b1e29d290ea42bb20bf45ef927ef6e Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 4 Jan 2019 01:38:29 +0000 Subject: [PATCH 0135/4277] f2fs: remove set but not used variable 'err' Fixes gcc '-Wunused-but-set-variable' warning: fs/f2fs/data.c: In function 'f2fs_dio_submit_bio': fs/f2fs/data.c:2585:6: warning: variable 'err' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f91d8630c9a2..4e402add2e60 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2582,14 +2582,11 @@ static void f2fs_dio_submit_bio(struct bio *bio, struct inode *inode, { struct f2fs_private_dio *dio; bool write = (bio_op(bio) == REQ_OP_WRITE); - int err; dio = f2fs_kzalloc(F2FS_I_SB(inode), sizeof(struct f2fs_private_dio), GFP_NOFS); - if (!dio) { - err = -ENOMEM; + if (!dio) goto out; - } dio->inode = inode; dio->orig_end_io = bio->bi_end_io; From 9ac6cb5fbb1781d120ca0ad29d014d35c9c3f0c4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:17 +0100 Subject: [PATCH 0136/4277] i2c: add suspended flag and accessors for i2c adapters A few drivers open code the handling of suspended adapters. It could be handled by the core, though, to ensure generic handling. This patch adds the flag and accessor functions. The usage of these helpers is optional, though. See the kerneldoc in this patch. Using the new flag, we now reject further transfers if the adapter is already marked suspended. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- Documentation/i2c/fault-codes | 4 ++++ drivers/i2c/i2c-core-base.c | 3 +++ include/linux/i2c.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/Documentation/i2c/fault-codes b/Documentation/i2c/fault-codes index 47c25abb7d52..0cee0fc545b4 100644 --- a/Documentation/i2c/fault-codes +++ b/Documentation/i2c/fault-codes @@ -112,6 +112,10 @@ EPROTO case is when the length of an SMBus block data response (from the SMBus slave) is outside the range 1-32 bytes. +ESHUTDOWN + Returned when a transfer was requested using an adapter + which is already suspended. + ETIMEDOUT This is returned by drivers when an operation took too much time, and was aborted before it completed. diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 28460f6a60cc..926ca0a7477f 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1232,6 +1232,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (!adap->lock_ops) adap->lock_ops = &i2c_adapter_lock_ops; + adap->locked_flags = 0; rt_mutex_init(&adap->bus_lock); rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); @@ -1865,6 +1866,8 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (WARN_ON(!msgs || num < 1)) return -EINVAL; + if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags))) + return -ESHUTDOWN; if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) return -EOPNOTSUPP; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 65b4eaed1d96..cba59d66c00d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -680,6 +680,8 @@ struct i2c_adapter { int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ + unsigned long locked_flags; /* owned by the I2C core */ +#define I2C_ALF_IS_SUSPENDED 0 int nr; char name[48]; @@ -762,6 +764,38 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) adapter->lock_ops->unlock_bus(adapter, flags); } +/** + * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core + * @adap: Adapter to mark as suspended + * + * When using this helper to mark an adapter as suspended, the core will reject + * further transfers to this adapter. The usage of this helper is optional but + * recommended for devices having distinct handlers for system suspend and + * runtime suspend. More complex devices are free to implement custom solutions + * to reject transfers when suspended. + */ +static inline void i2c_mark_adapter_suspended(struct i2c_adapter *adap) +{ + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + set_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); +} + +/** + * i2c_mark_adapter_resumed - Report resumed state of the adapter to the core + * @adap: Adapter to mark as resumed + * + * When using this helper to mark an adapter as resumed, the core will allow + * further transfers to this adapter. See also further notes to + * @i2c_mark_adapter_suspended(). + */ +static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) +{ + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + clear_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); +} + /*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ From 1adcc83e212c56afccdb633f5b3db020afbd0860 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:19 +0100 Subject: [PATCH 0137/4277] i2c: synquacer: remove unused is_suspended flag This flag was defined and checked but never set a value. Remove it. Signed-off-by: Wolfram Sang Acked-by: Ard Biesheuvel Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-synquacer.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c index 2184b7c3580e..d18b0941b71a 100644 --- a/drivers/i2c/busses/i2c-synquacer.c +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -144,8 +144,6 @@ struct synquacer_i2c { u32 timeout_ms; enum i2c_state state; struct i2c_adapter adapter; - - bool is_suspended; }; static inline int is_lastmsg(struct synquacer_i2c *i2c) @@ -316,9 +314,6 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c, unsigned long timeout; int ret; - if (i2c->is_suspended) - return -EBUSY; - synquacer_i2c_hw_init(i2c); bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR); if (bsr & SYNQUACER_I2C_BSR_BB) { From f2e0d28cba1e049ccf8aa11fc30c3d7fb9ab8278 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:20 +0100 Subject: [PATCH 0138/4277] i2c: brcmstb: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Signed-off-by: Wolfram Sang Reviewed-by: Kamal Dasu Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-brcmstb.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 826d32049996..f4d862234980 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -170,7 +170,6 @@ struct brcmstb_i2c_dev { struct bsc_regs *bsc_regmap; struct i2c_adapter adapter; struct completion done; - bool is_suspended; u32 clk_freq_hz; int data_regsz; }; @@ -467,9 +466,6 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter, int xfersz = brcmstb_i2c_get_xfersz(dev); u32 cond, cond_per_msg; - if (dev->is_suspended) - return -EBUSY; - /* Loop through all messages */ for (i = 0; i < num; i++) { pmsg = &msgs[i]; @@ -689,10 +685,7 @@ static int brcmstb_i2c_suspend(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i2c_dev->is_suspended = true; - i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - + i2c_mark_adapter_suspended(&i2c_dev->adapter); return 0; } @@ -700,10 +693,8 @@ static int brcmstb_i2c_resume(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); brcmstb_i2c_set_bsc_reg_defaults(i2c_dev); - i2c_dev->is_suspended = false; - i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_resumed(&i2c_dev->adapter); return 0; } From 5601df355b63a8ddac21b2dfc3433d941a994c32 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:21 +0100 Subject: [PATCH 0139/4277] i2c: zx2967: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Also, this will ensure proper locking which was forgotten in this open coded version and make sure resume mark is set after enabling clocks (not before). Signed-off-by: Wolfram Sang Acked-by: Shawn Guo Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-zx2967.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-zx2967.c b/drivers/i2c/busses/i2c-zx2967.c index b8f9e020d80e..7b98d97da3c6 100644 --- a/drivers/i2c/busses/i2c-zx2967.c +++ b/drivers/i2c/busses/i2c-zx2967.c @@ -66,7 +66,6 @@ struct zx2967_i2c { int msg_rd; u8 *cur_trans; u8 access_cnt; - bool is_suspended; int error; }; @@ -313,9 +312,6 @@ static int zx2967_i2c_xfer(struct i2c_adapter *adap, int ret; int i; - if (i2c->is_suspended) - return -EBUSY; - zx2967_set_addr(i2c, msgs->addr); for (i = 0; i < num; i++) { @@ -470,7 +466,7 @@ static int __maybe_unused zx2967_i2c_suspend(struct device *dev) { struct zx2967_i2c *i2c = dev_get_drvdata(dev); - i2c->is_suspended = true; + i2c_mark_adapter_suspended(&i2c->adap); clk_disable_unprepare(i2c->clk); return 0; @@ -480,8 +476,8 @@ static int __maybe_unused zx2967_i2c_resume(struct device *dev) { struct zx2967_i2c *i2c = dev_get_drvdata(dev); - i2c->is_suspended = false; clk_prepare_enable(i2c->clk); + i2c_mark_adapter_resumed(&i2c->adap); return 0; } From 7b6b69984e0411daac4ba78c7dc6c6457c4aa5b4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:22 +0100 Subject: [PATCH 0140/4277] i2c: sprd: don't use pdev as variable name for struct device * The pointer to a device is usually named 'dev'. These 'pdev' here look much like copy&paste errors. Fix them to avoid confusion. Signed-off-by: Wolfram Sang Reviewed-by: Baolin Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sprd.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index a94e724f51dc..e266d8a713d9 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -586,40 +586,40 @@ static int sprd_i2c_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev) +static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = true; i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - return pm_runtime_force_suspend(pdev); + return pm_runtime_force_suspend(dev); } -static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev) +static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); i2c_dev->is_suspended = false; i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - return pm_runtime_force_resume(pdev); + return pm_runtime_force_resume(dev); } -static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev) +static int __maybe_unused sprd_i2c_runtime_suspend(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); clk_disable_unprepare(i2c_dev->clk); return 0; } -static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev) +static int __maybe_unused sprd_i2c_runtime_resume(struct device *dev) { - struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); int ret; ret = clk_prepare_enable(i2c_dev->clk); From 5a7b81ff1b3e4493363c913e9f0affa904062798 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:23 +0100 Subject: [PATCH 0141/4277] i2c: sprd: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Signed-off-by: Wolfram Sang Reviewed-by: Baolin Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sprd.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index e266d8a713d9..961123529678 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -86,7 +86,6 @@ struct sprd_i2c { u32 count; int irq; int err; - bool is_suspended; }; static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count) @@ -284,9 +283,6 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct sprd_i2c *i2c_dev = i2c_adap->algo_data; int im, ret; - if (i2c_dev->is_suspended) - return -EBUSY; - ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) return ret; @@ -590,10 +586,7 @@ static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev) { struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - i2c_dev->is_suspended = true; - i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - + i2c_mark_adapter_suspended(&i2c_dev->adap); return pm_runtime_force_suspend(dev); } @@ -601,10 +594,7 @@ static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev) { struct sprd_i2c *i2c_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - i2c_dev->is_suspended = false; - i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER); - + i2c_mark_adapter_resumed(&i2c_dev->adap); return pm_runtime_force_resume(dev); } From d5c95bd6f3254e23736c48ff8f6c69c470b910a4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:24 +0100 Subject: [PATCH 0142/4277] i2c: exynos5: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Also, this will ensure proper locking which was forgotten in this open coded version. Signed-off-by: Wolfram Sang Tested-by: Marek Szyprowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-exynos5.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index c1ce2299a76e..41de4ee409b6 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -183,7 +183,6 @@ enum i2c_type_exynos { struct exynos5_i2c { struct i2c_adapter adap; - unsigned int suspended:1; struct i2c_msg *msg; struct completion msg_complete; @@ -715,11 +714,6 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap, struct exynos5_i2c *i2c = adap->algo_data; int i, ret; - if (i2c->suspended) { - dev_err(i2c->dev, "HS-I2C is not initialized.\n"); - return -EIO; - } - ret = clk_enable(i2c->clk); if (ret) return ret; @@ -847,8 +841,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev) { struct exynos5_i2c *i2c = dev_get_drvdata(dev); - i2c->suspended = 1; - + i2c_mark_adapter_suspended(&i2c->adap); clk_unprepare(i2c->clk); return 0; @@ -871,7 +864,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev) exynos5_i2c_init(i2c); clk_disable(i2c->clk); - i2c->suspended = 0; + i2c_mark_adapter_resumed(&i2c->adap); return 0; } From 2088716f90d160e735a469b8e1d0653facd74f16 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:25 +0100 Subject: [PATCH 0143/4277] i2c: s3c2410: use core helper to mark adapter suspended Rejecting transfers should be handled by the core. Also, this will ensure proper locking which was forgotten in this open coded version. Signed-off-by: Wolfram Sang Tested-by: Marek Szyprowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 2f2e28d60ef5..53bc021f4a5a 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -104,7 +104,6 @@ enum s3c24xx_i2c_state { struct s3c24xx_i2c { wait_queue_head_t wait; kernel_ulong_t quirks; - unsigned int suspended:1; struct i2c_msg *msg; unsigned int msg_num; @@ -703,9 +702,6 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, unsigned long timeout; int ret; - if (i2c->suspended) - return -EIO; - ret = s3c24xx_i2c_set_master(i2c); if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); @@ -1246,7 +1242,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev) { struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); - i2c->suspended = 1; + i2c_mark_adapter_suspended(&i2c->adap); if (!IS_ERR(i2c->sysreg)) regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg); @@ -1267,7 +1263,7 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev) return ret; s3c24xx_i2c_init(i2c); clk_disable(i2c->clk); - i2c->suspended = 0; + i2c_mark_adapter_resumed(&i2c->adap); return 0; } From 18569fa89a4db9ed6b5181624788a1574a9b6ed7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 19 Dec 2018 17:48:26 +0100 Subject: [PATCH 0144/4277] i2c: rcar: add suspend/resume support Because the adapter will be set up before every transaction anyhow, we just need to mark it as suspended to the I2C core. Signed-off-by: Hiromitsu Yamasaki Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 254e6219e538..1d6390eed861 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1017,10 +1017,35 @@ static int rcar_i2c_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int rcar_i2c_suspend(struct device *dev) +{ + struct rcar_i2c_priv *priv = dev_get_drvdata(dev); + + i2c_mark_adapter_suspended(&priv->adap); + return 0; +} + +static int rcar_i2c_resume(struct device *dev) +{ + struct rcar_i2c_priv *priv = dev_get_drvdata(dev); + + i2c_mark_adapter_resumed(&priv->adap); + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_i2c_pm_ops, rcar_i2c_suspend, rcar_i2c_resume); + +#define DEV_PM_OPS (&rcar_i2c_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + static struct platform_driver rcar_i2c_driver = { .driver = { .name = "i2c-rcar", .of_match_table = rcar_i2c_dt_ids, + .pm = DEV_PM_OPS, }, .probe = rcar_i2c_probe, .remove = rcar_i2c_remove, From 7a7b0fea6f848d0a73ee6defe6b9d192af2dda3a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Jan 2019 10:08:05 -0600 Subject: [PATCH 0145/4277] IB/srp: Use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Bart Van Assche Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/srp/ib_srp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 31d91538bbf4..0f855294ff3f 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -443,8 +443,7 @@ static struct srp_fr_pool *srp_create_fr_pool(struct ib_device *device, if (pool_size <= 0) goto err; ret = -ENOMEM; - pool = kzalloc(sizeof(struct srp_fr_pool) + - pool_size * sizeof(struct srp_fr_desc), GFP_KERNEL); + pool = kzalloc(struct_size(pool, desc, pool_size), GFP_KERNEL); if (!pool) goto err; pool->size = pool_size; From 4ab5a5d2a4a2289c2af07accbec7170ca5671f41 Mon Sep 17 00:00:00 2001 From: Thorsten Leemhuis Date: Tue, 8 Jan 2019 20:40:06 +0100 Subject: [PATCH 0146/4277] tools: add a kernel-chktaint to tools/debugging Add a script to the tools/ directory that shows if or why the running kernel was tainted. The script was mostly written by Randy Dunlap; I enhanced the script a bit. There does not appear to be a good home for this script. so create tools/debugging for tools of this nature. Signed-off-by: Randy Dunlap Signed-off-by: Thorsten Leemhuis [ jc: fixed conflicts, rewrote changelog ] Signed-off-by: Jonathan Corbet --- tools/Makefile | 14 ++- tools/debugging/Makefile | 16 +++ tools/debugging/kernel-chktaint | 202 ++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 6 deletions(-) create mode 100644 tools/debugging/Makefile create mode 100755 tools/debugging/kernel-chktaint diff --git a/tools/Makefile b/tools/Makefile index abb358a70ad0..c0d1e59f5abb 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' cgroup - cgroup tools' @echo ' cpupower - a tool for all things x86 CPU power' + @echo ' debugging - tools for debugging' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' freefall - laptop accelerometer program for disk protection' @echo ' gpio - GPIO tools' @@ -60,7 +61,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi pci: FORCE +cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi pci debugging: FORCE $(call descend,$@) liblockdep: FORCE @@ -95,7 +96,8 @@ kvm_stat: FORCE all: acpi cgroup cpupower gpio hv firewire liblockdep \ perf selftests spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ - tmon freefall iio objtool kvm_stat wmi pci + tmon freefall iio objtool kvm_stat wmi \ + pci debugging acpi_install: $(call descend,power/$(@:_install=),install) @@ -103,7 +105,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install: +cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install: $(call descend,$(@:_install=),install) liblockdep_install: @@ -129,7 +131,7 @@ install: acpi_install cgroup_install cpupower_install gpio_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install \ - wmi_install pci_install + wmi_install pci_install debugging_install acpi_clean: $(call descend,power/acpi,clean) @@ -137,7 +139,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean: +cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean debugging_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean: @@ -175,6 +177,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ - gpio_clean objtool_clean leds_clean wmi_clean pci_clean + gpio_clean objtool_clean leds_clean wmi_clean pci_clean debugging_clean .PHONY: FORCE diff --git a/tools/debugging/Makefile b/tools/debugging/Makefile new file mode 100644 index 000000000000..e2b7c1a6fb8f --- /dev/null +++ b/tools/debugging/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for debugging tools + +PREFIX ?= /usr +BINDIR ?= bin +INSTALL ?= install + +TARGET = kernel-chktaint + +all: $(TARGET) + +clean: + +install: kernel-chktaint + $(INSTALL) -D -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/$(BINDIR)/$(TARGET) + diff --git a/tools/debugging/kernel-chktaint b/tools/debugging/kernel-chktaint new file mode 100755 index 000000000000..2240cb56e6e5 --- /dev/null +++ b/tools/debugging/kernel-chktaint @@ -0,0 +1,202 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Randy Dunlap , 2018 +# Thorsten Leemhuis , 2018 + +usage() +{ + cat < + +Call without parameters to decode /proc/sys/kernel/tainted. + +Call with a positive integer as parameter to decode a value you +retrieved from /proc/sys/kernel/tainted on another system. + +EOF +} + +if [ "$1"x != "x" ]; then + if [ "$1"x == "--helpx" ] || [ "$1"x == "-hx" ] ; then + usage + exit 1 + elif [ $1 -ge 0 ] 2>/dev/null ; then + taint=$1 + else + echo "Error: Parameter '$1' not a positive interger. Aborting." >&2 + exit 1 + fi +else + TAINTFILE="/proc/sys/kernel/tainted" + if [ ! -r $TAINTFILE ]; then + echo "No file: $TAINTFILE" + exit + fi + + taint=`cat $TAINTFILE` +fi + +if [ $taint -eq 0 ]; then + echo "Kernel not Tainted" + exit +else + echo "Kernel is \"tainted\" for the following reasons:" +fi + +T=$taint +out= + +addout() { + out=$out$1 +} + +if [ `expr $T % 2` -eq 0 ]; then + addout "G" +else + addout "P" + echo " * proprietary module was loaded (#0)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "F" + echo " * module was force loaded (#1)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "S" + echo " * SMP kernel oops on an officially SMP incapable processor (#2)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "R" + echo " * module was force unloaded (#3)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "M" + echo " * processor reported a Machine Check Exception (MCE) (#4)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "B" + echo " * bad page referenced or some unexpected page flags (#5)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "U" + echo " * taint requested by userspace application (#6)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "D" + echo " * kernel died recently, i.e. there was an OOPS or BUG (#7)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "A" + echo " * an ACPI table was overridden by user (#8)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "W" + echo " * kernel issued warning (#9)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "C" + echo " * staging driver was loaded (#10)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "I" + echo " * workaround for bug in platform firmware applied (#11)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "O" + echo " * externally-built ('out-of-tree') module was loaded (#12)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "E" + echo " * unsigned module was loaded (#13)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "L" + echo " * soft lockup occurred (#14)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "K" + echo " * kernel has been live patched (#15)" +fi + +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "X" + echo " * auxiliary taint, defined for and used by distros (#16)" + +fi +T=`expr $T / 2` +if [ `expr $T % 2` -eq 0 ]; then + addout " " +else + addout "T" + echo " * kernel was built with the struct randomization plugin (#17)" +fi + +echo "For a more detailed explanation of the various taint flags see" +echo " Documentation/admin-guide/tainted-kernels.rst in the the Linux kernel sources" +echo " or https://kernel.org/doc/html/latest/admin-guide/tainted-kernels.html" +echo "Raw taint value as int/string: $taint/'$out'" +#EOF# From 896dd323abbf6a9980d8aca2656b6c4bf5352c3b Mon Sep 17 00:00:00 2001 From: Thorsten Leemhuis Date: Tue, 8 Jan 2019 20:40:07 +0100 Subject: [PATCH 0147/4277] docs: Revamp tainted-kernels.rst to make it more comprehensible Add a section about decoding /proc/sys/kernel/tainted, create a more understandable intro and a hopefully explain better the tainted flags in bugs, oops or panics messages. Only thing missing then is a table that quickly describes the various bits and taint flags before going into more detail, so add that as well. That table is partly based on a section from Documentation/sysctl/kernel.txt, but a bit more compact. To avoid confusion I added the shortened version to kernel.txt; the same table is used in three different places now: ./tools/debugging/kernel-chktaint, Documentation/admin-guide/tainted-kernels.rst and Documentation/sysctl/kernel.txt During review of v1 (see above) a number of existing issues with the text were raised, like outdated usages as well as incomplete or missing descriptions. Address most of those as well. Signed-off-by: Thorsten Leemhuis [jc: tightened up changelog] Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/tainted-kernels.rst | 157 +++++++++++++++--- Documentation/sysctl/kernel.txt | 50 +++--- 2 files changed, 153 insertions(+), 54 deletions(-) diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst index 28a869c509a0..71e9184a9079 100644 --- a/Documentation/admin-guide/tainted-kernels.rst +++ b/Documentation/admin-guide/tainted-kernels.rst @@ -1,59 +1,164 @@ Tainted kernels --------------- -Some oops reports contain the string **'Tainted: '** after the program -counter. This indicates that the kernel has been tainted by some -mechanism. The string is followed by a series of position-sensitive -characters, each representing a particular tainted value. +The kernel will mark itself as 'tainted' when something occurs that might be +relevant later when investigating problems. Don't worry too much about this, +most of the time it's not a problem to run a tainted kernel; the information is +mainly of interest once someone wants to investigate some problem, as its real +cause might be the event that got the kernel tainted. That's why bug reports +from tainted kernels will often be ignored by developers, hence try to reproduce +problems with an untainted kernel. - 1) ``G`` if all modules loaded have a GPL or compatible license, ``P`` if +Note the kernel will remain tainted even after you undo what caused the taint +(i.e. unload a proprietary kernel module), to indicate the kernel remains not +trustworthy. That's also why the kernel will print the tainted state when it +notices an internal problem (a 'kernel bug'), a recoverable error +('kernel oops') or a non-recoverable error ('kernel panic') and writes debug +information about this to the logs ``dmesg`` outputs. It's also possible to +check the tainted state at runtime through a file in ``/proc/``. + + +Tainted flag in bugs, oops or panics messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You find the tainted state near the top in a line starting with 'CPU:'; if or +why the kernel was tainted is shown after the Process ID ('PID:') and a shortened +name of the command ('Comm:') that triggered the event:: + + BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 + Oops: 0002 [#1] SMP PTI + CPU: 0 PID: 4424 Comm: insmod Tainted: P W O 4.20.0-0.rc6.fc30 #1 + Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 + RIP: 0010:my_oops_init+0x13/0x1000 [kpanic] + [...] + +You'll find a 'Not tainted: ' there if the kernel was not tainted at the +time of the event; if it was, then it will print 'Tainted: ' and characters +either letters or blanks. In above example it looks like this:: + + Tainted: P W O + +The meaning of those characters is explained in the table below. In tis case +the kernel got tainted earlier because a proprietary Module (``P``) was loaded, +a warning occurred (``W``), and an externally-built module was loaded (``O``). +To decode other letters use the table below. + + +Decoding tainted state at runtime +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At runtime, you can query the tainted state by reading +``cat /proc/sys/kernel/tainted``. If that returns ``0``, the kernel is not +tainted; any other number indicates the reasons why it is. The easiest way to +decode that number is the script ``tools/debugging/kernel-chktaint``, which your +distribution might ship as part of a package called ``linux-tools`` or +``kernel-tools``; if it doesn't you can download the script from +`git.kernel.org `_ +and execute it with ``sh kernel-chktaint``, which would print something like +this on the machine that had the statements in the logs that were quoted earlier:: + + Kernel is Tainted for following reasons: + * Proprietary module was loaded (#0) + * Kernel issued warning (#9) + * Externally-built ('out-of-tree') module was loaded (#12) + See Documentation/admin-guide/tainted-kernels.rst in the the Linux kernel or + https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html for + a more details explanation of the various taint flags. + Raw taint value as int/string: 4609/'P W O ' + +You can try to decode the number yourself. That's easy if there was only one +reason that got your kernel tainted, as in this case you can find the number +with the table below. If there were multiple reasons you need to decode the +number, as it is a bitfield, where each bit indicates the absence or presence of +a particular type of taint. It's best to leave that to the aforementioned +script, but if you need something quick you can use this shell command to check +which bits are set:: + + $ for i in $(seq 18); do echo $(($i-1)) $(($(cat /proc/sys/kernel/tainted)>>($i-1)&1));done + +Table for decoding tainted state +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +=== === ====== ======================================================== +Bit Log Number Reason that got the kernel tainted +=== === ====== ======================================================== + 0 G/P 1 proprietary module was loaded + 1 _/F 2 module was force loaded + 2 _/S 4 SMP kernel oops on an officially SMP incapable processor + 3 _/R 8 module was force unloaded + 4 _/M 16 processor reported a Machine Check Exception (MCE) + 5 _/B 32 bad page referenced or some unexpected page flags + 6 _/U 64 taint requested by userspace application + 7 _/D 128 kernel died recently, i.e. there was an OOPS or BUG + 8 _/A 256 ACPI table overridden by user + 9 _/W 512 kernel issued warning + 10 _/C 1024 staging driver was loaded + 11 _/I 2048 workaround for bug in platform firmware applied + 12 _/O 4096 externally-built ("out-of-tree") module was loaded + 13 _/E 8192 unsigned module was loaded + 14 _/L 16384 soft lockup occurred + 15 _/K 32768 kernel has been live patched + 16 _/X 65536 auxiliary taint, defined for and used by distros + 17 _/T 131072 kernel was built with the struct randomization plugin +=== === ====== ======================================================== + +Note: The character ``_`` is representing a blank in this table to make reading +easier. + +More detailed explanation for tainting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 0) ``G`` if all modules loaded have a GPL or compatible license, ``P`` if any proprietary module has been loaded. Modules without a MODULE_LICENSE or with a MODULE_LICENSE that is not recognised by insmod as GPL compatible are assumed to be proprietary. - 2) ``F`` if any module was force loaded by ``insmod -f``, ``' '`` if all + 1) ``F`` if any module was force loaded by ``insmod -f``, ``' '`` if all modules were loaded normally. - 3) ``S`` if the oops occurred on an SMP kernel running on hardware that + 2) ``S`` if the oops occurred on an SMP kernel running on hardware that hasn't been certified as safe to run multiprocessor. Currently this occurs only on various Athlons that are not SMP capable. - 4) ``R`` if a module was force unloaded by ``rmmod -f``, ``' '`` if all + 3) ``R`` if a module was force unloaded by ``rmmod -f``, ``' '`` if all modules were unloaded normally. - 5) ``M`` if any processor has reported a Machine Check Exception, + 4) ``M`` if any processor has reported a Machine Check Exception, ``' '`` if no Machine Check Exceptions have occurred. - 6) ``B`` if a page-release function has found a bad page reference or - some unexpected page flags. + 5) ``B`` If a page-release function has found a bad page reference or some + unexpected page flags. This indicates a hardware problem or a kernel bug; + there should be other information in the log indicating why this tainting + occured. - 7) ``U`` if a user or user application specifically requested that the + 6) ``U`` if a user or user application specifically requested that the Tainted flag be set, ``' '`` otherwise. - 8) ``D`` if the kernel has died recently, i.e. there was an OOPS or BUG. + 7) ``D`` if the kernel has died recently, i.e. there was an OOPS or BUG. - 9) ``A`` if the ACPI table has been overridden. + 8) ``A`` if an ACPI table has been overridden. - 10) ``W`` if a warning has previously been issued by the kernel. + 9) ``W`` if a warning has previously been issued by the kernel. (Though some warnings may set more specific taint flags.) - 11) ``C`` if a staging driver has been loaded. + 10) ``C`` if a staging driver has been loaded. - 12) ``I`` if the kernel is working around a severe bug in the platform + 11) ``I`` if the kernel is working around a severe bug in the platform firmware (BIOS or similar). - 13) ``O`` if an externally-built ("out-of-tree") module has been loaded. + 12) ``O`` if an externally-built ("out-of-tree") module has been loaded. - 14) ``E`` if an unsigned module has been loaded in a kernel supporting + 13) ``E`` if an unsigned module has been loaded in a kernel supporting module signature. - 15) ``L`` if a soft lockup has previously occurred on the system. + 14) ``L`` if a soft lockup has previously occurred on the system. - 16) ``K`` if the kernel has been live patched. + 15) ``K`` if the kernel has been live patched. -The primary reason for the **'Tainted: '** string is to tell kernel -debuggers if this is a clean kernel or if anything unusual has -occurred. Tainting is permanent: even if an offending module is -unloaded, the tainted value remains to indicate that the kernel is not -trustworthy. + 16) ``X`` Auxiliary taint, defined for and used by Linux distributors. + + 17) ``T`` Kernel was build with the randstruct plugin, which can intentionally + produce extremely unusual kernel structure layouts (even performance + pathological ones), which is important to know when debugging. Set at + build time. diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 1b8775298cf7..b279b824b887 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -93,7 +93,7 @@ show up in /proc/sys/kernel: - stop-a [ SPARC only ] - sysrq ==> Documentation/admin-guide/sysrq.rst - sysctl_writes_strict -- tainted +- tainted ==> Documentation/admin-guide/tainted-kernels.rst - threads-max - unknown_nmi_panic - watchdog @@ -1002,39 +1002,33 @@ compilation sees a 1% slowdown, other systems and workloads may vary. 1: kernel stack erasing is enabled (default), it is performed before returning to the userspace at the end of syscalls. - ============================================================== -tainted: +tainted Non-zero if the kernel has been tainted. Numeric values, which can be ORed together. The letters are seen in "Tainted" line of Oops reports. - 1 (P): A module with a non-GPL license has been loaded, this - includes modules with no license. - Set by modutils >= 2.4.9 and module-init-tools. - 2 (F): A module was force loaded by insmod -f. - Set by modutils >= 2.4.9 and module-init-tools. - 4 (S): Unsafe SMP processors: SMP with CPUs not designed for SMP. - 8 (R): A module was forcibly unloaded from the system by rmmod -f. - 16 (M): A hardware machine check error occurred on the system. - 32 (B): A bad page was discovered on the system. - 64 (U): The user has asked that the system be marked "tainted". This - could be because they are running software that directly modifies - the hardware, or for other reasons. - 128 (D): The system has died. - 256 (A): The ACPI DSDT has been overridden with one supplied by the user - instead of using the one provided by the hardware. - 512 (W): A kernel warning has occurred. - 1024 (C): A module from drivers/staging was loaded. - 2048 (I): The system is working around a severe firmware bug. - 4096 (O): An out-of-tree module has been loaded. - 8192 (E): An unsigned module has been loaded in a kernel supporting module - signature. - 16384 (L): A soft lockup has previously occurred on the system. - 32768 (K): The kernel has been live patched. - 65536 (X): Auxiliary taint, defined and used by for distros. -131072 (T): The kernel was built with the struct randomization plugin. + 1 (P): proprietary module was loaded + 2 (F): module was force loaded + 4 (S): SMP kernel oops on an officially SMP incapable processor + 8 (R): module was force unloaded + 16 (M): processor reported a Machine Check Exception (MCE) + 32 (B): bad page referenced or some unexpected page flags + 64 (U): taint requested by userspace application + 128 (D): kernel died recently, i.e. there was an OOPS or BUG + 256 (A): an ACPI table was overridden by user + 512 (W): kernel issued warning + 1024 (C): staging driver was loaded + 2048 (I): workaround for bug in platform firmware applied + 4096 (O): externally-built ("out-of-tree") module was loaded + 8192 (E): unsigned module was loaded + 16384 (L): soft lockup occurred + 32768 (K): kernel has been live patched + 65536 (X): Auxiliary taint, defined and used by for distros +131072 (T): The kernel was built with the struct randomization plugin + +See Documentation/admin-guide/tainted-kernels.rst for more information. ============================================================== From 96f87ee1811306d0c8cf94b8c37b0e4f725b01d1 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 8 Jan 2019 16:07:23 +0200 Subject: [PATCH 0148/4277] RDMA: Clean structures from CONFIG_INFINIBAND_ON_DEMAND_PAGING CONFIG_INFINIBAND_ON_DEMAND_PAGING is used in general structures to micro-optimize the memory footprint. Remove it, so it will allow us to simplify various ODP device flows. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 2 -- include/rdma/ib_verbs.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index b06d3b1efea8..93c288a4de2b 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -911,7 +911,6 @@ struct mlx5_ib_dev { /* Prevents soft lock on massive reg MRs */ struct mutex slow_path_mutex; int fill_delay; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING struct ib_odp_caps odp_caps; u64 odp_max_size; struct mlx5_ib_pf_eq odp_pf_eq; @@ -923,7 +922,6 @@ struct mlx5_ib_dev { struct srcu_struct mr_srcu; u32 null_mkey; struct workqueue_struct *advise_mr_wq; -#endif struct mlx5_ib_flow_db *flow_db; /* protect resources needed as part of reset flow */ spinlock_t reset_flow_resource_lock; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index a3ceed3a040a..3ddd199ba602 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1504,12 +1504,10 @@ struct ib_ucontext { bool cleanup_retryable; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING void (*invalidate_range)(struct ib_umem_odp *umem_odp, unsigned long start, unsigned long end); struct mutex per_mm_list_lock; struct list_head per_mm_list; -#endif struct ib_rdmacg_object cg_obj; /* From e502b8b011294c293064ef1baeb04235ccc05439 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 8 Jan 2019 16:07:24 +0200 Subject: [PATCH 0149/4277] RDMA/core: Don't depend device ODP capabilities on kconfig option Device capability bits are exposing what specific device supports from HW perspective. Those bits are not dependent on kernel configurations and RDMA/core should ensure that proper interfaces to users will be disabled if CONFIG_INFINIBAND_ON_DEMAND_PAGING is not set. Fixes: f4056bfd8ccf ("IB/core: Add on demand paging caps to ib_uverbs_ex_query_device") Fixes: 8cdd312cfed7 ("IB/mlx5: Implement the ODP capability query verb") Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_cmd.c | 2 -- drivers/infiniband/hw/mlx5/main.c | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 6b12cc5f97b2..549d9eedf62e 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -3609,7 +3609,6 @@ static int ib_uverbs_ex_query_device(struct uverbs_attr_bundle *attrs) copy_query_dev_fields(ucontext, &resp.base, &attr); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING resp.odp_caps.general_caps = attr.odp_caps.general_caps; resp.odp_caps.per_transport_caps.rc_odp_caps = attr.odp_caps.per_transport_caps.rc_odp_caps; @@ -3617,7 +3616,6 @@ static int ib_uverbs_ex_query_device(struct uverbs_attr_bundle *attrs) attr.odp_caps.per_transport_caps.uc_odp_caps; resp.odp_caps.per_transport_caps.ud_odp_caps = attr.odp_caps.per_transport_caps.ud_odp_caps; -#endif resp.timestamp_mask = attr.timestamp_mask; resp.hca_core_clock = attr.hca_core_clock; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 94fe253d4956..06ee1f0cb22d 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -923,11 +923,11 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, props->hca_core_clock = MLX5_CAP_GEN(mdev, device_frequency_khz); props->timestamp_mask = 0x7FFFFFFFFFFFFFFFULL; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (MLX5_CAP_GEN(mdev, pg)) - props->device_cap_flags |= IB_DEVICE_ON_DEMAND_PAGING; - props->odp_caps = dev->odp_caps; -#endif + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { + if (MLX5_CAP_GEN(mdev, pg)) + props->device_cap_flags |= IB_DEVICE_ON_DEMAND_PAGING; + props->odp_caps = dev->odp_caps; + } if (MLX5_CAP_GEN(mdev, cd)) props->device_cap_flags |= IB_DEVICE_CROSS_CHANNEL; From 8b4d5bc5cf3f813dc4df5d69c2fcde16c40d8abd Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 8 Jan 2019 16:07:25 +0200 Subject: [PATCH 0150/4277] RDMA/mlx5: Introduce and reuse helper to identify ODP MR Consolidate various checks if MR is ODP backed to one simple helper and update call sites to use it. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 10 ++++++++++ drivers/infiniband/hw/mlx5/mr.c | 13 +++---------- drivers/infiniband/hw/mlx5/odp.c | 6 +++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 93c288a4de2b..81efa5def8ad 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -589,6 +590,12 @@ struct mlx5_ib_mr { wait_queue_head_t q_leaf_free; }; +static inline bool is_odp_mr(struct mlx5_ib_mr *mr) +{ + return IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) && mr->umem && + mr->umem->is_odp; +} + struct mlx5_ib_mw { struct ib_mw ibmw; struct mlx5_core_mkey mmkey; @@ -1213,6 +1220,9 @@ mlx5_ib_advise_mr_prefetch(struct ib_pd *pd, { return -EOPNOTSUPP; } +static inline void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, + unsigned long start, + unsigned long end){}; #endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */ /* Needed for rep profile */ diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index fd6ea1f75085..c389750f771e 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -95,10 +95,9 @@ static bool use_umr_mtt_update(struct mlx5_ib_mr *mr, u64 start, u64 length) length + (start & (MLX5_ADAPTER_PAGE_SIZE - 1)); } -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING static void update_odp_mr(struct mlx5_ib_mr *mr) { - if (mr->umem->is_odp) { + if (is_odp_mr(mr)) { /* * This barrier prevents the compiler from moving the * setting of umem->odp_data->private to point to our @@ -121,7 +120,6 @@ static void update_odp_mr(struct mlx5_ib_mr *mr) smp_wmb(); } } -#endif static void reg_mr_callback(int status, void *context) { @@ -1386,9 +1384,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mr->umem = umem; set_mr_fields(dev, mr, npages, length, access_flags); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING update_odp_mr(mr); -#endif if (!populate_mtts) { int update_xlt_flags = MLX5_IB_UPD_XLT_ENABLE; @@ -1553,9 +1549,7 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, set_mr_fields(dev, mr, npages, len, access_flags); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING update_odp_mr(mr); -#endif return 0; err: @@ -1641,8 +1635,7 @@ static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) int npages = mr->npages; struct ib_umem *umem = mr->umem; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (umem && umem->is_odp) { + if (is_odp_mr(mr)) { struct ib_umem_odp *umem_odp = to_ib_umem_odp(umem); /* Prevent new page faults from succeeding */ @@ -1666,7 +1659,7 @@ static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) /* Avoid double-freeing the umem. */ umem = NULL; } -#endif + clean_mr(dev, mr); /* diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 01e0f6200631..37d6653f9636 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -103,7 +103,7 @@ static int check_parent(struct ib_umem_odp *odp, struct ib_ucontext_per_mm *mr_to_per_mm(struct mlx5_ib_mr *mr) { - if (WARN_ON(!mr || !mr->umem || !mr->umem->is_odp)) + if (WARN_ON(!mr || !is_odp_mr(mr))) return NULL; return to_ib_umem_odp(mr->umem)->per_mm; @@ -739,12 +739,12 @@ next_mr: goto srcu_unlock; } - if (prefetch && !mr->umem->is_odp) { + if (prefetch && !is_odp_mr(mr)) { ret = -EINVAL; goto srcu_unlock; } - if (!mr->umem->is_odp) { + if (!is_odp_mr(mr)) { mlx5_ib_dbg(dev, "skipping non ODP MR (lkey=0x%06x) in page fault handler.\n", key); if (bytes_mapped) From 13859d5df418ea535926e2b57c29d5161c522b9d Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 8 Jan 2019 16:07:26 +0200 Subject: [PATCH 0151/4277] RDMA/mlx5: Embed into the code flow the ODP config option Convert various places to more readable code, which embeds CONFIG_INFINIBAND_ON_DEMAND_PAGING into the code flow. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_cmd.c | 3 -- drivers/infiniband/hw/mlx5/main.c | 41 ++++++++++++++-------------- drivers/infiniband/hw/mlx5/mem.c | 5 +--- drivers/infiniband/hw/mlx5/mr.c | 28 ++++++++----------- include/rdma/ib_umem_odp.h | 26 +++++++++--------- 5 files changed, 46 insertions(+), 57 deletions(-) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 549d9eedf62e..d4f1a2ef5015 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -234,14 +234,11 @@ static int ib_uverbs_get_context(struct uverbs_attr_bundle *attrs) ucontext->closing = false; ucontext->cleanup_retryable = false; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING mutex_init(&ucontext->per_mm_list_lock); INIT_LIST_HEAD(&ucontext->per_mm_list); if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING)) ucontext->invalidate_range = NULL; -#endif - resp.num_comp_vectors = file->device->num_comp_vectors; ret = get_unused_fd_flags(O_CLOEXEC); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 06ee1f0cb22d..11e9783cefcc 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1763,9 +1763,9 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev, if (err) goto out_sys_pages; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - context->ibucontext.invalidate_range = &mlx5_ib_invalidate_range; -#endif + if (ibdev->attrs.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING) + context->ibucontext.invalidate_range = + &mlx5_ib_invalidate_range; if (req.flags & MLX5_IB_ALLOC_UCTX_DEVX) { err = mlx5_ib_devx_create(dev, true); @@ -1897,12 +1897,10 @@ static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext) struct mlx5_ib_dev *dev = to_mdev(ibcontext->device); struct mlx5_bfreg_info *bfregi; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING /* All umem's must be destroyed before destroying the ucontext. */ mutex_lock(&ibcontext->per_mm_list_lock); WARN_ON(!list_empty(&ibcontext->per_mm_list)); mutex_unlock(&ibcontext->per_mm_list_lock); -#endif bfregi = &context->bfregi; mlx5_ib_dealloc_transport_domain(dev, context->tdn, context->devx_uid); @@ -5722,11 +5720,11 @@ static struct ib_counters *mlx5_ib_create_counters(struct ib_device *device, void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev) { mlx5_ib_cleanup_multiport_master(dev); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - cleanup_srcu_struct(&dev->mr_srcu); - drain_workqueue(dev->advise_mr_wq); - destroy_workqueue(dev->advise_mr_wq); -#endif + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { + cleanup_srcu_struct(&dev->mr_srcu); + drain_workqueue(dev->advise_mr_wq); + destroy_workqueue(dev->advise_mr_wq); + } kfree(dev->port); } @@ -5779,19 +5777,20 @@ int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) spin_lock_init(&dev->memic.memic_lock); dev->memic.dev = mdev; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - dev->advise_mr_wq = alloc_ordered_workqueue("mlx5_ib_advise_mr_wq", 0); - if (!dev->advise_mr_wq) { - err = -ENOMEM; - goto err_mp; - } + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { + dev->advise_mr_wq = + alloc_ordered_workqueue("mlx5_ib_advise_mr_wq", 0); + if (!dev->advise_mr_wq) { + err = -ENOMEM; + goto err_mp; + } - err = init_srcu_struct(&dev->mr_srcu); - if (err) { - destroy_workqueue(dev->advise_mr_wq); - goto err_mp; + err = init_srcu_struct(&dev->mr_srcu); + if (err) { + destroy_workqueue(dev->advise_mr_wq); + goto err_mp; + } } -#endif return 0; err_mp: diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c index 549234988bb4..9f90be296ee0 100644 --- a/drivers/infiniband/hw/mlx5/mem.c +++ b/drivers/infiniband/hw/mlx5/mem.c @@ -111,7 +111,6 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, *count = i; } -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING static u64 umem_dma_to_mtt(dma_addr_t umem_dma) { u64 mtt_entry = umem_dma & ODP_DMA_ADDR_MASK; @@ -123,7 +122,6 @@ static u64 umem_dma_to_mtt(dma_addr_t umem_dma) return mtt_entry; } -#endif /* * Populate the given array with bus addresses from the umem. @@ -151,7 +149,7 @@ void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem, int len; struct scatterlist *sg; int entry; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING + if (umem->is_odp) { WARN_ON(shift != 0); WARN_ON(access_flags != (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE)); @@ -164,7 +162,6 @@ void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem, } return; } -#endif i = 0; for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index c389750f771e..494a90f4348c 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -71,10 +71,9 @@ static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) { int err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmkey); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - /* Wait until all page fault handlers using the mr complete. */ - synchronize_srcu(&dev->mr_srcu); -#endif + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) + /* Wait until all page fault handlers using the mr complete. */ + synchronize_srcu(&dev->mr_srcu); return err; } @@ -254,9 +253,8 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num) mlx5_core_destroy_mkey(dev->mdev, &mr->mmkey); } -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - synchronize_srcu(&dev->mr_srcu); -#endif + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) + synchronize_srcu(&dev->mr_srcu); list_for_each_entry_safe(mr, tmp_mr, &del_list, list) { list_del(&mr->list); @@ -1329,8 +1327,8 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mlx5_ib_dbg(dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx, access_flags 0x%x\n", start, virt_addr, length, access_flags); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (!start && length == U64_MAX) { + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) && !start && + length == U64_MAX) { if (!(access_flags & IB_ACCESS_ON_DEMAND) || !(dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) return ERR_PTR(-EINVAL); @@ -1340,7 +1338,6 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, return ERR_CAST(mr); return &mr->ibmr; } -#endif err = mr_umem_get(pd, start, length, access_flags, &umem, &npages, &page_shift, &ncont, &order); @@ -1401,9 +1398,9 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, } } -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - mr->live = 1; -#endif + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) + mr->live = 1; + return &mr->ibmr; error: ib_umem_release(umem); @@ -1518,9 +1515,8 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, } mr->allocated_from_cache = 0; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - mr->live = 1; -#endif + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) + mr->live = 1; } else { /* * Send a UMR WQE diff --git a/include/rdma/ib_umem_odp.h b/include/rdma/ib_umem_odp.h index 0b1446fe2fab..d3725cf13ecd 100644 --- a/include/rdma/ib_umem_odp.h +++ b/include/rdma/ib_umem_odp.h @@ -83,6 +83,19 @@ static inline struct ib_umem_odp *to_ib_umem_odp(struct ib_umem *umem) return container_of(umem, struct ib_umem_odp, umem); } +/* + * The lower 2 bits of the DMA address signal the R/W permissions for + * the entry. To upgrade the permissions, provide the appropriate + * bitmask to the map_dma_pages function. + * + * Be aware that upgrading a mapped address might result in change of + * the DMA address for the page. + */ +#define ODP_READ_ALLOWED_BIT (1<<0ULL) +#define ODP_WRITE_ALLOWED_BIT (1<<1ULL) + +#define ODP_DMA_ADDR_MASK (~(ODP_READ_ALLOWED_BIT | ODP_WRITE_ALLOWED_BIT)) + #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING struct ib_ucontext_per_mm { @@ -107,19 +120,6 @@ struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm, unsigned long addr, size_t size); void ib_umem_odp_release(struct ib_umem_odp *umem_odp); -/* - * The lower 2 bits of the DMA address signal the R/W permissions for - * the entry. To upgrade the permissions, provide the appropriate - * bitmask to the map_dma_pages function. - * - * Be aware that upgrading a mapped address might result in change of - * the DMA address for the page. - */ -#define ODP_READ_ALLOWED_BIT (1<<0ULL) -#define ODP_WRITE_ALLOWED_BIT (1<<1ULL) - -#define ODP_DMA_ADDR_MASK (~(ODP_READ_ALLOWED_BIT | ODP_WRITE_ALLOWED_BIT)) - int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, u64 start_offset, u64 bcnt, u64 access_mask, unsigned long current_seq); From 0ada768517dafa1504ef5986ba04f118b7436960 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 8 Jan 2019 16:07:27 +0200 Subject: [PATCH 0152/4277] RDMA/mlx5: Delete declaration of already removed function The implementation of mlx5_core_page_fault_resume() was removed in commit d5d284b829a6 ("{net,IB}/mlx5: Move Page fault EQ and ODP logic to RDMA"). This patch removes declaration too. Fixes: d5d284b829a6 ("{net,IB}/mlx5: Move Page fault EQ and ODP logic to RDMA") Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- include/linux/mlx5/driver.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 54299251d40d..b6f5839f129a 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -939,10 +939,6 @@ int mlx5_query_odp_caps(struct mlx5_core_dev *dev, struct mlx5_odp_caps *odp_caps); int mlx5_core_query_ib_ppcnt(struct mlx5_core_dev *dev, u8 port_num, void *out, size_t sz); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING -int mlx5_core_page_fault_resume(struct mlx5_core_dev *dev, u32 token, - u32 wq_num, u8 type, int error); -#endif int mlx5_init_rl_table(struct mlx5_core_dev *dev); void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev); From 9f475ebff8e437ccccb3cf0259b5de7540f413be Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:18 +0100 Subject: [PATCH 0153/4277] scsi: gdth: refactor ioc_general This function is a huge mess with duplicated error handling. Split out a few useful helpers and use goto labels to untangle the error handling and no-data ioctl handling. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 248 +++++++++++++++++++++++--------------------- 1 file changed, 131 insertions(+), 117 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 194c294f9b6c..dd6784e03a57 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -4155,131 +4155,145 @@ static int ioc_resetdrv(void __user *arg, char *cmnd) return 0; } +static void gdth_ioc_cacheservice(gdth_ha_str *ha, gdth_ioctl_general *gen, + u64 paddr) +{ + if (ha->cache_feat & GDT_64BIT) { + /* copy elements from 32-bit IOCTL structure */ + gen->command.u.cache64.BlockCnt = gen->command.u.cache.BlockCnt; + gen->command.u.cache64.BlockNo = gen->command.u.cache.BlockNo; + gen->command.u.cache64.DeviceNo = gen->command.u.cache.DeviceNo; + + if (ha->cache_feat & SCATTER_GATHER) { + gen->command.u.cache64.DestAddr = (u64)-1; + gen->command.u.cache64.sg_canz = 1; + gen->command.u.cache64.sg_lst[0].sg_ptr = paddr; + gen->command.u.cache64.sg_lst[0].sg_len = gen->data_len; + gen->command.u.cache64.sg_lst[1].sg_len = 0; + } else { + gen->command.u.cache64.DestAddr = paddr; + gen->command.u.cache64.sg_canz = 0; + } + } else { + if (ha->cache_feat & SCATTER_GATHER) { + gen->command.u.cache.DestAddr = 0xffffffff; + gen->command.u.cache.sg_canz = 1; + gen->command.u.cache.sg_lst[0].sg_ptr = (u32)paddr; + gen->command.u.cache.sg_lst[0].sg_len = gen->data_len; + gen->command.u.cache.sg_lst[1].sg_len = 0; + } else { + gen->command.u.cache.DestAddr = paddr; + gen->command.u.cache.sg_canz = 0; + } + } +} + +static void gdth_ioc_scsiraw(gdth_ha_str *ha, gdth_ioctl_general *gen, + u64 paddr) +{ + if (ha->raw_feat & GDT_64BIT) { + /* copy elements from 32-bit IOCTL structure */ + char cmd[16]; + + gen->command.u.raw64.sense_len = gen->command.u.raw.sense_len; + gen->command.u.raw64.bus = gen->command.u.raw.bus; + gen->command.u.raw64.lun = gen->command.u.raw.lun; + gen->command.u.raw64.target = gen->command.u.raw.target; + memcpy(cmd, gen->command.u.raw.cmd, 16); + memcpy(gen->command.u.raw64.cmd, cmd, 16); + gen->command.u.raw64.clen = gen->command.u.raw.clen; + gen->command.u.raw64.sdlen = gen->command.u.raw.sdlen; + gen->command.u.raw64.direction = gen->command.u.raw.direction; + + /* addresses */ + if (ha->raw_feat & SCATTER_GATHER) { + gen->command.u.raw64.sdata = (u64)-1; + gen->command.u.raw64.sg_ranz = 1; + gen->command.u.raw64.sg_lst[0].sg_ptr = paddr; + gen->command.u.raw64.sg_lst[0].sg_len = gen->data_len; + gen->command.u.raw64.sg_lst[1].sg_len = 0; + } else { + gen->command.u.raw64.sdata = paddr; + gen->command.u.raw64.sg_ranz = 0; + } + + gen->command.u.raw64.sense_data = paddr + gen->data_len; + } else { + if (ha->raw_feat & SCATTER_GATHER) { + gen->command.u.raw.sdata = 0xffffffff; + gen->command.u.raw.sg_ranz = 1; + gen->command.u.raw.sg_lst[0].sg_ptr = (u32)paddr; + gen->command.u.raw.sg_lst[0].sg_len = gen->data_len; + gen->command.u.raw.sg_lst[1].sg_len = 0; + } else { + gen->command.u.raw.sdata = paddr; + gen->command.u.raw.sg_ranz = 0; + } + + gen->command.u.raw.sense_data = (u32)paddr + gen->data_len; + } +} + static int ioc_general(void __user *arg, char *cmnd) { - gdth_ioctl_general gen; - char *buf = NULL; - u64 paddr; - gdth_ha_str *ha; - int rval; + gdth_ioctl_general gen; + gdth_ha_str *ha; + char *buf = NULL; + u64 paddr; + int rval; - if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general))) - return -EFAULT; - ha = gdth_find_ha(gen.ionode); - if (!ha) - return -EFAULT; + if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general))) + return -EFAULT; + ha = gdth_find_ha(gen.ionode); + if (!ha) + return -EFAULT; - if (gen.data_len > INT_MAX) - return -EINVAL; - if (gen.sense_len > INT_MAX) - return -EINVAL; - if (gen.data_len + gen.sense_len > INT_MAX) - return -EINVAL; + if (gen.data_len > INT_MAX) + return -EINVAL; + if (gen.sense_len > INT_MAX) + return -EINVAL; + if (gen.data_len + gen.sense_len > INT_MAX) + return -EINVAL; - if (gen.data_len + gen.sense_len != 0) { - if (!(buf = gdth_ioctl_alloc(ha, gen.data_len + gen.sense_len, - FALSE, &paddr))) - return -EFAULT; - if (copy_from_user(buf, arg + sizeof(gdth_ioctl_general), - gen.data_len + gen.sense_len)) { - gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); - return -EFAULT; - } + if (gen.data_len + gen.sense_len > 0) { + buf = gdth_ioctl_alloc(ha, gen.data_len + gen.sense_len, FALSE, + &paddr); + if (!buf) + return -EFAULT; - if (gen.command.OpCode == GDT_IOCTL) { - gen.command.u.ioctl.p_param = paddr; - } else if (gen.command.Service == CACHESERVICE) { - if (ha->cache_feat & GDT_64BIT) { - /* copy elements from 32-bit IOCTL structure */ - gen.command.u.cache64.BlockCnt = gen.command.u.cache.BlockCnt; - gen.command.u.cache64.BlockNo = gen.command.u.cache.BlockNo; - gen.command.u.cache64.DeviceNo = gen.command.u.cache.DeviceNo; - /* addresses */ - if (ha->cache_feat & SCATTER_GATHER) { - gen.command.u.cache64.DestAddr = (u64)-1; - gen.command.u.cache64.sg_canz = 1; - gen.command.u.cache64.sg_lst[0].sg_ptr = paddr; - gen.command.u.cache64.sg_lst[0].sg_len = gen.data_len; - gen.command.u.cache64.sg_lst[1].sg_len = 0; - } else { - gen.command.u.cache64.DestAddr = paddr; - gen.command.u.cache64.sg_canz = 0; - } - } else { - if (ha->cache_feat & SCATTER_GATHER) { - gen.command.u.cache.DestAddr = 0xffffffff; - gen.command.u.cache.sg_canz = 1; - gen.command.u.cache.sg_lst[0].sg_ptr = (u32)paddr; - gen.command.u.cache.sg_lst[0].sg_len = gen.data_len; - gen.command.u.cache.sg_lst[1].sg_len = 0; - } else { - gen.command.u.cache.DestAddr = paddr; - gen.command.u.cache.sg_canz = 0; - } - } - } else if (gen.command.Service == SCSIRAWSERVICE) { - if (ha->raw_feat & GDT_64BIT) { - /* copy elements from 32-bit IOCTL structure */ - char cmd[16]; - gen.command.u.raw64.sense_len = gen.command.u.raw.sense_len; - gen.command.u.raw64.bus = gen.command.u.raw.bus; - gen.command.u.raw64.lun = gen.command.u.raw.lun; - gen.command.u.raw64.target = gen.command.u.raw.target; - memcpy(cmd, gen.command.u.raw.cmd, 16); - memcpy(gen.command.u.raw64.cmd, cmd, 16); - gen.command.u.raw64.clen = gen.command.u.raw.clen; - gen.command.u.raw64.sdlen = gen.command.u.raw.sdlen; - gen.command.u.raw64.direction = gen.command.u.raw.direction; - /* addresses */ - if (ha->raw_feat & SCATTER_GATHER) { - gen.command.u.raw64.sdata = (u64)-1; - gen.command.u.raw64.sg_ranz = 1; - gen.command.u.raw64.sg_lst[0].sg_ptr = paddr; - gen.command.u.raw64.sg_lst[0].sg_len = gen.data_len; - gen.command.u.raw64.sg_lst[1].sg_len = 0; - } else { - gen.command.u.raw64.sdata = paddr; - gen.command.u.raw64.sg_ranz = 0; - } - gen.command.u.raw64.sense_data = paddr + gen.data_len; - } else { - if (ha->raw_feat & SCATTER_GATHER) { - gen.command.u.raw.sdata = 0xffffffff; - gen.command.u.raw.sg_ranz = 1; - gen.command.u.raw.sg_lst[0].sg_ptr = (u32)paddr; - gen.command.u.raw.sg_lst[0].sg_len = gen.data_len; - gen.command.u.raw.sg_lst[1].sg_len = 0; - } else { - gen.command.u.raw.sdata = paddr; - gen.command.u.raw.sg_ranz = 0; - } - gen.command.u.raw.sense_data = (u32)paddr + gen.data_len; - } - } else { - gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); - return -EFAULT; - } - } + rval = -EFAULT; + if (copy_from_user(buf, arg + sizeof(gdth_ioctl_general), + gen.data_len + gen.sense_len)) + goto out_free_buf; - rval = __gdth_execute(ha->sdev, &gen.command, cmnd, gen.timeout, &gen.info); - if (rval < 0) { + if (gen.command.OpCode == GDT_IOCTL) + gen.command.u.ioctl.p_param = paddr; + else if (gen.command.Service == CACHESERVICE) + gdth_ioc_cacheservice(ha, &gen, paddr); + else if (gen.command.Service == SCSIRAWSERVICE) + gdth_ioc_scsiraw(ha, &gen, paddr); + else + goto out_free_buf; + } + + rval = __gdth_execute(ha->sdev, &gen.command, cmnd, gen.timeout, + &gen.info); + if (rval < 0) + goto out_free_buf; + gen.status = rval; + + rval = -EFAULT; + if (copy_to_user(arg + sizeof(gdth_ioctl_general), buf, + gen.data_len + gen.sense_len)) + goto out_free_buf; + if (copy_to_user(arg, &gen, + sizeof(gdth_ioctl_general) - sizeof(gdth_cmd_str))) + goto out_free_buf; + + rval = 0; +out_free_buf: gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); - return rval; - } - gen.status = rval; - - if (copy_to_user(arg + sizeof(gdth_ioctl_general), buf, - gen.data_len + gen.sense_len)) { - gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); - return -EFAULT; - } - if (copy_to_user(arg, &gen, - sizeof(gdth_ioctl_general) - sizeof(gdth_cmd_str))) { - gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); - return -EFAULT; - } - gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); - return 0; + return rval; } static int ioc_hdrlist(void __user *arg, char *cmnd) From 8d22022c3a56314b182810794cae47686b765313 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:19 +0100 Subject: [PATCH 0154/4277] scsi: gdth: reuse dma coherent allocation in gdth_show_info gdth_show_info currently allocs and frees a dma buffer four times which isn't very efficient. Reuse a single allocation instead. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth_proc.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index 3a9751a80225..bd5532a80b0e 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -226,11 +226,13 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) #endif if (ha->more_proc) { + size_t size = max_t(size_t, GDTH_SCRATCH, sizeof(gdth_hget_str)); + /* more information: 2. about physical devices */ seq_puts(m, "\nPhysical Devices:"); flag = FALSE; - buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr); + buf = gdth_ioctl_alloc(ha, size, FALSE, &paddr); if (!buf) goto stop_output; for (i = 0; i < ha->bus_cnt; ++i) { @@ -323,7 +325,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) } } } - gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr); if (!flag) seq_puts(m, "\n --\n"); @@ -332,9 +333,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_puts(m, "\nLogical Drives:"); flag = FALSE; - buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr); - if (!buf) - goto stop_output; for (i = 0; i < MAX_LDRIVES; ++i) { if (!ha->hdr[i].is_logdrv) continue; @@ -408,7 +406,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_printf(m, " To Array Drv.:\t%s\n", hrec); } - gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr); if (!flag) seq_puts(m, "\n --\n"); @@ -417,9 +414,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_puts(m, "\nArray Drives:"); flag = FALSE; - buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr); - if (!buf) - goto stop_output; for (i = 0; i < MAX_LDRIVES; ++i) { if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master)) continue; @@ -468,8 +462,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) hrec); } } - gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr); - + if (!flag) seq_puts(m, "\n --\n"); @@ -477,9 +470,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_puts(m, "\nHost Drives:"); flag = FALSE; - buf = gdth_ioctl_alloc(ha, sizeof(gdth_hget_str), FALSE, &paddr); - if (!buf) - goto stop_output; for (i = 0; i < MAX_LDRIVES; ++i) { if (!ha->hdr[i].is_logdrv || (ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master)) @@ -510,7 +500,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) } } } - gdth_ioctl_free(ha, sizeof(gdth_hget_str), buf, paddr); + gdth_ioctl_free(ha, size, buf, paddr); for (i = 0; i < MAX_HDRIVES; ++i) { if (!(ha->hdr[i].present)) From 463563fa745ac7ac564458c1903325b937ee8c5f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:20 +0100 Subject: [PATCH 0155/4277] scsi: gdth: remove gdth_{alloc,free}_ioctl Out of the three callers once insists on the scratch buffer, and the others are fine with a new allocation. Switch those two to just use pci_alloc_consistent directly, and open code the scratch buffer allocation in the remaining one. This avoids a case where we might be doing a memory allocation under a spinlock with irqs disabled. [mkp: typo] Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 8 ++--- drivers/scsi/gdth_proc.c | 71 ++++++++++++---------------------------- drivers/scsi/gdth_proc.h | 3 -- 3 files changed, 25 insertions(+), 57 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index dd6784e03a57..87786e8fdf14 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -4239,7 +4239,7 @@ static int ioc_general(void __user *arg, char *cmnd) gdth_ioctl_general gen; gdth_ha_str *ha; char *buf = NULL; - u64 paddr; + dma_addr_t paddr; int rval; if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general))) @@ -4256,8 +4256,8 @@ static int ioc_general(void __user *arg, char *cmnd) return -EINVAL; if (gen.data_len + gen.sense_len > 0) { - buf = gdth_ioctl_alloc(ha, gen.data_len + gen.sense_len, FALSE, - &paddr); + buf = pci_alloc_consistent(ha->pdev, + gen.data_len + gen.sense_len, &paddr); if (!buf) return -EFAULT; @@ -4292,7 +4292,7 @@ static int ioc_general(void __user *arg, char *cmnd) rval = 0; out_free_buf: - gdth_ioctl_free(ha, gen.data_len+gen.sense_len, buf, paddr); + pci_free_consistent(ha->pdev, gen.data_len + gen.sense_len, buf, paddr); return rval; } diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index bd5532a80b0e..8e77f8fd8641 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -31,7 +31,6 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer, int i, found; gdth_cmd_str gdtcmd; gdth_cpar_str *pcpar; - u64 paddr; char cmnd[MAX_COMMAND_SIZE]; memset(cmnd, 0xff, 12); @@ -113,13 +112,23 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer, } if (wb_mode) { - if (!gdth_ioctl_alloc(ha, sizeof(gdth_cpar_str), TRUE, &paddr)) - return(-EBUSY); + unsigned long flags; + + BUILD_BUG_ON(sizeof(gdth_cpar_str) > GDTH_SCRATCH); + + spin_lock_irqsave(&ha->smp_lock, flags); + if (ha->scratch_busy) { + spin_unlock_irqrestore(&ha->smp_lock, flags); + return -EBUSY; + } + ha->scratch_busy = TRUE; + spin_unlock_irqrestore(&ha->smp_lock, flags); + pcpar = (gdth_cpar_str *)ha->pscratch; memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) ); gdtcmd.Service = CACHESERVICE; gdtcmd.OpCode = GDT_IOCTL; - gdtcmd.u.ioctl.p_param = paddr; + gdtcmd.u.ioctl.p_param = ha->scratch_phys; gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str); gdtcmd.u.ioctl.subfunc = CACHE_CONFIG; gdtcmd.u.ioctl.channel = INVALID_CHANNEL; @@ -127,7 +136,10 @@ static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer, gdth_execute(host, &gdtcmd, cmnd, 30, NULL); - gdth_ioctl_free(ha, GDTH_SCRATCH, ha->pscratch, paddr); + spin_lock_irqsave(&ha->smp_lock, flags); + ha->scratch_busy = FALSE; + spin_unlock_irqrestore(&ha->smp_lock, flags); + printk("Done.\n"); return(orig_length); } @@ -143,7 +155,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) int id, i, j, k, sec, flag; int no_mdrv = 0, drv_no, is_mirr; u32 cnt; - u64 paddr; + dma_addr_t paddr; int rc = -ENOMEM; gdth_cmd_str *gdtcmd; @@ -232,7 +244,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_puts(m, "\nPhysical Devices:"); flag = FALSE; - buf = gdth_ioctl_alloc(ha, size, FALSE, &paddr); + buf = pci_alloc_consistent(ha->pdev, size, &paddr); if (!buf) goto stop_output; for (i = 0; i < ha->bus_cnt; ++i) { @@ -406,7 +418,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_printf(m, " To Array Drv.:\t%s\n", hrec); } - + if (!flag) seq_puts(m, "\n --\n"); @@ -500,7 +512,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) } } } - gdth_ioctl_free(ha, size, buf, paddr); + pci_free_consistent(ha->pdev, size, buf, paddr); for (i = 0; i < MAX_HDRIVES; ++i) { if (!(ha->hdr[i].present)) @@ -553,47 +565,6 @@ free_fail: return rc; } -static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch, - u64 *paddr) -{ - unsigned long flags; - char *ret_val; - - if (size == 0) - return NULL; - - spin_lock_irqsave(&ha->smp_lock, flags); - - if (!ha->scratch_busy && size <= GDTH_SCRATCH) { - ha->scratch_busy = TRUE; - ret_val = ha->pscratch; - *paddr = ha->scratch_phys; - } else if (scratch) { - ret_val = NULL; - } else { - dma_addr_t dma_addr; - - ret_val = pci_alloc_consistent(ha->pdev, size, &dma_addr); - *paddr = dma_addr; - } - - spin_unlock_irqrestore(&ha->smp_lock, flags); - return ret_val; -} - -static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr) -{ - unsigned long flags; - - if (buf == ha->pscratch) { - spin_lock_irqsave(&ha->smp_lock, flags); - ha->scratch_busy = FALSE; - spin_unlock_irqrestore(&ha->smp_lock, flags); - } else { - pci_free_consistent(ha->pdev, size, buf, paddr); - } -} - #ifdef GDTH_IOCTL_PROC static int gdth_ioctl_check_bin(gdth_ha_str *ha, u16 size) { diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h index d7d0aa283695..4cc5377cb92e 100644 --- a/drivers/scsi/gdth_proc.h +++ b/drivers/scsi/gdth_proc.h @@ -12,9 +12,6 @@ int gdth_execute(struct Scsi_Host *shost, gdth_cmd_str *gdtcmd, char *cmnd, static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer, int length, gdth_ha_str *ha); -static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch, - u64 *paddr); -static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, u64 paddr); static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id); #endif From 314814552a0adfa6f27f05165c609044e2baad9c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:21 +0100 Subject: [PATCH 0156/4277] scsi: gdth: remove ISA and EISA support The non-PCI code has bitrotted for quite a while and will just oops on load because it passes a NULL pointer to the PCI DMA routines. Lets kill it for good - if someone really wants to use one of these cards I'll help mentoring them to write a proper driver glue. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 2 +- drivers/scsi/gdth.c | 709 ++----------------------------------------- drivers/scsi/gdth.h | 30 -- 3 files changed, 24 insertions(+), 717 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index f38882f6f37d..4d5d79d56f4b 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -665,7 +665,7 @@ config SCSI_DMX3191D config SCSI_GDTH tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support" - depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API + depends on PCI && SCSI ---help--- Formerly called GDT SCSI Disk Array Controller Support. diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 87786e8fdf14..da58ac703cbb 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -1,6 +1,6 @@ /************************************************************************ * Linux driver for * - * ICP vortex GmbH: GDT ISA/EISA/PCI Disk Array Controllers * + * ICP vortex GmbH: GDT PCI Disk Array Controllers * * Intel Corporation: Storage RAID Controllers * * * * gdth.c * @@ -32,15 +32,10 @@ ************************************************************************/ /* All GDT Disk Array Controllers are fully supported by this driver. - * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the + * This includes the PCI SCSI Disk Array Controllers and the * PCI Fibre Channel Disk Array Controllers. See gdth.h for a complete * list of all controller types. * - * If you have one or more GDT3000/3020 EISA controllers with - * controller BIOS disabled, you have to set the IRQ values with the - * command line option "gdth=irq1,irq2,...", where the irq1,irq2,... are - * the IRQ values for the EISA controllers. - * * After the optional list of IRQ values, other possible * command line options are: * disable:Y disable driver @@ -61,14 +56,12 @@ * access a shared resource from several nodes, * appropriate controller firmware required * shared_access:N enable driver reserve/release protocol - * probe_eisa_isa:Y scan for EISA/ISA controllers - * probe_eisa_isa:N do not scan for EISA/ISA controllers * force_dma32:Y use only 32 bit DMA mode * force_dma32:N use 64 bit DMA mode, if supported * * The default values are: "gdth=disable:N,reserve_mode:1,reverse_scan:N, * max_ids:127,rescan:N,hdr_channel:0, - * shared_access:Y,probe_eisa_isa:N,force_dma32:N". + * shared_access:Y,force_dma32:N". * Here is another example: "gdth=reserve_list:0,1,2,0,0,1,3,0,rescan:Y". * * When loading the gdth driver as a module, the same options are available. @@ -79,7 +72,7 @@ * * Default: "modprobe gdth disable=0 reserve_mode=1 reverse_scan=0 * max_ids=127 rescan=0 hdr_channel=0 shared_access=0 - * probe_eisa_isa=0 force_dma32=0" + * force_dma32=0" * The other example: "modprobe gdth reserve_list=0,1,2,0,0,1,3,0 rescan=1". */ @@ -286,12 +279,6 @@ static struct timer_list gdth_timer; #define BUS_L2P(a,b) ((b)>(a)->virt_bus ? (b-1):(b)) -#ifdef CONFIG_ISA -static u8 gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */ -#endif -#if defined(CONFIG_EISA) || defined(CONFIG_ISA) -static u8 gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */ -#endif static u8 gdth_polling; /* polling if TRUE */ static int gdth_ctr_count = 0; /* controller count */ static LIST_HEAD(gdth_instances); /* controller list */ @@ -325,10 +312,6 @@ static u8 gdth_direction_tab[0x100] = { }; /* LILO and modprobe/insmod parameters */ -/* IRQ list for GDT3000/3020 EISA controllers */ -static int irq[MAXHA] __initdata = -{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; /* disable driver flag */ static int disable __initdata = 0; /* reserve flag */ @@ -348,13 +331,10 @@ static int max_ids = MAXID; static int rescan = 0; /* shared access */ static int shared_access = 1; -/* enable support for EISA and ISA controllers */ -static int probe_eisa_isa = 0; /* 64 bit DMA mode, support for drives > 2 TB, if force_dma32 = 0 */ static int force_dma32 = 0; /* parameters for modprobe/insmod */ -module_param_hw_array(irq, int, irq, NULL, 0); module_param(disable, int, 0); module_param(reserve_mode, int, 0); module_param_array(reserve_list, int, NULL, 0); @@ -363,7 +343,6 @@ module_param(hdr_channel, int, 0); module_param(max_ids, int, 0); module_param(rescan, int, 0); module_param(shared_access, int, 0); -module_param(probe_eisa_isa, int, 0); module_param(force_dma32, int, 0); MODULE_AUTHOR("Achim Leubner"); MODULE_LICENSE("GPL"); @@ -515,45 +494,6 @@ static void gdth_eval_mapping(u32 size, u32 *cyls, int *heads, int *secs) } } -/* controller search and initialization functions */ -#ifdef CONFIG_EISA -static int __init gdth_search_eisa(u16 eisa_adr) -{ - u32 id; - - TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr)); - id = inl(eisa_adr+ID0REG); - if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */ - if ((inb(eisa_adr+EISAREG) & 8) == 0) - return 0; /* not EISA configured */ - return 1; - } - if (id == GDT3_ID) /* GDT3000 */ - return 1; - - return 0; -} -#endif /* CONFIG_EISA */ - -#ifdef CONFIG_ISA -static int __init gdth_search_isa(u32 bios_adr) -{ - void __iomem *addr; - u32 id; - - TRACE(("gdth_search_isa() bios adr. %x\n",bios_adr)); - if ((addr = ioremap(bios_adr+BIOS_ID_OFFS, sizeof(u32))) != NULL) { - id = readl(addr); - iounmap(addr); - if (id == GDT2_ID) /* GDT2000 */ - return 1; - } - return 0; -} -#endif /* CONFIG_ISA */ - -#ifdef CONFIG_PCI - static bool gdth_search_vortex(u16 device) { if (device <= PCI_DEVICE_ID_VORTEX_GDT6555) @@ -656,204 +596,7 @@ static int gdth_pci_init_one(struct pci_dev *pdev, return 0; } -#endif /* CONFIG_PCI */ -#ifdef CONFIG_EISA -static int __init gdth_init_eisa(u16 eisa_adr,gdth_ha_str *ha) -{ - u32 retries,id; - u8 prot_ver,eisacf,i,irq_found; - - TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr)); - - /* disable board interrupts, deinitialize services */ - outb(0xff,eisa_adr+EDOORREG); - outb(0x00,eisa_adr+EDENABREG); - outb(0x00,eisa_adr+EINTENABREG); - - outb(0xff,eisa_adr+LDOORREG); - retries = INIT_RETRIES; - gdth_delay(20); - while (inb(eisa_adr+EDOORREG) != 0xff) { - if (--retries == 0) { - printk("GDT-EISA: Initialization error (DEINIT failed)\n"); - return 0; - } - gdth_delay(1); - TRACE2(("wait for DEINIT: retries=%d\n",retries)); - } - prot_ver = inb(eisa_adr+MAILBOXREG); - outb(0xff,eisa_adr+EDOORREG); - if (prot_ver != PROTOCOL_VERSION) { - printk("GDT-EISA: Illegal protocol version\n"); - return 0; - } - ha->bmic = eisa_adr; - ha->brd_phys = (u32)eisa_adr >> 12; - - outl(0,eisa_adr+MAILBOXREG); - outl(0,eisa_adr+MAILBOXREG+4); - outl(0,eisa_adr+MAILBOXREG+8); - outl(0,eisa_adr+MAILBOXREG+12); - - /* detect IRQ */ - if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) { - ha->oem_id = OEM_ID_ICP; - ha->type = GDT_EISA; - ha->stype = id; - outl(1,eisa_adr+MAILBOXREG+8); - outb(0xfe,eisa_adr+LDOORREG); - retries = INIT_RETRIES; - gdth_delay(20); - while (inb(eisa_adr+EDOORREG) != 0xfe) { - if (--retries == 0) { - printk("GDT-EISA: Initialization error (get IRQ failed)\n"); - return 0; - } - gdth_delay(1); - } - ha->irq = inb(eisa_adr+MAILBOXREG); - outb(0xff,eisa_adr+EDOORREG); - TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq)); - /* check the result */ - if (ha->irq == 0) { - TRACE2(("Unknown IRQ, use IRQ table from cmd line !\n")); - for (i = 0, irq_found = FALSE; - i < MAXHA && irq[i] != 0xff; ++i) { - if (irq[i]==10 || irq[i]==11 || irq[i]==12 || irq[i]==14) { - irq_found = TRUE; - break; - } - } - if (irq_found) { - ha->irq = irq[i]; - irq[i] = 0; - printk("GDT-EISA: Can not detect controller IRQ,\n"); - printk("Use IRQ setting from command line (IRQ = %d)\n", - ha->irq); - } else { - printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n"); - printk("the controller BIOS or use command line parameters\n"); - return 0; - } - } - } else { - eisacf = inb(eisa_adr+EISAREG) & 7; - if (eisacf > 4) /* level triggered */ - eisacf -= 4; - ha->irq = gdth_irq_tab[eisacf]; - ha->oem_id = OEM_ID_ICP; - ha->type = GDT_EISA; - ha->stype = id; - } - - ha->dma64_support = 0; - return 1; -} -#endif /* CONFIG_EISA */ - -#ifdef CONFIG_ISA -static int __init gdth_init_isa(u32 bios_adr,gdth_ha_str *ha) -{ - register gdt2_dpram_str __iomem *dp2_ptr; - int i; - u8 irq_drq,prot_ver; - u32 retries; - - TRACE(("gdth_init_isa() bios adr. %x\n",bios_adr)); - - ha->brd = ioremap(bios_adr, sizeof(gdt2_dpram_str)); - if (ha->brd == NULL) { - printk("GDT-ISA: Initialization error (DPMEM remap error)\n"); - return 0; - } - dp2_ptr = ha->brd; - writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */ - /* reset interface area */ - memset_io(&dp2_ptr->u, 0, sizeof(dp2_ptr->u)); - if (readl(&dp2_ptr->u) != 0) { - printk("GDT-ISA: Initialization error (DPMEM write error)\n"); - iounmap(ha->brd); - return 0; - } - - /* disable board interrupts, read DRQ and IRQ */ - writeb(0xff, &dp2_ptr->io.irqdel); - writeb(0x00, &dp2_ptr->io.irqen); - writeb(0x00, &dp2_ptr->u.ic.S_Status); - writeb(0x00, &dp2_ptr->u.ic.Cmd_Index); - - irq_drq = readb(&dp2_ptr->io.rq); - for (i=0; i<3; ++i) { - if ((irq_drq & 1)==0) - break; - irq_drq >>= 1; - } - ha->drq = gdth_drq_tab[i]; - - irq_drq = readb(&dp2_ptr->io.rq) >> 3; - for (i=1; i<5; ++i) { - if ((irq_drq & 1)==0) - break; - irq_drq >>= 1; - } - ha->irq = gdth_irq_tab[i]; - - /* deinitialize services */ - writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]); - writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx); - writeb(0, &dp2_ptr->io.event); - retries = INIT_RETRIES; - gdth_delay(20); - while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) { - if (--retries == 0) { - printk("GDT-ISA: Initialization error (DEINIT failed)\n"); - iounmap(ha->brd); - return 0; - } - gdth_delay(1); - } - prot_ver = (u8)readl(&dp2_ptr->u.ic.S_Info[0]); - writeb(0, &dp2_ptr->u.ic.Status); - writeb(0xff, &dp2_ptr->io.irqdel); - if (prot_ver != PROTOCOL_VERSION) { - printk("GDT-ISA: Illegal protocol version\n"); - iounmap(ha->brd); - return 0; - } - - ha->oem_id = OEM_ID_ICP; - ha->type = GDT_ISA; - ha->ic_all_size = sizeof(dp2_ptr->u); - ha->stype= GDT2_ID; - ha->brd_phys = bios_adr >> 4; - - /* special request to controller BIOS */ - writel(0x00, &dp2_ptr->u.ic.S_Info[0]); - writel(0x00, &dp2_ptr->u.ic.S_Info[1]); - writel(0x01, &dp2_ptr->u.ic.S_Info[2]); - writel(0x00, &dp2_ptr->u.ic.S_Info[3]); - writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx); - writeb(0, &dp2_ptr->io.event); - retries = INIT_RETRIES; - gdth_delay(20); - while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) { - if (--retries == 0) { - printk("GDT-ISA: Initialization error\n"); - iounmap(ha->brd); - return 0; - } - gdth_delay(1); - } - writeb(0, &dp2_ptr->u.ic.Status); - writeb(0xff, &dp2_ptr->io.irqdel); - - ha->dma64_support = 0; - return 1; -} -#endif /* CONFIG_ISA */ - -#ifdef CONFIG_PCI static int gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr, gdth_ha_str *ha) { @@ -1228,30 +971,19 @@ static int gdth_init_pci(struct pci_dev *pdev, gdth_pci_str *pcistr, return 1; } -#endif /* CONFIG_PCI */ /* controller protocol functions */ static void gdth_enable_int(gdth_ha_str *ha) { unsigned long flags; - gdt2_dpram_str __iomem *dp2_ptr; gdt6_dpram_str __iomem *dp6_ptr; gdt6m_dpram_str __iomem *dp6m_ptr; TRACE(("gdth_enable_int() hanum %d\n",ha->hanum)); spin_lock_irqsave(&ha->smp_lock, flags); - if (ha->type == GDT_EISA) { - outb(0xff, ha->bmic + EDOORREG); - outb(0xff, ha->bmic + EDENABREG); - outb(0x01, ha->bmic + EINTENABREG); - } else if (ha->type == GDT_ISA) { - dp2_ptr = ha->brd; - writeb(1, &dp2_ptr->io.irqdel); - writeb(0, &dp2_ptr->u.ic.Cmd_Index); - writeb(1, &dp2_ptr->io.irqen); - } else if (ha->type == GDT_PCI) { + if (ha->type == GDT_PCI) { dp6_ptr = ha->brd; writeb(1, &dp6_ptr->io.irqdel); writeb(0, &dp6_ptr->u.ic.Cmd_Index); @@ -1275,12 +1007,7 @@ static u8 gdth_get_status(gdth_ha_str *ha) TRACE(("gdth_get_status() irq %d ctr_count %d\n", ha->irq, gdth_ctr_count)); - if (ha->type == GDT_EISA) - IStatus = inb((u16)ha->bmic + EDOORREG); - else if (ha->type == GDT_ISA) - IStatus = - readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index); - else if (ha->type == GDT_PCI) + if (ha->type == GDT_PCI) IStatus = readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index); else if (ha->type == GDT_PCINEW) @@ -1298,11 +1025,7 @@ static int gdth_test_busy(gdth_ha_str *ha) TRACE(("gdth_test_busy() hanum %d\n", ha->hanum)); - if (ha->type == GDT_EISA) - gdtsema0 = (int)inb(ha->bmic + SEMA0REG); - else if (ha->type == GDT_ISA) - gdtsema0 = (int)readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0); - else if (ha->type == GDT_PCI) + if (ha->type == GDT_PCI) gdtsema0 = (int)readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0); else if (ha->type == GDT_PCINEW) gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg)); @@ -1336,11 +1059,7 @@ static void gdth_set_sema0(gdth_ha_str *ha) { TRACE(("gdth_set_sema0() hanum %d\n", ha->hanum)); - if (ha->type == GDT_EISA) { - outb(1, ha->bmic + SEMA0REG); - } else if (ha->type == GDT_ISA) { - writeb(1, &((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0); - } else if (ha->type == GDT_PCI) { + if (ha->type == GDT_PCI) { writeb(1, &((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0); } else if (ha->type == GDT_PCINEW) { outb(1, PTR2USHORT(&ha->plx->sema0_reg)); @@ -1356,7 +1075,6 @@ static void gdth_copy_command(gdth_ha_str *ha) register gdt6m_dpram_str __iomem *dp6m_ptr; register gdt6c_dpram_str __iomem *dp6c_ptr; gdt6_dpram_str __iomem *dp6_ptr; - gdt2_dpram_str __iomem *dp2_ptr; u16 cp_count,dp_offset,cmd_no; TRACE(("gdth_copy_command() hanum %d\n", ha->hanum)); @@ -1367,8 +1085,6 @@ static void gdth_copy_command(gdth_ha_str *ha) cmd_ptr = ha->pccb; ++ha->cmd_cnt; - if (ha->type == GDT_EISA) - return; /* no DPMEM, no copy */ /* set cpcount dword aligned */ if (cp_count & 3) @@ -1377,14 +1093,7 @@ static void gdth_copy_command(gdth_ha_str *ha) ha->cmd_offs_dpmem += cp_count; /* set offset and service, copy command to DPMEM */ - if (ha->type == GDT_ISA) { - dp2_ptr = ha->brd; - writew(dp_offset + DPMEM_COMMAND_OFFSET, - &dp2_ptr->u.ic.comm_queue[cmd_no].offset); - writew((u16)cmd_ptr->Service, - &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id); - memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); - } else if (ha->type == GDT_PCI) { + if (ha->type == GDT_PCI) { dp6_ptr = ha->brd; writew(dp_offset + DPMEM_COMMAND_OFFSET, &dp6_ptr->u.ic.comm_queue[cmd_no].offset); @@ -1430,13 +1139,7 @@ static void gdth_release_event(gdth_ha_str *ha) if (ha->pccb->OpCode == GDT_INIT) ha->pccb->Service |= 0x80; - if (ha->type == GDT_EISA) { - if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */ - outl(ha->ccb_phys, ha->bmic + MAILBOXREG); - outb(ha->pccb->Service, ha->bmic + LDOORREG); - } else if (ha->type == GDT_ISA) { - writeb(0, &((gdt2_dpram_str __iomem *)ha->brd)->io.event); - } else if (ha->type == GDT_PCI) { + if (ha->type == GDT_PCI) { writeb(0, &((gdt6_dpram_str __iomem *)ha->brd)->io.event); } else if (ha->type == GDT_PCINEW) { outb(1, PTR2USHORT(&ha->plx->ldoor_reg)); @@ -2433,9 +2136,6 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n", scp->cmnd[0],scp->cmd_len,hdrive)); - if (ha->type==GDT_EISA && ha->cmd_cnt>0) - return 0; - mode64 = (ha->cache_feat & GDT_64BIT) ? TRUE : FALSE; /* test for READ_16, WRITE_16 if !mode64 ? --- not required, should not occur due to error return on @@ -2613,9 +2313,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b) TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n", scp->cmnd[0],b,t,l)); - if (ha->type==GDT_EISA && ha->cmd_cnt>0) - return 0; - mode64 = (ha->raw_feat & GDT_64BIT) ? TRUE : FALSE; cmdp->Service = SCSIRAWSERVICE; @@ -2778,9 +2475,6 @@ static int gdth_special_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp) cmdp= ha->pccb; TRACE2(("gdth_special_cmd(): ")); - if (ha->type==GDT_EISA && ha->cmd_cnt>0) - return 0; - *cmdp = *cmndinfo->internal_cmd_str; cmdp->RequestBuffer = scp; @@ -2959,7 +2653,6 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, { gdt6m_dpram_str __iomem *dp6m_ptr = NULL; gdt6_dpram_str __iomem *dp6_ptr; - gdt2_dpram_str __iomem *dp2_ptr; struct scsi_cmnd *scp; int rval, i; u8 IStatus; @@ -3015,35 +2708,7 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, } #endif - if (ha->type == GDT_EISA) { - if (IStatus & 0x80) { /* error flag */ - IStatus &= ~0x80; - ha->status = inw(ha->bmic + MAILBOXREG+8); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); - } else /* no error */ - ha->status = S_OK; - ha->info = inl(ha->bmic + MAILBOXREG+12); - ha->service = inw(ha->bmic + MAILBOXREG+10); - ha->info2 = inl(ha->bmic + MAILBOXREG+4); - - outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */ - outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */ - } else if (ha->type == GDT_ISA) { - dp2_ptr = ha->brd; - if (IStatus & 0x80) { /* error flag */ - IStatus &= ~0x80; - ha->status = readw(&dp2_ptr->u.ic.Status); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); - } else /* no error */ - ha->status = S_OK; - ha->info = readl(&dp2_ptr->u.ic.Info[0]); - ha->service = readw(&dp2_ptr->u.ic.Service); - ha->info2 = readl(&dp2_ptr->u.ic.Info[1]); - - writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */ - writeb(0, &dp2_ptr->u.ic.Cmd_Index);/* reset command index */ - writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */ - } else if (ha->type == GDT_PCI) { + if (ha->type == GDT_PCI) { dp6_ptr = ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; @@ -3610,12 +3275,7 @@ static int gdth_async_event(gdth_ha_str *ha) + sizeof(u64); ha->cmd_cnt = 0; gdth_copy_command(ha); - if (ha->type == GDT_EISA) - printk("[EISA slot %d] ",(u16)ha->brd_phys); - else if (ha->type == GDT_ISA) - printk("[DPMEM 0x%4X] ",(u16)ha->brd_phys); - else - printk("[PCI %d/%d] ",(u16)(ha->brd_phys>>8), + printk("[PCI %d/%d] ",(u16)(ha->brd_phys>>8), (u16)((ha->brd_phys>>3)&0x1f)); gdth_release_event(ha); } @@ -3756,23 +3416,12 @@ static inline void gdth_timer_init(void) static void __init internal_setup(char *str,int *ints) { - int i, argc; + int i; char *cur_str, *argv; TRACE2(("internal_setup() str %s ints[0] %d\n", str ? str:"NULL", ints ? ints[0]:0)); - /* read irq[] from ints[] */ - if (ints) { - argc = ints[0]; - if (argc > 0) { - if (argc > MAXHA) - argc = MAXHA; - for (i = 0; i < argc; ++i) - irq[i] = ints[i+1]; - } - } - /* analyse string */ argv = str; while (argv && (cur_str = strchr(argv, ':'))) { @@ -3799,8 +3448,6 @@ static void __init internal_setup(char *str,int *ints) rescan = val; else if (!strncmp(argv, "shared_access:", 14)) shared_access = val; - else if (!strncmp(argv, "probe_eisa_isa:", 15)) - probe_eisa_isa = val; else if (!strncmp(argv, "reserve_list:", 13)) { reserve_list[0] = val; for (i = 1; i < MAX_RES_ARGS; i++) { @@ -3847,18 +3494,7 @@ static const char *gdth_ctr_name(gdth_ha_str *ha) { TRACE2(("gdth_ctr_name()\n")); - if (ha->type == GDT_EISA) { - switch (ha->stype) { - case GDT3_ID: - return("GDT3000/3020"); - case GDT3A_ID: - return("GDT3000A/3020A/3050A"); - case GDT3B_ID: - return("GDT3000B/3010A"); - } - } else if (ha->type == GDT_ISA) { - return("GDT2000/2020"); - } else if (ha->type == GDT_PCI) { + if (ha->type == GDT_PCI) { switch (ha->pdev->device) { case PCI_DEVICE_ID_VORTEX_GDT60x0: return("GDT6000/6020/6050"); @@ -4528,22 +4164,17 @@ static int gdth_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) (NULL == (ha = gdth_find_ha(ctrt.ionode)))) return -EFAULT; - if (ha->type == GDT_ISA || ha->type == GDT_EISA) { - ctrt.type = (u8)((ha->stype>>20) - 0x10); + if (ha->type != GDT_PCIMPR) { + ctrt.type = (u8)((ha->stype<<4) + 6); } else { - if (ha->type != GDT_PCIMPR) { - ctrt.type = (u8)((ha->stype<<4) + 6); - } else { - ctrt.type = - (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe); - if (ha->stype >= 0x300) - ctrt.ext_type = 0x6000 | ha->pdev->subsystem_device; - else - ctrt.ext_type = 0x6000 | ha->stype; - } - ctrt.device_id = ha->pdev->device; - ctrt.sub_device_id = ha->pdev->subsystem_device; + ctrt.type = (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe); + if (ha->stype >= 0x300) + ctrt.ext_type = 0x6000 | ha->pdev->subsystem_device; + else + ctrt.ext_type = 0x6000 | ha->stype; } + ctrt.device_id = ha->pdev->device; + ctrt.sub_device_id = ha->pdev->subsystem_device; ctrt.info = ha->brd_phys; ctrt.oem_id = ha->oem_id; if (copy_to_user(argp, &ctrt, sizeof(gdth_ioctl_ctrtype))) @@ -4697,272 +4328,6 @@ static struct scsi_host_template gdth_template = { .no_write_same = 1, }; -#ifdef CONFIG_ISA -static int __init gdth_isa_probe_one(u32 isa_bios) -{ - struct Scsi_Host *shp; - gdth_ha_str *ha; - dma_addr_t scratch_dma_handle = 0; - int error, i; - - if (!gdth_search_isa(isa_bios)) - return -ENXIO; - - shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str)); - if (!shp) - return -ENOMEM; - ha = shost_priv(shp); - - error = -ENODEV; - if (!gdth_init_isa(isa_bios,ha)) - goto out_host_put; - - /* controller found and initialized */ - printk("Configuring GDT-ISA HA at BIOS 0x%05X IRQ %u DRQ %u\n", - isa_bios, ha->irq, ha->drq); - - error = request_irq(ha->irq, gdth_interrupt, 0, "gdth", ha); - if (error) { - printk("GDT-ISA: Unable to allocate IRQ\n"); - goto out_host_put; - } - - error = request_dma(ha->drq, "gdth"); - if (error) { - printk("GDT-ISA: Unable to allocate DMA channel\n"); - goto out_free_irq; - } - - set_dma_mode(ha->drq,DMA_MODE_CASCADE); - enable_dma(ha->drq); - shp->unchecked_isa_dma = 1; - shp->irq = ha->irq; - shp->dma_channel = ha->drq; - - ha->hanum = gdth_ctr_count++; - ha->shost = shp; - - ha->pccb = &ha->cmdext; - ha->ccb_phys = 0L; - ha->pdev = NULL; - - error = -ENOMEM; - - ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH, - &scratch_dma_handle); - if (!ha->pscratch) - goto out_dec_counters; - ha->scratch_phys = scratch_dma_handle; - - ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str), - &scratch_dma_handle); - if (!ha->pmsg) - goto out_free_pscratch; - ha->msg_phys = scratch_dma_handle; - -#ifdef INT_COAL - ha->coal_stat = pci_alloc_consistent(ha->pdev, - sizeof(gdth_coal_status) * MAXOFFSETS, - &scratch_dma_handle); - if (!ha->coal_stat) - goto out_free_pmsg; - ha->coal_stat_phys = scratch_dma_handle; -#endif - - ha->scratch_busy = FALSE; - ha->req_first = NULL; - ha->tid_cnt = MAX_HDRIVES; - if (max_ids > 0 && max_ids < ha->tid_cnt) - ha->tid_cnt = max_ids; - for (i = 0; i < GDTH_MAXCMDS; ++i) - ha->cmd_tab[i].cmnd = UNUSED_CMND; - ha->scan_mode = rescan ? 0x10 : 0; - - error = -ENODEV; - if (!gdth_search_drives(ha)) { - printk("GDT-ISA: Error during device scan\n"); - goto out_free_coal_stat; - } - - if (hdr_channel < 0 || hdr_channel > ha->bus_cnt) - hdr_channel = ha->bus_cnt; - ha->virt_bus = hdr_channel; - - if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT) - shp->max_cmd_len = 16; - - shp->max_id = ha->tid_cnt; - shp->max_lun = MAXLUN; - shp->max_channel = ha->bus_cnt; - - spin_lock_init(&ha->smp_lock); - gdth_enable_int(ha); - - error = scsi_add_host(shp, NULL); - if (error) - goto out_free_coal_stat; - list_add_tail(&ha->list, &gdth_instances); - gdth_timer_init(); - - scsi_scan_host(shp); - - return 0; - - out_free_coal_stat: -#ifdef INT_COAL - pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS, - ha->coal_stat, ha->coal_stat_phys); - out_free_pmsg: -#endif - pci_free_consistent(ha->pdev, sizeof(gdth_msg_str), - ha->pmsg, ha->msg_phys); - out_free_pscratch: - pci_free_consistent(ha->pdev, GDTH_SCRATCH, - ha->pscratch, ha->scratch_phys); - out_dec_counters: - gdth_ctr_count--; - out_free_irq: - free_irq(ha->irq, ha); - out_host_put: - scsi_host_put(shp); - return error; -} -#endif /* CONFIG_ISA */ - -#ifdef CONFIG_EISA -static int __init gdth_eisa_probe_one(u16 eisa_slot) -{ - struct Scsi_Host *shp; - gdth_ha_str *ha; - dma_addr_t scratch_dma_handle = 0; - int error, i; - - if (!gdth_search_eisa(eisa_slot)) - return -ENXIO; - - shp = scsi_host_alloc(&gdth_template, sizeof(gdth_ha_str)); - if (!shp) - return -ENOMEM; - ha = shost_priv(shp); - - error = -ENODEV; - if (!gdth_init_eisa(eisa_slot,ha)) - goto out_host_put; - - /* controller found and initialized */ - printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n", - eisa_slot >> 12, ha->irq); - - error = request_irq(ha->irq, gdth_interrupt, 0, "gdth", ha); - if (error) { - printk("GDT-EISA: Unable to allocate IRQ\n"); - goto out_host_put; - } - - shp->unchecked_isa_dma = 0; - shp->irq = ha->irq; - shp->dma_channel = 0xff; - - ha->hanum = gdth_ctr_count++; - ha->shost = shp; - - TRACE2(("EISA detect Bus 0: hanum %d\n", ha->hanum)); - - ha->pccb = &ha->cmdext; - ha->ccb_phys = 0L; - - error = -ENOMEM; - - ha->pdev = NULL; - ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH, - &scratch_dma_handle); - if (!ha->pscratch) - goto out_free_irq; - ha->scratch_phys = scratch_dma_handle; - - ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str), - &scratch_dma_handle); - if (!ha->pmsg) - goto out_free_pscratch; - ha->msg_phys = scratch_dma_handle; - -#ifdef INT_COAL - ha->coal_stat = pci_alloc_consistent(ha->pdev, - sizeof(gdth_coal_status) * MAXOFFSETS, - &scratch_dma_handle); - if (!ha->coal_stat) - goto out_free_pmsg; - ha->coal_stat_phys = scratch_dma_handle; -#endif - - ha->ccb_phys = pci_map_single(ha->pdev,ha->pccb, - sizeof(gdth_cmd_str), PCI_DMA_BIDIRECTIONAL); - if (!ha->ccb_phys) - goto out_free_coal_stat; - - ha->scratch_busy = FALSE; - ha->req_first = NULL; - ha->tid_cnt = MAX_HDRIVES; - if (max_ids > 0 && max_ids < ha->tid_cnt) - ha->tid_cnt = max_ids; - for (i = 0; i < GDTH_MAXCMDS; ++i) - ha->cmd_tab[i].cmnd = UNUSED_CMND; - ha->scan_mode = rescan ? 0x10 : 0; - - if (!gdth_search_drives(ha)) { - printk("GDT-EISA: Error during device scan\n"); - error = -ENODEV; - goto out_free_ccb_phys; - } - - if (hdr_channel < 0 || hdr_channel > ha->bus_cnt) - hdr_channel = ha->bus_cnt; - ha->virt_bus = hdr_channel; - - if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT) - shp->max_cmd_len = 16; - - shp->max_id = ha->tid_cnt; - shp->max_lun = MAXLUN; - shp->max_channel = ha->bus_cnt; - - spin_lock_init(&ha->smp_lock); - gdth_enable_int(ha); - - error = scsi_add_host(shp, NULL); - if (error) - goto out_free_ccb_phys; - list_add_tail(&ha->list, &gdth_instances); - gdth_timer_init(); - - scsi_scan_host(shp); - - return 0; - - out_free_ccb_phys: - pci_unmap_single(ha->pdev,ha->ccb_phys, sizeof(gdth_cmd_str), - PCI_DMA_BIDIRECTIONAL); - out_free_coal_stat: -#ifdef INT_COAL - pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS, - ha->coal_stat, ha->coal_stat_phys); - out_free_pmsg: -#endif - pci_free_consistent(ha->pdev, sizeof(gdth_msg_str), - ha->pmsg, ha->msg_phys); - out_free_pscratch: - pci_free_consistent(ha->pdev, GDTH_SCRATCH, - ha->pscratch, ha->scratch_phys); - out_free_irq: - free_irq(ha->irq, ha); - gdth_ctr_count--; - out_host_put: - scsi_host_put(shp); - return error; -} -#endif /* CONFIG_EISA */ - -#ifdef CONFIG_PCI static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) { struct Scsi_Host *shp; @@ -5105,7 +4470,6 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) scsi_host_put(shp); return error; } -#endif /* CONFIG_PCI */ static void gdth_remove_one(gdth_ha_str *ha) { @@ -5125,10 +4489,6 @@ static void gdth_remove_one(gdth_ha_str *ha) if (shp->irq) free_irq(shp->irq,ha); -#ifdef CONFIG_ISA - if (shp->dma_channel != 0xff) - free_dma(shp->dma_channel); -#endif #ifdef INT_COAL if (ha->coal_stat) pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * @@ -5181,26 +4541,6 @@ static int __init gdth_init(void) gdth_clear_events(); timer_setup(&gdth_timer, gdth_timeout, 0); - /* As default we do not probe for EISA or ISA controllers */ - if (probe_eisa_isa) { - /* scanning for controllers, at first: ISA controller */ -#ifdef CONFIG_ISA - u32 isa_bios; - for (isa_bios = 0xc8000UL; isa_bios <= 0xd8000UL; - isa_bios += 0x8000UL) - gdth_isa_probe_one(isa_bios); -#endif -#ifdef CONFIG_EISA - { - u16 eisa_slot; - for (eisa_slot = 0x1000; eisa_slot <= 0x8000; - eisa_slot += 0x1000) - gdth_eisa_probe_one(eisa_slot); - } -#endif - } - -#ifdef CONFIG_PCI /* scanning for PCI controllers */ if (pci_register_driver(&gdth_pci_driver)) { gdth_ha_str *ha; @@ -5209,7 +4549,6 @@ static int __init gdth_init(void) gdth_remove_one(ha); return -ENODEV; } -#endif /* CONFIG_PCI */ TRACE2(("gdth_detect() %d controller detected\n", gdth_ctr_count)); @@ -5230,9 +4569,7 @@ static void __exit gdth_exit(void) del_timer_sync(&gdth_timer); #endif -#ifdef CONFIG_PCI pci_unregister_driver(&gdth_pci_driver); -#endif list_for_each_entry(ha, &gdth_instances, list) gdth_remove_one(ha); diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index ee6ffcf388e8..5a13d406d40e 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -38,17 +38,9 @@ #define OEM_ID_INTEL 0x8000 /* controller classes */ -#define GDT_ISA 0x01 /* ISA controller */ -#define GDT_EISA 0x02 /* EISA controller */ #define GDT_PCI 0x03 /* PCI controller */ #define GDT_PCINEW 0x04 /* new PCI controller */ #define GDT_PCIMPR 0x05 /* PCI MPR controller */ -/* GDT_EISA, controller subtypes EISA */ -#define GDT3_ID 0x0130941c /* GDT3000/3020 */ -#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */ -#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */ -/* GDT_ISA */ -#define GDT2_ID 0x0120941c /* GDT2000/2020 */ #ifndef PCI_DEVICE_ID_VORTEX_GDT60x0 /* GDT_PCI */ @@ -281,17 +273,6 @@ #define GDTH_DATA_IN 0x01000000L /* data from target */ #define GDTH_DATA_OUT 0x00000000L /* data to target */ -/* BMIC registers (EISA controllers) */ -#define ID0REG 0x0c80 /* board ID */ -#define EINTENABREG 0x0c89 /* interrupt enable */ -#define SEMA0REG 0x0c8a /* command semaphore */ -#define SEMA1REG 0x0c8b /* status semaphore */ -#define LDOORREG 0x0c8d /* local doorbell */ -#define EDENABREG 0x0c8e /* EISA system doorbell enab. */ -#define EDOORREG 0x0c8f /* EISA system doorbell */ -#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */ -#define EISAREG 0x0cc0 /* EISA configuration */ - /* other defines */ #define LINUX_OS 8 /* used for cache optim. */ #define SECS32 0x1f /* round capacity */ @@ -706,21 +687,11 @@ typedef struct { u8 fw_magic; /* contr. ID from firmware */ } __attribute__((packed)) gdt_pci_sram; -/* SRAM structure EISA controllers (but NOT GDT3000/3020) */ -typedef struct { - u8 os_used[16]; /* OS code per service */ - u16 need_deinit; /* switch betw. BIOS/driver */ - u8 switch_support; /* see need_deinit */ - u8 padding; -} __attribute__((packed)) gdt_eisa_sram; - - /* DPRAM ISA controllers */ typedef struct { union { struct { u8 bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */ - u32 magic; /* controller (EISA) ID */ u16 need_deinit; /* switch betw. BIOS/driver */ u8 switch_support; /* see need_deinit */ u8 padding[9]; @@ -843,7 +814,6 @@ typedef struct { u16 cache_feat; /* feat. cache serv. (s/g,..)*/ u16 raw_feat; /* feat. raw service (s/g,..)*/ u16 screen_feat; /* feat. raw service (s/g,..)*/ - u16 bmic; /* BMIC address (EISA) */ void __iomem *brd; /* DPRAM address */ u32 brd_phys; /* slot number/BIOS address */ gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */ From adfa080bf2f985cef33108c3df009f90fdafa450 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:22 +0100 Subject: [PATCH 0157/4277] scsi: gdth: remove direct serial port access Remove never compile in support for sending debug traces straight to the serial port using direct port access. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 70 --------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index da58ac703cbb..9e81b9655c2d 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -185,79 +185,9 @@ static void gdth_scsi_done(struct scsi_cmnd *scp); #ifdef DEBUG_GDTH static u8 DebugState = DEBUG_GDTH; - -#ifdef __SERIAL__ -#define MAX_SERBUF 160 -static void ser_init(void); -static void ser_puts(char *str); -static void ser_putc(char c); -static int ser_printk(const char *fmt, ...); -static char strbuf[MAX_SERBUF+1]; -#ifdef __COM2__ -#define COM_BASE 0x2f8 -#else -#define COM_BASE 0x3f8 -#endif -static void ser_init() -{ - unsigned port=COM_BASE; - - outb(0x80,port+3); - outb(0,port+1); - /* 19200 Baud, if 9600: outb(12,port) */ - outb(6, port); - outb(3,port+3); - outb(0,port+1); - /* - ser_putc('I'); - ser_putc(' '); - */ -} - -static void ser_puts(char *str) -{ - char *ptr; - - ser_init(); - for (ptr=str;*ptr;++ptr) - ser_putc(*ptr); -} - -static void ser_putc(char c) -{ - unsigned port=COM_BASE; - - while ((inb(port+5) & 0x20)==0); - outb(c,port); - if (c==0x0a) - { - while ((inb(port+5) & 0x20)==0); - outb(0x0d,port); - } -} - -static int ser_printk(const char *fmt, ...) -{ - va_list args; - int i; - - va_start(args,fmt); - i = vsprintf(strbuf,fmt,args); - ser_puts(strbuf); - va_end(args); - return i; -} - -#define TRACE(a) {if (DebugState==1) {ser_printk a;}} -#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a;}} -#define TRACE3(a) {if (DebugState!=0) {ser_printk a;}} - -#else /* !__SERIAL__ */ #define TRACE(a) {if (DebugState==1) {printk a;}} #define TRACE2(a) {if (DebugState==1 || DebugState==2) {printk a;}} #define TRACE3(a) {if (DebugState!=0) {printk a;}} -#endif - #else /* !DEBUG */ #define TRACE(a) #define TRACE2(a) From e09c142891a9362b91208f3423961e7d436b540f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:23 +0100 Subject: [PATCH 0158/4277] scsi: gdth: remove dead rtc code This code has been under the never defined GDTH_RTC ifdef forever, nuke it. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 9e81b9655c2d..8e80902d909e 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -115,10 +115,6 @@ #include #include #include - -#ifdef GDTH_RTC -#include -#endif #include #include @@ -1197,11 +1193,6 @@ static int gdth_search_drives(gdth_ha_str *ha) gdth_perf_modes *pmod; #endif -#ifdef GDTH_RTC - u8 rtc[12]; - unsigned long flags; -#endif - TRACE(("gdth_search_drives() hanum %d\n", ha->hanum)); ok = 0; @@ -1221,29 +1212,6 @@ static int gdth_search_drives(gdth_ha_str *ha) } TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n")); -#ifdef GDTH_RTC - /* read realtime clock info, send to controller */ - /* 1. wait for the falling edge of update flag */ - spin_lock_irqsave(&rtc_lock, flags); - for (j = 0; j < 1000000; ++j) - if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) - break; - for (j = 0; j < 1000000; ++j) - if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) - break; - /* 2. read info */ - do { - for (j = 0; j < 12; ++j) - rtc[j] = CMOS_READ(j); - } while (rtc[0] != CMOS_READ(0)); - spin_unlock_irqrestore(&rtc_lock, flags); - TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(u32 *)&rtc[0], - *(u32 *)&rtc[4], *(u32 *)&rtc[8])); - /* 3. send to controller firmware */ - gdth_internal_cmd(ha, SCREENSERVICE, GDT_REALTIME, *(u32 *)&rtc[0], - *(u32 *)&rtc[4], *(u32 *)&rtc[8]); -#endif - /* unfreeze all IOs */ gdth_internal_cmd(ha, CACHESERVICE, GDT_UNFREEZE_IO, 0, 0, 0); From 1d978c336ae3067fd136b7238a60e928b922e9e7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:24 +0100 Subject: [PATCH 0159/4277] scsi: gdth: remove dead dma statistics code This code can't be built into the kernel without editing the source file and is not generally useful. [mkp: typo] Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 18 ------------------ drivers/scsi/gdth_proc.c | 8 -------- 2 files changed, 26 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 8e80902d909e..93d9c2f14ab9 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -2126,12 +2126,6 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, cmdp->u.cache64.sg_canz = sgcnt; scsi_for_each_sg(scp, sl, sgcnt, i) { cmdp->u.cache64.sg_lst[i].sg_ptr = sg_dma_address(sl); -#ifdef GDTH_DMA_STATISTICS - if (cmdp->u.cache64.sg_lst[i].sg_ptr > (u64)0xffffffff) - ha->dma64_cnt++; - else - ha->dma32_cnt++; -#endif cmdp->u.cache64.sg_lst[i].sg_len = sg_dma_len(sl); } } else { @@ -2141,9 +2135,6 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, cmdp->u.cache.sg_canz = sgcnt; scsi_for_each_sg(scp, sl, sgcnt, i) { cmdp->u.cache.sg_lst[i].sg_ptr = sg_dma_address(sl); -#ifdef GDTH_DMA_STATISTICS - ha->dma32_cnt++; -#endif cmdp->u.cache.sg_lst[i].sg_len = sg_dma_len(sl); } } @@ -2298,12 +2289,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b) cmdp->u.raw64.sg_ranz = sgcnt; scsi_for_each_sg(scp, sl, sgcnt, i) { cmdp->u.raw64.sg_lst[i].sg_ptr = sg_dma_address(sl); -#ifdef GDTH_DMA_STATISTICS - if (cmdp->u.raw64.sg_lst[i].sg_ptr > (u64)0xffffffff) - ha->dma64_cnt++; - else - ha->dma32_cnt++; -#endif cmdp->u.raw64.sg_lst[i].sg_len = sg_dma_len(sl); } } else { @@ -2313,9 +2298,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b) cmdp->u.raw.sg_ranz = sgcnt; scsi_for_each_sg(scp, sl, sgcnt, i) { cmdp->u.raw.sg_lst[i].sg_ptr = sg_dma_address(sl); -#ifdef GDTH_DMA_STATISTICS - ha->dma32_cnt++; -#endif cmdp->u.raw.sg_lst[i].sg_len = sg_dma_len(sl); } } diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index 8e77f8fd8641..fc36c49a5334 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -229,14 +229,6 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) " Serial No.: \t0x%8X\tCache RAM size:\t%d KB\n", ha->binfo.ser_no, ha->binfo.memsize / 1024); -#ifdef GDTH_DMA_STATISTICS - /* controller statistics */ - seq_puts(m, "\nController Statistics:\n"); - seq_printf(m, - " 32-bit DMA buffer:\t%lu\t64-bit DMA buffer:\t%lu\n", - ha->dma32_cnt, ha->dma64_cnt); -#endif - if (ha->more_proc) { size_t size = max_t(size_t, GDTH_SCRATCH, sizeof(gdth_hget_str)); From 9affa597850fb53918f7069f73bb7235756a4dc7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:25 +0100 Subject: [PATCH 0160/4277] scsi: gdth: remove dead code under #ifdef GDTH_IOCTL_PROC This can't ever be compiled into the kernel, so remove it. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth_ioctl.h | 89 --------------------------------------- drivers/scsi/gdth_proc.c | 18 -------- 2 files changed, 107 deletions(-) diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h index 4c91894ac244..ee4c9bf1022a 100644 --- a/drivers/scsi/gdth_ioctl.h +++ b/drivers/scsi/gdth_ioctl.h @@ -27,11 +27,7 @@ #define GDTH_MAXSG 32 /* max. s/g elements */ #define MAX_LDRIVES 255 /* max. log. drive count */ -#ifdef GDTH_IOCTL_PROC -#define MAX_HDRIVES 100 /* max. host drive count */ -#else #define MAX_HDRIVES MAX_LDRIVES /* max. host drive count */ -#endif /* scatter/gather element */ typedef struct { @@ -178,91 +174,6 @@ typedef struct { gdth_evt_data event_data; } __attribute__((packed)) gdth_evt_str; - -#ifdef GDTH_IOCTL_PROC -/* IOCTL structure (write) */ -typedef struct { - u32 magic; /* IOCTL magic */ - u16 ioctl; /* IOCTL */ - u16 ionode; /* controller number */ - u16 service; /* controller service */ - u16 timeout; /* timeout */ - union { - struct { - u8 command[512]; /* controller command */ - u8 data[1]; /* add. data */ - } general; - struct { - u8 lock; /* lock/unlock */ - u8 drive_cnt; /* drive count */ - u16 drives[MAX_HDRIVES];/* drives */ - } lockdrv; - struct { - u8 lock; /* lock/unlock */ - u8 channel; /* channel */ - } lockchn; - struct { - int erase; /* erase event ? */ - int handle; - u8 evt[EVENT_SIZE]; /* event structure */ - } event; - struct { - u8 bus; /* SCSI bus */ - u8 target; /* target ID */ - u8 lun; /* LUN */ - u8 cmd_len; /* command length */ - u8 cmd[12]; /* SCSI command */ - } scsi; - struct { - u16 hdr_no; /* host drive number */ - u8 flag; /* old meth./add/remove */ - } rescan; - } iu; -} gdth_iowr_str; - -/* IOCTL structure (read) */ -typedef struct { - u32 size; /* buffer size */ - u32 status; /* IOCTL error code */ - union { - struct { - u8 data[1]; /* data */ - } general; - struct { - u16 version; /* driver version */ - } drvers; - struct { - u8 type; /* controller type */ - u16 info; /* slot etc. */ - u16 oem_id; /* OEM ID */ - u16 bios_ver; /* not used */ - u16 access; /* not used */ - u16 ext_type; /* extended type */ - u16 device_id; /* device ID */ - u16 sub_device_id; /* sub device ID */ - } ctrtype; - struct { - u8 version; /* OS version */ - u8 subversion; /* OS subversion */ - u16 revision; /* revision */ - } osvers; - struct { - u16 count; /* controller count */ - } ctrcnt; - struct { - int handle; - u8 evt[EVENT_SIZE]; /* event structure */ - } event; - struct { - u8 bus; /* SCSI bus, 0xff: invalid */ - u8 target; /* target ID */ - u8 lun; /* LUN */ - u8 cluster_type; /* cluster properties */ - } hdr_list[MAX_HDRIVES]; /* index is host drive number */ - } iu; -} gdth_iord_str; -#endif - /* GDTIOCTL_GENERAL */ typedef struct { u16 ionode; /* controller number */ diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index fc36c49a5334..5a13ccac8dee 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -557,24 +557,6 @@ free_fail: return rc; } -#ifdef GDTH_IOCTL_PROC -static int gdth_ioctl_check_bin(gdth_ha_str *ha, u16 size) -{ - unsigned long flags; - int ret_val; - - spin_lock_irqsave(&ha->smp_lock, flags); - - ret_val = FALSE; - if (ha->scratch_busy) { - if (((gdth_iord_str *)ha->pscratch)->size == (u32)size) - ret_val = TRUE; - } - spin_unlock_irqrestore(&ha->smp_lock, flags); - return ret_val; -} -#endif - static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id) { unsigned long flags; From d8e1e6add226e57fe467483d117e4b6113fe2e2e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:26 +0100 Subject: [PATCH 0161/4277] scsi: gdth: remove interrupt coalescing support This code has been under a never defined ifdef since the beginning of time (or at least history), and has just bitrotted. Nuke it. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 151 ++++---------------------------------------- 1 file changed, 12 insertions(+), 139 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 93d9c2f14ab9..878b9ccc3d6d 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -89,10 +89,6 @@ * phase: unused */ - -/* interrupt coalescing */ -/* #define INT_COAL */ - /* statistics */ #define GDTH_STATISTICS @@ -192,9 +188,6 @@ static u8 DebugState = DEBUG_GDTH; #ifdef GDTH_STATISTICS static u32 max_rq=0, max_index=0, max_sg=0; -#ifdef INT_COAL -static u32 max_int_coal=0; -#endif static u32 act_ints=0, act_ios=0, act_stats=0, act_rq=0; static struct timer_list gdth_timer; #endif @@ -1189,9 +1182,6 @@ static int gdth_search_drives(gdth_ha_str *ha) gdth_arcdl_str *alst; gdth_alist_str *alst2; gdth_oem_str_ioctl *oemstr; -#ifdef INT_COAL - gdth_perf_modes *pmod; -#endif TRACE(("gdth_search_drives() hanum %d\n", ha->hanum)); ok = 0; @@ -1234,35 +1224,6 @@ static int gdth_search_drives(gdth_ha_str *ha) cdev_cnt = (u16)ha->info; ha->fw_vers = ha->service; -#ifdef INT_COAL - if (ha->type == GDT_PCIMPR) { - /* set perf. modes */ - pmod = (gdth_perf_modes *)ha->pscratch; - pmod->version = 1; - pmod->st_mode = 1; /* enable one status buffer */ - *((u64 *)&pmod->st_buff_addr1) = ha->coal_stat_phys; - pmod->st_buff_indx1 = COALINDEX; - pmod->st_buff_addr2 = 0; - pmod->st_buff_u_addr2 = 0; - pmod->st_buff_indx2 = 0; - pmod->st_buff_size = sizeof(gdth_coal_status) * MAXOFFSETS; - pmod->cmd_mode = 0; // disable all cmd buffers - pmod->cmd_buff_addr1 = 0; - pmod->cmd_buff_u_addr1 = 0; - pmod->cmd_buff_indx1 = 0; - pmod->cmd_buff_addr2 = 0; - pmod->cmd_buff_u_addr2 = 0; - pmod->cmd_buff_indx2 = 0; - pmod->cmd_buff_size = 0; - pmod->reserved1 = 0; - pmod->reserved2 = 0; - if (gdth_internal_cmd(ha, CACHESERVICE, GDT_IOCTL, SET_PERF_MODES, - INVALID_CHANNEL,sizeof(gdth_perf_modes))) { - printk("GDT-HA %d: Interrupt coalescing activated\n", ha->hanum); - } - } -#endif - /* detect number of buses - try new IOCTL */ iocr = (gdth_raw_iochan_str *)ha->pscratch; iocr->hdr.version = 0xffffffff; @@ -2538,12 +2499,6 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, u8 IStatus; u16 Service; unsigned long flags = 0; -#ifdef INT_COAL - int coalesced = FALSE; - int next = FALSE; - gdth_coal_status *pcs = NULL; - int act_int_coal = 0; -#endif TRACE(("gdth_interrupt() IRQ %d\n", ha->irq)); @@ -2570,24 +2525,6 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, ++act_ints; #endif -#ifdef INT_COAL - /* See if the fw is returning coalesced status */ - if (IStatus == COALINDEX) { - /* Coalesced status. Setup the initial status - buffer pointer and flags */ - pcs = ha->coal_stat; - coalesced = TRUE; - next = TRUE; - } - - do { - if (coalesced) { - /* For coalesced requests all status - information is found in the status buffer */ - IStatus = (u8)(pcs->status & 0xff); - } -#endif - if (ha->type == GDT_PCI) { dp6_ptr = ha->brd; if (IStatus & 0x80) { /* error flag */ @@ -2620,28 +2557,15 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, dp6m_ptr = ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; -#ifdef INT_COAL - if (coalesced) - ha->status = pcs->ext_status & 0xffff; - else -#endif - ha->status = readw(&dp6m_ptr->i960r.status); + ha->status = readw(&dp6m_ptr->i960r.status); TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); } else /* no error */ ha->status = S_OK; -#ifdef INT_COAL - /* get information */ - if (coalesced) { - ha->info = pcs->info0; - ha->info2 = pcs->info1; - ha->service = (pcs->ext_status >> 16) & 0xffff; - } else -#endif - { - ha->info = readl(&dp6m_ptr->i960r.info[0]); - ha->service = readw(&dp6m_ptr->i960r.service); - ha->info2 = readl(&dp6m_ptr->i960r.info[1]); - } + + ha->info = readl(&dp6m_ptr->i960r.info[0]); + ha->service = readw(&dp6m_ptr->i960r.service); + ha->info2 = readl(&dp6m_ptr->i960r.info[1]); + /* event string */ if (IStatus == ASYNCINDEX) { if (ha->service != SCREENSERVICE && @@ -2656,15 +2580,8 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, } } } -#ifdef INT_COAL - /* Make sure that non coalesced interrupts get cleared - before being handled by gdth_async_event/gdth_sync_event */ - if (!coalesced) -#endif - { - writeb(0xff, &dp6m_ptr->i960r.edoor_reg); - writeb(0, &dp6m_ptr->i960r.sema1_reg); - } + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(0, &dp6m_ptr->i960r.sema1_reg); } else { TRACE2(("gdth_interrupt() unknown controller type\n")); if (!gdth_polling) @@ -2727,31 +2644,6 @@ static irqreturn_t __gdth_interrupt(gdth_ha_str *ha, gdth_scsi_done(scp); } -#ifdef INT_COAL - if (coalesced) { - /* go to the next status in the status buffer */ - ++pcs; -#ifdef GDTH_STATISTICS - ++act_int_coal; - if (act_int_coal > max_int_coal) { - max_int_coal = act_int_coal; - printk("GDT: max_int_coal = %d\n",(u16)max_int_coal); - } -#endif - /* see if there is another status */ - if (pcs->status == 0) - /* Stop the coalesce loop */ - next = FALSE; - } - } while (next); - - /* coalescing only for new GDT_PCIMPR controllers available */ - if (ha->type == GDT_PCIMPR && coalesced) { - writeb(0xff, &dp6m_ptr->i960r.edoor_reg); - writeb(0, &dp6m_ptr->i960r.sema1_reg); - } -#endif - gdth_next(ha); return IRQ_HANDLED; } @@ -4264,15 +4156,6 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) goto out_free_pscratch; ha->msg_phys = scratch_dma_handle; -#ifdef INT_COAL - ha->coal_stat = pci_alloc_consistent(ha->pdev, - sizeof(gdth_coal_status) * MAXOFFSETS, - &scratch_dma_handle); - if (!ha->coal_stat) - goto out_free_pmsg; - ha->coal_stat_phys = scratch_dma_handle; -#endif - ha->scratch_busy = FALSE; ha->req_first = NULL; ha->tid_cnt = pdev->device >= 0x200 ? MAXID : MAX_HDRIVES; @@ -4285,7 +4168,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) error = -ENODEV; if (!gdth_search_drives(ha)) { printk("GDT-PCI %d: Error during device scan\n", ha->hanum); - goto out_free_coal_stat; + goto out_free_pmsg; } if (hdr_channel < 0 || hdr_channel > ha->bus_cnt) @@ -4298,7 +4181,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { printk(KERN_WARNING "GDT-PCI %d: " "Unable to set 32-bit DMA\n", ha->hanum); - goto out_free_coal_stat; + goto out_free_pmsg; } } else { shp->max_cmd_len = 16; @@ -4307,7 +4190,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { printk(KERN_WARNING "GDT-PCI %d: " "Unable to set 64/32-bit DMA\n", ha->hanum); - goto out_free_coal_stat; + goto out_free_pmsg; } } @@ -4320,7 +4203,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) error = scsi_add_host(shp, &pdev->dev); if (error) - goto out_free_coal_stat; + goto out_free_pmsg; list_add_tail(&ha->list, &gdth_instances); pci_set_drvdata(ha->pdev, ha); @@ -4332,12 +4215,7 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) return 0; - out_free_coal_stat: -#ifdef INT_COAL - pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * MAXOFFSETS, - ha->coal_stat, ha->coal_stat_phys); out_free_pmsg: -#endif pci_free_consistent(ha->pdev, sizeof(gdth_msg_str), ha->pmsg, ha->msg_phys); out_free_pscratch: @@ -4369,11 +4247,6 @@ static void gdth_remove_one(gdth_ha_str *ha) if (shp->irq) free_irq(shp->irq,ha); -#ifdef INT_COAL - if (ha->coal_stat) - pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) * - MAXOFFSETS, ha->coal_stat, ha->coal_stat_phys); -#endif if (ha->pscratch) pci_free_consistent(ha->pdev, GDTH_SCRATCH, ha->pscratch, ha->scratch_phys); From bb327df8326eff8b12bfcca39b8e1f25f616b1c3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 12 Dec 2018 08:41:27 +0100 Subject: [PATCH 0162/4277] scsi: gdth: use generic DMA API Switch from the legacy PCI DMA API to the generic DMA API. Also switch to dma_map_single from pci_map_page in one case where this makes the code simpler. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/gdth.c | 60 +++++++++++++++++++--------------------- drivers/scsi/gdth_proc.c | 4 +-- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 878b9ccc3d6d..e7f1dd4f3b66 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -2077,9 +2077,9 @@ static int gdth_fill_cache_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, if (scsi_bufflen(scp)) { cmndinfo->dma_dir = (read_write == 1 ? - PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - sgcnt = pci_map_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp), - cmndinfo->dma_dir); + DMA_TO_DEVICE : DMA_FROM_DEVICE); + sgcnt = dma_map_sg(&ha->pdev->dev, scsi_sglist(scp), + scsi_sg_count(scp), cmndinfo->dma_dir); if (mode64) { struct scatterlist *sl; @@ -2153,8 +2153,6 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b) dma_addr_t sense_paddr; int cmd_index, sgcnt, mode64; u8 t,l; - struct page *page; - unsigned long offset; struct gdth_cmndinfo *cmndinfo; t = scp->device->id; @@ -2196,10 +2194,8 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b) } } else { - page = virt_to_page(scp->sense_buffer); - offset = (unsigned long)scp->sense_buffer & ~PAGE_MASK; - sense_paddr = pci_map_page(ha->pdev,page,offset, - 16,PCI_DMA_FROMDEVICE); + sense_paddr = dma_map_single(&ha->pdev->dev, scp->sense_buffer, 16, + DMA_FROM_DEVICE); cmndinfo->sense_paddr = sense_paddr; cmdp->OpCode = GDT_WRITE; /* always */ @@ -2240,9 +2236,9 @@ static int gdth_fill_raw_cmd(gdth_ha_str *ha, struct scsi_cmnd *scp, u8 b) } if (scsi_bufflen(scp)) { - cmndinfo->dma_dir = PCI_DMA_BIDIRECTIONAL; - sgcnt = pci_map_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp), - cmndinfo->dma_dir); + cmndinfo->dma_dir = DMA_BIDIRECTIONAL; + sgcnt = dma_map_sg(&ha->pdev->dev, scsi_sglist(scp), + scsi_sg_count(scp), cmndinfo->dma_dir); if (mode64) { struct scatterlist *sl; @@ -2750,12 +2746,12 @@ static int gdth_sync_event(gdth_ha_str *ha, int service, u8 index, return 2; } if (scsi_bufflen(scp)) - pci_unmap_sg(ha->pdev, scsi_sglist(scp), scsi_sg_count(scp), + dma_unmap_sg(&ha->pdev->dev, scsi_sglist(scp), scsi_sg_count(scp), cmndinfo->dma_dir); if (cmndinfo->sense_paddr) - pci_unmap_page(ha->pdev, cmndinfo->sense_paddr, 16, - PCI_DMA_FROMDEVICE); + dma_unmap_page(&ha->pdev->dev, cmndinfo->sense_paddr, 16, + DMA_FROM_DEVICE); if (ha->status == S_OK) { cmndinfo->status = S_OK; @@ -3664,8 +3660,9 @@ static int ioc_general(void __user *arg, char *cmnd) return -EINVAL; if (gen.data_len + gen.sense_len > 0) { - buf = pci_alloc_consistent(ha->pdev, - gen.data_len + gen.sense_len, &paddr); + buf = dma_alloc_coherent(&ha->pdev->dev, + gen.data_len + gen.sense_len, &paddr, + GFP_KERNEL); if (!buf) return -EFAULT; @@ -3700,7 +3697,8 @@ static int ioc_general(void __user *arg, char *cmnd) rval = 0; out_free_buf: - pci_free_consistent(ha->pdev, gen.data_len + gen.sense_len, buf, paddr); + dma_free_coherent(&ha->pdev->dev, gen.data_len + gen.sense_len, buf, + paddr); return rval; } @@ -4144,14 +4142,14 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) error = -ENOMEM; - ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH, - &scratch_dma_handle); + ha->pscratch = dma_alloc_coherent(&ha->pdev->dev, GDTH_SCRATCH, + &scratch_dma_handle, GFP_KERNEL); if (!ha->pscratch) goto out_free_irq; ha->scratch_phys = scratch_dma_handle; - ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str), - &scratch_dma_handle); + ha->pmsg = dma_alloc_coherent(&ha->pdev->dev, sizeof(gdth_msg_str), + &scratch_dma_handle, GFP_KERNEL); if (!ha->pmsg) goto out_free_pscratch; ha->msg_phys = scratch_dma_handle; @@ -4178,16 +4176,16 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) /* 64-bit DMA only supported from FW >= x.43 */ if (!(ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT) || !ha->dma64_support) { - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { printk(KERN_WARNING "GDT-PCI %d: " "Unable to set 32-bit DMA\n", ha->hanum); goto out_free_pmsg; } } else { shp->max_cmd_len = 16; - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) { printk("GDT-PCI %d: 64-bit DMA enabled\n", ha->hanum); - } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + } else if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { printk(KERN_WARNING "GDT-PCI %d: " "Unable to set 64/32-bit DMA\n", ha->hanum); goto out_free_pmsg; @@ -4216,10 +4214,10 @@ static int gdth_pci_probe_one(gdth_pci_str *pcistr, gdth_ha_str **ha_out) return 0; out_free_pmsg: - pci_free_consistent(ha->pdev, sizeof(gdth_msg_str), + dma_free_coherent(&ha->pdev->dev, sizeof(gdth_msg_str), ha->pmsg, ha->msg_phys); out_free_pscratch: - pci_free_consistent(ha->pdev, GDTH_SCRATCH, + dma_free_coherent(&ha->pdev->dev, GDTH_SCRATCH, ha->pscratch, ha->scratch_phys); out_free_irq: free_irq(ha->irq, ha); @@ -4248,14 +4246,14 @@ static void gdth_remove_one(gdth_ha_str *ha) free_irq(shp->irq,ha); if (ha->pscratch) - pci_free_consistent(ha->pdev, GDTH_SCRATCH, + dma_free_coherent(&ha->pdev->dev, GDTH_SCRATCH, ha->pscratch, ha->scratch_phys); if (ha->pmsg) - pci_free_consistent(ha->pdev, sizeof(gdth_msg_str), + dma_free_coherent(&ha->pdev->dev, sizeof(gdth_msg_str), ha->pmsg, ha->msg_phys); if (ha->ccb_phys) - pci_unmap_single(ha->pdev,ha->ccb_phys, - sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL); + dma_unmap_single(&ha->pdev->dev, ha->ccb_phys, + sizeof(gdth_cmd_str), DMA_BIDIRECTIONAL); scsi_host_put(shp); } diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index 5a13ccac8dee..381d849726ac 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -236,7 +236,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) seq_puts(m, "\nPhysical Devices:"); flag = FALSE; - buf = pci_alloc_consistent(ha->pdev, size, &paddr); + buf = dma_alloc_coherent(&ha->pdev->dev, size, &paddr, GFP_KERNEL); if (!buf) goto stop_output; for (i = 0; i < ha->bus_cnt; ++i) { @@ -504,7 +504,7 @@ int gdth_show_info(struct seq_file *m, struct Scsi_Host *host) } } } - pci_free_consistent(ha->pdev, size, buf, paddr); + dma_free_coherent(&ha->pdev->dev, size, buf, paddr); for (i = 0; i < MAX_HDRIVES; ++i) { if (!(ha->hdr[i].present)) From 381d66da721219d9c7d6b03b0e79fd30d23500e1 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:24:03 +0800 Subject: [PATCH 0163/4277] scsi: arcmsr: Rename acb structure member roundup_ccbsize to ioqueue_size From Ching Huang Rename acb structure member roundup_ccbsize to ioqueue_size Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr.h | 2 +- drivers/scsi/arcmsr/arcmsr_hba.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h index 9c397a2794d6..256fe9a3ae1e 100644 --- a/drivers/scsi/arcmsr/arcmsr.h +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -739,7 +739,7 @@ struct AdapterControlBlock #define ACB_ADAPTER_TYPE_C 0x00000002 /* hbc L IOP */ #define ACB_ADAPTER_TYPE_D 0x00000003 /* hbd M IOP */ #define ACB_ADAPTER_TYPE_E 0x00000004 /* hba L IOP */ - u32 roundup_ccbsize; + u32 ioqueue_size; struct pci_dev * pdev; struct Scsi_Host * host; unsigned long vir2phy_offset; diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 0f6751b0a633..f3a7855deab5 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -229,7 +229,7 @@ static void arcmsr_free_mu(struct AdapterControlBlock *acb) case ACB_ADAPTER_TYPE_B: case ACB_ADAPTER_TYPE_D: case ACB_ADAPTER_TYPE_E: { - dma_free_coherent(&acb->pdev->dev, acb->roundup_ccbsize, + dma_free_coherent(&acb->pdev->dev, acb->ioqueue_size, acb->dma_coherent2, acb->dma_coherent_handle2); break; } @@ -586,8 +586,8 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_B: { struct MessageUnit_B *reg; - acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_B), 32); - dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->roundup_ccbsize, + acb->ioqueue_size = roundup(sizeof(struct MessageUnit_B), 32); + dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); if (!dma_coherent) { pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no); @@ -616,8 +616,8 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) case ACB_ADAPTER_TYPE_D: { struct MessageUnit_D *reg; - acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_D), 32); - dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->roundup_ccbsize, + acb->ioqueue_size = roundup(sizeof(struct MessageUnit_D), 32); + dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); if (!dma_coherent) { pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no); @@ -658,8 +658,8 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) case ACB_ADAPTER_TYPE_E: { uint32_t completeQ_size; completeQ_size = sizeof(struct deliver_completeQ) * ARCMSR_MAX_HBE_DONEQUEUE + 128; - acb->roundup_ccbsize = roundup(completeQ_size, 32); - dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->roundup_ccbsize, + acb->ioqueue_size = roundup(completeQ_size, 32); + dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); if (!dma_coherent){ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no); @@ -668,7 +668,7 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) acb->dma_coherent_handle2 = dma_coherent_handle; acb->dma_coherent2 = dma_coherent; acb->pCompletionQ = dma_coherent; - acb->completionQ_entry = acb->roundup_ccbsize / sizeof(struct deliver_completeQ); + acb->completionQ_entry = acb->ioqueue_size / sizeof(struct deliver_completeQ); acb->doneq_index = 0; } break; @@ -3787,7 +3787,7 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb) cdb_phyaddr_hi32 = (uint32_t)((dma_coherent_handle >> 16) >> 16); writel(cdb_phyaddr, ®->msgcode_rwbuffer[5]); writel(cdb_phyaddr_hi32, ®->msgcode_rwbuffer[6]); - writel(acb->roundup_ccbsize, ®->msgcode_rwbuffer[7]); + writel(acb->ioqueue_size, ®->msgcode_rwbuffer[7]); writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, ®->inbound_msgaddr0); acb->out_doorbell ^= ARCMSR_HBEMU_DRV2IOP_MESSAGE_CMD_DONE; writel(acb->out_doorbell, ®->iobound_doorbell); From 609d08581a5a27e89a1b027e027caba3f013e8ce Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:27:20 +0800 Subject: [PATCH 0164/4277] scsi: arcmsr: Rename arcmsr_free_mu to arcmsr_free_io_queue From Ching Huang Rename arcmsr_free_mu to arcmsr_free_io_queue Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index f3a7855deab5..f831c13b444e 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -223,7 +223,7 @@ static struct pci_driver arcmsr_pci_driver = { **************************************************************************** */ -static void arcmsr_free_mu(struct AdapterControlBlock *acb) +static void arcmsr_free_io_queue(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_B: @@ -990,7 +990,7 @@ scsi_host_remove: free_ccb_pool: arcmsr_free_ccb_pool(acb); free_hbb_mu: - arcmsr_free_mu(acb); + arcmsr_free_io_queue(acb); unmap_pci_region: arcmsr_unmap_pciregion(acb); pci_release_regs: @@ -1500,7 +1500,7 @@ static void arcmsr_free_pcidev(struct AdapterControlBlock *acb) pdev = acb->pdev; arcmsr_free_irq(pdev, acb); arcmsr_free_ccb_pool(acb); - arcmsr_free_mu(acb); + arcmsr_free_io_queue(acb); arcmsr_unmap_pciregion(acb); pci_release_regions(pdev); scsi_host_put(host); @@ -1558,7 +1558,7 @@ static void arcmsr_remove(struct pci_dev *pdev) } arcmsr_free_irq(pdev, acb); arcmsr_free_ccb_pool(acb); - arcmsr_free_mu(acb); + arcmsr_free_io_queue(acb); arcmsr_unmap_pciregion(acb); pci_release_regions(pdev); scsi_host_put(host); From 222f1189b01ff9df27af7ab12cb028e7f43a7363 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:31:00 +0800 Subject: [PATCH 0165/4277] scsi: arcmsr: Merge arcmsr_alloc_io_queue to arcmsr_alloc_ccb_pool From Ching Huang Merge arcmsr_alloc_io_queue to arcmsr_alloc_ccb_pool Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 127 +++++++++++++++++++------------ 1 file changed, 77 insertions(+), 50 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index f831c13b444e..e1c75ca71753 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -576,6 +576,58 @@ static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb) } } +static void arcmsr_hbaB_assign_regAddr(struct AdapterControlBlock *acb) +{ + struct MessageUnit_B *reg = acb->pmuB; + + if (acb->pdev->device == PCI_DEVICE_ID_ARECA_1203) { + reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_1203); + reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK_1203); + reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_1203); + reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK_1203); + } else { + reg->drv2iop_doorbell= MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL); + reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK); + reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL); + reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK); + } + reg->message_wbuffer = MEM_BASE1(ARCMSR_MESSAGE_WBUFFER); + reg->message_rbuffer = MEM_BASE1(ARCMSR_MESSAGE_RBUFFER); + reg->message_rwbuffer = MEM_BASE1(ARCMSR_MESSAGE_RWBUFFER); +} + +static void arcmsr_hbaD_assign_regAddr(struct AdapterControlBlock *acb) +{ + struct MessageUnit_D *reg = acb->pmuD; + + reg->chip_id = MEM_BASE0(ARCMSR_ARC1214_CHIP_ID); + reg->cpu_mem_config = MEM_BASE0(ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION); + reg->i2o_host_interrupt_mask = MEM_BASE0(ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK); + reg->sample_at_reset = MEM_BASE0(ARCMSR_ARC1214_SAMPLE_RESET); + reg->reset_request = MEM_BASE0(ARCMSR_ARC1214_RESET_REQUEST); + reg->host_int_status = MEM_BASE0(ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS); + reg->pcief0_int_enable = MEM_BASE0(ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE); + reg->inbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE0); + reg->inbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE1); + reg->outbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE0); + reg->outbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE1); + reg->inbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_INBOUND_DOORBELL); + reg->outbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL); + reg->outbound_doorbell_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE); + reg->inboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW); + reg->inboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH); + reg->inboundlist_write_pointer = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER); + reg->outboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW); + reg->outboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH); + reg->outboundlist_copy_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER); + reg->outboundlist_read_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER); + reg->outboundlist_interrupt_cause = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE); + reg->outboundlist_interrupt_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE); + reg->message_wbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_WBUFFER); + reg->message_rbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RBUFFER); + reg->msgcode_rwbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RWBUFFER); +} + static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) { bool rtn = true; @@ -585,7 +637,6 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_B: { - struct MessageUnit_B *reg; acb->ioqueue_size = roundup(sizeof(struct MessageUnit_B), 32); dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); @@ -595,27 +646,11 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) } acb->dma_coherent_handle2 = dma_coherent_handle; acb->dma_coherent2 = dma_coherent; - reg = (struct MessageUnit_B *)dma_coherent; - acb->pmuB = reg; - if (acb->pdev->device == PCI_DEVICE_ID_ARECA_1203) { - reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_1203); - reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK_1203); - reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_1203); - reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK_1203); - } else { - reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL); - reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK); - reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL); - reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK); - } - reg->message_wbuffer = MEM_BASE1(ARCMSR_MESSAGE_WBUFFER); - reg->message_rbuffer = MEM_BASE1(ARCMSR_MESSAGE_RBUFFER); - reg->message_rwbuffer = MEM_BASE1(ARCMSR_MESSAGE_RWBUFFER); + acb->pmuB = (struct MessageUnit_B *)dma_coherent; + arcmsr_hbaB_assign_regAddr(acb); } break; case ACB_ADAPTER_TYPE_D: { - struct MessageUnit_D *reg; - acb->ioqueue_size = roundup(sizeof(struct MessageUnit_D), 32); dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); @@ -625,34 +660,8 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) } acb->dma_coherent_handle2 = dma_coherent_handle; acb->dma_coherent2 = dma_coherent; - reg = (struct MessageUnit_D *)dma_coherent; - acb->pmuD = reg; - reg->chip_id = MEM_BASE0(ARCMSR_ARC1214_CHIP_ID); - reg->cpu_mem_config = MEM_BASE0(ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION); - reg->i2o_host_interrupt_mask = MEM_BASE0(ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK); - reg->sample_at_reset = MEM_BASE0(ARCMSR_ARC1214_SAMPLE_RESET); - reg->reset_request = MEM_BASE0(ARCMSR_ARC1214_RESET_REQUEST); - reg->host_int_status = MEM_BASE0(ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS); - reg->pcief0_int_enable = MEM_BASE0(ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE); - reg->inbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE0); - reg->inbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE1); - reg->outbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE0); - reg->outbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE1); - reg->inbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_INBOUND_DOORBELL); - reg->outbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL); - reg->outbound_doorbell_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE); - reg->inboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW); - reg->inboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH); - reg->inboundlist_write_pointer = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER); - reg->outboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW); - reg->outboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH); - reg->outboundlist_copy_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER); - reg->outboundlist_read_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER); - reg->outboundlist_interrupt_cause = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE); - reg->outboundlist_interrupt_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE); - reg->message_wbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_WBUFFER); - reg->message_rbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RBUFFER); - reg->msgcode_rwbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RWBUFFER); + acb->pmuD = (struct MessageUnit_D *)dma_coherent; + arcmsr_hbaD_assign_regAddr(acb); } break; case ACB_ADAPTER_TYPE_E: { @@ -706,6 +715,7 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb) acb->host->sg_tablesize = max_sg_entrys; roundup_ccbsize = roundup(sizeof(struct CommandControlBlock) + (max_sg_entrys - 1) * sizeof(struct SG64ENTRY), 32); acb->uncache_size = roundup_ccbsize * acb->maxFreeCCB; + acb->uncache_size += acb->ioqueue_size; dma_coherent = dma_alloc_coherent(&pdev->dev, acb->uncache_size, &dma_coherent_handle, GFP_KERNEL); if(!dma_coherent){ printk(KERN_NOTICE "arcmsr%d: dma_alloc_coherent got error\n", acb->host->host_no); @@ -738,6 +748,23 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb) ccb_tmp = (struct CommandControlBlock *)((unsigned long)ccb_tmp + roundup_ccbsize); dma_coherent_handle = dma_coherent_handle + roundup_ccbsize; } + acb->dma_coherent_handle2 = dma_coherent_handle; + acb->dma_coherent2 = ccb_tmp; + switch (acb->adapter_type) { + case ACB_ADAPTER_TYPE_B: + acb->pmuB = (struct MessageUnit_B *)acb->dma_coherent2; + arcmsr_hbaB_assign_regAddr(acb); + break; + case ACB_ADAPTER_TYPE_D: + acb->pmuD = (struct MessageUnit_D *)acb->dma_coherent2; + arcmsr_hbaD_assign_regAddr(acb); + break; + case ACB_ADAPTER_TYPE_E: + acb->pCompletionQ = acb->dma_coherent2; + acb->completionQ_entry = acb->ioqueue_size / sizeof(struct deliver_completeQ); + acb->doneq_index = 0; + break; + } return 0; } @@ -959,9 +986,10 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id) if(!error){ goto free_hbb_mu; } + arcmsr_free_io_queue(acb); error = arcmsr_alloc_ccb_pool(acb); if(error){ - goto free_hbb_mu; + goto unmap_pci_region; } error = scsi_add_host(host, &pdev->dev); if(error){ @@ -989,6 +1017,7 @@ scsi_host_remove: scsi_remove_host(host); free_ccb_pool: arcmsr_free_ccb_pool(acb); + goto unmap_pci_region; free_hbb_mu: arcmsr_free_io_queue(acb); unmap_pci_region: @@ -1500,7 +1529,6 @@ static void arcmsr_free_pcidev(struct AdapterControlBlock *acb) pdev = acb->pdev; arcmsr_free_irq(pdev, acb); arcmsr_free_ccb_pool(acb); - arcmsr_free_io_queue(acb); arcmsr_unmap_pciregion(acb); pci_release_regions(pdev); scsi_host_put(host); @@ -1558,7 +1586,6 @@ static void arcmsr_remove(struct pci_dev *pdev) } arcmsr_free_irq(pdev, acb); arcmsr_free_ccb_pool(acb); - arcmsr_free_io_queue(acb); arcmsr_unmap_pciregion(acb); pci_release_regions(pdev); scsi_host_put(host); From 7860a48686ff3a4d2ef9ed52d16c406b25148ba0 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:34:58 +0800 Subject: [PATCH 0166/4277] scsi: arcmsr: Update arcmsr_alloc_ccb_pool for ccb buffer address above 4GB From Ching Huang Update arcmsr_alloc_ccb_pool for ccb buffer address above 4GB Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr.h | 9 +++++---- drivers/scsi/arcmsr/arcmsr_hba.c | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h index 256fe9a3ae1e..9041edc1af16 100644 --- a/drivers/scsi/arcmsr/arcmsr.h +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -747,6 +747,7 @@ struct AdapterControlBlock uint32_t outbound_int_enable; uint32_t cdb_phyaddr_hi32; uint32_t reg_mu_acc_handle0; + uint64_t cdb_phyadd_hipart; spinlock_t eh_lock; spinlock_t ccblist_lock; spinlock_t postq_lock; @@ -855,11 +856,11 @@ struct AdapterControlBlock ******************************************************************************* */ struct CommandControlBlock{ - /*x32:sizeof struct_CCB=(32+60)byte, x64:sizeof struct_CCB=(64+60)byte*/ + /*x32:sizeof struct_CCB=(64+60)byte, x64:sizeof struct_CCB=(64+60)byte*/ struct list_head list; /*x32: 8byte, x64: 16byte*/ struct scsi_cmnd *pcmd; /*8 bytes pointer of linux scsi command */ struct AdapterControlBlock *acb; /*x32: 4byte, x64: 8byte*/ - uint32_t cdb_phyaddr; /*x32: 4byte, x64: 4byte*/ + unsigned long cdb_phyaddr; /*x32: 4byte, x64: 8byte*/ uint32_t arc_cdb_size; /*x32:4byte,x64:4byte*/ uint16_t ccb_flags; /*x32: 2byte, x64: 2byte*/ #define CCB_FLAG_READ 0x0000 @@ -875,10 +876,10 @@ struct CommandControlBlock{ uint32_t smid; #if BITS_PER_LONG == 64 /* ======================512+64 bytes======================== */ - uint32_t reserved[4]; /*16 byte*/ + uint32_t reserved[3]; /*12 byte*/ #else /* ======================512+32 bytes======================== */ - // uint32_t reserved; /*4 byte*/ + uint32_t reserved[8]; /*32 byte*/ #endif /* ======================================================= */ struct ARCMSR_CDB arcmsr_cdb; diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index e1c75ca71753..5353dbbcb629 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -694,11 +694,11 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb) dma_addr_t dma_coherent_handle; struct CommandControlBlock *ccb_tmp; int i = 0, j = 0; - dma_addr_t cdb_phyaddr; + unsigned long cdb_phyaddr, next_ccb_phy; unsigned long roundup_ccbsize; unsigned long max_xfer_len; unsigned long max_sg_entrys; - uint32_t firm_config_version; + uint32_t firm_config_version, curr_phy_upper32; for (i = 0; i < ARCMSR_MAX_TARGETID; i++) for (j = 0; j < ARCMSR_MAX_TARGETLUN; j++) @@ -726,9 +726,10 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb) memset(dma_coherent, 0, acb->uncache_size); acb->ccbsize = roundup_ccbsize; ccb_tmp = dma_coherent; + curr_phy_upper32 = upper_32_bits(dma_coherent_handle); acb->vir2phy_offset = (unsigned long)dma_coherent - (unsigned long)dma_coherent_handle; for(i = 0; i < acb->maxFreeCCB; i++){ - cdb_phyaddr = dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb); + cdb_phyaddr = (unsigned long)dma_coherent_handle + offsetof(struct CommandControlBlock, arcmsr_cdb); switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: case ACB_ADAPTER_TYPE_B: @@ -744,9 +745,16 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb) ccb_tmp->acb = acb; ccb_tmp->smid = (u32)i << 16; INIT_LIST_HEAD(&ccb_tmp->list); - list_add_tail(&ccb_tmp->list, &acb->ccb_free_list); + next_ccb_phy = dma_coherent_handle + roundup_ccbsize; + if (upper_32_bits(next_ccb_phy) != curr_phy_upper32) { + acb->maxFreeCCB = i; + acb->host->can_queue = i; + break; + } + else + list_add_tail(&ccb_tmp->list, &acb->ccb_free_list); ccb_tmp = (struct CommandControlBlock *)((unsigned long)ccb_tmp + roundup_ccbsize); - dma_coherent_handle = dma_coherent_handle + roundup_ccbsize; + dma_coherent_handle = next_ccb_phy; } acb->dma_coherent_handle2 = dma_coherent_handle; acb->dma_coherent2 = ccb_tmp; @@ -3701,6 +3709,7 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb) cdb_phyaddr = lower_32_bits(dma_coherent_handle); cdb_phyaddr_hi32 = upper_32_bits(dma_coherent_handle); acb->cdb_phyaddr_hi32 = cdb_phyaddr_hi32; + acb->cdb_phyadd_hipart = ((uint64_t)cdb_phyaddr_hi32) << 32; /* *********************************************************************** ** if adapter type B, set window of "post command Q" From 9e386a55e07253f1361223ecf08e78640463032a Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:43:15 +0800 Subject: [PATCH 0167/4277] scsi: arcmsr: Update ACB_ADAPTER_TYPE_A for >4GB ccb addressing From Ching Huang Update ACB_ADAPTER_TYPE_A for >4GB ccb addressing Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 5353dbbcb629..2ff1c9ae63d6 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1382,10 +1382,12 @@ static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, struct Comma static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) { int i = 0; - uint32_t flag_ccb, ccb_cdb_phy; + uint32_t flag_ccb; struct ARCMSR_CDB *pARCMSR_CDB; bool error; struct CommandControlBlock *pCCB; + unsigned long ccb_cdb_phy, cdb_phy_hipart; + switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { @@ -1397,7 +1399,10 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) writel(outbound_intstatus, ®->outbound_intstatus);/*clear interrupt*/ while(((flag_ccb = readl(®->outbound_queueport)) != 0xFFFFFFFF) && (i++ < acb->maxOutstanding)) { - pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/ + ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff; + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; + pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb); error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false; arcmsr_drain_donequeue(acb, pCCB, error); @@ -2333,8 +2338,13 @@ static void arcmsr_hbaA_postqueue_isr(struct AdapterControlBlock *acb) struct ARCMSR_CDB *pARCMSR_CDB; struct CommandControlBlock *pCCB; bool error; + unsigned long cdb_phy_addr; + while ((flag_ccb = readl(®->outbound_queueport)) != 0xFFFFFFFF) { - pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5));/*frame must be 32 bytes aligned*/ + cdb_phy_addr = (flag_ccb << 5) & 0xffffffff; + if (acb->cdb_phyadd_hipart) + cdb_phy_addr = cdb_phy_addr | acb->cdb_phyadd_hipart; + pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + cdb_phy_addr); pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb); error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false; arcmsr_drain_donequeue(acb, pCCB, error); @@ -3258,7 +3268,9 @@ static int arcmsr_hbaA_polling_ccbdone(struct AdapterControlBlock *acb, uint32_t flag_ccb, outbound_intstatus, poll_ccb_done = 0, poll_count = 0; int rtn; bool error; - polling_hba_ccb_retry: + unsigned long ccb_cdb_phy; + +polling_hba_ccb_retry: poll_count++; outbound_intstatus = readl(®->outbound_intstatus) & acb->outbound_int_enable; writel(outbound_intstatus, ®->outbound_intstatus);/*clear interrupt*/ @@ -3276,7 +3288,10 @@ static int arcmsr_hbaA_polling_ccbdone(struct AdapterControlBlock *acb, goto polling_hba_ccb_retry; } } - arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5)); + ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff; + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; + arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb); poll_ccb_done |= (ccb == poll_ccb) ? 1 : 0; if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) { From e66764f276e9dc5985ad1fbb32f7bde24a478b39 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:45:46 +0800 Subject: [PATCH 0168/4277] scsi: arcmsr: Update ACB_ADAPTER_TYPE_B for >4GB ccb addressing From Ching Huang Update ACB_ADAPTER_TYPE_B for >4GB ccb addressing Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 2ff1c9ae63d6..3d2727c0321b 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1418,7 +1418,10 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) flag_ccb = reg->done_qbuffer[i]; if (flag_ccb != 0) { reg->done_qbuffer[i] = 0; - pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/ + ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff; + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; + pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb); error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false; arcmsr_drain_donequeue(acb, pCCB, error); @@ -2358,13 +2361,18 @@ static void arcmsr_hbaB_postqueue_isr(struct AdapterControlBlock *acb) struct ARCMSR_CDB *pARCMSR_CDB; struct CommandControlBlock *pCCB; bool error; + unsigned long cdb_phy_addr; + index = reg->doneq_index; while ((flag_ccb = reg->done_qbuffer[index]) != 0) { - reg->done_qbuffer[index] = 0; - pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+(flag_ccb << 5));/*frame must be 32 bytes aligned*/ + cdb_phy_addr = (flag_ccb << 5) & 0xffffffff; + if (acb->cdb_phyadd_hipart) + cdb_phy_addr = cdb_phy_addr | acb->cdb_phyadd_hipart; + pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + cdb_phy_addr); pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb); error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE0) ? true : false; arcmsr_drain_donequeue(acb, pCCB, error); + reg->done_qbuffer[index] = 0; index++; index %= ARCMSR_MAX_HBB_POSTQUEUE; reg->doneq_index = index; @@ -3329,8 +3337,9 @@ static int arcmsr_hbaB_polling_ccbdone(struct AdapterControlBlock *acb, uint32_t flag_ccb, poll_ccb_done = 0, poll_count = 0; int index, rtn; bool error; - polling_hbb_ccb_retry: + unsigned long ccb_cdb_phy; +polling_hbb_ccb_retry: poll_count++; /* clear doorbell interrupt */ writel(ARCMSR_DOORBELL_INT_CLEAR_PATTERN, reg->iop2drv_doorbell); @@ -3356,7 +3365,10 @@ static int arcmsr_hbaB_polling_ccbdone(struct AdapterControlBlock *acb, index %= ARCMSR_MAX_HBB_POSTQUEUE; reg->doneq_index = index; /* check if command done with no error*/ - arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + (flag_ccb << 5)); + ccb_cdb_phy = (flag_ccb << 5) & 0xffffffff; + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; + arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); ccb = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb); poll_ccb_done |= (ccb == poll_ccb) ? 1 : 0; if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) { From c71ec5511b14dcf90141d334fb5277934b496d14 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:48:16 +0800 Subject: [PATCH 0169/4277] scsi: arcmsr: Update ACB_ADAPTER_TYPE_C for >4GB ccb addressing From Ching Huang Update ACB_ADAPTER_TYPE_C for >4GB ccb addressing Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 3d2727c0321b..39f3cd066034 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1438,7 +1438,9 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) /*need to do*/ flag_ccb = readl(®->outbound_queueport_low); ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0); - pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset+ccb_cdb_phy);/*frame must be 32 bytes aligned*/ + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; + pARCMSR_CDB = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); pCCB = container_of(pARCMSR_CDB, struct CommandControlBlock, arcmsr_cdb); error = (flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false; arcmsr_drain_donequeue(acb, pCCB, error); @@ -1786,12 +1788,8 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr arc_cdb_size = (ccb->arc_cdb_size > 0x300) ? 0x300 : ccb->arc_cdb_size; ccb_post_stamp = (cdb_phyaddr | ((arc_cdb_size - 1) >> 6) | 1); - if (acb->cdb_phyaddr_hi32) { - writel(acb->cdb_phyaddr_hi32, &phbcmu->inbound_queueport_high); - writel(ccb_post_stamp, &phbcmu->inbound_queueport_low); - } else { - writel(ccb_post_stamp, &phbcmu->inbound_queueport_low); - } + writel(upper_32_bits(ccb->cdb_phyaddr), &phbcmu->inbound_queueport_high); + writel(ccb_post_stamp, &phbcmu->inbound_queueport_low); } break; case ACB_ADAPTER_TYPE_D: { @@ -2384,7 +2382,8 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb) struct MessageUnit_C __iomem *phbcmu; struct ARCMSR_CDB *arcmsr_cdb; struct CommandControlBlock *ccb; - uint32_t flag_ccb, ccb_cdb_phy, throttling = 0; + uint32_t flag_ccb, throttling = 0; + unsigned long ccb_cdb_phy; int error; phbcmu = acb->pmuC; @@ -2394,6 +2393,8 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb) while ((flag_ccb = readl(&phbcmu->outbound_queueport_low)) != 0xFFFFFFFF) { ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0); + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); ccb = container_of(arcmsr_cdb, struct CommandControlBlock, @@ -3401,12 +3402,14 @@ static int arcmsr_hbaC_polling_ccbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_ccb) { struct MessageUnit_C __iomem *reg = acb->pmuC; - uint32_t flag_ccb, ccb_cdb_phy; + uint32_t flag_ccb; struct ARCMSR_CDB *arcmsr_cdb; bool error; struct CommandControlBlock *pCCB; uint32_t poll_ccb_done = 0, poll_count = 0; int rtn; + unsigned long ccb_cdb_phy; + polling_hbc_ccb_retry: poll_count++; while (1) { @@ -3425,7 +3428,9 @@ polling_hbc_ccb_retry: } flag_ccb = readl(®->outbound_queueport_low); ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0); - arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy);/*frame must be 32 bytes aligned*/ + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; + arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); pCCB = container_of(arcmsr_cdb, struct CommandControlBlock, arcmsr_cdb); poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0; /* check ifcommand done with no error*/ @@ -3801,7 +3806,6 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb) } break; case ACB_ADAPTER_TYPE_C: { - if (cdb_phyaddr_hi32 != 0) { struct MessageUnit_C __iomem *reg = acb->pmuC; printk(KERN_NOTICE "arcmsr%d: cdb_phyaddr_hi32=0x%x\n", @@ -3816,7 +3820,6 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb) return 1; } } - } break; case ACB_ADAPTER_TYPE_D: { uint32_t __iomem *rwbuffer; From a36ade416db7823eb4af9b29729ba3d4a3bb1304 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:51:14 +0800 Subject: [PATCH 0170/4277] scsi: arcmsr: Update ACB_ADAPTER_TYPE_D for >4GB ccb addressing From Ching Huang Update ACB_ADAPTER_TYPE_D for >4gb ccb addressing Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 39f3cd066034..8cc21347a378 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1468,9 +1468,13 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) ((toggle ^ 0x4000) + 1); doneq_index = pmu->doneq_index; spin_unlock_irqrestore(&acb->doneq_lock, flags); + cdb_phy_hipart = pmu->done_qbuffer[doneq_index & + 0xFFF].addressHigh; addressLow = pmu->done_qbuffer[doneq_index & 0xFFF].addressLow; ccb_cdb_phy = (addressLow & 0xFFFFFFF0); + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; pARCMSR_CDB = (struct ARCMSR_CDB *) (acb->vir2phy_offset + ccb_cdb_phy); pCCB = container_of(pARCMSR_CDB, @@ -1802,8 +1806,8 @@ static void arcmsr_post_ccb(struct AdapterControlBlock *acb, struct CommandContr spin_lock_irqsave(&acb->postq_lock, flags); postq_index = pmu->postq_index; pinbound_srb = (struct InBound_SRB *)&(pmu->post_qbuffer[postq_index & 0xFF]); - pinbound_srb->addressHigh = dma_addr_hi32(cdb_phyaddr); - pinbound_srb->addressLow = dma_addr_lo32(cdb_phyaddr); + pinbound_srb->addressHigh = upper_32_bits(ccb->cdb_phyaddr); + pinbound_srb->addressLow = cdb_phyaddr; pinbound_srb->length = ccb->arc_cdb_size >> 2; arcmsr_cdb->msgContext = dma_addr_lo32(cdb_phyaddr); toggle = postq_index & 0x4000; @@ -2415,12 +2419,12 @@ static void arcmsr_hbaC_postqueue_isr(struct AdapterControlBlock *acb) static void arcmsr_hbaD_postqueue_isr(struct AdapterControlBlock *acb) { u32 outbound_write_pointer, doneq_index, index_stripped, toggle; - uint32_t addressLow, ccb_cdb_phy; + uint32_t addressLow; int error; struct MessageUnit_D *pmu; struct ARCMSR_CDB *arcmsr_cdb; struct CommandControlBlock *ccb; - unsigned long flags; + unsigned long flags, ccb_cdb_phy, cdb_phy_hipart; spin_lock_irqsave(&acb->doneq_lock, flags); pmu = acb->pmuD; @@ -2434,9 +2438,13 @@ static void arcmsr_hbaD_postqueue_isr(struct AdapterControlBlock *acb) pmu->doneq_index = index_stripped ? (index_stripped | toggle) : ((toggle ^ 0x4000) + 1); doneq_index = pmu->doneq_index; + cdb_phy_hipart = pmu->done_qbuffer[doneq_index & + 0xFFF].addressHigh; addressLow = pmu->done_qbuffer[doneq_index & 0xFFF].addressLow; ccb_cdb_phy = (addressLow & 0xFFFFFFF0); + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); ccb = container_of(arcmsr_cdb, @@ -3464,9 +3472,9 @@ static int arcmsr_hbaD_polling_ccbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_ccb) { bool error; - uint32_t poll_ccb_done = 0, poll_count = 0, flag_ccb, ccb_cdb_phy; + uint32_t poll_ccb_done = 0, poll_count = 0, flag_ccb; int rtn, doneq_index, index_stripped, outbound_write_pointer, toggle; - unsigned long flags; + unsigned long flags, ccb_cdb_phy, cdb_phy_hipart; struct ARCMSR_CDB *arcmsr_cdb; struct CommandControlBlock *pCCB; struct MessageUnit_D *pmu = acb->pmuD; @@ -3498,8 +3506,12 @@ polling_hbaD_ccb_retry: ((toggle ^ 0x4000) + 1); doneq_index = pmu->doneq_index; spin_unlock_irqrestore(&acb->doneq_lock, flags); + cdb_phy_hipart = pmu->done_qbuffer[doneq_index & + 0xFFF].addressHigh; flag_ccb = pmu->done_qbuffer[doneq_index & 0xFFF].addressLow; ccb_cdb_phy = (flag_ccb & 0xFFFFFFF0); + if (acb->cdb_phyadd_hipart) + ccb_cdb_phy = ccb_cdb_phy | acb->cdb_phyadd_hipart; arcmsr_cdb = (struct ARCMSR_CDB *)(acb->vir2phy_offset + ccb_cdb_phy); pCCB = container_of(arcmsr_cdb, struct CommandControlBlock, From 7ec7261f66df089beab170441ecb38c5659444ec Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:53:44 +0800 Subject: [PATCH 0171/4277] scsi: arcmsr: Add an option of set dma_mask_64 for ACB_ADAPTER_TYPE_A From Ching Huang Add an option of set dma_mask_64 for ACB_ADAPTER_TYPE_A Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 8cc21347a378..45f1374294eb 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -91,6 +91,10 @@ static int cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN; module_param(cmd_per_lun, int, S_IRUGO); MODULE_PARM_DESC(cmd_per_lun, " device queue depth(1 ~ 128), default is 32"); +static int dma_mask_64 = 0; +module_param(dma_mask_64, int, S_IRUGO); +MODULE_PARM_DESC(dma_mask_64, " set DMA mask to 64 bits(0 ~ 1), dma_mask_64=1(64 bits), =0(32 bits)"); + static int set_date_time = 0; module_param(set_date_time, int, S_IRUGO); MODULE_PARM_DESC(set_date_time, " send date, time to iop(0 ~ 1), set_date_time=1(enable), default(=0) is disable"); From 1d120c6183aa3e53d3fb7fa094db5aa902381d50 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:56:34 +0800 Subject: [PATCH 0172/4277] scsi: arcmsr: Separate 'set dma mask' as a function From Ching Huang Separate 'set dma mask' as a function Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 51 +++++++++++++++++++------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 45f1374294eb..30dff6c3c2d4 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -927,6 +927,31 @@ static void arcmsr_init_set_datetime_timer(struct AdapterControlBlock *pacb) add_timer(&pacb->refresh_timer); } +static int arcmsr_set_dma_mask(struct AdapterControlBlock *acb) +{ + struct pci_dev *pcidev = acb->pdev; + + if (IS_DMA64) { + if (((acb->adapter_type == ACB_ADAPTER_TYPE_A) && !dma_mask_64) || + dma_set_mask(&pcidev->dev, DMA_BIT_MASK(64))) + goto dma32; + if (dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(64)) || + dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64))) { + printk("arcmsr: set DMA 64 mask failed\n"); + return -ENXIO; + } + } else { +dma32: + if (dma_set_mask(&pcidev->dev, DMA_BIT_MASK(32)) || + dma_set_coherent_mask(&pcidev->dev, DMA_BIT_MASK(32)) || + dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32))) { + printk("arcmsr: set DMA 32-bit mask failed\n"); + return -ENXIO; + } + } + return 0; +} + static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct Scsi_Host *host; @@ -941,22 +966,15 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id) if(!host){ goto pci_disable_dev; } - error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); - if(error){ - error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if(error){ - printk(KERN_WARNING - "scsi%d: No suitable DMA mask available\n", - host->host_no); - goto scsi_host_release; - } - } init_waitqueue_head(&wait_q); bus = pdev->bus->number; dev_fun = pdev->devfn; acb = (struct AdapterControlBlock *) host->hostdata; memset(acb,0,sizeof(struct AdapterControlBlock)); acb->pdev = pdev; + acb->adapter_type = id->driver_data; + if (arcmsr_set_dma_mask(acb)) + goto scsi_host_release; acb->host = host; host->max_lun = ARCMSR_MAX_TARGETLUN; host->max_id = ARCMSR_MAX_TARGETID; /*16:8*/ @@ -986,7 +1004,6 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id) ACB_F_MESSAGE_WQBUFFER_READED); acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER; INIT_LIST_HEAD(&acb->ccb_free_list); - acb->adapter_type = id->driver_data; error = arcmsr_remap_pciregion(acb); if(!error){ goto pci_release_regs; @@ -1077,7 +1094,6 @@ static int arcmsr_suspend(struct pci_dev *pdev, pm_message_t state) static int arcmsr_resume(struct pci_dev *pdev) { - int error; struct Scsi_Host *host = pci_get_drvdata(pdev); struct AdapterControlBlock *acb = (struct AdapterControlBlock *)host->hostdata; @@ -1089,15 +1105,8 @@ static int arcmsr_resume(struct pci_dev *pdev) pr_warn("%s: pci_enable_device error\n", __func__); return -ENODEV; } - error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); - if (error) { - error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (error) { - pr_warn("scsi%d: No suitable DMA mask available\n", - host->host_no); - goto controller_unregister; - } - } + if (arcmsr_set_dma_mask(acb)) + goto controller_unregister; pci_set_master(pdev); if (arcmsr_request_irq(pdev, acb) == FAILED) goto controller_stop; From afdda87852efc6d8fa55b17b73f842f13c8f8f0a Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 16:59:48 +0800 Subject: [PATCH 0173/4277] scsi: arcmsr: Fix suspend/resume of ACB_ADAPTER_TYPE_B From Ching Huang Fix suspend/resume of ACB_ADAPTER_TYPE_B Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 30dff6c3c2d4..9f85d5abbb0c 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1110,12 +1110,21 @@ static int arcmsr_resume(struct pci_dev *pdev) pci_set_master(pdev); if (arcmsr_request_irq(pdev, acb) == FAILED) goto controller_stop; - if (acb->adapter_type == ACB_ADAPTER_TYPE_E) { + switch (acb->adapter_type) { + case ACB_ADAPTER_TYPE_B: { + struct MessageUnit_B *reg = acb->pmuB; + reg->post_qbuffer[0] = 0; + reg->postq_index = 0; + reg->doneq_index = 0; + break; + } + case ACB_ADAPTER_TYPE_E: writel(0, &acb->pmuE->host_int_status); writel(ARCMSR_HBEMU_DOORBELL_SYNC, &acb->pmuE->iobound_doorbell); acb->in_doorbell = 0; acb->out_doorbell = 0; acb->doneq_index = 0; + break; } arcmsr_iop_init(acb); arcmsr_init_get_devmap_timer(acb); From ad796f790cc2db13d3899107b19aae4b439a2f9b Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Wed, 19 Dec 2018 17:02:08 +0800 Subject: [PATCH 0174/4277] scsi: arcmsr: Update driver version to v1.40.00.10-20181217 From Ching Huang Update driver version to v1.40.00.10-20181217 Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h index 9041edc1af16..a94c513a57d8 100644 --- a/drivers/scsi/arcmsr/arcmsr.h +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -49,7 +49,7 @@ struct device_attribute; #define ARCMSR_MAX_OUTSTANDING_CMD 1024 #define ARCMSR_DEFAULT_OUTSTANDING_CMD 128 #define ARCMSR_MIN_OUTSTANDING_CMD 32 -#define ARCMSR_DRIVER_VERSION "v1.40.00.09-20180709" +#define ARCMSR_DRIVER_VERSION "v1.40.00.10-20181217" #define ARCMSR_SCSI_INITIATOR_ID 255 #define ARCMSR_MAX_XFER_SECTORS 512 #define ARCMSR_MAX_XFER_SECTORS_B 4096 From ef63464bcf8feaa793e1aee4d3d186dfd1fff05f Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:39 +0800 Subject: [PATCH 0175/4277] scsi: hisi_sas: Create root and device debugfs directories This patch creates root directory at hisi_sas_init() and generates device directory when we probe device driver. And we remove the root directory at hisi_sas_exit(), but recursively delete device directory when we remove device driver. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 9 +++++++++ drivers/scsi/hisi_sas/hisi_sas_main.c | 27 ++++++++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 5 +++++ 3 files changed, 41 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index af291947a54d..3ad87462593b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -332,6 +333,8 @@ struct hisi_hba { u32 phy_state; u32 intr_coal_ticks; /* Time of interrupt coalesce in us */ u32 intr_coal_count; /* Interrupt count to coalesce */ + + struct dentry *debugfs_dir; }; /* Generic HW DMA host memory structures */ @@ -461,6 +464,10 @@ struct hisi_sas_slot_buf_table { }; extern struct scsi_transport_template *hisi_sas_stt; + +extern bool hisi_sas_debugfs_enable; +extern struct dentry *hisi_sas_debugfs_dir; + extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba); extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost); extern void hisi_sas_free(struct hisi_hba *hisi_hba); @@ -493,4 +500,6 @@ extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba); extern u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max); extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba); extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba); +extern void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba); +extern void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index eed7fc5b3389..919cd3bd746a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2457,6 +2457,23 @@ err_out_ha: } EXPORT_SYMBOL_GPL(hisi_sas_probe); +struct dentry *hisi_sas_debugfs_dir; + +void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + + hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev), + hisi_sas_debugfs_dir); +} +EXPORT_SYMBOL_GPL(hisi_sas_debugfs_init); + +void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba) +{ + debugfs_remove_recursive(hisi_hba->debugfs_dir); +} +EXPORT_SYMBOL_GPL(hisi_sas_debugfs_exit); + int hisi_sas_remove(struct platform_device *pdev) { struct sas_ha_struct *sha = platform_get_drvdata(pdev); @@ -2475,18 +2492,28 @@ int hisi_sas_remove(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(hisi_sas_remove); +bool hisi_sas_debugfs_enable; +EXPORT_SYMBOL_GPL(hisi_sas_debugfs_enable); +module_param_named(debugfs_enable, hisi_sas_debugfs_enable, bool, 0444); +MODULE_PARM_DESC(hisi_sas_debugfs_enable, "Enable driver debugfs (default disabled)"); + static __init int hisi_sas_init(void) { hisi_sas_stt = sas_domain_attach_transport(&hisi_sas_transport_ops); if (!hisi_sas_stt) return -ENOMEM; + if (hisi_sas_debugfs_enable) + hisi_sas_debugfs_dir = debugfs_create_dir("hisi_sas", NULL); + return 0; } static __exit void hisi_sas_exit(void) { sas_release_transport(hisi_sas_stt); + + debugfs_remove(hisi_sas_debugfs_dir); } module_init(hisi_sas_init); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index e2420a810e99..a7d9651d1ec3 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2507,6 +2507,9 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) sha->sas_port[i] = &hisi_hba->port[i].sas_port; } + if (hisi_sas_debugfs_enable) + hisi_sas_debugfs_init(hisi_hba); + rc = scsi_add_host(shost, dev); if (rc) goto err_out_ha; @@ -2565,6 +2568,8 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) struct hisi_hba *hisi_hba = sha->lldd_ha; struct Scsi_Host *shost = sha->core.shost; + hisi_sas_debugfs_exit(hisi_hba); + if (timer_pending(&hisi_hba->timer)) del_timer(&hisi_hba->timer); From eb1c2b72b7694c984d520300c901f5fc1fa8ea9e Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:40 +0800 Subject: [PATCH 0176/4277] scsi: hisi_sas: Alloc debugfs snapshot buffer memory for all registers This patch allocates snapshot memory for global reg, port regs, CQ, DQ, IOST, ITCT. When we fail to allocate memory for some registers, we free the memory and set hisi_sas_debugfs_enable as 0 to stop loading debugfs from running. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 15 +++++ drivers/scsi/hisi_sas/hisi_sas_main.c | 76 ++++++++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 8 +++ 3 files changed, 99 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 3ad87462593b..eca7e474ea01 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -221,6 +221,10 @@ struct hisi_sas_slot { u16 idx; }; +struct hisi_sas_debugfs_reg { + int count; +}; + struct hisi_sas_hw { int (*hw_init)(struct hisi_hba *hisi_hba); void (*setup_itct)(struct hisi_hba *hisi_hba, @@ -265,6 +269,9 @@ struct hisi_sas_hw { int max_command_entries; int complete_hdr_size; struct scsi_host_template *sht; + + const struct hisi_sas_debugfs_reg *debugfs_reg_global; + const struct hisi_sas_debugfs_reg *debugfs_reg_port; }; struct hisi_hba { @@ -334,6 +341,14 @@ struct hisi_hba { u32 intr_coal_ticks; /* Time of interrupt coalesce in us */ u32 intr_coal_count; /* Interrupt count to coalesce */ + /* debugfs memories */ + void *debugfs_global_reg; + void *debugfs_port_reg[HISI_SAS_MAX_PHYS]; + void *debugfs_complete_hdr[HISI_SAS_MAX_QUEUES]; + struct hisi_sas_cmd_hdr *debugfs_cmd_hdr[HISI_SAS_MAX_QUEUES]; + struct hisi_sas_iost *debugfs_iost; + struct hisi_sas_itct *debugfs_itct; + struct dentry *debugfs_dir; }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 919cd3bd746a..0b0dbaabcce8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2461,10 +2461,86 @@ struct dentry *hisi_sas_debugfs_dir; void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba) { + int max_command_entries = hisi_hba->hw->max_command_entries; struct device *dev = hisi_hba->dev; + int p, i, c, d; + size_t sz; hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev), hisi_sas_debugfs_dir); + + if (!hisi_hba->debugfs_dir) + return; + + /* Alloc buffer for global */ + sz = hisi_hba->hw->debugfs_reg_global->count * 4; + hisi_hba->debugfs_global_reg = + devm_kmalloc(dev, sz, GFP_KERNEL); + + if (!hisi_hba->debugfs_global_reg) + goto fail_global; + + /* Alloc buffer for port */ + sz = hisi_hba->hw->debugfs_reg_port->count * 4; + for (p = 0; p < hisi_hba->n_phy; p++) { + hisi_hba->debugfs_port_reg[p] = + devm_kmalloc(dev, sz, GFP_KERNEL); + + if (!hisi_hba->debugfs_port_reg[p]) + goto fail_port; + } + + /* Alloc buffer for cq */ + sz = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; + for (c = 0; c < hisi_hba->queue_count; c++) { + hisi_hba->debugfs_complete_hdr[c] = + devm_kmalloc(dev, sz, GFP_KERNEL); + + if (!hisi_hba->debugfs_complete_hdr[c]) + goto fail_cq; + } + + /* Alloc buffer for dq */ + sz = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; + for (d = 0; d < hisi_hba->queue_count; d++) { + hisi_hba->debugfs_cmd_hdr[d] = + devm_kmalloc(dev, sz, GFP_KERNEL); + + if (!hisi_hba->debugfs_cmd_hdr[d]) + goto fail_iost_dq; + } + + /* Alloc buffer for iost */ + sz = max_command_entries * sizeof(struct hisi_sas_iost); + + hisi_hba->debugfs_iost = devm_kmalloc(dev, sz, GFP_KERNEL); + if (!hisi_hba->debugfs_iost) + goto fail_iost_dq; + + /* Alloc buffer for itct */ + /* New memory allocation must be locate before itct */ + sz = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); + + hisi_hba->debugfs_itct = devm_kmalloc(dev, sz, GFP_KERNEL); + if (!hisi_hba->debugfs_itct) + goto fail_itct; + + return; +fail_itct: + devm_kfree(dev, hisi_hba->debugfs_iost); +fail_iost_dq: + for (i = 0; i < d; i++) + devm_kfree(dev, hisi_hba->debugfs_cmd_hdr[i]); +fail_cq: + for (i = 0; i < c; i++) + devm_kfree(dev, hisi_hba->debugfs_complete_hdr[i]); +fail_port: + for (i = 0; i < p; i++) + devm_kfree(dev, hisi_hba->debugfs_port_reg[i]); + devm_kfree(dev, hisi_hba->debugfs_global_reg); +fail_global: + debugfs_remove_recursive(hisi_hba->debugfs_dir); + dev_dbg(dev, "failed to init debugfs!\n"); } EXPORT_SYMBOL_GPL(hisi_sas_debugfs_init); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index a7d9651d1ec3..c1b47022d6fd 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2332,6 +2332,12 @@ static struct device_attribute *host_attrs_v3_hw[] = { NULL }; +static const struct hisi_sas_debugfs_reg debugfs_port_reg = { +}; + +static const struct hisi_sas_debugfs_reg debugfs_global_reg = { +}; + static struct scsi_host_template sht_v3_hw = { .name = DRV_NAME, .module = THIS_MODULE, @@ -2380,6 +2386,8 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .get_events = phy_get_events_v3_hw, .write_gpio = write_gpio_v3_hw, .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw, + .debugfs_reg_global = &debugfs_global_reg, + .debugfs_reg_port = &debugfs_port_reg, }; static struct Scsi_Host * From 49159a5e4175f9f7a5149e7e27f583de04e934c2 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:41 +0800 Subject: [PATCH 0177/4277] scsi: hisi_sas: Take debugfs snapshot for all regs This patch takes snapshot for global regs, port regs, CQ, DQ, IOST, ITCT. Add code for snapshot trig and generate dump directory. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 15 ++- drivers/scsi/hisi_sas/hisi_sas_main.c | 125 +++++++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 9 +- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 37 +++++++- 4 files changed, 180 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index eca7e474ea01..3b5fd6d638f0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -223,6 +223,12 @@ struct hisi_sas_slot { struct hisi_sas_debugfs_reg { int count; + int base_off; + union { + u32 (*read_global_reg)(struct hisi_hba *hisi_hba, u32 off); + u32 (*read_port_reg)(struct hisi_hba *hisi_hba, int port, + u32 off); + }; }; struct hisi_sas_hw { @@ -264,8 +270,10 @@ struct hisi_sas_hw { u32 (*get_phys_state)(struct hisi_hba *hisi_hba); int (*write_gpio)(struct hisi_hba *hisi_hba, u8 reg_type, u8 reg_index, u8 reg_count, u8 *write_data); - void (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba, - int delay_ms, int timeout_ms); + int (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms); + void (*snapshot_prepare)(struct hisi_hba *hisi_hba); + void (*snapshot_restore)(struct hisi_hba *hisi_hba); int max_command_entries; int complete_hdr_size; struct scsi_host_template *sht; @@ -337,6 +345,7 @@ struct hisi_hba { const struct hisi_sas_hw *hw; /* Low level hw interface */ unsigned long sata_dev_bitmap[BITS_TO_LONGS(HISI_SAS_MAX_DEVICES)]; struct work_struct rst_work; + struct work_struct debugfs_work; u32 phy_state; u32 intr_coal_ticks; /* Time of interrupt coalesce in us */ u32 intr_coal_count; /* Interrupt count to coalesce */ @@ -350,6 +359,7 @@ struct hisi_hba { struct hisi_sas_itct *debugfs_itct; struct dentry *debugfs_dir; + struct dentry *debugfs_dump_dentry; }; /* Generic HW DMA host memory structures */ @@ -517,4 +527,5 @@ extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba); extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba); extern void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba); extern void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba); +extern void hisi_sas_debugfs_work_handler(struct work_struct *work); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 0b0dbaabcce8..742f71a8412a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1429,6 +1429,10 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) struct Scsi_Host *shost = hisi_hba->shost; int rc; + if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct && + !hisi_hba->debugfs_dump_dentry) + queue_work(hisi_hba->wq, &hisi_hba->debugfs_work); + if (!hisi_hba->hw->soft_reset) return -1; @@ -1923,6 +1927,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, slot->task = NULL; } dev_err(dev, "internal task abort: timeout and not done.\n"); + res = -EIO; goto exit; } else @@ -2459,6 +2464,126 @@ EXPORT_SYMBOL_GPL(hisi_sas_probe); struct dentry *hisi_sas_debugfs_dir; +static void hisi_sas_debugfs_snapshot_cq_reg(struct hisi_hba *hisi_hba) +{ + int queue_entry_size = hisi_hba->hw->complete_hdr_size; + int i; + + for (i = 0; i < hisi_hba->queue_count; i++) + memcpy(hisi_hba->debugfs_complete_hdr[i], + hisi_hba->complete_hdr[i], + HISI_SAS_QUEUE_SLOTS * queue_entry_size); +} + +static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba) +{ + int queue_entry_size = hisi_hba->hw->complete_hdr_size; + int i; + + for (i = 0; i < hisi_hba->queue_count; i++) + memcpy(hisi_hba->debugfs_cmd_hdr[i], + hisi_hba->cmd_hdr[i], + HISI_SAS_QUEUE_SLOTS * queue_entry_size); +} + +static void hisi_sas_debugfs_snapshot_port_reg(struct hisi_hba *hisi_hba) +{ + const struct hisi_sas_debugfs_reg *port = + hisi_hba->hw->debugfs_reg_port; + int i, phy_cnt; + u32 offset; + u32 *databuf; + + for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) { + databuf = (u32 *)hisi_hba->debugfs_port_reg[phy_cnt]; + for (i = 0; i < port->count; i++, databuf++) { + offset = port->base_off + 4 * i; + *databuf = port->read_port_reg(hisi_hba, phy_cnt, + offset); + } + } +} + +static void hisi_sas_debugfs_snapshot_global_reg(struct hisi_hba *hisi_hba) +{ + u32 *databuf = (u32 *)hisi_hba->debugfs_global_reg; + const struct hisi_sas_debugfs_reg *global = + hisi_hba->hw->debugfs_reg_global; + int i; + + for (i = 0; i < global->count; i++, databuf++) + *databuf = global->read_global_reg(hisi_hba, 4 * i); +} + +static void hisi_sas_debugfs_snapshot_itct_reg(struct hisi_hba *hisi_hba) +{ + void *databuf = hisi_hba->debugfs_itct; + struct hisi_sas_itct *itct; + int i; + + itct = hisi_hba->itct; + + for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) { + memcpy(databuf, itct, sizeof(struct hisi_sas_itct)); + databuf += sizeof(struct hisi_sas_itct); + } +} + +static void hisi_sas_debugfs_snapshot_iost_reg(struct hisi_hba *hisi_hba) +{ + int max_command_entries = hisi_hba->hw->max_command_entries; + void *databuf = hisi_hba->debugfs_iost; + struct hisi_sas_iost *iost; + int i; + + iost = hisi_hba->iost; + + for (i = 0; i < max_command_entries; i++, iost++) { + memcpy(databuf, iost, sizeof(struct hisi_sas_iost)); + databuf += sizeof(struct hisi_sas_iost); + } +} + +static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) +{ + struct dentry *dump_dentry; + + /* Create dump dir inside device dir */ + dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir); + if (!dump_dentry) + goto fail; + + hisi_hba->debugfs_dump_dentry = dump_dentry; + return; +fail: + debugfs_remove_recursive(hisi_hba->debugfs_dir); +} + +static void hisi_sas_debugfs_snapshot_regs(struct hisi_hba *hisi_hba) +{ + hisi_hba->hw->snapshot_prepare(hisi_hba); + + hisi_sas_debugfs_snapshot_global_reg(hisi_hba); + hisi_sas_debugfs_snapshot_port_reg(hisi_hba); + hisi_sas_debugfs_snapshot_cq_reg(hisi_hba); + hisi_sas_debugfs_snapshot_dq_reg(hisi_hba); + hisi_sas_debugfs_snapshot_itct_reg(hisi_hba); + hisi_sas_debugfs_snapshot_iost_reg(hisi_hba); + + hisi_sas_debugfs_create_files(hisi_hba); + + hisi_hba->hw->snapshot_restore(hisi_hba); +} + +void hisi_sas_debugfs_work_handler(struct work_struct *work) +{ + struct hisi_hba *hisi_hba = + container_of(work, struct hisi_hba, debugfs_work); + + hisi_sas_debugfs_snapshot_regs(hisi_hba); +} +EXPORT_SYMBOL_GPL(hisi_sas_debugfs_work_handler); + void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba) { int max_command_entries = hisi_hba->hw->max_command_entries; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index c8ebff3ba559..5c4b8dff4c10 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3542,8 +3542,8 @@ static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type, return 0; } -static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba, - int delay_ms, int timeout_ms) +static int wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms) { struct device *dev = hisi_hba->dev; int entries, entries_old = 0, time; @@ -3557,7 +3557,12 @@ static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba, msleep(delay_ms); } + if (time >= timeout_ms) + return -ETIMEDOUT; + dev_dbg(dev, "wait commands complete %dms\n", time); + + return 0; } static struct device_attribute *host_attrs_v2_hw[] = { diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index c1b47022d6fd..5727c265abc7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2201,8 +2201,8 @@ static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type, return 0; } -static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba, - int delay_ms, int timeout_ms) +static int wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms) { struct device *dev = hisi_hba->dev; int entries, entries_old = 0, time; @@ -2216,7 +2216,12 @@ static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba, msleep(delay_ms); } + if (time >= timeout_ms) + return -ETIMEDOUT; + dev_dbg(dev, "wait commands complete %dms\n", time); + + return 0; } static ssize_t intr_conv_v3_hw_show(struct device *dev, @@ -2333,11 +2338,36 @@ static struct device_attribute *host_attrs_v3_hw[] = { }; static const struct hisi_sas_debugfs_reg debugfs_port_reg = { + .base_off = PORT_BASE, + .read_port_reg = hisi_sas_phy_read32, }; static const struct hisi_sas_debugfs_reg debugfs_global_reg = { + .read_global_reg = hisi_sas_read32, }; +static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + + set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); + + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); + + if (wait_cmds_complete_timeout_v3_hw(hisi_hba, 100, 5000) == -ETIMEDOUT) + dev_dbg(dev, "Wait commands complete timeout!\n"); + + hisi_sas_kill_tasklets(hisi_hba); +} + +static void debugfs_snapshot_restore_v3_hw(struct hisi_hba *hisi_hba) +{ + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, + (u32)((1ULL << hisi_hba->queue_count) - 1)); + + clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); +} + static struct scsi_host_template sht_v3_hw = { .name = DRV_NAME, .module = THIS_MODULE, @@ -2388,6 +2418,8 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw, .debugfs_reg_global = &debugfs_global_reg, .debugfs_reg_port = &debugfs_port_reg, + .snapshot_prepare = debugfs_snapshot_prepare_v3_hw, + .snapshot_restore = debugfs_snapshot_restore_v3_hw, }; static struct Scsi_Host * @@ -2405,6 +2437,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev) hisi_hba = shost_priv(shost); INIT_WORK(&hisi_hba->rst_work, hisi_sas_rst_work_handler); + INIT_WORK(&hisi_hba->debugfs_work, hisi_sas_debugfs_work_handler); hisi_hba->hw = &hisi_sas_v3_hw; hisi_hba->pci_dev = pdev; hisi_hba->dev = dev; From caefac1996764d19e4fa39d8d33651da257dcab2 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:42 +0800 Subject: [PATCH 0178/4277] scsi: hisi_sas: Debugfs global register create file and add file operations This patch create debugfs file for global register and add file operations. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 8 +++ drivers/scsi/hisi_sas/hisi_sas_main.c | 64 ++++++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 69 ++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 3b5fd6d638f0..6a1a5adc0878 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -221,7 +221,15 @@ struct hisi_sas_slot { u16 idx; }; +#define HISI_SAS_DEBUGFS_REG(x) {#x, x} + +struct hisi_sas_debugfs_reg_lu { + char *name; + int off; +}; + struct hisi_sas_debugfs_reg { + const struct hisi_sas_debugfs_reg_lu *lu; int count; int base_off; union { diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 742f71a8412a..fd85934d241f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2544,6 +2544,66 @@ static void hisi_sas_debugfs_snapshot_iost_reg(struct hisi_hba *hisi_hba) } } +const char * +hisi_sas_debugfs_to_reg_name(int off, int base_off, + const struct hisi_sas_debugfs_reg_lu *lu) +{ + for (; lu->name; lu++) { + if (off == lu->off - base_off) + return lu->name; + } + + return NULL; +} + +static void hisi_sas_debugfs_print_reg(u32 *regs_val, const void *ptr, + struct seq_file *s) +{ + const struct hisi_sas_debugfs_reg *reg = ptr; + int i; + + for (i = 0; i < reg->count; i++) { + int off = i * 4; + const char *name; + + name = hisi_sas_debugfs_to_reg_name(off, reg->base_off, + reg->lu); + + if (name) + seq_printf(s, "0x%08x 0x%08x %s\n", off, + le32_to_cpu(regs_val[i]), name); + else + seq_printf(s, "0x%08x 0x%08x\n", off, + le32_to_cpu(regs_val[i])); + } +} + +static int hisi_sas_debugfs_global_show(struct seq_file *s, void *p) +{ + struct hisi_hba *hisi_hba = s->private; + const struct hisi_sas_hw *hw = hisi_hba->hw; + const struct hisi_sas_debugfs_reg *reg_global = hw->debugfs_reg_global; + + hisi_sas_debugfs_print_reg((u32 *)hisi_hba->debugfs_global_reg, + reg_global, s); + + return 0; +} + +static int hisi_sas_debugfs_global_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hisi_sas_debugfs_global_show, + inode->i_private); +} + +static const struct file_operations hisi_sas_debugfs_global_fops = { + .open = hisi_sas_debugfs_global_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) { struct dentry *dump_dentry; @@ -2554,6 +2614,10 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) goto fail; hisi_hba->debugfs_dump_dentry = dump_dentry; + + if (!debugfs_create_file("global", 0400, dump_dentry, hisi_hba, + &hisi_sas_debugfs_global_fops)) + goto fail; return; fail: debugfs_remove_recursive(hisi_hba->debugfs_dir); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 5727c265abc7..4265454ca805 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2342,7 +2342,76 @@ static const struct hisi_sas_debugfs_reg debugfs_port_reg = { .read_port_reg = hisi_sas_phy_read32, }; +static const struct hisi_sas_debugfs_reg_lu debugfs_global_reg_lu[] = { + HISI_SAS_DEBUGFS_REG(DLVRY_QUEUE_ENABLE), + HISI_SAS_DEBUGFS_REG(PHY_CONTEXT), + HISI_SAS_DEBUGFS_REG(PHY_STATE), + HISI_SAS_DEBUGFS_REG(PHY_PORT_NUM_MA), + HISI_SAS_DEBUGFS_REG(PHY_CONN_RATE), + HISI_SAS_DEBUGFS_REG(ITCT_CLR), + HISI_SAS_DEBUGFS_REG(IO_SATA_BROKEN_MSG_ADDR_LO), + HISI_SAS_DEBUGFS_REG(IO_SATA_BROKEN_MSG_ADDR_HI), + HISI_SAS_DEBUGFS_REG(SATA_INITI_D2H_STORE_ADDR_LO), + HISI_SAS_DEBUGFS_REG(SATA_INITI_D2H_STORE_ADDR_HI), + HISI_SAS_DEBUGFS_REG(CFG_MAX_TAG), + HISI_SAS_DEBUGFS_REG(HGC_SAS_TX_OPEN_FAIL_RETRY_CTRL), + HISI_SAS_DEBUGFS_REG(HGC_SAS_TXFAIL_RETRY_CTRL), + HISI_SAS_DEBUGFS_REG(HGC_GET_ITV_TIME), + HISI_SAS_DEBUGFS_REG(DEVICE_MSG_WORK_MODE), + HISI_SAS_DEBUGFS_REG(OPENA_WT_CONTI_TIME), + HISI_SAS_DEBUGFS_REG(I_T_NEXUS_LOSS_TIME), + HISI_SAS_DEBUGFS_REG(MAX_CON_TIME_LIMIT_TIME), + HISI_SAS_DEBUGFS_REG(BUS_INACTIVE_LIMIT_TIME), + HISI_SAS_DEBUGFS_REG(REJECT_TO_OPEN_LIMIT_TIME), + HISI_SAS_DEBUGFS_REG(CQ_INT_CONVERGE_EN), + HISI_SAS_DEBUGFS_REG(CFG_AGING_TIME), + HISI_SAS_DEBUGFS_REG(HGC_DFX_CFG2), + HISI_SAS_DEBUGFS_REG(CFG_ABT_SET_QUERY_IPTT), + HISI_SAS_DEBUGFS_REG(CFG_ABT_SET_IPTT_DONE), + HISI_SAS_DEBUGFS_REG(HGC_IOMB_PROC1_STATUS), + HISI_SAS_DEBUGFS_REG(CHNL_INT_STATUS), + HISI_SAS_DEBUGFS_REG(HGC_AXI_FIFO_ERR_INFO), + HISI_SAS_DEBUGFS_REG(INT_COAL_EN), + HISI_SAS_DEBUGFS_REG(OQ_INT_COAL_TIME), + HISI_SAS_DEBUGFS_REG(OQ_INT_COAL_CNT), + HISI_SAS_DEBUGFS_REG(ENT_INT_COAL_TIME), + HISI_SAS_DEBUGFS_REG(ENT_INT_COAL_CNT), + HISI_SAS_DEBUGFS_REG(OQ_INT_SRC), + HISI_SAS_DEBUGFS_REG(OQ_INT_SRC_MSK), + HISI_SAS_DEBUGFS_REG(ENT_INT_SRC1), + HISI_SAS_DEBUGFS_REG(ENT_INT_SRC2), + HISI_SAS_DEBUGFS_REG(ENT_INT_SRC3), + HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK1), + HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK2), + HISI_SAS_DEBUGFS_REG(ENT_INT_SRC_MSK3), + HISI_SAS_DEBUGFS_REG(CHNL_PHYUPDOWN_INT_MSK), + HISI_SAS_DEBUGFS_REG(CHNL_ENT_INT_MSK), + HISI_SAS_DEBUGFS_REG(HGC_COM_INT_MSK), + HISI_SAS_DEBUGFS_REG(SAS_ECC_INTR), + HISI_SAS_DEBUGFS_REG(SAS_ECC_INTR_MSK), + HISI_SAS_DEBUGFS_REG(HGC_ERR_STAT_EN), + HISI_SAS_DEBUGFS_REG(CQE_SEND_CNT), + HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_DEPTH), + HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_WR_PTR), + HISI_SAS_DEBUGFS_REG(DLVRY_Q_0_RD_PTR), + HISI_SAS_DEBUGFS_REG(HYPER_STREAM_ID_EN_CFG), + HISI_SAS_DEBUGFS_REG(OQ0_INT_SRC_MSK), + HISI_SAS_DEBUGFS_REG(COMPL_Q_0_DEPTH), + HISI_SAS_DEBUGFS_REG(COMPL_Q_0_WR_PTR), + HISI_SAS_DEBUGFS_REG(COMPL_Q_0_RD_PTR), + HISI_SAS_DEBUGFS_REG(AWQOS_AWCACHE_CFG), + HISI_SAS_DEBUGFS_REG(ARQOS_ARCACHE_CFG), + HISI_SAS_DEBUGFS_REG(HILINK_ERR_DFX), + HISI_SAS_DEBUGFS_REG(SAS_GPIO_CFG_0), + HISI_SAS_DEBUGFS_REG(SAS_GPIO_CFG_1), + HISI_SAS_DEBUGFS_REG(SAS_GPIO_TX_0_1), + HISI_SAS_DEBUGFS_REG(SAS_CFG_DRIVE_VLD), + {} +}; + static const struct hisi_sas_debugfs_reg debugfs_global_reg = { + .lu = debugfs_global_reg_lu, + .count = 0x800, .read_global_reg = hisi_sas_read32, }; From 61a6ebf3f584288e68254ccaddc536fcc602e5ae Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:43 +0800 Subject: [PATCH 0179/4277] scsi: hisi_sas: Add debugfs for port registers This patch create debugfs file for port register and add file operations. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 43 ++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 55 ++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index fd85934d241f..fbd9b4aab628 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2604,9 +2604,38 @@ static const struct file_operations hisi_sas_debugfs_global_fops = { .owner = THIS_MODULE, }; +static int hisi_sas_debugfs_port_show(struct seq_file *s, void *p) +{ + struct hisi_sas_phy *phy = s->private; + struct hisi_hba *hisi_hba = phy->hisi_hba; + const struct hisi_sas_hw *hw = hisi_hba->hw; + const struct hisi_sas_debugfs_reg *reg_port = hw->debugfs_reg_port; + u32 *databuf = hisi_hba->debugfs_port_reg[phy->sas_phy.id]; + + hisi_sas_debugfs_print_reg(databuf, reg_port, s); + + return 0; +} + +static int hisi_sas_debugfs_port_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hisi_sas_debugfs_port_show, inode->i_private); +} + +static const struct file_operations hisi_sas_debugfs_port_fops = { + .open = hisi_sas_debugfs_port_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) { struct dentry *dump_dentry; + struct dentry *dentry; + char name[256]; + int p; /* Create dump dir inside device dir */ dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir); @@ -2618,6 +2647,20 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) if (!debugfs_create_file("global", 0400, dump_dentry, hisi_hba, &hisi_sas_debugfs_global_fops)) goto fail; + + /* Create port dir and files */ + dentry = debugfs_create_dir("port", dump_dentry); + if (!dentry) + goto fail; + + for (p = 0; p < hisi_hba->n_phy; p++) { + snprintf(name, 256, "%d", p); + if (!debugfs_create_file(name, 0400, dentry, + &hisi_hba->phy[p], + &hisi_sas_debugfs_port_fops)) + goto fail; + } + return; fail: debugfs_remove_recursive(hisi_hba->debugfs_dir); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 4265454ca805..b780b5269f0f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -186,6 +186,7 @@ #define CHL_INT0_MSK (PORT_BASE + 0x1c0) #define CHL_INT1_MSK (PORT_BASE + 0x1c4) #define CHL_INT2_MSK (PORT_BASE + 0x1c8) +#define SAS_EC_INT_COAL_TIME (PORT_BASE + 0x1cc) #define CHL_INT_COAL_EN (PORT_BASE + 0x1d0) #define SAS_RX_TRAIN_TIMER (PORT_BASE + 0x2a4) #define PHY_CTRL_RDY_MSK (PORT_BASE + 0x2b0) @@ -205,6 +206,7 @@ #define ERR_CNT_DWS_LOST (PORT_BASE + 0x380) #define ERR_CNT_RESET_PROB (PORT_BASE + 0x384) #define ERR_CNT_INVLD_DW (PORT_BASE + 0x390) +#define ERR_CNT_CODE_ERR (PORT_BASE + 0x394) #define ERR_CNT_DISP_ERR (PORT_BASE + 0x398) #define DEFAULT_ITCT_HW 2048 /* reset value, not reprogrammed */ @@ -2337,7 +2339,60 @@ static struct device_attribute *host_attrs_v3_hw[] = { NULL }; +static const struct hisi_sas_debugfs_reg_lu debugfs_port_reg_lu[] = { + HISI_SAS_DEBUGFS_REG(PHY_CFG), + HISI_SAS_DEBUGFS_REG(HARD_PHY_LINKRATE), + HISI_SAS_DEBUGFS_REG(PROG_PHY_LINK_RATE), + HISI_SAS_DEBUGFS_REG(PHY_CTRL), + HISI_SAS_DEBUGFS_REG(SL_CFG), + HISI_SAS_DEBUGFS_REG(AIP_LIMIT), + HISI_SAS_DEBUGFS_REG(SL_CONTROL), + HISI_SAS_DEBUGFS_REG(RX_PRIMS_STATUS), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD0), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD1), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD2), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD3), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD4), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD5), + HISI_SAS_DEBUGFS_REG(TX_ID_DWORD6), + HISI_SAS_DEBUGFS_REG(TXID_AUTO), + HISI_SAS_DEBUGFS_REG(RX_IDAF_DWORD0), + HISI_SAS_DEBUGFS_REG(RXOP_CHECK_CFG_H), + HISI_SAS_DEBUGFS_REG(STP_LINK_TIMER), + HISI_SAS_DEBUGFS_REG(STP_LINK_TIMEOUT_STATE), + HISI_SAS_DEBUGFS_REG(CON_CFG_DRIVER), + HISI_SAS_DEBUGFS_REG(SAS_SSP_CON_TIMER_CFG), + HISI_SAS_DEBUGFS_REG(SAS_SMP_CON_TIMER_CFG), + HISI_SAS_DEBUGFS_REG(SAS_STP_CON_TIMER_CFG), + HISI_SAS_DEBUGFS_REG(CHL_INT0), + HISI_SAS_DEBUGFS_REG(CHL_INT1), + HISI_SAS_DEBUGFS_REG(CHL_INT2), + HISI_SAS_DEBUGFS_REG(CHL_INT0_MSK), + HISI_SAS_DEBUGFS_REG(CHL_INT1_MSK), + HISI_SAS_DEBUGFS_REG(CHL_INT2_MSK), + HISI_SAS_DEBUGFS_REG(SAS_EC_INT_COAL_TIME), + HISI_SAS_DEBUGFS_REG(CHL_INT_COAL_EN), + HISI_SAS_DEBUGFS_REG(SAS_RX_TRAIN_TIMER), + HISI_SAS_DEBUGFS_REG(PHY_CTRL_RDY_MSK), + HISI_SAS_DEBUGFS_REG(PHYCTRL_NOT_RDY_MSK), + HISI_SAS_DEBUGFS_REG(PHYCTRL_DWS_RESET_MSK), + HISI_SAS_DEBUGFS_REG(PHYCTRL_PHY_ENA_MSK), + HISI_SAS_DEBUGFS_REG(SL_RX_BCAST_CHK_MSK), + HISI_SAS_DEBUGFS_REG(PHYCTRL_OOB_RESTART_MSK), + HISI_SAS_DEBUGFS_REG(DMA_TX_STATUS), + HISI_SAS_DEBUGFS_REG(DMA_RX_STATUS), + HISI_SAS_DEBUGFS_REG(COARSETUNE_TIME), + HISI_SAS_DEBUGFS_REG(ERR_CNT_DWS_LOST), + HISI_SAS_DEBUGFS_REG(ERR_CNT_RESET_PROB), + HISI_SAS_DEBUGFS_REG(ERR_CNT_INVLD_DW), + HISI_SAS_DEBUGFS_REG(ERR_CNT_CODE_ERR), + HISI_SAS_DEBUGFS_REG(ERR_CNT_DISP_ERR), + {} +}; + static const struct hisi_sas_debugfs_reg debugfs_port_reg = { + .lu = debugfs_port_reg_lu, + .count = 0x100, .base_off = PORT_BASE, .read_port_reg = hisi_sas_phy_read32, }; From 971afae7cf4f784c37ea89459c1b3627bcf601ff Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:44 +0800 Subject: [PATCH 0180/4277] scsi: hisi_sas: Add debugfs CQ file and add file operations This patch create debugfs file for CQ and add file operations. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index fbd9b4aab628..ce0a9dcc355b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2630,12 +2630,68 @@ static const struct file_operations hisi_sas_debugfs_port_fops = { .owner = THIS_MODULE, }; +static int hisi_sas_show_row_32(struct seq_file *s, int index, + int sz, u32 *ptr) +{ + int i; + + /* completion header size not fixed per HW version */ + seq_printf(s, "index %04d:\n\t", index); + for (i = 1; i <= sz / 4; i++, ptr++) { + if (!(i % 4)) + seq_puts(s, "\n\t"); + } + seq_puts(s, "\n"); + + return 0; +} + +static int hisi_sas_cq_show_slot(struct seq_file *s, int slot, void *cq_ptr) +{ + struct hisi_sas_cq *cq = cq_ptr; + struct hisi_hba *hisi_hba = cq->hisi_hba; + void *complete_queue = hisi_hba->debugfs_complete_hdr[cq->id]; + void *complete_hdr = complete_queue + + (hisi_hba->hw->complete_hdr_size * slot); + + return hisi_sas_show_row_32(s, slot, + hisi_hba->hw->complete_hdr_size, + complete_hdr); +} + +static int hisi_sas_debugfs_cq_show(struct seq_file *s, void *p) +{ + struct hisi_sas_cq *cq = s->private; + int slot, ret; + + for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) { + ret = hisi_sas_cq_show_slot(s, slot, cq); + if (ret) + return ret; + } + return 0; +} + +static int hisi_sas_debugfs_cq_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hisi_sas_debugfs_cq_show, inode->i_private); +} + +static const struct file_operations hisi_sas_debugfs_cq_fops = { + .open = hisi_sas_debugfs_cq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) { struct dentry *dump_dentry; struct dentry *dentry; char name[256]; int p; + int c; /* Create dump dir inside device dir */ dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir); @@ -2661,6 +2717,20 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) goto fail; } + /* Create CQ dir and files */ + dentry = debugfs_create_dir("cq", dump_dentry); + if (!dentry) + goto fail; + + for (c = 0; c < hisi_hba->queue_count; c++) { + snprintf(name, 256, "%d", c); + + if (!debugfs_create_file(name, 0400, dentry, + &hisi_hba->cq[c], + &hisi_sas_debugfs_cq_fops)) + goto fail; + } + return; fail: debugfs_remove_recursive(hisi_hba->debugfs_dir); From 148e379f60c5ce0a9741ccdada46ba91579a9df7 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:45 +0800 Subject: [PATCH 0181/4277] scsi: hisi_sas: Add debugfs DQ file and add file operations This patch create debugfs file for DQ and add file operations Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index ce0a9dcc355b..45a0be2411a5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2685,6 +2685,43 @@ static const struct file_operations hisi_sas_debugfs_cq_fops = { .owner = THIS_MODULE, }; +static int hisi_sas_dq_show_slot(struct seq_file *s, int slot, void *dq_ptr) +{ + struct hisi_sas_dq *dq = dq_ptr; + struct hisi_hba *hisi_hba = dq->hisi_hba; + void *cmd_queue = hisi_hba->debugfs_cmd_hdr[dq->id]; + void *cmd_hdr = cmd_queue + + hisi_hba->hw->complete_hdr_size * slot; + + return hisi_sas_show_row_32(s, slot, sizeof(struct hisi_sas_cmd_hdr), + cmd_hdr); +} + +static int hisi_sas_debugfs_dq_show(struct seq_file *s, void *p) +{ + int slot, ret; + + for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) { + ret = hisi_sas_dq_show_slot(s, slot, s->private); + if (ret) + return ret; + } + return 0; +} + +static int hisi_sas_debugfs_dq_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hisi_sas_debugfs_dq_show, inode->i_private); +} + +static const struct file_operations hisi_sas_debugfs_dq_fops = { + .open = hisi_sas_debugfs_dq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) { struct dentry *dump_dentry; @@ -2692,6 +2729,7 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) char name[256]; int p; int c; + int d; /* Create dump dir inside device dir */ dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir); @@ -2731,6 +2769,20 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) goto fail; } + /* Create DQ dir and files */ + dentry = debugfs_create_dir("dq", dump_dentry); + if (!dentry) + goto fail; + + for (d = 0; d < hisi_hba->queue_count; d++) { + snprintf(name, 256, "%d", d); + + if (!debugfs_create_file(name, 0400, dentry, + &hisi_hba->dq[d], + &hisi_sas_debugfs_dq_fops)) + goto fail; + } + return; fail: debugfs_remove_recursive(hisi_hba->debugfs_dir); From 1afb4b8524797f95b15480a6654780571cde4e56 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 19 Dec 2018 23:56:46 +0800 Subject: [PATCH 0182/4277] scsi: hisi_sas: Add debugfs IOST file and add file operations This patch create debugfs file for IOST and add file operations. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 45a0be2411a5..3d24366ceeef 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2630,6 +2630,24 @@ static const struct file_operations hisi_sas_debugfs_port_fops = { .owner = THIS_MODULE, }; +static int hisi_sas_show_row_64(struct seq_file *s, int index, + int sz, u64 *ptr) +{ + int i; + + /* completion header size not fixed per HW version */ + seq_printf(s, "index %04d:\n\t", index); + for (i = 1; i <= sz / 8; i++, ptr++) { + seq_printf(s, " 0x%016llx", le64_to_cpu(*ptr)); + if (!(i % 2)) + seq_puts(s, "\n\t"); + } + + seq_puts(s, "\n"); + + return 0; +} + static int hisi_sas_show_row_32(struct seq_file *s, int index, int sz, u32 *ptr) { @@ -2722,6 +2740,35 @@ static const struct file_operations hisi_sas_debugfs_dq_fops = { .owner = THIS_MODULE, }; +static int hisi_sas_debugfs_iost_show(struct seq_file *s, void *p) +{ + struct hisi_hba *hisi_hba = s->private; + struct hisi_sas_iost *debugfs_iost = hisi_hba->debugfs_iost; + int i, ret, max_command_entries = hisi_hba->hw->max_command_entries; + + for (i = 0; i < max_command_entries; i++, debugfs_iost++) { + ret = hisi_sas_show_row_64(s, i, sizeof(*debugfs_iost), + (u64 *)debugfs_iost); + if (ret) + return ret; + } + + return 0; +} + +static int hisi_sas_debugfs_iost_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hisi_sas_debugfs_iost_show, inode->i_private); +} + +static const struct file_operations hisi_sas_debugfs_iost_fops = { + .open = hisi_sas_debugfs_iost_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) { struct dentry *dump_dentry; @@ -2783,6 +2830,10 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) goto fail; } + if (!debugfs_create_file("iost", 0400, dump_dentry, hisi_hba, + &hisi_sas_debugfs_iost_fops)) + goto fail; + return; fail: debugfs_remove_recursive(hisi_hba->debugfs_dir); From 082c2cd2030e5e9322c835f577fdc9acff41ee5b Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 8 Jan 2019 23:14:52 +0800 Subject: [PATCH 0183/4277] scsi: sd: Make protection lookup tables static and relocate functions Currently the protection lookup tables in sd_prot_flag_mask() and sd_prot_op() are declared as non-static. As such, they will be rebuilt for each respective function call. Optimise by making them static. This saves ~100B object code for sd.c: Before: text data bss dec hex filename 25403 1024 16 26443 674b drivers/scsi/sd.o After: text data bss dec hex filename 25299 1024 16 26339 66e3 drivers/scsi/sd.o In addition, since those same functions are declared in sd.h, but each are only referenced in sd.c, relocate them to that same c file. The inline specifier is dropped also, since gcc should be able to make the decision to inline. Signed-off-by: John Garry Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/sd.h | 62 ----------------------------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index a1a44f52e0e8..c6ca3d915925 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -659,6 +659,68 @@ static int sd_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, } #endif /* CONFIG_BLK_SED_OPAL */ +/* + * Look up the DIX operation based on whether the command is read or + * write and whether dix and dif are enabled. + */ +static unsigned int sd_prot_op(bool write, bool dix, bool dif) +{ + /* Lookup table: bit 2 (write), bit 1 (dix), bit 0 (dif) */ + static const unsigned int ops[] = { /* wrt dix dif */ + SCSI_PROT_NORMAL, /* 0 0 0 */ + SCSI_PROT_READ_STRIP, /* 0 0 1 */ + SCSI_PROT_READ_INSERT, /* 0 1 0 */ + SCSI_PROT_READ_PASS, /* 0 1 1 */ + SCSI_PROT_NORMAL, /* 1 0 0 */ + SCSI_PROT_WRITE_INSERT, /* 1 0 1 */ + SCSI_PROT_WRITE_STRIP, /* 1 1 0 */ + SCSI_PROT_WRITE_PASS, /* 1 1 1 */ + }; + + return ops[write << 2 | dix << 1 | dif]; +} + +/* + * Returns a mask of the protection flags that are valid for a given DIX + * operation. + */ +static unsigned int sd_prot_flag_mask(unsigned int prot_op) +{ + static const unsigned int flag_mask[] = { + [SCSI_PROT_NORMAL] = 0, + + [SCSI_PROT_READ_STRIP] = SCSI_PROT_TRANSFER_PI | + SCSI_PROT_GUARD_CHECK | + SCSI_PROT_REF_CHECK | + SCSI_PROT_REF_INCREMENT, + + [SCSI_PROT_READ_INSERT] = SCSI_PROT_REF_INCREMENT | + SCSI_PROT_IP_CHECKSUM, + + [SCSI_PROT_READ_PASS] = SCSI_PROT_TRANSFER_PI | + SCSI_PROT_GUARD_CHECK | + SCSI_PROT_REF_CHECK | + SCSI_PROT_REF_INCREMENT | + SCSI_PROT_IP_CHECKSUM, + + [SCSI_PROT_WRITE_INSERT] = SCSI_PROT_TRANSFER_PI | + SCSI_PROT_REF_INCREMENT, + + [SCSI_PROT_WRITE_STRIP] = SCSI_PROT_GUARD_CHECK | + SCSI_PROT_REF_CHECK | + SCSI_PROT_REF_INCREMENT | + SCSI_PROT_IP_CHECKSUM, + + [SCSI_PROT_WRITE_PASS] = SCSI_PROT_TRANSFER_PI | + SCSI_PROT_GUARD_CHECK | + SCSI_PROT_REF_CHECK | + SCSI_PROT_REF_INCREMENT | + SCSI_PROT_IP_CHECKSUM, + }; + + return flag_mask[prot_op]; +} + static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, unsigned int dix, unsigned int dif) { diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 7f43e6839bce..1080c85d97f8 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -188,68 +188,6 @@ static inline sector_t sectors_to_logical(struct scsi_device *sdev, sector_t sec return sector >> (ilog2(sdev->sector_size) - 9); } -/* - * Look up the DIX operation based on whether the command is read or - * write and whether dix and dif are enabled. - */ -static inline unsigned int sd_prot_op(bool write, bool dix, bool dif) -{ - /* Lookup table: bit 2 (write), bit 1 (dix), bit 0 (dif) */ - const unsigned int ops[] = { /* wrt dix dif */ - SCSI_PROT_NORMAL, /* 0 0 0 */ - SCSI_PROT_READ_STRIP, /* 0 0 1 */ - SCSI_PROT_READ_INSERT, /* 0 1 0 */ - SCSI_PROT_READ_PASS, /* 0 1 1 */ - SCSI_PROT_NORMAL, /* 1 0 0 */ - SCSI_PROT_WRITE_INSERT, /* 1 0 1 */ - SCSI_PROT_WRITE_STRIP, /* 1 1 0 */ - SCSI_PROT_WRITE_PASS, /* 1 1 1 */ - }; - - return ops[write << 2 | dix << 1 | dif]; -} - -/* - * Returns a mask of the protection flags that are valid for a given DIX - * operation. - */ -static inline unsigned int sd_prot_flag_mask(unsigned int prot_op) -{ - const unsigned int flag_mask[] = { - [SCSI_PROT_NORMAL] = 0, - - [SCSI_PROT_READ_STRIP] = SCSI_PROT_TRANSFER_PI | - SCSI_PROT_GUARD_CHECK | - SCSI_PROT_REF_CHECK | - SCSI_PROT_REF_INCREMENT, - - [SCSI_PROT_READ_INSERT] = SCSI_PROT_REF_INCREMENT | - SCSI_PROT_IP_CHECKSUM, - - [SCSI_PROT_READ_PASS] = SCSI_PROT_TRANSFER_PI | - SCSI_PROT_GUARD_CHECK | - SCSI_PROT_REF_CHECK | - SCSI_PROT_REF_INCREMENT | - SCSI_PROT_IP_CHECKSUM, - - [SCSI_PROT_WRITE_INSERT] = SCSI_PROT_TRANSFER_PI | - SCSI_PROT_REF_INCREMENT, - - [SCSI_PROT_WRITE_STRIP] = SCSI_PROT_GUARD_CHECK | - SCSI_PROT_REF_CHECK | - SCSI_PROT_REF_INCREMENT | - SCSI_PROT_IP_CHECKSUM, - - [SCSI_PROT_WRITE_PASS] = SCSI_PROT_TRANSFER_PI | - SCSI_PROT_GUARD_CHECK | - SCSI_PROT_REF_CHECK | - SCSI_PROT_REF_INCREMENT | - SCSI_PROT_IP_CHECKSUM, - }; - - return flag_mask[prot_op]; -} - #ifdef CONFIG_BLK_DEV_INTEGRITY extern void sd_dif_config_host(struct scsi_disk *); From 7b8f32eee779b6ab93f65aaddbfce6a553b18a77 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 4 Jan 2019 17:02:24 +0000 Subject: [PATCH 0184/4277] scsi: bnx2i: fix indentation issue, remove a tab The return statement is indented incorrectly, fix this by removing a single tab. Signed-off-by: Colin Ian King Acked-by: Manish Rangankar Signed-off-by: Martin K. Petersen --- drivers/scsi/bnx2i/bnx2i_iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 69c75426c5eb..c5fa5f3b00e9 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -577,7 +577,7 @@ static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba) hba->dummy_buffer, hba->dummy_buf_dma); hba->dummy_buffer = NULL; } - return; + return; } /** From 36c5733f9570e9052a6437012be8034085f25ab7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 4 Jan 2019 17:39:53 +0800 Subject: [PATCH 0185/4277] f2fs: check inject_rate validity during configuring Type of inject_rate is unsigned int, let's check new value's validity during configuring. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 0575edbe3ed6..02d4012a9183 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -222,6 +222,8 @@ out: #ifdef CONFIG_F2FS_FAULT_INJECTION if (a->struct_type == FAULT_INFO_TYPE && t >= (1 << FAULT_MAX)) return -EINVAL; + if (a->struct_type == FAULT_INFO_RATE && t >= UINT_MAX) + return -EINVAL; #endif if (a->struct_type == RESERVED_BLOCKS) { spin_lock(&sbi->stat_lock); From 5d539245cb18afa8943cbf2be0cdc033e49f3a4a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 3 Jan 2019 17:19:08 -0800 Subject: [PATCH 0186/4277] f2fs: export FS_NOCOW_FL flag to user This exports pin_file status to user. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/file.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 12fabd6735dd..9286ec381453 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2301,11 +2301,12 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) #define F2FS_EXTENTS_FL 0x00080000 /* Inode uses extents */ #define F2FS_EA_INODE_FL 0x00200000 /* Inode used for large EA */ #define F2FS_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ +#define F2FS_NOCOW_FL 0x00800000 /* Do not cow file */ #define F2FS_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */ #define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ #define F2FS_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ -#define F2FS_FL_USER_VISIBLE 0x304BDFFF /* User visible flags */ +#define F2FS_FL_USER_VISIBLE 0x30CBDFFF /* User visible flags */ #define F2FS_FL_USER_MODIFIABLE 0x204BC0FF /* User modifiable flags */ /* Flags we can manipulate with through F2FS_IOC_FSSETXATTR */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index ae2b45e75847..0d461321edfc 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1651,6 +1651,8 @@ static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) flags |= F2FS_ENCRYPT_FL; if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) flags |= F2FS_INLINE_DATA_FL; + if (is_inode_flag_set(inode, FI_PIN_FILE)) + flags |= F2FS_NOCOW_FL; flags &= F2FS_FL_USER_VISIBLE; From c20e57b32d817d35271c4b205cb6ab80d8d93aeb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 4 Jan 2019 14:26:18 +0100 Subject: [PATCH 0187/4277] f2fs: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Jaegeuk Kim Cc: Chao Yu Cc: linux-f2fs-devel@lists.sourceforge.net Signed-off-by: Greg Kroah-Hartman Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 20 +++----------------- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/super.c | 5 +---- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 503fde8349e6..99e9a5c37b71 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -511,30 +511,16 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi) kvfree(si); } -int __init f2fs_create_root_stats(void) +void __init f2fs_create_root_stats(void) { - struct dentry *file; - f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL); - if (!f2fs_debugfs_root) - return -ENOMEM; - file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root, - NULL, &stat_fops); - if (!file) { - debugfs_remove(f2fs_debugfs_root); - f2fs_debugfs_root = NULL; - return -ENOMEM; - } - - return 0; + debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root, NULL, + &stat_fops); } void f2fs_destroy_root_stats(void) { - if (!f2fs_debugfs_root) - return; - debugfs_remove_recursive(f2fs_debugfs_root); f2fs_debugfs_root = NULL; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9286ec381453..7df41cd1eb35 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3329,7 +3329,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) int f2fs_build_stats(struct f2fs_sb_info *sbi); void f2fs_destroy_stats(struct f2fs_sb_info *sbi); -int __init f2fs_create_root_stats(void); +void __init f2fs_create_root_stats(void); void f2fs_destroy_root_stats(void); #else #define stat_inc_cp_count(si) do { } while (0) @@ -3367,7 +3367,7 @@ void f2fs_destroy_root_stats(void); static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } -static inline int __init f2fs_create_root_stats(void) { return 0; } +static inline void __init f2fs_create_root_stats(void) { } static inline void f2fs_destroy_root_stats(void) { } #endif diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index bc32a1035f65..ea514acede36 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3550,9 +3550,7 @@ static int __init init_f2fs_fs(void) err = register_filesystem(&f2fs_fs_type); if (err) goto free_shrinker; - err = f2fs_create_root_stats(); - if (err) - goto free_filesystem; + f2fs_create_root_stats(); err = f2fs_init_post_read_processing(); if (err) goto free_root_stats; @@ -3560,7 +3558,6 @@ static int __init init_f2fs_fs(void) free_root_stats: f2fs_destroy_root_stats(); -free_filesystem: unregister_filesystem(&f2fs_fs_type); free_shrinker: unregister_shrinker(&f2fs_shrinker_info); From bb8e82b64b4b93372a84c39e6fc1b17199e221ab Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 8 Jan 2019 12:50:01 -0800 Subject: [PATCH 0188/4277] etnaviv mailing list is moderated Signed-off-by: Matthew Wilcox Signed-off-by: Lucas Stach --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..44888eb121d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5141,7 +5141,7 @@ DRM DRIVERS FOR VIVANTE GPU IP M: Lucas Stach R: Russell King R: Christian Gmeiner -L: etnaviv@lists.freedesktop.org +L: etnaviv@lists.freedesktop.org (moderated for non-subscribers) L: dri-devel@lists.freedesktop.org S: Maintained F: drivers/gpu/drm/etnaviv/ From a20ca6744ba7e683a772bb5f1715e4662342be58 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 8 Jan 2019 12:43:56 -0800 Subject: [PATCH 0189/4277] Build etnaviv on non-ARM architectures I wanted to test-compile etnaviv on x86 after making a tree-wide change to it. Unfortunately, Kconfig has a bad dependency, so I couldn't. Signed-off-by: Matthew Wilcox Signed-off-by: Lucas Stach --- drivers/gpu/drm/etnaviv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index 041a77e400d4..342591a1084e 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -2,7 +2,7 @@ config DRM_ETNAVIV tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" depends on DRM - depends on ARCH_MXC || ARCH_DOVE || (ARM && COMPILE_TEST) + depends on ARCH_MXC || ARCH_DOVE || ARM || COMPILE_TEST depends on MMU select SHMEM select SYNC_FILE From 26fe27d9201bb0a5568325f053326671dad4412c Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 17 Dec 2018 19:12:14 -0700 Subject: [PATCH 0190/4277] clk: qcom: Add CLK_SET_RATE_PARENT for 8998 branch clocks A branch clock is basically a clock that can be gated for power savings, but is also what devices/drivers consume. Configuring a branch clock's rate needs to be done at the source, so for all branch clocks which have a defined parent, set CLK_SET_RATE_PARENT so that clk_set_rate() calls on branch clocks can do what is expected. This is important as most drivers do not check the resulting clock rate after a successful clk_set_rate() call, thus the driver may get out of sync with the actual hardware state and weird issues might crop up. This has been observed with issues getting SDHCI to reliably support "fast" cards such as SDR104. Fixes: 4807c71cc688 (arm64: dts: Add msm8998 SoC and MTP board support) Signed-off-by: Jeffrey Hugo Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-msm8998.c | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 1b779396e04f..b618bbfa9a7f 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -1189,6 +1189,7 @@ static struct clk_branch gcc_aggre1_ufs_axi_clk = { "ufs_axi_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1206,6 +1207,7 @@ static struct clk_branch gcc_aggre1_usb3_axi_clk = { "usb30_master_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1288,6 +1290,7 @@ static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = { "blsp1_qup1_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1305,6 +1308,7 @@ static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = { "blsp1_qup1_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1322,6 +1326,7 @@ static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = { "blsp1_qup2_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1339,6 +1344,7 @@ static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = { "blsp1_qup2_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1356,6 +1362,7 @@ static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = { "blsp1_qup3_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1373,6 +1380,7 @@ static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = { "blsp1_qup3_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1390,6 +1398,7 @@ static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = { "blsp1_qup4_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1407,6 +1416,7 @@ static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = { "blsp1_qup4_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1424,6 +1434,7 @@ static struct clk_branch gcc_blsp1_qup5_i2c_apps_clk = { "blsp1_qup5_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1441,6 +1452,7 @@ static struct clk_branch gcc_blsp1_qup5_spi_apps_clk = { "blsp1_qup5_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1458,6 +1470,7 @@ static struct clk_branch gcc_blsp1_qup6_i2c_apps_clk = { "blsp1_qup6_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1475,6 +1488,7 @@ static struct clk_branch gcc_blsp1_qup6_spi_apps_clk = { "blsp1_qup6_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1505,6 +1519,7 @@ static struct clk_branch gcc_blsp1_uart1_apps_clk = { "blsp1_uart1_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1522,6 +1537,7 @@ static struct clk_branch gcc_blsp1_uart2_apps_clk = { "blsp1_uart2_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1539,6 +1555,7 @@ static struct clk_branch gcc_blsp1_uart3_apps_clk = { "blsp1_uart3_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1569,6 +1586,7 @@ static struct clk_branch gcc_blsp2_qup1_i2c_apps_clk = { "blsp2_qup1_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1586,6 +1604,7 @@ static struct clk_branch gcc_blsp2_qup1_spi_apps_clk = { "blsp2_qup1_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1603,6 +1622,7 @@ static struct clk_branch gcc_blsp2_qup2_i2c_apps_clk = { "blsp2_qup2_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1620,6 +1640,7 @@ static struct clk_branch gcc_blsp2_qup2_spi_apps_clk = { "blsp2_qup2_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1637,6 +1658,7 @@ static struct clk_branch gcc_blsp2_qup3_i2c_apps_clk = { "blsp2_qup3_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1654,6 +1676,7 @@ static struct clk_branch gcc_blsp2_qup3_spi_apps_clk = { "blsp2_qup3_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1671,6 +1694,7 @@ static struct clk_branch gcc_blsp2_qup4_i2c_apps_clk = { "blsp2_qup4_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1688,6 +1712,7 @@ static struct clk_branch gcc_blsp2_qup4_spi_apps_clk = { "blsp2_qup4_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1705,6 +1730,7 @@ static struct clk_branch gcc_blsp2_qup5_i2c_apps_clk = { "blsp2_qup5_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1722,6 +1748,7 @@ static struct clk_branch gcc_blsp2_qup5_spi_apps_clk = { "blsp2_qup5_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1739,6 +1766,7 @@ static struct clk_branch gcc_blsp2_qup6_i2c_apps_clk = { "blsp2_qup6_i2c_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1756,6 +1784,7 @@ static struct clk_branch gcc_blsp2_qup6_spi_apps_clk = { "blsp2_qup6_spi_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1786,6 +1815,7 @@ static struct clk_branch gcc_blsp2_uart1_apps_clk = { "blsp2_uart1_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1803,6 +1833,7 @@ static struct clk_branch gcc_blsp2_uart2_apps_clk = { "blsp2_uart2_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1820,6 +1851,7 @@ static struct clk_branch gcc_blsp2_uart3_apps_clk = { "blsp2_uart3_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1837,6 +1869,7 @@ static struct clk_branch gcc_cfg_noc_usb3_axi_clk = { "usb30_master_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1854,6 +1887,7 @@ static struct clk_branch gcc_gp1_clk = { "gp1_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1871,6 +1905,7 @@ static struct clk_branch gcc_gp2_clk = { "gp2_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1888,6 +1923,7 @@ static struct clk_branch gcc_gp3_clk = { "gp3_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1957,6 +1993,7 @@ static struct clk_branch gcc_hmss_ahb_clk = { "hmss_ahb_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1987,6 +2024,7 @@ static struct clk_branch gcc_hmss_rbcpr_clk = { "hmss_rbcpr_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2088,6 +2126,7 @@ static struct clk_branch gcc_pcie_0_aux_clk = { "pcie_aux_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2157,6 +2196,7 @@ static struct clk_branch gcc_pcie_phy_aux_clk = { "pcie_aux_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2174,6 +2214,7 @@ static struct clk_branch gcc_pdm2_clk = { "pdm2_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2243,6 +2284,7 @@ static struct clk_branch gcc_sdcc2_apps_clk = { "sdcc2_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2273,6 +2315,7 @@ static struct clk_branch gcc_sdcc4_apps_clk = { "sdcc4_apps_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2316,6 +2359,7 @@ static struct clk_branch gcc_tsif_ref_clk = { "tsif_ref_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2346,6 +2390,7 @@ static struct clk_branch gcc_ufs_axi_clk = { "ufs_axi_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2441,6 +2486,7 @@ static struct clk_branch gcc_usb30_master_clk = { "usb30_master_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2458,6 +2504,7 @@ static struct clk_branch gcc_usb30_mock_utmi_clk = { "usb30_mock_utmi_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2488,6 +2535,7 @@ static struct clk_branch gcc_usb3_phy_aux_clk = { "usb3_phy_aux_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, From 0c8ff62504e3a667387e87889a259632c3199a86 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 4 Jan 2019 09:49:46 -0700 Subject: [PATCH 0191/4277] clk: qcom: Add missing freq for usb30_master_clk on 8998 The usb30_master_clk supports a 60Mhz frequency, but that is missing from the table of supported frequencies. Add it. Fixes: b5f5f525c547 (clk: qcom: Add MSM8998 Global Clock Control (GCC) driver) Signed-off-by: Jeffrey Hugo Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-msm8998.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index b618bbfa9a7f..96cf00c425a8 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -1112,6 +1112,7 @@ static struct clk_rcg2 ufs_axi_clk_src = { static const struct freq_tbl ftbl_usb30_master_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0), F(120000000, P_GPLL0_OUT_MAIN, 5, 0, 0), F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0), { } From 5f2420ed218981f037910bd029c19ff35ad1839c Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 4 Jan 2019 09:50:15 -0700 Subject: [PATCH 0192/4277] clk: qcom: Skip halt checks on gcc_usb3_phy_pipe_clk for 8998 The gcc_usb3_phy_pipe_clk is generated by the phy, but is also used by the phy during init. The clock needs to be enabled during the init sequence, but may not be fully active until after the init sequence is complete. This causes a catch-22 if the clock status is checked during enable. As a result, skip the checks to avoid the troubling situation. Signed-off-by: Jeffrey Hugo Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-msm8998.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 96cf00c425a8..3cbabbb8bd9a 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -2544,7 +2544,7 @@ static struct clk_branch gcc_usb3_phy_aux_clk = { static struct clk_branch gcc_usb3_phy_pipe_clk = { .halt_reg = 0x50004, - .halt_check = BRANCH_HALT, + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x50004, .enable_mask = BIT(0), From 5b42aac8904bfe8fcd65179c0fe71a6a1109ee53 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 13 Dec 2018 12:49:28 +0000 Subject: [PATCH 0193/4277] dt-bindings: clk: Add bindings for Fixed MMIO clock This patch adds a DT binding documentation for Fixed Memory Mapped IO clocks. Signed-off-by: Jan Kotas Signed-off-by: Stephen Boyd --- .../bindings/clock/fixed-mmio-clock.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/fixed-mmio-clock.txt diff --git a/Documentation/devicetree/bindings/clock/fixed-mmio-clock.txt b/Documentation/devicetree/bindings/clock/fixed-mmio-clock.txt new file mode 100644 index 000000000000..c359367fd1a9 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/fixed-mmio-clock.txt @@ -0,0 +1,24 @@ +Binding for simple memory mapped io fixed-rate clock sources. +The driver reads a clock frequency value from a single 32-bit memory mapped +I/O register and registers it as a fixed rate clock. + +It was designed for test systems, like FPGA, not for complete, finished SoCs. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "fixed-mmio-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- reg : Address and length of the clock value register set. + +Optional properties: +- clock-output-names : From common clock binding. + +Example: +sysclock: sysclock@fd020004 { + #clock-cells = <0>; + compatible = "fixed-mmio-clock"; + reg = <0xfd020004 0x4>; +}; From 50cc4caf531abb2075c6973451fccdaba9f02715 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 13 Dec 2018 12:49:29 +0000 Subject: [PATCH 0194/4277] clk: Add Fixed MMIO clock driver This patch adds a driver for Fixed MMIO clock. The driver reads a clock frequency value from a single 32-bit memory mapped register and registers it as a fixed rate clock. It can be enabled with COMMON_CLK_FIXED_MMIO Kconfig option. Signed-off-by: Jan Kotas [sboyd@kernel.org: Make of_fixed_mmio_clk_setup() static, use clk_hw based APIs] Signed-off-by: Stephen Boyd --- drivers/clk/Kconfig | 6 +++ drivers/clk/Makefile | 1 + drivers/clk/clk-fixed-mmio.c | 101 +++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 drivers/clk/clk-fixed-mmio.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index e5b2fe80eab4..296972518db4 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -290,6 +290,12 @@ config COMMON_CLK_BD718XX This driver supports ROHM BD71837 and ROHM BD71847 PMICs clock gates. +config COMMON_CLK_FIXED_MMIO + bool "Clock driver for Memory Mapped Fixed values" + depends on COMMON_CLK && OF + help + Support for Memory Mapped IO Fixed clocks + source "drivers/clk/actions/Kconfig" source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 8a9440a97500..d6cd32bda771 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o +obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o diff --git a/drivers/clk/clk-fixed-mmio.c b/drivers/clk/clk-fixed-mmio.c new file mode 100644 index 000000000000..d1a97d971183 --- /dev/null +++ b/drivers/clk/clk-fixed-mmio.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Memory Mapped IO Fixed clock driver + * + * Copyright (C) 2018 Cadence Design Systems, Inc. + * + * Authors: + * Jan Kotas + */ + +#include +#include +#include +#include + +static struct clk_hw *fixed_mmio_clk_setup(struct device_node *node) +{ + struct clk_hw *clk; + const char *clk_name = node->name; + void __iomem *base; + u32 freq; + int ret; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%pOFn: failed to map address\n", node); + return ERR_PTR(-EIO); + } + + freq = readl(base); + iounmap(base); + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0, freq); + if (IS_ERR(clk)) { + pr_err("%pOFn: failed to register fixed rate clock\n", node); + return clk; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, clk); + if (ret) { + pr_err("%pOFn: failed to add clock provider\n", node); + clk_hw_unregister(clk); + clk = ERR_PTR(ret); + } + + return clk; +} + +static void __init of_fixed_mmio_clk_setup(struct device_node *node) +{ + fixed_mmio_clk_setup(node); +} +CLK_OF_DECLARE(fixed_mmio_clk, "fixed-mmio-clock", of_fixed_mmio_clk_setup); + +/** + * This is not executed when of_fixed_mmio_clk_setup succeeded. + */ +static int of_fixed_mmio_clk_probe(struct platform_device *pdev) +{ + struct clk_hw *clk; + + clk = fixed_mmio_clk_setup(pdev->dev.of_node); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + platform_set_drvdata(pdev, clk); + + return 0; +} + +static int of_fixed_mmio_clk_remove(struct platform_device *pdev) +{ + struct clk_hw *clk = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + clk_hw_unregister_fixed_rate(clk); + + return 0; +} + +static const struct of_device_id of_fixed_mmio_clk_ids[] = { + { .compatible = "fixed-mmio-clock" }, + { } +}; +MODULE_DEVICE_TABLE(of, of_fixed_mmio_clk_ids); + +static struct platform_driver of_fixed_mmio_clk_driver = { + .driver = { + .name = "of_fixed_mmio_clk", + .of_match_table = of_fixed_mmio_clk_ids, + }, + .probe = of_fixed_mmio_clk_probe, + .remove = of_fixed_mmio_clk_remove, +}; +module_platform_driver(of_fixed_mmio_clk_driver); + +MODULE_AUTHOR("Jan Kotas "); +MODULE_DESCRIPTION("Memory Mapped IO Fixed clock driver"); +MODULE_LICENSE("GPL v2"); From 6131dc81211cfcd8b7bd210665fbe0b6ba2e5fc5 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 17 Dec 2018 19:15:36 -0700 Subject: [PATCH 0195/4277] clk: qcom: smd: Add support for MSM8998 rpm clocks Add rpm smd clocks, PMIC and bus clocks which are required on MSM8998 for clients to vote on. Signed-off-by: Jeffrey Hugo Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + drivers/clk/qcom/clk-smd-rpm.c | 63 +++++++++++++++++++ include/dt-bindings/clock/qcom,rpmcc.h | 10 +++ 3 files changed, 74 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 87b4949e9bc8..944719bd586f 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -16,6 +16,7 @@ Required properties : "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" "qcom,rpmcc-msm8996", "qcom,rpmcc" + "qcom,rpmcc-msm8998", "qcom,rpmcc" "qcom,rpmcc-qcs404", "qcom,rpmcc" - #clock-cells : shall contain 1 diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index d3aadaeb2903..22dd42ad9223 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -655,10 +655,73 @@ static const struct rpm_smd_clk_desc rpm_clk_qcs404 = { .num_clks = ARRAY_SIZE(qcs404_clks), }; +/* msm8998 */ +DEFINE_CLK_SMD_RPM(msm8998, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8998, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8998, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, div_clk1, div_clk1_a, 0xb); +DEFINE_CLK_SMD_RPM(msm8998, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk1, ln_bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, ln_bb_clk2, ln_bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, ln_bb_clk3_pin, ln_bb_clk3_a_pin, + 3); +DEFINE_CLK_SMD_RPM(msm8998, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk, + QCOM_SMD_RPM_MMAXI_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8998, aggre1_noc_clk, aggre1_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8998, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msm8998, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk2_pin, rf_clk2_a_pin, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk3, rf_clk3_a, 6); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk3_pin, rf_clk3_a_pin, 6); +static struct clk_smd_rpm *msm8998_clks[] = { + [RPM_SMD_SNOC_CLK] = &msm8998_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8998_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8998_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8998_cnoc_a_clk, + [RPM_SMD_CE1_CLK] = &msm8998_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8998_ce1_a_clk, + [RPM_SMD_DIV_CLK1] = &msm8998_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8998_div_clk1_a, + [RPM_SMD_IPA_CLK] = &msm8998_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8998_ipa_a_clk, + [RPM_SMD_LN_BB_CLK1] = &msm8998_ln_bb_clk1, + [RPM_SMD_LN_BB_CLK1_A] = &msm8998_ln_bb_clk1_a, + [RPM_SMD_LN_BB_CLK2] = &msm8998_ln_bb_clk2, + [RPM_SMD_LN_BB_CLK2_A] = &msm8998_ln_bb_clk2_a, + [RPM_SMD_LN_BB_CLK3_PIN] = &msm8998_ln_bb_clk3_pin, + [RPM_SMD_LN_BB_CLK3_A_PIN] = &msm8998_ln_bb_clk3_a_pin, + [RPM_SMD_MMAXI_CLK] = &msm8998_mmssnoc_axi_rpm_clk, + [RPM_SMD_MMAXI_A_CLK] = &msm8998_mmssnoc_axi_rpm_a_clk, + [RPM_SMD_AGGR1_NOC_CLK] = &msm8998_aggre1_noc_clk, + [RPM_SMD_AGGR1_NOC_A_CLK] = &msm8998_aggre1_noc_a_clk, + [RPM_SMD_AGGR2_NOC_CLK] = &msm8998_aggre2_noc_clk, + [RPM_SMD_AGGR2_NOC_A_CLK] = &msm8998_aggre2_noc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8998_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8998_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &msm8998_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8998_rf_clk1_a, + [RPM_SMD_RF_CLK2_PIN] = &msm8998_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8998_rf_clk2_a_pin, + [RPM_SMD_RF_CLK3] = &msm8998_rf_clk3, + [RPM_SMD_RF_CLK3_A] = &msm8998_rf_clk3_a, + [RPM_SMD_RF_CLK3_PIN] = &msm8998_rf_clk3_pin, + [RPM_SMD_RF_CLK3_A_PIN] = &msm8998_rf_clk3_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8998 = { + .clks = msm8998_clks, + .num_clks = ARRAY_SIZE(msm8998_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, + { .compatible = "qcom,rpmcc-msm8998", .data = &rpm_clk_msm8998 }, { .compatible = "qcom,rpmcc-qcs404", .data = &rpm_clk_qcs404 }, { } }; diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index 3658b0c14966..ede93a0ca156 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -127,5 +127,15 @@ #define RPM_SMD_BIMC_GPU_A_CLK 77 #define RPM_SMD_QPIC_CLK 78 #define RPM_SMD_QPIC_CLK_A 79 +#define RPM_SMD_LN_BB_CLK1 80 +#define RPM_SMD_LN_BB_CLK1_A 81 +#define RPM_SMD_LN_BB_CLK2 82 +#define RPM_SMD_LN_BB_CLK2_A 83 +#define RPM_SMD_LN_BB_CLK3_PIN 84 +#define RPM_SMD_LN_BB_CLK3_A_PIN 85 +#define RPM_SMD_RF_CLK3 86 +#define RPM_SMD_RF_CLK3_A 87 +#define RPM_SMD_RF_CLK3_PIN 88 +#define RPM_SMD_RF_CLK3_A_PIN 89 #endif From 77977b800451ea7f438bf0398c76e56ef92379fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 18 Dec 2018 12:20:48 +0100 Subject: [PATCH 0196/4277] clk: at91: enable AUDIOPLL as source for PCKx on SAMA5D2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Datasheet for SAMA5D2x mentions 6 sources for PCKx, the last being AUDIOPLL. Signed-off-by: Michał Mirosław Acked-by: Alexandre Belloni Signed-off-by: Stephen Boyd --- drivers/clk/at91/sama5d2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c index d69ad96fe988..ade063622531 100644 --- a/drivers/clk/at91/sama5d2.c +++ b/drivers/clk/at91/sama5d2.c @@ -241,13 +241,14 @@ static void __init sama5d2_pmc_setup(struct device_node *np) parent_names[2] = "plladivck"; parent_names[3] = "utmick"; parent_names[4] = "mck"; + parent_names[5] = "audiopll_pmcck"; for (i = 0; i < 3; i++) { char name[6]; snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, - parent_names, 5, i, + parent_names, 6, i, &at91sam9x5_programmable_layout); if (IS_ERR(hw)) goto err_free; From 31cc9e09688c77b2e87e5770802ebb1fd8ac9011 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Thu, 20 Dec 2018 11:54:33 +0300 Subject: [PATCH 0197/4277] clk: clps711x: Remove board support Since board support for the CLPS711X platform was removed, remove the board support from the clk-clps711x driver. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Stephen Boyd --- drivers/clk/clk-clps711x.c | 61 +++++++------------------------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index 2c04396402ab..c36c47bdba02 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c @@ -44,21 +44,21 @@ struct clps711x_clk { struct clk_hw_onecell_data clk_data; }; -static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, - u32 fref) +static void __init clps711x_clk_init_dt(struct device_node *np) { - u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi; + u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi, fref = 0; struct clps711x_clk *clps711x_clk; - unsigned i; + void __iomem *base; - if (!base) - return ERR_PTR(-ENOMEM); + WARN_ON(of_property_read_u32(np, "startup-frequency", &fref)); + + base = of_iomap(np, 0); + BUG_ON(!base); clps711x_clk = kzalloc(struct_size(clps711x_clk, clk_data.hws, CLPS711X_CLK_MAX), GFP_KERNEL); - if (!clps711x_clk) - return ERR_PTR(-ENOMEM); + BUG_ON(!clps711x_clk); spin_lock_init(&clps711x_clk->lock); @@ -137,52 +137,13 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, clk_hw_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); clps711x_clk->clk_data.hws[CLPS711X_CLK_TICK] = clk_hw_register_fixed_rate(NULL, "tick", NULL, 0, 64); - for (i = 0; i < CLPS711X_CLK_MAX; i++) - if (IS_ERR(clps711x_clk->clk_data.hws[i])) + for (tmp = 0; tmp < CLPS711X_CLK_MAX; tmp++) + if (IS_ERR(clps711x_clk->clk_data.hws[tmp])) pr_err("clk %i: register failed with %ld\n", - i, PTR_ERR(clps711x_clk->clk_data.hws[i])); - - return clps711x_clk; -} - -void __init clps711x_clk_init(void __iomem *base) -{ - struct clps711x_clk *clps711x_clk; - - clps711x_clk = _clps711x_clk_init(base, 73728000); - - BUG_ON(IS_ERR(clps711x_clk)); - - /* Clocksource */ - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1], - NULL, "clps711x-timer.0"); - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2], - NULL, "clps711x-timer.1"); - - /* Drivers */ - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM], - NULL, "clps711x-pwm"); - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART], - NULL, "clps711x-uart.0"); - clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART], - NULL, "clps711x-uart.1"); -} - -#ifdef CONFIG_OF -static void __init clps711x_clk_init_dt(struct device_node *np) -{ - void __iomem *base = of_iomap(np, 0); - struct clps711x_clk *clps711x_clk; - u32 fref = 0; - - WARN_ON(of_property_read_u32(np, "startup-frequency", &fref)); - - clps711x_clk = _clps711x_clk_init(base, fref); - BUG_ON(IS_ERR(clps711x_clk)); + tmp, PTR_ERR(clps711x_clk->clk_data.hws[tmp])); clps711x_clk->clk_data.num = CLPS711X_CLK_MAX; of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clps711x_clk->clk_data); } CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt); -#endif From 309b32fb97d8cf0771373fb55e16b2852840c9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:49:37 +0100 Subject: [PATCH 0198/4277] pwm: Don't use memcmp() to compare state variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given that struct pwm_state is sparse (at least on some platforms), variables of this type might represent the same state because all fields are pairwise identical but still memcmp() returns a difference because some of the unused bits are different. To prevent surprises compare member by member instead of the whole occupied memory. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f..253a459fe0d8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) state->duty_cycle > state->period) return -EINVAL; - if (!memcmp(state, &pwm->state, sizeof(*state))) + if (state->period == pwm->state.period && + state->duty_cycle == pwm->state.duty_cycle && + state->polarity == pwm->state.polarity && + state->enabled == pwm->state.enabled) return 0; if (pwm->chip->ops->apply) { From cc2d22477779f189595db5c515bd5ef9c75a1f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:49:39 +0100 Subject: [PATCH 0199/4277] pwm: Drop per-chip dbg_show callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This callback was introduced in commit 62099abf67a2 ("pwm: Add debugfs interface") in 2012 and up to now there is not a single user. So drop this unused code. Signed-off-by: Uwe Kleine-König [thierry.reding@gmail.com: remove kerneldoc for ->dbg_show()] Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 5 +---- include/linux/pwm.h | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 253a459fe0d8..3149204567f3 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1036,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v) dev_name(chip->dev), chip->npwm, (chip->npwm != 1) ? "s" : ""); - if (chip->ops->dbg_show) - chip->ops->dbg_show(chip, s); - else - pwm_dbg_show(chip, s); + pwm_dbg_show(chip, s); return 0; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index d5199b507d79..6a544cb89de4 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -254,7 +254,6 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, * @get_state: get the current PWM state. This function is only * called once per PWM device when the PWM chip is * registered. - * @dbg_show: optional routine to show contents in debugfs * @owner: helps prevent removal of modules exporting active PWMs */ struct pwm_ops { @@ -272,9 +271,6 @@ struct pwm_ops { struct pwm_state *state); void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state); -#ifdef CONFIG_DEBUG_FS - void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s); -#endif struct module *owner; }; From 5d0a4c11896e8b83f816f135c24b184d4ba57741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:49:41 +0100 Subject: [PATCH 0200/4277] pwm: Rearrange structures to group members by purpose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pwm_ops there are a few callbacks that are not supposed to be used by new drivers. Group them at the end of the structure and add a comment. Similarily for struct pwm_chip group the members that drivers shouldn't care about at the end and mark them as internal with another comment. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- include/linux/pwm.h | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 6a544cb89de4..b628abfffacc 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -242,11 +242,7 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, * struct pwm_ops - PWM controller operations * @request: optional hook for requesting a PWM * @free: optional hook for freeing a PWM - * @config: configure duty cycles and period length for this PWM - * @set_polarity: configure the polarity of this PWM * @capture: capture and report PWM signal - * @enable: enable PWM output toggling - * @disable: disable PWM output toggling * @apply: atomically apply a new PWM config. The state argument * should be adjusted with the real hardware config (if the * approximate the period or duty_cycle value, state should @@ -255,48 +251,55 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, * called once per PWM device when the PWM chip is * registered. * @owner: helps prevent removal of modules exporting active PWMs + * @config: configure duty cycles and period length for this PWM + * @set_polarity: configure the polarity of this PWM + * @enable: enable PWM output toggling + * @disable: disable PWM output toggling */ struct pwm_ops { int (*request)(struct pwm_chip *chip, struct pwm_device *pwm); void (*free)(struct pwm_chip *chip, struct pwm_device *pwm); - int (*config)(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns); - int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity); int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_capture *result, unsigned long timeout); - int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm); - void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm); int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state); void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state); struct module *owner; + + /* Only used by legacy drivers */ + int (*config)(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns); + int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity); + int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm); + void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm); }; /** * struct pwm_chip - abstract a PWM controller * @dev: device providing the PWMs - * @list: list node for internal use * @ops: callbacks for this PWM controller * @base: number of first PWM controlled by this chip * @npwm: number of PWMs controlled by this chip - * @pwms: array of PWM devices allocated by the framework * @of_xlate: request a PWM device given a device tree PWM specifier * @of_pwm_n_cells: number of cells expected in the device tree PWM specifier + * @list: list node for internal use + * @pwms: array of PWM devices allocated by the framework */ struct pwm_chip { struct device *dev; - struct list_head list; const struct pwm_ops *ops; int base; unsigned int npwm; - struct pwm_device *pwms; - struct pwm_device * (*of_xlate)(struct pwm_chip *pc, const struct of_phandle_args *args); unsigned int of_pwm_n_cells; + + /* only used internally by the PWM framework */ + struct list_head list; + struct pwm_device *pwms; }; /** From 01482d2443db24aa5234f3ffa03b09f03c94f603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:53:49 +0100 Subject: [PATCH 0201/4277] pwm: imx: Remove if block where the condition is always wrong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ->remove() callback is only called when probe returned successfully. In this case the driver data cannot be NULL. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 55a3a363d5be..1e90d2b78625 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -444,8 +444,6 @@ static int imx_pwm_remove(struct platform_device *pdev) struct imx_chip *imx; imx = platform_get_drvdata(pdev); - if (imx == NULL) - return -ENODEV; imx_pwm_clk_disable_unprepare(&imx->chip); From f20b187e32e07914ff9878880c7d08ca89b5c3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:53:50 +0100 Subject: [PATCH 0202/4277] pwm: imx: Set driver data earlier simplifying the end of ->probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When ->probe() fails the driver core takes care of unsetting the driver data. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 1e90d2b78625..809493d86d22 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -390,7 +390,6 @@ static int imx_pwm_probe(struct platform_device *pdev) const struct imx_pwm_data *data; struct imx_chip *imx; struct resource *r; - int ret = 0; if (!of_id) return -ENODEV; @@ -401,6 +400,8 @@ static int imx_pwm_probe(struct platform_device *pdev) if (imx == NULL) return -ENOMEM; + platform_set_drvdata(pdev, imx); + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx->clk_ipg)) { dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", @@ -431,12 +432,7 @@ static int imx_pwm_probe(struct platform_device *pdev) if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - ret = pwmchip_add(&imx->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, imx); - return 0; + return pwmchip_add(&imx->chip); } static int imx_pwm_remove(struct platform_device *pdev) From b9a5c60bc2f65561535dc05d0c740aa6e9e3bdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:53:51 +0100 Subject: [PATCH 0203/4277] pwm: imx: Don't print an error on -EPROBE_DEFER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When getting the peripheral clock fails with -EPROBE_DEFER the driver is usually probed again later and emitting an error message is irritating. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 809493d86d22..30380fcb5cfb 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -411,9 +411,14 @@ static int imx_pwm_probe(struct platform_device *pdev) imx->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(imx->clk_per)) { - dev_err(&pdev->dev, "getting per clock failed with %ld\n", - PTR_ERR(imx->clk_per)); - return PTR_ERR(imx->clk_per); + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; } imx->chip.ops = data->ops; From e8dacf5957ea583c2a1f48600043a6f76e340d1e Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 10 Jan 2019 14:26:21 +0200 Subject: [PATCH 0204/4277] gpio: mvebu: implement get_direction struct gpio_chip documentation recommends to always implement this callback function. A more concrete motivation is to be able (in combination with GPIOD_ASIS) to detect whether the bootloader has changed the state of a GPIO signal. Signed-off-by: Baruch Siach Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mvebu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 7d5c55494ccd..f97ed32b8beb 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -376,6 +376,16 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, return 0; } +static int mvebu_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); + u32 u; + + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); + + return !!(u & BIT(pin)); +} + static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); @@ -1130,6 +1140,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.parent = &pdev->dev; mvchip->chip.request = gpiochip_generic_request; mvchip->chip.free = gpiochip_generic_free; + mvchip->chip.get_direction = mvebu_gpio_get_direction; mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; From 72893f0c6bd399ce84e3c1c9fc69d234fe37d098 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 31 Dec 2018 21:55:21 +0100 Subject: [PATCH 0205/4277] gpiolib-acpi: Preserve non direction flags when updating gpiod_flags __acpi_gpio_update_gpiod_flags purpose is to make the gpiod_flags used when requesting a GPIO match the restrictions from the ACPI resource, as stored in acpi_gpio_info.flags. But acpi_gpio_info.flags only contains direction info, and the requester may have passed in special non-direction flags like GPIOD_FLAGS_BIT_NONEXCLUSIVE, which we currently clobber. This commit modifies __acpi_gpio_update_gpiod_flags to preserve these special flags, so that a requested of an ACPI GPIO can e.g. pass GPIOD_FLAGS_BIT_NONEXCLUSIV and have it work as intended. Acked-by: Mika Westerberg Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 48534bda73d3..f0763e0e02f1 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -474,6 +474,9 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) static int __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) { + const enum gpiod_flags mask = + GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | + GPIOD_FLAGS_BIT_DIR_VAL; int ret = 0; /* @@ -494,7 +497,7 @@ __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) ret = -EINVAL; - *flags = update; + *flags = (*flags & ~mask) | (update & mask); } return ret; } From a9074e1e3d78298dd085c84299c8de27766f3884 Mon Sep 17 00:00:00 2001 From: Dianlong Li Date: Thu, 13 Dec 2018 18:13:48 +0800 Subject: [PATCH 0206/4277] dt-bindings: define vendor prefix for whwave, Inc. Introduce vendor prefix for whwave, Inc. for SD3078 rtc device. Signed-off-by: Dianlong Li Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 389508584f48..475f90687c05 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -430,6 +430,7 @@ vot Vision Optical Technology Co., Ltd. wd Western Digital Corp. wetek WeTek Electronics, limited. wexler Wexler +whwave Shenzhen whwave Electronics, Inc. wi2wi Wi2Wi, Inc. winbond Winbond Electronics corp. winstar Winstar Display Corp. From a1c1eae4690aacd3a7ce9df97b47194ac2bf44e2 Mon Sep 17 00:00:00 2001 From: Dianlong Li Date: Thu, 13 Dec 2018 18:13:49 +0800 Subject: [PATCH 0207/4277] dt-bindings: rtc: sd3078: add device tree documentation The devicetree documentation for the SD3078 device tree. Signed-off-by: Dianlong Li Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 7c8da6926095..e46edab31403 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -62,3 +62,4 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC sii,s35390a 2-wire CMOS real-time clock +whwave,sd3078 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC From 1d67a232105b4dec2b10b2689f9cb89cc366d62f Mon Sep 17 00:00:00 2001 From: Dianlong Li Date: Thu, 13 Dec 2018 18:13:50 +0800 Subject: [PATCH 0208/4277] rtc: sd3078: new driver. The sd3078 is a combination RTC and SRAM device with I2C interface. Signed-off-by: Dianlong Li Signed-off-by: Alexandre Belloni --- MAINTAINERS | 6 + drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-sd3078.c | 232 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 drivers/rtc/rtc-sd3078.c diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..5b04149b2adf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16458,6 +16458,12 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-wcove.c +WHWAVE RTC DRIVER +M: Dianlong Li +L: linux-rtc@vger.kernel.org +S: Maintained +F: drivers/rtc/rtc-sd3078.c + WIIMOTE HID DRIVER M: David Herrmann L: linux-input@vger.kernel.org diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 225b0b8516f3..a24fe8b7b98a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -646,6 +646,15 @@ config RTC_DRV_S5M This driver can also be built as a module. If so, the module will be called rtc-s5m. +config RTC_DRV_SD3078 + tristate "ZXW Crystal SD3078" + help + If you say yes here you get support for the ZXW Crystal + SD3078 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-sd3078 + endif # I2C comment "SPI RTC drivers" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index df022d820bee..39eb6de4ad74 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -149,6 +149,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o +obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c new file mode 100644 index 000000000000..a70d7311d039 --- /dev/null +++ b/drivers/rtc/rtc-sd3078.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock (RTC) Driver for sd3078 + * Copyright (C) 2018 Zoro Li + */ + +#include +#include +#include +#include +#include +#include + +#define SD3078_REG_SC 0x00 +#define SD3078_REG_MN 0x01 +#define SD3078_REG_HR 0x02 +#define SD3078_REG_DW 0x03 +#define SD3078_REG_DM 0x04 +#define SD3078_REG_MO 0x05 +#define SD3078_REG_YR 0x06 + +#define SD3078_REG_CTRL1 0x0f +#define SD3078_REG_CTRL2 0x10 +#define SD3078_REG_CTRL3 0x11 + +#define KEY_WRITE1 0x80 +#define KEY_WRITE2 0x04 +#define KEY_WRITE3 0x80 + +#define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1) + +/* + * The sd3078 has write protection + * and we can choose whether or not to use it. + * Write protection is turned off by default. + */ +#define WRITE_PROTECT_EN 0 + +struct sd3078 { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +/* + * In order to prevent arbitrary modification of the time register, + * when modification of the register, + * the "write" bit needs to be written in a certain order. + * 1. set WRITE1 bit + * 2. set WRITE2 bit + * 3. set WRITE3 bit + */ +static void sd3078_enable_reg_write(struct sd3078 *sd3078) +{ + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, + KEY_WRITE1, KEY_WRITE1); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE2, KEY_WRITE2); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE3, KEY_WRITE3); +} + +#if WRITE_PROTECT_EN +/* + * In order to prevent arbitrary modification of the time register, + * we should disable the write function. + * when disable write, + * the "write" bit needs to be clear in a certain order. + * 1. clear WRITE2 bit + * 2. clear WRITE3 bit + * 3. clear WRITE1 bit + */ +static void sd3078_disable_reg_write(struct sd3078 *sd3078) +{ + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE2, 0); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, + KEY_WRITE3, 0); + regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, + KEY_WRITE1, 0); +} +#endif + +static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char hour; + unsigned char rtc_data[NUM_TIME_REGS] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct sd3078 *sd3078 = i2c_get_clientdata(client); + int ret; + + ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "reading from RTC failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F); + + /* + * The sd3078 supports 12/24 hour mode. + * When getting time, + * we need to convert the 12 hour mode to the 24 hour mode. + */ + hour = rtc_data[SD3078_REG_HR]; + if (hour & 0x80) /* 24H MODE */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F); + else if (hour & 0x20) /* 12H MODE PM */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12; + else /* 12H MODE AM */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F); + + tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F); + tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100; + + return 0; +} + +static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct i2c_client *client = to_i2c_client(dev); + struct sd3078 *sd3078 = i2c_get_clientdata(client); + int ret; + + rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec); + rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min); + rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80; + rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday); + rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07; + rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1; + rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100); + +#if WRITE_PROTECT_EN + sd3078_enable_reg_write(sd3078); +#endif + + ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "writing to RTC failed with err:%d\n", ret); + return ret; + } + +#if WRITE_PROTECT_EN + sd3078_disable_reg_write(sd3078); +#endif + + return 0; +} + +static const struct rtc_class_ops sd3078_rtc_ops = { + .read_time = sd3078_rtc_read_time, + .set_time = sd3078_rtc_set_time, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, +}; + +static int sd3078_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct sd3078 *sd3078; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL); + if (!sd3078) + return -ENOMEM; + + sd3078->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(sd3078->regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(sd3078->regmap); + } + + i2c_set_clientdata(client, sd3078); + + sd3078->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(sd3078->rtc)) + return PTR_ERR(sd3078->rtc); + + sd3078->rtc->ops = &sd3078_rtc_ops; + sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = rtc_register_device(sd3078->rtc); + if (ret) { + dev_err(&client->dev, "failed to register rtc device\n"); + return ret; + } + + sd3078_enable_reg_write(sd3078); + + return 0; +} + +static const struct i2c_device_id sd3078_id[] = { + {"sd3078", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, sd3078_id); + +static const struct of_device_id rtc_dt_match[] = { + { .compatible = "whwave,sd3078" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtc_dt_match); + +struct i2c_driver sd3078_driver = { + .driver = { + .name = "sd3078", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rtc_dt_match), + }, + .probe = sd3078_probe, + .id_table = sd3078_id, +}; + +module_i2c_driver(sd3078_driver); + +MODULE_AUTHOR("Dianlong Li "); +MODULE_DESCRIPTION("SD3078 RTC driver"); +MODULE_LICENSE("GPL v2"); From 7c617e0c5f37d2f681b3a568c3642115a32b7427 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 18 Dec 2018 22:52:12 +0100 Subject: [PATCH 0209/4277] rtc: pcf8523: Add rv8523 compatible The Microcrystal RV-8523 is compatible with the PCF8523. Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + drivers/rtc/rtc-pcf8523.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index e46edab31403..b79a4794a928 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -49,6 +49,7 @@ isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM isil,isl1218 Intersil ISL1218 Low Power RTC with Battery Backed SRAM isil,isl12022 Intersil ISL12022 Real-time Clock microcrystal,rv3029 Real Time Clock Module with I2C-Bus +microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock nxp,pcf2129 Real-time clock nxp,pcf8523 Real-time Clock diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 3fcd2cbafc84..fb13933f4006 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -374,6 +374,7 @@ MODULE_DEVICE_TABLE(i2c, pcf8523_id); #ifdef CONFIG_OF static const struct of_device_id pcf8523_of_match[] = { { .compatible = "nxp,pcf8523" }, + { .compatible = "microcrystal,rv8523" }, { } }; MODULE_DEVICE_TABLE(of, pcf8523_of_match); From 074b01a51d058f4216b7ce541e96b778aa6af60d Mon Sep 17 00:00:00 2001 From: ZhangXiaoxu Date: Thu, 20 Dec 2018 17:36:56 +0800 Subject: [PATCH 0210/4277] rtc: Fix UBSAN overflow warning Users may call 'ioctl' and pass a very big value on 'tm->tm_year'. It can be overflowed in 'int' after add 1900. In function 'rtc_month_days' and 'mktime64', also treated it as an 'unsigned' parameter. UBSAN: Undefined behaviour in drivers/rtc/rtc-lib.c:103:59 signed integer overflow: 2147483647 + 1900 cannot be represented in type 'int' UBSAN: Undefined behaviour in drivers/rtc/rtc-lib.c:119:30 signed integer overflow: 2147483647 + 1900 cannot be represented in type 'int' So, covert it to 'unsigned' explicitly. Signed-off-by: ZhangXiaoxu Signed-off-by: Alexandre Belloni --- drivers/rtc/lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c index ef160da84220..9714cb3d1e29 100644 --- a/drivers/rtc/lib.c +++ b/drivers/rtc/lib.c @@ -100,7 +100,7 @@ int rtc_valid_tm(struct rtc_time *tm) if (tm->tm_year < 70 || ((unsigned)tm->tm_mon) >= 12 || tm->tm_mday < 1 - || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) + || tm->tm_mday > rtc_month_days(tm->tm_mon, ((unsigned)tm->tm_year + 1900)) || ((unsigned)tm->tm_hour) >= 24 || ((unsigned)tm->tm_min) >= 60 || ((unsigned)tm->tm_sec) >= 60) @@ -116,8 +116,8 @@ EXPORT_SYMBOL(rtc_valid_tm); */ time64_t rtc_tm_to_time64(struct rtc_time *tm) { - return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + return mktime64(((unsigned)tm->tm_year + 1900), tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } EXPORT_SYMBOL(rtc_tm_to_time64); From c8c97a4fb52c526be7a78c4c1e61a95cf1c915cc Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Wed, 2 Jan 2019 16:00:17 +0000 Subject: [PATCH 0211/4277] rtc: isl1208: fix negative digital trim reporting isl1208_i2c_get_dtr() was returning the dtr value directly, but could also return a negative error code. Negative trimming values, e.g. -20, would get interpreted as an error code, e.g. -ENOTDIR. This patch offsets the dtr value by 100 so it's positive and won't alias an error code. Also fix check that considered a return value of -1 to be success. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 37ab3e1d25f5..263af3d8cd9f 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -161,6 +161,7 @@ isl1208_i2c_get_atr(struct i2c_client *client) return atr; } +/* returns adjustment value + 100 */ static int isl1208_i2c_get_dtr(struct i2c_client *client) { @@ -171,7 +172,7 @@ isl1208_i2c_get_dtr(struct i2c_client *client) /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1); - return dtr; + return dtr + 100; } static int @@ -248,8 +249,8 @@ isl1208_rtc_proc(struct device *dev, struct seq_file *seq) (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); dtr = isl1208_i2c_get_dtr(client); - if (dtr >= 0 - 1) - seq_printf(seq, "digital_trim\t: %d ppm\n", dtr); + if (dtr >= 0) + seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100); atr = isl1208_i2c_get_atr(client); if (atr >= 0) @@ -637,7 +638,7 @@ isl1208_sysfs_show_dtrim(struct device *dev, if (dtr < 0) return dtr; - return sprintf(buf, "%d ppm\n", dtr); + return sprintf(buf, "%d ppm\n", dtr - 100); } static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); From 6fa8f1afd3373c456e556815ebc8cb2330d6c3fe Mon Sep 17 00:00:00 2001 From: Shamir Rabinovitch Date: Wed, 9 Jan 2019 11:15:15 +0200 Subject: [PATCH 0212/4277] IB/{core,uverbs}: Move ib_umem_xxx functions from ib_core to ib_uverbs The next patch will add dependency from ib_umem_get in to ib_uverbs so move the required ib_umem_xxx functionality to it's correct module - ib_uverbs - and avoid circular dependecy from the form of ib_core -> ib_uverbs -> ib_core in depmod. Since this now requires all drivers to be build modular if uverbs is modular, hoist the test a couple drivers had into the main kconfig and apply it to all drivers uniformly. Signed-off-by: Shamir Rabinovitch Signed-off-by: Jason Gunthorpe --- drivers/infiniband/Kconfig | 15 +++++++-------- drivers/infiniband/core/Makefile | 4 ++-- drivers/infiniband/hw/hns/Kconfig | 1 - drivers/infiniband/hw/mlx4/Kconfig | 1 - drivers/infiniband/hw/mlx5/Kconfig | 1 - drivers/infiniband/hw/nes/Kconfig | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 0a3ec7c726ec..a1fb840de45d 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -89,6 +89,7 @@ config INFINIBAND_ADDR_TRANS_CONFIGFS This allows the user to config the default GID type that the CM uses for each device, when initiaing new connections. +if INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS source "drivers/infiniband/hw/mthca/Kconfig" source "drivers/infiniband/hw/qib/Kconfig" source "drivers/infiniband/hw/cxgb3/Kconfig" @@ -101,6 +102,12 @@ source "drivers/infiniband/hw/ocrdma/Kconfig" source "drivers/infiniband/hw/vmw_pvrdma/Kconfig" source "drivers/infiniband/hw/usnic/Kconfig" source "drivers/infiniband/hw/hns/Kconfig" +source "drivers/infiniband/hw/bnxt_re/Kconfig" +source "drivers/infiniband/hw/hfi1/Kconfig" +source "drivers/infiniband/hw/qedr/Kconfig" +source "drivers/infiniband/sw/rdmavt/Kconfig" +source "drivers/infiniband/sw/rxe/Kconfig" +endif source "drivers/infiniband/ulp/ipoib/Kconfig" @@ -111,13 +118,5 @@ source "drivers/infiniband/ulp/iser/Kconfig" source "drivers/infiniband/ulp/isert/Kconfig" source "drivers/infiniband/ulp/opa_vnic/Kconfig" -source "drivers/infiniband/sw/rdmavt/Kconfig" -source "drivers/infiniband/sw/rxe/Kconfig" - -source "drivers/infiniband/hw/hfi1/Kconfig" - -source "drivers/infiniband/hw/qedr/Kconfig" - -source "drivers/infiniband/hw/bnxt_re/Kconfig" endif # INFINIBAND diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index 69dee36e0e89..313f2349b518 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -15,8 +15,6 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \ nldev.o restrack.o ib_core-$(CONFIG_SECURITY_INFINIBAND) += security.o -ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o -ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o ib_cm-y := cm.o @@ -39,3 +37,5 @@ ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \ uverbs_std_types_flow_action.o uverbs_std_types_dm.o \ uverbs_std_types_mr.o uverbs_std_types_counters.o \ uverbs_uapi.o uverbs_std_types_device.o +ib_uverbs-$(CONFIG_INFINIBAND_USER_MEM) += umem.o +ib_uverbs-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o diff --git a/drivers/infiniband/hw/hns/Kconfig b/drivers/infiniband/hw/hns/Kconfig index 21c2100b2ea9..fddb5fdf92de 100644 --- a/drivers/infiniband/hw/hns/Kconfig +++ b/drivers/infiniband/hw/hns/Kconfig @@ -1,7 +1,6 @@ config INFINIBAND_HNS tristate "HNS RoCE Driver" depends on NET_VENDOR_HISILICON - depends on INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS depends on ARM64 || (COMPILE_TEST && 64BIT) ---help--- This is a RoCE/RDMA driver for the Hisilicon RoCE engine. The engine diff --git a/drivers/infiniband/hw/mlx4/Kconfig b/drivers/infiniband/hw/mlx4/Kconfig index d1de3285fd88..db4aa13ebae0 100644 --- a/drivers/infiniband/hw/mlx4/Kconfig +++ b/drivers/infiniband/hw/mlx4/Kconfig @@ -1,7 +1,6 @@ config MLX4_INFINIBAND tristate "Mellanox ConnectX HCA support" depends on NETDEVICES && ETHERNET && PCI && INET - depends on INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS depends on MAY_USE_DEVLINK select NET_VENDOR_MELLANOX select MLX4_CORE diff --git a/drivers/infiniband/hw/mlx5/Kconfig b/drivers/infiniband/hw/mlx5/Kconfig index 0440966bc6ec..8d651c05de62 100644 --- a/drivers/infiniband/hw/mlx5/Kconfig +++ b/drivers/infiniband/hw/mlx5/Kconfig @@ -1,7 +1,6 @@ config MLX5_INFINIBAND tristate "Mellanox 5th generation network adapters (ConnectX series) support" depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE - depends on INFINIBAND_USER_ACCESS || INFINIBAND_USER_ACCESS=n ---help--- This driver provides low-level InfiniBand support for Mellanox Connect-IB PCI Express host channel adapters (HCAs). diff --git a/drivers/infiniband/hw/nes/Kconfig b/drivers/infiniband/hw/nes/Kconfig index 7964eba8e7ed..52caae954e4a 100644 --- a/drivers/infiniband/hw/nes/Kconfig +++ b/drivers/infiniband/hw/nes/Kconfig @@ -1,6 +1,6 @@ config INFINIBAND_NES tristate "NetEffect RNIC Driver" - depends on PCI && INET && INFINIBAND + depends on PCI && INET select LIBCRC32C ---help--- This is the RDMA Network Interface Card (RNIC) driver for From b0ea0fa5435f9df7213a9af098558f7dd584d8e8 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 9 Jan 2019 11:15:16 +0200 Subject: [PATCH 0213/4277] IB/{core,hw}: Have ib_umem_get extract the ib_ucontext from ib_udata ib_umem_get() can only be called in a method callback, which always has a udata parameter. This allows ib_umem_get() to derive the ucontext pointer directly from the udata without requiring the drivers to find it in some way or another. Signed-off-by: Jason Gunthorpe Signed-off-by: Shamir Rabinovitch --- drivers/infiniband/core/umem.c | 9 ++++- drivers/infiniband/core/uverbs_main.c | 24 +++++++++++ drivers/infiniband/hw/bnxt_re/ib_verbs.c | 13 +++--- drivers/infiniband/hw/cxgb3/iwch_provider.c | 2 +- drivers/infiniband/hw/cxgb4/mem.c | 2 +- drivers/infiniband/hw/hns/hns_roce_cq.c | 9 +++-- drivers/infiniband/hw/hns/hns_roce_db.c | 6 +-- drivers/infiniband/hw/hns/hns_roce_device.h | 3 +- drivers/infiniband/hw/hns/hns_roce_mr.c | 7 ++-- drivers/infiniband/hw/hns/hns_roce_qp.c | 13 +++--- drivers/infiniband/hw/hns/hns_roce_srq.c | 7 ++-- drivers/infiniband/hw/i40iw/i40iw_verbs.c | 2 +- drivers/infiniband/hw/mlx4/cq.c | 17 ++++---- drivers/infiniband/hw/mlx4/doorbell.c | 6 +-- drivers/infiniband/hw/mlx4/mlx4_ib.h | 3 +- drivers/infiniband/hw/mlx4/mr.c | 11 ++--- drivers/infiniband/hw/mlx4/qp.c | 14 ++++--- drivers/infiniband/hw/mlx4/srq.c | 5 +-- drivers/infiniband/hw/mlx5/cq.c | 11 +++-- drivers/infiniband/hw/mlx5/devx.c | 2 +- drivers/infiniband/hw/mlx5/doorbell.c | 6 +-- drivers/infiniband/hw/mlx5/mlx5_ib.h | 4 +- drivers/infiniband/hw/mlx5/mr.c | 22 +++++----- drivers/infiniband/hw/mlx5/odp.c | 4 +- drivers/infiniband/hw/mlx5/qp.c | 40 +++++++++---------- drivers/infiniband/hw/mlx5/srq.c | 5 +-- drivers/infiniband/hw/mthca/mthca_provider.c | 2 +- drivers/infiniband/hw/nes/nes_verbs.c | 2 +- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 2 +- drivers/infiniband/hw/qedr/verbs.c | 33 ++++++++------- drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c | 2 +- drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c | 3 +- drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c | 6 +-- drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c | 4 +- drivers/infiniband/sw/rdmavt/mr.c | 3 +- drivers/infiniband/sw/rxe/rxe_mr.c | 2 +- include/rdma/ib_umem.h | 8 ++-- include/rdma/ib_verbs.h | 1 + 38 files changed, 168 insertions(+), 147 deletions(-) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index c6144df47ea4..1efe0a74e06b 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -72,15 +72,16 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d * If access flags indicate ODP memory, avoid pinning. Instead, stores * the mm for future page fault handling in conjunction with MMU notifiers. * - * @context: userspace context to pin memory for + * @udata: userspace context to pin memory for * @addr: userspace virtual address to start at * @size: length of region to pin * @access: IB_ACCESS_xxx flags for memory being pinned * @dmasync: flush in-flight DMA when the memory region is written */ -struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, +struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, size_t size, int access, int dmasync) { + struct ib_ucontext *context; struct ib_umem *umem; struct page **page_list; struct vm_area_struct **vma_list; @@ -95,6 +96,10 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, struct scatterlist *sg, *sg_list_start; unsigned int gup_flags = FOLL_WRITE; + context = rdma_get_ucontext(udata); + if (IS_ERR(context)) + return ERR_CAST(context); + if (dmasync) dma_attrs |= DMA_ATTR_WRITE_BARRIER; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index fb0007aa0c27..996f167d1436 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -101,6 +101,30 @@ struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile) } EXPORT_SYMBOL(ib_uverbs_get_ucontext_file); +/* rdma_get_ucontext - Return the ucontext from a udata + * @udata: The udata to get the context from + * + * This can only be called from within a uapi method that was passed ib_udata + * as a parameter. It returns the ucontext associated with the udata, or ERR_PTR + * if the udata is NULL or the ucontext has been disassociated. + */ +struct ib_ucontext *rdma_get_ucontext(struct ib_udata *udata) +{ + if (!udata) + return ERR_PTR(-EIO); + + /* + * FIXME: Really all cases that get here with a udata will have + * already called ib_uverbs_get_ucontext_file, or located a uobject + * that points to a ucontext. We could store that result in the udata + * so this function can't fail. + */ + return ib_uverbs_get_ucontext_file( + container_of(udata, struct uverbs_attr_bundle, driver_udata) + ->ufile); +} +EXPORT_SYMBOL(rdma_get_ucontext); + int uverbs_dealloc_mw(struct ib_mw *mw) { struct ib_pd *pd = mw->pd; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 1e2515e2eb62..9bc637e49faa 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -895,8 +895,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) bytes += (qplib_qp->sq.max_wqe * sizeof(struct sq_psn_search)); bytes = PAGE_ALIGN(bytes); - umem = ib_umem_get(context, ureq.qpsva, bytes, - IB_ACCESS_LOCAL_WRITE, 1); + umem = ib_umem_get(udata, ureq.qpsva, bytes, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(umem)) return PTR_ERR(umem); @@ -908,7 +907,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, if (!qp->qplib_qp.srq) { bytes = (qplib_qp->rq.max_wqe * BNXT_QPLIB_MAX_RQE_ENTRY_SIZE); bytes = PAGE_ALIGN(bytes); - umem = ib_umem_get(context, ureq.qprva, bytes, + umem = ib_umem_get(udata, ureq.qprva, bytes, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(umem)) goto rqfail; @@ -1370,8 +1369,7 @@ static int bnxt_re_init_user_srq(struct bnxt_re_dev *rdev, bytes = (qplib_srq->max_wqe * BNXT_QPLIB_MAX_RQE_ENTRY_SIZE); bytes = PAGE_ALIGN(bytes); - umem = ib_umem_get(context, ureq.srqva, bytes, - IB_ACCESS_LOCAL_WRITE, 1); + umem = ib_umem_get(udata, ureq.srqva, bytes, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(umem)) return PTR_ERR(umem); @@ -2622,7 +2620,7 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev, goto fail; } - cq->umem = ib_umem_get(context, req.cq_va, + cq->umem = ib_umem_get(udata, req.cq_va, entries * sizeof(struct cq_base), IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(cq->umem)) { @@ -3589,8 +3587,7 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, /* The fixed portion of the rkey is the same as the lkey */ mr->ib_mr.rkey = mr->qplib_mr.rkey; - umem = ib_umem_get(ib_pd->uobject->context, start, length, - mr_access_flags, 0); + umem = ib_umem_get(udata, start, length, mr_access_flags, 0); if (IS_ERR(umem)) { dev_err(rdev_to_dev(rdev), "Failed to get umem"); rc = -EFAULT; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index b34b1a1bd94b..92ee6761a3bd 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -540,7 +540,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mhp->rhp = rhp; - mhp->umem = ib_umem_get(pd->uobject->context, start, length, acc, 0); + mhp->umem = ib_umem_get(udata, start, length, acc, 0); if (IS_ERR(mhp->umem)) { err = PTR_ERR(mhp->umem); kfree(mhp); diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index 7b76e6f81aeb..96760a36b9fc 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -537,7 +537,7 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mhp->rhp = rhp; - mhp->umem = ib_umem_get(pd->uobject->context, start, length, acc, 0); + mhp->umem = ib_umem_get(udata, start, length, acc, 0); if (IS_ERR(mhp->umem)) goto err_free_skb; diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c index 3a485f50fede..1dfe5627006c 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cq.c +++ b/drivers/infiniband/hw/hns/hns_roce_cq.c @@ -215,7 +215,7 @@ void hns_roce_free_cq(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq) EXPORT_SYMBOL_GPL(hns_roce_free_cq); static int hns_roce_ib_get_cq_umem(struct hns_roce_dev *hr_dev, - struct ib_ucontext *context, + struct ib_udata *udata, struct hns_roce_cq_buf *buf, struct ib_umem **umem, u64 buf_addr, int cqe) { @@ -223,7 +223,7 @@ static int hns_roce_ib_get_cq_umem(struct hns_roce_dev *hr_dev, u32 page_shift; u32 npages; - *umem = ib_umem_get(context, buf_addr, cqe * hr_dev->caps.cq_entry_sz, + *umem = ib_umem_get(udata, buf_addr, cqe * hr_dev->caps.cq_entry_sz, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(*umem)) return PTR_ERR(*umem); @@ -347,7 +347,7 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev, } /* Get user space address, write it into mtt table */ - ret = hns_roce_ib_get_cq_umem(hr_dev, context, &hr_cq->hr_buf, + ret = hns_roce_ib_get_cq_umem(hr_dev, udata, &hr_cq->hr_buf, &hr_cq->umem, ucmd.buf_addr, cq_entries); if (ret) { @@ -358,7 +358,8 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev, if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) && (udata->outlen >= sizeof(resp))) { ret = hns_roce_db_map_user(to_hr_ucontext(context), - ucmd.db_addr, &hr_cq->db); + udata, ucmd.db_addr, + &hr_cq->db); if (ret) { dev_err(dev, "cq record doorbell map failed!\n"); goto err_mtt; diff --git a/drivers/infiniband/hw/hns/hns_roce_db.c b/drivers/infiniband/hw/hns/hns_roce_db.c index e2f93c1ce86a..0c6c1fe87705 100644 --- a/drivers/infiniband/hw/hns/hns_roce_db.c +++ b/drivers/infiniband/hw/hns/hns_roce_db.c @@ -8,7 +8,8 @@ #include #include "hns_roce_device.h" -int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt, +int hns_roce_db_map_user(struct hns_roce_ucontext *context, + struct ib_udata *udata, unsigned long virt, struct hns_roce_db *db) { struct hns_roce_user_db_page *page; @@ -28,8 +29,7 @@ int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt, refcount_set(&page->refcount, 1); page->user_virt = (virt & PAGE_MASK); - page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK, - PAGE_SIZE, 0, 0); + page->umem = ib_umem_get(udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0); if (IS_ERR(page->umem)) { ret = PTR_ERR(page->umem); kfree(page); diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 509e467843f6..9a4be70936e0 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -1133,7 +1133,8 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev, int hns_roce_ib_destroy_cq(struct ib_cq *ib_cq); void hns_roce_free_cq(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq); -int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt, +int hns_roce_db_map_user(struct hns_roce_ucontext *context, + struct ib_udata *udata, unsigned long virt, struct hns_roce_db *db); void hns_roce_db_unmap_user(struct hns_roce_ucontext *context, struct hns_roce_db *db); diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index ee5991bd4171..da4fffedb879 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -1110,8 +1110,7 @@ struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (!mr) return ERR_PTR(-ENOMEM); - mr->umem = ib_umem_get(pd->uobject->context, start, length, - access_flags, 0); + mr->umem = ib_umem_get(udata, start, length, access_flags, 0); if (IS_ERR(mr->umem)) { ret = PTR_ERR(mr->umem); goto err_free; @@ -1220,8 +1219,8 @@ int hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, u64 length, } ib_umem_release(mr->umem); - mr->umem = ib_umem_get(ibmr->uobject->context, start, length, - mr_access_flags, 0); + mr->umem = + ib_umem_get(udata, start, length, mr_access_flags, 0); if (IS_ERR(mr->umem)) { ret = PTR_ERR(mr->umem); mr->umem = NULL; diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index b9ba140b50d4..accf9ce1507d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -613,9 +613,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, goto err_rq_sge_list; } - hr_qp->umem = ib_umem_get(ib_pd->uobject->context, - ucmd.buf_addr, hr_qp->buff_size, 0, - 0); + hr_qp->umem = ib_umem_get(udata, ucmd.buf_addr, + hr_qp->buff_size, 0, 0); if (IS_ERR(hr_qp->umem)) { dev_err(dev, "ib_umem_get error for create qp\n"); ret = PTR_ERR(hr_qp->umem); @@ -654,8 +653,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, (udata->outlen >= sizeof(resp)) && hns_roce_qp_has_sq(init_attr)) { ret = hns_roce_db_map_user( - to_hr_ucontext(ib_pd->uobject->context), - ucmd.sdb_addr, &hr_qp->sdb); + to_hr_ucontext(ib_pd->uobject->context), udata, + ucmd.sdb_addr, &hr_qp->sdb); if (ret) { dev_err(dev, "sq record doorbell map failed!\n"); goto err_mtt; @@ -670,8 +669,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, (udata->outlen >= sizeof(resp)) && hns_roce_qp_has_rq(init_attr)) { ret = hns_roce_db_map_user( - to_hr_ucontext(ib_pd->uobject->context), - ucmd.db_addr, &hr_qp->rdb); + to_hr_ucontext(ib_pd->uobject->context), udata, + ucmd.db_addr, &hr_qp->rdb); if (ret) { dev_err(dev, "rq record doorbell map failed!\n"); goto err_sq_dbmap; diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c index 960b1946c365..8975f858b36f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_srq.c +++ b/drivers/infiniband/hw/hns/hns_roce_srq.c @@ -252,8 +252,8 @@ struct ib_srq *hns_roce_create_srq(struct ib_pd *pd, goto err_srq; } - srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, - srq_buf_size, 0, 0); + srq->umem = + ib_umem_get(udata, ucmd.buf_addr, srq_buf_size, 0, 0); if (IS_ERR(srq->umem)) { ret = PTR_ERR(srq->umem); goto err_srq; @@ -280,8 +280,7 @@ struct ib_srq *hns_roce_create_srq(struct ib_pd *pd, goto err_srq_mtt; /* config index queue BA */ - srq->idx_que.umem = ib_umem_get(pd->uobject->context, - ucmd.que_addr, + srq->idx_que.umem = ib_umem_get(udata, ucmd.que_addr, srq->idx_que.buf_size, 0, 0); if (IS_ERR(srq->idx_que.umem)) { dev_err(hr_dev->dev, diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 0b675b0742c2..80b66df95362 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -1852,7 +1852,7 @@ static struct ib_mr *i40iw_reg_user_mr(struct ib_pd *pd, if (length > I40IW_MAX_MR_SIZE) return ERR_PTR(-EINVAL); - region = ib_umem_get(pd->uobject->context, start, length, acc, 0); + region = ib_umem_get(udata, start, length, acc, 0); if (IS_ERR(region)) return (struct ib_mr *)region; diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index 43512347b4f0..db936c12b5bd 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -134,16 +134,16 @@ static void mlx4_ib_free_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf * mlx4_buf_free(dev->dev, (cqe + 1) * buf->entry_size, &buf->buf); } -static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, struct ib_ucontext *context, - struct mlx4_ib_cq_buf *buf, struct ib_umem **umem, - u64 buf_addr, int cqe) +static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, struct ib_udata *udata, + struct mlx4_ib_cq_buf *buf, + struct ib_umem **umem, u64 buf_addr, int cqe) { int err; int cqe_size = dev->dev->caps.cqe_size; int shift; int n; - *umem = ib_umem_get(context, buf_addr, cqe * cqe_size, + *umem = ib_umem_get(udata, buf_addr, cqe * cqe_size, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(*umem)) return PTR_ERR(*umem); @@ -213,14 +213,13 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, } buf_addr = (void *)(unsigned long)ucmd.buf_addr; - - err = mlx4_ib_get_cq_umem(dev, context, &cq->buf, &cq->umem, + err = mlx4_ib_get_cq_umem(dev, udata, &cq->buf, &cq->umem, ucmd.buf_addr, entries); if (err) goto err_cq; - err = mlx4_ib_db_map_user(to_mucontext(context), ucmd.db_addr, - &cq->db); + err = mlx4_ib_db_map_user(to_mucontext(context), udata, + ucmd.db_addr, &cq->db); if (err) goto err_mtt; @@ -336,7 +335,7 @@ static int mlx4_alloc_resize_umem(struct mlx4_ib_dev *dev, struct mlx4_ib_cq *cq if (!cq->resize_buf) return -ENOMEM; - err = mlx4_ib_get_cq_umem(dev, cq->umem->context, &cq->resize_buf->buf, + err = mlx4_ib_get_cq_umem(dev, udata, &cq->resize_buf->buf, &cq->resize_umem, ucmd.buf_addr, entries); if (err) { kfree(cq->resize_buf); diff --git a/drivers/infiniband/hw/mlx4/doorbell.c b/drivers/infiniband/hw/mlx4/doorbell.c index c51740986367..3aab71b29ce8 100644 --- a/drivers/infiniband/hw/mlx4/doorbell.c +++ b/drivers/infiniband/hw/mlx4/doorbell.c @@ -41,7 +41,8 @@ struct mlx4_ib_user_db_page { int refcnt; }; -int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt, +int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, + struct ib_udata *udata, unsigned long virt, struct mlx4_db *db) { struct mlx4_ib_user_db_page *page; @@ -61,8 +62,7 @@ int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt, page->user_virt = (virt & PAGE_MASK); page->refcnt = 0; - page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK, - PAGE_SIZE, 0, 0); + page->umem = ib_umem_get(udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0); if (IS_ERR(page->umem)) { err = PTR_ERR(page->umem); kfree(page); diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index e491f3eda6e7..60dc1347c5ab 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -722,7 +722,8 @@ static inline u8 mlx4_ib_bond_next_port(struct mlx4_ib_dev *dev) int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev); void mlx4_ib_close_sriov(struct mlx4_ib_dev *dev); -int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, unsigned long virt, +int mlx4_ib_db_map_user(struct mlx4_ib_ucontext *context, + struct ib_udata *udata, unsigned long virt, struct mlx4_db *db); void mlx4_ib_db_unmap_user(struct mlx4_ib_ucontext *context, struct mlx4_db *db); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index c7c85c22e4e3..56639ecd53ad 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -367,7 +367,8 @@ end: return block_shift; } -static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, u64 start, +static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, + struct ib_udata *udata, u64 start, u64 length, u64 virt_addr, int access_flags) { @@ -398,7 +399,7 @@ static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, u64 start, up_read(¤t->mm->mmap_sem); } - return ib_umem_get(context, start, length, access_flags, 0); + return ib_umem_get(udata, start, length, access_flags, 0); } struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, @@ -415,7 +416,7 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (!mr) return ERR_PTR(-ENOMEM); - mr->umem = mlx4_get_umem_mr(pd->uobject->context, start, length, + mr->umem = mlx4_get_umem_mr(pd->uobject->context, udata, start, length, virt_addr, access_flags); if (IS_ERR(mr->umem)) { err = PTR_ERR(mr->umem); @@ -506,8 +507,8 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, mlx4_mr_rereg_mem_cleanup(dev->dev, &mmr->mmr); ib_umem_release(mmr->umem); mmr->umem = - mlx4_get_umem_mr(mr->uobject->context, start, length, - virt_addr, mr_access_flags); + mlx4_get_umem_mr(mr->uobject->context, udata, start, + length, virt_addr, mr_access_flags); if (IS_ERR(mmr->umem)) { err = PTR_ERR(mmr->umem); /* Prevent mlx4_ib_dereg_mr from free'ing invalid pointer */ diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 971e9a9ebdaf..e38bab50cecf 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1015,9 +1015,11 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, (qp->sq.wqe_cnt << qp->sq.wqe_shift); } - qp->umem = ib_umem_get(pd->uobject->context, - (src == MLX4_IB_QP_SRC) ? ucmd.qp.buf_addr : - ucmd.wq.buf_addr, qp->buf_size, 0, 0); + qp->umem = + ib_umem_get(udata, + (src == MLX4_IB_QP_SRC) ? ucmd.qp.buf_addr : + ucmd.wq.buf_addr, + qp->buf_size, 0, 0); if (IS_ERR(qp->umem)) { err = PTR_ERR(qp->umem); goto err; @@ -1035,9 +1037,11 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, goto err_mtt; if (qp_has_rq(init_attr)) { - err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context), + err = mlx4_ib_db_map_user( + to_mucontext(pd->uobject->context), udata, (src == MLX4_IB_QP_SRC) ? ucmd.qp.db_addr : - ucmd.wq.db_addr, &qp->db); + ucmd.wq.db_addr, + &qp->db); if (err) goto err_mtt; } diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c index 4456f1b8921d..498588eac051 100644 --- a/drivers/infiniband/hw/mlx4/srq.c +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -113,8 +113,7 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, goto err_srq; } - srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, - buf_size, 0, 0); + srq->umem = ib_umem_get(udata, ucmd.buf_addr, buf_size, 0, 0); if (IS_ERR(srq->umem)) { err = PTR_ERR(srq->umem); goto err_srq; @@ -130,7 +129,7 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, goto err_mtt; err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context), - ucmd.db_addr, &srq->db); + udata, ucmd.db_addr, &srq->db); if (err) goto err_mtt; } else { diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 90f1b0bae5b5..c283c32f30fe 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -707,15 +707,15 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, *cqe_size = ucmd.cqe_size; - cq->buf.umem = ib_umem_get(context, ucmd.buf_addr, - entries * ucmd.cqe_size, - IB_ACCESS_LOCAL_WRITE, 1); + cq->buf.umem = + ib_umem_get(udata, ucmd.buf_addr, entries * ucmd.cqe_size, + IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(cq->buf.umem)) { err = PTR_ERR(cq->buf.umem); return err; } - err = mlx5_ib_db_map_user(to_mucontext(context), ucmd.db_addr, + err = mlx5_ib_db_map_user(to_mucontext(context), udata, ucmd.db_addr, &cq->db); if (err) goto err_umem; @@ -1111,7 +1111,6 @@ static int resize_user(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, struct ib_umem *umem; int err; int npages; - struct ib_ucontext *context = cq->buf.umem->context; err = ib_copy_from_udata(&ucmd, udata, sizeof(ucmd)); if (err) @@ -1124,7 +1123,7 @@ static int resize_user(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, if (ucmd.cqe_size && SIZE_MAX / ucmd.cqe_size <= entries - 1) return -EINVAL; - umem = ib_umem_get(context, ucmd.buf_addr, + umem = ib_umem_get(udata, ucmd.buf_addr, (size_t)ucmd.cqe_size * entries, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(umem)) { diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 5a588f3cfb1b..8bb711da7ee1 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1195,7 +1195,7 @@ static int devx_umem_get(struct mlx5_ib_dev *dev, struct ib_ucontext *ucontext, if (err) return err; - obj->umem = ib_umem_get(ucontext, addr, size, access, 0); + obj->umem = ib_umem_get(&attrs->driver_udata, addr, size, access, 0); if (IS_ERR(obj->umem)) return PTR_ERR(obj->umem); diff --git a/drivers/infiniband/hw/mlx5/doorbell.c b/drivers/infiniband/hw/mlx5/doorbell.c index a0e4e6ddb71a..8f4e5f22b84c 100644 --- a/drivers/infiniband/hw/mlx5/doorbell.c +++ b/drivers/infiniband/hw/mlx5/doorbell.c @@ -43,7 +43,8 @@ struct mlx5_ib_user_db_page { int refcnt; }; -int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt, +int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, + struct ib_udata *udata, unsigned long virt, struct mlx5_db *db) { struct mlx5_ib_user_db_page *page; @@ -63,8 +64,7 @@ int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt, page->user_virt = (virt & PAGE_MASK); page->refcnt = 0; - page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK, - PAGE_SIZE, 0, 0); + page->umem = ib_umem_get(udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0); if (IS_ERR(page->umem)) { err = PTR_ERR(page->umem); kfree(page); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 81efa5def8ad..b0a37ca2a714 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1037,7 +1037,8 @@ to_mflow_act(struct ib_flow_action *ibact) return container_of(ibact, struct mlx5_ib_flow_action, ib_action); } -int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt, +int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, + struct ib_udata *udata, unsigned long virt, struct mlx5_db *db); void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db); void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq); @@ -1103,6 +1104,7 @@ int mlx5_ib_dealloc_mw(struct ib_mw *mw); int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags); struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd, + struct ib_udata *udata, int access_flags); void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *mr); int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 494a90f4348c..6d763d6a189f 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -843,18 +843,17 @@ static int mr_cache_max_order(struct mlx5_ib_dev *dev) return MLX5_MAX_UMR_SHIFT; } -static int mr_umem_get(struct ib_pd *pd, u64 start, u64 length, - int access_flags, struct ib_umem **umem, - int *npages, int *page_shift, int *ncont, - int *order) +static int mr_umem_get(struct mlx5_ib_dev *dev, struct ib_udata *udata, + u64 start, u64 length, int access_flags, + struct ib_umem **umem, int *npages, int *page_shift, + int *ncont, int *order) { - struct mlx5_ib_dev *dev = to_mdev(pd->device); struct ib_umem *u; int err; *umem = NULL; - u = ib_umem_get(pd->uobject->context, start, length, access_flags, 0); + u = ib_umem_get(udata, start, length, access_flags, 0); err = PTR_ERR_OR_ZERO(u); if (err) { mlx5_ib_dbg(dev, "umem get failed (%d)\n", err); @@ -1333,14 +1332,14 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, !(dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) return ERR_PTR(-EINVAL); - mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), access_flags); + mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), udata, access_flags); if (IS_ERR(mr)) return ERR_CAST(mr); return &mr->ibmr; } - err = mr_umem_get(pd, start, length, access_flags, &umem, &npages, - &page_shift, &ncont, &order); + err = mr_umem_get(dev, udata, start, length, access_flags, &umem, + &npages, &page_shift, &ncont, &order); if (err < 0) return ERR_PTR(err); @@ -1488,8 +1487,9 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, flags |= IB_MR_REREG_TRANS; ib_umem_release(mr->umem); mr->umem = NULL; - err = mr_umem_get(pd, addr, len, access_flags, &mr->umem, - &npages, &page_shift, &ncont, &order); + err = mr_umem_get(dev, udata, addr, len, access_flags, + &mr->umem, &npages, &page_shift, &ncont, + &order); if (err) goto err; } diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 37d6653f9636..8d46b1dc5658 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -492,13 +492,13 @@ next_mr: } struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd, + struct ib_udata *udata, int access_flags) { - struct ib_ucontext *ctx = pd->ibpd.uobject->context; struct mlx5_ib_mr *imr; struct ib_umem *umem; - umem = ib_umem_get(ctx, 0, 0, IB_ACCESS_ON_DEMAND, 0); + umem = ib_umem_get(udata, 0, 0, IB_ACCESS_ON_DEMAND, 0); if (IS_ERR(umem)) return ERR_CAST(umem); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index dd2ae640bc84..529e76f67cb6 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -645,16 +645,14 @@ int bfregn_to_uar_index(struct mlx5_ib_dev *dev, return bfregi->sys_pages[index_of_sys_page] + offset; } -static int mlx5_ib_umem_get(struct mlx5_ib_dev *dev, - struct ib_pd *pd, +static int mlx5_ib_umem_get(struct mlx5_ib_dev *dev, struct ib_udata *udata, unsigned long addr, size_t size, - struct ib_umem **umem, - int *npages, int *page_shift, int *ncont, - u32 *offset) + struct ib_umem **umem, int *npages, int *page_shift, + int *ncont, u32 *offset) { int err; - *umem = ib_umem_get(pd->uobject->context, addr, size, 0, 0); + *umem = ib_umem_get(udata, addr, size, 0, 0); if (IS_ERR(*umem)) { mlx5_ib_dbg(dev, "umem_get failed\n"); return PTR_ERR(*umem); @@ -695,10 +693,9 @@ static void destroy_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd, } static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd, - struct mlx5_ib_rwq *rwq, + struct ib_udata *udata, struct mlx5_ib_rwq *rwq, struct mlx5_ib_create_wq *ucmd) { - struct mlx5_ib_ucontext *context; int page_shift = 0; int npages; u32 offset = 0; @@ -708,9 +705,7 @@ static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd, if (!ucmd->buf_addr) return -EINVAL; - context = to_mucontext(pd->uobject->context); - rwq->umem = ib_umem_get(pd->uobject->context, ucmd->buf_addr, - rwq->buf_size, 0, 0); + rwq->umem = ib_umem_get(udata, ucmd->buf_addr, rwq->buf_size, 0, 0); if (IS_ERR(rwq->umem)) { mlx5_ib_dbg(dev, "umem_get failed\n"); err = PTR_ERR(rwq->umem); @@ -735,7 +730,8 @@ static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd, (unsigned long long)ucmd->buf_addr, rwq->buf_size, npages, page_shift, ncont, offset); - err = mlx5_ib_db_map_user(context, ucmd->db_addr, &rwq->db); + err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context), udata, + ucmd->db_addr, &rwq->db); if (err) { mlx5_ib_dbg(dev, "map failed\n"); goto err_umem; @@ -819,10 +815,9 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, if (ucmd.buf_addr && ubuffer->buf_size) { ubuffer->buf_addr = ucmd.buf_addr; - err = mlx5_ib_umem_get(dev, pd, ubuffer->buf_addr, - ubuffer->buf_size, - &ubuffer->umem, &npages, &page_shift, - &ncont, &offset); + err = mlx5_ib_umem_get(dev, udata, ubuffer->buf_addr, + ubuffer->buf_size, &ubuffer->umem, + &npages, &page_shift, &ncont, &offset); if (err) goto err_bfreg; } else { @@ -856,7 +851,7 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, resp->bfreg_index = MLX5_IB_INVALID_BFREG; qp->bfregn = bfregn; - err = mlx5_ib_db_map_user(context, ucmd.db_addr, &qp->db); + err = mlx5_ib_db_map_user(context, udata, ucmd.db_addr, &qp->db); if (err) { mlx5_ib_dbg(dev, "map failed\n"); goto err_free; @@ -1119,6 +1114,7 @@ static void destroy_flow_rule_vport_sq(struct mlx5_ib_dev *dev, } static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev, + struct ib_udata *udata, struct mlx5_ib_sq *sq, void *qpin, struct ib_pd *pd) { @@ -1135,9 +1131,9 @@ static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev, int ncont = 0; u32 offset = 0; - err = mlx5_ib_umem_get(dev, pd, ubuffer->buf_addr, ubuffer->buf_size, - &sq->ubuffer.umem, &npages, &page_shift, - &ncont, &offset); + err = mlx5_ib_umem_get(dev, udata, ubuffer->buf_addr, ubuffer->buf_size, + &sq->ubuffer.umem, &npages, &page_shift, &ncont, + &offset); if (err) return err; @@ -1374,7 +1370,7 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, if (err) return err; - err = create_raw_packet_qp_sq(dev, sq, in, pd); + err = create_raw_packet_qp_sq(dev, udata, sq, in, pd); if (err) goto err_destroy_tis; @@ -5793,7 +5789,7 @@ static int prepare_user_rq(struct ib_pd *pd, return err; } - err = create_user_rq(dev, pd, rwq, &ucmd); + err = create_user_rq(dev, pd, udata, rwq, &ucmd); if (err) { mlx5_ib_dbg(dev, "err %d\n", err); return err; diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 4e8d18009f58..22bd774e0b4e 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -79,8 +79,7 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, srq->wq_sig = !!(ucmd.flags & MLX5_SRQ_FLAG_SIGNATURE); - srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, buf_size, - 0, 0); + srq->umem = ib_umem_get(udata, ucmd.buf_addr, buf_size, 0, 0); if (IS_ERR(srq->umem)) { mlx5_ib_dbg(dev, "failed umem get, size %d\n", buf_size); err = PTR_ERR(srq->umem); @@ -104,7 +103,7 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, mlx5_ib_populate_pas(dev, srq->umem, page_shift, in->pas, 0); - err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context), + err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context), udata, ucmd.db_addr, &srq->db); if (err) { mlx5_ib_dbg(dev, "map doorbell failed\n"); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 82cb6b71ac7c..53fff6aed896 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -931,7 +931,7 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (!mr) return ERR_PTR(-ENOMEM); - mr->umem = ib_umem_get(pd->uobject->context, start, length, acc, + mr->umem = ib_umem_get(udata, start, length, acc, ucmd.mr_attrs & MTHCA_MR_DMASYNC); if (IS_ERR(mr->umem)) { diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 4e7f08ee1907..feb4d259aab9 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -2134,7 +2134,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u8 stag_key; int first_page = 1; - region = ib_umem_get(pd->uobject->context, start, length, acc, 0); + region = ib_umem_get(udata, start, length, acc, 0); if (IS_ERR(region)) { return (struct ib_mr *)region; } diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index c46bed0c5513..a8b40cb7679e 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -916,7 +916,7 @@ struct ib_mr *ocrdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, mr = kzalloc(sizeof(*mr), GFP_KERNEL); if (!mr) return ERR_PTR(status); - mr->umem = ib_umem_get(ibpd->uobject->context, start, len, acc, 0); + mr->umem = ib_umem_get(udata, start, len, acc, 0); if (IS_ERR(mr->umem)) { status = -EFAULT; goto umem_err; diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index b342a70e2814..ba8cb6559122 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -736,11 +736,10 @@ static inline int qedr_align_cq_entries(int entries) return aligned_size / QEDR_CQE_SIZE; } -static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx, +static inline int qedr_init_user_queue(struct ib_udata *udata, struct qedr_dev *dev, - struct qedr_userq *q, - u64 buf_addr, size_t buf_len, - int access, int dmasync, + struct qedr_userq *q, u64 buf_addr, + size_t buf_len, int access, int dmasync, int alloc_and_init) { u32 fw_pages; @@ -748,7 +747,7 @@ static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx, q->buf_addr = buf_addr; q->buf_len = buf_len; - q->umem = ib_umem_get(ib_ctx, q->buf_addr, q->buf_len, access, dmasync); + q->umem = ib_umem_get(udata, q->buf_addr, q->buf_len, access, dmasync); if (IS_ERR(q->umem)) { DP_ERR(dev, "create user queue: failed ib_umem_get, got %ld\n", PTR_ERR(q->umem)); @@ -905,9 +904,9 @@ struct ib_cq *qedr_create_cq(struct ib_device *ibdev, cq->cq_type = QEDR_CQ_TYPE_USER; - rc = qedr_init_user_queue(ib_ctx, dev, &cq->q, ureq.addr, - ureq.len, IB_ACCESS_LOCAL_WRITE, - 1, 1); + rc = qedr_init_user_queue(udata, dev, &cq->q, ureq.addr, + ureq.len, IB_ACCESS_LOCAL_WRITE, 1, + 1); if (rc) goto err0; @@ -1344,7 +1343,7 @@ static void qedr_free_srq_kernel_params(struct qedr_srq *srq) hw_srq->phy_prod_pair_addr); } -static int qedr_init_srq_user_params(struct ib_ucontext *ib_ctx, +static int qedr_init_srq_user_params(struct ib_udata *udata, struct qedr_srq *srq, struct qedr_create_srq_ureq *ureq, int access, int dmasync) @@ -1352,14 +1351,14 @@ static int qedr_init_srq_user_params(struct ib_ucontext *ib_ctx, struct scatterlist *sg; int rc; - rc = qedr_init_user_queue(ib_ctx, srq->dev, &srq->usrq, ureq->srq_addr, + rc = qedr_init_user_queue(udata, srq->dev, &srq->usrq, ureq->srq_addr, ureq->srq_len, access, dmasync, 1); if (rc) return rc; - srq->prod_umem = ib_umem_get(ib_ctx, ureq->prod_pair_addr, - sizeof(struct rdma_srq_producers), - access, dmasync); + srq->prod_umem = + ib_umem_get(udata, ureq->prod_pair_addr, + sizeof(struct rdma_srq_producers), access, dmasync); if (IS_ERR(srq->prod_umem)) { qedr_free_pbl(srq->dev, &srq->usrq.pbl_info, srq->usrq.pbl_tbl); ib_umem_release(srq->usrq.umem); @@ -1468,7 +1467,7 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd, goto err0; } - rc = qedr_init_srq_user_params(ib_ctx, srq, &ureq, 0, 0); + rc = qedr_init_srq_user_params(udata, srq, &ureq, 0, 0); if (rc) goto err0; @@ -1714,14 +1713,14 @@ static int qedr_create_user_qp(struct qedr_dev *dev, } /* SQ - read access only (0), dma sync not required (0) */ - rc = qedr_init_user_queue(ib_ctx, dev, &qp->usq, ureq.sq_addr, + rc = qedr_init_user_queue(udata, dev, &qp->usq, ureq.sq_addr, ureq.sq_len, 0, 0, alloc_and_init); if (rc) return rc; if (!qp->srq) { /* RQ - read access only (0), dma sync not required (0) */ - rc = qedr_init_user_queue(ib_ctx, dev, &qp->urq, ureq.rq_addr, + rc = qedr_init_user_queue(udata, dev, &qp->urq, ureq.rq_addr, ureq.rq_len, 0, 0, alloc_and_init); if (rc) return rc; @@ -2719,7 +2718,7 @@ struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, mr->type = QEDR_MR_USER; - mr->umem = ib_umem_get(ibpd->uobject->context, start, len, acc, 0); + mr->umem = ib_umem_get(udata, start, len, acc, 0); if (IS_ERR(mr->umem)) { rc = -EFAULT; goto err0; diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c index 0f004c737620..104c7db4704f 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c @@ -141,7 +141,7 @@ struct ib_cq *pvrdma_create_cq(struct ib_device *ibdev, goto err_cq; } - cq->umem = ib_umem_get(context, ucmd.buf_addr, ucmd.buf_size, + cq->umem = ib_umem_get(udata, ucmd.buf_addr, ucmd.buf_size, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(cq->umem)) { ret = PTR_ERR(cq->umem); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c index fa96fa4fb829..a85884e90e84 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_mr.c @@ -126,8 +126,7 @@ struct ib_mr *pvrdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, return ERR_PTR(-EINVAL); } - umem = ib_umem_get(pd->uobject->context, start, - length, access_flags, 0); + umem = ib_umem_get(udata, start, length, access_flags, 0); if (IS_ERR(umem)) { dev_warn(&dev->pdev->dev, "could not get umem for mem region\n"); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c index 3acf74cbe266..5fc444cef011 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c @@ -262,8 +262,7 @@ struct ib_qp *pvrdma_create_qp(struct ib_pd *pd, if (!is_srq) { /* set qp->sq.wqe_cnt, shift, buf_size.. */ - qp->rumem = ib_umem_get(pd->uobject->context, - ucmd.rbuf_addr, + qp->rumem = ib_umem_get(udata, ucmd.rbuf_addr, ucmd.rbuf_size, 0, 0); if (IS_ERR(qp->rumem)) { ret = PTR_ERR(qp->rumem); @@ -275,8 +274,7 @@ struct ib_qp *pvrdma_create_qp(struct ib_pd *pd, qp->srq = to_vsrq(init_attr->srq); } - qp->sumem = ib_umem_get(pd->uobject->context, - ucmd.sbuf_addr, + qp->sumem = ib_umem_get(udata, ucmd.sbuf_addr, ucmd.sbuf_size, 0, 0); if (IS_ERR(qp->sumem)) { if (!is_srq) diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c index 06ba7c7a2235..951d9d68107a 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c @@ -153,9 +153,7 @@ struct ib_srq *pvrdma_create_srq(struct ib_pd *pd, goto err_srq; } - srq->umem = ib_umem_get(pd->uobject->context, - ucmd.buf_addr, - ucmd.buf_size, 0, 0); + srq->umem = ib_umem_get(udata, ucmd.buf_addr, ucmd.buf_size, 0, 0); if (IS_ERR(srq->umem)) { ret = PTR_ERR(srq->umem); goto err_srq; diff --git a/drivers/infiniband/sw/rdmavt/mr.c b/drivers/infiniband/sw/rdmavt/mr.c index 49c9541050d4..8b1c1e8dd7ef 100644 --- a/drivers/infiniband/sw/rdmavt/mr.c +++ b/drivers/infiniband/sw/rdmavt/mr.c @@ -388,8 +388,7 @@ struct ib_mr *rvt_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (length == 0) return ERR_PTR(-EINVAL); - umem = ib_umem_get(pd->uobject->context, start, length, - mr_access_flags, 0); + umem = ib_umem_get(udata, start, length, mr_access_flags, 0); if (IS_ERR(umem)) return (void *)umem; diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c index 9d3916b93f23..2438093776a0 100644 --- a/drivers/infiniband/sw/rxe/rxe_mr.c +++ b/drivers/infiniband/sw/rxe/rxe_mr.c @@ -171,7 +171,7 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start, void *vaddr; int err; - umem = ib_umem_get(pd->ibpd.uobject->context, start, length, access, 0); + umem = ib_umem_get(udata, start, length, access, 0); if (IS_ERR(umem)) { pr_warn("err %d from rxe_umem_get\n", (int)PTR_ERR(umem)); diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 5d3755ec5afa..73af05db04c7 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -36,6 +36,7 @@ #include #include #include +#include struct ib_ucontext; struct ib_umem_odp; @@ -80,7 +81,7 @@ static inline size_t ib_umem_num_pages(struct ib_umem *umem) #ifdef CONFIG_INFINIBAND_USER_MEM -struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, +struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, size_t size, int access, int dmasync); void ib_umem_release(struct ib_umem *umem); int ib_umem_page_count(struct ib_umem *umem); @@ -91,9 +92,10 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, #include -static inline struct ib_umem *ib_umem_get(struct ib_ucontext *context, +static inline struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, size_t size, - int access, int dmasync) { + int access, int dmasync) +{ return ERR_PTR(-EINVAL); } static inline void ib_umem_release(struct ib_umem *umem) { } diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 3ddd199ba602..aa1f126d3383 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -4200,6 +4200,7 @@ void rdma_roce_rescan_device(struct ib_device *ibdev); struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile); +struct ib_ucontext *rdma_get_ucontext(struct ib_udata *udata); int uverbs_destroy_def_handler(struct uverbs_attr_bundle *attrs); From 4959d5da5737dd804255c75b8cea0a2929ce279a Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 7 Jan 2019 17:27:54 +0200 Subject: [PATCH 0214/4277] IB/usnic: Fix out of bounds index check in query pkey The pkey table size is one element, index should be tested for > 0 instead of > 1. Fixes: e3cf00d0a87f ("IB/usnic: Add Cisco VIC low-level hardware driver") Signed-off-by: Gal Pressman Acked-by: Parvi Kaustubhi Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 1d4abef17e38..63c7b48774e9 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -449,7 +449,7 @@ struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num) int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { - if (index > 1) + if (index > 0) return -EINVAL; *pkey = 0xffff; From b188940796c7be31c1b8c25a9a0e0842c2e7a49e Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 7 Jan 2019 17:27:55 +0200 Subject: [PATCH 0215/4277] RDMA/ocrdma: Fix out of bounds index check in query pkey The pkey table size is one element, index should be tested for > 0 instead of > 1. Fixes: fe2caefcdf58 ("RDMA/ocrdma: Add driver for Emulex OneConnect IBoE RDMA adapter") Signed-off-by: Gal Pressman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index a8b40cb7679e..eb69e71c2053 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -55,7 +55,7 @@ int ocrdma_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { - if (index > 1) + if (index > 0) return -EINVAL; *pkey = 0xffff; From dbe30dae487e1a232158c24b432d45281c2805b7 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 7 Jan 2019 17:27:56 +0200 Subject: [PATCH 0216/4277] RDMA/qedr: Fix out of bounds index check in query pkey The pkey table size is QEDR_ROCE_PKEY_TABLE_LEN, index should be tested for >= QEDR_ROCE_PKEY_TABLE_LEN instead of > QEDR_ROCE_PKEY_TABLE_LEN. Fixes: a7efd7773e31 ("qedr: Add support for PD,PKEY and CQ verbs") Signed-off-by: Gal Pressman Acked-by: Michal Kalderon Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qedr/verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index ba8cb6559122..b27ff9408507 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -67,7 +67,7 @@ static inline int qedr_ib_copy_to_udata(struct ib_udata *udata, void *src, int qedr_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { - if (index > QEDR_ROCE_PKEY_TABLE_LEN) + if (index >= QEDR_ROCE_PKEY_TABLE_LEN) return -EINVAL; *pkey = QEDR_ROCE_PKEY_DEFAULT; From 8cbfaac3d0974ecf53b28db23ea8c105f491bb35 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 9 Jan 2019 20:15:58 +0200 Subject: [PATCH 0217/4277] RDMA: Clear PD objects during their allocation As part of an audit process to update drivers to use rdma_restrack_add() ensure that PD objects is cleared before access. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_pd.c | 2 +- drivers/infiniband/hw/mlx5/main.c | 2 +- drivers/infiniband/hw/mthca/mthca_provider.c | 2 +- drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c | 2 +- drivers/infiniband/sw/rdmavt/pd.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_pd.c b/drivers/infiniband/hw/hns/hns_roce_pd.c index e11c149da04d..4a29b2cb9bab 100644 --- a/drivers/infiniband/hw/hns/hns_roce_pd.c +++ b/drivers/infiniband/hw/hns/hns_roce_pd.c @@ -66,7 +66,7 @@ struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev, struct hns_roce_pd *pd; int ret; - pd = kmalloc(sizeof(*pd), GFP_KERNEL); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 11e9783cefcc..717e8535b41a 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2263,7 +2263,7 @@ static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, u32 in[MLX5_ST_SZ_DW(alloc_pd_in)] = {}; u16 uid = 0; - pd = kmalloc(sizeof(*pd), GFP_KERNEL); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 53fff6aed896..f87ee3058c65 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -381,7 +381,7 @@ static struct ib_pd *mthca_alloc_pd(struct ib_device *ibdev, struct mthca_pd *pd; int err; - pd = kmalloc(sizeof *pd, GFP_KERNEL); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index 4d238d0e484b..65b6115caa55 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -457,7 +457,7 @@ struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev, if (!atomic_add_unless(&dev->num_pds, 1, dev->dsr->caps.max_pd)) return ERR_PTR(-ENOMEM); - pd = kmalloc(sizeof(*pd), GFP_KERNEL); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { ptr = ERR_PTR(-ENOMEM); goto err; diff --git a/drivers/infiniband/sw/rdmavt/pd.c b/drivers/infiniband/sw/rdmavt/pd.c index 8a89afff3363..dcc1870b8d23 100644 --- a/drivers/infiniband/sw/rdmavt/pd.c +++ b/drivers/infiniband/sw/rdmavt/pd.c @@ -66,7 +66,7 @@ struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev, struct rvt_pd *pd; struct ib_pd *ret; - pd = kmalloc(sizeof(*pd), GFP_KERNEL); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { ret = ERR_PTR(-ENOMEM); goto bail; From 0975890ebe9b40abf08fecf216aed4f92805db70 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 9 Jan 2019 20:15:59 +0200 Subject: [PATCH 0218/4277] RDMA: Clear CQ objects during their allocation As part of an audit process to update drivers to use rdma_restrack_add() ensure that CQ objects is cleared before access. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx4/cq.c | 2 +- drivers/infiniband/hw/mthca/mthca_provider.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index db936c12b5bd..03ac72339dd2 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -190,7 +190,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, if (attr->flags & ~CQ_CREATE_FLAGS_SUPPORTED) return ERR_PTR(-EINVAL); - cq = kmalloc(sizeof *cq, GFP_KERNEL); + cq = kzalloc(sizeof(*cq), GFP_KERNEL); if (!cq) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index f87ee3058c65..1fcc007d1d34 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -684,7 +684,7 @@ static struct ib_cq *mthca_create_cq(struct ib_device *ibdev, goto err_unmap_set; } - cq = kmalloc(sizeof *cq, GFP_KERNEL); + cq = kzalloc(sizeof(*cq), GFP_KERNEL); if (!cq) { err = -ENOMEM; goto err_unmap_arm; From 081de9495c7a90560e9cc264cdcc2ff39b705843 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 9 Jan 2019 20:16:01 +0200 Subject: [PATCH 0219/4277] RDMA: Clear CTX objects during their allocation As part of an audit process to update drivers to use rdma_restrack_add() ensure that CTX objects is cleared before access. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_main.c | 2 +- drivers/infiniband/hw/mthca/mthca_provider.c | 2 +- drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 2 +- drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c | 2 +- drivers/infiniband/sw/rdmavt/vt.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index c79054ba9495..44a07fce0617 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -343,7 +343,7 @@ static struct ib_ucontext *hns_roce_alloc_ucontext(struct ib_device *ib_dev, resp.qp_tab_size = hr_dev->caps.num_qps; - context = kmalloc(sizeof(*context), GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 1fcc007d1d34..0a939ea5497b 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -318,7 +318,7 @@ static struct ib_ucontext *mthca_alloc_ucontext(struct ib_device *ibdev, else uresp.uarc_size = 0; - context = kmalloc(sizeof *context, GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 63c7b48774e9..432e6f6599fa 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -683,7 +683,7 @@ struct ib_ucontext *usnic_ib_alloc_ucontext(struct ib_device *ibdev, struct usnic_ib_dev *us_ibdev = to_usdev(ibdev); usnic_dbg("\n"); - context = kmalloc(sizeof(*context), GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index 65b6115caa55..e10149248ce2 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -327,7 +327,7 @@ struct ib_ucontext *pvrdma_alloc_ucontext(struct ib_device *ibdev, if (!vdev->ib_active) return ERR_PTR(-EAGAIN); - context = kmalloc(sizeof(*context), GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index aef3aa3fe667..8724a1817158 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -304,7 +304,7 @@ static struct ib_ucontext *rvt_alloc_ucontext(struct ib_device *ibdev, { struct rvt_ucontext *context; - context = kmalloc(sizeof(*context), GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return ERR_PTR(-ENOMEM); return &context->ibucontext; From f05c07b05d67c578d2f11c04482d983dba7ebc9c Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Tue, 18 Dec 2018 15:22:58 +0000 Subject: [PATCH 0220/4277] pinctrl: imx: add imx8qm driver MX8QM contains a system controller that is responsible for controlling the pad setting of the IPs that are present. Communication between the host processor running an OS and the system controller happens through a SCU protocol. This patch adds the SCU based MX8QM pinctrl driver. Cc: Linus Walleij Cc: Shawn Guo Cc: Fabio Estevam Cc: Stefan Agner Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/freescale/Kconfig | 7 + drivers/pinctrl/freescale/Makefile | 1 + drivers/pinctrl/freescale/pinctrl-imx8qm.c | 326 +++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 drivers/pinctrl/freescale/pinctrl-imx8qm.c diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig index 72b869d888e2..b318c24b61be 100644 --- a/drivers/pinctrl/freescale/Kconfig +++ b/drivers/pinctrl/freescale/Kconfig @@ -129,6 +129,13 @@ config PINCTRL_IMX8MQ help Say Y here to enable the imx8mq pinctrl driver +config PINCTRL_IMX8QM + bool "IMX8QM pinctrl driver" + depends on ARCH_MXC && ARM64 + select PINCTRL_IMX_SCU + help + Say Y here to enable the imx8qm pinctrl driver + config PINCTRL_IMX8QXP bool "IMX8QXP pinctrl driver" depends on ARCH_MXC && ARM64 diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile index 6ee398a3e406..a6783f2560ae 100644 --- a/drivers/pinctrl/freescale/Makefile +++ b/drivers/pinctrl/freescale/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PINCTRL_IMX6UL) += pinctrl-imx6ul.o obj-$(CONFIG_PINCTRL_IMX7D) += pinctrl-imx7d.o obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o obj-$(CONFIG_PINCTRL_IMX8MQ) += pinctrl-imx8mq.o +obj-$(CONFIG_PINCTRL_IMX8QM) += pinctrl-imx8qm.o obj-$(CONFIG_PINCTRL_IMX8QXP) += pinctrl-imx8qxp.o obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o diff --git a/drivers/pinctrl/freescale/pinctrl-imx8qm.c b/drivers/pinctrl/freescale/pinctrl-imx8qm.c new file mode 100644 index 000000000000..0b6029b29731 --- /dev/null +++ b/drivers/pinctrl/freescale/pinctrl-imx8qm.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * Dong Aisheng + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-imx.h" + +static const struct pinctrl_pin_desc imx8qm_pinctrl_pads[] = { + IMX_PINCTRL_PIN(IMX8QM_SIM0_CLK), + IMX_PINCTRL_PIN(IMX8QM_SIM0_RST), + IMX_PINCTRL_PIN(IMX8QM_SIM0_IO), + IMX_PINCTRL_PIN(IMX8QM_SIM0_PD), + IMX_PINCTRL_PIN(IMX8QM_SIM0_POWER_EN), + IMX_PINCTRL_PIN(IMX8QM_SIM0_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_SIM), + IMX_PINCTRL_PIN(IMX8QM_M40_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_M40_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_M40_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_M40_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_M41_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_M41_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_M41_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_M41_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_GPT0_CLK), + IMX_PINCTRL_PIN(IMX8QM_GPT0_CAPTURE), + IMX_PINCTRL_PIN(IMX8QM_GPT0_COMPARE), + IMX_PINCTRL_PIN(IMX8QM_GPT1_CLK), + IMX_PINCTRL_PIN(IMX8QM_GPT1_CAPTURE), + IMX_PINCTRL_PIN(IMX8QM_GPT1_COMPARE), + IMX_PINCTRL_PIN(IMX8QM_UART0_RX), + IMX_PINCTRL_PIN(IMX8QM_UART0_TX), + IMX_PINCTRL_PIN(IMX8QM_UART0_RTS_B), + IMX_PINCTRL_PIN(IMX8QM_UART0_CTS_B), + IMX_PINCTRL_PIN(IMX8QM_UART1_TX), + IMX_PINCTRL_PIN(IMX8QM_UART1_RX), + IMX_PINCTRL_PIN(IMX8QM_UART1_RTS_B), + IMX_PINCTRL_PIN(IMX8QM_UART1_CTS_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOLH), + IMX_PINCTRL_PIN(IMX8QM_SCU_PMIC_MEMC_ON), + IMX_PINCTRL_PIN(IMX8QM_SCU_WDOG_OUT), + IMX_PINCTRL_PIN(IMX8QM_PMIC_I2C_SDA), + IMX_PINCTRL_PIN(IMX8QM_PMIC_I2C_SCL), + IMX_PINCTRL_PIN(IMX8QM_PMIC_EARLY_WARNING), + IMX_PINCTRL_PIN(IMX8QM_PMIC_INT_B), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_02), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_03), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_04), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_05), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_06), + IMX_PINCTRL_PIN(IMX8QM_SCU_GPIO0_07), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE0), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE1), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE2), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE3), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE4), + IMX_PINCTRL_PIN(IMX8QM_SCU_BOOT_MODE5), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_GPIO00), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_GPIO01), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C1_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS0_I2C1_SDA), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_GPIO00), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_GPIO01), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C1_SCL), + IMX_PINCTRL_PIN(IMX8QM_LVDS1_I2C1_SDA), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_LVDSGPIO), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI0_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_DSI1_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_MIPIDSIGPIO), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_MCLK_OUT), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI0_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_MCLK_OUT), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_GPIO0_00), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_GPIO0_01), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_I2C0_SCL), + IMX_PINCTRL_PIN(IMX8QM_MIPI_CSI1_I2C0_SDA), + IMX_PINCTRL_PIN(IMX8QM_HDMI_TX0_TS_SCL), + IMX_PINCTRL_PIN(IMX8QM_HDMI_TX0_TS_SDA), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_3V3_HDMIGPIO), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_FSR), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_FST), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_SCKR), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_SCKT), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX0), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX2_RX3), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX3_RX2), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX4_RX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI1_TX5_RX0), + IMX_PINCTRL_PIN(IMX8QM_SPDIF0_RX), + IMX_PINCTRL_PIN(IMX8QM_SPDIF0_TX), + IMX_PINCTRL_PIN(IMX8QM_SPDIF0_EXT_CLK), + IMX_PINCTRL_PIN(IMX8QM_SPI3_SCK), + IMX_PINCTRL_PIN(IMX8QM_SPI3_SDO), + IMX_PINCTRL_PIN(IMX8QM_SPI3_SDI), + IMX_PINCTRL_PIN(IMX8QM_SPI3_CS0), + IMX_PINCTRL_PIN(IMX8QM_SPI3_CS1), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHB), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_FSR), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_FST), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_SCKR), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_SCKT), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX0), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX2_RX3), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX3_RX2), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX4_RX1), + IMX_PINCTRL_PIN(IMX8QM_ESAI0_TX5_RX0), + IMX_PINCTRL_PIN(IMX8QM_MCLK_IN0), + IMX_PINCTRL_PIN(IMX8QM_MCLK_OUT0), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHC), + IMX_PINCTRL_PIN(IMX8QM_SPI0_SCK), + IMX_PINCTRL_PIN(IMX8QM_SPI0_SDO), + IMX_PINCTRL_PIN(IMX8QM_SPI0_SDI), + IMX_PINCTRL_PIN(IMX8QM_SPI0_CS0), + IMX_PINCTRL_PIN(IMX8QM_SPI0_CS1), + IMX_PINCTRL_PIN(IMX8QM_SPI2_SCK), + IMX_PINCTRL_PIN(IMX8QM_SPI2_SDO), + IMX_PINCTRL_PIN(IMX8QM_SPI2_SDI), + IMX_PINCTRL_PIN(IMX8QM_SPI2_CS0), + IMX_PINCTRL_PIN(IMX8QM_SPI2_CS1), + IMX_PINCTRL_PIN(IMX8QM_SAI1_RXC), + IMX_PINCTRL_PIN(IMX8QM_SAI1_RXD), + IMX_PINCTRL_PIN(IMX8QM_SAI1_RXFS), + IMX_PINCTRL_PIN(IMX8QM_SAI1_TXC), + IMX_PINCTRL_PIN(IMX8QM_SAI1_TXD), + IMX_PINCTRL_PIN(IMX8QM_SAI1_TXFS), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIORHT), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN7), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN6), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN5), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN4), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN3), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN2), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN1), + IMX_PINCTRL_PIN(IMX8QM_ADC_IN0), + IMX_PINCTRL_PIN(IMX8QM_MLB_SIG), + IMX_PINCTRL_PIN(IMX8QM_MLB_CLK), + IMX_PINCTRL_PIN(IMX8QM_MLB_DATA), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOLHT), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN0_RX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN0_TX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN1_RX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN1_TX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN2_RX), + IMX_PINCTRL_PIN(IMX8QM_FLEXCAN2_TX), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOTHR), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC0), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC1), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC2), + IMX_PINCTRL_PIN(IMX8QM_USB_SS3_TC3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_3V3_USB3IO), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_RESET_B), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_VSELECT), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_RESET_B), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_VSELECT), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_WP), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_CD_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSELSEP), + IMX_PINCTRL_PIN(IMX8QM_ENET0_MDIO), + IMX_PINCTRL_PIN(IMX8QM_ENET0_MDC), + IMX_PINCTRL_PIN(IMX8QM_ENET0_REFCLK_125M_25M), + IMX_PINCTRL_PIN(IMX8QM_ENET1_REFCLK_125M_25M), + IMX_PINCTRL_PIN(IMX8QM_ENET1_MDIO), + IMX_PINCTRL_PIN(IMX8QM_ENET1_MDC), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_GPIOCT), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SS0_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SS1_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_SCLK), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DQS), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA3), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA2), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA1), + IMX_PINCTRL_PIN(IMX8QM_QSPI1A_DATA0), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_QSPI1), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA0), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA1), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA2), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DATA3), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_DQS), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SS0_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SS1_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI0A_SCLK), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SCLK), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA0), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA1), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA2), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DATA3), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_DQS), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SS0_B), + IMX_PINCTRL_PIN(IMX8QM_QSPI0B_SS1_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_QSPI0), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_CLKREQ_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_WAKE_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL0_PERST_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_CLKREQ_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_WAKE_B), + IMX_PINCTRL_PIN(IMX8QM_PCIE_CTRL1_PERST_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_PCIESEP), + IMX_PINCTRL_PIN(IMX8QM_USB_HSIC0_DATA), + IMX_PINCTRL_PIN(IMX8QM_USB_HSIC0_STROBE), + IMX_PINCTRL_PIN(IMX8QM_CALIBRATION_0_HSIC), + IMX_PINCTRL_PIN(IMX8QM_CALIBRATION_1_HSIC), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_CLK), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_CMD), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA0), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA1), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA2), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA3), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA4), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA5), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA6), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_DATA7), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_STROBE), + IMX_PINCTRL_PIN(IMX8QM_EMMC0_RESET_B), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_SD1FIX), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_CLK), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_CMD), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA0), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA1), + IMX_PINCTRL_PIN(IMX8QM_CTL_NAND_RE_P_N), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA2), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA3), + IMX_PINCTRL_PIN(IMX8QM_CTL_NAND_DQS_P_N), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA4), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA5), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA6), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_DATA7), + IMX_PINCTRL_PIN(IMX8QM_USDHC1_STROBE), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSEL2), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_CLK), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_CMD), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA0), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA1), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA2), + IMX_PINCTRL_PIN(IMX8QM_USDHC2_DATA3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_VSEL3), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXC), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_TXD3), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXC), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET0_RGMII_RXD3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETB), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXC), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_TXD3), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXC), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RX_CTL), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD0), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD1), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD2), + IMX_PINCTRL_PIN(IMX8QM_ENET1_RGMII_RXD3), + IMX_PINCTRL_PIN(IMX8QM_COMP_CTL_GPIO_1V8_3V3_ENET_ENETA), +}; + +static const struct imx_pinctrl_soc_info imx8qm_pinctrl_info = { + .pins = imx8qm_pinctrl_pads, + .npins = ARRAY_SIZE(imx8qm_pinctrl_pads), + .flags = IMX_USE_SCU, +}; + +static const struct of_device_id imx8qm_pinctrl_of_match[] = { + { .compatible = "fsl,imx8qm-iomuxc", }, + { /* sentinel */ } +}; + +static int imx8qm_pinctrl_probe(struct platform_device *pdev) +{ + int ret; + + ret = imx_pinctrl_sc_ipc_init(pdev); + if (ret) + return ret; + + return imx_pinctrl_probe(pdev, &imx8qm_pinctrl_info); +} + +static struct platform_driver imx8qm_pinctrl_driver = { + .driver = { + .name = "imx8qm-pinctrl", + .of_match_table = of_match_ptr(imx8qm_pinctrl_of_match), + .suppress_bind_attrs = true, + }, + .probe = imx8qm_pinctrl_probe, +}; + +static int __init imx8qm_pinctrl_init(void) +{ + return platform_driver_register(&imx8qm_pinctrl_driver); +} +arch_initcall(imx8qm_pinctrl_init); From 823868fceae3bac07cf5eccb128d6916e7a5ae9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Fri, 21 Dec 2018 18:32:57 +0100 Subject: [PATCH 0221/4277] pinctrl: armada-37xx: Correct mpp definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a cleanup and fix of the patch by Ken Ma . Fix the mpp definitions according to newest revision of the specification: - northbridge: fix pmic1 gpio number to 7 fix pmic0 gpio number to 6 - southbridge split pcie1 group bit mask to BIT(5) and BIT(9) fix ptp group bit mask to BIT(11) | BIT(12) | BIT(13) add smi group with bit mask BIT(4) [gregory: split the pcie group in 2, as at hardware level they can be configured separately] Signed-off-by: Marek Behún Signed-off-by: Gregory CLEMENT Tested-by: Miquel Raynal Signed-off-by: Linus Walleij --- .../pinctrl/marvell,armada-37xx-pinctrl.txt | 18 +++++++++++++----- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 10 ++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt index c7c088d2dd50..f69f82741cae 100644 --- a/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt @@ -58,11 +58,11 @@ group pwm3 - functions pwm, gpio group pmic1 - - pin 17 + - pin 7 - functions pmic, gpio group pmic0 - - pin 16 + - pin 6 - functions pmic, gpio group i2c2 @@ -112,17 +112,25 @@ group usb2_drvvbus1 - functions drvbus, gpio group sdio_sb - - pins 60-64 + - pins 60-65 - functions sdio, gpio group rgmii - - pins 42-55 + - pins 42-53 - functions mii, gpio group pcie1 - - pins 39-40 + - pins 39 - functions pcie, gpio +group pcie1_clkreq + - pins 40 + - functions pcie, gpio + +group smi + - pins 54-55 + - functions smi, gpio + group ptp - pins 56-58 - functions ptp, gpio diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index aa48b3f23c7f..267dfc530985 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -170,8 +170,8 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { PIN_GRP_GPIO("pwm1", 12, 1, BIT(4), "pwm"), PIN_GRP_GPIO("pwm2", 13, 1, BIT(5), "pwm"), PIN_GRP_GPIO("pwm3", 14, 1, BIT(6), "pwm"), - PIN_GRP_GPIO("pmic1", 17, 1, BIT(7), "pmic"), - PIN_GRP_GPIO("pmic0", 16, 1, BIT(8), "pmic"), + PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"), + PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"), PIN_GRP_GPIO("i2c2", 2, 2, BIT(9), "i2c"), PIN_GRP_GPIO("i2c1", 0, 2, BIT(10), "i2c"), PIN_GRP_GPIO("spi_cs1", 17, 1, BIT(12), "spi"), @@ -195,8 +195,10 @@ static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"), PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"), PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"), - PIN_GRP_GPIO("pcie1", 3, 2, BIT(4), "pcie"), - PIN_GRP_GPIO("ptp", 20, 3, BIT(5), "ptp"), + PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"), + PIN_GRP_GPIO("pcie1", 3, 1, BIT(5), "pcie"), + PIN_GRP_GPIO("pcie1_clkreq", 4, 1, BIT(9), "pcie"), + PIN_GRP_GPIO("ptp", 20, 3, BIT(11) | BIT(12) | BIT(13), "ptp"), PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"), PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"), PIN_GRP_GPIO_3("mii_col", 23, 1, BIT(8) | BIT(14), 0, BIT(8), BIT(14), From 4d98fbaacd79a82f408febb66a9c42fe42361b16 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 21 Dec 2018 18:32:58 +0100 Subject: [PATCH 0222/4277] pinctrl: armada-37xx: add missing pin: PCIe1 Wakeup Declare the PCIe1 Wakeup which was initially missing. Signed-off-by: Gregory CLEMENT Tested-by: Miquel Raynal Signed-off-by: Linus Walleij --- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 267dfc530985..4b9f2cf94431 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -198,6 +198,7 @@ static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"), PIN_GRP_GPIO("pcie1", 3, 1, BIT(5), "pcie"), PIN_GRP_GPIO("pcie1_clkreq", 4, 1, BIT(9), "pcie"), + PIN_GRP_GPIO("pcie1_wakeup", 5, 1, BIT(10), "pcie"), PIN_GRP_GPIO("ptp", 20, 3, BIT(11) | BIT(12) | BIT(13), "ptp"), PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"), PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"), From 3fbb9a8d79a84bf549979cad5511e202098d0aaa Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 21 Dec 2018 18:32:59 +0100 Subject: [PATCH 0223/4277] pinctrl: dt-bindings: Fix the armada-37xx documentation While it was possible to configure the PCIe1 Wakeup pin, it was missing in the bidding, let's document it. Signed-off-by: Gregory CLEMENT Tested-by: Miquel Raynal Signed-off-by: Linus Walleij --- .../bindings/pinctrl/marvell,armada-37xx-pinctrl.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt index f69f82741cae..38dc56a57760 100644 --- a/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada-37xx-pinctrl.txt @@ -127,12 +127,16 @@ group pcie1_clkreq - pins 40 - functions pcie, gpio +group pcie1_wakeup + - pins 41 + - functions pcie, gpio + group smi - pins 54-55 - functions smi, gpio group ptp - - pins 56-58 + - pins 56 - functions ptp, gpio group ptp_clk From e4889362e0389e95970d28707599aa8e08c35b51 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 31 Dec 2018 17:52:56 +0800 Subject: [PATCH 0224/4277] gpio: sama5d2-piobu: Set proper output level for direction_output Set proper output level base on the argument of direction_output. Also remove sama5d2_piobu_set_direction() as there is only one caller now. Signed-off-by: Axel Lin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-sama5d2-piobu.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 03a000659fa1..7d718557092e 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -108,16 +108,6 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, return val & mask; } -/** - * sama5d2_piobu_set_direction() - mark pin as input or output - */ -static int sama5d2_piobu_set_direction(struct gpio_chip *chip, - unsigned int direction, - unsigned int pin) -{ - return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction); -} - /** * sama5d2_piobu_get_direction() - gpiochip get_direction */ @@ -138,7 +128,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, static int sama5d2_piobu_direction_input(struct gpio_chip *chip, unsigned int pin) { - return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin); + return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); } /** @@ -147,7 +137,13 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, static int sama5d2_piobu_direction_output(struct gpio_chip *chip, unsigned int pin, int value) { - return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin); + unsigned int val = PIOBU_OUT; + + if (value) + val |= PIOBU_HIGH; + + return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD, + val); } /** From ba93c357229fd4666fe60f8d7ec97659882727c0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 30 Dec 2018 16:53:15 +0100 Subject: [PATCH 0225/4277] iommu/msm: Reduce indentation Delete tab aligning a statement with the right hand side of a preceding assignment rather than the left hand side. Found with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Joerg Roedel --- drivers/iommu/msm_iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index fc4270733f11..6dde3a9db92e 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -461,10 +461,10 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) master->num = msm_iommu_alloc_ctx(iommu->context_map, 0, iommu->ncb); - if (IS_ERR_VALUE(master->num)) { - ret = -ENODEV; - goto fail; - } + if (IS_ERR_VALUE(master->num)) { + ret = -ENODEV; + goto fail; + } config_mids(iommu, master); __program_context(iommu->base, master->num, priv); From bd3c2e66e4cb91ed9294b5da920deeb45db0c13a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 Jan 2019 23:16:57 +0200 Subject: [PATCH 0226/4277] iommu/iova: Allow compiling the library without IOMMU support Drivers such as the Intel IPU3 ImgU driver use the IOVA library to manage the device's own virtual address space while not implementing the IOMMU API. Currently the IOVA library is only compiled if the IOMMU support is enabled, resulting into a failure during linking due to missing symbols. Fix this by defining IOVA library Kconfig bits independently of IOMMU support configuration, and descending to the iommu directory unconditionally during the build. Signed-off-by: Sakari Ailus Signed-off-by: Joerg Roedel --- drivers/Makefile | 2 +- drivers/iommu/Kconfig | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index e1ce029d28fd..04da7876032c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -56,7 +56,7 @@ obj-y += tty/ obj-y += char/ # iommu/ comes before gpu as gpu are using iommu controllers -obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ +obj-y += iommu/ # gpu/ comes after char for AGP vs DRM startup and after iommu obj-y += gpu/ diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index d9a25715650e..d2c83e62873d 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -1,3 +1,7 @@ +# The IOVA library may also be used by non-IOMMU_API users +config IOMMU_IOVA + tristate + # IOMMU_API always gets selected by whoever wants it. config IOMMU_API bool @@ -81,9 +85,6 @@ config IOMMU_DEFAULT_PASSTHROUGH If unsure, say N here. -config IOMMU_IOVA - tristate - config OF_IOMMU def_bool y depends on OF && IOMMU_API From 5b438f4ba315db4f8c1489d175656798d58c014f Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Fri, 11 Jan 2019 13:04:57 +0800 Subject: [PATCH 0227/4277] iommu/vt-d: Support page request in scalable mode VT-d Rev3.0 has made a few changes to the page request interface, 1. widened PRQ descriptor from 128 bits to 256 bits; 2. removed streaming response type; 3. introduced private data that requires page response even the request is not last request in group (LPIG). This is a supplement to commit 1c4f88b7f1f92 ("iommu/vt-d: Shared virtual address in scalable mode") and makes the svm code compliant with VT-d Rev3.0. Cc: Ashok Raj Cc: Liu Yi L Cc: Kevin Tian Signed-off-by: Jacob Pan Fixes: 1c4f88b7f1f92 ("iommu/vt-d: Shared virtual address in scalable mode") Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-svm.c | 77 ++++++++++++++++++++++--------------- include/linux/intel-iommu.h | 21 +++++----- include/linux/intel-svm.h | 2 +- 3 files changed, 55 insertions(+), 45 deletions(-) diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index a2a2aa4439aa..79add5716552 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -470,20 +470,31 @@ EXPORT_SYMBOL_GPL(intel_svm_is_pasid_valid); /* Page request queue descriptor */ struct page_req_dsc { - u64 srr:1; - u64 bof:1; - u64 pasid_present:1; - u64 lpig:1; - u64 pasid:20; - u64 bus:8; - u64 private:23; - u64 prg_index:9; - u64 rd_req:1; - u64 wr_req:1; - u64 exe_req:1; - u64 priv_req:1; - u64 devfn:8; - u64 addr:52; + union { + struct { + u64 type:8; + u64 pasid_present:1; + u64 priv_data_present:1; + u64 rsvd:6; + u64 rid:16; + u64 pasid:20; + u64 exe_req:1; + u64 pm_req:1; + u64 rsvd2:10; + }; + u64 qw_0; + }; + union { + struct { + u64 rd_req:1; + u64 wr_req:1; + u64 lpig:1; + u64 prg_index:9; + u64 addr:52; + }; + u64 qw_1; + }; + u64 priv_data[2]; }; #define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10) @@ -596,7 +607,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) /* Accounting for major/minor faults? */ rcu_read_lock(); list_for_each_entry_rcu(sdev, &svm->devs, list) { - if (sdev->sid == PCI_DEVID(req->bus, req->devfn)) + if (sdev->sid == req->rid) break; } /* Other devices can go away, but the drivers are not permitted @@ -609,33 +620,35 @@ static irqreturn_t prq_event_thread(int irq, void *d) if (sdev && sdev->ops && sdev->ops->fault_cb) { int rwxp = (req->rd_req << 3) | (req->wr_req << 2) | - (req->exe_req << 1) | (req->priv_req); - sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, req->private, rwxp, result); + (req->exe_req << 1) | (req->pm_req); + sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, + req->priv_data, rwxp, result); } /* We get here in the error case where the PASID lookup failed, and these can be NULL. Do not use them below this point! */ sdev = NULL; svm = NULL; no_pasid: - if (req->lpig) { - /* Page Group Response */ + if (req->lpig || req->priv_data_present) { + /* + * Per VT-d spec. v3.0 ch7.7, system software must + * respond with page group response if private data + * is present (PDP) or last page in group (LPIG) bit + * is set. This is an additional VT-d feature beyond + * PCI ATS spec. + */ resp.qw0 = QI_PGRP_PASID(req->pasid) | - QI_PGRP_DID((req->bus << 8) | req->devfn) | + QI_PGRP_DID(req->rid) | QI_PGRP_PASID_P(req->pasid_present) | + QI_PGRP_PDP(req->pasid_present) | + QI_PGRP_RESP_CODE(result) | QI_PGRP_RESP_TYPE; resp.qw1 = QI_PGRP_IDX(req->prg_index) | - QI_PGRP_PRIV(req->private) | - QI_PGRP_RESP_CODE(result); - } else if (req->srr) { - /* Page Stream Response */ - resp.qw0 = QI_PSTRM_IDX(req->prg_index) | - QI_PSTRM_PRIV(req->private) | - QI_PSTRM_BUS(req->bus) | - QI_PSTRM_PASID(req->pasid) | - QI_PSTRM_RESP_TYPE; - resp.qw1 = QI_PSTRM_ADDR(address) | - QI_PSTRM_DEVFN(req->devfn) | - QI_PSTRM_RESP_CODE(result); + QI_PGRP_LPIG(req->lpig); + + if (req->priv_data_present) + memcpy(&resp.qw2, req->priv_data, + sizeof(req->priv_data)); } resp.qw2 = 0; resp.qw3 = 0; diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 0605f3bf6e79..fa364de9db18 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -374,20 +374,17 @@ enum { #define QI_DEV_EIOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | ((u64)(pfsid & 0xfff) << 52)) #define QI_DEV_EIOTLB_MAX_INVS 32 -#define QI_PGRP_IDX(idx) (((u64)(idx)) << 55) -#define QI_PGRP_PRIV(priv) (((u64)(priv)) << 32) -#define QI_PGRP_RESP_CODE(res) ((u64)(res)) -#define QI_PGRP_PASID(pasid) (((u64)(pasid)) << 32) -#define QI_PGRP_DID(did) (((u64)(did)) << 16) +/* Page group response descriptor QW0 */ #define QI_PGRP_PASID_P(p) (((u64)(p)) << 4) +#define QI_PGRP_PDP(p) (((u64)(p)) << 5) +#define QI_PGRP_RESP_CODE(res) (((u64)(res)) << 12) +#define QI_PGRP_DID(rid) (((u64)(rid)) << 16) +#define QI_PGRP_PASID(pasid) (((u64)(pasid)) << 32) + +/* Page group response descriptor QW1 */ +#define QI_PGRP_LPIG(x) (((u64)(x)) << 2) +#define QI_PGRP_IDX(idx) (((u64)(idx)) << 3) -#define QI_PSTRM_ADDR(addr) (((u64)(addr)) & VTD_PAGE_MASK) -#define QI_PSTRM_DEVFN(devfn) (((u64)(devfn)) << 4) -#define QI_PSTRM_RESP_CODE(res) ((u64)(res)) -#define QI_PSTRM_IDX(idx) (((u64)(idx)) << 55) -#define QI_PSTRM_PRIV(priv) (((u64)(priv)) << 32) -#define QI_PSTRM_BUS(bus) (((u64)(bus)) << 24) -#define QI_PSTRM_PASID(pasid) (((u64)(pasid)) << 4) #define QI_RESP_SUCCESS 0x0 #define QI_RESP_INVALID 0x1 diff --git a/include/linux/intel-svm.h b/include/linux/intel-svm.h index 99bc5b3ae26e..e3f76315ca4d 100644 --- a/include/linux/intel-svm.h +++ b/include/linux/intel-svm.h @@ -20,7 +20,7 @@ struct device; struct svm_dev_ops { void (*fault_cb)(struct device *dev, int pasid, u64 address, - u32 private, int rwxp, int response); + void *private, int rwxp, int response); }; /* Values for rxwp in fault_cb callback */ From 6e737a4e921ea99125955d0d8821d8da129b03bd Mon Sep 17 00:00:00 2001 From: "chuanjia.liu" Date: Thu, 3 Jan 2019 11:37:15 +0800 Subject: [PATCH 0228/4277] pinctrl: mediatek: add EINT support to virtual GPIOs Virtual gpio only used inside SOC and not being exported to outside SOC. Some modules use virtual gpio as eint and doesn't need SMT. So this patch add EINT support to virtual GPIOs. Signed-off-by: Chuanjia Liu Acked-by: Sean Wang Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c index 4a9e0d4c2bbc..b1c368455d30 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c @@ -290,7 +290,13 @@ static int mtk_xt_set_gpio_as_eint(void *data, unsigned long eint_n) return err; err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_SMT, MTK_ENABLE); - if (err) + /* SMT is supposed to be supported by every real GPIO and doesn't + * support virtual GPIOs, so the extra condition err != -ENOTSUPP + * is just for adding EINT support to these virtual GPIOs. It should + * add an extra flag in the pin descriptor when more pins with + * distinctive characteristic come out. + */ + if (err && err != -ENOTSUPP) return err; return 0; From 16f4372fd7a5a560abe0edb4c68be1200599dc8a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 11:37:33 -0600 Subject: [PATCH 0229/4277] pinctrl: mcp23s08: use struct_size() in devm_kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = devm_kzalloc(dev, sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = devm_kzalloc(dev, struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-mcp23s08.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index b03481ef99a1..6b4def13df8f 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -1139,8 +1139,7 @@ static int mcp23s08_probe(struct spi_device *spi) return -ENODEV; data = devm_kzalloc(&spi->dev, - sizeof(*data) + chips * sizeof(struct mcp23s08), - GFP_KERNEL); + struct_size(data, chip, chips), GFP_KERNEL); if (!data) return -ENOMEM; From 1e11820f72b71d276ae8fdc64fee7cdb49c05bc6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 4 Jan 2019 15:35:08 -0600 Subject: [PATCH 0230/4277] pinctrl: sirf: atlas7: use struct_size() in devm_kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = devm_kzalloc(dev, sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = devm_kzalloc(dev, struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Linus Walleij --- drivers/pinctrl/sirf/pinctrl-atlas7.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c index 4ba171827428..8a0eee044264 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas7.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c @@ -6007,8 +6007,8 @@ static int atlas7_gpio_probe(struct platform_device *pdev) } /* retrieve gpio descriptor data */ - a7gc = devm_kzalloc(&pdev->dev, sizeof(*a7gc) + - sizeof(struct atlas7_gpio_bank) * nbank, GFP_KERNEL); + a7gc = devm_kzalloc(&pdev->dev, struct_size(a7gc, banks, nbank), + GFP_KERNEL); if (!a7gc) return -ENOMEM; From 48c67f1fc663d86bbd486a2c664ba130e7c70790 Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Fri, 4 Jan 2019 22:49:12 +0100 Subject: [PATCH 0231/4277] pinctrl/amd: fix typo Fix spelling mistake: "lenght" -> "length" Signed-off-by: Matteo Croce Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 2a7d638978d8..6689995fa3aa 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -489,7 +489,7 @@ static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type) /* * If WAKE_INT_MASTER_REG.MaskStsEn is set, a software write to the * debounce registers of any GPIO will block wake/interrupt status - * generation for *all* GPIOs for a lenght of time that depends on + * generation for *all* GPIOs for a length of time that depends on * WAKE_INT_MASTER_REG.MaskStsLength[11:0]. During this period the * INTERRUPT_ENABLE bit will read as 0. * From fc9ac87eabb23b4c354cd2f3b89d94a0db9f9fdc Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:33:43 -0600 Subject: [PATCH 0232/4277] scsi: qla4xxx: ql4_os: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that, in this particular case, I replaced "allow fall-through" with a "fall through" annotation, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Acked-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla4xxx/ql4_os.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 949e186cc5d7..0a1940f947dc 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -2875,7 +2875,7 @@ static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess, chap_tbl.secret_len); } } - /* allow fall-through */ + /* fall through */ default: return iscsi_session_get_param(cls_sess, param, buf); } From 211d62a8ce689bf8e7fd0ede67b3a6b1d8de970b Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:33:54 -0600 Subject: [PATCH 0233/4277] scsi: st: mark expected switch fall-throughs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Addresses-Coverity-ID: 114994 ("Missing break in switch") Addresses-Coverity-ID: 114995 ("Missing break in switch") Signed-off-by: Gustavo A. R. Silva Acked-by: Kai Mäkisara Signed-off-by: Martin K. Petersen --- drivers/scsi/st.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 7ff22d3f03e3..7c7a742a5ef7 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -337,12 +337,14 @@ static void st_analyze_sense(struct st_request *SRpnt, struct st_cmdstatus *s) switch (sense[0] & 0x7f) { case 0x71: s->deferred = 1; + /* fall through */ case 0x70: s->fixed_format = 1; s->flags = sense[2] & 0xe0; break; case 0x73: s->deferred = 1; + /* fall through */ case 0x72: s->fixed_format = 0; ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4); @@ -2721,6 +2723,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon switch (cmd_in) { case MTFSFM: chg_eof = 0; /* Changed from the FSF after this */ + /* fall through */ case MTFSF: cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ @@ -2735,6 +2738,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon break; case MTBSFM: chg_eof = 0; /* Changed from the FSF after this */ + /* fall through */ case MTBSF: cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ From b9f57f5805cf0e72db20a435e72e8b557a8d28b1 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:28:57 -0600 Subject: [PATCH 0234/4277] scsi: esas2r: esas2r_init: mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Signed-off-by: Gustavo A. R. Silva Acked-by: Bradley Grove Signed-off-by: Martin K. Petersen --- drivers/scsi/esas2r/esas2r_init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c index 46b2c83ba21f..950cd92df2ff 100644 --- a/drivers/scsi/esas2r/esas2r_init.c +++ b/drivers/scsi/esas2r/esas2r_init.c @@ -1241,6 +1241,7 @@ static bool esas2r_format_init_msg(struct esas2r_adapter *a, a->init_msg = ESAS2R_INIT_MSG_GET_INIT; break; } + /* fall through */ case ESAS2R_INIT_MSG_GET_INIT: if (msg == ESAS2R_INIT_MSG_GET_INIT) { @@ -1254,7 +1255,7 @@ static bool esas2r_format_init_msg(struct esas2r_adapter *a, esas2r_hdebug("FAILED"); } } - /* fall through */ + /* fall through */ default: rq->req_stat = RS_SUCCESS; From 8425811b8d95d441499d963b7d09b7910c630f71 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:27:15 -0600 Subject: [PATCH 0235/4277] scsi: bfa: bfa_fcs_lport: Mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that, in this particular case, I replaced "!!! fall through !!!" with a "fall through" annotation, which is what GCC is expecting to find. Signed-off-by: Gustavo A. R. Silva Acked-by: Sudarsana Kalluru Signed-off-by: Martin K. Petersen --- drivers/scsi/bfa/bfa_fcs_lport.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index b4f2c1d8742e..646f09f66443 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -6430,9 +6430,7 @@ bfa_fcs_vport_sm_logo_for_stop(struct bfa_fcs_vport_s *vport, switch (event) { case BFA_FCS_VPORT_SM_OFFLINE: bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE); - /* - * !!! fall through !!! - */ + /* fall through */ case BFA_FCS_VPORT_SM_RSP_OK: case BFA_FCS_VPORT_SM_RSP_ERROR: @@ -6458,9 +6456,7 @@ bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport, switch (event) { case BFA_FCS_VPORT_SM_OFFLINE: bfa_sm_send_event(vport->lps, BFA_LPS_SM_OFFLINE); - /* - * !!! fall through !!! - */ + /* fall through */ case BFA_FCS_VPORT_SM_RSP_OK: case BFA_FCS_VPORT_SM_RSP_ERROR: From d14e4cd45ac83edf96b249a222b02344889fb6f9 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:27:23 -0600 Subject: [PATCH 0236/4277] scsi: bfa: bfa_fcs_rport: Mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that I replaced "!! fall through !!" and "!!! fall through !!!" comments with "fall through" annotations, which is what GCC is expecting to find. Addresses-Coverity-ID: 744899 ("Missing break in switch") Addresses-Coverity-ID: 744900 ("Missing break in switch") Addresses-Coverity-ID: 744901 ("Missing break in switch") Signed-off-by: Gustavo A. R. Silva Acked-by: Sudarsana Kalluru Signed-off-by: Martin K. Petersen --- drivers/scsi/bfa/bfa_fcs_rport.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c index de50349a39ce..1e400f2aaece 100644 --- a/drivers/scsi/bfa/bfa_fcs_rport.c +++ b/drivers/scsi/bfa/bfa_fcs_rport.c @@ -427,17 +427,13 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event) case RPSM_EVENT_LOGO_RCVD: bfa_fcs_rport_send_logo_acc(rport); - /* - * !! fall through !! - */ + /* fall through */ case RPSM_EVENT_PRLO_RCVD: if (rport->prlo == BFA_TRUE) bfa_fcs_rport_send_prlo_acc(rport); bfa_fcxp_discard(rport->fcxp); - /* - * !! fall through !! - */ + /* fall through */ case RPSM_EVENT_FAILED: if (rport->plogi_retries < BFA_FCS_RPORT_MAX_RETRIES) { rport->plogi_retries++; @@ -868,9 +864,7 @@ bfa_fcs_rport_sm_adisc_online(struct bfa_fcs_rport_s *rport, * At least go offline when a PLOGI is received. */ bfa_fcxp_discard(rport->fcxp); - /* - * !!! fall through !!! - */ + /* fall through */ case RPSM_EVENT_FAILED: case RPSM_EVENT_ADDRESS_CHANGE: @@ -1056,6 +1050,7 @@ bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport, case RPSM_EVENT_LOGO_RCVD: bfa_fcs_rport_send_logo_acc(rport); + /* fall through */ case RPSM_EVENT_PRLO_RCVD: if (rport->prlo == BFA_TRUE) bfa_fcs_rport_send_prlo_acc(rport); @@ -1144,9 +1139,7 @@ bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_send_plogiacc(rport, NULL); break; } - /* - * !! fall through !! - */ + /* fall through */ case RPSM_EVENT_ADDRESS_CHANGE: if (!bfa_fcs_lport_is_online(rport->port)) { @@ -1303,6 +1296,7 @@ bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport, case RPSM_EVENT_LOGO_RCVD: bfa_fcs_rport_send_logo_acc(rport); + /* fall through */ case RPSM_EVENT_PRLO_RCVD: if (rport->prlo == BFA_TRUE) bfa_fcs_rport_send_prlo_acc(rport); @@ -1346,6 +1340,7 @@ bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport, case RPSM_EVENT_LOGO_RCVD: bfa_fcs_rport_send_logo_acc(rport); + /* fall through */ case RPSM_EVENT_PRLO_RCVD: if (rport->prlo == BFA_TRUE) bfa_fcs_rport_send_prlo_acc(rport); From f1b1dceedd7b046d04031a187aa1d51a62d8847c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:27:32 -0600 Subject: [PATCH 0237/4277] scsi: bfa: bfa_ioc: Mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that, in this particular case, I replaced "!!! fall through !!!" comment with "fall through" annotations, which is what GCC is expecting to find. Addresses-Coverity-ID: 146155 ("Missing break in switch") Signed-off-by: Gustavo A. R. Silva Acked-by: Sudarsana Kalluru Signed-off-by: Martin K. Petersen --- drivers/scsi/bfa/bfa_ioc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index 9631877aba4f..79a55c3615be 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -978,9 +978,7 @@ bfa_iocpf_sm_enabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event) case IOCPF_E_INITFAIL: bfa_iocpf_timer_stop(ioc); - /* - * !!! fall through !!! - */ + /* fall through */ case IOCPF_E_TIMEOUT: writel(1, ioc->ioc_regs.ioc_sem_reg); @@ -1056,9 +1054,7 @@ bfa_iocpf_sm_disabling(struct bfa_iocpf_s *iocpf, enum iocpf_event event) case IOCPF_E_FAIL: bfa_iocpf_timer_stop(ioc); - /* - * !!! fall through !!! - */ + /* fall through */ case IOCPF_E_TIMEOUT: bfa_ioc_set_cur_ioc_fwstate(ioc, BFI_IOC_FAIL); @@ -6007,6 +6003,7 @@ bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf, case BFA_DCONF_SM_IOCDISABLE: case BFA_DCONF_SM_FLASH_COMP: bfa_timer_stop(&dconf->timer); + /* fall through */ case BFA_DCONF_SM_TIMEOUT: bfa_sm_set_state(dconf, bfa_dconf_sm_uninit); bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE); From 4edff70494e4c7c4371270a7b0b56b2776adc0ee Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Nov 2018 22:26:19 -0600 Subject: [PATCH 0238/4277] scsi: aic7xxx: aic79xx: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that, in some cases, I replaced "FALLTHROUGH" with a "fall through" annotation and then placed it at the bottom of the corresponding switch case, which is what GCC is expecting to find. Addresses-Coverity-ID: 114961 ("Missing break in switch") Addresses-Coverity-ID: 114962 ("Missing break in switch") Addresses-Coverity-ID: 114963 ("Missing break in switch") Addresses-Coverity-ID: 114964 ("Missing break in switch") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/aic7xxx/aic79xx_core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index 9ee75c9a9aa1..7e5044bf05c0 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -2285,6 +2285,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat) switch (scb->hscb->task_management) { case SIU_TASKMGMT_ABORT_TASK: tag = SCB_GET_TAG(scb); + /* fall through */ case SIU_TASKMGMT_ABORT_TASK_SET: case SIU_TASKMGMT_CLEAR_TASK_SET: lun = scb->hscb->lun; @@ -2295,6 +2296,7 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat) break; case SIU_TASKMGMT_LUN_RESET: lun = scb->hscb->lun; + /* fall through */ case SIU_TASKMGMT_TARGET_RESET: { struct ahd_devinfo devinfo; @@ -6550,8 +6552,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd) kfree(sns_map); } ahd_dma_tag_destroy(ahd, scb_data->sense_dmat); - /* FALLTHROUGH */ } + /* fall through */ case 6: { struct map_node *sg_map; @@ -6565,8 +6567,8 @@ ahd_fini_scbdata(struct ahd_softc *ahd) kfree(sg_map); } ahd_dma_tag_destroy(ahd, scb_data->sg_dmat); - /* FALLTHROUGH */ } + /* fall through */ case 5: { struct map_node *hscb_map; @@ -7209,6 +7211,7 @@ ahd_init(struct ahd_softc *ahd) case FLX_CSTAT_OVER: case FLX_CSTAT_UNDER: warn_user++; + /* fall through */ case FLX_CSTAT_INVALID: case FLX_CSTAT_OKAY: if (warn_user == 0 && bootverbose == 0) @@ -8413,7 +8416,7 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel, if ((scb->flags & SCB_ACTIVE) == 0) printk("Inactive SCB in Waiting List\n"); ahd_done_with_status(ahd, scb, status); - /* FALLTHROUGH */ + /* fall through */ case SEARCH_REMOVE: ahd_rem_wscb(ahd, scbid, prev, next, tid); *list_tail = prev; @@ -8422,6 +8425,7 @@ ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel, break; case SEARCH_PRINT: printk("0x%x ", scbid); + /* fall through */ case SEARCH_COUNT: prev = scbid; break; @@ -9547,8 +9551,8 @@ ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts) { fmt3_ins = &instr.format3; fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address); - /* FALLTHROUGH */ } + /* fall through */ case AIC_OP_OR: case AIC_OP_AND: case AIC_OP_XOR: @@ -9559,7 +9563,7 @@ ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts) fmt1_ins->immediate = dconsts[fmt1_ins->immediate]; } fmt1_ins->parity = 0; - /* FALLTHROUGH */ + /* fall through */ case AIC_OP_ROL: { int i, count; From 7855d2ba1172d716d96a628af7c5bafa5725ac57 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 21 Dec 2018 09:33:44 -0800 Subject: [PATCH 0239/4277] scsi: qla2xxx: Add protection mask module parameters Allow user to selectively enable/disable DIF/DIX protection capabilities mask. Signed-off-by: Martin K. Petersen Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_os.c | 36 +++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index ea69dafc9774..3a4f68f40057 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -285,6 +285,20 @@ MODULE_PARM_DESC(qla2xuseresexchforels, "Reserve 1/2 of emergency exchanges for ELS.\n" " 0 (default): disabled"); +int ql2xprotmask; +module_param(ql2xprotmask, int, 0644); +MODULE_PARM_DESC(ql2xprotmask, + "Override DIF/DIX protection capabilities mask\n" + "Default is 0 which sets protection mask based on " + "capabilities reported by HBA firmware.\n"); + +int ql2xprotguard; +module_param(ql2xprotguard, int, 0644); +MODULE_PARM_DESC(ql2xprotguard, "Override choice of DIX checksum\n" + " 0 -- Let HBA firmware decide\n" + " 1 -- Force T10 CRC\n" + " 2 -- Force IP checksum\n"); + /* * SCSI host template entry points */ @@ -3342,13 +3356,16 @@ skip_dpc: "Registering for DIF/DIX type 1 and 3 protection.\n"); if (ql2xenabledif == 1) prot = SHOST_DIX_TYPE0_PROTECTION; - scsi_host_set_prot(host, - prot | SHOST_DIF_TYPE1_PROTECTION - | SHOST_DIF_TYPE2_PROTECTION - | SHOST_DIF_TYPE3_PROTECTION - | SHOST_DIX_TYPE1_PROTECTION - | SHOST_DIX_TYPE2_PROTECTION - | SHOST_DIX_TYPE3_PROTECTION); + if (ql2xprotmask) + scsi_host_set_prot(host, ql2xprotmask); + else + scsi_host_set_prot(host, + prot | SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE2_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE2_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION); guard = SHOST_DIX_GUARD_CRC; @@ -3356,7 +3373,10 @@ skip_dpc: (ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha))) guard |= SHOST_DIX_GUARD_IP; - scsi_host_set_guard(host, guard); + if (ql2xprotguard) + scsi_host_set_guard(host, ql2xprotguard); + else + scsi_host_set_guard(host, guard); } else base_vha->flags.difdix_supported = 0; } From 50b812755e9766fa0a1a28533f4d11a34a5b813e Mon Sep 17 00:00:00 2001 From: Giridhar Malavali Date: Fri, 21 Dec 2018 09:33:45 -0800 Subject: [PATCH 0240/4277] scsi: qla2xxx: Fix DMA error when the DIF sg buffer crosses 4GB boundary When SGE buffer containing DIF information crosses 4G boundary, it results in DMA error. This patch fixes this issue by calculating SGE buffer size and if it crosses 4G boundary, driver will split it into multiple SGE buffers to avoid DMA error. Signed-off-by: Giridhar Malavali Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_attr.c | 21 +- drivers/scsi/qla2xxx/qla_def.h | 28 +++ drivers/scsi/qla2xxx/qla_gbl.h | 3 +- drivers/scsi/qla2xxx/qla_iocb.c | 333 ++++++++++++++++++++++++------ drivers/scsi/qla2xxx/qla_isr.c | 11 + drivers/scsi/qla2xxx/qla_os.c | 169 ++++++++++++++- drivers/scsi/qla2xxx/qla_target.c | 2 +- drivers/scsi/qla2xxx/qla_target.h | 2 + 8 files changed, 502 insertions(+), 67 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 00444dc79756..8b4dd72011bf 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1002,7 +1002,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha, bool stop_beacon) /* Scsi_Host attributes. */ static ssize_t -qla2x00_drvr_version_show(struct device *dev, +qla2x00_driver_version_show(struct device *dev, struct device_attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str); @@ -2059,7 +2059,21 @@ ql2xiniexchg_store(struct device *dev, struct device_attribute *attr, return strlen(buf); } -static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); +static ssize_t +qla2x00_dif_bundle_statistics_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + return scnprintf(buf, PAGE_SIZE, + "cross=%llu read=%llu write=%llu kalloc=%llu dma_alloc=%llu unusable=%u\n", + ha->dif_bundle_crossed_pages, ha->dif_bundle_reads, + ha->dif_bundle_writes, ha->dif_bundle_kallocs, + ha->dif_bundle_dma_allocs, ha->pool.unusable.count); +} + +static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_driver_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); static DEVICE_ATTR(isp_name, S_IRUGO, qla2x00_isp_name_show, NULL); @@ -2112,6 +2126,8 @@ static DEVICE_ATTR(zio_threshold, 0644, static DEVICE_ATTR_RW(qlini_mode); static DEVICE_ATTR_RW(ql2xexchoffld); static DEVICE_ATTR_RW(ql2xiniexchg); +static DEVICE_ATTR(dif_bundle_statistics, 0444, + qla2x00_dif_bundle_statistics_show, NULL); struct device_attribute *qla2x00_host_attrs[] = { @@ -2150,6 +2166,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_min_link_speed, &dev_attr_max_speed_sup, &dev_attr_zio_threshold, + &dev_attr_dif_bundle_statistics, NULL, /* reserve for qlini_mode */ NULL, /* reserve for ql2xiniexchg */ NULL, /* reserve for ql2xexchoffld */ diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 26b93c563f92..97f66b142ff2 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -314,6 +314,7 @@ struct srb_cmd { #define SRB_CRC_PROT_DMA_VALID BIT_4 /* DIF: prot DMA valid */ #define SRB_CRC_CTX_DSD_VALID BIT_5 /* DIF: dsd_list valid */ #define SRB_WAKEUP_ON_COMP BIT_6 +#define SRB_DIF_BUNDL_DMA_VALID BIT_7 /* DIF: DMA list valid */ /* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */ #define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID) @@ -1892,6 +1893,13 @@ struct crc_context { /* List of DMA context transfers */ struct list_head dsd_list; + /* List of DIF Bundling context DMA address */ + struct list_head ldif_dsd_list; + u8 no_ldif_dsd; + + struct list_head ldif_dma_hndl_list; + u32 dif_bundl_len; + u8 no_dif_bundl; /* This structure should not exceed 512 bytes */ }; @@ -4184,6 +4192,26 @@ struct qla_hw_data { uint16_t min_link_speed; uint16_t max_speed_sup; + /* DMA pool for the DIF bundling buffers */ + struct dma_pool *dif_bundl_pool; + #define DIF_BUNDLING_DMA_POOL_SIZE 1024 + struct { + struct { + struct list_head head; + uint count; + } good; + struct { + struct list_head head; + uint count; + } unusable; + } pool; + + unsigned long long dif_bundle_crossed_pages; + unsigned long long dif_bundle_reads; + unsigned long long dif_bundle_writes; + unsigned long long dif_bundle_kallocs; + unsigned long long dif_bundle_dma_allocs; + atomic_t nvme_active_aen_cnt; uint16_t nvme_last_rptd_aen; /* Last recorded aen count */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 3673fcdb033a..bcc17a7261e7 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -160,6 +160,7 @@ extern int ql2xautodetectsfp; extern int ql2xenablemsix; extern int qla2xuseresexchforels; extern int ql2xexlogins; +extern int ql2xdifbundlinginternalbuffers; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -285,7 +286,7 @@ extern int qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *, srb_t *, extern int qla24xx_walk_and_build_sglist(struct qla_hw_data *, srb_t *, uint32_t *, uint16_t, struct qla_tc_param *); extern int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *, srb_t *, - uint32_t *, uint16_t, struct qla_tc_param *); + uint32_t *, uint16_t, struct qla_tgt_cmd *); extern int qla24xx_get_one_block_sg(uint32_t, struct qla2_sgx *, uint32_t *); extern int qla24xx_configure_prot_mode(srb_t *, uint16_t *); extern int qla24xx_build_scsi_crc_2_iocbs(srb_t *, diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 032635321ad6..65ba0e36ee60 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -1098,88 +1098,300 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, - uint32_t *dsd, uint16_t tot_dsds, struct qla_tc_param *tc) + uint32_t *cur_dsd, uint16_t tot_dsds, struct qla_tgt_cmd *tc) { - void *next_dsd; - uint8_t avail_dsds = 0; - uint32_t dsd_list_len; - struct dsd_dma *dsd_ptr; + struct dsd_dma *dsd_ptr = NULL, *dif_dsd, *nxt_dsd; struct scatterlist *sg, *sgl; - int i; - struct scsi_cmnd *cmd; - uint32_t *cur_dsd = dsd; - uint16_t used_dsds = tot_dsds; + struct crc_context *difctx = NULL; struct scsi_qla_host *vha; + uint dsd_list_len; + uint avail_dsds = 0; + uint used_dsds = tot_dsds; + bool dif_local_dma_alloc = false; + bool direction_to_device = false; + int i; if (sp) { - cmd = GET_CMD_SP(sp); + struct scsi_cmnd *cmd = GET_CMD_SP(sp); sgl = scsi_prot_sglist(cmd); vha = sp->vha; + difctx = sp->u.scmd.ctx; + direction_to_device = cmd->sc_data_direction == DMA_TO_DEVICE; + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe021, + "%s: scsi_cmnd: %p, crc_ctx: %p, sp: %p\n", + __func__, cmd, difctx, sp); } else if (tc) { vha = tc->vha; sgl = tc->prot_sg; + difctx = tc->ctx; + direction_to_device = tc->dma_data_direction == DMA_TO_DEVICE; } else { BUG(); return 1; } - ql_dbg(ql_dbg_tgt, vha, 0xe021, - "%s: enter\n", __func__); + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe021, + "%s: enter (write=%u)\n", __func__, direction_to_device); - for_each_sg(sgl, sg, tot_dsds, i) { - dma_addr_t sle_dma; + /* if initiator doing write or target doing read */ + if (direction_to_device) { + for_each_sg(sgl, sg, tot_dsds, i) { + dma_addr_t sle_phys = sg_phys(sg); - /* Allocate additional continuation packets? */ - if (avail_dsds == 0) { - avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? - QLA_DSDS_PER_IOCB : used_dsds; - dsd_list_len = (avail_dsds + 1) * 12; - used_dsds -= avail_dsds; + /* If SGE addr + len flips bits in upper 32-bits */ + if (MSD(sle_phys + sg->length) ^ MSD(sle_phys)) { + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe022, + "%s: page boundary crossing (phys=%llx len=%x)\n", + __func__, sle_phys, sg->length); - /* allocate tracking DS */ - dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); - if (!dsd_ptr) - return 1; - - /* allocate new list */ - dsd_ptr->dsd_addr = next_dsd = - dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, - &dsd_ptr->dsd_list_dma); - - if (!next_dsd) { - /* - * Need to cleanup only this dsd_ptr, rest - * will be done by sp_free_dma() - */ - kfree(dsd_ptr); - return 1; + if (difctx) { + ha->dif_bundle_crossed_pages++; + dif_local_dma_alloc = true; + } else { + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, + vha, 0xe022, + "%s: difctx pointer is NULL\n", + __func__); + } + break; } - - if (sp) { - list_add_tail(&dsd_ptr->list, - &((struct crc_context *) - sp->u.scmd.ctx)->dsd_list); - - sp->flags |= SRB_CRC_CTX_DSD_VALID; - } else { - list_add_tail(&dsd_ptr->list, - &(tc->ctx->dsd_list)); - *tc->ctx_dsd_alloced = 1; - } - - /* add new list to cmd iocb or last list */ - *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); - *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); - *cur_dsd++ = dsd_list_len; - cur_dsd = (uint32_t *)next_dsd; } - sle_dma = sg_dma_address(sg); + ha->dif_bundle_writes++; + } else { + ha->dif_bundle_reads++; + } - *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); - *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); - *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + if (ql2xdifbundlinginternalbuffers) + dif_local_dma_alloc = direction_to_device; - avail_dsds--; + if (dif_local_dma_alloc) { + u32 track_difbundl_buf = 0; + u32 ldma_sg_len = 0; + u8 ldma_needed = 1; + + difctx->no_dif_bundl = 0; + difctx->dif_bundl_len = 0; + + /* Track DSD buffers */ + INIT_LIST_HEAD(&difctx->ldif_dsd_list); + /* Track local DMA buffers */ + INIT_LIST_HEAD(&difctx->ldif_dma_hndl_list); + + for_each_sg(sgl, sg, tot_dsds, i) { + u32 sglen = sg_dma_len(sg); + + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe023, + "%s: sg[%x] (phys=%llx sglen=%x) ldma_sg_len: %x dif_bundl_len: %x ldma_needed: %x\n", + __func__, i, sg_phys(sg), sglen, ldma_sg_len, + difctx->dif_bundl_len, ldma_needed); + + while (sglen) { + u32 xfrlen = 0; + + if (ldma_needed) { + /* + * Allocate list item to store + * the DMA buffers + */ + dsd_ptr = kzalloc(sizeof(*dsd_ptr), + GFP_ATOMIC); + if (!dsd_ptr) { + ql_dbg(ql_dbg_tgt, vha, 0xe024, + "%s: failed alloc dsd_ptr\n", + __func__); + return 1; + } + ha->dif_bundle_kallocs++; + + /* allocate dma buffer */ + dsd_ptr->dsd_addr = dma_pool_alloc + (ha->dif_bundl_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + ql_dbg(ql_dbg_tgt, vha, 0xe024, + "%s: failed alloc ->dsd_ptr\n", + __func__); + /* + * need to cleanup only this + * dsd_ptr rest will be done + * by sp_free_dma() + */ + kfree(dsd_ptr); + ha->dif_bundle_kallocs--; + return 1; + } + ha->dif_bundle_dma_allocs++; + ldma_needed = 0; + difctx->no_dif_bundl++; + list_add_tail(&dsd_ptr->list, + &difctx->ldif_dma_hndl_list); + } + + /* xfrlen is min of dma pool size and sglen */ + xfrlen = (sglen > + (DIF_BUNDLING_DMA_POOL_SIZE - ldma_sg_len)) ? + DIF_BUNDLING_DMA_POOL_SIZE - ldma_sg_len : + sglen; + + /* replace with local allocated dma buffer */ + sg_pcopy_to_buffer(sgl, sg_nents(sgl), + dsd_ptr->dsd_addr + ldma_sg_len, xfrlen, + difctx->dif_bundl_len); + difctx->dif_bundl_len += xfrlen; + sglen -= xfrlen; + ldma_sg_len += xfrlen; + if (ldma_sg_len == DIF_BUNDLING_DMA_POOL_SIZE || + sg_is_last(sg)) { + ldma_needed = 1; + ldma_sg_len = 0; + } + } + } + + track_difbundl_buf = used_dsds = difctx->no_dif_bundl; + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe025, + "dif_bundl_len=%x, no_dif_bundl=%x track_difbundl_buf: %x\n", + difctx->dif_bundl_len, difctx->no_dif_bundl, + track_difbundl_buf); + + if (sp) + sp->flags |= SRB_DIF_BUNDL_DMA_VALID; + else + tc->prot_flags = DIF_BUNDL_DMA_VALID; + + list_for_each_entry_safe(dif_dsd, nxt_dsd, + &difctx->ldif_dma_hndl_list, list) { + u32 sglen = (difctx->dif_bundl_len > + DIF_BUNDLING_DMA_POOL_SIZE) ? + DIF_BUNDLING_DMA_POOL_SIZE : difctx->dif_bundl_len; + + BUG_ON(track_difbundl_buf == 0); + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, + 0xe024, + "%s: adding continuation iocb's\n", + __func__); + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC); + if (!dsd_ptr) { + ql_dbg(ql_dbg_tgt, vha, 0xe026, + "%s: failed alloc dsd_ptr\n", + __func__); + return 1; + } + ha->dif_bundle_kallocs++; + + difctx->no_ldif_dsd++; + /* allocate new list */ + dsd_ptr->dsd_addr = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + ql_dbg(ql_dbg_tgt, vha, 0xe026, + "%s: failed alloc ->dsd_addr\n", + __func__); + /* + * need to cleanup only this dsd_ptr + * rest will be done by sp_free_dma() + */ + kfree(dsd_ptr); + ha->dif_bundle_kallocs--; + return 1; + } + ha->dif_bundle_dma_allocs++; + + if (sp) { + list_add_tail(&dsd_ptr->list, + &difctx->ldif_dsd_list); + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &difctx->ldif_dsd_list); + tc->ctx_dsd_alloced = 1; + } + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = + cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = + cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = dsd_ptr->dsd_addr; + } + *cur_dsd++ = cpu_to_le32(LSD(dif_dsd->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dif_dsd->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(sglen); + avail_dsds--; + difctx->dif_bundl_len -= sglen; + track_difbundl_buf--; + } + + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe026, + "%s: no_ldif_dsd:%x, no_dif_bundl:%x\n", __func__, + difctx->no_ldif_dsd, difctx->no_dif_bundl); + } else { + for_each_sg(sgl, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC); + if (!dsd_ptr) { + ql_dbg(ql_dbg_tgt + ql_dbg_verbose, + vha, 0xe027, + "%s: failed alloc dsd_dma...\n", + __func__); + return 1; + } + + /* allocate new list */ + dsd_ptr->dsd_addr = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + /* need to cleanup only this dsd_ptr */ + /* rest will be done by sp_free_dma() */ + kfree(dsd_ptr); + return 1; + } + + if (sp) { + list_add_tail(&dsd_ptr->list, + &difctx->dsd_list); + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &difctx->dsd_list); + tc->ctx_dsd_alloced = 1; + } + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = + cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = + cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = dsd_ptr->dsd_addr; + } + sle_dma = sg_dma_address(sg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + avail_dsds--; + } } /* Null termination */ *cur_dsd++ = 0; @@ -1187,7 +1399,6 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, *cur_dsd++ = 0; return 0; } - /** * qla24xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command * Type 6 IOCB types. diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 30d3090842f8..5c1de6ed825b 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2725,6 +2725,17 @@ check_scsi_status: cp->device->vendor); break; + case CS_DMA: + ql_log(ql_log_info, fcport->vha, 0x3022, + "CS_DMA error: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu portid=%06x oxid=0x%x cdb=%10phN len=0x%x rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n", + comp_status, scsi_status, res, vha->host_no, + cp->device->id, cp->device->lun, fcport->d_id.b24, + ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len, + resid_len, fw_resid_len, sp, cp); + ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee, + pkt, sizeof(*sts24)); + res = DID_ERROR << 16; + break; default: res = DID_ERROR << 16; break; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3a4f68f40057..715b5526fb0a 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -299,6 +299,13 @@ MODULE_PARM_DESC(ql2xprotguard, "Override choice of DIX checksum\n" " 1 -- Force T10 CRC\n" " 2 -- Force IP checksum\n"); +int ql2xdifbundlinginternalbuffers; +module_param(ql2xdifbundlinginternalbuffers, int, 0644); +MODULE_PARM_DESC(ql2xdifbundlinginternalbuffers, + "Force using internal buffers for DIF information\n" + "0 (Default). Based on check.\n" + "1 Force using internal buffers\n"); + /* * SCSI host template entry points */ @@ -818,7 +825,44 @@ qla2xxx_qpair_sp_free_dma(void *ptr) ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt; ha->gbl_dsd_avail += ctx1->dsd_use_cnt; mempool_free(ctx1, ha->ctx_mempool); + sp->flags &= ~SRB_FCP_CMND_DMA_VALID; } + if (sp->flags & SRB_DIF_BUNDL_DMA_VALID) { + struct crc_context *difctx = sp->u.scmd.ctx; + struct dsd_dma *dif_dsd, *nxt_dsd; + + list_for_each_entry_safe(dif_dsd, nxt_dsd, + &difctx->ldif_dma_hndl_list, list) { + list_del(&dif_dsd->list); + dma_pool_free(ha->dif_bundl_pool, dif_dsd->dsd_addr, + dif_dsd->dsd_list_dma); + kfree(dif_dsd); + difctx->no_dif_bundl--; + } + + list_for_each_entry_safe(dif_dsd, nxt_dsd, + &difctx->ldif_dsd_list, list) { + list_del(&dif_dsd->list); + dma_pool_free(ha->dl_dma_pool, dif_dsd->dsd_addr, + dif_dsd->dsd_list_dma); + kfree(dif_dsd); + difctx->no_ldif_dsd--; + } + + if (difctx->no_ldif_dsd) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, sp->vha, 0xe022, + "%s: difctx->no_ldif_dsd=%x\n", + __func__, difctx->no_ldif_dsd); + } + + if (difctx->no_dif_bundl) { + ql_dbg(ql_dbg_tgt+ql_dbg_verbose, sp->vha, 0xe022, + "%s: difctx->no_dif_bundl=%x\n", + __func__, difctx->no_dif_bundl); + } + sp->flags &= ~SRB_DIF_BUNDL_DMA_VALID; + } + end: CMD_SP(cmd) = NULL; qla2xxx_rel_qpair_sp(sp->qpair, sp); @@ -4017,9 +4061,86 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, "Failed to allocate memory for fcp_cmnd_dma_pool.\n"); goto fail_dl_dma_pool; } + + if (ql2xenabledif) { + u64 bufsize = DIF_BUNDLING_DMA_POOL_SIZE; + struct dsd_dma *dsd, *nxt; + uint i; + /* Creata a DMA pool of buffers for DIF bundling */ + ha->dif_bundl_pool = dma_pool_create(name, + &ha->pdev->dev, DIF_BUNDLING_DMA_POOL_SIZE, 8, 0); + if (!ha->dif_bundl_pool) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0024, + "%s: failed create dif_bundl_pool\n", + __func__); + goto fail_dif_bundl_dma_pool; + } + + INIT_LIST_HEAD(&ha->pool.good.head); + INIT_LIST_HEAD(&ha->pool.unusable.head); + ha->pool.good.count = 0; + ha->pool.unusable.count = 0; + for (i = 0; i < 128; i++) { + dsd = kzalloc(sizeof(*dsd), GFP_ATOMIC); + if (!dsd) { + ql_dbg_pci(ql_dbg_init, ha->pdev, + 0xe0ee, "%s: failed alloc dsd\n", + __func__); + return 1; + } + ha->dif_bundle_kallocs++; + + dsd->dsd_addr = dma_pool_alloc( + ha->dif_bundl_pool, GFP_ATOMIC, + &dsd->dsd_list_dma); + if (!dsd->dsd_addr) { + ql_dbg_pci(ql_dbg_init, ha->pdev, + 0xe0ee, + "%s: failed alloc ->dsd_addr\n", + __func__); + kfree(dsd); + ha->dif_bundle_kallocs--; + continue; + } + ha->dif_bundle_dma_allocs++; + + /* + * if DMA buffer crosses 4G boundary, + * put it on bad list + */ + if (MSD(dsd->dsd_list_dma) ^ + MSD(dsd->dsd_list_dma + bufsize)) { + list_add_tail(&dsd->list, + &ha->pool.unusable.head); + ha->pool.unusable.count++; + } else { + list_add_tail(&dsd->list, + &ha->pool.good.head); + ha->pool.good.count++; + } + } + + /* return the good ones back to the pool */ + list_for_each_entry_safe(dsd, nxt, + &ha->pool.good.head, list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, + dsd->dsd_addr, dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + } + + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0024, + "%s: dif dma pool (good=%u unusable=%u)\n", + __func__, ha->pool.good.count, + ha->pool.unusable.count); + } + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025, - "dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n", - ha->dl_dma_pool, ha->fcp_cmnd_dma_pool); + "dl_dma_pool=%p fcp_cmnd_dma_pool=%p dif_bundl_pool=%p.\n", + ha->dl_dma_pool, ha->fcp_cmnd_dma_pool, + ha->dif_bundl_pool); } /* Allocate memory for SNS commands */ @@ -4184,6 +4305,24 @@ fail_free_ms_iocb: dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt), ha->sns_cmd, ha->sns_cmd_dma); fail_dma_pool: + if (ql2xenabledif) { + struct dsd_dma *dsd, *nxt; + + list_for_each_entry_safe(dsd, nxt, &ha->pool.unusable.head, + list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, + dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + ha->pool.unusable.count--; + } + dma_pool_destroy(ha->dif_bundl_pool); + ha->dif_bundl_pool = NULL; + } + +fail_dif_bundl_dma_pool: if (IS_QLA82XX(ha) || ql2xenabledif) { dma_pool_destroy(ha->fcp_cmnd_dma_pool); ha->fcp_cmnd_dma_pool = NULL; @@ -4564,6 +4703,32 @@ qla2x00_mem_free(struct qla_hw_data *ha) mempool_destroy(ha->ctx_mempool); + if (ql2xenabledif) { + struct dsd_dma *dsd, *nxt; + + list_for_each_entry_safe(dsd, nxt, &ha->pool.unusable.head, + list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, + dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + ha->pool.unusable.count--; + } + list_for_each_entry_safe(dsd, nxt, &ha->pool.good.head, list) { + list_del(&dsd->list); + dma_pool_free(ha->dif_bundl_pool, dsd->dsd_addr, + dsd->dsd_list_dma); + ha->dif_bundle_dma_allocs--; + kfree(dsd); + ha->dif_bundle_kallocs--; + } + } + + if (ha->dif_bundl_pool) + dma_pool_destroy(ha->dif_bundl_pool); + qlt_mem_free(ha); if (ha->init_cb) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 510337eac106..764b6ed74cfc 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3230,7 +3230,7 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm) cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address; if (qla24xx_walk_and_build_prot_sglist(ha, NULL, cur_dsd, - prm->prot_seg_cnt, &tc)) + prm->prot_seg_cnt, cmd)) goto crc_queuing_error; } return QLA_SUCCESS; diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index 577e1786a3f1..f3de75000a08 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -928,6 +928,8 @@ struct qla_tgt_cmd { uint64_t lba; uint16_t a_guard, e_guard, a_app_tag, e_app_tag; uint32_t a_ref_tag, e_ref_tag; +#define DIF_BUNDL_DMA_VALID 1 + uint16_t prot_flags; uint64_t jiffies_at_alloc; uint64_t jiffies_at_free; From ff525b6e2d3011d5a1ef08d8b3f4f745c7245412 Mon Sep 17 00:00:00 2001 From: John Garry Date: Sat, 5 Jan 2019 00:01:26 +0800 Subject: [PATCH 0241/4277] scsi: libsas: Fix some indentation in libsas.h Currently much indentation in this file is done with whitespaces instead of tabs, which can make reading difficult, so fix this up. Some other little minor tidy-up is done, but this file still has many other checkpatch warnings (generally linelength > 80 or function arguments have no identifier names). All libsas code can be audited for checkpatch issues later. Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- include/scsi/libsas.h | 56 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 3de3b10da19a..857086cf7ebf 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -52,8 +52,8 @@ enum sas_phy_role { }; enum sas_phy_type { - PHY_TYPE_PHYSICAL, - PHY_TYPE_VIRTUAL + PHY_TYPE_PHYSICAL, + PHY_TYPE_VIRTUAL }; /* The events are mnemonically described in sas_dump.c @@ -91,7 +91,7 @@ enum discover_event { #define to_dom_device(_obj) container_of(_obj, struct domain_device, dev_obj) #define to_dev_attr(_attr) container_of(_attr, struct domain_dev_attribute,\ - attr) + attr) enum routing_attribute { DIRECT_ROUTING, @@ -184,37 +184,37 @@ struct domain_device { spinlock_t done_lock; enum sas_device_type dev_type; - enum sas_linkrate linkrate; - enum sas_linkrate min_linkrate; - enum sas_linkrate max_linkrate; + enum sas_linkrate linkrate; + enum sas_linkrate min_linkrate; + enum sas_linkrate max_linkrate; - int pathways; + int pathways; - struct domain_device *parent; - struct list_head siblings; /* devices on the same level */ - struct asd_sas_port *port; /* shortcut to root of the tree */ + struct domain_device *parent; + struct list_head siblings; /* devices on the same level */ + struct asd_sas_port *port; /* shortcut to root of the tree */ struct sas_phy *phy; - struct list_head dev_list_node; + struct list_head dev_list_node; struct list_head disco_list_node; /* awaiting probe or destruct */ - enum sas_protocol iproto; - enum sas_protocol tproto; + enum sas_protocol iproto; + enum sas_protocol tproto; - struct sas_rphy *rphy; + struct sas_rphy *rphy; - u8 sas_addr[SAS_ADDR_SIZE]; - u8 hashed_sas_addr[HASHED_SAS_ADDR_SIZE]; + u8 sas_addr[SAS_ADDR_SIZE]; + u8 hashed_sas_addr[HASHED_SAS_ADDR_SIZE]; - u8 frame_rcvd[32]; + u8 frame_rcvd[32]; - union { - struct expander_device ex_dev; - struct sata_device sata_dev; /* STP & directly attached */ + union { + struct expander_device ex_dev; + struct sata_device sata_dev; /* STP & directly attached */ struct ssp_device ssp_dev; - }; + }; - void *lldd_dev; + void *lldd_dev; unsigned long state; struct kref kref; }; @@ -512,10 +512,10 @@ enum exec_status { /* When a task finishes with a response, the LLDD examines the * response: - * - For an ATA task task_status_struct::stat is set to + * - For an ATA task task_status_struct::stat is set to * SAS_PROTO_RESPONSE, and the task_status_struct::buf is set to the * contents of struct ata_task_resp. - * - For SSP tasks, if no data is present or status/TMF response + * - For SSP tasks, if no data is present or status/TMF response * is valid, task_status_struct::stat is set. If data is present * (SENSE data), the LLDD copies up to SAS_STATUS_BUF_SIZE, sets * task_status_struct::buf_valid_size, and task_status_struct::stat is @@ -671,15 +671,13 @@ extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); extern void sas_resume_ha(struct sas_ha_struct *sas_ha); extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); -int sas_set_phy_speed(struct sas_phy *phy, - struct sas_phy_linkrates *rates); +int sas_set_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates); int sas_phy_reset(struct sas_phy *phy, int hard_reset); -extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *); +extern int sas_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); extern int sas_target_alloc(struct scsi_target *); extern int sas_slave_configure(struct scsi_device *); extern int sas_change_queue_depth(struct scsi_device *, int new_depth); -extern int sas_bios_param(struct scsi_device *, - struct block_device *, +extern int sas_bios_param(struct scsi_device *, struct block_device *, sector_t capacity, int *hsc); extern struct scsi_transport_template * sas_domain_attach_transport(struct sas_domain_function_template *); From 01929a65dfa13e18d89264ab1378854a91857e59 Mon Sep 17 00:00:00 2001 From: John Garry Date: Sat, 5 Jan 2019 00:01:27 +0800 Subject: [PATCH 0242/4277] scsi: libsas: Check SMP PHY control function result Currently the SMP PHY control execution result is checked, however the function result for the command is not. As such, we may be missing all potential errors, like SMP FUNCTION FAILED, INVALID REQUEST FRAME LENGTH, etc., meaning the PHY control request has failed. In some scenarios we need to ensure the function result is accepted, so add a check for this. Tested-by: Jian Luo Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_expander.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 17eb4185f29d..8817b8e6e507 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -614,7 +614,14 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, } res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE); - + if (res) { + pr_err("ex %016llx phy%02d PHY control failed: %d\n", + SAS_ADDR(dev->sas_addr), phy_id, res); + } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) { + pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n", + SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]); + res = pc_resp[2]; + } kfree(pc_resp); kfree(pc_req); return res; From cec9771d2e954650095aa37a6a97722c8194e7d2 Mon Sep 17 00:00:00 2001 From: John Garry Date: Sat, 5 Jan 2019 00:01:28 +0800 Subject: [PATCH 0243/4277] scsi: libsas: Support SATA PHY connection rate unmatch fixing during discovery +----------+ +----------+ | | | | | |--- 3.0 G ---| |--- 6.0 G --- SAS disk | | | | | |--- 3.0 G ---| |--- 6.0 G --- SAS disk |initiator | | | | device |--- 3.0 G ---| Expander |--- 6.0 G --- SAS disk | | | | | |--- 3.0 G ---| |--- 6.0 G --- SATA disk -->failed to connect | | | | | | | |--- 6.0 G --- SATA disk -->failed to connect | | | | +----------+ +----------+ According to Serial Attached SCSI - 1.1 (SAS-1.1): If an expander PHY attached to a SATA PHY is using a physical link rate greater than the maximum connection rate supported by the pathway from an STP initiator port, a management application client should use the SMP PHY CONTROL function (see 10.4.3.10) to set the PROGRAMMED MAXIMUM PHYSICAL LINK RATE field of the expander PHY to the maximum connection rate supported by the pathway from that STP initiator port. Currently libsas does not support checking if this condition occurs, nor rectifying when it does. Such a condition is not at all common, however it has been seen on some pre-silicon environments where the initiator PHY only supports a 1.5 Gbit maximum linkrate, mated with 12G expander PHYs and 3/6G SATA phy. This patch adds support for checking and rectifying this condition during initial device discovery only. We do support checking min pathway connection rate during revalidation phase, when new devices can be detected in the topology. However we do not support in the case of the the user reprogramming PHY linkrates, such that min pathway condition is not met/maintained. A note on root port PHY rates: The libsas root port PHY rates calculation is broken. Libsas sets the rates (min, max, and current linkrate) of a root port to the same linkrate of the first PHY member of that same port. In doing so, it assumes that all other PHYs which subsequently join the port to have the same negotiated linkrate, when they could actually be different. In practice this doesn't happen, as initiator and expander PHYs are normally initialised with consistent min/max linkrates. This has not caused an issue so far, so leave alone for now. Tested-by: Jian Luo Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_expander.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 8817b8e6e507..83e3715d30e4 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -824,6 +824,26 @@ static struct domain_device *sas_ex_discover_end_dev( #ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { + if (child->linkrate > parent->min_linkrate) { + struct sas_phy_linkrates rates = { + .maximum_linkrate = parent->min_linkrate, + .minimum_linkrate = parent->min_linkrate, + }; + int ret; + + pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n", + SAS_ADDR(child->sas_addr), phy_id); + ret = sas_smp_phy_control(parent, phy_id, + PHY_FUNC_LINK_RESET, &rates); + if (ret) { + pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n", + SAS_ADDR(child->sas_addr), phy_id, ret); + goto out_free; + } + pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n", + SAS_ADDR(child->sas_addr), phy_id); + child->linkrate = child->min_linkrate; + } res = sas_get_ata_info(child, phy); if (res) goto out_free; From afd055809b3829bd292ee6ef9b98693d34fcd0f0 Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Wed, 9 Jan 2019 20:08:56 +0100 Subject: [PATCH 0244/4277] scsi: doc: remove reference to tmscsim.txt file The tmscsim.txt doc file was removed in c121107d0f84. Fixes: c121107d0f84 ("scsi: documentation: Obsolete documentation references") Signed-off-by: Otto Sabart Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 4d5d79d56f4b..85c5c89f2fa5 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1196,8 +1196,6 @@ config SCSI_AM53C974 PCscsi/PCnet (Am53/79C974) solutions. This is a new implementation base on the generic esp_scsi driver. - Documentation can be found in . - Note that this driver does NOT support Tekram DC390W/U/F, which are based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those. From 326a859b28141bcd3445be0606647250742283a6 Mon Sep 17 00:00:00 2001 From: Evan Green Date: Fri, 11 Jan 2019 15:31:59 -0800 Subject: [PATCH 0245/4277] scsi: ufs: Remove select of phy-qcom-ufs from ufs-qcom CONFIG_SCSI_UFS_QCOM selects CONFIG_PHY_QCOM_UFS, assuming that this was the only possible PHY driver Qualcomm's UFS controller would use. But in SDM845, the UFS driver is bundled into phy-qcom-qmp, and phy-qcom-ufs is unused. Remove the select, since for SDM845 it adds useless drivers to the build. Signed-off-by: Evan Green Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index 2ddbb26d9c26..6db37cf306b0 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -99,7 +99,6 @@ config SCSI_UFS_DWC_TC_PLATFORM config SCSI_UFS_QCOM tristate "QCOM specific hooks to UFS controller platform driver" depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM - select PHY_QCOM_UFS help This selects the QCOM specific additions to UFSHCD platform driver. UFS host on QCOM needs some vendor specific configuration before From 88ca66d8540ca26119b1428cddb96b37925bdf01 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 11 Jan 2019 09:49:30 +0100 Subject: [PATCH 0246/4277] x86/asm: Remove dead __GNUC__ conditionals The minimum supported gcc version is >= 4.6, so these can be removed. Signed-off-by: Rasmus Villemoes Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Dan Williams Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Matthew Wilcox Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: x86-ml Link: https://lkml.kernel.org/r/20190111084931.24601-1-linux@rasmusvillemoes.dk --- arch/x86/include/asm/bitops.h | 6 ------ arch/x86/include/asm/string_32.h | 20 -------------------- arch/x86/include/asm/string_64.h | 15 --------------- 3 files changed, 41 deletions(-) diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h index ad7b210aa3f6..d153d570bb04 100644 --- a/arch/x86/include/asm/bitops.h +++ b/arch/x86/include/asm/bitops.h @@ -36,13 +36,7 @@ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). */ -#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1) -/* Technically wrong, but this avoids compilation errors on some gcc - versions. */ -#define BITOP_ADDR(x) "=m" (*(volatile long *) (x)) -#else #define BITOP_ADDR(x) "+m" (*(volatile long *) (x)) -#endif #define ADDR BITOP_ADDR(addr) diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h index 55d392c6bd29..2fd165f1cffa 100644 --- a/arch/x86/include/asm/string_32.h +++ b/arch/x86/include/asm/string_32.h @@ -179,14 +179,7 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len) * No 3D Now! */ -#if (__GNUC__ >= 4) #define memcpy(t, f, n) __builtin_memcpy(t, f, n) -#else -#define memcpy(t, f, n) \ - (__builtin_constant_p((n)) \ - ? __constant_memcpy((t), (f), (n)) \ - : __memcpy((t), (f), (n))) -#endif #endif #endif /* !CONFIG_FORTIFY_SOURCE */ @@ -282,12 +275,7 @@ void *__constant_c_and_count_memset(void *s, unsigned long pattern, { int d0, d1; -#if __GNUC__ == 4 && __GNUC_MINOR__ == 0 - /* Workaround for broken gcc 4.0 */ - register unsigned long eax asm("%eax") = pattern; -#else unsigned long eax = pattern; -#endif switch (count % 4) { case 0: @@ -321,15 +309,7 @@ void *__constant_c_and_count_memset(void *s, unsigned long pattern, #define __HAVE_ARCH_MEMSET extern void *memset(void *, int, size_t); #ifndef CONFIG_FORTIFY_SOURCE -#if (__GNUC__ >= 4) #define memset(s, c, count) __builtin_memset(s, c, count) -#else -#define memset(s, c, count) \ - (__builtin_constant_p(c) \ - ? __constant_c_x_memset((s), (0x01010101UL * (unsigned char)(c)), \ - (count)) \ - : __memset((s), (c), (count))) -#endif #endif /* !CONFIG_FORTIFY_SOURCE */ #define __HAVE_ARCH_MEMSET16 diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h index 4e4194e21a09..75314c3dbe47 100644 --- a/arch/x86/include/asm/string_64.h +++ b/arch/x86/include/asm/string_64.h @@ -14,21 +14,6 @@ extern void *memcpy(void *to, const void *from, size_t len); extern void *__memcpy(void *to, const void *from, size_t len); -#ifndef CONFIG_FORTIFY_SOURCE -#if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4 -#define memcpy(dst, src, len) \ -({ \ - size_t __len = (len); \ - void *__ret; \ - if (__builtin_constant_p(len) && __len >= 64) \ - __ret = __memcpy((dst), (src), __len); \ - else \ - __ret = __builtin_memcpy((dst), (src), __len); \ - __ret; \ -}) -#endif -#endif /* !CONFIG_FORTIFY_SOURCE */ - #define __HAVE_ARCH_MEMSET void *memset(void *s, int c, size_t n); void *__memset(void *s, int c, size_t n); From 2e905c7abdcd5ff09b9df33d25eb7148c85bed2a Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 11 Jan 2019 09:49:31 +0100 Subject: [PATCH 0247/4277] x86/asm: Remove unused __constant_c_x_memset() macro and inlines Nothing refers to the __constant_c_x_memset() macro anymore. Remove it and the two referenced static inline functions. Signed-off-by: Rasmus Villemoes Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Thomas Gleixner Cc: x86-ml Link: https://lkml.kernel.org/r/20190111084931.24601-2-linux@rasmusvillemoes.dk --- arch/x86/include/asm/string_32.h | 84 -------------------------------- 1 file changed, 84 deletions(-) diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h index 2fd165f1cffa..f74362b05619 100644 --- a/arch/x86/include/asm/string_32.h +++ b/arch/x86/include/asm/string_32.h @@ -209,29 +209,6 @@ static inline void *__memset_generic(void *s, char c, size_t count) /* we might want to write optimized versions of these later */ #define __constant_count_memset(s, c, count) __memset_generic((s), (c), (count)) -/* - * memset(x, 0, y) is a reasonably common thing to do, so we want to fill - * things 32 bits at a time even when we don't know the size of the - * area at compile-time.. - */ -static __always_inline -void *__constant_c_memset(void *s, unsigned long c, size_t count) -{ - int d0, d1; - asm volatile("rep ; stosl\n\t" - "testb $2,%b3\n\t" - "je 1f\n\t" - "stosw\n" - "1:\ttestb $1,%b3\n\t" - "je 2f\n\t" - "stosb\n" - "2:" - : "=&c" (d0), "=&D" (d1) - : "a" (c), "q" (count), "0" (count/4), "1" ((long)s) - : "memory"); - return s; -} - /* Added by Gertjan van Wingerde to make minix and sysv module work */ #define __HAVE_ARCH_STRNLEN extern size_t strnlen(const char *s, size_t count); @@ -240,67 +217,6 @@ extern size_t strnlen(const char *s, size_t count); #define __HAVE_ARCH_STRSTR extern char *strstr(const char *cs, const char *ct); -/* - * This looks horribly ugly, but the compiler can optimize it totally, - * as we by now know that both pattern and count is constant.. - */ -static __always_inline -void *__constant_c_and_count_memset(void *s, unsigned long pattern, - size_t count) -{ - switch (count) { - case 0: - return s; - case 1: - *(unsigned char *)s = pattern & 0xff; - return s; - case 2: - *(unsigned short *)s = pattern & 0xffff; - return s; - case 3: - *(unsigned short *)s = pattern & 0xffff; - *((unsigned char *)s + 2) = pattern & 0xff; - return s; - case 4: - *(unsigned long *)s = pattern; - return s; - } - -#define COMMON(x) \ - asm volatile("rep ; stosl" \ - x \ - : "=&c" (d0), "=&D" (d1) \ - : "a" (eax), "0" (count/4), "1" ((long)s) \ - : "memory") - - { - int d0, d1; - unsigned long eax = pattern; - - switch (count % 4) { - case 0: - COMMON(""); - return s; - case 1: - COMMON("\n\tstosb"); - return s; - case 2: - COMMON("\n\tstosw"); - return s; - default: - COMMON("\n\tstosw\n\tstosb"); - return s; - } - } - -#undef COMMON -} - -#define __constant_c_x_memset(s, c, count) \ - (__builtin_constant_p(count) \ - ? __constant_c_and_count_memset((s), (c), (count)) \ - : __constant_c_memset((s), (c), (count))) - #define __memset(s, c, count) \ (__builtin_constant_p(count) \ ? __constant_count_memset((s), (c), (count)) \ From fb5fc09cc8033db207803cb4b6d0409505dc0fd4 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 11 Jan 2019 17:44:31 -0800 Subject: [PATCH 0248/4277] Input: tca6416-keypad - use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tca6416-keypad.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index dc983ab6c0ad..cdeef180aead 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -219,9 +219,7 @@ static int tca6416_keypad_probe(struct i2c_client *client, return -EINVAL; } - chip = kzalloc(sizeof(struct tca6416_keypad_chip) + - pdata->nbuttons * sizeof(struct tca6416_button), - GFP_KERNEL); + chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL); input = input_allocate_device(); if (!chip || !input) { error = -ENOMEM; From a73450036e3222ffd1cb20fc4a88832bb17921b8 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Sun, 13 Jan 2019 22:30:18 -0800 Subject: [PATCH 0249/4277] Input: mcs_touchkey - use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mcs_touchkey.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index be56d4f262a7..b132662201a4 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -113,9 +113,8 @@ static int mcs_touchkey_probe(struct i2c_client *client, return -EINVAL; } - data = kzalloc(sizeof(struct mcs_touchkey_data) + - sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), - GFP_KERNEL); + data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1), + GFP_KERNEL); input_dev = input_allocate_device(); if (!data || !input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); From 1eb7ea26d12345898d2647e00c1f675204143bef Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 21 Dec 2018 00:45:41 -0800 Subject: [PATCH 0250/4277] Input: mtk-pmic-keys - remove duplicated include from mtk-pmic-keys.c iSOrt includes in alphabetical order and remove duplicated include file linux/kernel.h Signed-off-by: YueHaibing Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mtk-pmic-keys.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index 02c67a1749fc..8e6ebab05ab4 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -14,18 +14,17 @@ * */ -#include -#include #include #include -#include #include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include #define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1 #define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6 From a2f39dac0decd9943ba14e14fb58535bb149fd4e Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Sun, 13 Jan 2019 23:08:32 -0800 Subject: [PATCH 0251/4277] Input: edt-ft5x06 - add support for Evervision FT5726 Evervision displays are using different Focaltech touchscreen controllers. This commit adds the initial support for the ones using the FT5726 controller. Receiving the touch data is the same as for the GENERIC_FT but the x and y cooridnates are swapped. The main differences are the register addresses where the GAIN and THRESHOLD parameters are stored. Signed-off-by: Marco Felsch Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/edt-ft5x06.txt | 4 +- drivers/input/touchscreen/edt-ft5x06.c | 44 +++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index da2dc5d6c98b..179e434f5619 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -1,11 +1,12 @@ FocalTech EDT-FT5x06 Polytouch driver ===================================== -There are 3 variants of the chip for various touch panel sizes +There are 5 variants of the chip for various touch panel sizes FT5206GE1 2.8" .. 3.8" FT5306DE4 4.3" .. 7" FT5406EE8 7" .. 8.9" FT5506EEG 7" .. 8.9" +FT5726NEI 5.7” .. 11.6" The software interface is identical for all those chips, so that currently there is no need for the driver to distinguish between the @@ -19,6 +20,7 @@ Required properties: or: "edt,edt-ft5306" or: "edt,edt-ft5406" or: "edt,edt-ft5506" + or: "evervision,ev-ft5726" or: "focaltech,ft6236" - reg: I2C slave address of the chip (0x38) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 1e18ca0d1b4e..1cd5b804a456 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,9 @@ #define M09_REGISTER_NUM_X 0x94 #define M09_REGISTER_NUM_Y 0x95 +#define EV_REGISTER_THRESHOLD 0x40 +#define EV_REGISTER_GAIN 0x41 + #define NO_REGISTER 0xff #define WORK_REGISTER_OPMODE 0x3c @@ -73,6 +77,7 @@ enum edt_ver { EDT_M06, EDT_M09, EDT_M12, + EV_FT, GENERIC_FT, }; @@ -190,6 +195,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) case EDT_M09: case EDT_M12: + case EV_FT: case GENERIC_FT: cmd = 0x0; offset = 3; @@ -242,6 +248,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) x = ((buf[0] << 8) | buf[1]) & 0x0fff; y = ((buf[2] << 8) | buf[3]) & 0x0fff; + /* The FT5x26 send the y coordinate first */ + if (tsdata->version == EV_FT) + swap(x, y); + id = (buf[2] >> 4) & 0x0f; down = type != TOUCH_EVENT_UP; @@ -275,8 +285,10 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); + /* fallthrough */ case EDT_M09: case EDT_M12: + case EV_FT: case GENERIC_FT: wrbuf[0] = addr; wrbuf[1] = value; @@ -315,8 +327,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, } break; + /* fallthrough */ case EDT_M09: case EDT_M12: + case EV_FT: case GENERIC_FT: wrbuf[0] = addr; error = edt_ft5x06_ts_readwrite(tsdata->client, 1, @@ -605,8 +619,9 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) tsdata->threshold); edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, tsdata->gain); - edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, - tsdata->offset); + if (reg_addr->reg_offset != NO_REGISTER) + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, + tsdata->offset); if (reg_addr->reg_report_rate != NO_REGISTER) edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, tsdata->report_rate); @@ -867,6 +882,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, case 0x5a: /* Solomon Goldentek Display */ snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0"); break; + case 0x59: /* Evervision Display with FT5xx6 TS */ + tsdata->version = EV_FT; + error = edt_ft5x06_ts_readwrite(client, 1, "\x53", + 1, rdbuf); + if (error) + return error; + strlcpy(fw_version, rdbuf, 1); + snprintf(model_name, EDT_NAME_LEN, + "EVERVISION-FT5726NEi"); + break; default: snprintf(model_name, EDT_NAME_LEN, "generic ft5x06 (%02x)", @@ -912,7 +937,9 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) tsdata->threshold = edt_ft5x06_register_read(tsdata, reg_addr->reg_threshold); tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); - tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_offset != NO_REGISTER) + tsdata->offset = + edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); if (reg_addr->reg_report_rate != NO_REGISTER) tsdata->report_rate = edt_ft5x06_register_read(tsdata, reg_addr->reg_report_rate); @@ -954,6 +981,15 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_num_y = M09_REGISTER_NUM_Y; break; + case EV_FT: + reg_addr->reg_threshold = EV_REGISTER_THRESHOLD; + reg_addr->reg_gain = EV_REGISTER_GAIN; + reg_addr->reg_offset = NO_REGISTER; + reg_addr->reg_num_x = NO_REGISTER; + reg_addr->reg_num_y = NO_REGISTER; + reg_addr->reg_report_rate = NO_REGISTER; + break; + case GENERIC_FT: /* this is a guesswork */ reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; @@ -1155,6 +1191,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = { static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, + { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data }, /* Note no edt- prefix for compatibility with the ft6236.c driver */ { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, { /* sentinel */ } @@ -1167,6 +1204,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, + { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data }, /* Note focaltech vendor prefix for compatibility with ft6236.c */ { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, { /* sentinel */ } From 2ebc1919e9a9812903ab684cc53862015987e7a0 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Sun, 13 Jan 2019 23:10:22 -0800 Subject: [PATCH 0252/4277] Input: edt-ft5x06 - add support to update ev-ft5726 registers Currently only the threshold and gain parameters can be read. Signed-off-by: Marco Felsch Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 1cd5b804a456..a67915535b47 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -353,9 +353,10 @@ struct edt_ft5x06_attribute { u8 limit_high; u8 addr_m06; u8 addr_m09; + u8 addr_ev; }; -#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \ +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \ _limit_low, _limit_high) \ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ .dattr = __ATTR(_field, _mode, \ @@ -364,6 +365,7 @@ struct edt_ft5x06_attribute { .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ .addr_m06 = _addr_m06, \ .addr_m09 = _addr_m09, \ + .addr_ev = _addr_ev, \ .limit_low = _limit_low, \ .limit_high = _limit_high, \ } @@ -400,6 +402,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, addr = attr->addr_m09; break; + case EV_FT: + addr = attr->addr_ev; + break; + default: error = -ENODEV; goto out; @@ -471,6 +477,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, addr = attr->addr_m09; break; + case EV_FT: + addr = attr->addr_ev; + break; + default: error = -ENODEV; goto out; @@ -494,16 +504,16 @@ out: /* m06, m09: range 0-31, m12: range 0-5 */ static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, - M09_REGISTER_GAIN, 0, 31); + M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31); /* m06, m09: range 0-31, m12: range 0-16 */ static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, - M09_REGISTER_OFFSET, 0, 31); + M09_REGISTER_OFFSET, NO_REGISTER, 0, 31); /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, - M09_REGISTER_THRESHOLD, 0, 255); + M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); /* m06: range 3 to 14, m12: (0x64: 100Hz) */ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, - NO_REGISTER, 0, 255); + NO_REGISTER, NO_REGISTER, 0, 255); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, From b6eba86030bf2fa3abcf9a0e3fb04527330da52e Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Sun, 13 Jan 2019 23:10:50 -0800 Subject: [PATCH 0253/4277] Input: edt-ft5x06 - add offset support for ev-ft5726 Unfortunately the evervision focaltech implementation uses two offset registers, one for the x coordinate and one for y. This patch extends the driver to handle those offset registers only for devices that support these. Signed-off-by: Marco Felsch Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/edt-ft5x06.txt | 9 ++++ drivers/input/touchscreen/edt-ft5x06.c | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index 179e434f5619..870b8c5cce9b 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -44,6 +44,15 @@ Optional properties: - offset: allows setting the edge compensation in the range from 0 to 31. + + - offset-x: Same as offset, but applies only to the horizontal position. + Range from 0 to 80, only supported by evervision,ev-ft5726 + devices. + + - offset-y: Same as offset, but applies only to the vertical position. + Range from 0 to 80, only supported by evervision,ev-ft5726 + devices. + - touchscreen-size-x : See touchscreen.txt - touchscreen-size-y : See touchscreen.txt - touchscreen-fuzz-x : See touchscreen.txt diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index a67915535b47..702bfda7ee77 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -56,6 +56,8 @@ #define EV_REGISTER_THRESHOLD 0x40 #define EV_REGISTER_GAIN 0x41 +#define EV_REGISTER_OFFSET_Y 0x45 +#define EV_REGISTER_OFFSET_X 0x46 #define NO_REGISTER 0xff @@ -86,6 +88,8 @@ struct edt_reg_addr { int reg_report_rate; int reg_gain; int reg_offset; + int reg_offset_x; + int reg_offset_y; int reg_num_x; int reg_num_y; }; @@ -111,6 +115,8 @@ struct edt_ft5x06_ts_data { int threshold; int gain; int offset; + int offset_x; + int offset_y; int report_rate; int max_support_points; @@ -508,6 +514,12 @@ static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, /* m06, m09: range 0-31, m12: range 0-16 */ static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, M09_REGISTER_OFFSET, NO_REGISTER, 0, 31); +/* m06, m09, m12: no supported, ev_ft: range 0-80 */ +static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, + EV_REGISTER_OFFSET_X, 0, 80); +/* m06, m09, m12: no supported, ev_ft: range 0-80 */ +static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, + EV_REGISTER_OFFSET_Y, 0, 80); /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); @@ -518,6 +530,8 @@ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, &edt_ft5x06_attr_offset.dattr.attr, + &edt_ft5x06_attr_offset_x.dattr.attr, + &edt_ft5x06_attr_offset_y.dattr.attr, &edt_ft5x06_attr_threshold.dattr.attr, &edt_ft5x06_attr_report_rate.dattr.attr, NULL @@ -632,6 +646,12 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) if (reg_addr->reg_offset != NO_REGISTER) edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, tsdata->offset); + if (reg_addr->reg_offset_x != NO_REGISTER) + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, + tsdata->offset_x); + if (reg_addr->reg_offset_y != NO_REGISTER) + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, + tsdata->offset_y); if (reg_addr->reg_report_rate != NO_REGISTER) edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, tsdata->report_rate); @@ -937,6 +957,18 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev, edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val); tsdata->offset = val; } + + error = device_property_read_u32(dev, "offset-x", &val); + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val); + tsdata->offset_x = val; + } + + error = device_property_read_u32(dev, "offset-y", &val); + if (!error) { + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val); + tsdata->offset_y = val; + } } static void @@ -950,6 +982,12 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) if (reg_addr->reg_offset != NO_REGISTER) tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_offset_x != NO_REGISTER) + tsdata->offset_x = edt_ft5x06_register_read(tsdata, + reg_addr->reg_offset_x); + if (reg_addr->reg_offset_y != NO_REGISTER) + tsdata->offset_y = edt_ft5x06_register_read(tsdata, + reg_addr->reg_offset_y); if (reg_addr->reg_report_rate != NO_REGISTER) tsdata->report_rate = edt_ft5x06_register_read(tsdata, reg_addr->reg_report_rate); @@ -977,6 +1015,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; reg_addr->reg_gain = WORK_REGISTER_GAIN; reg_addr->reg_offset = WORK_REGISTER_OFFSET; + reg_addr->reg_offset_x = NO_REGISTER; + reg_addr->reg_offset_y = NO_REGISTER; reg_addr->reg_num_x = WORK_REGISTER_NUM_X; reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; break; @@ -987,6 +1027,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_report_rate = NO_REGISTER; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_offset_x = NO_REGISTER; + reg_addr->reg_offset_y = NO_REGISTER; reg_addr->reg_num_x = M09_REGISTER_NUM_X; reg_addr->reg_num_y = M09_REGISTER_NUM_Y; break; @@ -995,6 +1037,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_threshold = EV_REGISTER_THRESHOLD; reg_addr->reg_gain = EV_REGISTER_GAIN; reg_addr->reg_offset = NO_REGISTER; + reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X; + reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y; reg_addr->reg_num_x = NO_REGISTER; reg_addr->reg_num_y = NO_REGISTER; reg_addr->reg_report_rate = NO_REGISTER; @@ -1005,6 +1049,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_offset_x = NO_REGISTER; + reg_addr->reg_offset_y = NO_REGISTER; break; } } From 6daae00243e622dd3feec7965bfe421ad6dd317e Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 12 Jan 2019 13:59:13 +0100 Subject: [PATCH 0254/4277] pinctrl: meson: meson8b: add the eth_rxd2 and eth_rxd3 pins Gigabit Ethernet requires the Ethernet TXD0..3 and RXD0..3 data lines. Add the missing eth_rxd2 and eth_rxd3 definitions so we don't have to rely on the bootloader to set them up correctly. The vendor u-boot sources for Odroid-C1 use the following Ethernet pinmux configuration: SET_CBUS_REG_MASK(PERIPHS_PIN_MUX_6, 0x3f4f); SET_CBUS_REG_MASK(PERIPHS_PIN_MUX_7, 0xf00000); This translates to the following pin groups in the mainline kernel: - register 6 bit 0: eth_rxd1 (DIF_0_P) - register 6 bit 1: eth_rxd0 (DIF_0_N) - register 6 bit 2: eth_rx_dv (DIF_1_P) - register 6 bit 3: eth_rx_clk (DIF_1_N) - register 6 bit 6: eth_tx_en (DIF_3_P) - register 6 bit 8: eth_ref_clk (DIF_3_N) - register 6 bit 9: eth_mdc (DIF_4_P) - register 6 bit 10: eth_mdio_en (DIF_4_N) - register 6 bit 11: eth_tx_clk (GPIOH_9) - register 6 bit 12: eth_txd2 (GPIOH_8) - register 6 bit 13: eth_txd3 (GPIOH_7) - register 7 bit 20: eth_txd0_0 (GPIOH_6) - register 7 bit 21: eth_txd1_0 (GPIOH_5) - register 7 bit 22: eth_rxd3 (DIF_2_P) - register 7 bit 23: eth_rxd2 (DIF_2_N) All functions except eth_rxd2 and eth_rxd3 are already supported by the pinctrl-meson8b driver. Suggested-by: Jianxin Pan Signed-off-by: Martin Blumenstingl Reviewed-by: Kevin Hilman Tested-by: Emiliano Ingrassia Reviewed-by: Emiliano Ingrassia Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-meson8b.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c index c69ca95b1ad5..84938bd73ac2 100644 --- a/drivers/pinctrl/meson/pinctrl-meson8b.c +++ b/drivers/pinctrl/meson/pinctrl-meson8b.c @@ -346,6 +346,8 @@ static const unsigned int eth_rx_dv_pins[] = { DIF_1_P }; static const unsigned int eth_rx_clk_pins[] = { DIF_1_N }; static const unsigned int eth_txd0_1_pins[] = { DIF_2_P }; static const unsigned int eth_txd1_1_pins[] = { DIF_2_N }; +static const unsigned int eth_rxd3_pins[] = { DIF_2_P }; +static const unsigned int eth_rxd2_pins[] = { DIF_2_N }; static const unsigned int eth_tx_en_pins[] = { DIF_3_P }; static const unsigned int eth_ref_clk_pins[] = { DIF_3_N }; static const unsigned int eth_mdc_pins[] = { DIF_4_P }; @@ -599,6 +601,8 @@ static struct meson_pmx_group meson8b_cbus_groups[] = { GROUP(eth_ref_clk, 6, 8), GROUP(eth_mdc, 6, 9), GROUP(eth_mdio_en, 6, 10), + GROUP(eth_rxd3, 7, 22), + GROUP(eth_rxd2, 7, 23), }; static struct meson_pmx_group meson8b_aobus_groups[] = { @@ -748,7 +752,7 @@ static const char * const ethernet_groups[] = { "eth_tx_clk", "eth_tx_en", "eth_txd1_0", "eth_txd1_1", "eth_txd0_0", "eth_txd0_1", "eth_rx_clk", "eth_rx_dv", "eth_rxd1", "eth_rxd0", "eth_mdio_en", "eth_mdc", "eth_ref_clk", - "eth_txd2", "eth_txd3" + "eth_txd2", "eth_txd3", "eth_rxd3", "eth_rxd2" }; static const char * const i2c_a_groups[] = { From 226a893bbb1f4495d7139fd57143475ada0de61d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 3 Jan 2019 17:45:58 +0100 Subject: [PATCH 0255/4277] microblaze: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Michal Simek Cc: Rob Herring Signed-off-by: Greg Kroah-Hartman Signed-off-by: Michal Simek --- arch/microblaze/kernel/setup.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index bbd6968ce55b..522a0c5d9c59 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -192,23 +192,14 @@ struct dentry *of_debugfs_root; static int microblaze_debugfs_init(void) { of_debugfs_root = debugfs_create_dir("microblaze", NULL); - - return of_debugfs_root == NULL; + return 0; } arch_initcall(microblaze_debugfs_init); # ifdef CONFIG_MMU static int __init debugfs_tlb(void) { - struct dentry *d; - - if (!of_debugfs_root) - return -ENODEV; - - d = debugfs_create_u32("tlb_skip", S_IRUGO, of_debugfs_root, &tlb_skip); - if (!d) - return -ENOMEM; - + debugfs_create_u32("tlb_skip", S_IRUGO, of_debugfs_root, &tlb_skip); return 0; } device_initcall(debugfs_tlb); From b62928ff55290e47a7d546d427bf7b4ccf5ea944 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2019 17:36:45 +0200 Subject: [PATCH 0256/4277] x86/MCE: Switch to use the new generic UUID API Switch the code to use the new, generic helpers. Signed-off-by: Andy Shevchenko Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Christoph Hellwig Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Tony Luck Cc: x86-ml Link: https://lkml.kernel.org/r/20190110153645.40649-1-andriy.shevchenko@linux.intel.com --- arch/x86/kernel/cpu/mce/apei.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/mce/apei.c b/arch/x86/kernel/cpu/mce/apei.c index 1d9b3ce662a0..c038e5c00a59 100644 --- a/arch/x86/kernel/cpu/mce/apei.c +++ b/arch/x86/kernel/cpu/mce/apei.c @@ -64,11 +64,11 @@ void apei_mce_report_mem_error(int severity, struct cper_sec_mem_err *mem_err) EXPORT_SYMBOL_GPL(apei_mce_report_mem_error); #define CPER_CREATOR_MCE \ - UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ - 0x64, 0x90, 0xb8, 0x9d) + GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ + 0x64, 0x90, 0xb8, 0x9d) #define CPER_SECTION_TYPE_MCE \ - UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ - 0x04, 0x4a, 0x38, 0xfc) + GUID_INIT(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ + 0x04, 0x4a, 0x38, 0xfc) /* * CPER specification (in UEFI specification 2.3 appendix N) requires @@ -135,7 +135,7 @@ retry: goto out; /* try to skip other type records in storage */ else if (rc != sizeof(rcd) || - uuid_le_cmp(rcd.hdr.creator_id, CPER_CREATOR_MCE)) + !guid_equal(&rcd.hdr.creator_id, &CPER_CREATOR_MCE)) goto retry; memcpy(m, &rcd.mce, sizeof(*m)); rc = sizeof(*m); From c061ce249f9bd21a7b6effa46a99563b876f76b6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 10 Jan 2019 16:16:28 -0600 Subject: [PATCH 0257/4277] dt-bindings: arm: Convert PMU binding to json-schema Convert ARM PMU binding to DT schema format using json-schema. Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Will Deacon --- Documentation/devicetree/bindings/arm/pmu.txt | 70 --------------- .../devicetree/bindings/arm/pmu.yaml | 87 +++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 88 insertions(+), 71 deletions(-) delete mode 100644 Documentation/devicetree/bindings/arm/pmu.txt create mode 100644 Documentation/devicetree/bindings/arm/pmu.yaml diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt deleted file mode 100644 index 13611a8199bb..000000000000 --- a/Documentation/devicetree/bindings/arm/pmu.txt +++ /dev/null @@ -1,70 +0,0 @@ -* ARM Performance Monitor Units - -ARM cores often have a PMU for counting cpu and cache events like cache misses -and hits. The interface to the PMU is part of the ARM ARM. The ARM PMU -representation in the device tree should be done as under:- - -Required properties: - -- compatible : should be one of - "apm,potenza-pmu" - "arm,armv8-pmuv3" - "arm,cortex-a73-pmu" - "arm,cortex-a72-pmu" - "arm,cortex-a57-pmu" - "arm,cortex-a53-pmu" - "arm,cortex-a35-pmu" - "arm,cortex-a17-pmu" - "arm,cortex-a15-pmu" - "arm,cortex-a12-pmu" - "arm,cortex-a9-pmu" - "arm,cortex-a8-pmu" - "arm,cortex-a7-pmu" - "arm,cortex-a5-pmu" - "arm,arm11mpcore-pmu" - "arm,arm1176-pmu" - "arm,arm1136-pmu" - "brcm,vulcan-pmu" - "cavium,thunder-pmu" - "qcom,scorpion-pmu" - "qcom,scorpion-mp-pmu" - "qcom,krait-pmu" -- interrupts : 1 combined interrupt or 1 per core. If the interrupt is a per-cpu - interrupt (PPI) then 1 interrupt should be specified. - -Optional properties: - -- interrupt-affinity : When using SPIs, specifies a list of phandles to CPU - nodes corresponding directly to the affinity of - the SPIs listed in the interrupts property. - - When using a PPI, specifies a list of phandles to CPU - nodes corresponding to the set of CPUs which have - a PMU of this type signalling the PPI listed in the - interrupts property, unless this is already specified - by the PPI interrupt specifier itself (in which case - the interrupt-affinity property shouldn't be present). - - This property should be present when there is more than - a single SPI. - - -- qcom,no-pc-write : Indicates that this PMU doesn't support the 0xc and 0xd - events. - -- secure-reg-access : Indicates that the ARMv7 Secure Debug Enable Register - (SDER) is accessible. This will cause the driver to do - any setup required that is only possible in ARMv7 secure - state. If not present the ARMv7 SDER will not be touched, - which means the PMU may fail to operate unless external - code (bootloader or security monitor) has performed the - appropriate initialisation. Note that this property is - not valid for non-ARMv7 CPUs or ARMv7 CPUs booting Linux - in Non-secure state. - -Example: - -pmu { - compatible = "arm,cortex-a9-pmu"; - interrupts = <100 101>; -}; diff --git a/Documentation/devicetree/bindings/arm/pmu.yaml b/Documentation/devicetree/bindings/arm/pmu.yaml new file mode 100644 index 000000000000..52ae094ce330 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/pmu.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/pmu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Performance Monitor Units + +maintainers: + - Mark Rutland + - Will Deacon + +description: |+ + ARM cores often have a PMU for counting cpu and cache events like cache misses + and hits. The interface to the PMU is part of the ARM ARM. The ARM PMU + representation in the device tree should be done as under:- + +properties: + compatible: + items: + - enum: + - apm,potenza-pmu + - arm,armv8-pmuv3 + - arm,cortex-a73-pmu + - arm,cortex-a72-pmu + - arm,cortex-a57-pmu + - arm,cortex-a53-pmu + - arm,cortex-a35-pmu + - arm,cortex-a17-pmu + - arm,cortex-a15-pmu + - arm,cortex-a12-pmu + - arm,cortex-a9-pmu + - arm,cortex-a8-pmu + - arm,cortex-a7-pmu + - arm,cortex-a5-pmu + - arm,arm11mpcore-pmu + - arm,arm1176-pmu + - arm,arm1136-pmu + - brcm,vulcan-pmu + - cavium,thunder-pmu + - qcom,scorpion-pmu + - qcom,scorpion-mp-pmu + - qcom,krait-pmu + + interrupts: + # Don't know how many CPUs, so no constraints to specify + description: 1 per-cpu interrupt (PPI) or 1 interrupt per core. + + interrupt-affinity: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + When using SPIs, specifies a list of phandles to CPU + nodes corresponding directly to the affinity of + the SPIs listed in the interrupts property. + + When using a PPI, specifies a list of phandles to CPU + nodes corresponding to the set of CPUs which have + a PMU of this type signalling the PPI listed in the + interrupts property, unless this is already specified + by the PPI interrupt specifier itself (in which case + the interrupt-affinity property shouldn't be present). + + This property should be present when there is more than + a single SPI. + + qcom,no-pc-write: + type: boolean + description: + Indicates that this PMU doesn't support the 0xc and 0xd events. + + secure-reg-access: + type: boolean + description: + Indicates that the ARMv7 Secure Debug Enable Register + (SDER) is accessible. This will cause the driver to do + any setup required that is only possible in ARMv7 secure + state. If not present the ARMv7 SDER will not be touched, + which means the PMU may fail to operate unless external + code (bootloader or security monitor) has performed the + appropriate initialisation. Note that this property is + not valid for non-ARMv7 CPUs or ARMv7 CPUs booting Linux + in Non-secure state. + +required: + - compatible + +... diff --git a/MAINTAINERS b/MAINTAINERS index 4d04cebb4a71..20339d2e3fb3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1159,7 +1159,7 @@ F: arch/arm*/include/asm/hw_breakpoint.h F: arch/arm*/include/asm/perf_event.h F: drivers/perf/* F: include/linux/perf/arm_pmu.h -F: Documentation/devicetree/bindings/arm/pmu.txt +F: Documentation/devicetree/bindings/arm/pmu.yaml F: Documentation/devicetree/bindings/perf/ ARM PORT From 7fbc258fea74d888b5ac6ba15c50ef2a05b5243b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 13 Jan 2019 19:28:58 -0800 Subject: [PATCH 0258/4277] Documentation: fix coding-style.rst Sphinx warning Fix Sphinx warning in coding-style.rst: Documentation/process/coding-style.rst:446: WARNING: Inline interpreted text or phrase reference start-string without end-string. Signed-off-by: Randy Dunlap Signed-off-by: Jonathan Corbet --- Documentation/process/coding-style.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index afa11024279a..4e611a01adc6 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -443,7 +443,7 @@ In function prototypes, include parameter names with their data types. Although this is not required by the C language, it is preferred in Linux because it is a simple way to add valuable information for the reader. -Do not use the `extern' keyword with function prototypes as this makes +Do not use the ``extern`` keyword with function prototypes as this makes lines longer and isn't strictly necessary. From 5591a3075e956af501430bdf2b3516a69f4fe65d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 13 Jan 2019 19:21:46 -0800 Subject: [PATCH 0259/4277] Documentation: add ibmvmc to toctree(index) and fix warnings Fix Sphinx warnings in ibmvmc.rst, add an index.rst file in Documentation/misc-devices/, and insert that index file into the top-level index file. Documentation/misc-devices/ibmvmc.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent. Documentation/misc-devices/ibmvmc.rst:: WARNING: document isn't included in any toctree Signed-off-by: Randy Dunlap Cc: Steven Royer Cc: Jonathan Corbet Signed-off-by: Jonathan Corbet --- Documentation/index.rst | 1 + Documentation/misc-devices/ibmvmc.rst | 1 + Documentation/misc-devices/index.rst | 17 +++++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 Documentation/misc-devices/index.rst diff --git a/Documentation/index.rst b/Documentation/index.rst index c858c2e66e36..80a421cb935e 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -90,6 +90,7 @@ needed). filesystems/index vm/index bpf/index + misc-devices/index Architecture-specific documentation ----------------------------------- diff --git a/Documentation/misc-devices/ibmvmc.rst b/Documentation/misc-devices/ibmvmc.rst index 46ded79554d4..b46df4ea2b81 100644 --- a/Documentation/misc-devices/ibmvmc.rst +++ b/Documentation/misc-devices/ibmvmc.rst @@ -1,4 +1,5 @@ .. SPDX-License-Identifier: GPL-2.0+ + ====================================================== IBM Virtual Management Channel Kernel Driver (IBMVMC) ====================================================== diff --git a/Documentation/misc-devices/index.rst b/Documentation/misc-devices/index.rst new file mode 100644 index 000000000000..dfd1f45a3127 --- /dev/null +++ b/Documentation/misc-devices/index.rst @@ -0,0 +1,17 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +Assorted Miscellaneous Devices Documentation +============================================ + +This documentation contains information for assorted devices that do not +fit into other categories. + +.. class:: toc-title + + Table of contents + +.. toctree:: + :maxdepth: 2 + + ibmvmc From ea4baf7f116a18382df331db2123d98bc1c3cd83 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Tue, 18 Dec 2018 14:28:30 +0200 Subject: [PATCH 0260/4277] RDMA: Rename port_callback to init_port Most provider routines are callback routines which ib core invokes. _callback suffix doesn't convey information about when such callback is invoked. Therefore, rename port_callback to init_port. Additionally, store the init_port function pointer in ib_device_ops, so that it can be accessed in subsequent patches when binding rdma device to net namespace. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/core_priv.h | 4 +--- drivers/infiniband/core/device.c | 7 +++---- drivers/infiniband/core/sysfs.c | 16 ++++++---------- drivers/infiniband/hw/bnxt_re/main.c | 2 +- drivers/infiniband/hw/cxgb3/iwch_provider.c | 2 +- drivers/infiniband/hw/cxgb4/provider.c | 2 +- drivers/infiniband/hw/hfi1/verbs.c | 2 +- drivers/infiniband/hw/hns/hns_roce_main.c | 2 +- drivers/infiniband/hw/i40iw/i40iw_verbs.c | 2 +- drivers/infiniband/hw/mlx4/main.c | 2 +- drivers/infiniband/hw/mlx5/main.c | 2 +- drivers/infiniband/hw/mthca/mthca_provider.c | 2 +- drivers/infiniband/hw/nes/nes_verbs.c | 2 +- drivers/infiniband/hw/ocrdma/ocrdma_main.c | 2 +- drivers/infiniband/hw/qedr/main.c | 2 +- drivers/infiniband/hw/qib/qib_verbs.c | 2 +- drivers/infiniband/hw/usnic/usnic_ib_main.c | 2 +- drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c | 2 +- drivers/infiniband/sw/rdmavt/vt.c | 5 ++--- drivers/infiniband/sw/rxe/rxe_verbs.c | 2 +- include/rdma/ib_verbs.h | 10 +++++++--- include/rdma/rdma_vt.h | 3 --- 22 files changed, 35 insertions(+), 42 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index 3cd830d52967..aca75c74e451 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -54,9 +54,7 @@ struct pkey_index_qp_list { struct list_head qp_list; }; -int ib_device_register_sysfs(struct ib_device *device, - int (*port_callback)(struct ib_device *, - u8, struct kobject *)); +int ib_device_register_sysfs(struct ib_device *device); void ib_device_unregister_sysfs(struct ib_device *device); int ib_device_rename(struct ib_device *ibdev, const char *name); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 8872453e26c0..66867e92ddea 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -574,9 +574,7 @@ port_cleanup: * callback for each device that is added. @device must be allocated * with ib_alloc_device(). */ -int ib_register_device(struct ib_device *device, const char *name, - int (*port_callback)(struct ib_device *, u8, - struct kobject *)) +int ib_register_device(struct ib_device *device, const char *name) { int ret; struct ib_client *client; @@ -613,7 +611,7 @@ int ib_register_device(struct ib_device *device, const char *name, goto dev_cleanup; } - ret = ib_device_register_sysfs(device, port_callback); + ret = ib_device_register_sysfs(device); if (ret) { dev_warn(&device->dev, "Couldn't register device with driver model\n"); @@ -1283,6 +1281,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, get_vector_affinity); SET_DEVICE_OP(dev_ops, get_vf_config); SET_DEVICE_OP(dev_ops, get_vf_stats); + SET_DEVICE_OP(dev_ops, init_port); SET_DEVICE_OP(dev_ops, map_mr_sg); SET_DEVICE_OP(dev_ops, map_phys_fmr); SET_DEVICE_OP(dev_ops, mmap); diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 80f68eb0ba5c..7a5679933df6 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -1015,9 +1015,7 @@ err_free_stats: return; } -static int add_port(struct ib_device *device, int port_num, - int (*port_callback)(struct ib_device *, - u8, struct kobject *)) +static int add_port(struct ib_device *device, int port_num) { struct ib_port *p; struct ib_port_attr attr; @@ -1113,8 +1111,8 @@ static int add_port(struct ib_device *device, int port_num, if (ret) goto err_free_pkey; - if (port_callback) { - ret = port_callback(device, port_num, &p->kobj); + if (device->ops.init_port) { + ret = device->ops.init_port(device, port_num, &p->kobj); if (ret) goto err_remove_pkey; } @@ -1308,9 +1306,7 @@ static void free_port_list_attributes(struct ib_device *device) kobject_put(device->ports_kobj); } -int ib_device_register_sysfs(struct ib_device *device, - int (*port_callback)(struct ib_device *, - u8, struct kobject *)) +int ib_device_register_sysfs(struct ib_device *device) { struct device *class_dev = &device->dev; int ret; @@ -1330,12 +1326,12 @@ int ib_device_register_sysfs(struct ib_device *device, } if (rdma_cap_ib_switch(device)) { - ret = add_port(device, 0, port_callback); + ret = add_port(device, 0); if (ret) goto err_put; } else { for (i = 1; i <= device->phys_port_cnt; ++i) { - ret = add_port(device, i, port_callback); + ret = add_port(device, i); if (ret) goto err_put; } diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index e7a997f2a537..797a3e943366 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -662,7 +662,7 @@ static int bnxt_re_register_ib(struct bnxt_re_dev *rdev) rdma_set_device_sysfs_group(ibdev, &bnxt_re_dev_attr_group); ibdev->driver_id = RDMA_DRIVER_BNXT_RE; ib_set_device_ops(ibdev, &bnxt_re_dev_ops); - return ib_register_device(ibdev, "bnxt_re%d", NULL); + return ib_register_device(ibdev, "bnxt_re%d"); } static void bnxt_re_dev_remove(struct bnxt_re_dev *rdev) diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 92ee6761a3bd..ffdde3cca268 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1409,7 +1409,7 @@ int iwch_register_device(struct iwch_dev *dev) dev->ibdev.driver_id = RDMA_DRIVER_CXGB3; rdma_set_device_sysfs_group(&dev->ibdev, &iwch_attr_group); ib_set_device_ops(&dev->ibdev, &iwch_dev_ops); - ret = ib_register_device(&dev->ibdev, "cxgb3_%d", NULL); + ret = ib_register_device(&dev->ibdev, "cxgb3_%d"); if (ret) kfree(dev->ibdev.iwcm); return ret; diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 586b0c37481f..0a99894b0160 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -634,7 +634,7 @@ void c4iw_register_device(struct work_struct *work) rdma_set_device_sysfs_group(&dev->ibdev, &c4iw_attr_group); dev->ibdev.driver_id = RDMA_DRIVER_CXGB4; ib_set_device_ops(&dev->ibdev, &c4iw_dev_ops); - ret = ib_register_device(&dev->ibdev, "cxgb4_%d", NULL); + ret = ib_register_device(&dev->ibdev, "cxgb4_%d"); if (ret) goto err_kfree_iwcm; return; diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index ec582d86025f..8957adf58af7 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -1622,6 +1622,7 @@ static const struct ib_device_ops hfi1_dev_ops = { .alloc_rdma_netdev = hfi1_vnic_alloc_rn, .get_dev_fw_str = hfi1_get_dev_fw_str, .get_hw_stats = get_hw_stats, + .init_port = hfi1_create_port_files, .modify_device = modify_device, /* keep process mad in the driver */ .process_mad = hfi1_process_mad, @@ -1679,7 +1680,6 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) /* * Fill in rvt info object. */ - dd->verbs_dev.rdi.driver_f.port_callback = hfi1_create_port_files; dd->verbs_dev.rdi.driver_f.get_pci_dev = get_pci_dev; dd->verbs_dev.rdi.driver_f.check_ah = hfi1_check_ah; dd->verbs_dev.rdi.driver_f.notify_new_ah = hfi1_notify_new_ah; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 44a07fce0617..46ede58ef3b8 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -564,7 +564,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev) ib_dev->driver_id = RDMA_DRIVER_HNS; ib_set_device_ops(ib_dev, hr_dev->hw->hns_roce_dev_ops); ib_set_device_ops(ib_dev, &hns_roce_dev_ops); - ret = ib_register_device(ib_dev, "hns_%d", NULL); + ret = ib_register_device(ib_dev, "hns_%d"); if (ret) { dev_err(dev, "ib_register_device failed!\n"); return ret; diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 80b66df95362..af66ab9d150b 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -2868,7 +2868,7 @@ int i40iw_register_rdma_device(struct i40iw_device *iwdev) iwibdev = iwdev->iwibdev; rdma_set_device_sysfs_group(&iwibdev->ibdev, &i40iw_attr_group); iwibdev->ibdev.driver_id = RDMA_DRIVER_I40IW; - ret = ib_register_device(&iwibdev->ibdev, "i40iw%d", NULL); + ret = ib_register_device(&iwibdev->ibdev, "i40iw%d"); if (ret) goto error; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 1f15ec3e2b83..c3f950d82ed0 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2856,7 +2856,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) rdma_set_device_sysfs_group(&ibdev->ib_dev, &mlx4_attr_group); ibdev->ib_dev.driver_id = RDMA_DRIVER_MLX4; - if (ib_register_device(&ibdev->ib_dev, "mlx4_%d", NULL)) + if (ib_register_device(&ibdev->ib_dev, "mlx4_%d")) goto err_diag_counters; if (mlx4_ib_mad_init(ibdev)) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 717e8535b41a..948617a60d44 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -6233,7 +6233,7 @@ int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev) name = "mlx5_%d"; else name = "mlx5_bond_%d"; - return ib_register_device(&dev->ib_dev, name, NULL); + return ib_register_device(&dev->ib_dev, name); } void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev) diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 0a939ea5497b..3473c6c51b92 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -1338,7 +1338,7 @@ int mthca_register_device(struct mthca_dev *dev) rdma_set_device_sysfs_group(&dev->ib_dev, &mthca_attr_group); dev->ib_dev.driver_id = RDMA_DRIVER_MTHCA; - ret = ib_register_device(&dev->ib_dev, "mthca%d", NULL); + ret = ib_register_device(&dev->ib_dev, "mthca%d"); if (ret) return ret; diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index feb4d259aab9..34601f0cbd74 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -3801,7 +3801,7 @@ int nes_register_ofa_device(struct nes_ib_device *nesibdev) rdma_set_device_sysfs_group(&nesvnic->nesibdev->ibdev, &nes_attr_group); nesvnic->nesibdev->ibdev.driver_id = RDMA_DRIVER_NES; - ret = ib_register_device(&nesvnic->nesibdev->ibdev, "nes%d", NULL); + ret = ib_register_device(&nesvnic->nesibdev->ibdev, "nes%d"); if (ret) { return ret; } diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index 1f393842453a..f45b996f617f 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -243,7 +243,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev) } rdma_set_device_sysfs_group(&dev->ibdev, &ocrdma_attr_group); dev->ibdev.driver_id = RDMA_DRIVER_OCRDMA; - return ib_register_device(&dev->ibdev, "ocrdma%d", NULL); + return ib_register_device(&dev->ibdev, "ocrdma%d"); } static int ocrdma_alloc_resources(struct ocrdma_dev *dev) diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index 75940e2a8791..8e5c76d06855 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -290,7 +290,7 @@ static int qedr_register_device(struct qedr_dev *dev) ib_set_device_ops(&dev->ibdev, &qedr_dev_ops); dev->ibdev.driver_id = RDMA_DRIVER_QEDR; - return ib_register_device(&dev->ibdev, "qedr%d", NULL); + return ib_register_device(&dev->ibdev, "qedr%d"); } /* This function allocates fast-path status block memory */ diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c index 276304f611ab..ff8dab8e2344 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.c +++ b/drivers/infiniband/hw/qib/qib_verbs.c @@ -1494,6 +1494,7 @@ static void qib_fill_device_attr(struct qib_devdata *dd) } static const struct ib_device_ops qib_dev_ops = { + .init_port = qib_create_port_files, .modify_device = qib_modify_device, .process_mad = qib_process_mad, }; @@ -1567,7 +1568,6 @@ int qib_register_ib_device(struct qib_devdata *dd) /* * Fill in rvt info object. */ - dd->verbs_dev.rdi.driver_f.port_callback = qib_create_port_files; dd->verbs_dev.rdi.driver_f.get_pci_dev = qib_get_pci_dev; dd->verbs_dev.rdi.driver_f.check_ah = qib_check_ah; dd->verbs_dev.rdi.driver_f.setup_wqe = qib_check_send_wqe; diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index b2323a52a0dd..3201dd1899c7 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -422,7 +422,7 @@ static void *usnic_ib_device_add(struct pci_dev *dev) us_ibdev->ib_dev.driver_id = RDMA_DRIVER_USNIC; rdma_set_device_sysfs_group(&us_ibdev->ib_dev, &usnic_attr_group); - if (ib_register_device(&us_ibdev->ib_dev, "usnic_%d", NULL)) + if (ib_register_device(&us_ibdev->ib_dev, "usnic_%d")) goto err_fwd_dealloc; usnic_fwd_set_mtu(us_ibdev->ufdev, us_ibdev->netdev->mtu); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index eaa109dbc96a..1bc415483d9b 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -278,7 +278,7 @@ static int pvrdma_register_device(struct pvrdma_dev *dev) spin_lock_init(&dev->srq_tbl_lock); rdma_set_device_sysfs_group(&dev->ib_dev, &pvrdma_attr_group); - ret = ib_register_device(&dev->ib_dev, "vmw_pvrdma%d", NULL); + ret = ib_register_device(&dev->ib_dev, "vmw_pvrdma%d"); if (ret) goto err_srq_free; diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index 8724a1817158..7de7389d0235 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -446,7 +446,7 @@ static noinline int check_support(struct rvt_dev_info *rdi, int verb) * These functions are not part of verbs specifically but are * required for rdmavt to function. */ - if ((!rdi->driver_f.port_callback) || + if ((!rdi->ibdev.ops.init_port) || (!rdi->driver_f.get_pci_dev)) return -EINVAL; break; @@ -644,8 +644,7 @@ int rvt_register_device(struct rvt_dev_info *rdi, u32 driver_id) rdi->ibdev.driver_id = driver_id; /* We are now good to announce we exist */ - ret = ib_register_device(&rdi->ibdev, dev_name(&rdi->ibdev.dev), - rdi->driver_f.port_callback); + ret = ib_register_device(&rdi->ibdev, dev_name(&rdi->ibdev.dev)); if (ret) { rvt_pr_err(rdi, "Failed to register driver with ib core.\n"); goto bail_wss; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index b20e6e0415f5..43171148e9c5 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1258,7 +1258,7 @@ int rxe_register_device(struct rxe_dev *rxe) rdma_set_device_sysfs_group(dev, &rxe_attr_group); dev->driver_id = RDMA_DRIVER_RXE; - err = ib_register_device(dev, "rxe%d", NULL); + err = ib_register_device(dev, "rxe%d"); if (err) { pr_warn("%s failed with error %d\n", __func__, err); goto err1; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index aa1f126d3383..1d1902fd9f87 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2504,6 +2504,12 @@ struct ib_device_ops { */ int (*get_hw_stats)(struct ib_device *device, struct rdma_hw_stats *stats, u8 port, int index); + /* + * This function is called once for each port when a ib device is + * registered. + */ + int (*init_port)(struct ib_device *device, u8 port_num, + struct kobject *port_sysfs); }; struct ib_device { @@ -2620,9 +2626,7 @@ void ib_dealloc_device(struct ib_device *device); void ib_get_device_fw_str(struct ib_device *device, char *str); -int ib_register_device(struct ib_device *device, const char *name, - int (*port_callback)(struct ib_device *, u8, - struct kobject *)); +int ib_register_device(struct ib_device *device, const char *name); void ib_unregister_device(struct ib_device *device); int ib_register_client (struct ib_client *client); diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index dd0ed8048bb4..acb3bc96dfa7 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -250,9 +250,6 @@ struct rvt_driver_provided { */ void (*do_send)(struct rvt_qp *qp); - /* Passed to ib core registration. Callback to create syfs files */ - int (*port_callback)(struct ib_device *, u8, struct kobject *); - /* * Returns a pointer to the undelying hardware's PCI device. This is * used to display information as to what hardware is being referenced From 54747231150f0dddf68f2ee29ec2970fcc433909 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Tue, 18 Dec 2018 14:15:56 +0200 Subject: [PATCH 0261/4277] RDMA: Introduce and use rdma_device_to_ibdev() Introduce and use rdma_device_to_ibdev() API for those drivers which are registering one sysfs group and also use in ib_core. In subsequent patch, device->provider_ibdev one-to-one mapping is no longer holds true during accessing sysfs entries. Therefore, introduce an API rdma_device_to_ibdev() that provides such information. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 2 -- drivers/infiniband/core/sysfs.c | 12 ++++----- drivers/infiniband/hw/bnxt_re/main.c | 6 +++-- drivers/infiniband/hw/cxgb3/iwch_provider.c | 14 ++++++----- drivers/infiniband/hw/cxgb4/provider.c | 14 ++++++----- drivers/infiniband/hw/hfi1/sysfs.c | 16 ++++++------ drivers/infiniband/hw/i40iw/i40iw_verbs.c | 5 ++-- drivers/infiniband/hw/mlx4/main.c | 7 +++--- drivers/infiniband/hw/mlx5/main.c | 13 ++++++---- drivers/infiniband/hw/mthca/mthca_provider.c | 9 ++++--- drivers/infiniband/hw/nes/nes_verbs.c | 2 +- drivers/infiniband/hw/ocrdma/ocrdma_main.c | 6 +++-- drivers/infiniband/hw/qedr/main.c | 3 ++- drivers/infiniband/hw/qib/qib_sysfs.c | 18 +++++++------- drivers/infiniband/hw/usnic/usnic_ib_sysfs.c | 26 +++++++++----------- drivers/infiniband/sw/rxe/rxe_verbs.c | 4 +-- include/rdma/ib_verbs.h | 23 +++++++++++++++++ 17 files changed, 106 insertions(+), 74 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 66867e92ddea..f8180cf1a004 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -296,8 +296,6 @@ struct ib_device *ib_alloc_device(size_t size) device->dev.class = &ib_class; device_initialize(&device->dev); - dev_set_drvdata(&device->dev, device); - INIT_LIST_HEAD(&device->event_handler_list); spin_lock_init(&device->event_handler_lock); rwlock_init(&device->client_data_lock); diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 7a5679933df6..c75692802da8 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -1187,7 +1187,7 @@ err_put: static ssize_t node_type_show(struct device *device, struct device_attribute *attr, char *buf) { - struct ib_device *dev = container_of(device, struct ib_device, dev); + struct ib_device *dev = rdma_device_to_ibdev(device); switch (dev->node_type) { case RDMA_NODE_IB_CA: return sprintf(buf, "%d: CA\n", dev->node_type); @@ -1204,7 +1204,7 @@ static DEVICE_ATTR_RO(node_type); static ssize_t sys_image_guid_show(struct device *device, struct device_attribute *dev_attr, char *buf) { - struct ib_device *dev = container_of(device, struct ib_device, dev); + struct ib_device *dev = rdma_device_to_ibdev(device); return sprintf(buf, "%04x:%04x:%04x:%04x\n", be16_to_cpu(((__be16 *) &dev->attrs.sys_image_guid)[0]), @@ -1217,7 +1217,7 @@ static DEVICE_ATTR_RO(sys_image_guid); static ssize_t node_guid_show(struct device *device, struct device_attribute *attr, char *buf) { - struct ib_device *dev = container_of(device, struct ib_device, dev); + struct ib_device *dev = rdma_device_to_ibdev(device); return sprintf(buf, "%04x:%04x:%04x:%04x\n", be16_to_cpu(((__be16 *) &dev->node_guid)[0]), @@ -1230,7 +1230,7 @@ static DEVICE_ATTR_RO(node_guid); static ssize_t node_desc_show(struct device *device, struct device_attribute *attr, char *buf) { - struct ib_device *dev = container_of(device, struct ib_device, dev); + struct ib_device *dev = rdma_device_to_ibdev(device); return sprintf(buf, "%.64s\n", dev->node_desc); } @@ -1239,7 +1239,7 @@ static ssize_t node_desc_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { - struct ib_device *dev = container_of(device, struct ib_device, dev); + struct ib_device *dev = rdma_device_to_ibdev(device); struct ib_device_modify desc = {}; int ret; @@ -1258,7 +1258,7 @@ static DEVICE_ATTR_RW(node_desc); static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr, char *buf) { - struct ib_device *dev = container_of(device, struct ib_device, dev); + struct ib_device *dev = rdma_device_to_ibdev(device); ib_get_device_fw_str(dev, buf); strlcat(buf, "\n", IB_FW_VERSION_NAME_MAX); diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 797a3e943366..16eecfa5882c 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -538,7 +538,8 @@ static struct bnxt_en_dev *bnxt_re_dev_probe(struct net_device *netdev) static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { - struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev); + struct bnxt_re_dev *rdev = + rdma_device_to_drv_device(device, struct bnxt_re_dev, ibdev); return scnprintf(buf, PAGE_SIZE, "0x%x\n", rdev->en_dev->pdev->vendor); } @@ -547,7 +548,8 @@ static DEVICE_ATTR_RO(hw_rev); static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf) { - struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev); + struct bnxt_re_dev *rdev = + rdma_device_to_drv_device(device, struct bnxt_re_dev, ibdev); return scnprintf(buf, PAGE_SIZE, "%s\n", rdev->ibdev.node_desc); } diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index ffdde3cca268..07c20cd07f33 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1130,8 +1130,9 @@ static int iwch_query_port(struct ib_device *ibdev, static ssize_t hw_rev_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct iwch_dev *iwch_dev = container_of(dev, struct iwch_dev, - ibdev.dev); + struct iwch_dev *iwch_dev = + rdma_device_to_drv_device(dev, struct iwch_dev, ibdev); + pr_debug("%s dev 0x%p\n", __func__, dev); return sprintf(buf, "%d\n", iwch_dev->rdev.t3cdev_p->type); } @@ -1140,8 +1141,8 @@ static DEVICE_ATTR_RO(hw_rev); static ssize_t hca_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct iwch_dev *iwch_dev = container_of(dev, struct iwch_dev, - ibdev.dev); + struct iwch_dev *iwch_dev = + rdma_device_to_drv_device(dev, struct iwch_dev, ibdev); struct ethtool_drvinfo info; struct net_device *lldev = iwch_dev->rdev.t3cdev_p->lldev; @@ -1154,8 +1155,9 @@ static DEVICE_ATTR_RO(hca_type); static ssize_t board_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct iwch_dev *iwch_dev = container_of(dev, struct iwch_dev, - ibdev.dev); + struct iwch_dev *iwch_dev = + rdma_device_to_drv_device(dev, struct iwch_dev, ibdev); + pr_debug("%s dev 0x%p\n", __func__, dev); return sprintf(buf, "%x.%x\n", iwch_dev->rdev.rnic_info.pdev->vendor, iwch_dev->rdev.rnic_info.pdev->device); diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 0a99894b0160..f977f8e7e162 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -376,8 +376,9 @@ static int c4iw_query_port(struct ib_device *ibdev, u8 port, static ssize_t hw_rev_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, - ibdev.dev); + struct c4iw_dev *c4iw_dev = + rdma_device_to_drv_device(dev, struct c4iw_dev, ibdev); + pr_debug("dev 0x%p\n", dev); return sprintf(buf, "%d\n", CHELSIO_CHIP_RELEASE(c4iw_dev->rdev.lldi.adapter_type)); @@ -387,8 +388,8 @@ static DEVICE_ATTR_RO(hw_rev); static ssize_t hca_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, - ibdev.dev); + struct c4iw_dev *c4iw_dev = + rdma_device_to_drv_device(dev, struct c4iw_dev, ibdev); struct ethtool_drvinfo info; struct net_device *lldev = c4iw_dev->rdev.lldi.ports[0]; @@ -401,8 +402,9 @@ static DEVICE_ATTR_RO(hca_type); static ssize_t board_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, - ibdev.dev); + struct c4iw_dev *c4iw_dev = + rdma_device_to_drv_device(dev, struct c4iw_dev, ibdev); + pr_debug("dev 0x%p\n", dev); return sprintf(buf, "%x.%x\n", c4iw_dev->rdev.lldi.pdev->vendor, c4iw_dev->rdev.lldi.pdev->device); diff --git a/drivers/infiniband/hw/hfi1/sysfs.c b/drivers/infiniband/hw/hfi1/sysfs.c index 2be513d4c9da..90f62c4bddba 100644 --- a/drivers/infiniband/hw/hfi1/sysfs.c +++ b/drivers/infiniband/hw/hfi1/sysfs.c @@ -498,7 +498,7 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); return sprintf(buf, "%x\n", dd_from_dev(dev)->minrev); } @@ -508,7 +508,7 @@ static ssize_t board_id_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); int ret; @@ -524,7 +524,7 @@ static ssize_t boardversion_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); /* The string printed here is already newline-terminated. */ @@ -536,7 +536,7 @@ static ssize_t nctxts_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); /* @@ -555,7 +555,7 @@ static ssize_t nfreectxts_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); /* Return the number of free user ports (contexts) available. */ @@ -567,7 +567,7 @@ static ssize_t serial_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); return scnprintf(buf, PAGE_SIZE, "%s", dd->serial); @@ -579,7 +579,7 @@ static ssize_t chip_reset_store(struct device *device, size_t count) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); int ret; @@ -609,7 +609,7 @@ static ssize_t tempsense_show(struct device *device, struct device_attribute *attr, char *buf) { struct hfi1_ibdev *dev = - container_of(device, struct hfi1_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct hfi1_ibdev, rdi.ibdev); struct hfi1_devdata *dd = dd_from_dev(dev); struct hfi1_temp temp; int ret; diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index af66ab9d150b..12b31a8440be 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -2139,9 +2139,8 @@ static int i40iw_dereg_mr(struct ib_mr *ib_mr) static ssize_t hw_rev_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct i40iw_ib_device *iwibdev = container_of(dev, - struct i40iw_ib_device, - ibdev.dev); + struct i40iw_ib_device *iwibdev = + rdma_device_to_drv_device(dev, struct i40iw_ib_device, ibdev); u32 hw_rev = iwibdev->iwdev->sc_dev.hw_rev; return sprintf(buf, "%x\n", hw_rev); diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index c3f950d82ed0..dc2ffd293a11 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2043,7 +2043,7 @@ static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx4_ib_dev *dev = - container_of(device, struct mlx4_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx4_ib_dev, ib_dev); return sprintf(buf, "MT%d\n", dev->dev->persist->pdev->device); } static DEVICE_ATTR_RO(hca_type); @@ -2052,7 +2052,7 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx4_ib_dev *dev = - container_of(device, struct mlx4_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx4_ib_dev, ib_dev); return sprintf(buf, "%x\n", dev->dev->rev_id); } static DEVICE_ATTR_RO(hw_rev); @@ -2061,7 +2061,8 @@ static ssize_t board_id_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx4_ib_dev *dev = - container_of(device, struct mlx4_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx4_ib_dev, ib_dev); + return sprintf(buf, "%.*s\n", MLX4_BOARD_ID_LEN, dev->dev->board_id); } diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 948617a60d44..4b1b56d54301 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4104,7 +4104,7 @@ static ssize_t fw_pages_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx5_ib_dev *dev = - container_of(device, struct mlx5_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev); return sprintf(buf, "%d\n", dev->mdev->priv.fw_pages); } @@ -4114,7 +4114,7 @@ static ssize_t reg_pages_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx5_ib_dev *dev = - container_of(device, struct mlx5_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev); return sprintf(buf, "%d\n", atomic_read(&dev->mdev->priv.reg_pages)); } @@ -4124,7 +4124,8 @@ static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx5_ib_dev *dev = - container_of(device, struct mlx5_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev); + return sprintf(buf, "MT%d\n", dev->mdev->pdev->device); } static DEVICE_ATTR_RO(hca_type); @@ -4133,7 +4134,8 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx5_ib_dev *dev = - container_of(device, struct mlx5_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev); + return sprintf(buf, "%x\n", dev->mdev->rev_id); } static DEVICE_ATTR_RO(hw_rev); @@ -4142,7 +4144,8 @@ static ssize_t board_id_show(struct device *device, struct device_attribute *attr, char *buf) { struct mlx5_ib_dev *dev = - container_of(device, struct mlx5_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mlx5_ib_dev, ib_dev); + return sprintf(buf, "%.*s\n", MLX5_BOARD_ID_LEN, dev->mdev->board_id); } diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 3473c6c51b92..63003b4d2485 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -1081,7 +1081,8 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { struct mthca_dev *dev = - container_of(device, struct mthca_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mthca_dev, ib_dev); + return sprintf(buf, "%x\n", dev->rev_id); } static DEVICE_ATTR_RO(hw_rev); @@ -1090,7 +1091,8 @@ static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf) { struct mthca_dev *dev = - container_of(device, struct mthca_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mthca_dev, ib_dev); + switch (dev->pdev->device) { case PCI_DEVICE_ID_MELLANOX_TAVOR: return sprintf(buf, "MT23108\n"); @@ -1111,7 +1113,8 @@ static ssize_t board_id_show(struct device *device, struct device_attribute *attr, char *buf) { struct mthca_dev *dev = - container_of(device, struct mthca_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct mthca_dev, ib_dev); + return sprintf(buf, "%.*s\n", MTHCA_BOARD_ID_LEN, dev->board_id); } static DEVICE_ATTR_RO(board_id); diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 34601f0cbd74..034156f7e9ed 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -2560,7 +2560,7 @@ static ssize_t hw_rev_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nes_ib_device *nesibdev = - container_of(dev, struct nes_ib_device, ibdev.dev); + rdma_device_to_drv_device(dev, struct nes_ib_device, ibdev); struct nes_vnic *nesvnic = nesibdev->nesvnic; nes_debug(NES_DBG_INIT, "\n"); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index f45b996f617f..b0491b9ecfe4 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -118,7 +118,8 @@ static void get_dev_fw_str(struct ib_device *device, char *str) static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { - struct ocrdma_dev *dev = dev_get_drvdata(device); + struct ocrdma_dev *dev = + rdma_device_to_drv_device(device, struct ocrdma_dev, ibdev); return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->nic_info.pdev->vendor); } @@ -127,7 +128,8 @@ static DEVICE_ATTR_RO(hw_rev); static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf) { - struct ocrdma_dev *dev = dev_get_drvdata(device); + struct ocrdma_dev *dev = + rdma_device_to_drv_device(device, struct ocrdma_dev, ibdev); return scnprintf(buf, PAGE_SIZE, "%s\n", &dev->model_number[0]); } diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index 8e5c76d06855..f85e72b65a10 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -137,7 +137,8 @@ static int qedr_iw_port_immutable(struct ib_device *ibdev, u8 port_num, static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { - struct qedr_dev *dev = dev_get_drvdata(device); + struct qedr_dev *dev = + rdma_device_to_drv_device(device, struct qedr_dev, ibdev); return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->pdev->vendor); } diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c index 1cf4ca3f23e3..905206a0c2d5 100644 --- a/drivers/infiniband/hw/qib/qib_sysfs.c +++ b/drivers/infiniband/hw/qib/qib_sysfs.c @@ -555,7 +555,7 @@ static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); return sprintf(buf, "%x\n", dd_from_dev(dev)->minrev); } @@ -565,7 +565,7 @@ static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); int ret; @@ -590,7 +590,7 @@ static ssize_t boardversion_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); /* The string printed here is already newline-terminated. */ @@ -602,7 +602,7 @@ static ssize_t localbus_info_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); /* The string printed here is already newline-terminated. */ @@ -614,7 +614,7 @@ static ssize_t nctxts_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); /* Return the number of user ports (contexts) available. */ @@ -630,7 +630,7 @@ static ssize_t nfreectxts_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); /* Return the number of free user ports (contexts) available. */ @@ -642,7 +642,7 @@ static ssize_t serial_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); buf[sizeof(dd->serial)] = '\0'; @@ -657,7 +657,7 @@ static ssize_t chip_reset_store(struct device *device, size_t count) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); int ret; @@ -679,7 +679,7 @@ static ssize_t tempsense_show(struct device *device, struct device_attribute *attr, char *buf) { struct qib_ibdev *dev = - container_of(device, struct qib_ibdev, rdi.ibdev.dev); + rdma_device_to_drv_device(device, struct qib_ibdev, rdi.ibdev); struct qib_devdata *dd = dd_from_dev(dev); int ret; int idx; diff --git a/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c b/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c index a7e4b2ccfaf8..c85d48ae7442 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_sysfs.c @@ -50,7 +50,7 @@ static ssize_t board_id_show(struct device *device, struct device_attribute *attr, char *buf) { struct usnic_ib_dev *us_ibdev = - container_of(device, struct usnic_ib_dev, ib_dev.dev); + rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev); unsigned short subsystem_device_id; mutex_lock(&us_ibdev->usdev_lock); @@ -67,14 +67,13 @@ static DEVICE_ATTR_RO(board_id); static ssize_t config_show(struct device *device, struct device_attribute *attr, char *buf) { - struct usnic_ib_dev *us_ibdev; + struct usnic_ib_dev *us_ibdev = + rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev); char *ptr; unsigned left; unsigned n; enum usnic_vnic_res_type res_type; - us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); - /* Buffer space limit is 1 page */ ptr = buf; left = PAGE_SIZE; @@ -130,9 +129,8 @@ static DEVICE_ATTR_RO(config); static ssize_t iface_show(struct device *device, struct device_attribute *attr, char *buf) { - struct usnic_ib_dev *us_ibdev; - - us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); + struct usnic_ib_dev *us_ibdev = + rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev); return scnprintf(buf, PAGE_SIZE, "%s\n", netdev_name(us_ibdev->netdev)); @@ -142,9 +140,8 @@ static DEVICE_ATTR_RO(iface); static ssize_t max_vf_show(struct device *device, struct device_attribute *attr, char *buf) { - struct usnic_ib_dev *us_ibdev; - - us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); + struct usnic_ib_dev *us_ibdev = + rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev); return scnprintf(buf, PAGE_SIZE, "%u\n", kref_read(&us_ibdev->vf_cnt)); @@ -154,10 +151,10 @@ static DEVICE_ATTR_RO(max_vf); static ssize_t qp_per_vf_show(struct device *device, struct device_attribute *attr, char *buf) { - struct usnic_ib_dev *us_ibdev; + struct usnic_ib_dev *us_ibdev = + rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev); int qp_per_vf; - us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); qp_per_vf = max(us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_WQ], us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_RQ]); @@ -169,9 +166,8 @@ static DEVICE_ATTR_RO(qp_per_vf); static ssize_t cq_per_vf_show(struct device *device, struct device_attribute *attr, char *buf) { - struct usnic_ib_dev *us_ibdev; - - us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); + struct usnic_ib_dev *us_ibdev = + rdma_device_to_drv_device(device, struct usnic_ib_dev, ib_dev); return scnprintf(buf, PAGE_SIZE, "%d\n", us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_CQ]); diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 43171148e9c5..3d01247a28db 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1129,8 +1129,8 @@ static int rxe_detach_mcast(struct ib_qp *ibqp, union ib_gid *mgid, u16 mlid) static ssize_t parent_show(struct device *device, struct device_attribute *attr, char *buf) { - struct rxe_dev *rxe = container_of(device, struct rxe_dev, - ib_dev.dev); + struct rxe_dev *rxe = + rdma_device_to_drv_device(device, struct rxe_dev, ib_dev); return snprintf(buf, 16, "%s\n", rxe_parent_name(rxe, 1)); } diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 1d1902fd9f87..94b6e1dd4dab 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -4241,4 +4241,27 @@ rdma_set_device_sysfs_group(struct ib_device *dev, dev->groups[1] = group; } +/** + * rdma_device_to_ibdev - Get ib_device pointer from device pointer + * + * @device: device pointer for which ib_device pointer to retrieve + * + * rdma_device_to_ibdev() retrieves ib_device pointer from device. + * + */ +static inline struct ib_device *rdma_device_to_ibdev(struct device *device) +{ + return container_of(device, struct ib_device, dev); +} + +/** + * rdma_device_to_drv_device - Helper macro to reach back to driver's + * ib_device holder structure from device pointer. + * + * NOTE: New drivers should not make use of this API; This API is only for + * existing drivers who have exposed sysfs entries using + * rdma_set_device_sysfs_group(). + */ +#define rdma_device_to_drv_device(dev, drv_dev_struct, ibdev_member) \ + container_of(rdma_device_to_ibdev(dev), drv_dev_struct, ibdev_member) #endif /* IB_VERBS_H */ From 97099cc6529cdf50af32a496b588d9428c57341f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Jan 2019 16:00:19 +0300 Subject: [PATCH 0262/4277] RDMA/bnxt_re: fix a size calculation This is from static analysis not from testing. Depending on the value of rcfw->cmdq_depth, then this might not cause an issue at runtime. The BITS_TO_LONGS() macro tells us how many longs it take to hold a bitmap. In other words, it divides by the number if bits per long and rounds up. Then we want to take that number and multiple by sizeof(long) to get the number of bytes to allocate. The code here does the multiplication first so the rounding up is done in the wrong place. So imagine we want to allocate 1 bit, then "(1 * 8) / 64 = 1" when we round up. But it should be "(1 / 64) * 8 = 8". In other words, because of the rounding difference we might allocate up to "sizeof(long) - 1" bytes fewer than intended. Fixes: 1ac5a4047975 ("RDMA/bnxt_re: Add bnxt_re RoCE driver") Signed-off-by: Dan Carpenter Acked-By: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 326805461265..742346ea5b0d 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -684,8 +684,7 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, /* General */ rcfw->seq_num = 0; set_bit(FIRMWARE_FIRST_FLAG, &rcfw->flags); - bmap_size = BITS_TO_LONGS(rcfw->cmdq_depth * - sizeof(unsigned long)); + bmap_size = BITS_TO_LONGS(rcfw->cmdq_depth) * sizeof(unsigned long); rcfw->cmdq_bitmap = kzalloc(bmap_size, GFP_KERNEL); if (!rcfw->cmdq_bitmap) return -ENOMEM; From 5fc01fb846bce8fa6d5f95e2625b8ce0f8e86810 Mon Sep 17 00:00:00 2001 From: Myungho Jung Date: Wed, 9 Jan 2019 22:27:31 -0800 Subject: [PATCH 0263/4277] RDMA/cma: Rollback source IP address if failing to acquire device If cma_acquire_dev_by_src_ip() returns error in addr_handler(), the device state changes back to RDMA_CM_ADDR_BOUND but the resolved source IP address is still left. After that, if rdma_destroy_id() is called after rdma_listen(), the device is freed without removed from listen_any_list in cma_cancel_operation(). Revert to the previous IP address if acquiring device fails. Reported-by: syzbot+f3ce716af730c8f96637@syzkaller.appspotmail.com Signed-off-by: Myungho Jung Reviewed-by: Parav Pandit Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 63a7cc00bae0..8cd113b0ddfb 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2963,13 +2963,22 @@ static void addr_handler(int status, struct sockaddr *src_addr, { struct rdma_id_private *id_priv = context; struct rdma_cm_event event = {}; + struct sockaddr *addr; + struct sockaddr_storage old_addr; mutex_lock(&id_priv->handler_mutex); if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_QUERY, RDMA_CM_ADDR_RESOLVED)) goto out; - memcpy(cma_src_addr(id_priv), src_addr, rdma_addr_size(src_addr)); + /* + * Store the previous src address, so that if we fail to acquire + * matching rdma device, old address can be restored back, which helps + * to cancel the cma listen operation correctly. + */ + addr = cma_src_addr(id_priv); + memcpy(&old_addr, addr, rdma_addr_size(addr)); + memcpy(addr, src_addr, rdma_addr_size(src_addr)); if (!status && !id_priv->cma_dev) { status = cma_acquire_dev_by_src_ip(id_priv); if (status) @@ -2980,6 +2989,8 @@ static void addr_handler(int status, struct sockaddr *src_addr, } if (status) { + memcpy(addr, &old_addr, + rdma_addr_size((struct sockaddr *)&old_addr)); if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED, RDMA_CM_ADDR_BOUND)) goto out; From 1d09d57728fedd7357ee22d407ae8aae6477757f Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:41:36 +0100 Subject: [PATCH 0264/4277] PCI: Mark expected switch fall-through There is a plan to build the kernel with -Wimplicit-fallthrough and these places in the code produced warnings (W=1). Fix them up. Signed-off-by: Mathieu Malaterre [bhelgaas: squash into one patch, drop extra changelog detail] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- drivers/pci/quirks.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..94069fc35e91 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -861,7 +861,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) need_restore = true; - /* Fall-through: force to D0 */ + /* Fall-through - force to D0 */ default: pmcsr = 0; break; @@ -2260,7 +2260,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup) case PCI_D2: if (pci_no_d1d2(dev)) break; - /* else: fall through */ + /* else, fall through */ default: target_state = state; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b0a413f3f7ca..9fabcef345ed 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2138,7 +2138,7 @@ static void quirk_netmos(struct pci_dev *dev) if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return; - /* else: fall through */ + /* else, fall through */ case PCI_DEVICE_ID_NETMOS_9735: case PCI_DEVICE_ID_NETMOS_9745: case PCI_DEVICE_ID_NETMOS_9845: From 25bd879ec16ad3b83a5b1c3f16faa55e696bfccb Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 7 Jan 2019 16:09:40 +0300 Subject: [PATCH 0265/4277] PCI: pciehp: Assign ctrl->slot_ctrl before writing it to hardware Shameerali reported that running v4.20-rc1 as QEMU guest, the PCIe hotplug port times out during boot: pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x03f1 (issued 1016 msec ago) pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x03f1 (issued 1024 msec ago) pciehp 0000:00:01.0:pcie004: Failed to check link status pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x02f1 (issued 2520 msec ago) The issue was bisected down to commit 720d6a671a6e ("PCI: pciehp: Do not handle events if interrupts are masked") and was further analyzed by the reporter to be caused by the fact that pciehp first updates the hardware and only then cache the ctrl->slot_ctrl in pcie_do_write_cmd(). If the interrupt happens before we cache the value, pciehp_isr() reads value 0 and decides that the interrupt was not meant for it causing the above timeout to trigger. Fix by moving ctrl->slot_ctrl assignment to happen before it is written to the hardware. Fixes: 720d6a671a6e ("PCI: pciehp: Do not handle events if interrupts are masked") Link: https://lore.kernel.org/linux-pci/5FC3163CFD30C246ABAA99954A238FA8387DD344@FRAEML521-MBX.china.huawei.com Reported-by: Shameerali Kolothum Thodi Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7dd443aea5a5..cd9eae650aa5 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -156,9 +156,9 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); + ctrl->slot_ctrl = slot_ctrl; pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); ctrl->cmd_started = jiffies; - ctrl->slot_ctrl = slot_ctrl; /* * Controllers with the Intel CF118 and similar errata advertise From 35283f56626cbc6d5b2dcb42f6d2189ce34fee3d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 11 Jan 2019 14:40:59 +0100 Subject: [PATCH 0266/4277] Documentation/filesystems: add binderfs This documents the Android binderfs filesystem used to dynamically add and remove binder devices that are private to each instance. Signed-off-by: Christian Brauner [jc: tweaked markup and added to filesystems/index.rst] Signed-off-by: Jonathan Corbet --- Documentation/filesystems/binderfs.rst | 68 ++++++++++++++++++++++++++ Documentation/filesystems/index.rst | 7 +++ 2 files changed, 75 insertions(+) create mode 100644 Documentation/filesystems/binderfs.rst diff --git a/Documentation/filesystems/binderfs.rst b/Documentation/filesystems/binderfs.rst new file mode 100644 index 000000000000..c009671f8434 --- /dev/null +++ b/Documentation/filesystems/binderfs.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0 + +The Android binderfs Filesystem +=============================== + +Android binderfs is a filesystem for the Android binder IPC mechanism. It +allows to dynamically add and remove binder devices at runtime. Binder devices +located in a new binderfs instance are independent of binder devices located in +other binderfs instances. Mounting a new binderfs instance makes it possible +to get a set of private binder devices. + +Mounting binderfs +----------------- + +Android binderfs can be mounted with:: + + mkdir /dev/binderfs + mount -t binder binder /dev/binderfs + +at which point a new instance of binderfs will show up at ``/dev/binderfs``. +In a fresh instance of binderfs no binder devices will be present. There will +only be a ``binder-control`` device which serves as the request handler for +binderfs. Mounting another binderfs instance at a different location will +create a new and separate instance from all other binderfs mounts. This is +identical to the behavior of e.g. ``devpts`` and ``tmpfs``. The Android +binderfs filesystem can be mounted in user namespaces. + +Options +------- +max + binderfs instances can be mounted with a limit on the number of binder + devices that can be allocated. The ``max=`` mount option serves as + a per-instance limit. If ``max=`` is set then only ```` number + of binder devices can be allocated in this binderfs instance. + +Allocating binder Devices +------------------------- + +.. _ioctl: http://man7.org/linux/man-pages/man2/ioctl.2.html + +To allocate a new binder device in a binderfs instance a request needs to be +sent through the ``binder-control`` device node. A request is sent in the form +of an `ioctl() `_. + +What a program needs to do is to open the ``binder-control`` device node and +send a ``BINDER_CTL_ADD`` request to the kernel. Users of binderfs need to +tell the kernel which name the new binder device should get. By default a name +can only contain up to ``BINDERFS_MAX_NAME`` chars including the terminating +zero byte. + +Once the request is made via an `ioctl() `_ passing a ``struct +binder_device`` with the name to the kernel it will allocate a new binder +device and return the major and minor number of the new device in the struct +(This is necessary because binderfs allocates a major device number +dynamically.). After the `ioctl() `_ returns there will be a new +binder device located under /dev/binderfs with the chosen name. + +Deleting binder Devices +----------------------- + +.. _unlink: http://man7.org/linux/man-pages/man2/unlink.2.html +.. _rm: http://man7.org/linux/man-pages/man1/rm.1.html + +Binderfs binder devices can be deleted via `unlink() `_. This means +that the `rm() `_ tool can be used to delete them. Note that the +``binder-control`` device cannot be deleted since this would make the binderfs +instance unuseable. The ``binder-control`` device will be deleted when the +binderfs instance is unmounted and all references to it have been dropped. diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst index 605befab300b..61d2441b25d5 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -380,3 +380,10 @@ including: :maxdepth: 2 path-lookup.rst + +binderfs +======== + +.. toctree:: + + binderfs.rst From 9762dc1432e10d5571c1ae746c41469504360c2a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 11 Jan 2019 14:41:00 +0100 Subject: [PATCH 0267/4277] samples: add binderfs sample program This adds a simple sample program mounting binderfs and adding, then removing a binder device. Hopefully, it will be helpful to users who want to know how binderfs is supposed to be used. Signed-off-by: Christian Brauner Signed-off-by: Jonathan Corbet --- samples/Kconfig | 7 +++ samples/Makefile | 2 +- samples/binderfs/Makefile | 1 + samples/binderfs/binderfs_example.c | 83 +++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 samples/binderfs/Makefile create mode 100644 samples/binderfs/binderfs_example.c diff --git a/samples/Kconfig b/samples/Kconfig index ad1ec7016d4c..d19754ccad08 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -147,6 +147,13 @@ config SAMPLE_VFIO_MDEV_MBOCHS Specifically it does *not* include any legacy vga stuff. Device looks a lot like "qemu -device secondary-vga". +config SAMPLE_ANDROID_BINDERFS + bool "Build Android binderfs example" + depends on CONFIG_ANDROID_BINDERFS + help + Builds a sample program to illustrate the use of the Android binderfs + filesystem. + config SAMPLE_STATX bool "Build example extended-stat using code" depends on BROKEN diff --git a/samples/Makefile b/samples/Makefile index bd601c038b86..b1142a958811 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ configfs/ connector/ v4l/ trace_printk/ \ - vfio-mdev/ statx/ qmi/ + vfio-mdev/ statx/ qmi/ binderfs/ diff --git a/samples/binderfs/Makefile b/samples/binderfs/Makefile new file mode 100644 index 000000000000..01ca9f2529a7 --- /dev/null +++ b/samples/binderfs/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs_example.o diff --git a/samples/binderfs/binderfs_example.c b/samples/binderfs/binderfs_example.c new file mode 100644 index 000000000000..5bbd2ebc0aea --- /dev/null +++ b/samples/binderfs/binderfs_example.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd, ret, saved_errno; + size_t len; + struct binderfs_device device = { 0 }; + + ret = unshare(CLONE_NEWNS); + if (ret < 0) { + fprintf(stderr, "%s - Failed to unshare mount namespace\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0); + if (ret < 0) { + fprintf(stderr, "%s - Failed to mount / as private\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + ret = mkdir("/dev/binderfs", 0755); + if (ret < 0 && errno != EEXIST) { + fprintf(stderr, "%s - Failed to create binderfs mountpoint\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + ret = mount(NULL, "/dev/binderfs", "binder", 0, 0); + if (ret < 0) { + fprintf(stderr, "%s - Failed to mount binderfs\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + memcpy(device.name, "my-binder", strlen("my-binder")); + + fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "%s - Failed to open binder-control device\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + ret = ioctl(fd, BINDER_CTL_ADD, &device); + saved_errno = errno; + close(fd); + errno = saved_errno; + if (ret < 0) { + fprintf(stderr, "%s - Failed to allocate new binder device\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + printf("Allocated new binder device with major %d, minor %d, and name %s\n", + device.major, device.minor, device.name); + + ret = unlink("/dev/binderfs/my-binder"); + if (ret < 0) { + fprintf(stderr, "%s - Failed to delete binder device\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* Cleanup happens when the mount namespace dies. */ + exit(EXIT_SUCCESS); +} From 631605c007531ef0b48690740519d0b9a6b22d38 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Fri, 11 Jan 2019 17:14:10 +0100 Subject: [PATCH 0268/4277] Documentation/sysctl/vm.txt: Fix drop_caches bit number Bits are usually numbered starting from zero, so 4 should be bit 2, not bit 3. Suggested-by: Matthew Wilcox Signed-off-by: Vincent Whitchurch Signed-off-by: Jonathan Corbet --- Documentation/sysctl/vm.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 187ce4f599a2..6af24cdb25cc 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -237,7 +237,7 @@ used: cat (1234): drop_caches: 3 These are informational only. They do not mean that anything is wrong -with your system. To disable them, echo 4 (bit 3) into drop_caches. +with your system. To disable them, echo 4 (bit 2) into drop_caches. ============================================================== From 58f4df3c1bde999574d3e66b20eb7ee796a2647e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 14 Jan 2019 11:08:07 +0100 Subject: [PATCH 0269/4277] Documentation/dev-tools: Use gcc version number instead svn revision number svn commit 231296 matches commit d29e939c63b71 ("Add fuzzing coverage support") in the gcc git. The change is part of gcc 6.1.0. Replace the svn commit number with a gcc version which everyone can easily compare. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jonathan Corbet --- Documentation/dev-tools/kcov.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst index c2f6452e38ed..42b612677799 100644 --- a/Documentation/dev-tools/kcov.rst +++ b/Documentation/dev-tools/kcov.rst @@ -22,7 +22,7 @@ Configure the kernel with:: CONFIG_KCOV=y -CONFIG_KCOV requires gcc built on revision 231296 or later. +CONFIG_KCOV requires gcc 6.1.0 or later. If the comparison operands need to be collected, set:: From 98e5f349c9a0253d4a213d54db918f2aab5846ee Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 14 Jan 2019 13:47:34 +0200 Subject: [PATCH 0270/4277] docs/core-api: memory-allocation: add mention of kmem_cache_create_userspace Mention that when a part of a slab cache might be exported to the userspace, the cache should be created using kmem_cache_create_usercopy() Signed-off-by: Mike Rapoport Signed-off-by: Jonathan Corbet --- Documentation/core-api/memory-allocation.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Documentation/core-api/memory-allocation.rst b/Documentation/core-api/memory-allocation.rst index 8954a88ff5b7..51a200d59f51 100644 --- a/Documentation/core-api/memory-allocation.rst +++ b/Documentation/core-api/memory-allocation.rst @@ -113,9 +113,11 @@ see :c:func:`kvmalloc_node` reference documentation. Note that If you need to allocate many identical objects you can use the slab cache allocator. The cache should be set up with -:c:func:`kmem_cache_create` before it can be used. Afterwards -:c:func:`kmem_cache_alloc` and its convenience wrappers can allocate -memory from that cache. +:c:func:`kmem_cache_create` or :c:func:`kmem_cache_create_usercopy` +before it can be used. The second function should be used if a part of +the cache might be copied to the userspace. After the cache is +created :c:func:`kmem_cache_alloc` and its convenience wrappers can +allocate memory from that cache. When the allocated memory is no longer needed it must be freed. You can use :c:func:`kvfree` for the memory allocated with `kmalloc`, From 80a76c7261d5c3a21eca3dacd6f10f9553c08244 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 14 Jan 2019 20:32:58 +0200 Subject: [PATCH 0271/4277] docs/core-api/mm: fix GFP combinations section name Fix the mismatch between "Useful GFP flag combinations" section naming in the DOC: section in include/linux/gfp.h and Documentation/core-api/mm-api.rst. This brings in the documentation, and eliminates one warning: ./include/linux/gfp.h:1: warning: no structured comments found Signed-off-by: Mike Rapoport [jc: tweaked changelog] Signed-off-by: Jonathan Corbet --- Documentation/core-api/mm-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/core-api/mm-api.rst b/Documentation/core-api/mm-api.rst index aa8e54b85221..128e8a721c1e 100644 --- a/Documentation/core-api/mm-api.rst +++ b/Documentation/core-api/mm-api.rst @@ -35,7 +35,7 @@ users will want to use a plain ``GFP_KERNEL``. :doc: Reclaim modifiers .. kernel-doc:: include/linux/gfp.h - :doc: Common combinations + :doc: Useful GFP flag combinations The Slab Cache ============== From 4d01460ec9a454a9b536fd4e050f61389c381d9e Mon Sep 17 00:00:00 2001 From: Joel Nider Date: Mon, 14 Jan 2019 09:14:59 +0200 Subject: [PATCH 0272/4277] docs-rst: doc-guide: Minor grammar fixes While using this guide to learn the new documentation method, I saw a few phrases that I felt could be improved. These small changes improve the grammar and choice of words to further enhance the installation instructions. Signed-off-by: Joel Nider Acked-by: Mike Rapoport Acked-by: Matthew Wilcox Signed-off-by: Jonathan Corbet --- Documentation/doc-guide/sphinx.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/doc-guide/sphinx.rst b/Documentation/doc-guide/sphinx.rst index 02605ee1d876..c039224b404e 100644 --- a/Documentation/doc-guide/sphinx.rst +++ b/Documentation/doc-guide/sphinx.rst @@ -27,8 +27,8 @@ Sphinx Install ============== The ReST markups currently used by the Documentation/ files are meant to be -built with ``Sphinx`` version 1.3 or upper. If you're desiring to build -PDF outputs, it is recommended to use version 1.4.6 or upper. +built with ``Sphinx`` version 1.3 or higher. If you desire to build +PDF output, it is recommended to use version 1.4.6 or higher. There's a script that checks for the Sphinx requirements. Please see :ref:`sphinx-pre-install` for further details. @@ -37,15 +37,15 @@ Most distributions are shipped with Sphinx, but its toolchain is fragile, and it is not uncommon that upgrading it or some other Python packages on your machine would cause the documentation build to break. -A way to get rid of that is to use a different version than the one shipped -on your distributions. In order to do that, it is recommended to install +A way to avoid that is to use a different version than the one shipped +with your distributions. In order to do so, it is recommended to install Sphinx inside a virtual environment, using ``virtualenv-3`` or ``virtualenv``, depending on how your distribution packaged Python 3. .. note:: #) Sphinx versions below 1.5 don't work properly with Python's - docutils version 0.13.1 or upper. So, if you're willing to use + docutils version 0.13.1 or higher. So, if you're willing to use those versions, you should run ``pip install 'docutils==0.12'``. #) It is recommended to use the RTD theme for html output. Depending @@ -82,7 +82,7 @@ output. PDF and LaTeX builds -------------------- -Such builds are currently supported only with Sphinx versions 1.4 and upper. +Such builds are currently supported only with Sphinx versions 1.4 and higher. For PDF and LaTeX output, you'll also need ``XeLaTeX`` version 3.14159265. From b631c7f513548f4097687603ef6f959ccae18da9 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 9 Jan 2019 14:01:32 -0700 Subject: [PATCH 0273/4277] docs: don't try to get comments from rcupdate_wait.h or rcutree.h Neither file contains any kerneldoc comments, so including them generates these warnings in the docs build: ./include/linux/rcupdate_wait.h:1: warning: no structured comments found ./include/linux/rcutree.h:1: warning: no structured comments found Remove them and make life a little quieter. Signed-off-by: Jonathan Corbet --- Documentation/core-api/kernel-api.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Documentation/core-api/kernel-api.rst b/Documentation/core-api/kernel-api.rst index cdd24943fbcc..71f5d2fe39b7 100644 --- a/Documentation/core-api/kernel-api.rst +++ b/Documentation/core-api/kernel-api.rst @@ -356,10 +356,6 @@ Read-Copy Update (RCU) .. kernel-doc:: include/linux/rcupdate.h -.. kernel-doc:: include/linux/rcupdate_wait.h - -.. kernel-doc:: include/linux/rcutree.h - .. kernel-doc:: kernel/rcu/tree.c .. kernel-doc:: kernel/rcu/tree_plugin.h From 557599f14aab2e1e459478c57b5fd83d153189de Mon Sep 17 00:00:00 2001 From: Brajeswar Ghosh Date: Wed, 9 Jan 2019 19:59:59 +0530 Subject: [PATCH 0274/4277] remoteproc/qcom_sysmon.c: Remove duplicate header Remove linux/notifier.h which is included more than once Acked-by: Souptick Joarder Signed-off-by: Brajeswar Ghosh Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_sysmon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index e976a602b015..603b813151f2 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include From c95b323dcd3598dd7ef5005d6723c1ba3b801093 Mon Sep 17 00:00:00 2001 From: Shirish S Date: Thu, 10 Jan 2019 07:54:40 +0000 Subject: [PATCH 0275/4277] x86/MCE/AMD: Turn off MC4_MISC thresholding on all family 0x15 models MC4_MISC thresholding is not supported on all family 0x15 processors, hence skip the x86_model check when applying the quirk. [ bp: massage commit message. ] Signed-off-by: Shirish S Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Tony Luck Cc: Vishal Verma Cc: x86-ml Link: https://lkml.kernel.org/r/1547106849-3476-2-git-send-email-shirish.s@amd.com --- arch/x86/kernel/cpu/mce/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 672c7225cb1b..d0c54160b439 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1612,11 +1612,10 @@ static int __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c) mce_flags.overflow_recov = 1; /* - * Turn off MC4_MISC thresholding banks on those models since + * Turn off MC4_MISC thresholding banks on all models since * they're not supported there. */ - if (c->x86 == 0x15 && - (c->x86_model >= 0x10 && c->x86_model <= 0x1f)) { + if (c->x86 == 0x15) { int i; u64 hwcr; bool need_toggle; From 966d23a006ca7b44ac8cf4d0c96b19785e0c3da0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 15 Jan 2019 10:47:00 -0800 Subject: [PATCH 0276/4277] libnvdimm/label: Clear 'updating' flag after label-set update The UEFI 2.7 specification sets expectations that the 'updating' flag is eventually cleared. To date, the libnvdimm core has never adhered to that protocol. The policy of the core matches the policy of other multi-device info-block formats like MD-Software-RAID that expect administrator intervention on inconsistent info-blocks, not automatic invalidation. However, some pre-boot environments may unfortunately attempt to "clean up" the labels and invalidate a set when it fails to find at least one "non-updating" label in the set. Clear the updating flag after set updates to minimize the window of vulnerability to aggressive pre-boot environments. Ideally implementations would not write to the label area outside of creating namespaces. Note that this only minimizes the window, it does not close it as the system can still crash while clearing the flag and the set can be subsequently deleted / invalidated by the pre-boot environment. Fixes: f524bf271a5c ("libnvdimm: write pmem label set") Cc: Cc: Kelly Couch Signed-off-by: Dan Williams --- drivers/nvdimm/label.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index a11bf4e6b451..6d6e9a12150b 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -755,7 +755,7 @@ static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, static int __pmem_label_update(struct nd_region *nd_region, struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, - int pos) + int pos, unsigned long flags) { struct nd_namespace_common *ndns = &nspm->nsio.common; struct nd_interleave_set *nd_set = nd_region->nd_set; @@ -796,7 +796,7 @@ static int __pmem_label_update(struct nd_region *nd_region, memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN); if (nspm->alt_name) memcpy(nd_label->name, nspm->alt_name, NSLABEL_NAME_LEN); - nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_UPDATING); + nd_label->flags = __cpu_to_le32(flags); nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); nd_label->position = __cpu_to_le16(pos); nd_label->isetcookie = __cpu_to_le64(cookie); @@ -1249,13 +1249,13 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) int nd_pmem_namespace_label_update(struct nd_region *nd_region, struct nd_namespace_pmem *nspm, resource_size_t size) { - int i; + int i, rc; for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct resource *res; - int rc, count = 0; + int count = 0; if (size == 0) { rc = del_labels(nd_mapping, nspm->uuid); @@ -1273,7 +1273,20 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region, if (rc < 0) return rc; - rc = __pmem_label_update(nd_region, nd_mapping, nspm, i); + rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, + NSLABEL_FLAG_UPDATING); + if (rc) + return rc; + } + + if (size == 0) + return 0; + + /* Clear the UPDATING flag per UEFI 2.7 expectations */ + for (i = 0; i < nd_region->ndr_mappings; i++) { + struct nd_mapping *nd_mapping = &nd_region->mapping[i]; + + rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0); if (rc) return rc; } From 5b3a23a3cc94864aacc012532b36afa92a25b9c0 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 9 Jan 2019 06:56:49 +0100 Subject: [PATCH 0277/4277] i2c: imx: notify about real errors on dma i2c_imx_dma_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least on i.MX5x, the DMA events for I2C and SDHC use the same channel and there can only be a single user. So in this case there should be no message emitted that looks like an error if the I2C device doesn't have an assigned DMA channel. In contrast real problems that were only emitted at debug level before should be described at a higher level to be better visible and so understandable. Signed-off-by: Oleksij Rempel Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index fa9ad53845d9..e28ef494dac8 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -285,9 +285,11 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, if (!dma) return; - dma->chan_tx = dma_request_slave_channel(dev, "tx"); - if (!dma->chan_tx) { - dev_dbg(dev, "can't request DMA tx channel\n"); + dma->chan_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(dma->chan_tx)) { + ret = PTR_ERR(dma->chan_rx); + if (ret != -ENODEV && ret != -EPROBE_DEFER) + dev_err(dev, "can't request DMA tx channel (%d)\n", ret); goto fail_al; } @@ -298,13 +300,15 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_sconfig.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig); if (ret < 0) { - dev_dbg(dev, "can't configure tx channel\n"); + dev_err(dev, "can't configure tx channel (%d)\n", ret); goto fail_tx; } - dma->chan_rx = dma_request_slave_channel(dev, "rx"); - if (!dma->chan_rx) { - dev_dbg(dev, "can't request DMA rx channel\n"); + dma->chan_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(dma->chan_rx)) { + ret = PTR_ERR(dma->chan_rx); + if (ret != -ENODEV && ret != -EPROBE_DEFER) + dev_err(dev, "can't request DMA rx channel (%d)\n", ret); goto fail_tx; } @@ -315,7 +319,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_sconfig.direction = DMA_DEV_TO_MEM; ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig); if (ret < 0) { - dev_dbg(dev, "can't configure rx channel\n"); + dev_err(dev, "can't configure rx channel (%d)\n", ret); goto fail_rx; } @@ -332,7 +336,6 @@ fail_tx: dma_release_channel(dma->chan_tx); fail_al: devm_kfree(dev, dma); - dev_info(dev, "can't use DMA, using PIO instead.\n"); } static void i2c_imx_dma_callback(void *arg) From e1ab9a468e3b1636d60cebd0a778461270dde208 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 9 Jan 2019 06:56:50 +0100 Subject: [PATCH 0278/4277] i2c: imx: improve the error handling in i2c_imx_dma_request() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the error handling in i2c_imx_dma_request() and let it return an error indication that the caller then can handle accordingly. Signed-off-by: Oleksij Rempel Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index e28ef494dac8..09b124547669 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -273,8 +273,8 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, } /* Functions for DMA support */ -static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, - dma_addr_t phy_addr) +static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, + dma_addr_t phy_addr) { struct imx_i2c_dma *dma; struct dma_slave_config dma_sconfig; @@ -283,7 +283,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); if (!dma) - return; + return -ENOMEM; dma->chan_tx = dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { @@ -328,7 +328,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); - return; + return 0; fail_rx: dma_release_channel(dma->chan_rx); @@ -336,6 +336,8 @@ fail_tx: dma_release_channel(dma->chan_tx); fail_al: devm_kfree(dev, dma); + /* return successfully if there is no dma support */ + return ret == -ENODEV ? 0 : ret; } static void i2c_imx_dma_callback(void *arg) @@ -1163,11 +1165,13 @@ static int i2c_imx_probe(struct platform_device *pdev) dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", i2c_imx->adapter.name); - dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); /* Init DMA config if supported */ - i2c_imx_dma_request(i2c_imx, phy_addr); + ret = i2c_imx_dma_request(i2c_imx, phy_addr); + if (ret < 0) + goto clk_notifier_unregister; + dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); return 0; /* Return OK */ clk_notifier_unregister: From b33a02aadcc6330a61e511240b634dc11112e65e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2019 17:24:55 +0200 Subject: [PATCH 0279/4277] i2c: acpi: Move I2C bits from acpi.h to i2c.h As discussed previously the best location for certain bus related bits, e.g. I2C, is its own realm of the headers. In order to uncontaminate acpi.h move the I2C bits to i2c.h. There is no functional change intended. Link: https://lkml.org/lkml/2018/11/28/744 Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Wolfram Sang --- include/linux/acpi.h | 11 ----------- include/linux/i2c.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 87715f20b69a..13f5cb2c4763 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1061,17 +1061,6 @@ static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) } #endif -#if defined(CONFIG_ACPI) && IS_ENABLED(CONFIG_I2C) -bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, - struct acpi_resource_i2c_serialbus **i2c); -#else -static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, - struct acpi_resource_i2c_serialbus **i2c) -{ - return false; -} -#endif - /* Device properties */ #ifdef CONFIG_ACPI diff --git a/include/linux/i2c.h b/include/linux/i2c.h index cba59d66c00d..1f45331924d6 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -967,11 +967,21 @@ static inline int of_i2c_get_board_info(struct device *dev, #endif /* CONFIG_OF */ +struct acpi_resource; +struct acpi_resource_i2c_serialbus; + #if IS_ENABLED(CONFIG_ACPI) +bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c); u32 i2c_acpi_find_bus_speed(struct device *dev); struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, struct i2c_board_info *info); #else +static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c) +{ + return false; +} static inline u32 i2c_acpi_find_bus_speed(struct device *dev) { return 0; From 3352976c892301fd576a2e9ff0ac7337b2e2ca48 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Fri, 11 Jan 2019 20:27:18 +0530 Subject: [PATCH 0280/4277] RDMA/iw_cxgb4: Fix the unchecked ep dereference The patch 944661dd97f4: "RDMA/iw_cxgb4: atomically lookup ep and get a reference" from May 6, 2016, leads to the following Smatch complaint: drivers/infiniband/hw/cxgb4/cm.c:2953 terminate() error: we previously assumed 'ep' could be null (see line 2945) Fixes: 944661dd97f4 ("RDMA/iw_cxgb4: atomically lookup ep and get a reference") Reported-by: Dan Carpenter Signed-off-by: Raju Rangoju Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/cm.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 8221813219e5..59917eb124da 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2942,15 +2942,18 @@ static int terminate(struct c4iw_dev *dev, struct sk_buff *skb) ep = get_ep_from_tid(dev, tid); - if (ep && ep->com.qp) { - pr_warn("TERM received tid %u qpid %u\n", - tid, ep->com.qp->wq.sq.qid); - attrs.next_state = C4IW_QP_STATE_TERMINATE; - c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, - C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + if (ep) { + if (ep->com.qp) { + pr_warn("TERM received tid %u qpid %u\n", tid, + ep->com.qp->wq.sq.qid); + attrs.next_state = C4IW_QP_STATE_TERMINATE; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + + c4iw_put_ep(&ep->com); } else pr_warn("TERM received tid %u no ep/qp\n", tid); - c4iw_put_ep(&ep->com); return 0; } From 8e8aa1454205839030c9406febd1f6d4ece0d573 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 15 Jan 2019 00:00:48 -0600 Subject: [PATCH 0281/4277] RDMA/mlx5: Replace kzalloc with kcalloc Replace kzalloc() function with its 2-factor argument form, kcalloc(). This patch replaces cases of: kzalloc(a * b, gfp) with: kcalloc(a, b, gfp) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 4b1b56d54301..61064b7171fc 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3823,7 +3823,7 @@ mlx5_ib_raw_fs_rule_add(struct mlx5_ib_dev *dev, if (fs_matcher->priority > MLX5_IB_FLOW_LAST_PRIO) return ERR_PTR(-ENOMEM); - dst = kzalloc(sizeof(*dst) * 2, GFP_KERNEL); + dst = kcalloc(2, sizeof(*dst), GFP_KERNEL); if (!dst) return ERR_PTR(-ENOMEM); From d80f8206905c1a8c3857d90f12bbfd6293b48a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Jan 2019 20:53:52 +0100 Subject: [PATCH 0282/4277] pwm: imx: Split into two drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two PWM implementations called v1 (for i.MX1 and i.MX21) and v2 (for i.MX27 and later) have nothing in common apart from needing two clocks named "per" and "ipg" and being integrated in a SoC named i.MX. So split the file containing the two disjunct drivers into two files and two complete separate drivers. Signed-off-by: Uwe Kleine-König [thierry.reding@gmail.com: fix a modular build issue] Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 17 ++- drivers/pwm/Makefile | 3 +- drivers/pwm/pwm-imx1.c | 199 +++++++++++++++++++++++++ drivers/pwm/{pwm-imx.c => pwm-imx27.c} | 191 ++++++------------------ 4 files changed, 257 insertions(+), 153 deletions(-) create mode 100644 drivers/pwm/pwm-imx1.c rename drivers/pwm/{pwm-imx.c => pwm-imx27.c} (61%) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a8f47df0655a..54f8238aac0d 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -192,14 +192,23 @@ config PWM_IMG To compile this driver as a module, choose M here: the module will be called pwm-img -config PWM_IMX - tristate "i.MX PWM support" +config PWM_IMX1 + tristate "i.MX1 PWM support" depends on ARCH_MXC help - Generic PWM framework driver for i.MX. + Generic PWM framework driver for i.MX1 and i.MX21 To compile this driver as a module, choose M here: the module - will be called pwm-imx. + will be called pwm-imx1. + +config PWM_IMX27 + tristate "i.MX27 PWM support" + depends on ARCH_MXC + help + Generic PWM framework driver for i.MX27 and later i.MX SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-imx27. config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 9c676a0dadf5..448825e892bc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o -obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c new file mode 100644 index 000000000000..f8b2c2e001a7 --- /dev/null +++ b/drivers/pwm/pwm-imx1.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * simple driver for PWM (Pulse Width Modulator) controller + * + * Derived from pxa PWM driver by eric miao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MX1_PWMC 0x00 /* PWM Control Register */ +#define MX1_PWMS 0x04 /* PWM Sample Register */ +#define MX1_PWMP 0x08 /* PWM Period Register */ + +#define MX1_PWMC_EN BIT(4) + +struct pwm_imx1_chip { + struct clk *clk_ipg; + struct clk *clk_per; + void __iomem *mmio_base; + struct pwm_chip chip; +}; + +#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip) + +static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + int ret; + + ret = clk_prepare_enable(imx->clk_ipg); + if (ret) + return ret; + + ret = clk_prepare_enable(imx->clk_per); + if (ret) { + clk_disable_unprepare(imx->clk_ipg); + return ret; + } + + return 0; +} + +static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + + clk_disable_unprepare(imx->clk_per); + clk_disable_unprepare(imx->clk_ipg); +} + +static int pwm_imx1_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 max, p; + + /* + * The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + max = readl(imx->mmio_base + MX1_PWMP); + p = max * duty_ns / period_ns; + + writel(max - p, imx->mmio_base + MX1_PWMS); + + return 0; +} + +static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + int ret; + + ret = pwm_imx1_clk_prepare_enable(chip); + if (ret < 0) + return ret; + + value = readl(imx->mmio_base + MX1_PWMC); + value |= MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + return 0; +} + +static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + + value = readl(imx->mmio_base + MX1_PWMC); + value &= ~MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + pwm_imx1_clk_disable_unprepare(chip); +} + +static const struct pwm_ops pwm_imx1_ops = { + .enable = pwm_imx1_enable, + .disable = pwm_imx1_disable, + .config = pwm_imx1_config, + .owner = THIS_MODULE, +}; + +static const struct of_device_id pwm_imx1_dt_ids[] = { + { .compatible = "fsl,imx1-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids); + +static int pwm_imx1_probe(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx; + struct resource *r; + + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); + if (!imx) + return -ENOMEM; + + platform_set_drvdata(pdev, imx); + + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(imx->clk_ipg)) { + dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", + PTR_ERR(imx->clk_ipg)); + return PTR_ERR(imx->clk_ipg); + } + + imx->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(imx->clk_per)) { + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; + } + + imx->chip.ops = &pwm_imx1_ops; + imx->chip.dev = &pdev->dev; + imx->chip.base = -1; + imx->chip.npwm = 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(imx->mmio_base)) + return PTR_ERR(imx->mmio_base); + + return pwmchip_add(&imx->chip); +} + +static int pwm_imx1_remove(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); + + pwm_imx1_clk_disable_unprepare(&imx->chip); + + return pwmchip_remove(&imx->chip); +} + +static struct platform_driver pwm_imx1_driver = { + .driver = { + .name = "pwm-imx1", + .of_match_table = pwm_imx1_dt_ids, + }, + .probe = pwm_imx1_probe, + .remove = pwm_imx1_remove, +}; +module_platform_driver(pwm_imx1_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer "); diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx27.c similarity index 61% rename from drivers/pwm/pwm-imx.c rename to drivers/pwm/pwm-imx27.c index 30380fcb5cfb..8b8b1c6b7f29 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx27.c @@ -19,16 +19,6 @@ #include #include -/* i.MX1 and i.MX21 share the same PWM function block: */ - -#define MX1_PWMC 0x00 /* PWM Control Register */ -#define MX1_PWMS 0x04 /* PWM Sample Register */ -#define MX1_PWMP 0x08 /* PWM Period Register */ - -#define MX1_PWMC_EN BIT(4) - -/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ - #define MX3_PWMCR 0x00 /* PWM Control Register */ #define MX3_PWMSR 0x04 /* PWM Status Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ @@ -86,21 +76,18 @@ /* PWMPR register value of 0xffff has the same effect as 0xfffe */ #define MX3_PWMPR_MAX 0xfffe -struct imx_chip { +struct pwm_imx27_chip { struct clk *clk_ipg; - struct clk *clk_per; - void __iomem *mmio_base; - struct pwm_chip chip; }; -#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) +#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip) -static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) +static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); int ret; ret = clk_prepare_enable(imx->clk_ipg); @@ -116,22 +103,22 @@ static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) return 0; } -static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip) +static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); clk_disable_unprepare(imx->clk_per); clk_disable_unprepare(imx->clk_ipg); } -static void imx_pwm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, struct pwm_state *state) +static void pwm_imx27_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, struct pwm_state *state) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); u32 period, prescaler, pwm_clk, ret, val; u64 tmp; - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret < 0) return; @@ -139,7 +126,7 @@ static void imx_pwm_get_state(struct pwm_chip *chip, if (val & MX3_PWMCR_EN) { state->enabled = true; - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret) return; } else { @@ -176,70 +163,12 @@ static void imx_pwm_get_state(struct pwm_chip *chip, state->duty_cycle = 0; } - imx_pwm_clk_disable_unprepare(chip); + pwm_imx27_clk_disable_unprepare(chip); } -static int imx_pwm_config_v1(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns) +static void pwm_imx27_sw_reset(struct pwm_chip *chip) { - struct imx_chip *imx = to_imx_chip(chip); - - /* - * The PWM subsystem allows for exact frequencies. However, - * I cannot connect a scope on my device to the PWM line and - * thus cannot provide the program the PWM controller - * exactly. Instead, I'm relying on the fact that the - * Bootloader (u-boot or WinCE+haret) has programmed the PWM - * function group already. So I'll just modify the PWM sample - * register to follow the ratio of duty_ns vs. period_ns - * accordingly. - * - * This is good enough for programming the brightness of - * the LCD backlight. - * - * The real implementation would divide PERCLK[0] first by - * both the prescaler (/1 .. /128) and then by CLKSEL - * (/2 .. /16). - */ - u32 max = readl(imx->mmio_base + MX1_PWMP); - u32 p = max * duty_ns / period_ns; - writel(max - p, imx->mmio_base + MX1_PWMS); - - return 0; -} - -static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - int ret; - - ret = imx_pwm_clk_prepare_enable(chip); - if (ret < 0) - return ret; - - val = readl(imx->mmio_base + MX1_PWMC); - val |= MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - return 0; -} - -static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - - val = readl(imx->mmio_base + MX1_PWMC); - val &= ~MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - imx_pwm_clk_disable_unprepare(chip); -} - -static void imx_pwm_sw_reset(struct pwm_chip *chip) -{ - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct device *dev = chip->dev; int wait_count = 0; u32 cr; @@ -255,10 +184,10 @@ static void imx_pwm_sw_reset(struct pwm_chip *chip) dev_warn(dev, "software reset timeout\n"); } -static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, - struct pwm_device *pwm) +static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, + struct pwm_device *pwm) { - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct device *dev = chip->dev; unsigned int period_ms; int fifoav; @@ -277,11 +206,11 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, } } -static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { unsigned long period_cycles, duty_cycles, prescale; - struct imx_chip *imx = to_imx_chip(chip); + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); struct pwm_state cstate; unsigned long long c; int ret; @@ -318,13 +247,13 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, * enabled. */ if (cstate.enabled) { - imx_pwm_wait_fifo_slot(chip, pwm); + pwm_imx27_wait_fifo_slot(chip, pwm); } else { - ret = imx_pwm_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(chip); if (ret) return ret; - imx_pwm_sw_reset(chip); + pwm_imx27_sw_reset(chip); } writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); @@ -343,59 +272,29 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, } else if (cstate.enabled) { writel(0, imx->mmio_base + MX3_PWMCR); - imx_pwm_clk_disable_unprepare(chip); + pwm_imx27_clk_disable_unprepare(chip); } return 0; } -static const struct pwm_ops imx_pwm_ops_v1 = { - .enable = imx_pwm_enable_v1, - .disable = imx_pwm_disable_v1, - .config = imx_pwm_config_v1, +static const struct pwm_ops pwm_imx27_ops = { + .apply = pwm_imx27_apply, + .get_state = pwm_imx27_get_state, .owner = THIS_MODULE, }; -static const struct pwm_ops imx_pwm_ops_v2 = { - .apply = imx_pwm_apply_v2, - .get_state = imx_pwm_get_state, - .owner = THIS_MODULE, -}; - -struct imx_pwm_data { - bool polarity_supported; - const struct pwm_ops *ops; -}; - -static struct imx_pwm_data imx_pwm_data_v1 = { - .ops = &imx_pwm_ops_v1, -}; - -static struct imx_pwm_data imx_pwm_data_v2 = { - .polarity_supported = true, - .ops = &imx_pwm_ops_v2, -}; - -static const struct of_device_id imx_pwm_dt_ids[] = { - { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, - { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, +static const struct of_device_id pwm_imx27_dt_ids[] = { + { .compatible = "fsl,imx27-pwm", }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); +MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); -static int imx_pwm_probe(struct platform_device *pdev) +static int pwm_imx27_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(imx_pwm_dt_ids, &pdev->dev); - const struct imx_pwm_data *data; - struct imx_chip *imx; + struct pwm_imx27_chip *imx; struct resource *r; - if (!of_id) - return -ENODEV; - - data = of_id->data; - imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) return -ENOMEM; @@ -421,16 +320,13 @@ static int imx_pwm_probe(struct platform_device *pdev) return ret; } - imx->chip.ops = data->ops; + imx->chip.ops = &pwm_imx27_ops; imx->chip.dev = &pdev->dev; imx->chip.base = -1; imx->chip.npwm = 1; - if (data->polarity_supported) { - dev_dbg(&pdev->dev, "PWM supports output inversion\n"); - imx->chip.of_xlate = of_pwm_xlate_with_flags; - imx->chip.of_pwm_n_cells = 3; - } + imx->chip.of_xlate = of_pwm_xlate_with_flags; + imx->chip.of_pwm_n_cells = 3; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); @@ -440,26 +336,25 @@ static int imx_pwm_probe(struct platform_device *pdev) return pwmchip_add(&imx->chip); } -static int imx_pwm_remove(struct platform_device *pdev) +static int pwm_imx27_remove(struct platform_device *pdev) { - struct imx_chip *imx; + struct pwm_imx27_chip *imx; imx = platform_get_drvdata(pdev); - imx_pwm_clk_disable_unprepare(&imx->chip); + pwm_imx27_clk_disable_unprepare(&imx->chip); return pwmchip_remove(&imx->chip); } static struct platform_driver imx_pwm_driver = { - .driver = { - .name = "imx-pwm", - .of_match_table = imx_pwm_dt_ids, + .driver = { + .name = "pwm-imx27", + .of_match_table = pwm_imx27_dt_ids, }, - .probe = imx_pwm_probe, - .remove = imx_pwm_remove, + .probe = pwm_imx27_probe, + .remove = pwm_imx27_remove, }; - module_platform_driver(imx_pwm_driver); MODULE_LICENSE("GPL v2"); From 7ca17b207127240b1e4c8687ba7b9077e655336d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Jan 2019 11:27:47 +0300 Subject: [PATCH 0283/4277] pwm: imx: Signedness bug in imx_pwm_get_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "ret" only holds zero and negative error codes. It needs to be signed for the error handling to work. Fixes: 9f4c8f9607c3 ("pwm: imx: Add ipg clock operation") Signed-off-by: Dan Carpenter Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 8b8b1c6b7f29..55666cca4cee 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -115,8 +115,9 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); - u32 period, prescaler, pwm_clk, ret, val; + u32 period, prescaler, pwm_clk, val; u64 tmp; + int ret; ret = pwm_imx27_clk_prepare_enable(chip); if (ret < 0) From 270a60bcc8f23254d749987f3d2acb79ea5c599e Mon Sep 17 00:00:00 2001 From: Philippe Schenker Date: Fri, 21 Dec 2018 14:46:30 +0100 Subject: [PATCH 0284/4277] dt-bindings: stmpe: Reformatting parameter list and use tabs only This patch reformats the parameter list for stmpe device in a table-style so it is more clear to read. Signed-off-by: Philippe Schenker Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- .../bindings/input/touchscreen/stmpe.txt | 64 +++++++++++++------ .../devicetree/bindings/mfd/stmpe.txt | 14 ++-- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt index 127baa31a77a..bf66a55a7de5 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt @@ -5,24 +5,52 @@ Required properties: - compatible: "st,stmpe-ts" Optional properties: -- st,sample-time: ADC converstion time in number of clock. (0 -> 36 clocks, 1 -> - 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, 4 -> 80 clocks, 5 -> 96 clocks, 6 - -> 144 clocks), recommended is 4. -- st,mod-12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) -- st,ref-sel: ADC reference source (0 -> internal reference, 1 -> external - reference) -- st,adc-freq: ADC Clock speed (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) -- st,ave-ctrl: Sample average control (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 - samples, 3 -> 8 samples) -- st,touch-det-delay: Touch detect interrupt delay (0 -> 10 us, 1 -> 50 us, 2 -> - 100 us, 3 -> 500 us, 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms) recommended - is 3 -- st,settling: Panel driver settling time (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 - -> 1 ms, 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms) recommended is 2 -- st,fraction-z: Length of the fractional part in z (fraction-z ([0..7]) = Count of - the fractional part) recommended is 7 -- st,i-drive: current limit value of the touchscreen drivers (0 -> 20 mA typical 35 - mA max, 1 -> 50 mA typical 80 mA max) +- st,sample-time : ADC conversion time in number of clock. + 0 -> 36 clocks + 1 -> 44 clocks + 2 -> 56 clocks + 3 -> 64 clocks + 4 -> 80 clocks (recommended) + 5 -> 96 clocks + 6 -> 144 clocks +- st,mod-12b : ADC Bit mode + 0 -> 10bit ADC + 1 -> 12bit ADC +- st,ref-sel : ADC reference source + 0 -> internal + 1 -> external +- st,adc-freq : ADC Clock speed + 0 -> 1.625 MHz + 1 -> 3.25 MHz + 2 || 3 -> 6.5 MHz +- st,ave-ctrl : Sample average control + 0 -> 1 sample + 1 -> 2 samples + 2 -> 4 samples + 3 -> 8 samples +- st,touch-det-delay : Touch detect interrupt delay (recommended is 3) + 0 -> 10 us + 1 -> 50 us + 2 -> 100 us + 3 -> 500 us + 4 -> 1 ms + 5 -> 5 ms + 6 -> 10 ms + 7 -> 50 ms +- st,settling : Panel driver settling time (recommended is 2) + 0 -> 10 us + 1 -> 100 us + 2 -> 500 us + 3 -> 1 ms + 4 -> 5 ms + 5 -> 10 ms + 6 -> 50 ms + 7 -> 100 ms +- st,fraction-z : Length of the fractional part in z (recommended is 7) + (fraction-z ([0..7]) = Count of the fractional part) +- st,i-drive : current limit value of the touchscreen drivers + 0 -> 20 mA (typical 35mA max) + 1 -> 50 mA (typical 80 mA max) Node name must be stmpe_touchscreen and should be child node of stmpe node to which it belongs. diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index c797c05cd3c2..a46e7177195d 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -4,15 +4,15 @@ STMPE is an MFD device which may expose the following inbuilt devices: gpio, keypad, touchscreen, adc, pwm, rotator. Required properties: - - compatible : "st,stmpe[610|801|811|1600|1601|2401|2403]" - - reg : I2C/SPI address of the device + - compatible : "st,stmpe[610|801|811|1600|1601|2401|2403]" + - reg : I2C/SPI address of the device Optional properties: - - interrupts : The interrupt outputs from the controller - - interrupt-controller : Marks the device node as an interrupt controller - - wakeup-source : Marks the input device as wakable - - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 - - irq-gpio : If present, which GPIO to use for event IRQ + - interrupts : The interrupt outputs from the controller + - interrupt-controller : Marks the device node as an interrupt controller + - wakeup-source : Marks the input device as wakable + - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 + - irq-gpio : If present, which GPIO to use for event IRQ Example: From 063755ab1d1c1127adc09703185967862584935b Mon Sep 17 00:00:00 2001 From: Philippe Schenker Date: Fri, 21 Dec 2018 14:46:31 +0100 Subject: [PATCH 0285/4277] mfd: stmpe: Move ADC related defines to MFD header Move defines that are ADC related to the header of the overlying MFD, so they can be used from multiple sub-devices. Signed-off-by: Philippe Schenker Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/touchscreen/stmpe-ts.c | 34 +++++++++++----------------- include/linux/mfd/stmpe.h | 11 +++++++++ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 2a78e27b4495..c5d9006588a2 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -49,17 +49,6 @@ #define STMPE_IRQ_TOUCH_DET 0 -#define SAMPLE_TIME(x) ((x & 0xf) << 4) -#define MOD_12B(x) ((x & 0x1) << 3) -#define REF_SEL(x) ((x & 0x1) << 1) -#define ADC_FREQ(x) (x & 0x3) -#define AVE_CTRL(x) ((x & 0x3) << 6) -#define DET_DELAY(x) ((x & 0x7) << 3) -#define SETTLING(x) (x & 0x7) -#define FRACTION_Z(x) (x & 0x7) -#define I_DRIVE(x) (x & 0x1) -#define OP_MODE(x) ((x & 0x7) << 1) - #define STMPE_TS_NAME "stmpe-ts" #define XY_MASK 0xfff @@ -213,9 +202,10 @@ static int stmpe_init_hw(struct stmpe_touch *ts) return ret; } - adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) | - REF_SEL(ts->ref_sel); - adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff); + adc_ctrl1 = STMPE_SAMPLE_TIME(ts->sample_time) | + STMPE_MOD_12B(ts->mod_12b) | STMPE_REF_SEL(ts->ref_sel); + adc_ctrl1_mask = STMPE_SAMPLE_TIME(0xff) | STMPE_MOD_12B(0xff) | + STMPE_REF_SEL(0xff); ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1, adc_ctrl1_mask, adc_ctrl1); @@ -225,15 +215,17 @@ static int stmpe_init_hw(struct stmpe_touch *ts) } ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2, - ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq)); + STMPE_ADC_FREQ(0xff), STMPE_ADC_FREQ(ts->adc_freq)); if (ret) { dev_err(dev, "Could not setup ADC\n"); return ret; } - tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) | - SETTLING(ts->settling); - tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff); + tsc_cfg = STMPE_AVE_CTRL(ts->ave_ctrl) | + STMPE_DET_DELAY(ts->touch_det_delay) | + STMPE_SETTLING(ts->settling); + tsc_cfg_mask = STMPE_AVE_CTRL(0xff) | STMPE_DET_DELAY(0xff) | + STMPE_SETTLING(0xff); ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg); if (ret) { @@ -242,14 +234,14 @@ static int stmpe_init_hw(struct stmpe_touch *ts) } ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z, - FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z)); + STMPE_FRACTION_Z(0xff), STMPE_FRACTION_Z(ts->fraction_z)); if (ret) { dev_err(dev, "Could not config touch\n"); return ret; } ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE, - I_DRIVE(0xff), I_DRIVE(ts->i_drive)); + STMPE_I_DRIVE(0xff), STMPE_I_DRIVE(ts->i_drive)); if (ret) { dev_err(dev, "Could not config touch\n"); return ret; @@ -263,7 +255,7 @@ static int stmpe_init_hw(struct stmpe_touch *ts) } ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL, - OP_MODE(0xff), OP_MODE(OP_MOD_XYZ)); + STMPE_OP_MODE(0xff), STMPE_OP_MODE(OP_MOD_XYZ)); if (ret) { dev_err(dev, "Could not set mode\n"); return ret; diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 4a827af17e59..c0353f6431f9 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -10,6 +10,17 @@ #include +#define STMPE_SAMPLE_TIME(x) ((x & 0xf) << 4) +#define STMPE_MOD_12B(x) ((x & 0x1) << 3) +#define STMPE_REF_SEL(x) ((x & 0x1) << 1) +#define STMPE_ADC_FREQ(x) (x & 0x3) +#define STMPE_AVE_CTRL(x) ((x & 0x3) << 6) +#define STMPE_DET_DELAY(x) ((x & 0x7) << 3) +#define STMPE_SETTLING(x) (x & 0x7) +#define STMPE_FRACTION_Z(x) (x & 0x7) +#define STMPE_I_DRIVE(x) (x & 0x1) +#define STMPE_OP_MODE(x) ((x & 0x7) << 1) + struct device; struct regulator; From 6377cfa3b857ced301f2079ac97de6c19057ab65 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 21 Dec 2018 14:46:32 +0100 Subject: [PATCH 0286/4277] mfd: stmpe: Preparations for STMPE ADC driver This prepares the MFD for the STMPE ADC driver. This commit introduces devicetree settings that are used by the ADC and adds an init function. Common ADC settings that are shared with the touchscreen driver can now reside in the overlying MFD. Signed-off-by: Stefan Agner Signed-off-by: Max Krummenacher Signed-off-by: Philippe Schenker Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 3 +- drivers/mfd/stmpe.c | 68 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/stmpe.h | 10 ++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8c5dfdce4326..bba159e8eaa4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1204,7 +1204,7 @@ config MFD_STMPE Currently supported devices are: - STMPE811: GPIO, Touchscreen + STMPE811: GPIO, Touchscreen, ADC STMPE1601: GPIO, Keypad STMPE1801: GPIO, Keypad STMPE2401: GPIO, Keypad @@ -1217,6 +1217,7 @@ config MFD_STMPE GPIO: stmpe-gpio Keypad: stmpe-keypad Touchscreen: stmpe-ts + ADC: stmpe-adc menu "STMicroelectronics STMPE Interface Drivers" depends on MFD_STMPE diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 566caca4efd8..f582531a8f3e 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -463,6 +463,28 @@ static const struct mfd_cell stmpe_ts_cell = { .num_resources = ARRAY_SIZE(stmpe_ts_resources), }; +/* + * ADC (STMPE811) + */ + +static struct resource stmpe_adc_resources[] = { + { + .name = "STMPE_TEMP_SENS", + .flags = IORESOURCE_IRQ, + }, + { + .name = "STMPE_ADC", + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct mfd_cell stmpe_adc_cell = { + .name = "stmpe-adc", + .of_compatible = "st,stmpe-adc", + .resources = stmpe_adc_resources, + .num_resources = ARRAY_SIZE(stmpe_adc_resources), +}; + /* * STMPE811 or STMPE610 */ @@ -497,6 +519,11 @@ static struct stmpe_variant_block stmpe811_blocks[] = { .irq = STMPE811_IRQ_TOUCH_DET, .block = STMPE_BLOCK_TOUCHSCREEN, }, + { + .cell = &stmpe_adc_cell, + .irq = STMPE811_IRQ_TEMP_SENS, + .block = STMPE_BLOCK_ADC, + }, }; static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks, @@ -517,6 +544,35 @@ static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks, enable ? 0 : mask); } +int stmpe811_adc_common_init(struct stmpe *stmpe) +{ + int ret; + u8 adc_ctrl1, adc_ctrl1_mask; + + adc_ctrl1 = STMPE_SAMPLE_TIME(stmpe->sample_time) | + STMPE_MOD_12B(stmpe->mod_12b) | + STMPE_REF_SEL(stmpe->ref_sel); + adc_ctrl1_mask = STMPE_SAMPLE_TIME(0xff) | STMPE_MOD_12B(0xff) | + STMPE_REF_SEL(0xff); + + ret = stmpe_set_bits(stmpe, STMPE811_REG_ADC_CTRL1, + adc_ctrl1_mask, adc_ctrl1); + if (ret) { + dev_err(stmpe->dev, "Could not setup ADC\n"); + return ret; + } + + ret = stmpe_set_bits(stmpe, STMPE811_REG_ADC_CTRL2, + STMPE_ADC_FREQ(0xff), STMPE_ADC_FREQ(stmpe->adc_freq)); + if (ret) { + dev_err(stmpe->dev, "Could not setup ADC\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(stmpe811_adc_common_init); + static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block) { /* 0 for touchscreen, 1 for GPIO */ @@ -1325,6 +1381,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) struct device_node *np = ci->dev->of_node; struct stmpe *stmpe; int ret; + u32 val; pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1342,6 +1399,15 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) mutex_init(&stmpe->irq_lock); mutex_init(&stmpe->lock); + if (!of_property_read_u32(np, "st,sample-time", &val)) + stmpe->sample_time = val; + if (!of_property_read_u32(np, "st,mod-12b", &val)) + stmpe->mod_12b = val; + if (!of_property_read_u32(np, "st,ref-sel", &val)) + stmpe->ref_sel = val; + if (!of_property_read_u32(np, "st,adc-freq", &val)) + stmpe->adc_freq = val; + stmpe->dev = ci->dev; stmpe->client = ci->client; stmpe->pdata = pdata; @@ -1433,6 +1499,8 @@ int stmpe_remove(struct stmpe *stmpe) if (!IS_ERR(stmpe->vcc)) regulator_disable(stmpe->vcc); + __stmpe_disable(stmpe, STMPE_BLOCK_ADC); + mfd_remove_devices(stmpe->dev); return 0; diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index c0353f6431f9..07f55aac9390 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -21,6 +21,9 @@ #define STMPE_I_DRIVE(x) (x & 0x1) #define STMPE_OP_MODE(x) ((x & 0x7) << 1) +#define STMPE811_REG_ADC_CTRL1 0x20 +#define STMPE811_REG_ADC_CTRL2 0x21 + struct device; struct regulator; @@ -134,6 +137,12 @@ struct stmpe { u8 ier[2]; u8 oldier[2]; struct stmpe_platform_data *pdata; + + /* For devices that use an ADC */ + u8 sample_time; + u8 mod_12b; + u8 ref_sel; + u8 adc_freq; }; extern int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 data); @@ -147,6 +156,7 @@ extern int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block); extern int stmpe_enable(struct stmpe *stmpe, unsigned int blocks); extern int stmpe_disable(struct stmpe *stmpe, unsigned int blocks); +extern int stmpe811_adc_common_init(struct stmpe *stmpe); #define STMPE_GPIO_NOREQ_811_TOUCH (0xf0) From 88f29d0f2c8f0d8097dfca9fc3619dfe893e0b15 Mon Sep 17 00:00:00 2001 From: Philippe Schenker Date: Fri, 21 Dec 2018 14:46:33 +0100 Subject: [PATCH 0287/4277] Input: stmpe-ts: preparations for STMPE ADC driver This patch removes common ADC settings in favor to use stmpe811_adc_common_init that is present in MFD. This is necessary in preparation for the stmpe-adc driver, because those two drivers have common settings for the ADC. Signed-off-by: Philippe Schenker Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/touchscreen/stmpe-ts.c | 42 +++++----------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index c5d9006588a2..cf9c9aa39f6e 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -30,8 +30,6 @@ * with touchscreen controller */ #define STMPE_REG_INT_STA 0x0B -#define STMPE_REG_ADC_CTRL1 0x20 -#define STMPE_REG_ADC_CTRL2 0x21 #define STMPE_REG_TSC_CTRL 0x40 #define STMPE_REG_TSC_CFG 0x41 #define STMPE_REG_FIFO_TH 0x4A @@ -58,15 +56,6 @@ * @idev: registered input device * @work: a work item used to scan the device * @dev: a pointer back to the MFD cell struct device* - * @sample_time: ADC converstion time in number of clock. - * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, - * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks), - * recommended is 4. - * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) - * @ref_sel: ADC reference source - * (0 -> internal reference, 1 -> external reference) - * @adc_freq: ADC Clock speed - * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) * @ave_ctrl: Sample average control * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples) * @touch_det_delay: Touch detect interrupt delay @@ -88,10 +77,6 @@ struct stmpe_touch { struct input_dev *idev; struct delayed_work work; struct device *dev; - u8 sample_time; - u8 mod_12b; - u8 ref_sel; - u8 adc_freq; u8 ave_ctrl; u8 touch_det_delay; u8 settling; @@ -192,7 +177,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) static int stmpe_init_hw(struct stmpe_touch *ts) { int ret; - u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask; + u8 tsc_cfg, tsc_cfg_mask; struct stmpe *stmpe = ts->stmpe; struct device *dev = ts->dev; @@ -202,22 +187,9 @@ static int stmpe_init_hw(struct stmpe_touch *ts) return ret; } - adc_ctrl1 = STMPE_SAMPLE_TIME(ts->sample_time) | - STMPE_MOD_12B(ts->mod_12b) | STMPE_REF_SEL(ts->ref_sel); - adc_ctrl1_mask = STMPE_SAMPLE_TIME(0xff) | STMPE_MOD_12B(0xff) | - STMPE_REF_SEL(0xff); - - ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1, - adc_ctrl1_mask, adc_ctrl1); + ret = stmpe811_adc_common_init(stmpe); if (ret) { - dev_err(dev, "Could not setup ADC\n"); - return ret; - } - - ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2, - STMPE_ADC_FREQ(0xff), STMPE_ADC_FREQ(ts->adc_freq)); - if (ret) { - dev_err(dev, "Could not setup ADC\n"); + stmpe_disable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC); return ret; } @@ -295,13 +267,13 @@ static void stmpe_ts_get_platform_info(struct platform_device *pdev, if (np) { if (!of_property_read_u32(np, "st,sample-time", &val)) - ts->sample_time = val; + ts->stmpe->sample_time = val; if (!of_property_read_u32(np, "st,mod-12b", &val)) - ts->mod_12b = val; + ts->stmpe->mod_12b = val; if (!of_property_read_u32(np, "st,ref-sel", &val)) - ts->ref_sel = val; + ts->stmpe->ref_sel = val; if (!of_property_read_u32(np, "st,adc-freq", &val)) - ts->adc_freq = val; + ts->stmpe->adc_freq = val; if (!of_property_read_u32(np, "st,ave-ctrl", &val)) ts->ave_ctrl = val; if (!of_property_read_u32(np, "st,touch-det-delay", &val)) From 9f3d084965a5544f096d4f769e274d4c2892079b Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 21 Dec 2018 14:46:34 +0100 Subject: [PATCH 0288/4277] iio: adc: add STMPE ADC driver using IIO framework This adds an ADC driver for the STMPE device using the industrial input/output interface. The driver supports raw reading of values. The driver depends on the MFD STMPE driver. If the touchscreen block is enabled too, only four of the 8 ADC channels are available. Signed-off-by: Stefan Agner Signed-off-by: Max Krummenacher Signed-off-by: Philippe Schenker Reviewed-by: Jonathan Cameron Signed-off-by: Lee Jones --- drivers/iio/adc/Kconfig | 7 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/stmpe-adc.c | 363 ++++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/iio/adc/stmpe-adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7a3ca4ec0cb7..5b72e2963153 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -755,6 +755,13 @@ config STM32_DFSDM_ADC This driver can also be built as a module. If so, the module will be called stm32-dfsdm-adc. +config STMPE_ADC + tristate "STMicroelectronics STMPE ADC driver" + depends on OF && MFD_STMPE + help + Say yes here to build support for ST Microelectronics STMPE + built-in ADC block (stmpe811). + config STX104 tristate "Apex Embedded Systems STX104 driver" depends on PC104 && X86 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 07df37f621bd..6d4e3149ac15 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o +obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c new file mode 100644 index 000000000000..37f4b74a5d32 --- /dev/null +++ b/drivers/iio/adc/stmpe-adc.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * STMicroelectronics STMPE811 IIO ADC Driver + * + * 4 channel, 10/12-bit ADC + * + * Copyright (C) 2013-2018 Toradex AG + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STMPE_REG_INT_STA 0x0B +#define STMPE_REG_ADC_INT_EN 0x0E +#define STMPE_REG_ADC_INT_STA 0x0F + +#define STMPE_REG_ADC_CTRL1 0x20 +#define STMPE_REG_ADC_CTRL2 0x21 +#define STMPE_REG_ADC_CAPT 0x22 +#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel)) + +#define STMPE_REG_TEMP_CTRL 0x60 +#define STMPE_TEMP_CTRL_ENABLE BIT(0) +#define STMPE_TEMP_CTRL_ACQ BIT(1) +#define STMPE_TEMP_CTRL_THRES_EN BIT(3) +#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \ + STMPE_TEMP_CTRL_ACQ | \ + STMPE_TEMP_CTRL_THRES_EN) +#define STMPE_REG_TEMP_DATA 0x61 +#define STMPE_REG_TEMP_TH 0x63 +#define STMPE_ADC_LAST_NR 7 +#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1) + +#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff) + +#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000) + +struct stmpe_adc { + struct stmpe *stmpe; + struct clk *clk; + struct device *dev; + struct mutex lock; + + /* We are allocating plus one for the temperature channel */ + struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2]; + + struct completion completion; + + u8 channel; + u32 value; +}; + +static int stmpe_read_voltage(struct stmpe_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + long ret; + + mutex_lock(&info->lock); + + info->channel = (u8)chan->channel; + + if (info->channel > STMPE_ADC_LAST_NR) { + mutex_unlock(&info->lock); + return -EINVAL; + } + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN, + STMPE_ADC_CH(info->channel)); + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT, + STMPE_ADC_CH(info->channel)); + + *val = info->value; + + ret = wait_for_completion_interruptible_timeout + (&info->completion, STMPE_ADC_TIMEOUT); + + if (ret <= 0) { + mutex_unlock(&info->lock); + if (ret == 0) + return -ETIMEDOUT; + else + return ret; + } + + *val = info->value; + + mutex_unlock(&info->lock); + + return 0; +} + +static int stmpe_read_temp(struct stmpe_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + long ret; + + mutex_lock(&info->lock); + + info->channel = (u8)chan->channel; + + if (info->channel != STMPE_TEMP_CHANNEL) { + mutex_unlock(&info->lock); + return -EINVAL; + } + + stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL, + STMPE_START_ONE_TEMP_CONV); + + ret = wait_for_completion_interruptible_timeout + (&info->completion, STMPE_ADC_TIMEOUT); + + if (ret <= 0) { + mutex_unlock(&info->lock); + if (ret == 0) + return -ETIMEDOUT; + else + return ret; + } + + /* + * absolute temp = +V3.3 * value /7.51 [K] + * scale to [milli °C] + */ + *val = ((449960l * info->value) / 1024l) - 273150; + + mutex_unlock(&info->lock); + + return 0; +} + +static int stmpe_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct stmpe_adc *info = iio_priv(indio_dev); + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + + switch (chan->type) { + case IIO_VOLTAGE: + ret = stmpe_read_voltage(info, chan, val); + break; + + case IIO_TEMP: + ret = stmpe_read_temp(info, chan, val); + break; + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 3300; + *val2 = info->stmpe->mod_12b ? 12 : 10; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +static irqreturn_t stmpe_adc_isr(int irq, void *dev_id) +{ + struct stmpe_adc *info = (struct stmpe_adc *)dev_id; + u16 data; + + if (info->channel > STMPE_TEMP_CHANNEL) + return IRQ_NONE; + + if (info->channel <= STMPE_ADC_LAST_NR) { + int int_sta; + + int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA); + + /* Is the interrupt relevant */ + if (!(int_sta & STMPE_ADC_CH(info->channel))) + return IRQ_NONE; + + /* Read value */ + stmpe_block_read(info->stmpe, + STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data); + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta); + } else if (info->channel == STMPE_TEMP_CHANNEL) { + /* Read value */ + stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2, + (u8 *) &data); + } + + info->value = (u32) be16_to_cpu(data); + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info stmpe_adc_iio_info = { + .read_raw = &stmpe_read_raw, +}; + +static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan) +{ + ics->type = IIO_VOLTAGE; + ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + ics->indexed = 1; + ics->channel = chan; +} + +static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan) +{ + ics->type = IIO_TEMP; + ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED); + ics->indexed = 1; + ics->channel = chan; +} + +static int stmpe_adc_init_hw(struct stmpe_adc *adc) +{ + int ret; + struct stmpe *stmpe = adc->stmpe; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC); + if (ret) { + dev_err(stmpe->dev, "Could not enable clock for ADC\n"); + return ret; + } + + ret = stmpe811_adc_common_init(stmpe); + if (ret) { + stmpe_disable(stmpe, STMPE_BLOCK_ADC); + return ret; + } + + /* use temp irq for each conversion completion */ + stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0); + stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0); + + return 0; +} + +static int stmpe_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct stmpe_adc *info; + struct device_node *np; + u32 norequest_mask = 0; + int irq_temp, irq_adc; + int num_chan = 0; + int i = 0; + int ret; + + irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC"); + if (irq_adc < 0) + return irq_adc; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + mutex_init(&info->lock); + + init_completion(&info->completion); + ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL, + stmpe_adc_isr, IRQF_ONESHOT, + "stmpe-adc", info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", + irq_adc); + return ret; + } + + irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS"); + if (irq_temp >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL, + stmpe_adc_isr, IRQF_ONESHOT, + "stmpe-adc", info); + if (ret < 0) + dev_warn(&pdev->dev, "failed requesting irq for" + " temp sensor, irq = %d\n", irq_temp); + } + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &stmpe_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + info->stmpe = dev_get_drvdata(pdev->dev.parent); + + np = pdev->dev.of_node; + + if (!np) + dev_err(&pdev->dev, "no device tree node found\n"); + + of_property_read_u32(np, "st,norequest-mask", &norequest_mask); + + for_each_clear_bit(i, (unsigned long *) &norequest_mask, + (STMPE_ADC_LAST_NR + 1)) { + stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i); + num_chan++; + } + stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i); + num_chan++; + indio_dev->channels = info->stmpe_adc_iio_channels; + indio_dev->num_channels = num_chan; + + ret = stmpe_adc_init_hw(info); + if (ret) + return ret; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static int __maybe_unused stmpe_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stmpe_adc *info = iio_priv(indio_dev); + + stmpe_adc_init_hw(info); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume); + +static struct platform_driver stmpe_adc_driver = { + .probe = stmpe_adc_probe, + .driver = { + .name = "stmpe-adc", + .pm = &stmpe_adc_pm_ops, + }, +}; + +module_platform_driver(stmpe_adc_driver); + +MODULE_AUTHOR("Stefan Agner "); +MODULE_DESCRIPTION("STMPEXXX ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stmpe-adc"); From 81cdab79818988d27d8aeb162b7988c9e6dde936 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 21 Dec 2018 14:46:35 +0100 Subject: [PATCH 0289/4277] iio: adc: add STMPE ADC devicetree bindings This adds the devicetree bindings for the STMPE ADC. This also corrects a typo in st,sample-time it is rather "6 -> 124 clocks" according to the datasheet and not 144. We need to use the naming stmpe_adc in devicetree because this is given by the mfd device. Signed-off-by: Stefan Agner Signed-off-by: Max Krummenacher Signed-off-by: Philippe Schenker Reviewed-by: Jonathan Cameron Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- .../devicetree/bindings/iio/adc/stmpe-adc.txt | 21 +++++ .../bindings/input/touchscreen/stmpe.txt | 88 +++++++++++++------ .../devicetree/bindings/mfd/stmpe.txt | 14 +++ 3 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/stmpe-adc.txt diff --git a/Documentation/devicetree/bindings/iio/adc/stmpe-adc.txt b/Documentation/devicetree/bindings/iio/adc/stmpe-adc.txt new file mode 100644 index 000000000000..480e66422625 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/stmpe-adc.txt @@ -0,0 +1,21 @@ +STMPE ADC driver +---------------- + +Required properties: + - compatible: "st,stmpe-adc" + +Optional properties: +Note that the ADC is shared with the STMPE touchscreen. ADC related settings +have to be done in the mfd. +- st,norequest-mask: bitmask specifying which ADC channels should _not_ be + requestable due to different usage (e.g. touch) + +Node name must be stmpe_adc and should be child node of stmpe node to +which it belongs. + +Example: + + stmpe_adc { + compatible = "st,stmpe-adc"; + st,norequest-mask = <0x0F>; /* dont use ADC CH3-0 */ + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt index bf66a55a7de5..c549924603d2 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt @@ -5,24 +5,6 @@ Required properties: - compatible: "st,stmpe-ts" Optional properties: -- st,sample-time : ADC conversion time in number of clock. - 0 -> 36 clocks - 1 -> 44 clocks - 2 -> 56 clocks - 3 -> 64 clocks - 4 -> 80 clocks (recommended) - 5 -> 96 clocks - 6 -> 144 clocks -- st,mod-12b : ADC Bit mode - 0 -> 10bit ADC - 1 -> 12bit ADC -- st,ref-sel : ADC reference source - 0 -> internal - 1 -> external -- st,adc-freq : ADC Clock speed - 0 -> 1.625 MHz - 1 -> 3.25 MHz - 2 || 3 -> 6.5 MHz - st,ave-ctrl : Sample average control 0 -> 1 sample 1 -> 2 samples @@ -52,20 +34,76 @@ Optional properties: 0 -> 20 mA (typical 35mA max) 1 -> 50 mA (typical 80 mA max) +Optional properties common with MFD (deprecated): + - st,sample-time : ADC conversion time in number of clock. + 0 -> 36 clocks + 1 -> 44 clocks + 2 -> 56 clocks + 3 -> 64 clocks + 4 -> 80 clocks (recommended) + 5 -> 96 clocks + 6 -> 124 clocks + - st,mod-12b : ADC Bit mode + 0 -> 10bit ADC + 1 -> 12bit ADC + - st,ref-sel : ADC reference source + 0 -> internal + 1 -> external + - st,adc-freq : ADC Clock speed + 0 -> 1.625 MHz + 1 -> 3.25 MHz + 2 || 3 -> 6.5 MHz + Node name must be stmpe_touchscreen and should be child node of stmpe node to which it belongs. +Note that common ADC settings of stmpe_touchscreen (child) will take precedence +over the settings done in MFD. + Example: +stmpe811@41 { + compatible = "st,stmpe811"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_touch_int>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x41>; + interrupts = <10 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio4>; + interrupt-controller; + id = <0>; + blocks = <0x5>; + irq-trigger = <0x1>; + /* Common ADC settings */ + /* 3.25 MHz ADC clock speed */ + st,adc-freq = <1>; + /* 12-bit ADC */ + st,mod-12b = <1>; + /* internal ADC reference */ + st,ref-sel = <0>; + /* ADC converstion time: 80 clocks */ + st,sample-time = <4>; + stmpe_touchscreen { compatible = "st,stmpe-ts"; - st,sample-time = <4>; - st,mod-12b = <1>; - st,ref-sel = <0>; - st,adc-freq = <1>; - st,ave-ctrl = <1>; - st,touch-det-delay = <2>; - st,settling = <2>; + reg = <0>; + /* 8 sample average control */ + st,ave-ctrl = <3>; + /* 5 ms touch detect interrupt delay */ + st,touch-det-delay = <5>; + /* 1 ms panel driver settling time */ + st,settling = <3>; + /* 7 length fractional part in z */ st,fraction-z = <7>; + /* + * 50 mA typical 80 mA max touchscreen drivers + * current limit value + */ st,i-drive = <1>; }; + stmpe_adc { + compatible = "st,stmpe-adc"; + st,norequest-mask = <0x0F>; + }; +}; diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index a46e7177195d..d4408a417193 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -14,6 +14,20 @@ Optional properties: - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 - irq-gpio : If present, which GPIO to use for event IRQ +Optional properties for devices with touch and ADC (STMPE811|STMPE610): + - st,sample-time : ADC conversion time in number of clock. + 0 -> 36 clocks 4 -> 80 clocks (recommended) + 1 -> 44 clocks 5 -> 96 clocks + 2 -> 56 clocks 6 -> 124 clocks + 3 -> 64 clocks + - st,mod-12b : ADC Bit mode + 0 -> 10bit ADC 1 -> 12bit ADC + - st,ref-sel : ADC reference source + 0 -> internal 1 -> external + - st,adc-freq : ADC Clock speed + 0 -> 1.625 MHz 2 || 3 -> 6.5 MHz + 1 -> 3.25 MHz + Example: stmpe1601: stmpe1601@40 { From 4f821c1002cb236f14d0b4572b0b98fe800178a8 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:44 +0300 Subject: [PATCH 0290/4277] iommu/tegra: gart: Remove pr_fmt and clean up includes Remove unneeded headers inclusion and sort the headers in alphabet order. Remove pr_fmt macro since there is no pr_*() in the code and it doesn't affect dev_*() functions. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index da6a4e357b2b..ff75cf60117b 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -19,22 +19,15 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ -#define pr_fmt(fmt) "%s(): " fmt, __func__ - #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include - -#include +#include +#include +#include +#include +#include +#include /* bitmap of the page sizes currently supported */ #define GART_IOMMU_PGSIZES (SZ_4K) From ae95c46dbed56af0e1d106865801bf06e1762c2c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:45 +0300 Subject: [PATCH 0291/4277] iommu/tegra: gart: Clean up driver probe errors handling Properly clean up allocated resources on the drivers probe failure and remove unneeded checks. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index ff75cf60117b..1cd470b2beea 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -411,9 +411,6 @@ static int tegra_gart_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - if (gart_handle) - return -EIO; - BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT); /* the GART memory aperture is required */ @@ -448,8 +445,7 @@ static int tegra_gart_probe(struct platform_device *pdev) ret = iommu_device_register(&gart->iommu); if (ret) { dev_err(dev, "Failed to register IOMMU\n"); - iommu_device_sysfs_remove(&gart->iommu); - return ret; + goto remove_sysfs; } gart->dev = &pdev->dev; @@ -463,7 +459,8 @@ static int tegra_gart_probe(struct platform_device *pdev) gart->savedata = vmalloc(array_size(sizeof(u32), gart->page_count)); if (!gart->savedata) { dev_err(dev, "failed to allocate context save area\n"); - return -ENOMEM; + ret = -ENOMEM; + goto unregister_iommu; } platform_set_drvdata(pdev, gart); @@ -472,6 +469,13 @@ static int tegra_gart_probe(struct platform_device *pdev) gart_handle = gart; return 0; + +unregister_iommu: + iommu_device_unregister(&gart->iommu); +remove_sysfs: + iommu_device_sysfs_remove(&gart->iommu); + + return ret; } static const struct dev_pm_ops tegra_gart_pm_ops = { From 4b6f0ea384f4a39c07fad556a9b4d619759de6c5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:46 +0300 Subject: [PATCH 0292/4277] iommu/tegra: gart: Ignore devices without IOMMU phandle in DT GART can't handle all devices, hence ignore devices that aren't related to GART. IOMMU phandle must be explicitly assign to devices in the device tree. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 1cd470b2beea..37a76388ff7e 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -345,8 +345,12 @@ static bool gart_iommu_capable(enum iommu_cap cap) static int gart_iommu_add_device(struct device *dev) { - struct iommu_group *group = iommu_group_get_for_dev(dev); + struct iommu_group *group; + if (!dev->iommu_fwspec) + return -ENODEV; + + group = iommu_group_get_for_dev(dev); if (IS_ERR(group)) return PTR_ERR(group); @@ -363,6 +367,12 @@ static void gart_iommu_remove_device(struct device *dev) iommu_device_unlink(&gart_handle->iommu, dev); } +static int gart_iommu_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + return 0; +} + static const struct iommu_ops gart_iommu_ops = { .capable = gart_iommu_capable, .domain_alloc = gart_iommu_domain_alloc, @@ -376,6 +386,7 @@ static const struct iommu_ops gart_iommu_ops = { .unmap = gart_iommu_unmap, .iova_to_phys = gart_iommu_iova_to_phys, .pgsize_bitmap = GART_IOMMU_PGSIZES, + .of_xlate = gart_iommu_of_xlate, }; static int tegra_gart_suspend(struct device *dev) @@ -441,6 +452,7 @@ static int tegra_gart_probe(struct platform_device *pdev) } iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); + iommu_device_set_fwnode(&gart->iommu, dev->fwnode); ret = iommu_device_register(&gart->iommu); if (ret) { From 1d7ae53b152dbc5ba0a4f6a83ecc42ac66f52d11 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:47 +0300 Subject: [PATCH 0293/4277] iommu: Introduce iotlb_sync_map callback Introduce iotlb_sync_map() callback that is invoked in the end of iommu_map(). This new callback allows IOMMU drivers to avoid syncing after mapping of each contiguous chunk and sync only when the whole mapping is completed, optimizing performance of the mapping operation. Signed-off-by: Dmitry Osipenko Reviewed-by: Robin Murphy Reviewed-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 8 ++++++-- include/linux/iommu.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 3ed4db334341..ed0e63f2cd9b 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1585,13 +1585,14 @@ static size_t iommu_pgsize(struct iommu_domain *domain, int iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { + const struct iommu_ops *ops = domain->ops; unsigned long orig_iova = iova; unsigned int min_pagesz; size_t orig_size = size; phys_addr_t orig_paddr = paddr; int ret = 0; - if (unlikely(domain->ops->map == NULL || + if (unlikely(ops->map == NULL || domain->pgsize_bitmap == 0UL)) return -ENODEV; @@ -1620,7 +1621,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", iova, &paddr, pgsize); - ret = domain->ops->map(domain, iova, paddr, pgsize, prot); + ret = ops->map(domain, iova, paddr, pgsize, prot); if (ret) break; @@ -1629,6 +1630,9 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, size -= pgsize; } + if (ops->iotlb_sync_map) + ops->iotlb_sync_map(domain); + /* unroll mapping in case something went wrong */ if (ret) iommu_unmap(domain, orig_iova, orig_size - size); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index e90da6b6f3d1..477ef47c357c 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -201,6 +201,7 @@ struct iommu_ops { void (*flush_iotlb_all)(struct iommu_domain *domain); void (*iotlb_range_add)(struct iommu_domain *domain, unsigned long iova, size_t size); + void (*iotlb_sync_map)(struct iommu_domain *domain); void (*iotlb_sync)(struct iommu_domain *domain); phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); int (*add_device)(struct device *dev); From 2fc0ac180d0f160cdd5fc69407ca7eb47b3eff99 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:48 +0300 Subject: [PATCH 0294/4277] iommu/tegra: gart: Optimize mapping / unmapping performance Currently GART writes one page entry at a time. More optimal would be to aggregate the writes and flush BUS buffer in the end, this gives map/unmap 10-40% performance boost (depending on size of mapping) in comparison to flushing after each page entry update. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 37a76388ff7e..835fea461c59 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -290,7 +290,6 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, } } gart_set_pte(gart, iova, GART_PTE(pfn)); - FLUSH_GART_REGS(gart); spin_unlock_irqrestore(&gart->pte_lock, flags); return 0; } @@ -307,7 +306,6 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, spin_lock_irqsave(&gart->pte_lock, flags); gart_set_pte(gart, iova, 0); - FLUSH_GART_REGS(gart); spin_unlock_irqrestore(&gart->pte_lock, flags); return bytes; } @@ -373,6 +371,14 @@ static int gart_iommu_of_xlate(struct device *dev, return 0; } +static void gart_iommu_sync(struct iommu_domain *domain) +{ + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; + + FLUSH_GART_REGS(gart); +} + static const struct iommu_ops gart_iommu_ops = { .capable = gart_iommu_capable, .domain_alloc = gart_iommu_domain_alloc, @@ -387,6 +393,8 @@ static const struct iommu_ops gart_iommu_ops = { .iova_to_phys = gart_iommu_iova_to_phys, .pgsize_bitmap = GART_IOMMU_PGSIZES, .of_xlate = gart_iommu_of_xlate, + .iotlb_sync_map = gart_iommu_sync, + .iotlb_sync = gart_iommu_sync, }; static int tegra_gart_suspend(struct device *dev) From 53f986accf34d4428b90b26086b9571ea653b920 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:49 +0300 Subject: [PATCH 0295/4277] dt-bindings: memory: tegra: Squash tegra20-gart into tegra20-mc Splitting GART and Memory Controller wasn't a good decision that was made back in the day. Given that the GART driver wasn't ever been used by anything in the kernel, we decided that it will be better to correct the mistakes of the past and merge two bindings into a single one. As a result there is a DT ABI change for the Memory Controller that allows not to break newer kernels using older DT and not to break older kernels using newer DT, that is done by changing the 'compatible' of the node to 'tegra20-mc-gart' and adding a new-required clock property. The new clock property also puts the tegra20-mc binding in line with the bindings of the later Tegra generations. Signed-off-by: Dmitry Osipenko Reviewed-by: Rob Herring Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- .../bindings/iommu/nvidia,tegra20-gart.txt | 14 ---------- .../memory-controllers/nvidia,tegra20-mc.txt | 27 +++++++++++++------ 2 files changed, 19 insertions(+), 22 deletions(-) delete mode 100644 Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt diff --git a/Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt b/Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt deleted file mode 100644 index 099d9362ebc1..000000000000 --- a/Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt +++ /dev/null @@ -1,14 +0,0 @@ -NVIDIA Tegra 20 GART - -Required properties: -- compatible: "nvidia,tegra20-gart" -- reg: Two pairs of cells specifying the physical address and size of - the memory controller registers and the GART aperture respectively. - -Example: - - gart { - compatible = "nvidia,tegra20-gart"; - reg = <0x7000f024 0x00000018 /* controller registers */ - 0x58000000 0x02000000>; /* GART aperture */ - }; diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-mc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-mc.txt index 7d60a50a4fa1..e55328237df4 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-mc.txt +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-mc.txt @@ -1,26 +1,37 @@ NVIDIA Tegra20 MC(Memory Controller) Required properties: -- compatible : "nvidia,tegra20-mc" -- reg : Should contain 2 register ranges(address and length); see the - example below. Note that the MC registers are interleaved with the - GART registers, and hence must be represented as multiple ranges. +- compatible : "nvidia,tegra20-mc-gart" +- reg : Should contain 2 register ranges: physical base address and length of + the controller's registers and the GART aperture respectively. +- clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names: Must include the following entries: + - mc: the module's clock input - interrupts : Should contain MC General interrupt. - #reset-cells : Should be 1. This cell represents memory client module ID. The assignments may be found in header file or in the TRM documentation. +- #iommu-cells: Should be 0. This cell represents the number of cells in an + IOMMU specifier needed to encode an address. GART supports only a single + address space that is shared by all devices, therefore no additional + information needed for the address encoding. Example: mc: memory-controller@7000f000 { - compatible = "nvidia,tegra20-mc"; - reg = <0x7000f000 0x024 - 0x7000f03c 0x3c4>; - interrupts = <0 77 0x04>; + compatible = "nvidia,tegra20-mc-gart"; + reg = <0x7000f000 0x400 /* controller registers */ + 0x58000000 0x02000000>; /* GART aperture */ + clocks = <&tegra_car TEGRA20_CLK_MC>; + clock-names = "mc"; + interrupts = ; #reset-cells = <1>; + #iommu-cells = <0>; }; video-codec@6001a000 { compatible = "nvidia,tegra20-vde"; ... resets = <&mc TEGRA20_MC_RESET_VDE>; + iommus = <&mc>; }; From 334175243cc6b97aa7e846e623cc500e1d56a39c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:50 +0300 Subject: [PATCH 0296/4277] ARM: dts: tegra20: Update Memory Controller node to the new binding Device tree binding of Memory Controller has been changed: GART has been squashed into the MC, there are a new mandatory clock and #iommu-cells properties, the compatible has been changed to 'tegra20-mc-gart'. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- arch/arm/boot/dts/tegra20.dtsi | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index dcad6d6128cf..8c942e60703e 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -616,17 +616,14 @@ }; mc: memory-controller@7000f000 { - compatible = "nvidia,tegra20-mc"; - reg = <0x7000f000 0x024 - 0x7000f03c 0x3c4>; + compatible = "nvidia,tegra20-mc-gart"; + reg = <0x7000f000 0x400 /* controller registers */ + 0x58000000 0x02000000>; /* GART aperture */ + clocks = <&tegra_car TEGRA20_CLK_MC>; + clock-names = "mc"; interrupts = ; #reset-cells = <1>; - }; - - iommu@7000f024 { - compatible = "nvidia,tegra20-gart"; - reg = <0x7000f024 0x00000018 /* controller registers */ - 0x58000000 0x02000000>; /* GART aperture */ + #iommu-cells = <0>; }; memory-controller@7000f400 { From be4dbdec2bab8635c7a41573668624ee13d83022 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:51 +0300 Subject: [PATCH 0297/4277] memory: tegra: Don't invoke Tegra30+ specific memory timing setup on Tegra20 This fixes irrelevant "tegra-mc 7000f000.memory-controller: no memory timings for RAM code 0 registered" warning message during of kernels boot-up on Tegra20. Fixes: a8d502fd3348 ("memory: tegra: Squash tegra20-mc into common tegra-mc driver") Signed-off-by: Dmitry Osipenko Acked-by: Jon Hunter Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 24afc36833bf..b99f3c620f6c 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -664,12 +664,13 @@ static int tegra_mc_probe(struct platform_device *pdev) } isr = tegra_mc_irq; - } - err = tegra_mc_setup_timings(mc); - if (err < 0) { - dev_err(&pdev->dev, "failed to setup timings: %d\n", err); - return err; + err = tegra_mc_setup_timings(mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to setup timings: %d\n", + err); + return err; + } } mc->irq = platform_get_irq(pdev, 0); From 96efa118c03648fdc76acad9ca8fe018a6be7145 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:52 +0300 Subject: [PATCH 0298/4277] memory: tegra: Adapt to Tegra20 device-tree binding changes The tegra20-mc device-tree binding has been changed, GART has been squashed into Memory Controller and now the clock property is mandatory for Tegra20, the DT compatible has been changed as well. Adapt driver to the DT changes. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.c | 21 ++++++++------------- drivers/memory/tegra/mc.h | 6 ------ include/soc/tegra/mc.h | 2 +- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index b99f3c620f6c..59db13287b47 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -51,7 +51,7 @@ static const struct of_device_id tegra_mc_of_match[] = { #ifdef CONFIG_ARCH_TEGRA_2x_SOC - { .compatible = "nvidia,tegra20-mc", .data = &tegra20_mc_soc }, + { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, #endif #ifdef CONFIG_ARCH_TEGRA_3x_SOC { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, @@ -638,24 +638,19 @@ static int tegra_mc_probe(struct platform_device *pdev) if (IS_ERR(mc->regs)) return PTR_ERR(mc->regs); + mc->clk = devm_clk_get(&pdev->dev, "mc"); + if (IS_ERR(mc->clk)) { + dev_err(&pdev->dev, "failed to get MC clock: %ld\n", + PTR_ERR(mc->clk)); + return PTR_ERR(mc->clk); + } + #ifdef CONFIG_ARCH_TEGRA_2x_SOC if (mc->soc == &tegra20_mc_soc) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - mc->regs2 = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mc->regs2)) - return PTR_ERR(mc->regs2); - isr = tegra20_mc_irq; } else #endif { - mc->clk = devm_clk_get(&pdev->dev, "mc"); - if (IS_ERR(mc->clk)) { - dev_err(&pdev->dev, "failed to get MC clock: %ld\n", - PTR_ERR(mc->clk)); - return PTR_ERR(mc->clk); - } - err = tegra_mc_setup_latency_allowance(mc); if (err < 0) { dev_err(&pdev->dev, "failed to setup latency allowance: %d\n", diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 01065f12ebeb..9856f085e487 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -26,18 +26,12 @@ static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) { - if (mc->regs2 && offset >= 0x24) - return readl(mc->regs2 + offset - 0x3c); - return readl(mc->regs + offset); } static inline void mc_writel(struct tegra_mc *mc, u32 value, unsigned long offset) { - if (mc->regs2 && offset >= 0x24) - return writel(value, mc->regs2 + offset - 0x3c); - writel(value, mc->regs + offset); } diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index b43f37fea096..db5bfdf589b4 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -144,7 +144,7 @@ struct tegra_mc_soc { struct tegra_mc { struct device *dev; struct tegra_smmu *smmu; - void __iomem *regs, *regs2; + void __iomem *regs; struct clk *clk; int irq; From b3bb6b858f2a60fe3ac0c3833084386f7dd420e4 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:53 +0300 Subject: [PATCH 0299/4277] memory: tegra: Read client ID on GART page fault With the device tree binding changes, now Memory Controller has access to GART registers. Hence it is now possible to read client ID on GART page fault to get information about what memory client causes the fault. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 59db13287b47..ce8cf81b55d7 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -38,6 +38,7 @@ #define MC_ERR_ADR 0x0c +#define MC_GART_ERROR_REQ 0x30 #define MC_DECERR_EMEM_OTHERS_STATUS 0x58 #define MC_SECURITY_VIOLATION_STATUS 0x74 @@ -575,8 +576,15 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data) break; case MC_INT_INVALID_GART_PAGE: - dev_err_ratelimited(mc->dev, "%s\n", error); - continue; + reg = MC_GART_ERROR_REQ; + value = mc_readl(mc, reg); + + id = (value >> 1) & mc->soc->client_id_mask; + desc = error_names[2]; + + if (value & BIT(0)) + direction = "write"; + break; case MC_INT_SECURITY_VIOLATION: reg = MC_SECURITY_VIOLATION_STATUS; From 59cd046f7f94543540d82216dbbfd8146db43b70 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:54 +0300 Subject: [PATCH 0300/4277] memory: tegra: Use of_device_get_match_data() There is no need to match device with the DT node since it was already matched, use of_device_get_match_data() helper to get the match-data. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index ce8cf81b55d7..55ecfb2d8cfd 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -619,23 +620,18 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data) static int tegra_mc_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct resource *res; struct tegra_mc *mc; void *isr; int err; - match = of_match_node(tegra_mc_of_match, pdev->dev.of_node); - if (!match) - return -ENODEV; - mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; platform_set_drvdata(pdev, mc); spin_lock_init(&mc->lock); - mc->soc = match->data; + mc->soc = of_device_get_match_data(&pdev->dev); mc->dev = &pdev->dev; /* length of MC tick in nanoseconds */ From 45594c683ef780f20f11b1e0018b933b6ff5d9a1 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:55 +0300 Subject: [PATCH 0301/4277] memory: tegra: Use relaxed versions of readl/writel There is no need for inserting of memory barriers to access registers of Memory Controller. Hence use the relaxed versions of the accessors. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 9856f085e487..887a3b07334f 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -26,13 +26,13 @@ static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) { - return readl(mc->regs + offset); + return readl_relaxed(mc->regs + offset); } static inline void mc_writel(struct tegra_mc *mc, u32 value, unsigned long offset) { - writel(value, mc->regs + offset); + writel_relaxed(value, mc->regs + offset); } extern const struct tegra_mc_reset_ops terga_mc_reset_ops_common; From ce2785a75dbca27375f3723f4e697a2a8dc096ee Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:56 +0300 Subject: [PATCH 0302/4277] iommu/tegra: gart: Integrate with Memory Controller driver The device-tree binding has been changed. There is no separate GART device anymore, it is squashed into the Memory Controller. Integrate GART module with the MC in a way it is done for the SMMU on Tegra30+. Signed-off-by: Dmitry Osipenko Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 1 + drivers/iommu/tegra-gart.c | 71 ++++++++++---------------------------- drivers/memory/tegra/mc.c | 43 +++++++++++++++++++++++ include/soc/tegra/mc.h | 25 ++++++++++++++ 4 files changed, 87 insertions(+), 53 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index d9a25715650e..83c099bb7288 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -282,6 +282,7 @@ config ROCKCHIP_IOMMU config TEGRA_IOMMU_GART bool "Tegra GART IOMMU Support" depends on ARCH_TEGRA_2x_SOC + depends on TEGRA_MC select IOMMU_API help Enables support for remapping discontiguous physical memory diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 835fea461c59..b35ffa312a83 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -19,16 +19,17 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ -#include #include #include #include #include -#include +#include #include #include #include +#include + /* bitmap of the page sizes currently supported */ #define GART_IOMMU_PGSIZES (SZ_4K) @@ -397,9 +398,8 @@ static const struct iommu_ops gart_iommu_ops = { .iotlb_sync = gart_iommu_sync, }; -static int tegra_gart_suspend(struct device *dev) +int tegra_gart_suspend(struct gart_device *gart) { - struct gart_device *gart = dev_get_drvdata(dev); unsigned long iova; u32 *data = gart->savedata; unsigned long flags; @@ -411,9 +411,8 @@ static int tegra_gart_suspend(struct device *dev) return 0; } -static int tegra_gart_resume(struct device *dev) +int tegra_gart_resume(struct gart_device *gart) { - struct gart_device *gart = dev_get_drvdata(dev); unsigned long flags; spin_lock_irqsave(&gart->pte_lock, flags); @@ -422,41 +421,33 @@ static int tegra_gart_resume(struct device *dev) return 0; } -static int tegra_gart_probe(struct platform_device *pdev) +struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) { struct gart_device *gart; - struct resource *res, *res_remap; + struct resource *res_remap; void __iomem *gart_regs; - struct device *dev = &pdev->dev; int ret; BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT); /* the GART memory aperture is required */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res || !res_remap) { + res_remap = platform_get_resource(to_platform_device(dev), + IORESOURCE_MEM, 1); + if (!res_remap) { dev_err(dev, "GART memory aperture expected\n"); - return -ENXIO; + return ERR_PTR(-ENXIO); } gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL); if (!gart) { dev_err(dev, "failed to allocate gart_device\n"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } - gart_regs = devm_ioremap(dev, res->start, resource_size(res)); - if (!gart_regs) { - dev_err(dev, "failed to remap GART registers\n"); - return -ENXIO; - } - - ret = iommu_device_sysfs_add(&gart->iommu, &pdev->dev, NULL, - dev_name(&pdev->dev)); + ret = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart"); if (ret) { dev_err(dev, "Failed to register IOMMU in sysfs\n"); - return ret; + return ERR_PTR(ret); } iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); @@ -468,7 +459,8 @@ static int tegra_gart_probe(struct platform_device *pdev) goto remove_sysfs; } - gart->dev = &pdev->dev; + gart->dev = dev; + gart_regs = mc->regs + GART_REG_BASE; spin_lock_init(&gart->pte_lock); spin_lock_init(&gart->client_lock); INIT_LIST_HEAD(&gart->client); @@ -483,46 +475,19 @@ static int tegra_gart_probe(struct platform_device *pdev) goto unregister_iommu; } - platform_set_drvdata(pdev, gart); do_gart_setup(gart, NULL); gart_handle = gart; - return 0; + return gart; unregister_iommu: iommu_device_unregister(&gart->iommu); remove_sysfs: iommu_device_sysfs_remove(&gart->iommu); - return ret; + return ERR_PTR(ret); } -static const struct dev_pm_ops tegra_gart_pm_ops = { - .suspend = tegra_gart_suspend, - .resume = tegra_gart_resume, -}; - -static const struct of_device_id tegra_gart_of_match[] = { - { .compatible = "nvidia,tegra20-gart", }, - { }, -}; - -static struct platform_driver tegra_gart_driver = { - .probe = tegra_gart_probe, - .driver = { - .name = "tegra-gart", - .pm = &tegra_gart_pm_ops, - .of_match_table = tegra_gart_of_match, - .suppress_bind_attrs = true, - }, -}; - -static int __init tegra_gart_init(void) -{ - return platform_driver_register(&tegra_gart_driver); -} -subsys_initcall(tegra_gart_init); - module_param(gart_debug, bool, 0644); MODULE_PARM_DESC(gart_debug, "Enable GART debugging"); diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 55ecfb2d8cfd..e684e234361a 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -702,13 +702,56 @@ static int tegra_mc_probe(struct platform_device *pdev) PTR_ERR(mc->smmu)); } + if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) { + mc->gart = tegra_gart_probe(&pdev->dev, mc); + if (IS_ERR(mc->gart)) { + dev_err(&pdev->dev, "failed to probe GART: %ld\n", + PTR_ERR(mc->gart)); + mc->gart = NULL; + } + } + return 0; } +static int tegra_mc_suspend(struct device *dev) +{ + struct tegra_mc *mc = dev_get_drvdata(dev); + int err; + + if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) { + err = tegra_gart_suspend(mc->gart); + if (err) + return err; + } + + return 0; +} + +static int tegra_mc_resume(struct device *dev) +{ + struct tegra_mc *mc = dev_get_drvdata(dev); + int err; + + if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) { + err = tegra_gart_resume(mc->gart); + if (err) + return err; + } + + return 0; +} + +static const struct dev_pm_ops tegra_mc_pm_ops = { + .suspend = tegra_mc_suspend, + .resume = tegra_mc_resume, +}; + static struct platform_driver tegra_mc_driver = { .driver = { .name = "tegra-mc", .of_match_table = tegra_mc_of_match, + .pm = &tegra_mc_pm_ops, .suppress_bind_attrs = true, }, .prevent_deferred_probe = true, diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index db5bfdf589b4..e489a028ec9f 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -9,6 +9,7 @@ #ifndef __SOC_TEGRA_MC_H__ #define __SOC_TEGRA_MC_H__ +#include #include #include @@ -77,6 +78,7 @@ struct tegra_smmu_soc { struct tegra_mc; struct tegra_smmu; +struct gart_device; #ifdef CONFIG_TEGRA_IOMMU_SMMU struct tegra_smmu *tegra_smmu_probe(struct device *dev, @@ -96,6 +98,28 @@ static inline void tegra_smmu_remove(struct tegra_smmu *smmu) } #endif +#ifdef CONFIG_TEGRA_IOMMU_GART +struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc); +int tegra_gart_suspend(struct gart_device *gart); +int tegra_gart_resume(struct gart_device *gart); +#else +static inline struct gart_device * +tegra_gart_probe(struct device *dev, struct tegra_mc *mc) +{ + return ERR_PTR(-ENODEV); +} + +static inline int tegra_gart_suspend(struct gart_device *gart) +{ + return -ENODEV; +} + +static inline int tegra_gart_resume(struct gart_device *gart) +{ + return -ENODEV; +} +#endif + struct tegra_mc_reset { const char *name; unsigned long id; @@ -144,6 +168,7 @@ struct tegra_mc_soc { struct tegra_mc { struct device *dev; struct tegra_smmu *smmu; + struct gart_device *gart; void __iomem *regs; struct clk *clk; int irq; From 568ece5bab24d41bec4d1610439277032840a133 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:57 +0300 Subject: [PATCH 0303/4277] memory: tegra: Do not try to probe SMMU on Tegra20 Tegra20 doesn't have SMMU. Move out checking of the SMMU presence from the SMMU driver into the Memory Controller driver. This change makes code consistent in regards to how GART/SMMU presence checking is performed. Signed-off-by: Dmitry Osipenko Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 4 ---- drivers/memory/tegra/mc.c | 6 ++++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 3a5c7dc6dc57..5182c7d6171e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -982,10 +982,6 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, u32 value; int err; - /* This can happen on Tegra20 which doesn't have an SMMU */ - if (!soc) - return NULL; - smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); if (!smmu) return ERR_PTR(-ENOMEM); diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index e684e234361a..3545868c51c0 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -695,11 +695,13 @@ static int tegra_mc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register reset controller: %d\n", err); - if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) { + if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); - if (IS_ERR(mc->smmu)) + if (IS_ERR(mc->smmu)) { dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", PTR_ERR(mc->smmu)); + mc->smmu = NULL; + } } if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) { From 33ea002aadd9cbf1836a13c71e424f113bce635a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:58 +0300 Subject: [PATCH 0304/4277] memory: tegra: Do not ask for IRQ sharing Memory Controller driver never shared IRQ with any other driver and very unlikely that it will. Hence there is no need to request IRQ sharing and the corresponding flag can be dropped safely. Signed-off-by: Dmitry Osipenko Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 3545868c51c0..570da2129fa6 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -682,7 +682,7 @@ static int tegra_mc_probe(struct platform_device *pdev) mc_writel(mc, mc->soc->intmask, MC_INTMASK); - err = devm_request_irq(&pdev->dev, mc->irq, isr, IRQF_SHARED, + err = devm_request_irq(&pdev->dev, mc->irq, isr, 0, dev_name(&pdev->dev), mc); if (err < 0) { dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, From f2dcded1be0d024870b5a4ef8e4a587857f303b4 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:38:59 +0300 Subject: [PATCH 0305/4277] memory: tegra: Clean up error messages Make all messages to start with a lower case and don't unnecessarily go over 80 chars in the code. Signed-off-by: Dmitry Osipenko Signed-off-by: Joerg Roedel --- drivers/memory/tegra/mc.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 570da2129fa6..0a53598d982f 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -163,7 +163,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, /* block clients DMA requests */ err = rst_ops->block_dma(mc, rst); if (err) { - dev_err(mc->dev, "Failed to block %s DMA: %d\n", + dev_err(mc->dev, "failed to block %s DMA: %d\n", rst->name, err); return err; } @@ -173,7 +173,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, /* wait for completion of the outstanding DMA requests */ while (!rst_ops->dma_idling(mc, rst)) { if (!retries--) { - dev_err(mc->dev, "Failed to flush %s DMA\n", + dev_err(mc->dev, "failed to flush %s DMA\n", rst->name); return -EBUSY; } @@ -186,7 +186,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, /* clear clients DMA requests sitting before arbitration */ err = rst_ops->hotreset_assert(mc, rst); if (err) { - dev_err(mc->dev, "Failed to hot reset %s: %d\n", + dev_err(mc->dev, "failed to hot reset %s: %d\n", rst->name, err); return err; } @@ -215,7 +215,7 @@ static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev, /* take out client from hot reset */ err = rst_ops->hotreset_deassert(mc, rst); if (err) { - dev_err(mc->dev, "Failed to deassert hot reset %s: %d\n", + dev_err(mc->dev, "failed to deassert hot reset %s: %d\n", rst->name, err); return err; } @@ -225,7 +225,7 @@ static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev, /* allow new DMA requests to proceed to arbitration */ err = rst_ops->unblock_dma(mc, rst); if (err) { - dev_err(mc->dev, "Failed to unblock %s DMA : %d\n", + dev_err(mc->dev, "failed to unblock %s DMA : %d\n", rst->name, err); return err; } @@ -657,7 +657,8 @@ static int tegra_mc_probe(struct platform_device *pdev) { err = tegra_mc_setup_latency_allowance(mc); if (err < 0) { - dev_err(&pdev->dev, "failed to setup latency allowance: %d\n", + dev_err(&pdev->dev, + "failed to setup latency allowance: %d\n", err); return err; } @@ -678,7 +679,7 @@ static int tegra_mc_probe(struct platform_device *pdev) return mc->irq; } - WARN(!mc->soc->client_id_mask, "Missing client ID mask for this SoC\n"); + WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); mc_writel(mc, mc->soc->intmask, MC_INTMASK); From c3086fad2755f5e446f6a69fda3b2a8c16a1b5ce Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:00 +0300 Subject: [PATCH 0306/4277] iommu/tegra: gart: Fix spinlock recursion Fix spinlock recursion bug that happens on IOMMU domain destruction if any of the allocated domains have devices attached to them. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index b35ffa312a83..a7a9400e0cd8 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -197,25 +197,33 @@ fail: return err; } -static void gart_iommu_detach_dev(struct iommu_domain *domain, - struct device *dev) +static void __gart_iommu_detach_dev(struct iommu_domain *domain, + struct device *dev) { struct gart_domain *gart_domain = to_gart_domain(domain); struct gart_device *gart = gart_domain->gart; struct gart_client *c; - spin_lock(&gart->client_lock); - list_for_each_entry(c, &gart->client, list) { if (c->dev == dev) { list_del(&c->list); devm_kfree(gart->dev, c); dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); - goto out; + return; } } - dev_err(gart->dev, "Couldn't find\n"); -out: + + dev_err(gart->dev, "Couldn't find %s to detach\n", dev_name(dev)); +} + +static void gart_iommu_detach_dev(struct iommu_domain *domain, + struct device *dev) +{ + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; + + spin_lock(&gart->client_lock); + __gart_iommu_detach_dev(domain, dev); spin_unlock(&gart->client_lock); } @@ -255,7 +263,7 @@ static void gart_iommu_domain_free(struct iommu_domain *domain) struct gart_client *c; list_for_each_entry(c, &gart->client, list) - gart_iommu_detach_dev(domain, c->dev); + __gart_iommu_detach_dev(domain, c->dev); } spin_unlock(&gart->client_lock); } From 8e924910dd07f1614fe534f8bdfdc17f7de3d68b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:01 +0300 Subject: [PATCH 0307/4277] iommu/tegra: gart: Fix NULL pointer dereference Fix NULL pointer dereference on IOMMU domain destruction that happens because clients list is being iterated unsafely and its elements are getting deleted during the iteration. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index a7a9400e0cd8..7fdd8b12efd5 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -260,9 +260,9 @@ static void gart_iommu_domain_free(struct iommu_domain *domain) if (gart) { spin_lock(&gart->client_lock); if (!list_empty(&gart->client)) { - struct gart_client *c; + struct gart_client *c, *tmp; - list_for_each_entry(c, &gart->client, list) + list_for_each_entry_safe(c, tmp, &gart->client, list) __gart_iommu_detach_dev(domain, c->dev); } spin_unlock(&gart->client_lock); From 7d849b7b40b9b3f574f50bbf44503b38e319fca5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:02 +0300 Subject: [PATCH 0308/4277] iommu/tegra: gart: Allow only one active domain at a time GART has a single address space that is shared by all devices, hence only one domain could be active at a time. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 7fdd8b12efd5..71ff22be9560 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -57,6 +57,7 @@ struct gart_device { spinlock_t pte_lock; /* for pagetable */ struct list_head client; spinlock_t client_lock; /* for client list */ + struct iommu_domain *active_domain; /* current active domain */ struct device *dev; struct iommu_device iommu; /* IOMMU Core handle */ @@ -186,6 +187,12 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain, goto fail; } } + if (gart->active_domain && gart->active_domain != domain) { + dev_err(gart->dev, "Only one domain can be active at a time\n"); + err = -EINVAL; + goto fail; + } + gart->active_domain = domain; list_add(&client->list, &gart->client); spin_unlock(&gart->client_lock); dev_dbg(gart->dev, "Attached %s\n", dev_name(dev)); @@ -208,6 +215,8 @@ static void __gart_iommu_detach_dev(struct iommu_domain *domain, if (c->dev == dev) { list_del(&c->list); devm_kfree(gart->dev, c); + if (list_empty(&gart->client)) + gart->active_domain = NULL; dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); return; } From 167d67d550b9e94207578679c3a6feef78449f38 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:03 +0300 Subject: [PATCH 0309/4277] iommu/tegra: gart: Don't use managed resources GART is a part of the Memory Controller driver that is always built-in, hence there is no benefit from the use of managed resources. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 71ff22be9560..2e0e6aff8f70 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -173,7 +173,7 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain, struct gart_client *client, *c; int err = 0; - client = devm_kzalloc(gart->dev, sizeof(*c), GFP_KERNEL); + client = kzalloc(sizeof(*c), GFP_KERNEL); if (!client) return -ENOMEM; client->dev = dev; @@ -199,7 +199,7 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain, return 0; fail: - devm_kfree(gart->dev, client); + kfree(client); spin_unlock(&gart->client_lock); return err; } @@ -214,7 +214,7 @@ static void __gart_iommu_detach_dev(struct iommu_domain *domain, list_for_each_entry(c, &gart->client, list) { if (c->dev == dev) { list_del(&c->list); - devm_kfree(gart->dev, c); + kfree(c); if (list_empty(&gart->client)) gart->active_domain = NULL; dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); @@ -455,7 +455,7 @@ struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) return ERR_PTR(-ENXIO); } - gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL); + gart = kzalloc(sizeof(*gart), GFP_KERNEL); if (!gart) { dev_err(dev, "failed to allocate gart_device\n"); return ERR_PTR(-ENOMEM); @@ -464,7 +464,7 @@ struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) ret = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart"); if (ret) { dev_err(dev, "Failed to register IOMMU in sysfs\n"); - return ERR_PTR(ret); + goto free_gart; } iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); @@ -502,6 +502,8 @@ unregister_iommu: iommu_device_unregister(&gart->iommu); remove_sysfs: iommu_device_sysfs_remove(&gart->iommu); +free_gart: + kfree(gart); return ERR_PTR(ret); } From 5dd82cdb36880e0c87b58a002a0513cfbc18552a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:04 +0300 Subject: [PATCH 0310/4277] iommu/tegra: gart: Prepend error/debug messages with "gart:" GART became a part of Memory Controller, hence now the drivers device is Memory Controller and not GART. As a result all printed messages are prepended with the "tegra-mc 7000f000.memory-controller:", so let's prepend GART's messages with "gart:" in order to differentiate them from the MC. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 2e0e6aff8f70..a3ce6918577d 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -19,6 +19,8 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ +#define dev_fmt(fmt) "gart: " fmt + #include #include #include From cc0e1205766b8be1e6f7985991ad80c8c5e791c2 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:05 +0300 Subject: [PATCH 0311/4277] iommu/tegra: gart: Don't detach devices from inactive domains There could be unlimited number of allocated domains, but only one domain can be active at a time. Hence devices must be detached only from the active domain. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index a3ce6918577d..74c9be13f043 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -171,7 +171,7 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; + struct gart_device *gart = gart_handle; struct gart_client *client, *c; int err = 0; @@ -195,6 +195,7 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain, goto fail; } gart->active_domain = domain; + gart_domain->gart = gart; list_add(&client->list, &gart->client); spin_unlock(&gart->client_lock); dev_dbg(gart->dev, "Attached %s\n", dev_name(dev)); @@ -217,8 +218,10 @@ static void __gart_iommu_detach_dev(struct iommu_domain *domain, if (c->dev == dev) { list_del(&c->list); kfree(c); - if (list_empty(&gart->client)) + if (list_empty(&gart->client)) { gart->active_domain = NULL; + gart_domain->gart = NULL; + } dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); return; } @@ -254,7 +257,6 @@ static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) if (!gart_domain) return NULL; - gart_domain->gart = gart; gart_domain->domain.geometry.aperture_start = gart->iovmm_base; gart_domain->domain.geometry.aperture_end = gart->iovmm_base + gart->page_count * GART_PAGE_SIZE - 1; From e7e2367041179318591d3a656f146e20418f99bb Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:06 +0300 Subject: [PATCH 0312/4277] iommu/tegra: gart: Simplify clients-tracking code GART is a simple IOMMU provider that has single address space. There is no need to setup global clients list and manage it for tracking of the active domain, hence lot's of code could be safely removed and replaced with a simpler alternative. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 153 ++++++++++--------------------------- 1 file changed, 39 insertions(+), 114 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 74c9be13f043..ad348c61d5e7 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -46,30 +45,20 @@ #define GART_PAGE_MASK \ (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) -struct gart_client { - struct device *dev; - struct list_head list; -}; - struct gart_device { void __iomem *regs; u32 *savedata; u32 page_count; /* total remappable size */ dma_addr_t iovmm_base; /* offset to vmm_area */ spinlock_t pte_lock; /* for pagetable */ - struct list_head client; - spinlock_t client_lock; /* for client list */ + spinlock_t dom_lock; /* for active domain */ + unsigned int active_devices; /* number of active devices */ struct iommu_domain *active_domain; /* current active domain */ struct device *dev; struct iommu_device iommu; /* IOMMU Core handle */ }; -struct gart_domain { - struct iommu_domain domain; /* generic domain handle */ - struct gart_device *gart; /* link to gart device */ -}; - static struct gart_device *gart_handle; /* unique for a system */ static bool gart_debug; @@ -77,11 +66,6 @@ static bool gart_debug; #define GART_PTE(_pfn) \ (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) -static struct gart_domain *to_gart_domain(struct iommu_domain *dom) -{ - return container_of(dom, struct gart_domain, domain); -} - /* * Any interaction between any block on PPSB and a block on APB or AHB * must have these read-back to ensure the APB/AHB bus transaction is @@ -170,125 +154,70 @@ static inline bool gart_iova_range_valid(struct gart_device *gart, static int gart_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { - struct gart_domain *gart_domain = to_gart_domain(domain); struct gart_device *gart = gart_handle; - struct gart_client *client, *c; - int err = 0; + int ret = 0; - client = kzalloc(sizeof(*c), GFP_KERNEL); - if (!client) - return -ENOMEM; - client->dev = dev; + spin_lock(&gart->dom_lock); - spin_lock(&gart->client_lock); - list_for_each_entry(c, &gart->client, list) { - if (c->dev == dev) { - dev_err(gart->dev, - "%s is already attached\n", dev_name(dev)); - err = -EINVAL; - goto fail; - } - } if (gart->active_domain && gart->active_domain != domain) { - dev_err(gart->dev, "Only one domain can be active at a time\n"); - err = -EINVAL; - goto fail; - } - gart->active_domain = domain; - gart_domain->gart = gart; - list_add(&client->list, &gart->client); - spin_unlock(&gart->client_lock); - dev_dbg(gart->dev, "Attached %s\n", dev_name(dev)); - return 0; - -fail: - kfree(client); - spin_unlock(&gart->client_lock); - return err; -} - -static void __gart_iommu_detach_dev(struct iommu_domain *domain, - struct device *dev) -{ - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; - struct gart_client *c; - - list_for_each_entry(c, &gart->client, list) { - if (c->dev == dev) { - list_del(&c->list); - kfree(c); - if (list_empty(&gart->client)) { - gart->active_domain = NULL; - gart_domain->gart = NULL; - } - dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); - return; - } + ret = -EBUSY; + } else if (dev->archdata.iommu != domain) { + dev->archdata.iommu = domain; + gart->active_domain = domain; + gart->active_devices++; } - dev_err(gart->dev, "Couldn't find %s to detach\n", dev_name(dev)); + spin_unlock(&gart->dom_lock); + + return ret; } static void gart_iommu_detach_dev(struct iommu_domain *domain, struct device *dev) { - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; + struct gart_device *gart = gart_handle; - spin_lock(&gart->client_lock); - __gart_iommu_detach_dev(domain, dev); - spin_unlock(&gart->client_lock); + spin_lock(&gart->dom_lock); + + if (dev->archdata.iommu == domain) { + dev->archdata.iommu = NULL; + + if (--gart->active_devices == 0) + gart->active_domain = NULL; + } + + spin_unlock(&gart->dom_lock); } static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) { - struct gart_domain *gart_domain; - struct gart_device *gart; + struct gart_device *gart = gart_handle; + struct iommu_domain *domain; if (type != IOMMU_DOMAIN_UNMANAGED) return NULL; - gart = gart_handle; - if (!gart) - return NULL; - - gart_domain = kzalloc(sizeof(*gart_domain), GFP_KERNEL); - if (!gart_domain) - return NULL; - - gart_domain->domain.geometry.aperture_start = gart->iovmm_base; - gart_domain->domain.geometry.aperture_end = gart->iovmm_base + + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (domain) { + domain->geometry.aperture_start = gart->iovmm_base; + domain->geometry.aperture_end = gart->iovmm_base + gart->page_count * GART_PAGE_SIZE - 1; - gart_domain->domain.geometry.force_aperture = true; + domain->geometry.force_aperture = true; + } - return &gart_domain->domain; + return domain; } static void gart_iommu_domain_free(struct iommu_domain *domain) { - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; - - if (gart) { - spin_lock(&gart->client_lock); - if (!list_empty(&gart->client)) { - struct gart_client *c, *tmp; - - list_for_each_entry_safe(c, tmp, &gart->client, list) - __gart_iommu_detach_dev(domain, c->dev); - } - spin_unlock(&gart->client_lock); - } - - kfree(gart_domain); + WARN_ON(gart_handle->active_domain == domain); + kfree(domain); } static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t pa, size_t bytes, int prot) { - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; + struct gart_device *gart = gart_handle; unsigned long flags; unsigned long pfn; unsigned long pte; @@ -319,8 +248,7 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t bytes) { - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; + struct gart_device *gart = gart_handle; unsigned long flags; if (!gart_iova_range_valid(gart, iova, bytes)) @@ -335,8 +263,7 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; + struct gart_device *gart = gart_handle; unsigned long pte; phys_addr_t pa; unsigned long flags; @@ -395,8 +322,7 @@ static int gart_iommu_of_xlate(struct device *dev, static void gart_iommu_sync(struct iommu_domain *domain) { - struct gart_domain *gart_domain = to_gart_domain(domain); - struct gart_device *gart = gart_domain->gart; + struct gart_device *gart = gart_handle; FLUSH_GART_REGS(gart); } @@ -483,8 +409,7 @@ struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) gart->dev = dev; gart_regs = mc->regs + GART_REG_BASE; spin_lock_init(&gart->pte_lock); - spin_lock_init(&gart->client_lock); - INIT_LIST_HEAD(&gart->client); + spin_lock_init(&gart->dom_lock); gart->regs = gart_regs; gart->iovmm_base = (dma_addr_t)res_remap->start; gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT); From 707223095ccf1fd81c9fa5346239b79127c322c2 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 12 Dec 2018 23:39:07 +0300 Subject: [PATCH 0313/4277] iommu/tegra: gart: Perform code refactoring Removed redundant safety-checks in the code and some debug code that isn't actually very useful for debugging, like enormous pagetable dump on each fault. The majority of the changes are code reshuffling, variables/whitespaces clean up and removal of debug messages that duplicate messages of the IOMMU-core. Now the GART translation is kept disabled while GART is suspended. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 255 +++++++++++++++---------------------- 1 file changed, 106 insertions(+), 149 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index ad348c61d5e7..4d8057916552 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -1,5 +1,5 @@ /* - * IOMMU API for GART in Tegra20 + * IOMMU API for Graphics Address Relocation Table on Tegra20 * * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. * @@ -31,70 +31,63 @@ #include -/* bitmap of the page sizes currently supported */ -#define GART_IOMMU_PGSIZES (SZ_4K) - #define GART_REG_BASE 0x24 #define GART_CONFIG (0x24 - GART_REG_BASE) #define GART_ENTRY_ADDR (0x28 - GART_REG_BASE) #define GART_ENTRY_DATA (0x2c - GART_REG_BASE) -#define GART_ENTRY_PHYS_ADDR_VALID (1 << 31) + +#define GART_ENTRY_PHYS_ADDR_VALID BIT(31) #define GART_PAGE_SHIFT 12 #define GART_PAGE_SIZE (1 << GART_PAGE_SHIFT) -#define GART_PAGE_MASK \ - (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) +#define GART_PAGE_MASK GENMASK(30, GART_PAGE_SHIFT) + +/* bitmap of the page sizes currently supported */ +#define GART_IOMMU_PGSIZES (GART_PAGE_SIZE) struct gart_device { void __iomem *regs; u32 *savedata; - u32 page_count; /* total remappable size */ - dma_addr_t iovmm_base; /* offset to vmm_area */ + unsigned long iovmm_base; /* offset to vmm_area start */ + unsigned long iovmm_end; /* offset to vmm_area end */ spinlock_t pte_lock; /* for pagetable */ spinlock_t dom_lock; /* for active domain */ unsigned int active_devices; /* number of active devices */ struct iommu_domain *active_domain; /* current active domain */ - struct device *dev; - struct iommu_device iommu; /* IOMMU Core handle */ + struct device *dev; }; static struct gart_device *gart_handle; /* unique for a system */ static bool gart_debug; -#define GART_PTE(_pfn) \ - (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) - /* * Any interaction between any block on PPSB and a block on APB or AHB * must have these read-back to ensure the APB/AHB bus transaction is * complete before initiating activity on the PPSB block. */ -#define FLUSH_GART_REGS(gart) ((void)readl((gart)->regs + GART_CONFIG)) +#define FLUSH_GART_REGS(gart) readl_relaxed((gart)->regs + GART_CONFIG) #define for_each_gart_pte(gart, iova) \ for (iova = gart->iovmm_base; \ - iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \ + iova < gart->iovmm_end; \ iova += GART_PAGE_SIZE) static inline void gart_set_pte(struct gart_device *gart, - unsigned long offs, u32 pte) + unsigned long iova, unsigned long pte) { - writel(offs, gart->regs + GART_ENTRY_ADDR); - writel(pte, gart->regs + GART_ENTRY_DATA); - - dev_dbg(gart->dev, "%s %08lx:%08x\n", - pte ? "map" : "unmap", offs, pte & GART_PAGE_MASK); + writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR); + writel_relaxed(pte, gart->regs + GART_ENTRY_DATA); } static inline unsigned long gart_read_pte(struct gart_device *gart, - unsigned long offs) + unsigned long iova) { unsigned long pte; - writel(offs, gart->regs + GART_ENTRY_ADDR); - pte = readl(gart->regs + GART_ENTRY_DATA); + writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR); + pte = readl_relaxed(gart->regs + GART_ENTRY_DATA); return pte; } @@ -106,49 +99,20 @@ static void do_gart_setup(struct gart_device *gart, const u32 *data) for_each_gart_pte(gart, iova) gart_set_pte(gart, iova, data ? *(data++) : 0); - writel(1, gart->regs + GART_CONFIG); + writel_relaxed(1, gart->regs + GART_CONFIG); FLUSH_GART_REGS(gart); } -#ifdef DEBUG -static void gart_dump_table(struct gart_device *gart) +static inline bool gart_iova_range_invalid(struct gart_device *gart, + unsigned long iova, size_t bytes) { - unsigned long iova; - unsigned long flags; - - spin_lock_irqsave(&gart->pte_lock, flags); - for_each_gart_pte(gart, iova) { - unsigned long pte; - - pte = gart_read_pte(gart, iova); - - dev_dbg(gart->dev, "%s %08lx:%08lx\n", - (GART_ENTRY_PHYS_ADDR_VALID & pte) ? "v" : " ", - iova, pte & GART_PAGE_MASK); - } - spin_unlock_irqrestore(&gart->pte_lock, flags); + return unlikely(iova < gart->iovmm_base || bytes != GART_PAGE_SIZE || + iova + bytes > gart->iovmm_end); } -#else -static inline void gart_dump_table(struct gart_device *gart) + +static inline bool gart_pte_valid(struct gart_device *gart, unsigned long iova) { -} -#endif - -static inline bool gart_iova_range_valid(struct gart_device *gart, - unsigned long iova, size_t bytes) -{ - unsigned long iova_start, iova_end, gart_start, gart_end; - - iova_start = iova; - iova_end = iova_start + bytes - 1; - gart_start = gart->iovmm_base; - gart_end = gart_start + gart->page_count * GART_PAGE_SIZE - 1; - - if (iova_start < gart_start) - return false; - if (iova_end > gart_end) - return false; - return true; + return !!(gart_read_pte(gart, iova) & GART_ENTRY_PHYS_ADDR_VALID); } static int gart_iommu_attach_dev(struct iommu_domain *domain, @@ -191,7 +155,6 @@ static void gart_iommu_detach_dev(struct iommu_domain *domain, static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) { - struct gart_device *gart = gart_handle; struct iommu_domain *domain; if (type != IOMMU_DOMAIN_UNMANAGED) @@ -199,9 +162,8 @@ static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) domain = kzalloc(sizeof(*domain), GFP_KERNEL); if (domain) { - domain->geometry.aperture_start = gart->iovmm_base; - domain->geometry.aperture_end = gart->iovmm_base + - gart->page_count * GART_PAGE_SIZE - 1; + domain->geometry.aperture_start = gart_handle->iovmm_base; + domain->geometry.aperture_end = gart_handle->iovmm_end - 1; domain->geometry.force_aperture = true; } @@ -214,34 +176,45 @@ static void gart_iommu_domain_free(struct iommu_domain *domain) kfree(domain); } +static inline int __gart_iommu_map(struct gart_device *gart, unsigned long iova, + unsigned long pa) +{ + if (unlikely(gart_debug && gart_pte_valid(gart, iova))) { + dev_err(gart->dev, "Page entry is in-use\n"); + return -EINVAL; + } + + gart_set_pte(gart, iova, GART_ENTRY_PHYS_ADDR_VALID | pa); + + return 0; +} + static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t pa, size_t bytes, int prot) { struct gart_device *gart = gart_handle; - unsigned long flags; - unsigned long pfn; - unsigned long pte; + int ret; - if (!gart_iova_range_valid(gart, iova, bytes)) + if (gart_iova_range_invalid(gart, iova, bytes)) return -EINVAL; - spin_lock_irqsave(&gart->pte_lock, flags); - pfn = __phys_to_pfn(pa); - if (!pfn_valid(pfn)) { - dev_err(gart->dev, "Invalid page: %pa\n", &pa); - spin_unlock_irqrestore(&gart->pte_lock, flags); + spin_lock(&gart->pte_lock); + ret = __gart_iommu_map(gart, iova, (unsigned long)pa); + spin_unlock(&gart->pte_lock); + + return ret; +} + +static inline int __gart_iommu_unmap(struct gart_device *gart, + unsigned long iova) +{ + if (unlikely(gart_debug && !gart_pte_valid(gart, iova))) { + dev_err(gart->dev, "Page entry is invalid\n"); return -EINVAL; } - if (gart_debug) { - pte = gart_read_pte(gart, iova); - if (pte & GART_ENTRY_PHYS_ADDR_VALID) { - spin_unlock_irqrestore(&gart->pte_lock, flags); - dev_err(gart->dev, "Page entry is in-use\n"); - return -EBUSY; - } - } - gart_set_pte(gart, iova, GART_PTE(pfn)); - spin_unlock_irqrestore(&gart->pte_lock, flags); + + gart_set_pte(gart, iova, 0); + return 0; } @@ -249,15 +222,16 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t bytes) { struct gart_device *gart = gart_handle; - unsigned long flags; + int err; - if (!gart_iova_range_valid(gart, iova, bytes)) + if (gart_iova_range_invalid(gart, iova, bytes)) return 0; - spin_lock_irqsave(&gart->pte_lock, flags); - gart_set_pte(gart, iova, 0); - spin_unlock_irqrestore(&gart->pte_lock, flags); - return bytes; + spin_lock(&gart->pte_lock); + err = __gart_iommu_unmap(gart, iova); + spin_unlock(&gart->pte_lock); + + return err ? 0 : bytes; } static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, @@ -265,24 +239,15 @@ static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, { struct gart_device *gart = gart_handle; unsigned long pte; - phys_addr_t pa; - unsigned long flags; - if (!gart_iova_range_valid(gart, iova, 0)) + if (gart_iova_range_invalid(gart, iova, GART_PAGE_SIZE)) return -EINVAL; - spin_lock_irqsave(&gart->pte_lock, flags); + spin_lock(&gart->pte_lock); pte = gart_read_pte(gart, iova); - spin_unlock_irqrestore(&gart->pte_lock, flags); + spin_unlock(&gart->pte_lock); - pa = (pte & GART_PAGE_MASK); - if (!pfn_valid(__phys_to_pfn(pa))) { - dev_err(gart->dev, "No entry for %08llx:%pa\n", - (unsigned long long)iova, &pa); - gart_dump_table(gart); - return -EINVAL; - } - return pa; + return pte & GART_PAGE_MASK; } static bool gart_iommu_capable(enum iommu_cap cap) @@ -322,9 +287,7 @@ static int gart_iommu_of_xlate(struct device *dev, static void gart_iommu_sync(struct iommu_domain *domain) { - struct gart_device *gart = gart_handle; - - FLUSH_GART_REGS(gart); + FLUSH_GART_REGS(gart_handle); } static const struct iommu_ops gart_iommu_ops = { @@ -347,84 +310,78 @@ static const struct iommu_ops gart_iommu_ops = { int tegra_gart_suspend(struct gart_device *gart) { - unsigned long iova; u32 *data = gart->savedata; - unsigned long flags; + unsigned long iova; + + /* + * All GART users shall be suspended at this point. Disable + * address translation to trap all GART accesses as invalid + * memory accesses. + */ + writel_relaxed(0, gart->regs + GART_CONFIG); + FLUSH_GART_REGS(gart); - spin_lock_irqsave(&gart->pte_lock, flags); for_each_gart_pte(gart, iova) *(data++) = gart_read_pte(gart, iova); - spin_unlock_irqrestore(&gart->pte_lock, flags); + return 0; } int tegra_gart_resume(struct gart_device *gart) { - unsigned long flags; - - spin_lock_irqsave(&gart->pte_lock, flags); do_gart_setup(gart, gart->savedata); - spin_unlock_irqrestore(&gart->pte_lock, flags); + return 0; } struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) { struct gart_device *gart; - struct resource *res_remap; - void __iomem *gart_regs; - int ret; + struct resource *res; + int err; BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT); /* the GART memory aperture is required */ - res_remap = platform_get_resource(to_platform_device(dev), - IORESOURCE_MEM, 1); - if (!res_remap) { - dev_err(dev, "GART memory aperture expected\n"); + res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "Memory aperture resource unavailable\n"); return ERR_PTR(-ENXIO); } gart = kzalloc(sizeof(*gart), GFP_KERNEL); - if (!gart) { - dev_err(dev, "failed to allocate gart_device\n"); + if (!gart) return ERR_PTR(-ENOMEM); - } - ret = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart"); - if (ret) { - dev_err(dev, "Failed to register IOMMU in sysfs\n"); + gart_handle = gart; + + gart->dev = dev; + gart->regs = mc->regs + GART_REG_BASE; + gart->iovmm_base = res->start; + gart->iovmm_end = res->end + 1; + spin_lock_init(&gart->pte_lock); + spin_lock_init(&gart->dom_lock); + + do_gart_setup(gart, NULL); + + err = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart"); + if (err) goto free_gart; - } iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); iommu_device_set_fwnode(&gart->iommu, dev->fwnode); - ret = iommu_device_register(&gart->iommu); - if (ret) { - dev_err(dev, "Failed to register IOMMU\n"); + err = iommu_device_register(&gart->iommu); + if (err) goto remove_sysfs; - } - gart->dev = dev; - gart_regs = mc->regs + GART_REG_BASE; - spin_lock_init(&gart->pte_lock); - spin_lock_init(&gart->dom_lock); - gart->regs = gart_regs; - gart->iovmm_base = (dma_addr_t)res_remap->start; - gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT); - - gart->savedata = vmalloc(array_size(sizeof(u32), gart->page_count)); + gart->savedata = vmalloc(resource_size(res) / GART_PAGE_SIZE * + sizeof(u32)); if (!gart->savedata) { - dev_err(dev, "failed to allocate context save area\n"); - ret = -ENOMEM; + err = -ENOMEM; goto unregister_iommu; } - do_gart_setup(gart, NULL); - - gart_handle = gart; - return gart; unregister_iommu: @@ -434,7 +391,7 @@ remove_sysfs: free_gart: kfree(gart); - return ERR_PTR(ret); + return ERR_PTR(err); } module_param(gart_debug, bool, 0644); From 3eafbd3a7745e1579665cbb8c3959a74a666b8c9 Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Mon, 14 Jan 2019 10:05:15 +0000 Subject: [PATCH 0314/4277] dt-bindings: mfd: Document STPMIC1 STPMIC1 is a PMIC from STMicroelectronics. The STPMIC1 integrates 10 regulators, 3 power switches, a watchdog and an input for a power on key. Signed-off-by: Pascal Paillet Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/st,stpmic1.txt | 61 +++++++++++++++++++ include/dt-bindings/mfd/st,stpmic1.h | 50 +++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt create mode 100644 include/dt-bindings/mfd/st,stpmic1.h diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt new file mode 100644 index 000000000000..afd45c089585 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt @@ -0,0 +1,61 @@ +* STMicroelectronics STPMIC1 Power Management IC + +Required properties: +- compatible: : "st,stpmic1" +- reg: : The I2C slave address for the STPMIC1 chip. +- interrupts: : The interrupt line the device is connected to. +- #interrupt-cells: : Should be 1. +- interrupt-controller: : Marks the device node as an interrupt controller. + Interrupt numbers are defined at + dt-bindings/mfd/st,stpmic1.h. + +STPMIC1 consists in a varied group of sub-devices. +Each sub-device binding is be described in own documentation file. + +Device Description +------ ------------ +st,stpmic1-onkey : Power on key, see ../input/st,stpmic1-onkey.txt +st,stpmic1-regulators : Regulators, see ../regulator/st,stpmic1-regulator.txt +st,stpmic1-wdt : Watchdog, see ../watchdog/st,stpmic1-wdt.txt + +Example: + +#include + +pmic: pmic@33 { + compatible = "st,stpmic1"; + reg = <0x33>; + interrupt-parent = <&gpioa>; + interrupts = <0 2>; + + interrupt-controller; + #interrupt-cells = <2>; + + onkey { + compatible = "st,stpmic1-onkey"; + interrupts = ,; + interrupt-names = "onkey-falling", "onkey-rising"; + power-off-time-sec = <10>; + }; + + watchdog { + compatible = "st,stpmic1-wdt"; + }; + + regulators { + compatible = "st,stpmic1-regulators"; + + vdd_core: buck1 { + regulator-name = "vdd_core"; + regulator-boot-on; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1200000>; + }; + vdd: buck3 { + regulator-name = "vdd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-pull-down; + }; + }; diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h new file mode 100644 index 000000000000..321cd08797d9 --- /dev/null +++ b/include/dt-bindings/mfd/st,stpmic1.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Philippe Peurichard , + * Pascal Paillet for STMicroelectronics. + */ + +#ifndef __DT_BINDINGS_STPMIC1_H__ +#define __DT_BINDINGS_STPMIC1_H__ + +/* IRQ definitions */ +#define IT_PONKEY_F 0 +#define IT_PONKEY_R 1 +#define IT_WAKEUP_F 2 +#define IT_WAKEUP_R 3 +#define IT_VBUS_OTG_F 4 +#define IT_VBUS_OTG_R 5 +#define IT_SWOUT_F 6 +#define IT_SWOUT_R 7 + +#define IT_CURLIM_BUCK1 8 +#define IT_CURLIM_BUCK2 9 +#define IT_CURLIM_BUCK3 10 +#define IT_CURLIM_BUCK4 11 +#define IT_OCP_OTG 12 +#define IT_OCP_SWOUT 13 +#define IT_OCP_BOOST 14 +#define IT_OVP_BOOST 15 + +#define IT_CURLIM_LDO1 16 +#define IT_CURLIM_LDO2 17 +#define IT_CURLIM_LDO3 18 +#define IT_CURLIM_LDO4 19 +#define IT_CURLIM_LDO5 20 +#define IT_CURLIM_LDO6 21 +#define IT_SHORT_SWOTG 22 +#define IT_SHORT_SWOUT 23 + +#define IT_TWARN_F 24 +#define IT_TWARN_R 25 +#define IT_VINLOW_F 26 +#define IT_VINLOW_R 27 +#define IT_SWIN_F 30 +#define IT_SWIN_R 31 + +/* BUCK MODES definitions */ +#define STPMIC1_BUCK_MODE_NORMAL 0 +#define STPMIC1_BUCK_MODE_LP 2 + +#endif /* __DT_BINDINGS_STPMIC1_H__ */ From 51908d2e9b7c7730608a19f24fc8718af745bb2f Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Mon, 14 Jan 2019 10:05:16 +0000 Subject: [PATCH 0315/4277] mfd: stpmic1: Add STPMIC1 driver STPMIC1 is a PMIC from STMicroelectronics. The STPMIC1 integrates 10 regulators, 3 power switches, a watchdog and an input for a power on key. Signed-off-by: Pascal Paillet Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 16 +++ drivers/mfd/Makefile | 1 + drivers/mfd/stpmic1.c | 213 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/stpmic1.h | 212 +++++++++++++++++++++++++++++++++++ 4 files changed, 442 insertions(+) create mode 100644 drivers/mfd/stpmic1.c create mode 100644 include/linux/mfd/stpmic1.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8c5dfdce4326..0761cb83d174 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1871,6 +1871,22 @@ config MFD_STM32_TIMERS for PWM and IIO Timer. This driver allow to share the registers between the others drivers. +config MFD_STPMIC1 + tristate "Support for STPMIC1 PMIC" + depends on (I2C=y && OF) + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 has power on + key, watchdog and regulator functionalities which are supported via + the relevant subsystems. This driver provides core support for the + STPMIC1. In order to use the actual functionaltiy of the device other + drivers must be enabled. + + To compile this driver as a module, choose M here: the + module will be called stpmic1. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 12980a4ad460..a62fb0112d9f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -233,6 +233,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o +obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c new file mode 100644 index 000000000000..7dfbe8906cb8 --- /dev/null +++ b/drivers/mfd/stpmic1.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define STPMIC1_MAIN_IRQ 0 + +static const struct regmap_range stpmic1_readable_ranges[] = { + regmap_reg_range(TURN_ON_SR, VERSION_SR), + regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(BST_SW_CR, BST_SW_CR), + regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4), + regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), + regmap_reg_range(INT_MASK_R1, INT_MASK_R4), + regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4), + regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4), + regmap_reg_range(INT_SRC_R1, INT_SRC_R1), +}; + +static const struct regmap_range stpmic1_writeable_ranges[] = { + regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(BST_SW_CR, BST_SW_CR), + regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), + regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4), + regmap_reg_range(INT_CLEAR_MASK_R1, INT_CLEAR_MASK_R4), +}; + +static const struct regmap_range stpmic1_volatile_ranges[] = { + regmap_reg_range(TURN_ON_SR, VERSION_SR), + regmap_reg_range(WCHDG_CR, WCHDG_CR), + regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4), + regmap_reg_range(INT_SRC_R1, INT_SRC_R4), +}; + +static const struct regmap_access_table stpmic1_readable_table = { + .yes_ranges = stpmic1_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(stpmic1_readable_ranges), +}; + +static const struct regmap_access_table stpmic1_writeable_table = { + .yes_ranges = stpmic1_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(stpmic1_writeable_ranges), +}; + +static const struct regmap_access_table stpmic1_volatile_table = { + .yes_ranges = stpmic1_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(stpmic1_volatile_ranges), +}; + +const struct regmap_config stpmic1_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = PMIC_MAX_REGISTER_ADDRESS, + .rd_table = &stpmic1_readable_table, + .wr_table = &stpmic1_writeable_table, + .volatile_table = &stpmic1_volatile_table, +}; + +static const struct regmap_irq stpmic1_irqs[] = { + REGMAP_IRQ_REG(IT_PONKEY_F, 0, 0x01), + REGMAP_IRQ_REG(IT_PONKEY_R, 0, 0x02), + REGMAP_IRQ_REG(IT_WAKEUP_F, 0, 0x04), + REGMAP_IRQ_REG(IT_WAKEUP_R, 0, 0x08), + REGMAP_IRQ_REG(IT_VBUS_OTG_F, 0, 0x10), + REGMAP_IRQ_REG(IT_VBUS_OTG_R, 0, 0x20), + REGMAP_IRQ_REG(IT_SWOUT_F, 0, 0x40), + REGMAP_IRQ_REG(IT_SWOUT_R, 0, 0x80), + + REGMAP_IRQ_REG(IT_CURLIM_BUCK1, 1, 0x01), + REGMAP_IRQ_REG(IT_CURLIM_BUCK2, 1, 0x02), + REGMAP_IRQ_REG(IT_CURLIM_BUCK3, 1, 0x04), + REGMAP_IRQ_REG(IT_CURLIM_BUCK4, 1, 0x08), + REGMAP_IRQ_REG(IT_OCP_OTG, 1, 0x10), + REGMAP_IRQ_REG(IT_OCP_SWOUT, 1, 0x20), + REGMAP_IRQ_REG(IT_OCP_BOOST, 1, 0x40), + REGMAP_IRQ_REG(IT_OVP_BOOST, 1, 0x80), + + REGMAP_IRQ_REG(IT_CURLIM_LDO1, 2, 0x01), + REGMAP_IRQ_REG(IT_CURLIM_LDO2, 2, 0x02), + REGMAP_IRQ_REG(IT_CURLIM_LDO3, 2, 0x04), + REGMAP_IRQ_REG(IT_CURLIM_LDO4, 2, 0x08), + REGMAP_IRQ_REG(IT_CURLIM_LDO5, 2, 0x10), + REGMAP_IRQ_REG(IT_CURLIM_LDO6, 2, 0x20), + REGMAP_IRQ_REG(IT_SHORT_SWOTG, 2, 0x40), + REGMAP_IRQ_REG(IT_SHORT_SWOUT, 2, 0x80), + + REGMAP_IRQ_REG(IT_TWARN_F, 3, 0x01), + REGMAP_IRQ_REG(IT_TWARN_R, 3, 0x02), + REGMAP_IRQ_REG(IT_VINLOW_F, 3, 0x04), + REGMAP_IRQ_REG(IT_VINLOW_R, 3, 0x08), + REGMAP_IRQ_REG(IT_SWIN_F, 3, 0x40), + REGMAP_IRQ_REG(IT_SWIN_R, 3, 0x80), +}; + +static const struct regmap_irq_chip stpmic1_regmap_irq_chip = { + .name = "pmic_irq", + .status_base = INT_PENDING_R1, + .mask_base = INT_CLEAR_MASK_R1, + .unmask_base = INT_SET_MASK_R1, + .ack_base = INT_CLEAR_R1, + .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS, + .irqs = stpmic1_irqs, + .num_irqs = ARRAY_SIZE(stpmic1_irqs), +}; + +static int stpmic1_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct stpmic1 *ddata; + struct device *dev = &i2c->dev; + int ret; + struct device_node *np = dev->of_node; + u32 reg; + + ddata = devm_kzalloc(dev, sizeof(struct stpmic1), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + i2c_set_clientdata(i2c, ddata); + ddata->dev = dev; + + ddata->regmap = devm_regmap_init_i2c(i2c, &stpmic1_regmap_config); + if (IS_ERR(ddata->regmap)) + return PTR_ERR(ddata->regmap); + + ddata->irq = of_irq_get(np, STPMIC1_MAIN_IRQ); + if (ddata->irq < 0) { + dev_err(dev, "Failed to get main IRQ: %d\n", ddata->irq); + return ddata->irq; + } + + ret = regmap_read(ddata->regmap, VERSION_SR, ®); + if (ret) { + dev_err(dev, "Unable to read PMIC version\n"); + return ret; + } + dev_info(dev, "PMIC Chip Version: 0x%x\n", reg); + + /* Initialize PMIC IRQ Chip & associated IRQ domains */ + ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq, + IRQF_ONESHOT | IRQF_SHARED, + 0, &stpmic1_regmap_irq_chip, + &ddata->irq_data); + if (ret) { + dev_err(dev, "IRQ Chip registration failed: %d\n", ret); + return ret; + } + + return devm_of_platform_populate(dev); +} + +#ifdef CONFIG_PM_SLEEP +static int stpmic1_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + + disable_irq(pmic_dev->irq); + + return 0; +} + +static int stpmic1_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + int ret; + + ret = regcache_sync(pmic_dev->regmap); + if (ret) + return ret; + + enable_irq(pmic_dev->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume); + +static const struct of_device_id stpmic1_of_match[] = { + { .compatible = "st,stpmic1", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stpmic1_of_match); + +static struct i2c_driver stpmic1_driver = { + .driver = { + .name = "stpmic1", + .of_match_table = of_match_ptr(stpmic1_of_match), + .pm = &stpmic1_pm, + }, + .probe = stpmic1_probe, +}; + +module_i2c_driver(stpmic1_driver); + +MODULE_DESCRIPTION("STPMIC1 PMIC Driver"); +MODULE_AUTHOR("Pascal Paillet "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h new file mode 100644 index 000000000000..fa3f99f7e9a1 --- /dev/null +++ b/include/linux/mfd/stpmic1.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Philippe Peurichard , + * Pascal Paillet for STMicroelectronics. + */ + +#ifndef __LINUX_MFD_STPMIC1_H +#define __LINUX_MFD_STPMIC1_H + +#define TURN_ON_SR 0x1 +#define TURN_OFF_SR 0x2 +#define ICC_LDO_TURN_OFF_SR 0x3 +#define ICC_BUCK_TURN_OFF_SR 0x4 +#define RREQ_STATE_SR 0x5 +#define VERSION_SR 0x6 + +#define SWOFF_PWRCTRL_CR 0x10 +#define PADS_PULL_CR 0x11 +#define BUCKS_PD_CR 0x12 +#define LDO14_PD_CR 0x13 +#define LDO56_VREF_PD_CR 0x14 +#define VBUS_DET_VIN_CR 0x15 +#define PKEY_TURNOFF_CR 0x16 +#define BUCKS_MASK_RANK_CR 0x17 +#define BUCKS_MASK_RESET_CR 0x18 +#define LDOS_MASK_RANK_CR 0x19 +#define LDOS_MASK_RESET_CR 0x1A +#define WCHDG_CR 0x1B +#define WCHDG_TIMER_CR 0x1C +#define BUCKS_ICCTO_CR 0x1D +#define LDOS_ICCTO_CR 0x1E + +#define BUCK1_ACTIVE_CR 0x20 +#define BUCK2_ACTIVE_CR 0x21 +#define BUCK3_ACTIVE_CR 0x22 +#define BUCK4_ACTIVE_CR 0x23 +#define VREF_DDR_ACTIVE_CR 0x24 +#define LDO1_ACTIVE_CR 0x25 +#define LDO2_ACTIVE_CR 0x26 +#define LDO3_ACTIVE_CR 0x27 +#define LDO4_ACTIVE_CR 0x28 +#define LDO5_ACTIVE_CR 0x29 +#define LDO6_ACTIVE_CR 0x2A + +#define BUCK1_STDBY_CR 0x30 +#define BUCK2_STDBY_CR 0x31 +#define BUCK3_STDBY_CR 0x32 +#define BUCK4_STDBY_CR 0x33 +#define VREF_DDR_STDBY_CR 0x34 +#define LDO1_STDBY_CR 0x35 +#define LDO2_STDBY_CR 0x36 +#define LDO3_STDBY_CR 0x37 +#define LDO4_STDBY_CR 0x38 +#define LDO5_STDBY_CR 0x39 +#define LDO6_STDBY_CR 0x3A + +#define BST_SW_CR 0x40 + +#define INT_PENDING_R1 0x50 +#define INT_PENDING_R2 0x51 +#define INT_PENDING_R3 0x52 +#define INT_PENDING_R4 0x53 + +#define INT_DBG_LATCH_R1 0x60 +#define INT_DBG_LATCH_R2 0x61 +#define INT_DBG_LATCH_R3 0x62 +#define INT_DBG_LATCH_R4 0x63 + +#define INT_CLEAR_R1 0x70 +#define INT_CLEAR_R2 0x71 +#define INT_CLEAR_R3 0x72 +#define INT_CLEAR_R4 0x73 + +#define INT_MASK_R1 0x80 +#define INT_MASK_R2 0x81 +#define INT_MASK_R3 0x82 +#define INT_MASK_R4 0x83 + +#define INT_SET_MASK_R1 0x90 +#define INT_SET_MASK_R2 0x91 +#define INT_SET_MASK_R3 0x92 +#define INT_SET_MASK_R4 0x93 + +#define INT_CLEAR_MASK_R1 0xA0 +#define INT_CLEAR_MASK_R2 0xA1 +#define INT_CLEAR_MASK_R3 0xA2 +#define INT_CLEAR_MASK_R4 0xA3 + +#define INT_SRC_R1 0xB0 +#define INT_SRC_R2 0xB1 +#define INT_SRC_R3 0xB2 +#define INT_SRC_R4 0xB3 + +#define PMIC_MAX_REGISTER_ADDRESS INT_SRC_R4 + +#define STPMIC1_PMIC_NUM_IRQ_REGS 4 + +#define TURN_OFF_SR_ICC_EVENT 0x08 + +#define LDO_VOLTAGE_MASK GENMASK(6, 2) +#define BUCK_VOLTAGE_MASK GENMASK(7, 2) +#define LDO_BUCK_VOLTAGE_SHIFT 2 + +#define LDO_ENABLE_MASK BIT(0) +#define BUCK_ENABLE_MASK BIT(0) + +#define BUCK_HPLP_ENABLE_MASK BIT(1) +#define BUCK_HPLP_SHIFT 1 + +#define STDBY_ENABLE_MASK BIT(0) + +#define BUCKS_PD_CR_REG_MASK GENMASK(7, 0) +#define BUCK_MASK_RANK_REGISTER_MASK GENMASK(3, 0) +#define BUCK_MASK_RESET_REGISTER_MASK GENMASK(3, 0) +#define LDO1234_PULL_DOWN_REGISTER_MASK GENMASK(7, 0) +#define LDO56_VREF_PD_CR_REG_MASK GENMASK(5, 0) +#define LDO_MASK_RANK_REGISTER_MASK GENMASK(5, 0) +#define LDO_MASK_RESET_REGISTER_MASK GENMASK(5, 0) + +#define BUCK1_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK1_PULL_DOWN_MASK BIT(0) +#define BUCK2_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK2_PULL_DOWN_MASK BIT(2) +#define BUCK3_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK3_PULL_DOWN_MASK BIT(4) +#define BUCK4_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK4_PULL_DOWN_MASK BIT(6) + +#define LDO1_PULL_DOWN_REG LDO14_PD_CR +#define LDO1_PULL_DOWN_MASK BIT(0) +#define LDO2_PULL_DOWN_REG LDO14_PD_CR +#define LDO2_PULL_DOWN_MASK BIT(2) +#define LDO3_PULL_DOWN_REG LDO14_PD_CR +#define LDO3_PULL_DOWN_MASK BIT(4) +#define LDO4_PULL_DOWN_REG LDO14_PD_CR +#define LDO4_PULL_DOWN_MASK BIT(6) +#define LDO5_PULL_DOWN_REG LDO56_VREF_PD_CR +#define LDO5_PULL_DOWN_MASK BIT(0) +#define LDO6_PULL_DOWN_REG LDO56_VREF_PD_CR +#define LDO6_PULL_DOWN_MASK BIT(2) +#define VREF_DDR_PULL_DOWN_REG LDO56_VREF_PD_CR +#define VREF_DDR_PULL_DOWN_MASK BIT(4) + +#define BUCKS_ICCTO_CR_REG_MASK GENMASK(6, 0) +#define LDOS_ICCTO_CR_REG_MASK GENMASK(5, 0) + +#define LDO_BYPASS_MASK BIT(7) + +/* Main PMIC Control Register + * SWOFF_PWRCTRL_CR + * Address : 0x10 + */ +#define ICC_EVENT_ENABLED BIT(4) +#define PWRCTRL_POLARITY_HIGH BIT(3) +#define PWRCTRL_PIN_VALID BIT(2) +#define RESTART_REQUEST_ENABLED BIT(1) +#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0) + +/* Main PMIC PADS Control Register + * PADS_PULL_CR + * Address : 0x11 + */ +#define WAKEUP_DETECTOR_DISABLED BIT(4) +#define PWRCTRL_PD_ACTIVE BIT(3) +#define PWRCTRL_PU_ACTIVE BIT(2) +#define WAKEUP_PD_ACTIVE BIT(1) +#define PONKEY_PU_INACTIVE BIT(0) + +/* Main PMIC VINLOW Control Register + * VBUS_DET_VIN_CRC DMSC + * Address : 0x15 + */ +#define SWIN_DETECTOR_ENABLED BIT(7) +#define SWOUT_DETECTOR_ENABLED BIT(6) +#define VINLOW_ENABLED BIT(0) +#define VINLOW_CTRL_REG_MASK GENMASK(7, 0) + +/* USB Control Register + * Address : 0x40 + */ +#define BOOST_OVP_DISABLED BIT(7) +#define VBUS_OTG_DETECTION_DISABLED BIT(6) +#define SW_OUT_DISCHARGE BIT(5) +#define VBUS_OTG_DISCHARGE BIT(4) +#define OCP_LIMIT_HIGH BIT(3) +#define SWIN_SWOUT_ENABLED BIT(2) +#define USBSW_OTG_SWITCH_ENABLED BIT(1) +#define BOOST_ENABLED BIT(0) + +/* PKEY_TURNOFF_CR + * Address : 0x16 + */ +#define PONKEY_PWR_OFF BIT(7) +#define PONKEY_CC_FLAG_CLEAR BIT(6) +#define PONKEY_TURNOFF_TIMER_MASK GENMASK(3, 0) +#define PONKEY_TURNOFF_MASK GENMASK(7, 0) + +/* + * struct stpmic1 - stpmic1 master device for sub-drivers + * @dev: master device of the chip (can be used to access platform data) + * @irq: main IRQ number + * @regmap_irq_chip_data: irq chip data + */ +struct stpmic1 { + struct device *dev; + struct regmap *regmap; + int irq; + struct regmap_irq_chip_data *irq_data; +}; + +#endif /* __LINUX_MFD_STPMIC1_H */ From d1f1c76896c012e71ea76e48fc039df72cc30a69 Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Mon, 14 Jan 2019 10:05:16 +0000 Subject: [PATCH 0316/4277] dt-bindings: input: Document STPMIC1 PMIC onkey The STPMIC1 PMIC is able to manage an onkey button. It can be configured to shut-down the power supplies on a long key-press with an adjustable duration. Signed-off-by: Pascal Paillet Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- .../bindings/input/st,stpmic1-onkey.txt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt diff --git a/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt new file mode 100644 index 000000000000..4494613ae7ad --- /dev/null +++ b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt @@ -0,0 +1,28 @@ +STMicroelectronics STPMIC1 Onkey + +Required properties: + +- compatible = "st,stpmic1-onkey"; +- interrupts: interrupt line to use +- interrupt-names = "onkey-falling", "onkey-rising" + onkey-falling: happens when onkey is pressed; IT_PONKEY_F of pmic + onkey-rising: happens when onkey is released; IT_PONKEY_R of pmic + +Optional properties: + +- st,onkey-clear-cc-flag: onkey is able power on after an + over-current shutdown event. +- st,onkey-pu-inactive: onkey pull up is not active +- power-off-time-sec: Duration in seconds which the key should be kept + pressed for device to power off automatically (from 1 to 16 seconds). + see See Documentation/devicetree/bindings/input/keys.txt + +Example: + +onkey { + compatible = "st,stpmic1-onkey"; + interrupt-parent = <&pmic>; + interrupts = ,; + interrupt-names = "onkey-falling", "onkey-rising"; + power-off-time-sec = <10>; +}; From 9eb9cc932cfa43ac53d82b1b8d593a3a975be99e Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Mon, 14 Jan 2019 10:05:17 +0000 Subject: [PATCH 0317/4277] input: stpmic1: Add STPMIC1 onkey driver The STPMIC1 pmic is able to manage an onkey button. This driver exposes the STPMIC1 onkey as an input device. It can also be configured to shut-down the power supplies on a long key-press with an adjustable duration. Signed-off-by: Pascal Paillet Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/misc/Kconfig | 11 ++ drivers/input/misc/Makefile | 2 + drivers/input/misc/stpmic1_onkey.c | 198 +++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 drivers/input/misc/stpmic1_onkey.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ca59a2be9bc5..279fb02a0f14 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -851,4 +851,15 @@ config INPUT_SC27XX_VIBRA To compile this driver as a module, choose M here. The module will be called sc27xx_vibra. +config INPUT_STPMIC1_ONKEY + tristate "STPMIC1 PMIC Onkey support" + depends on MFD_STPMIC1 + help + Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey + can be used to wakeup from low power modes and force a shut-down on + long press. + + To compile this driver as a module, choose M here: the + module will be called stpmic1_onkey. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9d0f9d1ff68f..1b44202ad8f7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o @@ -81,3 +82,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o + diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c new file mode 100644 index 000000000000..7b49c9997df7 --- /dev/null +++ b/drivers/input/misc/stpmic1_onkey.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet for STMicroelectronics. + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct stpmic1_onkey - OnKey data + * @input_dev: pointer to input device + * @irq_falling: irq that we are hooked on to + * @irq_rising: irq that we are hooked on to + */ +struct stpmic1_onkey { + struct input_dev *input_dev; + int irq_falling; + int irq_rising; +}; + +static irqreturn_t onkey_falling_irq(int irq, void *ponkey) +{ + struct stpmic1_onkey *onkey = ponkey; + struct input_dev *input_dev = onkey->input_dev; + + input_report_key(input_dev, KEY_POWER, 1); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t onkey_rising_irq(int irq, void *ponkey) +{ + struct stpmic1_onkey *onkey = ponkey; + struct input_dev *input_dev = onkey->input_dev; + + input_report_key(input_dev, KEY_POWER, 0); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static int stpmic1_onkey_probe(struct platform_device *pdev) +{ + struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct stpmic1_onkey *onkey; + unsigned int val, reg = 0; + int error; + + onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); + if (!onkey) + return -ENOMEM; + + onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling"); + if (onkey->irq_falling < 0) { + dev_err(dev, "failed: request IRQ onkey-falling %d\n", + onkey->irq_falling); + return onkey->irq_falling; + } + + onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising"); + if (onkey->irq_rising < 0) { + dev_err(dev, "failed: request IRQ onkey-rising %d\n", + onkey->irq_rising); + return onkey->irq_rising; + } + + if (!device_property_read_u32(dev, "power-off-time-sec", &val)) { + if (val > 0 && val <= 16) { + dev_dbg(dev, "power-off-time=%d seconds\n", val); + reg |= PONKEY_PWR_OFF; + reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK); + } else { + dev_err(dev, "power-off-time-sec out of range\n"); + return -EINVAL; + } + } + + if (device_property_present(dev, "st,onkey-clear-cc-flag")) + reg |= PONKEY_CC_FLAG_CLEAR; + + error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR, + PONKEY_TURNOFF_MASK, reg); + if (error) { + dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error); + return error; + } + + if (device_property_present(dev, "st,onkey-pu-inactive")) { + error = regmap_update_bits(pmic->regmap, PADS_PULL_CR, + PONKEY_PU_INACTIVE, + PONKEY_PU_INACTIVE); + if (error) { + dev_err(dev, "ONKEY Pads configuration failed: %d\n", + error); + return error; + } + } + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) { + dev_err(dev, "Can't allocate Pwr Onkey Input Device\n"); + return -ENOMEM; + } + + input_dev->name = "pmic_onkey"; + input_dev->phys = "pmic_onkey/input0"; + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + + onkey->input_dev = input_dev; + + /* interrupt is nested in a thread */ + error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL, + onkey_falling_irq, IRQF_ONESHOT, + dev_name(dev), onkey); + if (error) { + dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL, + onkey_rising_irq, IRQF_ONESHOT, + dev_name(dev), onkey); + if (error) { + dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error); + return error; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, onkey); + device_init_wakeup(dev, true); + + return 0; +} + +static int __maybe_unused stpmic1_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) { + enable_irq_wake(onkey->irq_falling); + enable_irq_wake(onkey->irq_rising); + } + return 0; +} + +static int __maybe_unused stpmic1_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) { + disable_irq_wake(onkey->irq_falling); + disable_irq_wake(onkey->irq_rising); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm, + stpmic1_onkey_suspend, + stpmic1_onkey_resume); + +static const struct of_device_id of_stpmic1_onkey_match[] = { + { .compatible = "st,stpmic1-onkey" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match); + +static struct platform_driver stpmic1_onkey_driver = { + .probe = stpmic1_onkey_probe, + .driver = { + .name = "stpmic1_onkey", + .of_match_table = of_match_ptr(of_stpmic1_onkey_match), + .pm = &stpmic1_onkey_pm, + }, +}; +module_platform_driver(stpmic1_onkey_driver); + +MODULE_DESCRIPTION("Onkey driver for STPMIC1"); +MODULE_AUTHOR("Pascal Paillet "); +MODULE_LICENSE("GPL v2"); From 6e4531090fcddf436acf6248d3a32aa516b07075 Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Mon, 14 Jan 2019 10:05:17 +0000 Subject: [PATCH 0318/4277] dt-bindings: watchdog: document STPMIC1 PMIC watchdog The STPMIC1 PMIC embeds a watchdog which is disabled by default. In case of watchdog, the PMIC goes off. Signed-off-by: Pascal Paillet Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- .../devicetree/bindings/watchdog/st,stpmic1-wdt.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt new file mode 100644 index 000000000000..7cc1407f15cb --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt @@ -0,0 +1,11 @@ +STMicroelectronics STPMIC1 Watchdog + +Required properties: + +- compatible : should be "st,stpmic1-wdt" + +Example: + +watchdog { + compatible = "st,stpmic1-wdt"; +}; From 28804c2c8fb3401aa4b7f16a883d49dd82f5e377 Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Mon, 14 Jan 2019 10:05:18 +0000 Subject: [PATCH 0319/4277] watchdog: stpmic1: Add STPMIC1 watchdog driver The STPMIC1 PMIC embeds a watchdog which is disabled by default. As soon as the watchdog is started, it must be refreshed periodically otherwise the PMIC goes off. Signed-off-by: Pascal Paillet Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/watchdog/Kconfig | 12 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/stpmic1_wdt.c | 139 +++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 drivers/watchdog/stpmic1_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 57f017d74a97..65c3c42b5e7c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -817,6 +817,18 @@ config STM32_WATCHDOG To compile this driver as a module, choose M here: the module will be called stm32_iwdg. +config STPMIC1_WATCHDOG + tristate "STPMIC1 PMIC watchdog support" + depends on MFD_STPMIC1 + select WATCHDOG_CORE + help + Say Y here to include watchdog support embedded into STPMIC1 PMIC. + If the watchdog timer expires, stpmic1 will shut down all its power + supplies. + + To compile this driver as a module, choose M here: the + module will be called spmic1_wdt. + config UNIPHIER_WATCHDOG tristate "UniPhier watchdog support" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a0917ef28e07..4e78a8c73f0c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -220,3 +220,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o +obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c new file mode 100644 index 000000000000..ad431d8ad95f --- /dev/null +++ b/drivers/watchdog/stpmic1_wdt.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet for STMicroelectronics. + +#include +#include +#include +#include +#include +#include +#include +#include + +/* WATCHDOG CONTROL REGISTER bit */ +#define WDT_START BIT(0) +#define WDT_PING BIT(1) +#define WDT_START_MASK BIT(0) +#define WDT_PING_MASK BIT(1) +#define WDT_STOP 0 + +#define PMIC_WDT_MIN_TIMEOUT 1 +#define PMIC_WDT_MAX_TIMEOUT 256 +#define PMIC_WDT_DEFAULT_TIMEOUT 30 + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct stpmic1_wdt { + struct stpmic1 *pmic; + struct watchdog_device wdtdev; +}; + +static int pmic_wdt_start(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_START_MASK, WDT_START); +} + +static int pmic_wdt_stop(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_START_MASK, WDT_STOP); +} + +static int pmic_wdt_ping(struct watchdog_device *wdd) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + return regmap_update_bits(wdt->pmic->regmap, + WCHDG_CR, WDT_PING_MASK, WDT_PING); +} + +static int pmic_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); + + wdd->timeout = timeout; + /* timeout is equal to register value + 1 */ + return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1); +} + +static const struct watchdog_info pmic_watchdog_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "STPMIC1 PMIC Watchdog", +}; + +static const struct watchdog_ops pmic_watchdog_ops = { + .owner = THIS_MODULE, + .start = pmic_wdt_start, + .stop = pmic_wdt_stop, + .ping = pmic_wdt_ping, + .set_timeout = pmic_wdt_set_timeout, +}; + +static int pmic_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct stpmic1 *pmic; + struct stpmic1_wdt *wdt; + + if (!pdev->dev.parent) + return -EINVAL; + + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) + return -EINVAL; + + wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->pmic = pmic; + + wdt->wdtdev.info = &pmic_watchdog_info; + wdt->wdtdev.ops = &pmic_watchdog_ops; + wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT; + wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT; + wdt->wdtdev.parent = &pdev->dev; + + wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev); + + watchdog_set_nowayout(&wdt->wdtdev, nowayout); + watchdog_set_drvdata(&wdt->wdtdev, wdt); + + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); + if (ret) + return ret; + + dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n"); + return 0; +} + +static const struct of_device_id of_pmic_wdt_match[] = { + { .compatible = "st,stpmic1-wdt" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_pmic_wdt_match); + +static struct platform_driver stpmic1_wdt_driver = { + .probe = pmic_wdt_probe, + .driver = { + .name = "stpmic1-wdt", + .of_match_table = of_pmic_wdt_match, + }, +}; +module_platform_driver(stpmic1_wdt_driver); + +MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device"); +MODULE_AUTHOR("Pascal Paillet "); +MODULE_LICENSE("GPL v2"); From f8261c376e7f8cb9024af5a6c54be540c7f9108e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 14 Jan 2019 13:49:46 +0300 Subject: [PATCH 0320/4277] drm/etnaviv: NULL vs IS_ERR() buf in etnaviv_core_dump() The etnaviv_gem_get_pages() never returns NULL. It returns error pointers on error. Fixes: a8c21a5451d8 ("drm/etnaviv: add initial etnaviv DRM driver") Signed-off-by: Dan Carpenter Signed-off-by: Lucas Stach --- drivers/gpu/drm/etnaviv/etnaviv_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c index 3fbb4855396c..33854c94cb85 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c @@ -215,7 +215,7 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) mutex_lock(&obj->lock); pages = etnaviv_gem_get_pages(obj); mutex_unlock(&obj->lock); - if (pages) { + if (!IS_ERR(pages)) { int j; iter.hdr->data[0] = bomap - bomap_start; From c1d5fb019f65e1093c77734356f97b6e7ac4d629 Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Fri, 9 Nov 2018 14:03:26 -0500 Subject: [PATCH 0321/4277] media: usb: pwc: Introduce TRACE_EVENTs for pwc_isoc_handler() There were reports that PWC-based webcams don't work at some embedded ARM platforms. [1] Isochronous transfer handler seems to work too long leading to the issues in MUSB USB host subsystem. Also note, that urb->giveback() handlers are still called with disabled interrupts. In order to be able to measure performance of PWC driver, traces are introduced in URB handler section. [1] https://www.spinics.net/lists/linux-usb/msg165735.html Signed-off-by: Matwey V. Kornilov Reviewed-by: Steven Rostedt (VMware) Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 7 ++++ include/trace/events/pwc.h | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 include/trace/events/pwc.h diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 72704f4d5330..53c111bd5a22 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -76,6 +76,9 @@ #include "pwc-dec23.h" #include "pwc-dec1.h" +#define CREATE_TRACE_POINTS +#include + /* Function prototypes and driver templates */ /* hotplug device table support */ @@ -260,6 +263,8 @@ static void pwc_isoc_handler(struct urb *urb) int i, fst, flen; unsigned char *iso_buf = NULL; + trace_pwc_handler_enter(urb, pdev); + if (urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN) { PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronously.\n", @@ -348,6 +353,8 @@ static void pwc_isoc_handler(struct urb *urb) } handler_end: + trace_pwc_handler_exit(urb, pdev); + i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0) PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); diff --git a/include/trace/events/pwc.h b/include/trace/events/pwc.h new file mode 100644 index 000000000000..a2da764a3b41 --- /dev/null +++ b/include/trace/events/pwc.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(_TRACE_PWC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_PWC_H + +#include +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM pwc + +TRACE_EVENT(pwc_handler_enter, + TP_PROTO(struct urb *urb, struct pwc_device *pdev), + TP_ARGS(urb, pdev), + TP_STRUCT__entry( + __field(struct urb*, urb) + __field(struct pwc_frame_buf*, fbuf) + __field(int, urb__status) + __field(u32, urb__actual_length) + __field(int, fbuf__filled) + __string(name, pdev->v4l2_dev.name) + ), + TP_fast_assign( + __entry->urb = urb; + __entry->fbuf = pdev->fill_buf; + __entry->urb__status = urb->status; + __entry->urb__actual_length = urb->actual_length; + __entry->fbuf__filled = (pdev->fill_buf + ? pdev->fill_buf->filled : 0); + __assign_str(name, pdev->v4l2_dev.name); + ), + TP_printk("dev=%s (fbuf=%p filled=%d) urb=%p (status=%d actual_length=%u)", + __get_str(name), + __entry->fbuf, + __entry->fbuf__filled, + __entry->urb, + __entry->urb__status, + __entry->urb__actual_length) +); + +TRACE_EVENT(pwc_handler_exit, + TP_PROTO(struct urb *urb, struct pwc_device *pdev), + TP_ARGS(urb, pdev), + TP_STRUCT__entry( + __field(struct urb*, urb) + __field(struct pwc_frame_buf*, fbuf) + __field(int, fbuf__filled) + __string(name, pdev->v4l2_dev.name) + ), + TP_fast_assign( + __entry->urb = urb; + __entry->fbuf = pdev->fill_buf; + __entry->fbuf__filled = pdev->fill_buf->filled; + __assign_str(name, pdev->v4l2_dev.name); + ), + TP_printk(" dev=%s (fbuf=%p filled=%d) urb=%p", + __get_str(name), + __entry->fbuf, + __entry->fbuf__filled, + __entry->urb) +); + +#endif /* _TRACE_PWC_H */ + +/* This part must be outside protection */ +#include From 1161db6776bd1c35257e1e362e91bcb1b3fe4347 Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Fri, 9 Nov 2018 14:03:27 -0500 Subject: [PATCH 0322/4277] media: usb: pwc: Don't use coherent DMA buffers for ISO transfer DMA cocherency slows the transfer down on systems without hardware coherent DMA. Instead we use noncocherent DMA memory and explicit sync at data receive handler. Based on previous commit the following performance benchmarks have been carried out. Average memcpy() data transfer rate (rate) and handler completion time (time) have been measured when running video stream at 640x480 resolution at 10fps. x86_64 based system (Intel Core i5-3470). This platform has hardware coherent DMA support and proposed change doesn't make big difference here. * kmalloc: rate = (2.0 +- 0.4) GBps time = (5.0 +- 3.0) usec * usb_alloc_coherent: rate = (3.4 +- 1.2) GBps time = (3.5 +- 3.0) usec We see that the measurements agree within error ranges in this case. So theoretically predicted performance downgrade cannot be reliably measured here. armv7l based system (TI AM335x BeagleBone Black @ 300MHz). This platform has no hardware coherent DMA support. DMA coherence is implemented via disabled page caching that slows down memcpy() due to memory controller behaviour. * kmalloc: rate = ( 94 +- 4) MBps time = (101 +- 4) usec * usb_alloc_coherent: rate = (28.1 +- 0.1) MBps time = (341 +- 2) usec Note, that quantative difference leads (this commit leads to 3.3 times acceleration) to qualitative behavior change in this case. As it was stated before, the video stream cannot be successfully received at AM335x platforms with MUSB based USB host controller due to performance issues [1]. [1] https://www.spinics.net/lists/linux-usb/msg165735.html Signed-off-by: Matwey V. Kornilov Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 62 +++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 53c111bd5a22..a81fb319b339 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -159,6 +159,32 @@ static const struct video_device pwc_template = { /***************************************************************************/ /* Private functions */ +static void *pwc_alloc_urb_buffer(struct device *dev, + size_t size, dma_addr_t *dma_handle) +{ + void *buffer = kmalloc(size, GFP_KERNEL); + + if (!buffer) + return NULL; + + *dma_handle = dma_map_single(dev, buffer, size, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, *dma_handle)) { + kfree(buffer); + return NULL; + } + + return buffer; +} + +static void pwc_free_urb_buffer(struct device *dev, + size_t size, + void *buffer, + dma_addr_t dma_handle) +{ + dma_unmap_single(dev, dma_handle, size, DMA_FROM_DEVICE); + kfree(buffer); +} + static struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev) { unsigned long flags = 0; @@ -306,6 +332,11 @@ static void pwc_isoc_handler(struct urb *urb) /* Reset ISOC error counter. We did get here, after all. */ pdev->visoc_errors = 0; + dma_sync_single_for_cpu(&urb->dev->dev, + urb->transfer_dma, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + /* vsync: 0 = don't copy data 1 = sync-hunt 2 = synched @@ -352,6 +383,11 @@ static void pwc_isoc_handler(struct urb *urb) pdev->vlast_packet_size = flen; } + dma_sync_single_for_device(&urb->dev->dev, + urb->transfer_dma, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + handler_end: trace_pwc_handler_exit(urb, pdev); @@ -428,16 +464,15 @@ retry: urb->dev = udev; urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_buffer = usb_alloc_coherent(udev, - ISO_BUFFER_SIZE, - GFP_KERNEL, - &urb->transfer_dma); + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->transfer_buffer = pwc_alloc_urb_buffer(&udev->dev, + urb->transfer_buffer_length, + &urb->transfer_dma); if (urb->transfer_buffer == NULL) { PWC_ERROR("Failed to allocate urb buffer %d\n", i); pwc_isoc_cleanup(pdev); return -ENOMEM; } - urb->transfer_buffer_length = ISO_BUFFER_SIZE; urb->complete = pwc_isoc_handler; urb->context = pdev; urb->start_frame = 0; @@ -488,15 +523,16 @@ static void pwc_iso_free(struct pwc_device *pdev) /* Freeing ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { - if (pdev->urbs[i]) { + struct urb *urb = pdev->urbs[i]; + + if (urb) { PWC_DEBUG_MEMORY("Freeing URB\n"); - if (pdev->urbs[i]->transfer_buffer) { - usb_free_coherent(pdev->udev, - pdev->urbs[i]->transfer_buffer_length, - pdev->urbs[i]->transfer_buffer, - pdev->urbs[i]->transfer_dma); - } - usb_free_urb(pdev->urbs[i]); + if (urb->transfer_buffer) + pwc_free_urb_buffer(&urb->dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); pdev->urbs[i] = NULL; } } From 986ac47eaff77892a6393b4685c36ab9c9a4e089 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 12 Dec 2018 12:36:43 -0500 Subject: [PATCH 0323/4277] media: MAINTAINERS: added include/trace/events/pwc.h Added include/trace/events/pwc.h to the list of files. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..f916b6ca5a42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12368,6 +12368,7 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git S: Odd Fixes F: drivers/media/usb/pwc/* +F: include/trace/events/pwc.h PWM FAN DRIVER M: Kamil Debski From 6c67fa07a3b43dd83158e55584addae00d38a514 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 7 Dec 2018 11:31:34 -0500 Subject: [PATCH 0324/4277] media: vsp1: Fix trivial documentation In the partition sizing the term 'prevents' is inappropriately pluralized. Simplify to 'prevent'. Signed-off-by: Kieran Bingham Reviewed-by: Simon Horman Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 771dfe1f7c20..7ceaf3222145 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -223,7 +223,7 @@ static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, * If the modulus is less than half of the partition size, * the penultimate partition is reduced to half, which is added * to the final partition: |1234|1234|1234|12|341| - * to prevents this: |1234|1234|1234|1234|1|. + * to prevent this: |1234|1234|1234|1234|1|. */ if (modulus) { /* From aa8a1012ba624e92fd1ab58463f22501bce78af8 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 10 Dec 2018 09:53:55 -0500 Subject: [PATCH 0325/4277] media: rcar-csi2: Fix PHTW table values for E3/V3M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PHTW selection algorithm implemented in rcsi2_phtw_write_mbps() checks for lower bound of the interval used to match the desired bandwidth. Use that in place of the currently used upper bound. Fixes: 10c08812fe60 ("media: rcar: rcar-csi2: Update V3M/E3 PHTW tables") Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 62 ++++++++++----------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 6d356f5a9456..b2bd0b9627d9 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -152,37 +152,37 @@ static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { }; static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { - { .mbps = 89, .reg = 0x00 }, - { .mbps = 99, .reg = 0x20 }, - { .mbps = 109, .reg = 0x40 }, - { .mbps = 129, .reg = 0x02 }, - { .mbps = 139, .reg = 0x22 }, - { .mbps = 149, .reg = 0x42 }, - { .mbps = 169, .reg = 0x04 }, - { .mbps = 179, .reg = 0x24 }, - { .mbps = 199, .reg = 0x44 }, - { .mbps = 219, .reg = 0x06 }, - { .mbps = 239, .reg = 0x26 }, - { .mbps = 249, .reg = 0x46 }, - { .mbps = 269, .reg = 0x08 }, - { .mbps = 299, .reg = 0x28 }, - { .mbps = 329, .reg = 0x0a }, - { .mbps = 359, .reg = 0x2a }, - { .mbps = 399, .reg = 0x4a }, - { .mbps = 449, .reg = 0x0c }, - { .mbps = 499, .reg = 0x2c }, - { .mbps = 549, .reg = 0x0e }, - { .mbps = 599, .reg = 0x2e }, - { .mbps = 649, .reg = 0x10 }, - { .mbps = 699, .reg = 0x30 }, - { .mbps = 749, .reg = 0x12 }, - { .mbps = 799, .reg = 0x32 }, - { .mbps = 849, .reg = 0x52 }, - { .mbps = 899, .reg = 0x72 }, - { .mbps = 949, .reg = 0x14 }, - { .mbps = 999, .reg = 0x34 }, - { .mbps = 1049, .reg = 0x54 }, - { .mbps = 1099, .reg = 0x74 }, + { .mbps = 80, .reg = 0x00 }, + { .mbps = 90, .reg = 0x20 }, + { .mbps = 100, .reg = 0x40 }, + { .mbps = 110, .reg = 0x02 }, + { .mbps = 130, .reg = 0x22 }, + { .mbps = 140, .reg = 0x42 }, + { .mbps = 150, .reg = 0x04 }, + { .mbps = 170, .reg = 0x24 }, + { .mbps = 180, .reg = 0x44 }, + { .mbps = 200, .reg = 0x06 }, + { .mbps = 220, .reg = 0x26 }, + { .mbps = 240, .reg = 0x46 }, + { .mbps = 250, .reg = 0x08 }, + { .mbps = 270, .reg = 0x28 }, + { .mbps = 300, .reg = 0x0a }, + { .mbps = 330, .reg = 0x2a }, + { .mbps = 360, .reg = 0x4a }, + { .mbps = 400, .reg = 0x0c }, + { .mbps = 450, .reg = 0x2c }, + { .mbps = 500, .reg = 0x0e }, + { .mbps = 550, .reg = 0x2e }, + { .mbps = 600, .reg = 0x10 }, + { .mbps = 650, .reg = 0x30 }, + { .mbps = 700, .reg = 0x12 }, + { .mbps = 750, .reg = 0x32 }, + { .mbps = 800, .reg = 0x52 }, + { .mbps = 850, .reg = 0x72 }, + { .mbps = 900, .reg = 0x14 }, + { .mbps = 950, .reg = 0x34 }, + { .mbps = 1000, .reg = 0x54 }, + { .mbps = 1050, .reg = 0x74 }, { .mbps = 1125, .reg = 0x16 }, { /* sentinel */ }, }; From 66e988e96b31d0c06471234cabb0a77316841fc3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 13 Dec 2018 08:31:07 -0500 Subject: [PATCH 0326/4277] media: Change Andrzej Pietrasiewicz's e-mail address My @samsung.com address is going to cease existing soon, so change it to an address which can actually be used to contact me. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 2 +- drivers/media/platform/s5p-jpeg/jpeg-core.c | 4 ++-- drivers/media/platform/s5p-jpeg/jpeg-core.h | 2 +- drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c | 2 +- drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h | 2 +- drivers/media/platform/s5p-jpeg/jpeg-regs.h | 2 +- include/media/videobuf2-dma-sg.h | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 015e737095cd..f02876d971d7 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * 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 diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3f9000b70385..580d6d02557b 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -3,7 +3,7 @@ * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * Author: Jacek Anaszewski * * This program is free software; you can redistribute it and/or modify @@ -3220,7 +3220,7 @@ static struct platform_driver s5p_jpeg_driver = { module_platform_driver(s5p_jpeg_driver); -MODULE_AUTHOR("Andrzej Pietrasiewicz "); +MODULE_AUTHOR("Andrzej Pietrasiewicz "); MODULE_AUTHOR("Jacek Anaszewski "); MODULE_DESCRIPTION("Samsung JPEG codec driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index a46465e10351..90fda4b720eb 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -3,7 +3,7 @@ * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c index b5f20e722b63..59c6263a71bf 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -3,7 +3,7 @@ * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h index f208fa3ed738..bfe746f8f750 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h @@ -3,7 +3,7 @@ * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index df790b10140c..574f0e8021e5 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h @@ -5,7 +5,7 @@ * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * Author: Jacek Anaszewski * * This program is free software; you can redistribute it and/or modify diff --git a/include/media/videobuf2-dma-sg.h b/include/media/videobuf2-dma-sg.h index 52afa0e2bb17..f28fcb0cfac7 100644 --- a/include/media/videobuf2-dma-sg.h +++ b/include/media/videobuf2-dma-sg.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * 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 From 886ba97fb69eee710e1fd90d8aac5937e7fabfd1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 13 Dec 2018 08:36:38 -0500 Subject: [PATCH 0327/4277] media: MAINTAINERS: Change s5p-jpeg maintainer information. My @samsung.com address is going to cease existing soon, so change it to an address which can actually be used to contact me. Adding Sylwester Nawrocki, who still has access to a wide spectrum of Exynos-based hardware. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f916b6ca5a42..02d9ebcafb97 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2084,8 +2084,9 @@ F: drivers/media/platform/s5p-cec/ F: Documentation/devicetree/bindings/media/s5p-cec.txt ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT -M: Andrzej Pietrasiewicz +M: Andrzej Pietrasiewicz M: Jacek Anaszewski +M: Sylwester Nawrocki L: linux-arm-kernel@lists.infradead.org L: linux-media@vger.kernel.org S: Maintained From 2a850b94135db67d87f4ab6ab2a325a42ecbe00a Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 17 Dec 2018 12:56:17 -0500 Subject: [PATCH 0328/4277] media: vicodec: bugfix - replace '=' with '|=' In the fwht_encode_frame, 'encoding = encode_plane' should be replaced with 'encoding |= encode_plane' so existing flags won't be overwrriten. Signed-off-by: Dafna Hirschfeld Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c index 5630f1dc45e6..a6fd0477633b 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/platform/vicodec/codec-fwht.c @@ -787,10 +787,10 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, if (frm->components_num == 4) { rlco_max = rlco + size / 2 - 256; - encoding = encode_plane(frm->alpha, ref_frm->alpha, &rlco, - rlco_max, cf, frm->height, frm->width, - frm->luma_alpha_step, - is_intra, next_is_intra); + encoding |= encode_plane(frm->alpha, ref_frm->alpha, &rlco, + rlco_max, cf, frm->height, frm->width, + frm->luma_alpha_step, + is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_ALPHA_UNENCODED; encoding &= ~FWHT_FRAME_UNENCODED; From f45f3f753b0a3d739acda8e311b4f744d82dc52a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 18 Dec 2018 08:37:08 -0500 Subject: [PATCH 0329/4277] media: v4l2-ctrls.c/uvc: zero v4l2_event Control events can leak kernel memory since they do not fully zero the event. The same code is present in both v4l2-ctrls.c and uvc_ctrl.c, so fix both. It appears that all other event code is properly zeroing the structure, it's these two places. Signed-off-by: Hans Verkuil Reported-by: syzbot+4f021cf3697781dbd9fb@syzkaller.appspotmail.com Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 2 +- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index d45415cbe6e7..14cff91b7aea 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1212,7 +1212,7 @@ static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); - memset(ev->reserved, 0, sizeof(ev->reserved)); + memset(ev, 0, sizeof(*ev)); ev->type = V4L2_EVENT_CTRL; ev->id = v4l2_ctrl.id; ev->u.ctrl.value = value; diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e3bd441fa29a..8c47d8f00429 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1387,7 +1387,7 @@ static u32 user_flags(const struct v4l2_ctrl *ctrl) static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { - memset(ev->reserved, 0, sizeof(ev->reserved)); + memset(ev, 0, sizeof(*ev)); ev->type = V4L2_EVENT_CTRL; ev->id = ctrl->id; ev->u.ctrl.changes = changes; From 6f2c6afa79e0513d339871337bfc8c6c621d3ab1 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Fri, 21 Dec 2018 11:56:41 -0500 Subject: [PATCH 0330/4277] media: sunxi: cedrus: Fix missing error message context When cedrus_hw_probe is called, v4l2_dev is not yet initialized. Use dev_err instead. Signed-off-by: Ondrej Jirman Acked-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/sunxi/cedrus/cedrus_hw.c | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index 300339fee1bc..0acf219a8c91 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -157,14 +157,14 @@ int cedrus_hw_probe(struct cedrus_dev *dev) irq_dec = platform_get_irq(dev->pdev, 0); if (irq_dec <= 0) { - v4l2_err(&dev->v4l2_dev, "Failed to get IRQ\n"); + dev_err(dev->dev, "Failed to get IRQ\n"); return irq_dec; } ret = devm_request_irq(dev->dev, irq_dec, cedrus_irq, 0, dev_name(dev->dev), dev); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to request IRQ\n"); + dev_err(dev->dev, "Failed to request IRQ\n"); return ret; } @@ -182,21 +182,21 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ret = of_reserved_mem_device_init(dev->dev); if (ret && ret != -ENODEV) { - v4l2_err(&dev->v4l2_dev, "Failed to reserve memory\n"); + dev_err(dev->dev, "Failed to reserve memory\n"); return ret; } ret = sunxi_sram_claim(dev->dev); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to claim SRAM\n"); + dev_err(dev->dev, "Failed to claim SRAM\n"); goto err_mem; } dev->ahb_clk = devm_clk_get(dev->dev, "ahb"); if (IS_ERR(dev->ahb_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get AHB clock\n"); + dev_err(dev->dev, "Failed to get AHB clock\n"); ret = PTR_ERR(dev->ahb_clk); goto err_sram; @@ -204,7 +204,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->mod_clk = devm_clk_get(dev->dev, "mod"); if (IS_ERR(dev->mod_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get MOD clock\n"); + dev_err(dev->dev, "Failed to get MOD clock\n"); ret = PTR_ERR(dev->mod_clk); goto err_sram; @@ -212,7 +212,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->ram_clk = devm_clk_get(dev->dev, "ram"); if (IS_ERR(dev->ram_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get RAM clock\n"); + dev_err(dev->dev, "Failed to get RAM clock\n"); ret = PTR_ERR(dev->ram_clk); goto err_sram; @@ -220,7 +220,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->rstc = devm_reset_control_get(dev->dev, NULL); if (IS_ERR(dev->rstc)) { - v4l2_err(&dev->v4l2_dev, "Failed to get reset control\n"); + dev_err(dev->dev, "Failed to get reset control\n"); ret = PTR_ERR(dev->rstc); goto err_sram; @@ -229,7 +229,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(dev->dev, res); if (IS_ERR(dev->base)) { - v4l2_err(&dev->v4l2_dev, "Failed to map registers\n"); + dev_err(dev->dev, "Failed to map registers\n"); ret = PTR_ERR(dev->base); goto err_sram; @@ -237,35 +237,35 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to set clock rate\n"); + dev_err(dev->dev, "Failed to set clock rate\n"); goto err_sram; } ret = clk_prepare_enable(dev->ahb_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable AHB clock\n"); + dev_err(dev->dev, "Failed to enable AHB clock\n"); goto err_sram; } ret = clk_prepare_enable(dev->mod_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable MOD clock\n"); + dev_err(dev->dev, "Failed to enable MOD clock\n"); goto err_ahb_clk; } ret = clk_prepare_enable(dev->ram_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable RAM clock\n"); + dev_err(dev->dev, "Failed to enable RAM clock\n"); goto err_mod_clk; } ret = reset_control_reset(dev->rstc); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to apply reset\n"); + dev_err(dev->dev, "Failed to apply reset\n"); goto err_ram_clk; } From 5ceaf5452c1b2a452dadaf377f9f07af7bda9cc3 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 01:31:21 -0500 Subject: [PATCH 0331/4277] media: usb: gspca: add a missed return-value check for do_command do_command() may fail. The fix adds the missed return value of do_command(). If it fails, returns its error code. Signed-off-by: Kangjie Lu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/cpia1.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 2b09af8865f4..23fbda56fc91 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -547,10 +547,14 @@ static int do_command(struct gspca_dev *gspca_dev, u16 command, } if (sd->params.qx3.button) { /* button pressed - unlock the latch */ - do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 3, 0xdf, 0xdf, 0); - do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + if (ret) + return ret; + ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 3, 0xff, 0xff, 0); + if (ret) + return ret; } /* test whether microscope is cradled */ From 5b711870bec4dc9a6d705d41e127e73944fa3650 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 03:24:41 -0500 Subject: [PATCH 0332/4277] media: usb: gspca: add a missed check for goto_low_power The fix checks if goto_low_power() fails, and if so, issues an error message. Signed-off-by: Kangjie Lu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/cpia1.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 23fbda56fc91..7c817a4a93c4 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -1434,6 +1434,7 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + int ret; sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ; reset_camera_params(gspca_dev); @@ -1445,7 +1446,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = mode; cam->nmodes = ARRAY_SIZE(mode); - goto_low_power(gspca_dev); + ret = goto_low_power(gspca_dev); + if (ret) + gspca_err(gspca_dev, "Cannot go to low power mode: %d\n", + ret); /* Check the firmware version. */ sd->params.version.firmwareVersion = 0; get_version_information(gspca_dev); From a21a0eb56b4e8fe4a330243af8030f890cde2283 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 28 Dec 2018 13:37:36 -0500 Subject: [PATCH 0333/4277] media: gspca: Check the return value of write_bridge for timeout In po1030_probe(), m5602_write_bridge() can timeout and return an error value. The fix checks for the return value and propagates upstream consistent with other usb drivers. Signed-off-by: Aditya Pakki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/m5602/m5602_po1030.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c index 37d2891e5f5b..5e43b4782f02 100644 --- a/drivers/media/usb/gspca/m5602/m5602_po1030.c +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c @@ -158,6 +158,7 @@ static const struct v4l2_ctrl_config po1030_greenbal_cfg = { int po1030_probe(struct sd *sd) { + int rc = 0; u8 dev_id_h = 0, i; struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; @@ -177,11 +178,14 @@ int po1030_probe(struct sd *sd) for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { u8 data = preinit_po1030[i][2]; if (preinit_po1030[i][0] == SENSOR) - m5602_write_sensor(sd, + rc |= m5602_write_sensor(sd, preinit_po1030[i][1], &data, 1); else - m5602_write_bridge(sd, preinit_po1030[i][1], data); + rc |= m5602_write_bridge(sd, preinit_po1030[i][1], + data); } + if (rc < 0) + return rc; if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1)) return -ENODEV; From 656025850074f5c1ba2e05be37bda57ba2b8d491 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 28 Dec 2018 13:51:10 -0500 Subject: [PATCH 0334/4277] media: gspca: mt9m111: Check write_bridge for timeout In mt9m111_probe, m5602_write_bridge can timeout and return a negative error value. The fix checks for this error and passes it upstream. Signed-off-by: Aditya Pakki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/m5602/m5602_mt9m111.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c index c9947c4a0f63..8fac814f4779 100644 --- a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c @@ -199,7 +199,7 @@ static const struct v4l2_ctrl_config mt9m111_greenbal_cfg = { int mt9m111_probe(struct sd *sd) { u8 data[2] = {0x00, 0x00}; - int i; + int i, rc = 0; struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if (force_sensor) { @@ -217,16 +217,18 @@ int mt9m111_probe(struct sd *sd) /* Do the preinit */ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { if (preinit_mt9m111[i][0] == BRIDGE) { - m5602_write_bridge(sd, + rc |= m5602_write_bridge(sd, preinit_mt9m111[i][1], preinit_mt9m111[i][2]); } else { data[0] = preinit_mt9m111[i][2]; data[1] = preinit_mt9m111[i][3]; - m5602_write_sensor(sd, + rc |= m5602_write_sensor(sd, preinit_mt9m111[i][1], data, 2); } } + if (rc < 0) + return rc; if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) return -ENODEV; From 49710c32cd9d6626a77c9f5f978a5f58cb536b35 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Sat, 29 Dec 2018 10:46:01 -0500 Subject: [PATCH 0335/4277] media: s5p-jpeg: Check for fmt_ver_flag when doing fmt enumeration Previously when doing format enumeration, it was returning all formats supported by driver, even if they're not supported by hw. Add missing check for fmt_ver_flag, so it'll be fixed and only those supported by hw will be returned. Similar thing is already done in s5p_jpeg_find_format. It was found by using v4l2-compliance tool and checking result of VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS test and using v4l2-ctl to get list of all supported formats. Tested on s5pv210-galaxys (Samsung i9000 phone). Fixes: bb677f3ac434 ("[media] Exynos4 JPEG codec v4l2 driver") Signed-off-by: Pawe? Chmiel Reviewed-by: Jacek Anaszewski [hverkuil-cisco@xs4all.nl: fix a few alignment issues] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 580d6d02557b..85110766043f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1293,13 +1293,16 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, return 0; } -static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n, +static int enum_fmt(struct s5p_jpeg_ctx *ctx, + struct s5p_jpeg_fmt *sjpeg_formats, int n, struct v4l2_fmtdesc *f, u32 type) { int i, num = 0; + unsigned int fmt_ver_flag = ctx->jpeg->variant->fmt_ver_flag; for (i = 0; i < n; ++i) { - if (sjpeg_formats[i].flags & type) { + if (sjpeg_formats[i].flags & type && + sjpeg_formats[i].flags & fmt_ver_flag) { /* index-th format of type type found ? */ if (num == f->index) break; @@ -1326,11 +1329,11 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, SJPEG_FMT_FLAG_ENC_CAPTURE); - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_DEC_CAPTURE); + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_CAPTURE); } static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, @@ -1339,11 +1342,11 @@ static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, SJPEG_FMT_FLAG_ENC_OUTPUT); - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_DEC_OUTPUT); + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_OUTPUT); } static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, From d6b10dd0017641fee31daa84e478708a33d2f00a Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 30 Dec 2018 06:41:40 -0500 Subject: [PATCH 0336/4277] media: tw9910: fix failure handling in tw9910_power_on() If gpiod_get_optional() fails in tw9910_power_on(), clk is left undisabled. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tw9910.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index a54548cc4285..0971f8a34afb 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -610,6 +610,11 @@ static int tw9910_power_on(struct tw9910_priv *priv) GPIOD_OUT_LOW); if (IS_ERR(priv->rstb_gpio)) { dev_info(&client->dev, "Unable to get GPIO \"rstb\""); + clk_disable_unprepare(priv->clk); + if (priv->pdn_gpio) { + gpiod_set_value(priv->pdn_gpio, 1); + usleep_range(500, 1000); + } return PTR_ERR(priv->rstb_gpio); } From cf6a9896622da6233f8b25beeabb4a98729a49c3 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 30 Dec 2018 06:41:41 -0500 Subject: [PATCH 0337/4277] media: tw9910: add helper function for setting gpiod value tw9910 driver tries to sleep for the same period of time after each gpiod_set_value(). The patch moves duplicated code to a helper function. Signed-off-by: Alexey Khoroshilov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tw9910.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 0971f8a34afb..8d1138e13803 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -584,6 +584,14 @@ static int tw9910_s_register(struct v4l2_subdev *sd, } #endif +static void tw9910_set_gpio_value(struct gpio_desc *desc, int value) +{ + if (desc) { + gpiod_set_value(desc, value); + usleep_range(500, 1000); + } +} + static int tw9910_power_on(struct tw9910_priv *priv) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); @@ -595,10 +603,7 @@ static int tw9910_power_on(struct tw9910_priv *priv) return ret; } - if (priv->pdn_gpio) { - gpiod_set_value(priv->pdn_gpio, 0); - usleep_range(500, 1000); - } + tw9910_set_gpio_value(priv->pdn_gpio, 0); /* * FIXME: The reset signal is connected to a shared GPIO on some @@ -611,18 +616,13 @@ static int tw9910_power_on(struct tw9910_priv *priv) if (IS_ERR(priv->rstb_gpio)) { dev_info(&client->dev, "Unable to get GPIO \"rstb\""); clk_disable_unprepare(priv->clk); - if (priv->pdn_gpio) { - gpiod_set_value(priv->pdn_gpio, 1); - usleep_range(500, 1000); - } + tw9910_set_gpio_value(priv->pdn_gpio, 1); return PTR_ERR(priv->rstb_gpio); } if (priv->rstb_gpio) { - gpiod_set_value(priv->rstb_gpio, 1); - usleep_range(500, 1000); - gpiod_set_value(priv->rstb_gpio, 0); - usleep_range(500, 1000); + tw9910_set_gpio_value(priv->rstb_gpio, 1); + tw9910_set_gpio_value(priv->rstb_gpio, 0); gpiod_put(priv->rstb_gpio); } @@ -633,11 +633,7 @@ static int tw9910_power_on(struct tw9910_priv *priv) static int tw9910_power_off(struct tw9910_priv *priv) { clk_disable_unprepare(priv->clk); - - if (priv->pdn_gpio) { - gpiod_set_value(priv->pdn_gpio, 1); - usleep_range(500, 1000); - } + tw9910_set_gpio_value(priv->pdn_gpio, 1); return 0; } From a86f2b590aa0259b961ffc39852e7c88f6c7e468 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 30 Dec 2018 08:20:16 -0500 Subject: [PATCH 0338/4277] media: staging: bcm2835-camera: use V4L2_FRACT_COMPARE Now the equivalent of FRACT_CMP() is added in v4l2 common internal API header. Signed-off-by: Akinobu Mita Signed-off-by: Mauro Carvalho Chehab --- .../staging/vc04_services/bcm2835-camera/bcm2835-camera.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 611a6ee2943a..7c6cf41645eb 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1370,10 +1370,6 @@ static int vidioc_g_parm(struct file *file, void *priv, return 0; } -#define FRACT_CMP(a, OP, b) \ - ((u64)(a).numerator * (b).denominator OP \ - (u64)(b).numerator * (a).denominator) - static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) { @@ -1387,8 +1383,8 @@ static int vidioc_s_parm(struct file *file, void *priv, /* tpf: {*, 0} resets timing; clip to [min, max]*/ tpf = tpf.denominator ? tpf : tpf_default; - tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; - tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf; dev->capture.timeperframe = tpf; parm->parm.capture.timeperframe = tpf; From 6458afc8c49148f0a5015d8853039a0b3fb91d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 4 Jan 2019 11:12:18 -0500 Subject: [PATCH 0339/4277] media: rcar-vin: remove unneeded locking in async callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The locking implemented in the async notifier callbacks are unnecessary as the global list_lock in v4l2-async.c is held whenever one of the callbacks are called. The locking in itself is not harmful however it produces a LOCKDEP warning between the global v4l2-async list_lock and the rcar-vin local locking schema. Remove the rcar-vin locking for the async callbacks to reduce complexity and silent the false LOCKDEP warning. Signed-off-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index f0719ce24b97..0e81b557f3b6 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -542,9 +542,7 @@ static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); - mutex_lock(&vin->lock); rvin_parallel_subdevice_detach(vin); - mutex_unlock(&vin->lock); } static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, @@ -554,9 +552,7 @@ static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; - mutex_lock(&vin->lock); ret = rvin_parallel_subdevice_attach(vin, subdev); - mutex_unlock(&vin->lock); if (ret) return ret; @@ -664,7 +660,6 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) } /* Create all media device links between VINs and CSI-2's. */ - mutex_lock(&vin->group->lock); for (route = vin->info->routes; route->mask; route++) { struct media_pad *source_pad, *sink_pad; struct media_entity *source, *sink; @@ -700,7 +695,6 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) break; } } - mutex_unlock(&vin->group->lock); return ret; } @@ -716,8 +710,6 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, if (vin->group->vin[i]) rvin_v4l2_unregister(vin->group->vin[i]); - mutex_lock(&vin->group->lock); - for (i = 0; i < RVIN_CSI_MAX; i++) { if (vin->group->csi[i].fwnode != asd->match.fwnode) continue; @@ -725,8 +717,6 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i); break; } - - mutex_unlock(&vin->group->lock); } static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, @@ -736,8 +726,6 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; - mutex_lock(&vin->group->lock); - for (i = 0; i < RVIN_CSI_MAX; i++) { if (vin->group->csi[i].fwnode != asd->match.fwnode) continue; @@ -746,8 +734,6 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, break; } - mutex_unlock(&vin->group->lock); - return 0; } From f27dd0ad68850fdb806536a733a32d8f74810f1e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 15:41:40 -0500 Subject: [PATCH 0340/4277] media: seco-cec: fix RC_CORE dependency All other drivers that need RC_CORE have a dependency rather than using 'select', so we should do the same here to avoid circular dependencies as well as this warning about missing dependencies: WARNING: unmet direct dependencies detected for RC_CORE Depends on [n]: INPUT [=n] Selected by [y]: - VIDEO_SECO_RC [=y] && MEDIA_SUPPORT [=y] && CEC_PLATFORM_DRIVERS [=y] && VIDEO_SECO_CEC [=y] Fixes: daef95769b3a ("media: seco-cec: add Consumer-IR support") Signed-off-by: Arnd Bergmann Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index a505e9f5a1e2..b5ccb60cf664 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -650,7 +650,7 @@ config VIDEO_SECO_CEC config VIDEO_SECO_RC bool "SECO Boards IR RC5 support" depends on VIDEO_SECO_CEC - select RC_CORE + depends on RC_CORE help If you say yes here you will get support for the SECO Boards Consumer-IR in seco-cec driver. From c9b7d8f252a5a6f8ca6e948151367cbc7bc4b776 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Thu, 20 Dec 2018 02:48:42 -0500 Subject: [PATCH 0341/4277] media: lgdt3306a: fix a missing check of return value If lgdt3306a_read_reg() fails, the read data in "val" is incorrect, thus shouldn't be further used. The fix inserts a check for the return value of lgdt3306a_read_reg(). If it fails, goto fail. Signed-off-by: Kangjie Lu Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/lgdt3306a.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index cee9c83e48de..99c6289ae585 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -1685,7 +1685,10 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, case QAM_256: case QAM_AUTO: /* need to know actual modulation to set proper SNR baseline */ - lgdt3306a_read_reg(state, 0x00a6, &val); + ret = lgdt3306a_read_reg(state, 0x00a6, &val); + if (lg_chkerr(ret)) + goto fail; + if(val & 0x04) ref_snr = 2800; /* QAM-256 28dB */ else From 9502cdf0807058a10029488052b064cecceb7fc9 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Fri, 21 Dec 2018 02:07:20 -0500 Subject: [PATCH 0342/4277] media: mt312: fix a missing check of mt312 reset mt312_reset() may fail. Although it is called in the end of mt312_set_frontend(), we better check its status and return its error code upstream instead of 0. Signed-off-by: Kangjie Lu Reviewed-by: Matthias Schwarzott Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mt312.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index 03e74a729168..bfbb879469f2 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -645,7 +645,9 @@ static int mt312_set_frontend(struct dvb_frontend *fe) if (ret < 0) return ret; - mt312_reset(state, 0); + ret = mt312_reset(state, 0); + if (ret < 0) + return ret; return 0; } From c54dbfcda3059c255f335e75a27d7f5c64e6549e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Dec 2018 09:12:26 -0500 Subject: [PATCH 0343/4277] media: cxd2880-spi: fix two memory leaks of dvb_spi There are two return paths that do not kfree dvb_spi. Fix the memory leaks by returning via the exit label fail_adapter that will free dvi_spi. Detected by CoverityScan, CID#1475991 ("Resource Leak") Fixes: cb496cd472af ("media: cxd2880-spi: Add optional vcc regulator") Signed-off-by: Colin Ian King Acked-by: Yasunari Takiguchi ? Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/spi/cxd2880-spi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c index d5c433e20d4a..4077217777f9 100644 --- a/drivers/media/spi/cxd2880-spi.c +++ b/drivers/media/spi/cxd2880-spi.c @@ -522,13 +522,15 @@ cxd2880_spi_probe(struct spi_device *spi) dvb_spi->vcc_supply = devm_regulator_get_optional(&spi->dev, "vcc"); if (IS_ERR(dvb_spi->vcc_supply)) { - if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto fail_adapter; + } dvb_spi->vcc_supply = NULL; } else { ret = regulator_enable(dvb_spi->vcc_supply); if (ret) - return ret; + goto fail_adapter; } dvb_spi->spi = spi; From 88903a1abd00e051f3ddfe7b86949882260ddf86 Mon Sep 17 00:00:00 2001 From: Ettore Chimenti Date: Thu, 27 Dec 2018 08:06:35 -0500 Subject: [PATCH 0344/4277] media: secocec: fix ir address shift The actual value of the RC5 System Number (address) is stored in the IR_READ_DATA common register masked with 0x1F00 so it have to be shifted by 8 bits. Signed-off-by: Ettore Chimenti Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/seco-cec/seco-cec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/seco-cec/seco-cec.h b/drivers/media/platform/seco-cec/seco-cec.h index e632c4a2a044..843de8c7dfd4 100644 --- a/drivers/media/platform/seco-cec/seco-cec.h +++ b/drivers/media/platform/seco-cec/seco-cec.h @@ -106,7 +106,7 @@ #define SECOCEC_IR_COMMAND_MASK 0x007F #define SECOCEC_IR_COMMAND_SHL 0 #define SECOCEC_IR_ADDRESS_MASK 0x1F00 -#define SECOCEC_IR_ADDRESS_SHL 7 +#define SECOCEC_IR_ADDRESS_SHL 8 #define SECOCEC_IR_TOGGLE_MASK 0x8000 #define SECOCEC_IR_TOGGLE_SHL 15 From 0f787c12ee7b2b41a74594ed158a0112736f4e4e Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Thu, 27 Dec 2018 13:47:20 -0500 Subject: [PATCH 0345/4277] media: dvb: add return value check on Write16 Write16 can return an error code -1 when the i2c_write fails. The fix checks for these failures and returns the error upstream Signed-off-by: Aditya Pakki Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxd_hard.c | 30 ++++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 684d428efb0d..0a5b15bee1d7 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -1144,6 +1144,8 @@ static int EnableAndResetMB(struct drxd_state *state) static int InitCC(struct drxd_state *state) { + int status = 0; + if (state->osc_clock_freq == 0 || state->osc_clock_freq > 20000 || (state->osc_clock_freq % 4000) != 0) { @@ -1151,14 +1153,17 @@ static int InitCC(struct drxd_state *state) return -1; } - Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); - Write16(state, CC_REG_PLL_MODE__A, CC_REG_PLL_MODE_BYPASS_PLL | - CC_REG_PLL_MODE_PUMP_CUR_12, 0); - Write16(state, CC_REG_REF_DIVIDE__A, state->osc_clock_freq / 4000, 0); - Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, 0); - Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); + status |= Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); + status |= Write16(state, CC_REG_PLL_MODE__A, + CC_REG_PLL_MODE_BYPASS_PLL | + CC_REG_PLL_MODE_PUMP_CUR_12, 0); + status |= Write16(state, CC_REG_REF_DIVIDE__A, + state->osc_clock_freq / 4000, 0); + status |= Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, + 0); + status |= Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); - return 0; + return status; } static int ResetECOD(struct drxd_state *state) @@ -1312,7 +1317,10 @@ static int SC_SendCommand(struct drxd_state *state, u16 cmd) int status = 0, ret; u16 errCode; - Write16(state, SC_RA_RAM_CMD__A, cmd, 0); + status = Write16(state, SC_RA_RAM_CMD__A, cmd, 0); + if (status < 0) + return status; + SC_WaitForReady(state); ret = Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0); @@ -1339,9 +1347,9 @@ static int SC_ProcStartCommand(struct drxd_state *state, break; } SC_WaitForReady(state); - Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); - Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); - Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); + status |= Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); + status |= Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); + status |= Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START); } while (0); From 467a37fba93f2b4fe3ab597ff6a517b22b566882 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Thu, 27 Dec 2018 13:58:01 -0500 Subject: [PATCH 0346/4277] media: dvb: Add check on sp8870_readreg In sp8870_set_frontend_parameters, the function sp8870_readreg may return an error when i2c_transfer fails. The fix checks for this error and returns upstream consistent with other invocations. Signed-off-by: Aditya Pakki Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/sp8870.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c index 8d31cf3f4f07..270a3c559e08 100644 --- a/drivers/media/dvb-frontends/sp8870.c +++ b/drivers/media/dvb-frontends/sp8870.c @@ -293,7 +293,9 @@ static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) sp8870_writereg(state, 0xc05, reg0xc05); // read status reg in order to clear pending irqs - sp8870_readreg(state, 0x200); + err = sp8870_readreg(state, 0x200); + if (err) + return err; // system controller start sp8870_microcontroller_start(state); From 6af61a0bc43201425afa6052d24f2a67648f69d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 28 Nov 2018 21:01:44 -0500 Subject: [PATCH 0347/4277] media: dt-bindings: adv748x: make data-lanes property mandatory for CSI-2 endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CSI-2 transmitters can use a different number of lanes to transmit data. Make the data-lanes mandatory for the endpoints that describe the transmitters as no good default can be set to fallback on. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/adv748x.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/adv748x.txt b/Documentation/devicetree/bindings/media/i2c/adv748x.txt index 5dddc95f9cc4..4f91686e54a6 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv748x.txt +++ b/Documentation/devicetree/bindings/media/i2c/adv748x.txt @@ -48,7 +48,16 @@ are numbered as follows. TXA source 10 TXB source 11 -The digital output port nodes must contain at least one endpoint. +The digital output port nodes, when present, shall contain at least one +endpoint. Each of those endpoints shall contain the data-lanes property as +described in video-interfaces.txt. + +Required source endpoint properties: + - data-lanes: an array of physical data lane indexes + The accepted value(s) for this property depends on which of the two + sources are described. For TXA 1, 2 or 4 data lanes can be described + while for TXB only 1 data lane is valid. See video-interfaces.txt + for detailed description. Ports are optional if they are not connected to anything at the hardware level. From 90917bc8f68ff8357eccda3a53519a53504a99a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 28 Nov 2018 21:01:45 -0500 Subject: [PATCH 0348/4277] media: i2c: adv748x: reuse power up sequence when initializing CSI-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the MIPI CSI-2 power up sequence to match the power up sequence in the hardware manual chapter "9.5.1 Power Up Sequence". This change allows the power up functions to be reused when initializing the hardware reducing code duplicating as well aligning with the documentation. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 50 ++++++------------------ 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 6854d898fdd1..2384f50dacb0 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -234,6 +234,12 @@ static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = { {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ + {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ @@ -263,6 +269,11 @@ static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = { {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ + {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ @@ -383,25 +394,6 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { {ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */ {ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */ - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ - {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ - - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */ - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; @@ -435,24 +427,6 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { {ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */ {ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */ - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ - {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */ - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; @@ -474,6 +448,7 @@ static int adv748x_reset(struct adv748x_state *state) if (ret) return ret; + adv748x_tx_power(&state->txa, 1); adv748x_tx_power(&state->txa, 0); /* Init and power down TXB */ @@ -481,6 +456,7 @@ static int adv748x_reset(struct adv748x_state *state) if (ret) return ret; + adv748x_tx_power(&state->txb, 1); adv748x_tx_power(&state->txb, 0); /* Disable chip powerdown & Enable HDMI Rx block */ From 6a18865da8e3ebc16b7783c126fbb18be91b5622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 28 Nov 2018 21:01:46 -0500 Subject: [PATCH 0349/4277] media: i2c: adv748x: store number of CSI-2 lanes described in device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adv748x CSI-2 transmitters TXA and TXB can use different number of lanes to transmit data. In order to be able to configure the device correctly this information need to be parsed from device tree and stored in each TX private data structure. TXA supports 1, 2 and 4 lanes while TXB supports 1 lane. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 50 ++++++++++++++++++++++++ drivers/media/i2c/adv748x/adv748x.h | 1 + 2 files changed, 51 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 2384f50dacb0..9d80d7f3062b 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "adv748x.h" @@ -521,12 +522,56 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state, sd->entity.ops = &adv748x_media_ops; } +static int adv748x_parse_csi2_lanes(struct adv748x_state *state, + unsigned int port, + struct device_node *ep) +{ + struct v4l2_fwnode_endpoint vep; + unsigned int num_lanes; + int ret; + + if (port != ADV748X_PORT_TXA && port != ADV748X_PORT_TXB) + return 0; + + vep.bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &vep); + if (ret) + return ret; + + num_lanes = vep.bus.mipi_csi2.num_data_lanes; + + if (vep.base.port == ADV748X_PORT_TXA) { + if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4) { + adv_err(state, "TXA: Invalid number (%u) of lanes\n", + num_lanes); + return -EINVAL; + } + + state->txa.num_lanes = num_lanes; + adv_dbg(state, "TXA: using %u lanes\n", state->txa.num_lanes); + } + + if (vep.base.port == ADV748X_PORT_TXB) { + if (num_lanes != 1) { + adv_err(state, "TXB: Invalid number (%u) of lanes\n", + num_lanes); + return -EINVAL; + } + + state->txb.num_lanes = num_lanes; + adv_dbg(state, "TXB: using %u lanes\n", state->txb.num_lanes); + } + + return 0; +} + static int adv748x_parse_dt(struct adv748x_state *state) { struct device_node *ep_np = NULL; struct of_endpoint ep; bool out_found = false; bool in_found = false; + int ret; for_each_endpoint_of_node(state->dev->of_node, ep_np) { of_graph_parse_endpoint(ep_np, &ep); @@ -557,6 +602,11 @@ static int adv748x_parse_dt(struct adv748x_state *state) in_found = true; else out_found = true; + + /* Store number of CSI-2 lanes used for TXA and TXB. */ + ret = adv748x_parse_csi2_lanes(state, ep.port, ep_np); + if (ret) + return ret; } return in_found && out_found ? 0 : -ENODEV; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 39c2fdc3b416..b482c7fe6957 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -79,6 +79,7 @@ struct adv748x_csi2 { struct v4l2_mbus_framefmt format; unsigned int page; unsigned int port; + unsigned int num_lanes; struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct v4l2_ctrl_handler ctrl_hdl; From 0ad5b80cff509360ef9953ac7ce5a56964210c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 28 Nov 2018 21:01:47 -0500 Subject: [PATCH 0350/4277] media: i2c: adv748x: configure number of lanes used for TXA CSI-2 transmitter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver fixed the TXA CSI-2 transmitter in 4-lane mode while it could operate using 1-, 2- and 4-lanes. Update the driver to support all available modes. The driver makes use of large tables of static register/value writes when powering up/down the TXA and TXB transmitters which include the write to the NUM_LANES register. By converting the tables into functions and using parameters the power up/down functions for TXA and TXB power up/down can be merged and used for both transmitters. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 131 ++++++++++++----------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 9d80d7f3062b..d94c63cb6a2e 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -125,6 +125,16 @@ int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value) return regmap_write(state->regmap[page], reg, value); } +static int adv748x_write_check(struct adv748x_state *state, u8 page, u8 reg, + u8 value, int *error) +{ + if (*error) + return *error; + + *error = adv748x_write(state, page, reg, value); + return *error; +} + /* adv748x_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX * size to one or more registers. * @@ -231,79 +241,77 @@ static int adv748x_write_regs(struct adv748x_state *state, * TXA and TXB */ -static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = { +static int adv748x_power_up_tx(struct adv748x_csi2 *tx) +{ + struct adv748x_state *state = tx->state; + u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; + int ret = 0; - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ - {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ + /* Enable n-lane MIPI */ + adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret); - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */ + /* Set Auto DPHY Timing */ + adv748x_write_check(state, page, 0x00, 0xa0 | tx->num_lanes, &ret); - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + /* ADI Required Write */ + if (is_txa(tx)) { + adv748x_write_check(state, page, 0xdb, 0x10, &ret); + adv748x_write_check(state, page, 0xd6, 0x07, &ret); + } else { + adv748x_write_check(state, page, 0xd2, 0x40, &ret); + } -static const struct adv748x_reg_value adv748x_power_down_txa_4lane[] = { + adv748x_write_check(state, page, 0xc4, 0x0a, &ret); + adv748x_write_check(state, page, 0x71, 0x33, &ret); + adv748x_write_check(state, page, 0x72, 0x11, &ret); - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x1e, 0x00}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_TXA, 0xc1, 0x3b}, /* ADI Required Write */ + /* i2c_dphy_pwdn - 1'b0 */ + adv748x_write_check(state, page, 0xf0, 0x00, &ret); - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + /* ADI Required Writes*/ + adv748x_write_check(state, page, 0x31, 0x82, &ret); + adv748x_write_check(state, page, 0x1e, 0x40, &ret); -static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = { + /* i2c_mipi_pll_en - 1'b1 */ + adv748x_write_check(state, page, 0xda, 0x01, &ret); + usleep_range(2000, 2500); - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ - {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ + /* Power-up CSI-TX */ + adv748x_write_check(state, page, 0x00, 0x20 | tx->num_lanes, &ret); + usleep_range(1000, 1500); - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */ + /* ADI Required Writes */ + adv748x_write_check(state, page, 0xc1, 0x2b, &ret); + usleep_range(1000, 1500); + adv748x_write_check(state, page, 0x31, 0x80, &ret); - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + return ret; +} -static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = { +static int adv748x_power_down_tx(struct adv748x_csi2 *tx) +{ + struct adv748x_state *state = tx->state; + u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; + int ret = 0; - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */ + /* ADI Required Writes */ + adv748x_write_check(state, page, 0x31, 0x82, &ret); + adv748x_write_check(state, page, 0x1e, 0x00, &ret); - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + /* Enable n-lane MIPI */ + adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret); + + /* i2c_mipi_pll_en - 1'b1 */ + adv748x_write_check(state, page, 0xda, 0x01, &ret); + + /* ADI Required Write */ + adv748x_write_check(state, page, 0xc1, 0x3b, &ret); + + return ret; +} int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) { - struct adv748x_state *state = tx->state; - const struct adv748x_reg_value *reglist; int val; if (!is_tx_enabled(tx)) @@ -321,14 +329,7 @@ int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN), "Enabling with unknown bit set"); - if (on) - reglist = is_txa(tx) ? adv748x_power_up_txa_4lane : - adv748x_power_up_txb_1lane; - else - reglist = is_txa(tx) ? adv748x_power_down_txa_4lane : - adv748x_power_down_txb_1lane; - - return adv748x_write_regs(state, reglist); + return on ? adv748x_power_up_tx(tx) : adv748x_power_down_tx(tx); } /* ----------------------------------------------------------------------------- From 9640ee1c308c1bb21c224a0cf41d9ed1b7e3a427 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:21:49 -0500 Subject: [PATCH 0351/4277] media: dt-bindings: rcar-csi2: Add r8a774c0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the compatible string for RZ/G2E (a.k.a. R8A774C0) to the list of supported SoCs. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt b/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt index 541d936b62e8..d63275e17afd 100644 --- a/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt +++ b/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt @@ -2,12 +2,13 @@ Renesas R-Car MIPI CSI-2 ------------------------ The R-Car CSI-2 receiver device provides MIPI CSI-2 capabilities for the -Renesas R-Car family of devices. It is used in conjunction with the +Renesas R-Car and RZ/G2 family of devices. It is used in conjunction with the R-Car VIN module, which provides the video capture capabilities. Mandatory properties -------------------- - compatible: Must be one or more of the following + - "renesas,r8a774c0-csi2" for the R8A774C0 device. - "renesas,r8a7795-csi2" for the R8A7795 device. - "renesas,r8a7796-csi2" for the R8A7796 device. - "renesas,r8a77965-csi2" for the R8A77965 device. From 73960b787c615b41333600e1291f6a47ff7db21c Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:21:59 -0500 Subject: [PATCH 0352/4277] media: dt-bindings: rcar-vin: Add R8A774C0 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the compatible string for RZ/G2E (a.k.a. R8A774C0) to the list of SoCs supported by rcar-vin driver. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index 0dd84a183ca7..224a4615b418 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -7,12 +7,13 @@ family of devices. Each VIN instance has a single parallel input that supports RGB and YUV video, with both external synchronization and BT.656 synchronization for the latter. Depending on the instance the VIN input is connected to external SoC pins, or -on Gen3 platforms to a CSI-2 receiver. +on Gen3 and RZ/G2 platforms to a CSI-2 receiver. - compatible: Must be one or more of the following - "renesas,vin-r8a7743" for the R8A7743 device - "renesas,vin-r8a7744" for the R8A7744 device - "renesas,vin-r8a7745" for the R8A7745 device + - "renesas,vin-r8a774c0" for the R8A774C0 device - "renesas,vin-r8a7778" for the R8A7778 device - "renesas,vin-r8a7779" for the R8A7779 device - "renesas,vin-r8a7790" for the R8A7790 device @@ -61,10 +62,10 @@ The per-board settings Gen2 platforms: - data-enable-active: polarity of CLKENB signal, see [1] for description. Default is active high. -The per-board settings Gen3 platforms: +The per-board settings Gen3 and RZ/G2 platforms: -Gen3 platforms can support both a single connected parallel input source -from external SoC pins (port@0) and/or multiple parallel input sources +Gen3 and RZ/G2 platforms can support both a single connected parallel input +source from external SoC pins (port@0) and/or multiple parallel input sources from local SoC CSI-2 receivers (port@1) depending on SoC. - renesas,id - ID number of the VIN, VINx in the documentation. From f74e5a32fa0c03d9f4bb84ea20d0c7be34fe3725 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:24:04 -0500 Subject: [PATCH 0353/4277] media: rcar-vin: Add support for RZ/G2E MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the RZ/G2 User's manual, RZ/G2E and R-Car E3 VIN blocks are identical, therefore use R-Car E3 definitions to add RZ/G2E support. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 0e81b557f3b6..b28e3ff191c2 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1131,6 +1131,10 @@ static const struct rvin_info rcar_info_r8a77995 = { }; static const struct of_device_id rvin_of_id_table[] = { + { + .compatible = "renesas,vin-r8a774c0", + .data = &rcar_info_r8a77990, + }, { .compatible = "renesas,vin-r8a7778", .data = &rcar_info_m1, From 8809b8ceb0bd283c0528a0c7233656b12bcda2dc Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:24:12 -0500 Subject: [PATCH 0354/4277] media: rcar-csi2: Add support for RZ/G2E MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the RZ/G2 User's manual, RZ/G2E and R-Car E3 CSI-2 blocks are identical, therefore use R-Car E3 definitions to add RZ/G2E support. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index b2bd0b9627d9..f64528d2be3c 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -985,6 +985,10 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { }; static const struct of_device_id rcar_csi2_of_table[] = { + { + .compatible = "renesas,r8a774c0-csi2", + .data = &rcar_csi2_info_r8a77990, + }, { .compatible = "renesas,r8a7795-csi2", .data = &rcar_csi2_info_r8a7795, From 8db63f3d14dc44c043afc00e5adeb541106ec16b Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 9 Jan 2019 06:24:56 -0500 Subject: [PATCH 0355/4277] media: saa7146: make use of i2c_8bit_addr_from_msg Because it looks neater. Signed-off-by: Peter Rosin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_i2c.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c index 3feddc52c446..df9ebe2a168c 100644 --- a/drivers/media/common/saa7146/saa7146_i2c.c +++ b/drivers/media/common/saa7146/saa7146_i2c.c @@ -54,10 +54,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) /* loop through all messages */ for(i = 0; i < num; i++) { - /* insert the address of the i2c-slave. - note: we get 7 bit i2c-addresses, - so we have to perform a translation */ - addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0); + addr = i2c_8bit_addr_from_msg(&m[i]); h1 = op_count/3; h2 = op_count%3; op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8)); op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2)); From 19c624c6b29e244c418f8b44a711cbf5e82e3cd4 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Wed, 9 Jan 2019 13:00:41 -0500 Subject: [PATCH 0356/4277] media: s5p-jpeg: Correct step and max values for V4L2_CID_JPEG_RESTART_INTERVAL This commit corrects max and step values for v4l2 control for V4L2_CID_JPEG_RESTART_INTERVAL. Max should be 0xffff and step should be 1. It was found by using v4l2-compliance tool and checking result of VIDIOC_QUERY_EXT_CTRL/QUERYMENU test. Previously it was complaining that step was bigger than difference between max and min. Fixes: 15f4bc3b1f42 ("[media] s5p-jpeg: Add JPEG controls support") Signed-off-by: Pawe? Chmiel Reviewed-by: Jacek Anaszewski Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 85110766043f..0a23b2d19e14 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2005,7 +2005,7 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, V4L2_CID_JPEG_RESTART_INTERVAL, - 0, 3, 0xffff, 0); + 0, 0xffff, 1, 0); if (ctx->jpeg->variant->version == SJPEG_S5P) mask = ~0x06; /* 422, 420 */ } From b9da9b376711f61d0e3c3e419ee348249ca2bd80 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 30 Nov 2018 17:30:48 -0500 Subject: [PATCH 0357/4277] media: ipu3-cio2: Allow probe to succeed if there are no sensors connected The device won't be powered off on systems that have no sensors connected unless it has a driver bound to it. Allow that to happen even if there are no sensors connected to cio2. Reviewed-by: Rajneesh Bhardwaj Tested-by: Rajneesh Bhardwaj Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 447baaebca44..e281e55cdca4 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1810,7 +1810,8 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, /* Register notifier for subdevices we care */ r = cio2_notifier_init(cio2); - if (r) + /* Proceed without sensors connected to allow the device to suspend. */ + if (r && r != -ENODEV) goto fail_cio2_queue_exit; r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq, From 47ee7bde31fa035fbe92a55ca8782a4eafefb0b6 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 5 Dec 2018 11:32:21 -0500 Subject: [PATCH 0358/4277] media: imx274: fix wrong order in test pattern menus The description of test patterns 11 and 12 are swapped. Checked against the live sensor. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 5fac7fd32634..95e3d90309e8 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -207,8 +207,8 @@ static const char * const tp_qmenu[] = { "Vertical Stripe (555h / 000h)", "Vertical Stripe (000h / FFFh)", "Vertical Stripe (FFFh / 000h)", - "Horizontal Color Bars", "Vertical Color Bars", + "Horizontal Color Bars", }; /* From b0a7d0e1c09b70a07f3251af7d4e538f310245f4 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 9 Dec 2018 00:20:31 -0500 Subject: [PATCH 0359/4277] media: ov2640: set default window and format code at probe time Set default window and format code at probe time instead of always checking if they have not been set yet when VIDIOC_SUBDEV_G_FMT ioctl is called. This change simplifies the next patch (make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY). Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 5d2d6735cc78..1fd893be76f3 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -842,9 +842,6 @@ static int ov2640_set_params(struct i2c_client *client, u8 val; int ret; - if (!win) - return -EINVAL; - switch (code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__); @@ -929,10 +926,6 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; - if (!priv->win) { - priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); - priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; - } mf->width = priv->win->width; mf->height = priv->win->height; @@ -1193,6 +1186,9 @@ static int ov2640_probe(struct i2c_client *client, if (ret) goto err_clk; + priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); + priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; + v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; From 8d3b307a150a50cb7c1c905209488dbfe9b6221a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 9 Dec 2018 00:20:32 -0500 Subject: [PATCH 0360/4277] media: ov2640: make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE is specified. Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 1fd893be76f3..d3f10d92fcc0 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -926,6 +926,15 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *mf; + return 0; +#else + return -ENOTTY; +#endif + } mf->width = priv->win->width; mf->height = priv->win->height; @@ -992,6 +1001,27 @@ out: return ret; } +static int ov2640_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, cfg, 0); + + try_fmt->width = priv->win->width; + try_fmt->height = priv->win->height; + try_fmt->code = priv->cfmt_code; + try_fmt->colorspace = V4L2_COLORSPACE_SRGB; + try_fmt->field = V4L2_FIELD_NONE; + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +#endif + return 0; +} + static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -1101,6 +1131,7 @@ static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { }; static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { + .init_cfg = ov2640_init_cfg, .enum_mbus_code = ov2640_enum_mbus_code, .get_selection = ov2640_get_selection, .get_fmt = ov2640_get_fmt, From bd0405f929ce4c8b8bd0feca0a8efe144345306b Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 9 Dec 2018 00:20:33 -0500 Subject: [PATCH 0361/4277] media: ov2640: set all mbus format field when G_FMT and S_FMT ioctls This driver doesn't set all members of mbus format field when the VIDIOC_SUBDEV_{S,G}_FMT ioctls are called. This is detected by v4l2-compliance. Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index d3f10d92fcc0..37e7c986af86 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -941,6 +941,9 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, mf->code = priv->cfmt_code; mf->colorspace = V4L2_COLORSPACE_SRGB; mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; return 0; } @@ -967,6 +970,9 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; switch (mf->code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: From fe367be508473dac43c3277062555d3ab2b995ec Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 14 Nov 2018 07:13:38 -0500 Subject: [PATCH 0362/4277] media: dt-bindings: media: i2c: Fix external clock frequency for OV5645 Commit "4adb0a0432f4 media: ov5645: Supported external clock is 24MHz" modified the external clock frequency to be 24MHz instead of the 23.88MHz in driver. Hence, modify the frequency value in binding. Signed-off-by: Manivannan Sadhasivam Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/ov5645.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ov5645.txt b/Documentation/devicetree/bindings/media/i2c/ov5645.txt index fd7aec9f8e24..b032abfcea36 100644 --- a/Documentation/devicetree/bindings/media/i2c/ov5645.txt +++ b/Documentation/devicetree/bindings/media/i2c/ov5645.txt @@ -37,7 +37,7 @@ Example: clocks = <&clks 200>; clock-names = "xclk"; - clock-frequency = <23880000>; + clock-frequency = <24000000>; vdddo-supply = <&camera_dovdd_1v8>; vdda-supply = <&camera_avdd_2v8>; From 961304d17a615f42c38b867640980a3390e3baa6 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 9 Nov 2018 02:56:43 -0500 Subject: [PATCH 0363/4277] media: dt-bindings: media: i2c: Fix i2c address for OV5645 camera sensor The i2c address for the Omnivision OV5645 camera sensor is 0x3c. It is incorrectly mentioned as 0x78 in binding. Hence fix that. Fixes: 09c716af36e6 [media] media: i2c/ov5645: add the device tree binding document Signed-off-by: Manivannan Sadhasivam Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/ov5645.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ov5645.txt b/Documentation/devicetree/bindings/media/i2c/ov5645.txt index b032abfcea36..72ad992f77be 100644 --- a/Documentation/devicetree/bindings/media/i2c/ov5645.txt +++ b/Documentation/devicetree/bindings/media/i2c/ov5645.txt @@ -26,9 +26,9 @@ Example: &i2c1 { ... - ov5645: ov5645@78 { + ov5645: ov5645@3c { compatible = "ovti,ov5645"; - reg = <0x78>; + reg = <0x3c>; enable-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio5 20 GPIO_ACTIVE_LOW>; From b7ed3abd808afd90ce8a9701799af8a158846b14 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 2 Nov 2018 12:38:43 -0400 Subject: [PATCH 0364/4277] media: ov5640: Add RAW bayer format support OV5640 sensor supports raw image output (bayer). Configure ISP mux/format registers accordingly. Signed-off-by: Loic Poulain Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 58 ++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index bef3f3aae0ed..5a909abd0a2d 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -115,6 +115,15 @@ enum ov5640_frame_rate { OV5640_NUM_FRAMERATES, }; +enum ov5640_format_mux { + OV5640_FMT_MUX_YUV422 = 0, + OV5640_FMT_MUX_RGB, + OV5640_FMT_MUX_DITHER, + OV5640_FMT_MUX_RAW_DPC, + OV5640_FMT_MUX_SNR_RAW, + OV5640_FMT_MUX_RAW_CIP, +}; + struct ov5640_pixfmt { u32 code; u32 colorspace; @@ -126,6 +135,10 @@ static const struct ov5640_pixfmt ov5640_formats[] = { { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, }, }; /* @@ -2200,46 +2213,67 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, struct v4l2_mbus_framefmt *format) { int ret = 0; - bool is_rgb = false; bool is_jpeg = false; - u8 val; + u8 fmt, mux; switch (format->code) { case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422, UYVY */ - val = 0x3f; + fmt = 0x3f; + mux = OV5640_FMT_MUX_YUV422; break; case MEDIA_BUS_FMT_YUYV8_2X8: /* YUV422, YUYV */ - val = 0x30; + fmt = 0x30; + mux = OV5640_FMT_MUX_YUV422; break; case MEDIA_BUS_FMT_RGB565_2X8_LE: /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ - val = 0x6F; - is_rgb = true; + fmt = 0x6F; + mux = OV5640_FMT_MUX_RGB; break; case MEDIA_BUS_FMT_RGB565_2X8_BE: /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ - val = 0x61; - is_rgb = true; + fmt = 0x61; + mux = OV5640_FMT_MUX_RGB; break; case MEDIA_BUS_FMT_JPEG_1X8: /* YUV422, YUYV */ - val = 0x30; + fmt = 0x30; + mux = OV5640_FMT_MUX_YUV422; is_jpeg = true; break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + /* Raw, BGBG... / GRGR... */ + fmt = 0x00; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + /* Raw bayer, GBGB... / RGRG... */ + fmt = 0x01; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + /* Raw bayer, GRGR... / BGBG... */ + fmt = 0x02; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* Raw bayer, RGRG... / GBGB... */ + fmt = 0x03; + mux = OV5640_FMT_MUX_RAW_DPC; + break; default: return -EINVAL; } /* FORMAT CONTROL00: YUV and RGB formatting */ - ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, val); + ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt); if (ret) return ret; /* FORMAT MUX CONTROL: ISP YUV or RGB */ - ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, - is_rgb ? 0x01 : 0x00); + ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux); if (ret) return ret; From 61da76beef1e4f0b6ba7be4f8d0cf0dac7ce1f55 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 29 Dec 2017 07:22:26 -0500 Subject: [PATCH 0365/4277] media: v4l2: i2c: ov7670: Fix PLL bypass register values The following commits: commit f6dd927f34d6 ("[media] media: ov7670: calculate framerate properly for ov7675") commit 04ee6d92047e ("[media] media: ov7670: add possibility to bypass pll for ov7675") introduced the ability to bypass PLL multiplier and use input clock (xvclk) as pixel clock output frequency for ov7675 sensor. PLL is bypassed using register DBLV[7:6], according to ov7670 and ov7675 sensor manuals. Macros used to set DBLV register seem wrong in the driver, as their values do not match what reported in the datasheet. Fix by changing DBLV_* macros to use bits [7:6] and set bits [3:0] to default 0x0a reserved value (according to datasheets). While at there, remove a write to DBLV register in "ov7675_set_framerate()" that over-writes the previous one to the same register that takes "info->pll_bypass" flag into account instead of setting PLL multiplier to 4x unconditionally. And, while at there, since "info->pll_bypass" is only used in set/get_framerate() functions used by ov7675 only, it is not necessary to check for the device id at probe time to make sure that when using ov7670 "info->pll_bypass" is set to false. Fixes: f6dd927f34d6 ("[media] media: ov7670: calculate framerate properly for ov7675") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index a70a6ff7b36e..4939a83b50e4 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -160,10 +160,10 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_GFIX 0x69 /* Fix gain control */ #define REG_DBLV 0x6b /* PLL control an debugging */ -#define DBLV_BYPASS 0x00 /* Bypass PLL */ -#define DBLV_X4 0x01 /* clock x4 */ -#define DBLV_X6 0x10 /* clock x6 */ -#define DBLV_X8 0x11 /* clock x8 */ +#define DBLV_BYPASS 0x0a /* Bypass PLL */ +#define DBLV_X4 0x4a /* clock x4 */ +#define DBLV_X6 0x8a /* clock x6 */ +#define DBLV_X8 0xca /* clock x8 */ #define REG_SCALING_XSC 0x70 /* Test pattern and horizontal scale factor */ #define TEST_PATTTERN_0 0x80 @@ -863,7 +863,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, if (ret < 0) return ret; - return ov7670_write(sd, REG_DBLV, DBLV_X4); + return 0; } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, @@ -1801,11 +1801,7 @@ static int ov7670_probe(struct i2c_client *client, if (config->clock_speed) info->clock_speed = config->clock_speed; - /* - * It should be allowed for ov7670 too when it is migrated to - * the new frame rate formula. - */ - if (config->pll_bypass && id->driver_data != MODEL_OV7670) + if (config->pll_bypass) info->pll_bypass = true; if (config->pclk_hb_disable) From 57b0ad9ebe60366b1243bcf612a93a990a3ceff1 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:12 -0500 Subject: [PATCH 0366/4277] media: soc_camera: ov9640: move ov9640 out of soc_camera Initial part of ov9640 transition from soc_camera subsystem to a standalone v4l2 subdevice. The soc_camera version seems to be used only in Palm Zire72 and in (the future) HTC Magician. On these two devices the support is broken as pxa_camera driver doesn't use soc_camera anymore. The other mentions from git grep are "TODOs" (in board-osk.c) or chip names for unsupported sensors on HW which doesn't use soc_camera at all (irelevant). Copy the driver files from soc_camera and mark the original ones in the Kconfig description as obsoleted. Add config option VIDEO_OV9640 to the build files in drivers/media/i2c. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov9640.c | 738 +++++++++++++++++++++++++++ drivers/media/i2c/ov9640.h | 208 ++++++++ drivers/media/i2c/soc_camera/Kconfig | 6 +- 5 files changed, 958 insertions(+), 2 deletions(-) create mode 100644 drivers/media/i2c/ov9640.c create mode 100644 drivers/media/i2c/ov9640.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 4c936e129500..119aaee5318b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -820,6 +820,13 @@ config VIDEO_OV7740 This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. +config VIDEO_OV9640 + tristate "OmniVision OV9640 sensor support" + depends on I2C && VIDEO_V4L2 + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9640 camera sensor. + config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 65fae7732de0..5639cb2f20aa 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c new file mode 100644 index 000000000000..eb91b8240083 --- /dev/null +++ b/drivers/media/i2c/ov9640.c @@ -0,0 +1,738 @@ +/* + * OmniVision OV96xx Camera Driver + * + * Copyright (C) 2009 Marek Vasut + * + * Based on ov772x camera driver: + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ov9640.h" + +#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) + +/* default register setup */ +static const struct ov9640_reg ov9640_regs_dflt[] = { + { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, + { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | + OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, + { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, + { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, + { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, + { OV9640_COM16, OV9640_COM16_RB_AVG }, + + /* Gamma curve P */ + { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, + { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, + { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, + { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, + + /* Gamma curve T */ + { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, + { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, + { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, + { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, +}; + +/* Configurations + * NOTE: for YUV, alter the following registers: + * COM12 |= OV9640_COM12_YUV_AVG + * + * for RGB, alter the following registers: + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode + */ +static const struct ov9640_reg ov9640_regs_qqcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qqvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_cif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_CIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_vga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_VGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_sxga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, 0 }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_yuv[] = { + { OV9640_MTX1, 0x58 }, + { OV9640_MTX2, 0x48 }, + { OV9640_MTX3, 0x10 }, + { OV9640_MTX4, 0x28 }, + { OV9640_MTX5, 0x48 }, + { OV9640_MTX6, 0x70 }, + { OV9640_MTX7, 0x40 }, + { OV9640_MTX8, 0x40 }, + { OV9640_MTX9, 0x40 }, + { OV9640_MTXS, 0x0f }, +}; + +static const struct ov9640_reg ov9640_regs_rgb[] = { + { OV9640_MTX1, 0x71 }, + { OV9640_MTX2, 0x3e }, + { OV9640_MTX3, 0x0c }, + { OV9640_MTX4, 0x33 }, + { OV9640_MTX5, 0x72 }, + { OV9640_MTX6, 0x00 }, + { OV9640_MTX7, 0x2b }, + { OV9640_MTX8, 0x66 }, + { OV9640_MTX9, 0xd2 }, + { OV9640_MTXS, 0x65 }, +}; + +static u32 ov9640_codes[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + MEDIA_BUS_FMT_RGB565_2X8_LE, +}; + +/* read a register */ +static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) +{ + int ret; + u8 data = reg; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + *val = data; + return 0; + +err: + dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); + return ret; +} + +/* write a register */ +static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + u8 _val; + unsigned char data[2] = { reg, val }; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); + return ret; + } + + /* we have to read the register back ... no idea why, maybe HW bug */ + ret = ov9640_reg_read(client, reg, &_val); + if (ret) + dev_err(&client->dev, + "Failed reading back register 0x%02x!\n", reg); + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9640_reg_read(client, reg, &val); + if (ret) { + dev_err(&client->dev, + "[Read]-Modify-Write of register %02x failed!\n", reg); + return ret; + } + + val |= set; + val &= ~unset; + + ret = ov9640_reg_write(client, reg, val); + if (ret) + dev_err(&client->dev, + "Read-Modify-[Write] of register %02x failed!\n", reg); + + return ret; +} + +/* Soft reset the camera. This has nothing to do with the RESET pin! */ +static int ov9640_reset(struct i2c_client *client) +{ + int ret; + + ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); + if (ret) + dev_err(&client->dev, + "An error occurred while entering soft reset!\n"); + + return ret; +} + +/* Start/Stop streaming from the device */ +static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + return ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_V, 0); + return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); + case V4L2_CID_HFLIP: + if (ctrl->val) + return ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_H, 0); + return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); + } + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9640_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xff) + return -EINVAL; + + reg->size = 1; + + ret = ov9640_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return 0; +} + +static int ov9640_set_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xff || reg->val & ~0xff) + return -EINVAL; + + return ov9640_reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov9640_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov9640_priv *priv = to_ov9640_sensor(sd); + + return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); +} + +/* select nearest higher resolution for capture */ +static void ov9640_res_roundup(u32 *width, u32 *height) +{ + int i; + enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; + static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + + for (i = 0; i < ARRAY_SIZE(res_x); i++) { + if (res_x[i] >= *width && res_y[i] >= *height) { + *width = res_x[i]; + *height = res_y[i]; + return; + } + } + + *width = res_x[SXGA]; + *height = res_y[SXGA]; +} + +/* Prepare necessary register changes depending on color encoding */ +static void ov9640_alter_regs(u32 code, + struct ov9640_reg_alt *alt) +{ + switch (code) { + default: + case MEDIA_BUS_FMT_UYVY8_2X8: + alt->com12 = OV9640_COM12_YUV_AVG; + alt->com13 = OV9640_COM13_Y_DELAY_EN | + OV9640_COM13_YUV_DLY(0x01); + break; + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_555; + break; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_565; + break; + } +} + +/* Setup registers according to resolution and color encoding */ +static int ov9640_write_regs(struct i2c_client *client, u32 width, + u32 code, struct ov9640_reg_alt *alts) +{ + const struct ov9640_reg *ov9640_regs, *matrix_regs; + int ov9640_regs_len, matrix_regs_len; + int i, ret; + u8 val; + + /* select register configuration for given resolution */ + switch (width) { + case W_QQCIF: + ov9640_regs = ov9640_regs_qqcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); + break; + case W_QQVGA: + ov9640_regs = ov9640_regs_qqvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); + break; + case W_QCIF: + ov9640_regs = ov9640_regs_qcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); + break; + case W_QVGA: + ov9640_regs = ov9640_regs_qvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); + break; + case W_CIF: + ov9640_regs = ov9640_regs_cif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); + break; + case W_VGA: + ov9640_regs = ov9640_regs_vga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); + break; + case W_SXGA: + ov9640_regs = ov9640_regs_sxga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); + break; + default: + dev_err(&client->dev, "Failed to select resolution!\n"); + return -EINVAL; + } + + /* select color matrix configuration for given color encoding */ + if (code == MEDIA_BUS_FMT_UYVY8_2X8) { + matrix_regs = ov9640_regs_yuv; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); + } else { + matrix_regs = ov9640_regs_rgb; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); + } + + /* write register settings into the module */ + for (i = 0; i < ov9640_regs_len; i++) { + val = ov9640_regs[i].val; + + switch (ov9640_regs[i].reg) { + case OV9640_COM7: + val |= alts->com7; + break; + case OV9640_COM12: + val |= alts->com12; + break; + case OV9640_COM13: + val |= alts->com13; + break; + case OV9640_COM15: + val |= alts->com15; + break; + } + + ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); + if (ret) + return ret; + } + + /* write color matrix configuration into the module */ + for (i = 0; i < matrix_regs_len; i++) { + ret = ov9640_reg_write(client, matrix_regs[i].reg, + matrix_regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/* program default register values */ +static int ov9640_prog_dflt(struct i2c_client *client) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { + ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, + ov9640_regs_dflt[i].val); + if (ret) + return ret; + } + + /* wait for the changes to actually happen, 140ms are not enough yet */ + mdelay(150); + + return 0; +} + +/* set the format we will capture in */ +static int ov9640_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9640_reg_alt alts = {0}; + int ret; + + ov9640_alter_regs(mf->code, &alts); + + ov9640_reset(client); + + ret = ov9640_prog_dflt(client); + if (ret) + return ret; + + return ov9640_write_regs(client, mf->width, mf->code, &alts); +} + +static int ov9640_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + ov9640_res_roundup(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + + switch (mf->code) { + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + mf->colorspace = V4L2_COLORSPACE_SRGB; + break; + default: + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + /* fall through */ + case MEDIA_BUS_FMT_UYVY8_2X8: + mf->colorspace = V4L2_COLORSPACE_JPEG; + break; + } + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov9640_s_fmt(sd, mf); + + cfg->try_fmt = *mf; + return 0; +} + +static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) + return -EINVAL; + + code->code = ov9640_codes[code->index]; + return 0; +} + +static int ov9640_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + sel->r.left = 0; + sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + sel->r.width = W_SXGA; + sel->r.height = H_SXGA; + return 0; + default: + return -EINVAL; + } +} + +static int ov9640_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9640_priv *priv = to_ov9640_sensor(sd); + u8 pid, ver, midh, midl; + const char *devname; + int ret; + + ret = ov9640_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* + * check and show product ID and manufacturer ID + */ + + ret = ov9640_reg_read(client, OV9640_PID, &pid); + if (!ret) + ret = ov9640_reg_read(client, OV9640_VER, &ver); + if (!ret) + ret = ov9640_reg_read(client, OV9640_MIDH, &midh); + if (!ret) + ret = ov9640_reg_read(client, OV9640_MIDL, &midl); + if (ret) + goto done; + + switch (VERSION(pid, ver)) { + case OV9640_V2: + devname = "ov9640"; + priv->revision = 2; + break; + case OV9640_V3: + devname = "ov9640"; + priv->revision = 3; + break; + default: + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, midh, midl); + + ret = v4l2_ctrl_handler_setup(&priv->hdl); + +done: + ov9640_s_power(&priv->subdev, 0); + return ret; +} + +static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { + .s_ctrl = ov9640_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops ov9640_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9640_get_register, + .s_register = ov9640_set_register, +#endif + .s_power = ov9640_s_power, +}; + +/* Request bus settings on camera side */ +static int ov9640_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov9640_video_ops = { + .s_stream = ov9640_s_stream, + .g_mbus_config = ov9640_g_mbus_config, +}; + +static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { + .enum_mbus_code = ov9640_enum_mbus_code, + .get_selection = ov9640_get_selection, + .set_fmt = ov9640_set_fmt, +}; + +static const struct v4l2_subdev_ops ov9640_subdev_ops = { + .core = &ov9640_core_ops, + .video = &ov9640_video_ops, + .pad = &ov9640_pad_ops, +}; + +/* + * i2c_driver function + */ +static int ov9640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9640_priv *priv; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); + + v4l2_ctrl_handler_init(&priv->hdl, 2); + v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) + return priv->hdl.error; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto eclkget; + } + + ret = ov9640_video_probe(client); + if (ret) { + v4l2_clk_put(priv->clk); +eclkget: + v4l2_ctrl_handler_free(&priv->hdl); + } + + return ret; +} + +static int ov9640_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9640_priv *priv = to_ov9640_sensor(sd); + + v4l2_clk_put(priv->clk); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); + return 0; +} + +static const struct i2c_device_id ov9640_id[] = { + { "ov9640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9640_id); + +static struct i2c_driver ov9640_i2c_driver = { + .driver = { + .name = "ov9640", + }, + .probe = ov9640_probe, + .remove = ov9640_remove, + .id_table = ov9640_id, +}; + +module_i2c_driver(ov9640_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h new file mode 100644 index 000000000000..65d13ff17536 --- /dev/null +++ b/drivers/media/i2c/ov9640.h @@ -0,0 +1,208 @@ +/* + * OmniVision OV96xx Camera Header File + * + * Copyright (C) 2009 Marek Vasut + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ +#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ + +/* Register definitions */ +#define OV9640_GAIN 0x00 +#define OV9640_BLUE 0x01 +#define OV9640_RED 0x02 +#define OV9640_VFER 0x03 +#define OV9640_COM1 0x04 +#define OV9640_BAVE 0x05 +#define OV9640_GEAVE 0x06 +#define OV9640_RSID 0x07 +#define OV9640_RAVE 0x08 +#define OV9640_COM2 0x09 +#define OV9640_PID 0x0a +#define OV9640_VER 0x0b +#define OV9640_COM3 0x0c +#define OV9640_COM4 0x0d +#define OV9640_COM5 0x0e +#define OV9640_COM6 0x0f +#define OV9640_AECH 0x10 +#define OV9640_CLKRC 0x11 +#define OV9640_COM7 0x12 +#define OV9640_COM8 0x13 +#define OV9640_COM9 0x14 +#define OV9640_COM10 0x15 +/* 0x16 - RESERVED */ +#define OV9640_HSTART 0x17 +#define OV9640_HSTOP 0x18 +#define OV9640_VSTART 0x19 +#define OV9640_VSTOP 0x1a +#define OV9640_PSHFT 0x1b +#define OV9640_MIDH 0x1c +#define OV9640_MIDL 0x1d +#define OV9640_MVFP 0x1e +#define OV9640_LAEC 0x1f +#define OV9640_BOS 0x20 +#define OV9640_GBOS 0x21 +#define OV9640_GROS 0x22 +#define OV9640_ROS 0x23 +#define OV9640_AEW 0x24 +#define OV9640_AEB 0x25 +#define OV9640_VPT 0x26 +#define OV9640_BBIAS 0x27 +#define OV9640_GBBIAS 0x28 +/* 0x29 - RESERVED */ +#define OV9640_EXHCH 0x2a +#define OV9640_EXHCL 0x2b +#define OV9640_RBIAS 0x2c +#define OV9640_ADVFL 0x2d +#define OV9640_ADVFH 0x2e +#define OV9640_YAVE 0x2f +#define OV9640_HSYST 0x30 +#define OV9640_HSYEN 0x31 +#define OV9640_HREF 0x32 +#define OV9640_CHLF 0x33 +#define OV9640_ARBLM 0x34 +/* 0x35..0x36 - RESERVED */ +#define OV9640_ADC 0x37 +#define OV9640_ACOM 0x38 +#define OV9640_OFON 0x39 +#define OV9640_TSLB 0x3a +#define OV9640_COM11 0x3b +#define OV9640_COM12 0x3c +#define OV9640_COM13 0x3d +#define OV9640_COM14 0x3e +#define OV9640_EDGE 0x3f +#define OV9640_COM15 0x40 +#define OV9640_COM16 0x41 +#define OV9640_COM17 0x42 +/* 0x43..0x4e - RESERVED */ +#define OV9640_MTX1 0x4f +#define OV9640_MTX2 0x50 +#define OV9640_MTX3 0x51 +#define OV9640_MTX4 0x52 +#define OV9640_MTX5 0x53 +#define OV9640_MTX6 0x54 +#define OV9640_MTX7 0x55 +#define OV9640_MTX8 0x56 +#define OV9640_MTX9 0x57 +#define OV9640_MTXS 0x58 +/* 0x59..0x61 - RESERVED */ +#define OV9640_LCC1 0x62 +#define OV9640_LCC2 0x63 +#define OV9640_LCC3 0x64 +#define OV9640_LCC4 0x65 +#define OV9640_LCC5 0x66 +#define OV9640_MANU 0x67 +#define OV9640_MANV 0x68 +#define OV9640_HV 0x69 +#define OV9640_MBD 0x6a +#define OV9640_DBLV 0x6b +#define OV9640_GSP 0x6c /* ... till 0x7b */ +#define OV9640_GST 0x7c /* ... till 0x8a */ + +#define OV9640_CLKRC_DPLL_EN 0x80 +#define OV9640_CLKRC_DIRECT 0x40 +#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) + +#define OV9640_PSHFT_VAL(x) ((x) & 0xff) + +#define OV9640_ACOM_2X_ANALOG 0x80 +#define OV9640_ACOM_RSVD 0x12 + +#define OV9640_MVFP_V 0x10 +#define OV9640_MVFP_H 0x20 + +#define OV9640_COM1_HREF_NOSKIP 0x00 +#define OV9640_COM1_HREF_2SKIP 0x04 +#define OV9640_COM1_HREF_3SKIP 0x08 +#define OV9640_COM1_QQFMT 0x20 + +#define OV9640_COM2_SSM 0x10 + +#define OV9640_COM3_VP 0x04 + +#define OV9640_COM4_QQ_VP 0x80 +#define OV9640_COM4_RSVD 0x40 + +#define OV9640_COM5_SYSCLK 0x80 +#define OV9640_COM5_LONGEXP 0x01 + +#define OV9640_COM6_OPT_BLC 0x40 +#define OV9640_COM6_ADBLC_BIAS 0x08 +#define OV9640_COM6_FMT_RST 0x82 +#define OV9640_COM6_ADBLC_OPTEN 0x01 + +#define OV9640_COM7_RAW_RGB 0x01 +#define OV9640_COM7_RGB 0x04 +#define OV9640_COM7_QCIF 0x08 +#define OV9640_COM7_QVGA 0x10 +#define OV9640_COM7_CIF 0x20 +#define OV9640_COM7_VGA 0x40 +#define OV9640_COM7_SCCB_RESET 0x80 + +#define OV9640_TSLB_YVYU_YUYV 0x04 +#define OV9640_TSLB_YUYV_UYVY 0x08 + +#define OV9640_COM12_YUV_AVG 0x04 +#define OV9640_COM12_RSVD 0x40 + +#define OV9640_COM13_GAMMA_NONE 0x00 +#define OV9640_COM13_GAMMA_Y 0x40 +#define OV9640_COM13_GAMMA_RAW 0x80 +#define OV9640_COM13_RGB_AVG 0x20 +#define OV9640_COM13_MATRIX_EN 0x10 +#define OV9640_COM13_Y_DELAY_EN 0x08 +#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) + +#define OV9640_COM15_OR_00FF 0x00 +#define OV9640_COM15_OR_01FE 0x40 +#define OV9640_COM15_OR_10F0 0xc0 +#define OV9640_COM15_RGB_NORM 0x00 +#define OV9640_COM15_RGB_565 0x10 +#define OV9640_COM15_RGB_555 0x30 + +#define OV9640_COM16_RB_AVG 0x01 + +/* IDs */ +#define OV9640_V2 0x9648 +#define OV9640_V3 0x9649 +#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) + +/* supported resolutions */ +enum { + W_QQCIF = 88, + W_QQVGA = 160, + W_QCIF = 176, + W_QVGA = 320, + W_CIF = 352, + W_VGA = 640, + W_SXGA = 1280 +}; +#define H_SXGA 960 + +/* Misc. structures */ +struct ov9640_reg_alt { + u8 com7; + u8 com12; + u8 com13; + u8 com15; +}; + +struct ov9640_reg { + u8 reg; + u8 val; +}; + +struct ov9640_priv { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_clk *clk; + + int model; + int revision; +}; + +#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 7c2aabc8a3f6..7d7b801ab2ce 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -42,10 +42,12 @@ config SOC_CAMERA_OV772X This is a ov772x camera driver config SOC_CAMERA_OV9640 - tristate "ov9640 camera support" + tristate "ov9640 camera support (OBSOLETE)" + default n depends on SOC_CAMERA && I2C help - This is a ov9640 camera driver + This is an obsoleted version of ov9640 camera driver. Please use + the v4l2 standalone one (VIDEO_OV9640). config SOC_CAMERA_OV9740 tristate "ov9740 camera support" From 9f7e55d235b62e95043917fcb86a87064678074c Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:13 -0500 Subject: [PATCH 0367/4277] media: i2c: ov9640: drop soc_camera code and switch to v4l2_async This patch removes the dependency on an obsoleted soc_camera from ov9640 driver and changes the code to be a standalone v4l2 async subdevice. It also adds GPIO allocations for power and reset signals (as they are not handled by soc_camera now). The values for waiting on GPIOs (reset and power) settling down were taken from the datasheet (> 1 ms after HW/SW reset). The upper limit was chosen as an arbitrary value. Also one occurrence of mdelay() was changed to msleep(). The delays were successfully tested on a real hardware. The patch makes ov9640 sensor again compatible with the pxa_camera driver. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 74 ++++++++++++++++++++++++++------------ drivers/media/i2c/ov9640.h | 2 ++ 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index eb91b8240083..1f7cf01d037f 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -9,6 +9,7 @@ * Kuninori Morimoto * * Based on ov7670 and soc_camera_platform driver, + * transition from soc_camera to pxa_camera based on mt9m111 * * Copyright 2006-7 Jonathan Corbet * Copyright (C) 2008 Magnus Damm @@ -27,10 +28,14 @@ #include #include -#include +#include #include #include #include +#include +#include + +#include #include "ov9640.h" @@ -323,11 +328,23 @@ static int ov9640_set_register(struct v4l2_subdev *sd, static int ov9640_s_power(struct v4l2_subdev *sd, int on) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct ov9640_priv *priv = to_ov9640_sensor(sd); + int ret = 0; - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); + if (on) { + gpiod_set_value(priv->gpio_power, 1); + usleep_range(1000, 2000); + ret = v4l2_clk_enable(priv->clk); + usleep_range(1000, 2000); + gpiod_set_value(priv->gpio_reset, 0); + } else { + gpiod_set_value(priv->gpio_reset, 1); + usleep_range(1000, 2000); + v4l2_clk_disable(priv->clk); + usleep_range(1000, 2000); + gpiod_set_value(priv->gpio_power, 0); + } + return ret; } /* select nearest higher resolution for capture */ @@ -475,7 +492,7 @@ static int ov9640_prog_dflt(struct i2c_client *client) } /* wait for the changes to actually happen, 140ms are not enough yet */ - mdelay(150); + msleep(150); return 0; } @@ -630,14 +647,10 @@ static const struct v4l2_subdev_core_ops ov9640_core_ops = { static int ov9640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -666,18 +679,27 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), + GFP_KERNEL); if (!priv) return -ENOMEM; + priv->gpio_power = devm_gpiod_get(&client->dev, "Camera power", + GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(priv->gpio_power)) { + ret = PTR_ERR(priv->gpio_power); + return ret; + } + + priv->gpio_reset = devm_gpiod_get(&client->dev, "Camera reset", + GPIOD_OUT_HIGH); + if (IS_ERR_OR_NULL(priv->gpio_reset)) { + ret = PTR_ERR(priv->gpio_reset); + return ret; + } + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 2); @@ -696,12 +718,20 @@ static int ov9640_probe(struct i2c_client *client, } ret = ov9640_video_probe(client); - if (ret) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } + if (ret) + goto eprobe; + priv->subdev.dev = &client->dev; + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret) + goto eprobe; + + return 0; + +eprobe: + v4l2_clk_put(priv->clk); +eclkget: + v4l2_ctrl_handler_free(&priv->hdl); return ret; } @@ -711,7 +741,7 @@ static int ov9640_remove(struct i2c_client *client) struct ov9640_priv *priv = to_ov9640_sensor(sd); v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); + v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); return 0; } diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h index 65d13ff17536..be5e4b29ac69 100644 --- a/drivers/media/i2c/ov9640.h +++ b/drivers/media/i2c/ov9640.h @@ -200,6 +200,8 @@ struct ov9640_priv { struct v4l2_subdev subdev; struct v4l2_ctrl_handler hdl; struct v4l2_clk *clk; + struct gpio_desc *gpio_power; + struct gpio_desc *gpio_reset; int model; int revision; From f2ed15c30ea21a921ab5d3f7f7791507a407f807 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:14 -0500 Subject: [PATCH 0368/4277] media: MAINTAINERS: add Petr Cvek as a maintainer for the ov9640 driver The soc_camera drivers are marked as orphaned. Add Petr Cvek as a new maintainer for ov9640 driver after its switch from the soc_camera. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 02d9ebcafb97..3a3a60615b88 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11229,6 +11229,12 @@ S: Maintained F: drivers/media/i2c/ov7740.c F: Documentation/devicetree/bindings/media/i2c/ov7740.txt +OMNIVISION OV9640 SENSOR DRIVER +M: Petr Cvek +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/ov9640.* + OMNIVISION OV9650 SENSOR DRIVER M: Sakari Ailus R: Akinobu Mita From f8de593d5348259c25bb383e80698213b9af4ff0 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:15 -0500 Subject: [PATCH 0369/4277] media: i2c: ov9640: add missing SPDX identifiers Add missing SPDX identifiers to .c and .h files of the sensor driver. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 5 +---- drivers/media/i2c/ov9640.h | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 1f7cf01d037f..9a6fa609e8c4 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OmniVision OV96xx Camera Driver * @@ -14,10 +15,6 @@ * Copyright 2006-7 Jonathan Corbet * Copyright (C) 2008 Magnus Damm * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h index be5e4b29ac69..a8ed6992c1a8 100644 --- a/drivers/media/i2c/ov9640.h +++ b/drivers/media/i2c/ov9640.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * OmniVision OV96xx Camera Header File * * Copyright (C) 2009 Marek Vasut - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ From 74d01576fc85a5a5e7ea8c4de276c7db0d6abc1a Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:16 -0500 Subject: [PATCH 0370/4277] media: i2c: ov9640: change array index or length variables to unsigned The driver uses variables to store frame resolutions and to indexing various arrays. These should be unsigned. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 9a6fa609e8c4..08f3f8247759 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -347,10 +347,10 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on) /* select nearest higher resolution for capture */ static void ov9640_res_roundup(u32 *width, u32 *height) { - int i; + unsigned int i; enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; - static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; - static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + static const u32 res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + static const u32 res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; for (i = 0; i < ARRAY_SIZE(res_x); i++) { if (res_x[i] >= *width && res_y[i] >= *height) { @@ -393,8 +393,9 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, u32 code, struct ov9640_reg_alt *alts) { const struct ov9640_reg *ov9640_regs, *matrix_regs; - int ov9640_regs_len, matrix_regs_len; - int i, ret; + unsigned int ov9640_regs_len, matrix_regs_len; + unsigned int i; + int ret; u8 val; /* select register configuration for given resolution */ @@ -479,7 +480,8 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, /* program default register values */ static int ov9640_prog_dflt(struct i2c_client *client) { - int i, ret; + unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, From 856b0b8ffbf69d7b146a62669904576538d74aac Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:17 -0500 Subject: [PATCH 0371/4277] media: i2c: ov9640: add space before return for better clarity Some returns were adjoined to unrelated code blocks. This patch adds a space inbetween. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 08f3f8247759..2839aa3b4fb4 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -286,6 +286,7 @@ static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) OV9640_MVFP_H, 0); return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); } + return -EINVAL; } @@ -341,6 +342,7 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on) usleep_range(1000, 2000); gpiod_set_value(priv->gpio_power, 0); } + return ret; } @@ -545,6 +547,7 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, return ov9640_s_fmt(sd, mf); cfg->try_fmt = *mf; + return 0; } @@ -556,6 +559,7 @@ static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; code->code = ov9640_codes[code->index]; + return 0; } @@ -731,6 +735,7 @@ eprobe: v4l2_clk_put(priv->clk); eclkget: v4l2_ctrl_handler_free(&priv->hdl); + return ret; } From 95531e462288a9afdcb494abf6d1d123f5a4ff1c Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:18 -0500 Subject: [PATCH 0372/4277] media: i2c: ov9640: make array of supported formats constant An array which defines sensor's supported formats is not written anywhere, so it can be constant. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 2839aa3b4fb4..9739fa8d433a 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -161,7 +161,7 @@ static const struct ov9640_reg ov9640_regs_rgb[] = { { OV9640_MTXS, 0x65 }, }; -static u32 ov9640_codes[] = { +static const u32 ov9640_codes[] = { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, MEDIA_BUS_FMT_RGB565_2X8_LE, From ffe305d261f4d96eb851f1cad860d845edfaef25 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:19 -0500 Subject: [PATCH 0373/4277] media: i2c: ov9640: fix missing error handling in probe Control handlers registration lacked error path with v4l2_ctrl_handler_free() call. Fix it by using goto to alread existing v4l2_ctrl_handler_free() call. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 9739fa8d433a..c183273fd332 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -710,14 +710,18 @@ static int ov9640_probe(struct i2c_client *client, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (priv->hdl.error) { + ret = priv->hdl.error; + goto ectrlinit; + } + priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; priv->clk = v4l2_clk_get(&client->dev, "mclk"); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); - goto eclkget; + goto ectrlinit; } ret = ov9640_video_probe(client); @@ -733,7 +737,7 @@ static int ov9640_probe(struct i2c_client *client, eprobe: v4l2_clk_put(priv->clk); -eclkget: +ectrlinit: v4l2_ctrl_handler_free(&priv->hdl); return ret; From 16aaf112d3e53c9e450cbfc6063b407d1686fe1a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 13 Dec 2018 15:29:34 -0500 Subject: [PATCH 0374/4277] media: ov9640: Wrap long and unwrap short lines, align wrapped lines correctly Some little style fixup work. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index c183273fd332..d6831f28378b 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -271,19 +271,20 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) /* Set status of additional camera capabilities */ static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) { - struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); + struct ov9640_priv *priv = container_of(ctrl->handler, + struct ov9640_priv, hdl); struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->val) return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_V, 0); + OV9640_MVFP_V, 0); return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); case V4L2_CID_HFLIP: if (ctrl->val) return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_H, 0); + OV9640_MVFP_H, 0); return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); } @@ -471,7 +472,7 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, /* write color matrix configuration into the module */ for (i = 0; i < matrix_regs_len; i++) { ret = ov9640_reg_write(client, matrix_regs[i].reg, - matrix_regs[i].val); + matrix_regs[i].val); if (ret) return ret; } @@ -487,7 +488,7 @@ static int ov9640_prog_dflt(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, - ov9640_regs_dflt[i].val); + ov9640_regs_dflt[i].val); if (ret) return ret; } @@ -684,8 +685,7 @@ static int ov9640_probe(struct i2c_client *client, struct ov9640_priv *priv; int ret; - priv = devm_kzalloc(&client->dev, sizeof(*priv), - GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -707,9 +707,9 @@ static int ov9640_probe(struct i2c_client *client, v4l2_ctrl_handler_init(&priv->hdl, 2); v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); + V4L2_CID_HFLIP, 0, 1, 1, 0); if (priv->hdl.error) { ret = priv->hdl.error; @@ -751,6 +751,7 @@ static int ov9640_remove(struct i2c_client *client) v4l2_clk_put(priv->clk); v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); + return 0; } From aace50f1fd8426c8dfffd9999a7c408db3c2f40e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 30 Nov 2018 02:58:44 -0500 Subject: [PATCH 0375/4277] media: dt-bindings: media: sun6i: Separate H3 compatible from A31 The CSI controller found on the H3 (and H5) is a reduced version of the one found on the A31. It only has 1 channel, instead of 4 channels for time-multiplexed BT.656. Since the H3 is a reduced version, it cannot "fallback" to a compatible that implements more features than it supports. Split out the H3 compatible as a separate entry, with no fallback. Fixes: b7eadaa3a02a ("media: dt-bindings: media: sun6i: Add A31 and H3 compatibles") Signed-off-by: Chen-Yu Tsai Reviewed-by: Jagan Teki Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/sun6i-csi.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt index d4ab34f2240c..cc37cf7fd051 100644 --- a/Documentation/devicetree/bindings/media/sun6i-csi.txt +++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt @@ -6,7 +6,7 @@ Allwinner V3s SoC features a CSI module(CSI1) with parallel interface. Required properties: - compatible: value must be one of: * "allwinner,sun6i-a31-csi" - * "allwinner,sun8i-h3-csi", "allwinner,sun6i-a31-csi" + * "allwinner,sun8i-h3-csi" * "allwinner,sun8i-v3s-csi" - reg: base address and size of the memory-mapped region. - interrupts: interrupt associated to this IP From 2d9a43be4a2e9e5ba0f9810de81ffe5436dc3eff Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 30 Nov 2018 02:58:45 -0500 Subject: [PATCH 0376/4277] media: sun6i: Add H3 compatible The CSI controller found on the H3 (and H5) is a reduced version of the one found on the A31. It only has 1 channel, instead of 4 channels for time-multiplexed BT.656. Since the H3 is a reduced version, it cannot "fallback" to a compatible that implements more features than it supports. Add a compatible string entry for the H3. Signed-off-by: Chen-Yu Tsai Reviewed-by: Jagan Teki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 6950585edb5a..ee882b66a5ea 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -893,6 +893,7 @@ static int sun6i_csi_remove(struct platform_device *pdev) static const struct of_device_id sun6i_csi_of_match[] = { { .compatible = "allwinner,sun6i-a31-csi", }, + { .compatible = "allwinner,sun8i-h3-csi", }, { .compatible = "allwinner,sun8i-v3s-csi", }, {}, }; From 63ca0d7d032153312d2a2acdd21e69f665ba8e3a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 7 Jan 2019 06:06:18 -0500 Subject: [PATCH 0377/4277] media: MAINTAINERS: Update reviewers for ipu3-cio2 Remove Jian Xu from the driver's reviewers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3a3a60615b88..17ad1d7b5510 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7696,7 +7696,6 @@ M: Yong Zhi M: Sakari Ailus M: Bingbu Cao R: Tian Shu Qiu -R: Jian Xu Zheng L: linux-media@vger.kernel.org S: Maintained F: drivers/media/pci/intel/ipu3/ From 8f57763e521c2798a8e1c91efb822fd6e0fb3b72 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 7 Jan 2019 06:07:05 -0500 Subject: [PATCH 0378/4277] media: ipu3-cio2, dw9714: Remove Jian Xu's e-mail Jian Xu has left the company. Remove his e-mail address that no longer works. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/dw9714.c | 2 +- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 26d83693a681..3f0b082f863f 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -267,7 +267,7 @@ static struct i2c_driver dw9714_i2c_driver = { module_i2c_driver(dw9714_i2c_driver); MODULE_AUTHOR("Tianshu Qiu "); -MODULE_AUTHOR("Jian Xu Zheng "); +MODULE_AUTHOR("Jian Xu Zheng"); MODULE_AUTHOR("Yuning Pu "); MODULE_AUTHOR("Jouni Ukkonen "); MODULE_AUTHOR("Tommi Franttila "); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index e281e55cdca4..01ebd3550d88 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -2048,7 +2048,7 @@ module_pci_driver(cio2_pci_driver); MODULE_AUTHOR("Tuukka Toivonen "); MODULE_AUTHOR("Tianshu Qiu "); -MODULE_AUTHOR("Jian Xu Zheng "); +MODULE_AUTHOR("Jian Xu Zheng"); MODULE_AUTHOR("Yuning Pu "); MODULE_AUTHOR("Yong Zhi "); MODULE_LICENSE("GPL v2"); From 6f7f1bff2d9896fc743e7e110e9ab288f5ad7cbf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 08:50:54 -0500 Subject: [PATCH 0379/4277] media: vivid: disable VB2_USERPTR if dma_contig was configured It makes no sense to support the USERPTR memory model if the vivid instance was configured as dma_contig. Disable it if this is the case. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index c931f007e5b0..745ba6678b40 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -1094,7 +1094,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vid_cap_q; q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vid_cap_qops; @@ -1115,7 +1117,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vid_out_q; q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vid_out_qops; @@ -1136,7 +1140,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vbi_cap_q; q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE : V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vbi_cap_qops; @@ -1157,7 +1163,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vbi_out_q; q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT : V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vbi_out_qops; @@ -1177,7 +1185,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* initialize sdr_cap queue */ q = &dev->vb_sdr_cap_q; q->type = V4L2_BUF_TYPE_SDR_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_sdr_cap_qops; From 4b837c6d7ee771f68a30f362c9f68171a95be222 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 14 Jan 2019 09:01:54 -0500 Subject: [PATCH 0380/4277] media: v4l: uAPI: V4L2_BUF_TYPE_META_OUTPUT is an output buffer type V4L2_BUF_TYPE_META_OUTPUT was added by commit 72148d1a57e7 ("media: v4l: Add support for V4L2_BUF_TYPE_META_OUTPUT") but the patch missed adding the type to the macro telling whether a given type is an output type or not. Do that now. Getting this wrong leads to handling the buffer as a capture buffer in a lot of places. Fixes: 72148d1a57e7 ("media: v4l: Add support for V4L2_BUF_TYPE_META_OUTPUT") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index d6eed479c3a6..c0c36c165bf4 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -161,7 +161,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \ || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \ || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT \ - || (type) == V4L2_BUF_TYPE_SDR_OUTPUT) + || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ + || (type) == V4L2_BUF_TYPE_META_OUTPUT) enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, From 987303cc172a0899bc25b913d1315809a9fb8d17 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 11:23:52 -0500 Subject: [PATCH 0381/4277] media: vivid: take data_offset into account for video output The video output sizeimage calculation did not take data_offset into account. This can cause problems with video loopback or exporting output buffers for use as dmabuf import buffers since the output buffer size is now too small. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index ea250aee2b2e..55c0628ee9e1 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -28,11 +28,12 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const struct vivid_fmt *vfmt = dev->fmt_out; unsigned planes = vfmt->buffers; unsigned h = dev->fmt_out_rect.height; - unsigned size = dev->bytesperline_out[0] * h; + unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0]; unsigned p; for (p = vfmt->buffers; p < vfmt->planes; p++) - size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; + size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] + + vfmt->data_offset[p]; if (dev->field_out == V4L2_FIELD_ALTERNATE) { /* @@ -62,12 +63,14 @@ static int vid_out_queue_setup(struct vb2_queue *vq, if (sizes[0] < size) return -EINVAL; for (p = 1; p < planes; p++) { - if (sizes[p] < dev->bytesperline_out[p] * h) + if (sizes[p] < dev->bytesperline_out[p] * h + + vfmt->data_offset[p]) return -EINVAL; } } else { for (p = 0; p < planes; p++) - sizes[p] = p ? dev->bytesperline_out[p] * h : size; + sizes[p] = p ? dev->bytesperline_out[p] * h + + vfmt->data_offset[p] : size; } if (vq->num_buffers + *nbuffers < 2) @@ -321,7 +324,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; mp->plane_fmt[p].sizeimage = - mp->plane_fmt[p].bytesperline * mp->height; + mp->plane_fmt[p].bytesperline * mp->height + + fmt->data_offset[p]; } for (p = fmt->buffers; p < fmt->planes; p++) { unsigned stride = dev->bytesperline_out[p]; @@ -399,7 +403,7 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv, pfmt[p].bytesperline = bytesperline; pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) / - fmt->vdownsampling[p]; + fmt->vdownsampling[p] + fmt->data_offset[p]; memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); } From 30aa3d26edb0f3d7992757287eec0ca588a5c259 Mon Sep 17 00:00:00 2001 From: Shirish S Date: Wed, 16 Jan 2019 15:10:40 +0000 Subject: [PATCH 0382/4277] x86/MCE/AMD: Carve out the MC4_MISC thresholding quirk The MC4_MISC thresholding quirk needs to be applied during S5 -> S0 and S3 -> S0 state transitions, which follow different code paths. Carve it out into a separate function and call it mce_amd_feature_init() where the two code paths of the state transitions converge. [ bp: massage commit message and the carved out function. ] Signed-off-by: Shirish S Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Kees Cook Cc: Thomas Gleixner Cc: Tony Luck Cc: Vishal Verma Cc: Yazen Ghannam Cc: x86-ml Link: https://lkml.kernel.org/r/1547651417-23583-3-git-send-email-shirish.s@amd.com --- arch/x86/kernel/cpu/mce/amd.c | 36 ++++++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/mce/core.c | 29 --------------------------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index 89298c83de53..ed3327342b40 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -545,6 +545,40 @@ out: return offset; } +/* + * Turn off MC4_MISC thresholding banks on all family 0x15 models since + * they're not supported there. + */ +void disable_err_thresholding(struct cpuinfo_x86 *c) +{ + int i; + u64 hwcr; + bool need_toggle; + u32 msrs[] = { + 0x00000413, /* MC4_MISC0 */ + 0xc0000408, /* MC4_MISC1 */ + }; + + if (c->x86 != 0x15) + return; + + rdmsrl(MSR_K7_HWCR, hwcr); + + /* McStatusWrEn has to be set */ + need_toggle = !(hwcr & BIT(18)); + + if (need_toggle) + wrmsrl(MSR_K7_HWCR, hwcr | BIT(18)); + + /* Clear CntP bit safely */ + for (i = 0; i < ARRAY_SIZE(msrs); i++) + msr_clear_bit(msrs[i], 62); + + /* restore old settings */ + if (need_toggle) + wrmsrl(MSR_K7_HWCR, hwcr); +} + /* cpu init entry point, called from mce.c with preempt off */ void mce_amd_feature_init(struct cpuinfo_x86 *c) { @@ -552,6 +586,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c) unsigned int bank, block, cpu = smp_processor_id(); int offset = -1; + disable_err_thresholding(c); + for (bank = 0; bank < mca_cfg.banks; ++bank) { if (mce_flags.smca) smca_configure(bank, cpu); diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index d0c54160b439..6063ae2376b2 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1611,35 +1611,6 @@ static int __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c) if (c->x86 == 0x15 && c->x86_model <= 0xf) mce_flags.overflow_recov = 1; - /* - * Turn off MC4_MISC thresholding banks on all models since - * they're not supported there. - */ - if (c->x86 == 0x15) { - int i; - u64 hwcr; - bool need_toggle; - u32 msrs[] = { - 0x00000413, /* MC4_MISC0 */ - 0xc0000408, /* MC4_MISC1 */ - }; - - rdmsrl(MSR_K7_HWCR, hwcr); - - /* McStatusWrEn has to be set */ - need_toggle = !(hwcr & BIT(18)); - - if (need_toggle) - wrmsrl(MSR_K7_HWCR, hwcr | BIT(18)); - - /* Clear CntP bit safely */ - for (i = 0; i < ARRAY_SIZE(msrs); i++) - msr_clear_bit(msrs[i], 62); - - /* restore old settings */ - if (need_toggle) - wrmsrl(MSR_K7_HWCR, hwcr); - } } if (c->x86_vendor == X86_VENDOR_INTEL) { From 3f122df4a2ba6704ccf51f5caec583443514644f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2019 06:37:03 -0500 Subject: [PATCH 0383/4277] media: vivid: do not implement VIDIOC_S_PARM for output streams v4l2_compliance gave a warning for the S_PARM test for output streams: warn: v4l2-test-formats.cpp(1235): S_PARM is supported for buftype 2, but not for ENUM_FRAMEINTERVALS The reason is that vivid mapped s_parm for output streams to g_parm. But if S_PARM doesn't actually change anything, then it shouldn't be enabled at all. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 745ba6678b40..29e7b14fa704 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -371,7 +371,7 @@ static int vidioc_s_parm(struct file *file, void *fh, if (vdev->vfl_dir == VFL_DIR_RX) return vivid_vid_cap_s_parm(file, fh, parm); - return vivid_vid_out_g_parm(file, fh, parm); + return -ENOTTY; } static int vidioc_log_status(struct file *file, void *fh) From 86824694184642a4dc1be0c050df9854da5f51d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2019 06:43:12 -0500 Subject: [PATCH 0384/4277] media: vim2m: the v4l2_m2m_buf_copy_data args were swapped The buffer arguments to v4l2_m2m_buf_copy_data args were swapped. The reason is confusing naming conventions in vim2m. It certainly could be improved. Fixes: 7aca565ee3d0 ("media: vim2m: use v4l2_m2m_buf_copy_data") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 33397d4a1402..a7a152fb3075 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -241,7 +241,7 @@ static int device_process(struct vim2m_ctx *ctx, out_vb->sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; in_vb->sequence = q_data->sequence++; - v4l2_m2m_buf_copy_data(out_vb, in_vb, true); + v4l2_m2m_buf_copy_data(in_vb, out_vb, true); switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: From 4d4abf064d676dcc27952ed90f4f50d7087318c7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:24 -0500 Subject: [PATCH 0385/4277] media: gspca: ov534: replace msleep(10) with usleep_range For short waits, usleep_range should be used instead of msleep, see Documentation/timers/timers-howto.txt. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index d06dc0755b9a..44f06a58bb67 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -679,7 +679,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev) int i; for (i = 0; i < 5; i++) { - msleep(10); + usleep_range(10000, 20000); data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); switch (data) { @@ -1277,7 +1277,7 @@ static int sd_init(struct gspca_dev *gspca_dev) /* reset sensor */ sccb_reg_write(gspca_dev, 0x12, 0x80); - msleep(10); + usleep_range(10000, 20000); /* probe the sensor */ sccb_reg_read(gspca_dev, 0x0a); From fc78e0b2471d6efa19f896d7c93a8ecda9913ddd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:25 -0500 Subject: [PATCH 0386/4277] media: gspca: support multiple pixel formats in ENUM_FRAMEINTERVALS If a driver supports multiple pixel formats with the same frame size, ENUM_FRAMEINTERVALS will currently only work for the first pixel format. Fix this by adding pixelformat support to wxh_to_mode(). Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 3137f5d89d80..13361cfa6903 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -912,13 +912,14 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev) } static int wxh_to_mode(struct gspca_dev *gspca_dev, - int width, int height) + int width, int height, u32 pixelformat) { int i; for (i = 0; i < gspca_dev->cam.nmodes; i++) { if (width == gspca_dev->cam.cam_mode[i].width - && height == gspca_dev->cam.cam_mode[i].height) + && height == gspca_dev->cam.cam_mode[i].height + && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) return i; } return -EINVAL; @@ -1152,7 +1153,8 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv, int mode; __u32 i; - mode = wxh_to_mode(gspca_dev, fival->width, fival->height); + mode = wxh_to_mode(gspca_dev, fival->width, fival->height, + fival->pixel_format); if (mode < 0) return -EINVAL; From d4809b778a0899f732e808e60b3a42cfce0b5e40 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:26 -0500 Subject: [PATCH 0387/4277] media: gspca: support multiple pixel formats in TRY_FMT If a driver supports multiple pixel formats with the same frame size, TRY_FMT will currently always return the first pixel format. Fix this by adding pixelformat support to wxh_to_nearest_mode(). Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 13361cfa6903..ac70b36d67b7 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -926,10 +926,16 @@ static int wxh_to_mode(struct gspca_dev *gspca_dev, } static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, - int width, int height) + int width, int height, u32 pixelformat) { int i; + for (i = gspca_dev->cam.nmodes; --i > 0; ) { + if (width >= gspca_dev->cam.cam_mode[i].width + && height >= gspca_dev->cam.cam_mode[i].height + && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) + return i; + } for (i = gspca_dev->cam.nmodes; --i > 0; ) { if (width >= gspca_dev->cam.cam_mode[i].width && height >= gspca_dev->cam.cam_mode[i].height) @@ -1059,7 +1065,7 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, fmt->fmt.pix.pixelformat, w, h); /* search the nearest mode for width and height */ - mode = wxh_to_nearest_mode(gspca_dev, w, h); + mode = wxh_to_nearest_mode(gspca_dev, w, h, fmt->fmt.pix.pixelformat); /* OK if right palette */ if (gspca_dev->cam.cam_mode[mode].pixelformat From c53304f2ce702091d47fffbac52d351ff8094c68 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:27 -0500 Subject: [PATCH 0388/4277] media: gspca: ov543-ov772x: move video format specific registers into bridge_start In preparation for adding SGBRG8 as a second video format besides YUYV, move video format specific register settings from the bridge_init array into the bridge_start arrays. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 44f06a58bb67..077c49a74709 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -411,9 +411,7 @@ static const u8 sensor_start_qvga_767x[][2] = { }; static const u8 bridge_init_772x[][2] = { - { 0xc2, 0x0c }, { 0x88, 0xf8 }, - { 0xc3, 0x69 }, { 0x89, 0xff }, { 0x76, 0x03 }, { 0x92, 0x01 }, @@ -439,7 +437,6 @@ static const u8 bridge_init_772x[][2] = { { 0x1f, 0x81 }, { 0x34, 0x05 }, { 0xe3, 0x04 }, - { 0x88, 0x00 }, { 0x89, 0x00 }, { 0x76, 0x00 }, { 0xe7, 0x2e }, @@ -460,13 +457,7 @@ static const u8 bridge_init_772x[][2] = { { 0x1d, 0x08 }, /* turn on UVC header */ { 0x1d, 0x0e }, /* .. */ - { 0x8d, 0x1c }, - { 0x8e, 0x80 }, { 0xe5, 0x04 }, - - { 0xc0, 0x50 }, - { 0xc1, 0x3c }, - { 0xc2, 0x0c }, }; static const u8 sensor_init_772x[][2] = { { 0x12, 0x80 }, @@ -562,6 +553,7 @@ static const u8 sensor_init_772x[][2] = { { 0x0c, 0xd0 } }; static const u8 bridge_start_vga_772x[][2] = { + {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -569,8 +561,12 @@ static const u8 bridge_start_vga_772x[][2] = { {0x1d, 0x02}, {0x1d, 0x58}, {0x1d, 0x00}, + {0x8d, 0x1c}, + {0x8e, 0x80}, {0xc0, 0x50}, {0xc1, 0x3c}, + {0xc2, 0x0c}, + {0xc3, 0x69}, }; static const u8 sensor_start_vga_772x[][2] = { {0x12, 0x00}, @@ -583,6 +579,7 @@ static const u8 sensor_start_vga_772x[][2] = { {0x65, 0x20}, }; static const u8 bridge_start_qvga_772x[][2] = { + {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -590,8 +587,12 @@ static const u8 bridge_start_qvga_772x[][2] = { {0x1d, 0x01}, {0x1d, 0x4b}, {0x1d, 0x00}, + {0x8d, 0x1c}, + {0x8e, 0x80}, {0xc0, 0x28}, {0xc1, 0x1e}, + {0xc2, 0x0c}, + {0xc3, 0x69}, }; static const u8 sensor_start_qvga_772x[][2] = { {0x12, 0x40}, From 52b756aa90713e4d403ef841a9403610de3fd5b5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:28 -0500 Subject: [PATCH 0389/4277] media: gspca: ov534-ov772x: add SGBRG8 bayer mode support Add support to pass through the sensor's native SGBRG8 bayer pattern, allowing to cut the required USB bandwidth in half. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 115 +++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 077c49a74709..5b73f7f58ae6 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -103,6 +103,16 @@ static const struct v4l2_pix_format ov772x_mode[] = { .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, + {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, }; static const struct v4l2_pix_format ov767x_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -127,6 +137,14 @@ static const struct framerates ov772x_framerates[] = { .rates = vga_rates, .nrates = ARRAY_SIZE(vga_rates), }, + { /* 320x240 SGBRG8 */ + .rates = qvga_rates, + .nrates = ARRAY_SIZE(qvga_rates), + }, + { /* 640x480 SGBRG8 */ + .rates = vga_rates, + .nrates = ARRAY_SIZE(vga_rates), + }, }; struct reg_array { @@ -552,7 +570,7 @@ static const u8 sensor_init_772x[][2] = { { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; -static const u8 bridge_start_vga_772x[][2] = { +static const u8 bridge_start_vga_yuyv_772x[][2] = { {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, @@ -568,7 +586,7 @@ static const u8 bridge_start_vga_772x[][2] = { {0xc2, 0x0c}, {0xc3, 0x69}, }; -static const u8 sensor_start_vga_772x[][2] = { +static const u8 sensor_start_vga_yuyv_772x[][2] = { {0x12, 0x00}, {0x17, 0x26}, {0x18, 0xa0}, @@ -577,8 +595,9 @@ static const u8 sensor_start_vga_772x[][2] = { {0x29, 0xa0}, {0x2c, 0xf0}, {0x65, 0x20}, + {0x67, 0x00}, }; -static const u8 bridge_start_qvga_772x[][2] = { +static const u8 bridge_start_qvga_yuyv_772x[][2] = { {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, @@ -594,7 +613,7 @@ static const u8 bridge_start_qvga_772x[][2] = { {0xc2, 0x0c}, {0xc3, 0x69}, }; -static const u8 sensor_start_qvga_772x[][2] = { +static const u8 sensor_start_qvga_yuyv_772x[][2] = { {0x12, 0x40}, {0x17, 0x3f}, {0x18, 0x50}, @@ -603,6 +622,61 @@ static const u8 sensor_start_qvga_772x[][2] = { {0x29, 0x50}, {0x2c, 0x78}, {0x65, 0x2f}, + {0x67, 0x00}, +}; +static const u8 bridge_start_vga_gbrg_772x[][2] = { + {0x88, 0x08}, + {0x1c, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x2c}, + {0x1d, 0x00}, + {0x8d, 0x00}, + {0x8e, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0xc2, 0x01}, + {0xc3, 0x01}, +}; +static const u8 sensor_start_vga_gbrg_772x[][2] = { + {0x12, 0x01}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, + {0x67, 0x02}, +}; +static const u8 bridge_start_qvga_gbrg_772x[][2] = { + {0x88, 0x08}, + {0x1c, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0x8d, 0x00}, + {0x8e, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, + {0xc2, 0x01}, + {0xc3, 0x01}, +}; +static const u8 sensor_start_qvga_gbrg_772x[][2] = { + {0x12, 0x41}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, + {0x67, 0x02}, }; static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) @@ -1316,25 +1390,33 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int mode; - static const struct reg_array bridge_start[NSENSORS][2] = { + static const struct reg_array bridge_start[NSENSORS][4] = { [SENSOR_OV767x] = {{bridge_start_qvga_767x, ARRAY_SIZE(bridge_start_qvga_767x)}, {bridge_start_vga_767x, ARRAY_SIZE(bridge_start_vga_767x)}}, - [SENSOR_OV772x] = {{bridge_start_qvga_772x, - ARRAY_SIZE(bridge_start_qvga_772x)}, - {bridge_start_vga_772x, - ARRAY_SIZE(bridge_start_vga_772x)}}, + [SENSOR_OV772x] = {{bridge_start_qvga_yuyv_772x, + ARRAY_SIZE(bridge_start_qvga_yuyv_772x)}, + {bridge_start_vga_yuyv_772x, + ARRAY_SIZE(bridge_start_vga_yuyv_772x)}, + {bridge_start_qvga_gbrg_772x, + ARRAY_SIZE(bridge_start_qvga_gbrg_772x)}, + {bridge_start_vga_gbrg_772x, + ARRAY_SIZE(bridge_start_vga_gbrg_772x)} }, }; - static const struct reg_array sensor_start[NSENSORS][2] = { + static const struct reg_array sensor_start[NSENSORS][4] = { [SENSOR_OV767x] = {{sensor_start_qvga_767x, ARRAY_SIZE(sensor_start_qvga_767x)}, {sensor_start_vga_767x, ARRAY_SIZE(sensor_start_vga_767x)}}, - [SENSOR_OV772x] = {{sensor_start_qvga_772x, - ARRAY_SIZE(sensor_start_qvga_772x)}, - {sensor_start_vga_772x, - ARRAY_SIZE(sensor_start_vga_772x)}}, + [SENSOR_OV772x] = {{sensor_start_qvga_yuyv_772x, + ARRAY_SIZE(sensor_start_qvga_yuyv_772x)}, + {sensor_start_vga_yuyv_772x, + ARRAY_SIZE(sensor_start_vga_yuyv_772x)}, + {sensor_start_qvga_gbrg_772x, + ARRAY_SIZE(sensor_start_qvga_gbrg_772x)}, + {sensor_start_vga_gbrg_772x, + ARRAY_SIZE(sensor_start_vga_gbrg_772x)} }, }; /* (from ms-win trace) */ @@ -1440,10 +1522,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - if (gspca_dev->pixfmt.pixelformat == V4L2_PIX_FMT_YUYV + if (gspca_dev->pixfmt.pixelformat != V4L2_PIX_FMT_JPEG && gspca_dev->image_len + len - 12 != - gspca_dev->pixfmt.width * - gspca_dev->pixfmt.height * 2) { + gspca_dev->pixfmt.sizeimage) { gspca_dbg(gspca_dev, D_PACK, "wrong sized frame\n"); goto discard; } From ee1845d1cb9f6e6c74cfce958ce51985bf2a631e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:29 -0500 Subject: [PATCH 0390/4277] media: gspca: ov534-ov722x: remove mode specific video data registers from bridge_init The video format, payload size, and frame size setup is video format and frame size specific. Those registers are overwritten during bridge_start anyway. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 5b73f7f58ae6..bc9d2eb5db30 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -462,15 +462,6 @@ static const u8 bridge_init_772x[][2] = { { 0x25, 0x42 }, { 0x21, 0xf0 }, - { 0x1c, 0x00 }, - { 0x1d, 0x40 }, - { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ - { 0x1d, 0x00 }, /* payload size */ - - { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ - { 0x1d, 0x58 }, /* frame size */ - { 0x1d, 0x00 }, /* frame size */ - { 0x1c, 0x0a }, { 0x1d, 0x08 }, /* turn on UVC header */ { 0x1d, 0x0e }, /* .. */ From c5550bb9502bf6ef2eb514eee5eec486fe1cfb5c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:30 -0500 Subject: [PATCH 0391/4277] media: gspca: ov534-ov722x: remove camera clock setup from bridge_init This register is later overwritten by set_frame_rate anyway. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index bc9d2eb5db30..23deeedd3279 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -465,8 +465,6 @@ static const u8 bridge_init_772x[][2] = { { 0x1c, 0x0a }, { 0x1d, 0x08 }, /* turn on UVC header */ { 0x1d, 0x0e }, /* .. */ - - { 0xe5, 0x04 }, }; static const u8 sensor_init_772x[][2] = { { 0x12, 0x80 }, From 530993347bae43648f4aca2966c37cd269af66b7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:31 -0500 Subject: [PATCH 0392/4277] media: gspca: ov534-ov772x: remove unnecessary COM3 initialization The COM3 register at address 0x0c already defaults to 0x10, the two bits COM3[7:6] are set according to V4L2 controls by sethvflip later. There is no need to set it multiple times during bridge initialization. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 23deeedd3279..02c90ad96b76 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -543,13 +543,10 @@ static const u8 sensor_init_772x[][2] = { { 0x8c, 0xe8 }, { 0x8d, 0x20 }, - { 0x0c, 0x90 }, - { 0x2b, 0x00 }, { 0x22, 0x7f }, { 0x23, 0x03 }, { 0x11, 0x01 }, - { 0x0c, 0xd0 }, { 0x64, 0xff }, { 0x0d, 0x41 }, @@ -557,7 +554,6 @@ static const u8 sensor_init_772x[][2] = { { 0x0e, 0xcd }, { 0xac, 0xbf }, { 0x8e, 0x00 }, /* De-noise threshold */ - { 0x0c, 0xd0 } }; static const u8 bridge_start_vga_yuyv_772x[][2] = { {0x88, 0x00}, From 50656bad786d001b294764e9f047c5d5b3e4db75 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:09 -0500 Subject: [PATCH 0393/4277] media: v4l2-ctrl: Add control to enable h.264 constrained intra prediction Allow to enable h.264 constrained intra prediction (macroblocks using intra prediction modes are not allowed to use residual data and decoded samples of neighboring macroblocks coded using inter prediction modes). This control directly corresponds to the constrained_intra_pred_flag field in the h.264 picture parameter set. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 4 ++++ drivers/media/v4l2-core/v4l2-ctrls.c | 2 ++ include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 7 insertions(+) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index af4273aa5e85..235d0c293983 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1154,6 +1154,10 @@ enum v4l2_mpeg_video_h264_entropy_mode - ``V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (boolean)`` Enable 8X8 transform for H264. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (boolean)`` + Enable constrained intra prediction for H264. Applicable to the H264 + encoder. + ``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` Cyclic intra macroblock refresh. This is the number of continuous macroblocks refreshed every frame. Each frame a successive set of diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 8c47d8f00429..50b56185c02e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -825,6 +825,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER:return "H264 Number of HC Layers"; case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP: return "H264 Set QP Value for HC Layers"; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + return "H264 Constrained Intra Pred"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 3dcfc6148f99..fd65c710b144 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -533,6 +533,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type { }; #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (V4L2_CID_MPEG_BASE+381) #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (V4L2_CID_MPEG_BASE+382) +#define V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (V4L2_CID_MPEG_BASE+383) #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400) #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401) #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402) From d034696cbe5a6e00f76ca4b7869c6cdef66aebd5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:10 -0500 Subject: [PATCH 0394/4277] media: v4l2-ctrl: Add control for h.264 chroma qp offset Allow to add fixed quantization parameter offset between luma and chroma quantization parameters. This control directly corresponds to the chroma_qp_index_offset field of the h.264 picture parameter set. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 5 +++++ drivers/media/v4l2-core/v4l2-ctrls.c | 1 + include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 7 insertions(+) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 235d0c293983..00934efdc9e4 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1158,6 +1158,11 @@ enum v4l2_mpeg_video_h264_entropy_mode - Enable constrained intra prediction for H264. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (integer)`` + Specify the offset that should be added to the luma quantization + parameter to determine the chroma quantization parameter. Applicable + to the H264 encoder. + ``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` Cyclic intra macroblock refresh. This is the number of continuous macroblocks refreshed every frame. Each frame a successive set of diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 50b56185c02e..99308dac2daa 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -827,6 +827,7 @@ const char *v4l2_ctrl_get_name(u32 id) return "H264 Set QP Value for HC Layers"; case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: return "H264 Constrained Intra Pred"; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: return "H264 Chroma QP Index Offset"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index fd65c710b144..06479f2fb3ae 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -534,6 +534,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type { #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (V4L2_CID_MPEG_BASE+381) #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (V4L2_CID_MPEG_BASE+382) #define V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (V4L2_CID_MPEG_BASE+383) +#define V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (V4L2_CID_MPEG_BASE+384) #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400) #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401) #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402) From 31489f6bdabf4fa96f354fe3520ea779bebc6ee7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:11 -0500 Subject: [PATCH 0395/4277] media: coda: Add control for h.264 constrained intra prediction Allow to enable constrained intra prediction in the h.264 encoder. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 4 +++- drivers/media/platform/coda/coda-common.c | 6 ++++++ drivers/media/platform/coda/coda.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 8e0194993a52..2998c869f79f 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1010,7 +1010,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | ((ctx->params.h264_slice_beta_offset_div2 & CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET); + CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | + (ctx->params.h264_constrained_intra_pred_flag << + CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET); coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); break; case V4L2_PIX_FMT_JPEG: diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 7518f01c48f7..f6c9273805bb 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1839,6 +1839,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + ctx->params.h264_constrained_intra_pred_flag = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: /* TODO: switch between baseline and constrained baseline */ if (ctx->inst_type == CODA_INST_ENCODER) @@ -1925,6 +1928,9 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, + 0); v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 31cea72f5b2a..f3d0cff4ef3a 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -118,6 +118,7 @@ struct coda_params { u8 h264_disable_deblocking_filter_idc; s8 h264_slice_alpha_c0_offset_div2; s8 h264_slice_beta_offset_div2; + bool h264_constrained_intra_pred_flag; u8 h264_profile_idc; u8 h264_level_idc; u8 mpeg4_intra_qp; From 3943f059823b6e15884387f31618b84826e924b3 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:12 -0500 Subject: [PATCH 0396/4277] media: coda: Add control for h.264 chroma qp index offset Allow to set a fixed quantization parameter offset between luma and chroma in the h.264 encoder. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 4 +++- drivers/media/platform/coda/coda-common.c | 5 +++++ drivers/media/platform/coda/coda.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 2998c869f79f..88065b07149c 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1012,7 +1012,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | (ctx->params.h264_constrained_intra_pred_flag << - CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET); + CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) | + (ctx->params.h264_chroma_qp_index_offset & + CODA_264PARAM_CHROMAQPOFFSET_MASK); coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); break; case V4L2_PIX_FMT_JPEG: diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index f6c9273805bb..390d1ce6ab32 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1842,6 +1842,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: ctx->params.h264_constrained_intra_pred_flag = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + ctx->params.h264_chroma_qp_index_offset = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: /* TODO: switch between baseline and constrained baseline */ if (ctx->inst_type == CODA_INST_ENCODER) @@ -1931,6 +1934,8 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index f3d0cff4ef3a..31c80bda2c0b 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -119,6 +119,7 @@ struct coda_params { s8 h264_slice_alpha_c0_offset_div2; s8 h264_slice_beta_offset_div2; bool h264_constrained_intra_pred_flag; + s8 h264_chroma_qp_index_offset; u8 h264_profile_idc; u8 h264_level_idc; u8 mpeg4_intra_qp; From c5ff0edb8e2270a75935c73217fb0de1abd2d910 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 14 Jan 2019 20:10:19 -0500 Subject: [PATCH 0397/4277] media: rcar-vin: Allow independent VIN link enablement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a block of code in rvin_group_link_notify() that prevents enabling a link to a VIN node if any entity in the media graph is in use. This prevents enabling a VIN link even if there is an in-use entity somewhere in the graph that is independent of the link's pipeline. For example, the code block will prevent enabling a link from the first rcar-csi2 receiver to a VIN node even if there is an enabled link somewhere far upstream on the second independent rcar-csi2 receiver pipeline. If this code block is meant to prevent modifying a link if any entity in the graph is actively involved in streaming (because modifying the CHSEL register fields can disrupt any/all running streams), then the entities stream counts should be checked rather than the use counts. (There is already such a check in __media_entity_setup_link() that verifies the stream_count of the link's source and sink entities are both zero, but that is insufficient, since there should be no running streams in the entire graph). Modify the code block to check the entity stream_count instead of the use_count (and elaborate on the comment). VIN node links can now be enabled even if there are other independent in-use entities that are not streaming. Fixes: c0cc5aef31 ("media: rcar-vin: add link notify for Gen3") Signed-off-by: Steve Longerbeam Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index b28e3ff191c2..594d80434004 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -131,9 +131,13 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, !is_media_entity_v4l2_video_device(link->sink->entity)) return 0; - /* If any entity is in use don't allow link changes. */ + /* + * Don't allow link changes if any entity in the graph is + * streaming, modifying the CHSEL register fields can disrupt + * running streams. + */ media_device_for_each_entity(entity, &group->mdev) - if (entity->use_count) + if (entity->stream_count) return -EBUSY; mutex_lock(&group->lock); From 1c3721b1f22286033abeda30b7e12439b083ed0f Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:04 -0500 Subject: [PATCH 0398/4277] media: videodev2.h: Add more field helper macros Adds two helper macros: V4L2_FIELD_IS_SEQUENTIAL: returns true if the given field type is 'sequential', that is a full frame is transmitted, or exists in memory, as all top field lines followed by all bottom field lines, or vice-versa. V4L2_FIELD_IS_INTERLACED: returns true if the given field type is 'interlaced', that is a full frame is transmitted, or exists in memory, as top field lines interlaced with bottom field lines. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c0c36c165bf4..9a920f071ff9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -130,6 +130,13 @@ enum v4l2_field { ((field) == V4L2_FIELD_BOTTOM ||\ (field) == V4L2_FIELD_TOP ||\ (field) == V4L2_FIELD_ALTERNATE) +#define V4L2_FIELD_IS_INTERLACED(field) \ + ((field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT) +#define V4L2_FIELD_IS_SEQUENTIAL(field) \ + ((field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_SEQ_BT) enum v4l2_buf_type { V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, From fc8c723852380b0f4349bb06cf553a6bba47462e Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:05 -0500 Subject: [PATCH 0399/4277] media: gpu: ipu-csi: Swap fields according to input/output field types The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++++++++++++++------- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h | 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, - struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* + * Write the H-V-F codes the CSI will match against the + * incoming data for start/end of active and blanking + * field intervals. If input and output field types are + * sequential but not the same (one is SEQ_BT and the other + * is SEQ_TB), swap the F-bit so that the CSI will capture + * field 1 lines before field 0 lines. + */ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* + * Field0BlankEnd = 110, Field0BlankStart = 010 + * Field0ActiveEnd = 100, Field0ActiveStart = 000 + * Field1BlankEnd = 111, Field1BlankStart = 011 + * Field1ActiveEnd = 101, Field1ActiveStart = 001 + */ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); if (ret < 0) return ret; /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; + width = infmt->width; + height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + height *= 2; /* Set the CSI_SENS_CONF register remaining fields */ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | @@ -416,42 +479,22 @@ int ipu_csi_init_interface(struct ipu_csi *csi, ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); break; case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: - if (mbus_fmt->width == 720 && mbus_fmt->height == 576) { - /* - * PAL case - * - * Field0BlankEnd = 0x6, Field0BlankStart = 0x2, - * Field0ActiveEnd = 0x4, Field0ActiveStart = 0 - * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, - * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 - */ - height = 625; /* framelines for PAL */ - - ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, - CSI_CCIR_CODE_1); - ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); - ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); - } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) { - /* - * NTSC case - * - * Field0BlankEnd = 0x7, Field0BlankStart = 0x3, - * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1 - * Field1BlankEnd = 0x6, Field1BlankStart = 0x2, - * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 - */ - height = 525; /* framelines for NTSC */ - - ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, - CSI_CCIR_CODE_1); - ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); - ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); + if (width == 720 && height == 480) { + std = V4L2_STD_NTSC; + height = 525; + } else if (width == 720 && height == 576) { + std = V4L2_STD_PAL; + height = 625; } else { dev_err(csi->ipu->dev, - "Unsupported CCIR656 interlaced video mode\n"); - spin_unlock_irqrestore(&csi->lock, flags); - return -EINVAL; + "Unsupported interlaced video mode\n"); + ret = -EINVAL; + goto out_unlock; } + + ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); + if (ret) + goto out_unlock; break; case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: @@ -476,9 +519,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi, dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); +out_unlock: spin_unlock_irqrestore(&csi->lock, flags); - return 0; + return ret; } EXPORT_SYMBOL_GPL(ipu_csi_init_interface); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4223f8d418ae..c2a8d9cd31b7 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -679,12 +679,7 @@ static int csi_setup(struct csi_priv *priv) priv->upstream_ep.bus.parallel.flags : priv->upstream_ep.bus.mipi_csi2.flags; - /* - * we need to pass input frame to CSI interface, but - * with translated field type from output format - */ if_fmt = *infmt; - if_fmt.field = outfmt->field; crop = priv->crop; /* @@ -702,7 +697,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt); + ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt); ipu_csi_set_dest(priv->csi, priv->dest); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index e582e8e7527a..bbc8481f567d 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -354,8 +354,9 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, */ struct ipu_csi; int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt); + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt); bool ipu_csi_is_interlaced(struct ipu_csi *csi); void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w); void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w); From 9b5c8d5ffb20cc4f4b1476cb1ad57e0f9b934788 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:06 -0500 Subject: [PATCH 0400/4277] media: gpu: ipu-v3: Add planar support to interlaced scan To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++++++++++++++++++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); From d969291d8479dcbb7f093738279527d14cdb756c Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:07 -0500 Subject: [PATCH 0401/4277] media: imx: Fix field negotiation IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 ++++-- drivers/staging/media/imx/imx-media-capture.c | 14 ++++ drivers/staging/media/imx/imx-media-csi.c | 64 ++++++++++++++----- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -239,6 +239,20 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, cc = cc_src; } + /* allow IDMAC interweave but enforce field order from source */ + if (V4L2_FIELD_IS_INTERLACED(f->fmt.pix.field)) { + switch (fmt_src.format.field) { + case V4L2_FIELD_SEQ_TB: + fmt_src.format.field = V4L2_FIELD_INTERLACED_TB; + break; + case V4L2_FIELD_SEQ_BT: + fmt_src.format.field = V4L2_FIELD_INTERLACED_BT; + break; + default: + break; + } + } + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc); return 0; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index da4808348845..e3a4f39dbf73 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -398,16 +398,18 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; + bool passthrough, interweave; struct ipu_image image; u32 passthrough_bits; u32 passthrough_cycles; dma_addr_t phys[2]; - bool passthrough; u32 burst_size; int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; incc = priv->cc[CSI_SINK_PAD]; + outfmt = &priv->format_mbus[CSI_SRC_PAD_IDMAC]; ipu_cpmem_zero(priv->idmac_ch); @@ -424,6 +426,14 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc); passthrough_cycles = 1; + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: @@ -509,8 +519,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ipu_smfc_set_burstsize(priv->smfc, burst_size); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field)) + if (interweave) ipu_cpmem_interlaced_scan(priv->idmac_ch, image.pix.bytesperline, image.pix.pixelformat); @@ -1304,6 +1313,38 @@ out: return ret; } +static void csi_try_field(struct csi_priv *priv, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct v4l2_mbus_framefmt *infmt = + __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which); + + /* no restrictions on sink pad field type */ + if (sdformat->pad == CSI_SINK_PAD) + return; + + switch (infmt->field) { + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_ALTERNATE: + /* + * If the sink is sequential or alternating fields, + * allow only SEQ_TB at the source. + * + * This driver does not support alternate field mode, and + * the CSI captures a whole frame, so the CSI never presents + * alternate mode at its source pads. + */ + sdformat->format.field = V4L2_FIELD_SEQ_TB; + break; + default: + /* Passthrough for all other input field types */ + sdformat->format.field = infmt->field; + break; + } +} + static void csi_try_fmt(struct csi_priv *priv, struct v4l2_fwnode_endpoint *upstream_ep, struct v4l2_subdev_pad_config *cfg, @@ -1343,25 +1384,14 @@ static void csi_try_fmt(struct csi_priv *priv, } } - if (sdformat->pad == CSI_SRC_PAD_DIRECT || - sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; - - /* - * translate V4L2_FIELD_ALTERNATE to SEQ_TB or SEQ_BT - * depending on input height (assume NTSC top-bottom - * order if 480 lines, otherwise PAL bottom-top order). - */ - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) { - sdformat->format.field = (infmt->height == 480) ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; - } + csi_try_field(priv, cfg, sdformat); /* propagate colorimetry from sink */ sdformat->format.colorspace = infmt->colorspace; sdformat->format.xfer_func = infmt->xfer_func; sdformat->format.quantization = infmt->quantization; sdformat->format.ycbcr_enc = infmt->ycbcr_enc; + break; case CSI_SINK_PAD: v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W, @@ -1389,6 +1419,8 @@ static void csi_try_fmt(struct csi_priv *priv, sdformat->format.code = (*cc)->codes[0]; } + csi_try_field(priv, cfg, sdformat); + imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); From 2a87c0c9344489afe6c7a613d883a041a5441bcd Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:08 -0500 Subject: [PATCH 0402/4277] media: imx-csi: Double crop height for alternate fields at sink If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e3a4f39dbf73..10945cbdbd71 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1142,6 +1142,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1149,6 +1151,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1158,12 +1164,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1403,6 +1409,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1530,6 +1538,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; From bc11dd6699960c241dc067d55aa4be37b722afd5 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:09 -0500 Subject: [PATCH 0403/4277] media: imx: interweave and odd-chroma-row skip are incompatible If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Ha?asa Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++++++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* + * Skip writing U and V components to odd rows in the output + * channels for planar 4:2:0 (but not when enabling IDMAC + * interweaving, they are incompatible). + */ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 10945cbdbd71..604d0bd24389 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* + * Skip writing U and V components to odd rows (but not + * when enabling IDMAC interweaving, they are incompatible). + */ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: From 792f061b9f752f28fdaa1986b43f109689dc1416 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:10 -0500 Subject: [PATCH 0404/4277] media: imx-csi: Allow skipping odd chroma rows for YVU420 Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 604d0bd24389..6f1e11b8a7cb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? From 6e537b58de772cfc89d4fbd0cddd0c1de5f93822 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:11 -0500 Subject: [PATCH 0405/4277] media: imx: vdic: rely on VDIC for correct field order prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); From ab2f05cdb15421c1e57d3345852f6fb1405e3398 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:12 -0500 Subject: [PATCH 0406/4277] media: imx-csi: Move crop/compose reset after filling default mbus fields If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 27 ++++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 6f1e11b8a7cb..8537ecb7dd17 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1409,19 +1409,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1437,6 +1424,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } From 3c6640a6065fe77691e88ae135f45be6826acf0a Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:13 -0500 Subject: [PATCH 0407/4277] media: imx: Allow interweave with top/bottom lines swapped Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 25 +++++++++---- drivers/staging/media/imx/imx-media-csi.c | 40 ++++++++++++++++++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..33ada6612fee 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, * the IDMAC output channel. */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && - V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && - channel == priv->out_ch; + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, * channels for planar 4:2:0 (but not when enabling IDMAC * interweaving, they are incompatible). */ - if (!interweave && (channel == priv->out_ch || - channel == priv->rot_out_ch)) { + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8537ecb7dd17..555aa45e02e3 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv) if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num); + if (priv->interweave_swap) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys); } @@ -433,6 +437,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: @@ -486,6 +492,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) } if (passthrough) { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.phys0 += image.pix.bytesperline; + image.phys1 += image.pix.bytesperline; + } + ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width * passthrough_cycles, image.rect.height); @@ -495,6 +507,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ipu_cpmem_set_format_passthrough(priv->idmac_ch, passthrough_bits); } else { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + ret = ipu_cpmem_set_image(priv->idmac_ch, &image); if (ret) goto unsetup_vb2; @@ -526,6 +543,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (interweave) ipu_cpmem_interlaced_scan(priv->idmac_ch, + priv->interweave_swap ? + -image.pix.bytesperline : image.pix.bytesperline, image.pix.pixelformat); @@ -1338,16 +1357,27 @@ static void csi_try_field(struct csi_priv *priv, switch (infmt->field) { case V4L2_FIELD_SEQ_TB: case V4L2_FIELD_SEQ_BT: + /* + * If the user requests sequential at the source pad, + * allow it (along with possibly inverting field order). + * Otherwise passthrough the field type. + */ + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = infmt->field; + break; case V4L2_FIELD_ALTERNATE: /* - * If the sink is sequential or alternating fields, - * allow only SEQ_TB at the source. - * * This driver does not support alternate field mode, and * the CSI captures a whole frame, so the CSI never presents - * alternate mode at its source pads. + * alternate mode at its source pads. If user has not + * already requested sequential, translate ALTERNATE at + * sink pad to SEQ_TB or SEQ_BT at the source pad depending + * on input height (assume NTSC BT order if 480 total active + * frame lines, otherwise PAL TB order). */ - sdformat->format.field = V4L2_FIELD_SEQ_TB; + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = (infmt->height == 480 / 2) ? + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB; break; default: /* Passthrough for all other input field types */ From eed2235876ef5b0a04cd4716c5b2bb7bf68a56ca Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:14 -0500 Subject: [PATCH 0408/4277] media: imx.rst: Update doc to reflect fixes to interlaced capture Also add an example pipeline for unconverted capture with interweave on SabreAuto. Cleanup some language in various places in the process. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/imx.rst | 101 +++++++++++++++--------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/Documentation/media/v4l-drivers/imx.rst b/Documentation/media/v4l-drivers/imx.rst index 6922dde4a82b..9314af00d067 100644 --- a/Documentation/media/v4l-drivers/imx.rst +++ b/Documentation/media/v4l-drivers/imx.rst @@ -24,8 +24,8 @@ memory. Various dedicated DMA channels exist for both video capture and display paths. During transfer, the IDMAC is also capable of vertical image flip, 8x8 block transfer (see IRT description), pixel component re-ordering (for example UYVY to YUYV) within the same colorspace, and -even packed <--> planar conversion. It can also perform a simple -de-interlacing by interleaving even and odd lines during transfer +packed <--> planar conversion. The IDMAC can also perform a simple +de-interlacing by interweaving even and odd lines during transfer (without motion compensation which requires the VDIC). The CSI is the backend capture unit that interfaces directly with @@ -175,15 +175,21 @@ via the SMFC and an IDMAC channel, bypassing IC pre-processing. This source pad is routed to a capture device node, with a node name of the format "ipuX_csiY capture". -Note that since the IDMAC source pad makes use of an IDMAC channel, it -can do pixel reordering within the same colorspace. For example, the -sink pad can take UYVY2X8, but the IDMAC source pad can output YUYV2X8. -If the sink pad is receiving YUV, the output at the capture device can -also be converted to a planar YUV format such as YUV420. +Note that since the IDMAC source pad makes use of an IDMAC channel, +pixel reordering within the same colorspace can be carried out by the +IDMAC channel. For example, if the CSI sink pad is receiving in UYVY +order, the capture device linked to the IDMAC source pad can capture +in YUYV order. Also, if the CSI sink pad is receiving a packed YUV +format, the capture device can capture a planar YUV format such as +YUV420. -It will also perform simple de-interlace without motion compensation, -which is activated if the sink pad's field type is an interlaced type, -and the IDMAC source pad field type is set to none. +The IDMAC channel at the IDMAC source pad also supports simple +interweave without motion compensation, which is activated if the source +pad's field type is sequential top-bottom or bottom-top, and the +requested capture interface field type is set to interlaced (t-b, b-t, +or unqualified interlaced). The capture interface will enforce the same +field order as the source pad field order (interlaced-bt if source pad +is seq-bt, interlaced-tb if source pad is seq-tb). This subdev can generate the following event when enabling the second IDMAC source pad: @@ -325,14 +331,14 @@ ipuX_vdic The VDIC carries out motion compensated de-interlacing, with three motion compensation modes: low, medium, and high motion. The mode is -specified with the menu control V4L2_CID_DEINTERLACING_MODE. It has -two sink pads and a single source pad. +specified with the menu control V4L2_CID_DEINTERLACING_MODE. The VDIC +has two sink pads and a single source pad. The direct sink pad receives from an ipuX_csiY direct pad. With this link the VDIC can only operate in high motion mode. When the IDMAC sink pad is activated, it receives from an output -or mem2mem device node. With this pipeline, it can also operate +or mem2mem device node. With this pipeline, the VDIC can also operate in low and medium modes, because these modes require receiving frames from memory buffers. Note that an output or mem2mem device is not implemented yet, so this sink pad currently has no links. @@ -345,8 +351,8 @@ ipuX_ic_prp This is the IC pre-processing entity. It acts as a router, routing data from its sink pad to one or both of its source pads. -It has a single sink pad. The sink pad can receive from the ipuX_csiY -direct pad, or from ipuX_vdic. +This entity has a single sink pad. The sink pad can receive from the +ipuX_csiY direct pad, or from ipuX_vdic. This entity has two source pads. One source pad routes to the pre-process encode task entity (ipuX_ic_prpenc), the other to the @@ -369,8 +375,8 @@ color-space conversion, resizing (downscaling and upscaling), horizontal and vertical flip, and 90/270 degree rotation. Flip and rotation are provided via standard V4L2 controls. -Like the ipuX_csiY IDMAC source, it can also perform simple de-interlace -without motion compensation, and pixel reordering. +Like the ipuX_csiY IDMAC source, this entity also supports simple +de-interlace without motion compensation, and pixel reordering. ipuX_ic_prpvf ------------- @@ -380,18 +386,18 @@ pad from ipuX_ic_prp, and a single source pad. The source pad is routed to a capture device node, with a node name of the format "ipuX_ic_prpvf capture". -It is identical in operation to ipuX_ic_prpenc, with the same resizing -and CSC operations and flip/rotation controls. It will receive and -process de-interlaced frames from the ipuX_vdic if ipuX_ic_prp is +This entity is identical in operation to ipuX_ic_prpenc, with the same +resizing and CSC operations and flip/rotation controls. It will receive +and process de-interlaced frames from the ipuX_vdic if ipuX_ic_prp is receiving from ipuX_vdic. -Like the ipuX_csiY IDMAC source, it can perform simple de-interlace -without motion compensation. However, note that if the ipuX_vdic is -included in the pipeline (ipuX_ic_prp is receiving from ipuX_vdic), -it's not possible to use simple de-interlace in ipuX_ic_prpvf, since -the ipuX_vdic has already carried out de-interlacing (with motion -compensation) and therefore the field type output from ipuX_ic_prp can -only be none. +Like the ipuX_csiY IDMAC source, this entity supports simple +interweaving without motion compensation. However, note that if the +ipuX_vdic is included in the pipeline (ipuX_ic_prp is receiving from +ipuX_vdic), it's not possible to use interweave in ipuX_ic_prpvf, +since the ipuX_vdic has already carried out de-interlacing (with +motion compensation) and therefore the field type output from +ipuX_vdic can only be none (progressive). Capture Pipelines ----------------- @@ -516,10 +522,33 @@ On the SabreAuto, an on-board ADV7180 SD decoder is connected to the parallel bus input on the internal video mux to IPU1 CSI0. The following example configures a pipeline to capture from the ADV7180 -video decoder, assuming NTSC 720x480 input signals, with Motion -Compensated de-interlacing. Pad field types assume the adv7180 outputs -"interlaced". $outputfmt can be any format supported by the ipu1_ic_prpvf -entity at its output pad: +video decoder, assuming NTSC 720x480 input signals, using simple +interweave (unconverted and without motion compensation). The adv7180 +must output sequential or alternating fields (field type 'seq-bt' for +NTSC, or 'alternate'): + +.. code-block:: none + + # Setup links + media-ctl -l "'adv7180 3-0021':0 -> 'ipu1_csi0_mux':1[1]" + media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]" + media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]" + # Configure pads + media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x480 field:seq-bt]" + media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x480]" + media-ctl -V "'ipu1_csi0':2 [fmt:AYUV32/720x480]" + # Configure "ipu1_csi0 capture" interface (assumed at /dev/video4) + v4l2-ctl -d4 --set-fmt-video=field=interlaced_bt + +Streaming can then begin on /dev/video4. The v4l2-ctl tool can also be +used to select any supported YUV pixelformat on /dev/video4. + +This example configures a pipeline to capture from the ADV7180 +video decoder, assuming PAL 720x576 input signals, with Motion +Compensated de-interlacing. The adv7180 must output sequential or +alternating fields (field type 'seq-tb' for PAL, or 'alternate'). +$outputfmt can be any format supported by the ipu1_ic_prpvf entity +at its output pad: .. code-block:: none @@ -531,11 +560,11 @@ entity at its output pad: media-ctl -l "'ipu1_ic_prp':2 -> 'ipu1_ic_prpvf':0[1]" media-ctl -l "'ipu1_ic_prpvf':1 -> 'ipu1_ic_prpvf capture':0[1]" # Configure pads - media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x480]" - media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x480 field:interlaced]" - media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x480 field:interlaced]" - media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x480 field:none]" - media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x480 field:none]" + media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x576 field:seq-tb]" + media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x576]" + media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x576]" + media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x576 field:none]" + media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x576 field:none]" media-ctl -V "'ipu1_ic_prpvf':1 [fmt:$outputfmt field:none]" Streaming can then begin on the capture device node at From 053bc5693863d9230f9a11b923ea713aa6a4ce91 Mon Sep 17 00:00:00 2001 From: Yang Shi Date: Wed, 16 Jan 2019 07:51:35 +0800 Subject: [PATCH 0409/4277] doc: memcontrol: fix the obsolete content about force empty We don't do page cache reparent anymore when offlining memcg, so update force empty related content accordingly. Reviewed-by: Shakeel Butt Acked-by: Michal Hocko Cc: Johannes Weiner Signed-off-by: Yang Shi Signed-off-by: Jonathan Corbet --- Documentation/cgroup-v1/memory.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/cgroup-v1/memory.txt b/Documentation/cgroup-v1/memory.txt index 3682e99234c2..8e2cb1dabeb0 100644 --- a/Documentation/cgroup-v1/memory.txt +++ b/Documentation/cgroup-v1/memory.txt @@ -70,7 +70,7 @@ Brief summary of control files. memory.soft_limit_in_bytes # set/show soft limit of memory usage memory.stat # show various statistics memory.use_hierarchy # set/show hierarchical account enabled - memory.force_empty # trigger forced move charge to parent + memory.force_empty # trigger forced page reclaim memory.pressure_level # set memory pressure notifications memory.swappiness # set/show swappiness parameter of vmscan (See sysctl's vm.swappiness) @@ -459,8 +459,9 @@ About use_hierarchy, see Section 6. the cgroup will be reclaimed and as many pages reclaimed as possible. The typical use case for this interface is before calling rmdir(). - Because rmdir() moves all pages to parent, some out-of-use page caches can be - moved to the parent. If you want to avoid that, force_empty will be useful. + Though rmdir() offlines memcg, but the memcg may still stay there due to + charged file caches. Some out-of-use page caches may keep charged until + memory pressure happens. If you want to avoid that, force_empty will be useful. Also, note that when memory.kmem.limit_in_bytes is set the charges due to kernel pages will still be seen. This is not considered a failure and the From 6e6c61d3e342ee0830844b1ce3d433decfc2321e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jan 2019 11:26:52 +0100 Subject: [PATCH 0410/4277] LICENSES: Add GCC runtime library exception text A recent commit added SPDX identifiers to the SuperH low level library code which originates from GCC. This code is licensed under the GPL 2.0 or later with the GCC runtime library exception. Unfortunately the authors did not bother to add the exception text to the LICENSES directory so spdxcheck fails with: arch/sh/lib/ashiftrt.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/ashlsi3.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/ashrsi3.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/lshrsi3.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/movmem.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/udiv_qrnnd.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/udivsi3.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/udivsi3_i4i-Os.S: 1:42 Invalid Exception ID: GCC-exception-2.0 arch/sh/lib/udivsi3_i4i.S: 1:42 Invalid Exception ID: GCC-exception-2.0 Add the exception text along with the required tags which allow automated checking. Fixes: 4494ce4fb4ff ("sh: lib: convert to SPDX identifiers") Signed-off-by: Thomas Gleixner Acked-by: Greg Kroah-Hartman Cc: Kuninori Morimoto Cc: Simon Horman Cc: Yoshinori Sato Cc: Rich Felker Cc: Andrew Morton Cc: Kate Stewart Signed-off-by: Jonathan Corbet --- LICENSES/exceptions/GCC-exception-2.0 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 LICENSES/exceptions/GCC-exception-2.0 diff --git a/LICENSES/exceptions/GCC-exception-2.0 b/LICENSES/exceptions/GCC-exception-2.0 new file mode 100644 index 000000000000..422914a88c36 --- /dev/null +++ b/LICENSES/exceptions/GCC-exception-2.0 @@ -0,0 +1,18 @@ +SPDX-Exception-Identifier: GCC-exception-2.0 +SPDX-URL: https://spdx.org/licenses/GCC-exception-2.0.html +SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-2.0-only, GPL-2.0-or-later +Usage-Guide: + This exception is used together with one of the above SPDX-Licenses to + allow linking the compiled version of code to non GPL compliant code. + To use this exception add it with the keyword WITH to one of the + identifiers in the SPDX-Licenses tag: + SPDX-License-Identifier: WITH GCC-exception-2.0 +License-Text: + +In addition to the permissions in the GNU Library General Public License, +the Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, and to +distribute those programs without any restriction coming from the use of +this file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) From 959b49687838c9eae38f19cb19d504ba880b6cd0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jan 2019 11:26:53 +0100 Subject: [PATCH 0411/4277] scripts/spdxcheck.py: Handle special quotation mark comments The SuperH boot code files use a magic format for the SPDX identifier comment: LIST "SPDX-License-Identifier: .... " The trailing quotation mark is not stripped before the token parser is invoked and causes the scan to fail. Handle it gracefully. Fixes: 6a0abce4c4cc ("sh: include: convert to SPDX identifiers") Signed-off-by: Thomas Gleixner Cc: Kuninori Morimoto Cc: Simon Horman Cc: Yoshinori Sato Cc: Rich Felker Cc: Andrew Morton Cc: Kate Stewart Reviewed-by: Greg Kroah-Hartman Signed-off-by: Jonathan Corbet --- scripts/spdxcheck.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/spdxcheck.py b/scripts/spdxcheck.py index e559c6294c39..3fb020c2cb7f 100755 --- a/scripts/spdxcheck.py +++ b/scripts/spdxcheck.py @@ -175,7 +175,13 @@ class id_parser(object): self.lines_checked += 1 if line.find("SPDX-License-Identifier:") < 0: continue - expr = line.split(':')[1].replace('*/', '').strip() + expr = line.split(':')[1].strip() + # Remove trailing comment closure + if line.startswith('/*'): + expr = expr.rstrip('*/').strip() + # Special case for SH magic boot code files + if line.startswith('LIST \"'): + expr = expr.rstrip('\"').strip() self.parse(expr) self.spdx_valid += 1 # From be5cd20c9b491504dfb9105404913de25c47c580 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 11 Jan 2019 12:31:39 -0700 Subject: [PATCH 0412/4277] kernel-doc: suppress 'not described' warnings for embedded struct fields The ability to add kerneldoc comments for fields in embedded structures is useful, but it brought along a whole bunch of warnings for fields that could not be described before. In many cases, there's little value in adding docs for these nested fields, and in cases like: struct a { struct b { int c; } d, e; }; "c" would have to be described twice (as d.c and e.c) to make the warnings go away. We can no doubt do something smarter, but simply suppressing the warnings for this case removes about 70 warnings from the docs build, freeing us to focus on the ones that matter more. So make kerneldoc be silent about missing descriptions for any field containing a ".". Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index c5333d251985..3350e498b4ce 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -1474,7 +1474,7 @@ sub push_parameter($$$$) { if (!defined $parameterdescs{$param} && $param !~ /^#/) { $parameterdescs{$param} = $undescribed; - if (show_warnings($type, $declaration_name)) { + if (show_warnings($type, $declaration_name) && $param !~ /\./) { print STDERR "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; ++$warnings; From e8f9b16d72631870e30a3d8e4ee9f1c097bc7ba0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 17 Jan 2019 07:53:55 -0500 Subject: [PATCH 0413/4277] media: remove soc_camera ov9640 This driver got converted to not depend on soc_camera on commit 57b0ad9ebe60 ("media: soc_camera: ov9640: move ov9640 out of soc_camera"). There's no sense on keeping the old version there. Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 8 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/ov9640.h | 208 ------ drivers/media/i2c/soc_camera/soc_ov9640.c | 738 ---------------------- 4 files changed, 955 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/ov9640.h delete mode 100644 drivers/media/i2c/soc_camera/soc_ov9640.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 7d7b801ab2ce..f67499187bda 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -41,14 +41,6 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver -config SOC_CAMERA_OV9640 - tristate "ov9640 camera support (OBSOLETE)" - default n - depends on SOC_CAMERA && I2C - help - This is an obsoleted version of ov9640 camera driver. Please use - the v4l2 standalone one (VIDEO_OV9640). - config SOC_CAMERA_OV9740 tristate "ov9740 camera support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 09ae483b96ef..0d15864cfda8 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_SOC_CAMERA_MT9T112) += soc_mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o -obj-$(CONFIG_SOC_CAMERA_OV9640) += soc_ov9640.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h deleted file mode 100644 index 65d13ff17536..000000000000 --- a/drivers/media/i2c/soc_camera/ov9640.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * OmniVision OV96xx Camera Header File - * - * Copyright (C) 2009 Marek Vasut - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ -#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ - -/* Register definitions */ -#define OV9640_GAIN 0x00 -#define OV9640_BLUE 0x01 -#define OV9640_RED 0x02 -#define OV9640_VFER 0x03 -#define OV9640_COM1 0x04 -#define OV9640_BAVE 0x05 -#define OV9640_GEAVE 0x06 -#define OV9640_RSID 0x07 -#define OV9640_RAVE 0x08 -#define OV9640_COM2 0x09 -#define OV9640_PID 0x0a -#define OV9640_VER 0x0b -#define OV9640_COM3 0x0c -#define OV9640_COM4 0x0d -#define OV9640_COM5 0x0e -#define OV9640_COM6 0x0f -#define OV9640_AECH 0x10 -#define OV9640_CLKRC 0x11 -#define OV9640_COM7 0x12 -#define OV9640_COM8 0x13 -#define OV9640_COM9 0x14 -#define OV9640_COM10 0x15 -/* 0x16 - RESERVED */ -#define OV9640_HSTART 0x17 -#define OV9640_HSTOP 0x18 -#define OV9640_VSTART 0x19 -#define OV9640_VSTOP 0x1a -#define OV9640_PSHFT 0x1b -#define OV9640_MIDH 0x1c -#define OV9640_MIDL 0x1d -#define OV9640_MVFP 0x1e -#define OV9640_LAEC 0x1f -#define OV9640_BOS 0x20 -#define OV9640_GBOS 0x21 -#define OV9640_GROS 0x22 -#define OV9640_ROS 0x23 -#define OV9640_AEW 0x24 -#define OV9640_AEB 0x25 -#define OV9640_VPT 0x26 -#define OV9640_BBIAS 0x27 -#define OV9640_GBBIAS 0x28 -/* 0x29 - RESERVED */ -#define OV9640_EXHCH 0x2a -#define OV9640_EXHCL 0x2b -#define OV9640_RBIAS 0x2c -#define OV9640_ADVFL 0x2d -#define OV9640_ADVFH 0x2e -#define OV9640_YAVE 0x2f -#define OV9640_HSYST 0x30 -#define OV9640_HSYEN 0x31 -#define OV9640_HREF 0x32 -#define OV9640_CHLF 0x33 -#define OV9640_ARBLM 0x34 -/* 0x35..0x36 - RESERVED */ -#define OV9640_ADC 0x37 -#define OV9640_ACOM 0x38 -#define OV9640_OFON 0x39 -#define OV9640_TSLB 0x3a -#define OV9640_COM11 0x3b -#define OV9640_COM12 0x3c -#define OV9640_COM13 0x3d -#define OV9640_COM14 0x3e -#define OV9640_EDGE 0x3f -#define OV9640_COM15 0x40 -#define OV9640_COM16 0x41 -#define OV9640_COM17 0x42 -/* 0x43..0x4e - RESERVED */ -#define OV9640_MTX1 0x4f -#define OV9640_MTX2 0x50 -#define OV9640_MTX3 0x51 -#define OV9640_MTX4 0x52 -#define OV9640_MTX5 0x53 -#define OV9640_MTX6 0x54 -#define OV9640_MTX7 0x55 -#define OV9640_MTX8 0x56 -#define OV9640_MTX9 0x57 -#define OV9640_MTXS 0x58 -/* 0x59..0x61 - RESERVED */ -#define OV9640_LCC1 0x62 -#define OV9640_LCC2 0x63 -#define OV9640_LCC3 0x64 -#define OV9640_LCC4 0x65 -#define OV9640_LCC5 0x66 -#define OV9640_MANU 0x67 -#define OV9640_MANV 0x68 -#define OV9640_HV 0x69 -#define OV9640_MBD 0x6a -#define OV9640_DBLV 0x6b -#define OV9640_GSP 0x6c /* ... till 0x7b */ -#define OV9640_GST 0x7c /* ... till 0x8a */ - -#define OV9640_CLKRC_DPLL_EN 0x80 -#define OV9640_CLKRC_DIRECT 0x40 -#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) - -#define OV9640_PSHFT_VAL(x) ((x) & 0xff) - -#define OV9640_ACOM_2X_ANALOG 0x80 -#define OV9640_ACOM_RSVD 0x12 - -#define OV9640_MVFP_V 0x10 -#define OV9640_MVFP_H 0x20 - -#define OV9640_COM1_HREF_NOSKIP 0x00 -#define OV9640_COM1_HREF_2SKIP 0x04 -#define OV9640_COM1_HREF_3SKIP 0x08 -#define OV9640_COM1_QQFMT 0x20 - -#define OV9640_COM2_SSM 0x10 - -#define OV9640_COM3_VP 0x04 - -#define OV9640_COM4_QQ_VP 0x80 -#define OV9640_COM4_RSVD 0x40 - -#define OV9640_COM5_SYSCLK 0x80 -#define OV9640_COM5_LONGEXP 0x01 - -#define OV9640_COM6_OPT_BLC 0x40 -#define OV9640_COM6_ADBLC_BIAS 0x08 -#define OV9640_COM6_FMT_RST 0x82 -#define OV9640_COM6_ADBLC_OPTEN 0x01 - -#define OV9640_COM7_RAW_RGB 0x01 -#define OV9640_COM7_RGB 0x04 -#define OV9640_COM7_QCIF 0x08 -#define OV9640_COM7_QVGA 0x10 -#define OV9640_COM7_CIF 0x20 -#define OV9640_COM7_VGA 0x40 -#define OV9640_COM7_SCCB_RESET 0x80 - -#define OV9640_TSLB_YVYU_YUYV 0x04 -#define OV9640_TSLB_YUYV_UYVY 0x08 - -#define OV9640_COM12_YUV_AVG 0x04 -#define OV9640_COM12_RSVD 0x40 - -#define OV9640_COM13_GAMMA_NONE 0x00 -#define OV9640_COM13_GAMMA_Y 0x40 -#define OV9640_COM13_GAMMA_RAW 0x80 -#define OV9640_COM13_RGB_AVG 0x20 -#define OV9640_COM13_MATRIX_EN 0x10 -#define OV9640_COM13_Y_DELAY_EN 0x08 -#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) - -#define OV9640_COM15_OR_00FF 0x00 -#define OV9640_COM15_OR_01FE 0x40 -#define OV9640_COM15_OR_10F0 0xc0 -#define OV9640_COM15_RGB_NORM 0x00 -#define OV9640_COM15_RGB_565 0x10 -#define OV9640_COM15_RGB_555 0x30 - -#define OV9640_COM16_RB_AVG 0x01 - -/* IDs */ -#define OV9640_V2 0x9648 -#define OV9640_V3 0x9649 -#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) - -/* supported resolutions */ -enum { - W_QQCIF = 88, - W_QQVGA = 160, - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SXGA = 1280 -}; -#define H_SXGA 960 - -/* Misc. structures */ -struct ov9640_reg_alt { - u8 com7; - u8 com12; - u8 com13; - u8 com15; -}; - -struct ov9640_reg { - u8 reg; - u8 val; -}; - -struct ov9640_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - - int model; - int revision; -}; - -#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/i2c/soc_camera/soc_ov9640.c b/drivers/media/i2c/soc_camera/soc_ov9640.c deleted file mode 100644 index eb91b8240083..000000000000 --- a/drivers/media/i2c/soc_camera/soc_ov9640.c +++ /dev/null @@ -1,738 +0,0 @@ -/* - * OmniVision OV96xx Camera Driver - * - * Copyright (C) 2009 Marek Vasut - * - * Based on ov772x camera driver: - * - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov7670 and soc_camera_platform driver, - * - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ov9640.h" - -#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) - -/* default register setup */ -static const struct ov9640_reg ov9640_regs_dflt[] = { - { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, - { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | - OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, - { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, - { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, - { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, - { OV9640_COM16, OV9640_COM16_RB_AVG }, - - /* Gamma curve P */ - { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, - { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, - { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, - { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, - - /* Gamma curve T */ - { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, - { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, - { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, - { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, -}; - -/* Configurations - * NOTE: for YUV, alter the following registers: - * COM12 |= OV9640_COM12_YUV_AVG - * - * for RGB, alter the following registers: - * COM7 |= OV9640_COM7_RGB - * COM13 |= OV9640_COM13_RGB_AVG - * COM15 |= proper RGB color encoding mode - */ -static const struct ov9640_reg ov9640_regs_qqcif[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, - { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QCIF }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_qqvga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, - { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QVGA }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_qcif[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QCIF }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_qvga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QVGA }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_cif[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, - { OV9640_COM3, OV9640_COM3_VP }, - { OV9640_COM7, OV9640_COM7_CIF }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_vga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, - { OV9640_COM3, OV9640_COM3_VP }, - { OV9640_COM7, OV9640_COM7_VGA }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_sxga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, - { OV9640_COM3, OV9640_COM3_VP }, - { OV9640_COM7, 0 }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_yuv[] = { - { OV9640_MTX1, 0x58 }, - { OV9640_MTX2, 0x48 }, - { OV9640_MTX3, 0x10 }, - { OV9640_MTX4, 0x28 }, - { OV9640_MTX5, 0x48 }, - { OV9640_MTX6, 0x70 }, - { OV9640_MTX7, 0x40 }, - { OV9640_MTX8, 0x40 }, - { OV9640_MTX9, 0x40 }, - { OV9640_MTXS, 0x0f }, -}; - -static const struct ov9640_reg ov9640_regs_rgb[] = { - { OV9640_MTX1, 0x71 }, - { OV9640_MTX2, 0x3e }, - { OV9640_MTX3, 0x0c }, - { OV9640_MTX4, 0x33 }, - { OV9640_MTX5, 0x72 }, - { OV9640_MTX6, 0x00 }, - { OV9640_MTX7, 0x2b }, - { OV9640_MTX8, 0x66 }, - { OV9640_MTX9, 0xd2 }, - { OV9640_MTXS, 0x65 }, -}; - -static u32 ov9640_codes[] = { - MEDIA_BUS_FMT_UYVY8_2X8, - MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - MEDIA_BUS_FMT_RGB565_2X8_LE, -}; - -/* read a register */ -static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) -{ - int ret; - u8 data = reg; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &data, - }; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - goto err; - - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - goto err; - - *val = data; - return 0; - -err: - dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); - return ret; -} - -/* write a register */ -static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) -{ - int ret; - u8 _val; - unsigned char data[2] = { reg, val }; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = data, - }; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); - return ret; - } - - /* we have to read the register back ... no idea why, maybe HW bug */ - ret = ov9640_reg_read(client, reg, &_val); - if (ret) - dev_err(&client->dev, - "Failed reading back register 0x%02x!\n", reg); - - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) -{ - u8 val; - int ret; - - ret = ov9640_reg_read(client, reg, &val); - if (ret) { - dev_err(&client->dev, - "[Read]-Modify-Write of register %02x failed!\n", reg); - return ret; - } - - val |= set; - val &= ~unset; - - ret = ov9640_reg_write(client, reg, val); - if (ret) - dev_err(&client->dev, - "Read-Modify-[Write] of register %02x failed!\n", reg); - - return ret; -} - -/* Soft reset the camera. This has nothing to do with the RESET pin! */ -static int ov9640_reset(struct i2c_client *client) -{ - int ret; - - ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); - if (ret) - dev_err(&client->dev, - "An error occurred while entering soft reset!\n"); - - return ret; -} - -/* Start/Stop streaming from the device */ -static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) -{ - return 0; -} - -/* Set status of additional camera capabilities */ -static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_V, 0); - return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); - case V4L2_CID_HFLIP: - if (ctrl->val) - return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_H, 0); - return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); - } - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov9640_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xff) - return -EINVAL; - - reg->size = 1; - - ret = ov9640_reg_read(client, reg->reg, &val); - if (ret) - return ret; - - reg->val = (__u64)val; - - return 0; -} - -static int ov9640_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xff || reg->val & ~0xff) - return -EINVAL; - - return ov9640_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov9640_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -/* select nearest higher resolution for capture */ -static void ov9640_res_roundup(u32 *width, u32 *height) -{ - int i; - enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; - static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; - static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; - - for (i = 0; i < ARRAY_SIZE(res_x); i++) { - if (res_x[i] >= *width && res_y[i] >= *height) { - *width = res_x[i]; - *height = res_y[i]; - return; - } - } - - *width = res_x[SXGA]; - *height = res_y[SXGA]; -} - -/* Prepare necessary register changes depending on color encoding */ -static void ov9640_alter_regs(u32 code, - struct ov9640_reg_alt *alt) -{ - switch (code) { - default: - case MEDIA_BUS_FMT_UYVY8_2X8: - alt->com12 = OV9640_COM12_YUV_AVG; - alt->com13 = OV9640_COM13_Y_DELAY_EN | - OV9640_COM13_YUV_DLY(0x01); - break; - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - alt->com7 = OV9640_COM7_RGB; - alt->com13 = OV9640_COM13_RGB_AVG; - alt->com15 = OV9640_COM15_RGB_555; - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - alt->com7 = OV9640_COM7_RGB; - alt->com13 = OV9640_COM13_RGB_AVG; - alt->com15 = OV9640_COM15_RGB_565; - break; - } -} - -/* Setup registers according to resolution and color encoding */ -static int ov9640_write_regs(struct i2c_client *client, u32 width, - u32 code, struct ov9640_reg_alt *alts) -{ - const struct ov9640_reg *ov9640_regs, *matrix_regs; - int ov9640_regs_len, matrix_regs_len; - int i, ret; - u8 val; - - /* select register configuration for given resolution */ - switch (width) { - case W_QQCIF: - ov9640_regs = ov9640_regs_qqcif; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); - break; - case W_QQVGA: - ov9640_regs = ov9640_regs_qqvga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); - break; - case W_QCIF: - ov9640_regs = ov9640_regs_qcif; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); - break; - case W_QVGA: - ov9640_regs = ov9640_regs_qvga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); - break; - case W_CIF: - ov9640_regs = ov9640_regs_cif; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); - break; - case W_VGA: - ov9640_regs = ov9640_regs_vga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); - break; - case W_SXGA: - ov9640_regs = ov9640_regs_sxga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); - break; - default: - dev_err(&client->dev, "Failed to select resolution!\n"); - return -EINVAL; - } - - /* select color matrix configuration for given color encoding */ - if (code == MEDIA_BUS_FMT_UYVY8_2X8) { - matrix_regs = ov9640_regs_yuv; - matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); - } else { - matrix_regs = ov9640_regs_rgb; - matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); - } - - /* write register settings into the module */ - for (i = 0; i < ov9640_regs_len; i++) { - val = ov9640_regs[i].val; - - switch (ov9640_regs[i].reg) { - case OV9640_COM7: - val |= alts->com7; - break; - case OV9640_COM12: - val |= alts->com12; - break; - case OV9640_COM13: - val |= alts->com13; - break; - case OV9640_COM15: - val |= alts->com15; - break; - } - - ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); - if (ret) - return ret; - } - - /* write color matrix configuration into the module */ - for (i = 0; i < matrix_regs_len; i++) { - ret = ov9640_reg_write(client, matrix_regs[i].reg, - matrix_regs[i].val); - if (ret) - return ret; - } - - return 0; -} - -/* program default register values */ -static int ov9640_prog_dflt(struct i2c_client *client) -{ - int i, ret; - - for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { - ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, - ov9640_regs_dflt[i].val); - if (ret) - return ret; - } - - /* wait for the changes to actually happen, 140ms are not enough yet */ - mdelay(150); - - return 0; -} - -/* set the format we will capture in */ -static int ov9640_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9640_reg_alt alts = {0}; - int ret; - - ov9640_alter_regs(mf->code, &alts); - - ov9640_reset(client); - - ret = ov9640_prog_dflt(client); - if (ret) - return ret; - - return ov9640_write_regs(client, mf->width, mf->code, &alts); -} - -static int ov9640_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - ov9640_res_roundup(&mf->width, &mf->height); - - mf->field = V4L2_FIELD_NONE; - - switch (mf->code) { - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - mf->colorspace = V4L2_COLORSPACE_SRGB; - break; - default: - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - /* fall through */ - case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; - break; - } - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov9640_s_fmt(sd, mf); - - cfg->try_fmt = *mf; - return 0; -} - -static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) - return -EINVAL; - - code->code = ov9640_codes[code->index]; - return 0; -} - -static int ov9640_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - sel->r.width = W_SXGA; - sel->r.height = H_SXGA; - return 0; - default: - return -EINVAL; - } -} - -static int ov9640_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - u8 pid, ver, midh, midl; - const char *devname; - int ret; - - ret = ov9640_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - - ret = ov9640_reg_read(client, OV9640_PID, &pid); - if (!ret) - ret = ov9640_reg_read(client, OV9640_VER, &ver); - if (!ret) - ret = ov9640_reg_read(client, OV9640_MIDH, &midh); - if (!ret) - ret = ov9640_reg_read(client, OV9640_MIDL, &midl); - if (ret) - goto done; - - switch (VERSION(pid, ver)) { - case OV9640_V2: - devname = "ov9640"; - priv->revision = 2; - break; - case OV9640_V3: - devname = "ov9640"; - priv->revision = 3; - break; - default: - dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", - devname, pid, ver, midh, midl); - - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov9640_s_power(&priv->subdev, 0); - return ret; -} - -static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { - .s_ctrl = ov9640_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops ov9640_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov9640_get_register, - .s_register = ov9640_set_register, -#endif - .s_power = ov9640_s_power, -}; - -/* Request bus settings on camera side */ -static int ov9640_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov9640_video_ops = { - .s_stream = ov9640_s_stream, - .g_mbus_config = ov9640_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { - .enum_mbus_code = ov9640_enum_mbus_code, - .get_selection = ov9640_get_selection, - .set_fmt = ov9640_set_fmt, -}; - -static const struct v4l2_subdev_ops ov9640_subdev_ops = { - .core = &ov9640_core_ops, - .video = &ov9640_video_ops, - .pad = &ov9640_pad_ops, -}; - -/* - * i2c_driver function - */ -static int ov9640_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov9640_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); - - v4l2_ctrl_handler_init(&priv->hdl, 2); - v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov9640_video_probe(client); - if (ret) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } - - return ret; -} - -static int ov9640_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov9640_id[] = { - { "ov9640", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov9640_id); - -static struct i2c_driver ov9640_i2c_driver = { - .driver = { - .name = "ov9640", - }, - .probe = ov9640_probe, - .remove = ov9640_remove, - .id_table = ov9640_id, -}; - -module_i2c_driver(ov9640_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); -MODULE_AUTHOR("Marek Vasut "); -MODULE_LICENSE("GPL v2"); From 5b8e432dbb0e20bd8e99033f4a0bfa0d38c0e08e Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Tue, 15 Jan 2019 00:41:43 +0800 Subject: [PATCH 0414/4277] xen/blkback: add stack variable 'blkif' in connect_ring() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As 'be->blkif' is used for many times in connect_ring(), the stack variable 'blkif' is added to substitute 'be-blkif'. Suggested-by: Paul Durrant Signed-off-by: Dongli Zhang Reviewed-by: Paul Durrant Reviewed-by: Roger Pau Monné Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkback/xenbus.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index a4bc74e72c39..a4aadac772e5 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -1023,6 +1023,7 @@ fail: static int connect_ring(struct backend_info *be) { struct xenbus_device *dev = be->dev; + struct xen_blkif *blkif = be->blkif; unsigned int pers_grants; char protocol[64] = ""; int err, i; @@ -1033,25 +1034,25 @@ static int connect_ring(struct backend_info *be) pr_debug("%s %s\n", __func__, dev->otherend); - be->blkif->blk_protocol = BLKIF_PROTOCOL_DEFAULT; + blkif->blk_protocol = BLKIF_PROTOCOL_DEFAULT; err = xenbus_scanf(XBT_NIL, dev->otherend, "protocol", "%63s", protocol); if (err <= 0) strcpy(protocol, "unspecified, assuming default"); else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE)) - be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; + blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE; else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) - be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; + blkif->blk_protocol = BLKIF_PROTOCOL_X86_32; else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) - be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; + blkif->blk_protocol = BLKIF_PROTOCOL_X86_64; else { xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol); return -ENOSYS; } pers_grants = xenbus_read_unsigned(dev->otherend, "feature-persistent", 0); - be->blkif->vbd.feature_gnt_persistent = pers_grants; - be->blkif->vbd.overflow_max_grants = 0; + blkif->vbd.feature_gnt_persistent = pers_grants; + blkif->vbd.overflow_max_grants = 0; /* * Read the number of hardware queues from frontend. @@ -1067,16 +1068,16 @@ static int connect_ring(struct backend_info *be) requested_num_queues, xenblk_max_queues); return -ENOSYS; } - be->blkif->nr_rings = requested_num_queues; - if (xen_blkif_alloc_rings(be->blkif)) + blkif->nr_rings = requested_num_queues; + if (xen_blkif_alloc_rings(blkif)) return -ENOMEM; pr_info("%s: using %d queues, protocol %d (%s) %s\n", dev->nodename, - be->blkif->nr_rings, be->blkif->blk_protocol, protocol, + blkif->nr_rings, blkif->blk_protocol, protocol, pers_grants ? "persistent grants" : ""); - if (be->blkif->nr_rings == 1) - return read_per_ring_refs(&be->blkif->rings[0], dev->otherend); + if (blkif->nr_rings == 1) + return read_per_ring_refs(&blkif->rings[0], dev->otherend); else { xspathsize = strlen(dev->otherend) + xenstore_path_ext_size; xspath = kmalloc(xspathsize, GFP_KERNEL); @@ -1085,10 +1086,10 @@ static int connect_ring(struct backend_info *be) return -ENOMEM; } - for (i = 0; i < be->blkif->nr_rings; i++) { + for (i = 0; i < blkif->nr_rings; i++) { memset(xspath, 0, xspathsize); snprintf(xspath, xspathsize, "%s/queue-%u", dev->otherend, i); - err = read_per_ring_refs(&be->blkif->rings[i], xspath); + err = read_per_ring_refs(&blkif->rings[i], xspath); if (err) { kfree(xspath); return err; From fd4b77e8deea9cced6c1eca8bd64d2f03fc7cc94 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Tue, 15 Jan 2019 09:53:22 +0100 Subject: [PATCH 0415/4277] drm/etnaviv: don't restrict to certain architectures The Vivante GPU cores are found in many different SoCs and the driver does not depend on anything architecture specific, so just drop the architecture restriction. Signed-off-by: Lucas Stach Acked-by: Sam Ravnborg --- drivers/gpu/drm/etnaviv/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index 342591a1084e..21df44b78df3 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -2,7 +2,6 @@ config DRM_ETNAVIV tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" depends on DRM - depends on ARCH_MXC || ARCH_DOVE || ARM || COMPILE_TEST depends on MMU select SHMEM select SYNC_FILE From 6d7fbce7da0cd06ff3f3f30e009a15a6243f0bc0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Jan 2019 12:02:57 -0500 Subject: [PATCH 0416/4277] kill kernfs_pin_sb() unused now and impossible to use safely anyway. Signed-off-by: Al Viro --- fs/kernfs/mount.c | 30 ------------------------------ include/linux/kernfs.h | 1 - 2 files changed, 31 deletions(-) diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index d71c9405874a..4d303047a4f8 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -377,36 +377,6 @@ void kernfs_kill_sb(struct super_block *sb) kfree(info); } -/** - * kernfs_pin_sb: try to pin the superblock associated with a kernfs_root - * @kernfs_root: the kernfs_root in question - * @ns: the namespace tag - * - * Pin the superblock so the superblock won't be destroyed in subsequent - * operations. This can be used to block ->kill_sb() which may be useful - * for kernfs users which dynamically manage superblocks. - * - * Returns NULL if there's no superblock associated to this kernfs_root, or - * -EINVAL if the superblock is being freed. - */ -struct super_block *kernfs_pin_sb(struct kernfs_root *root, const void *ns) -{ - struct kernfs_super_info *info; - struct super_block *sb = NULL; - - mutex_lock(&kernfs_mutex); - list_for_each_entry(info, &root->supers, node) { - if (info->ns == ns) { - sb = info->sb; - if (!atomic_inc_not_zero(&info->sb->s_active)) - sb = ERR_PTR(-EINVAL); - break; - } - } - mutex_unlock(&kernfs_mutex); - return sb; -} - void __init kernfs_init(void) { diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 5b36b1287a5a..44acb4c3659c 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -357,7 +357,6 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, struct kernfs_root *root, unsigned long magic, bool *new_sb_created, const void *ns); void kernfs_kill_sb(struct super_block *sb); -struct super_block *kernfs_pin_sb(struct kernfs_root *root, const void *ns); void kernfs_init(void); From c0e6f5bf7676677af48abb8de706b2fb10191a27 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 16 Jan 2019 18:54:33 +0100 Subject: [PATCH 0417/4277] clk: meson: gxbb: claim clock controller input clock from DT Instead of relying on a fixed name for the xtal clock, claim the controller input clock trough DT. Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190116175435.4990-2-jbrunet@baylibre.com --- drivers/clk/meson/gxbb.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 65f2599e5243..ecf9a8f6281c 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -16,6 +16,8 @@ #include "gxbb.h" #include "clk-regmap.h" +#define IN_PREFIX "ee-in-" + static DEFINE_SPINLOCK(meson_clk_lock); static const struct pll_params_table gxbb_gp0_pll_params_table[] = { @@ -118,7 +120,7 @@ static struct clk_regmap gxbb_fixed_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "fixed_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -148,7 +150,7 @@ static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = { .hw.init = &(struct clk_init_data){ .name = "hdmi_pll_pre_mult", .ops = &clk_fixed_factor_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -241,7 +243,7 @@ static struct clk_regmap gxl_hdmi_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "hdmi_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, /* * Display directly handle hdmi pll registers ATM, we need @@ -378,7 +380,7 @@ static struct clk_regmap gxbb_sys_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "sys_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -439,7 +441,7 @@ static struct clk_regmap gxbb_gp0_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp0_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -491,7 +493,7 @@ static struct clk_regmap gxl_gp0_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp0_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -789,7 +791,7 @@ static struct clk_regmap gxbb_mpll2 = { static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; static const char * const clk81_parent_names[] = { - "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", + IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", "fclk_div3", "fclk_div5" }; @@ -852,7 +854,7 @@ static struct clk_regmap gxbb_sar_adc_clk_sel = { .name = "sar_adc_clk_sel", .ops = &clk_regmap_mux_ops, /* NOTE: The datasheet doesn't list the parents for bit 10 */ - .parent_names = (const char *[]){ "xtal", "clk81", }, + .parent_names = (const char *[]){ IN_PREFIX "xtal", "clk81", }, .num_parents = 2, }, }; @@ -891,7 +893,7 @@ static struct clk_regmap gxbb_sar_adc_clk = { */ static const char * const gxbb_mali_0_1_parent_names[] = { - "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7", + IN_PREFIX "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7", "fclk_div4", "fclk_div3", "fclk_div5" }; @@ -1153,7 +1155,7 @@ static struct clk_regmap gxbb_32k_clk = { }; static const char * const gxbb_32k_clk_parent_names[] = { - "xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5" + IN_PREFIX "xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5" }; static struct clk_regmap gxbb_32k_clk_sel = { @@ -1172,7 +1174,7 @@ static struct clk_regmap gxbb_32k_clk_sel = { }; static const char * const gxbb_sd_emmc_clk0_parent_names[] = { - "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", /* * Following these parent clocks, we should also have had mpll2, mpll3 @@ -2138,7 +2140,7 @@ static struct clk_regmap gxbb_hdmi_tx = { /* HDMI Clocks */ static const char * const gxbb_hdmi_parent_names[] = { - "xtal", "fclk_div4", "fclk_div3", "fclk_div5" + IN_PREFIX "xtal", "fclk_div4", "fclk_div3", "fclk_div5" }; static struct clk_regmap gxbb_hdmi_sel = { @@ -2285,7 +2287,7 @@ static struct clk_regmap gxbb_vdec_hevc = { static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, }; static const char * const gen_clk_parent_names[] = { - "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2", + IN_PREFIX "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2", "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll", }; @@ -3085,6 +3087,7 @@ static const struct of_device_id clkc_match_table[] = { static int gxbb_clkc_probe(struct platform_device *pdev) { const struct clkc_data *clkc_data; + struct clk_hw *input; struct regmap *map; int ret, i; struct device *dev = &pdev->dev; @@ -3100,6 +3103,14 @@ static int gxbb_clkc_probe(struct platform_device *pdev) return PTR_ERR(map); } + input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); + if (IS_ERR(input)) { + ret = PTR_ERR(input); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get input clock"); + return ret; + } + /* Populate regmap for the common regmap backed clocks */ for (i = 0; i < ARRAY_SIZE(gx_clk_regmaps); i++) gx_clk_regmaps[i]->map = map; From 6e73dac7078f13d7fd2ee0d59941c5425bcf8ed8 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 16 Jan 2019 18:54:34 +0100 Subject: [PATCH 0418/4277] clk: meson: axg: claim clock controller input clock from DT Instead of relying on a fixed name for the xtal clock, claim the controller input clock trough DT. Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190116175435.4990-3-jbrunet@baylibre.com --- drivers/clk/meson/axg.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 792735d7e46e..1c6539eeee7c 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -20,6 +20,8 @@ #include "clkc.h" #include "axg.h" +#define IN_PREFIX "ee-in-" + static DEFINE_SPINLOCK(meson_clk_lock); static struct clk_regmap axg_fixed_pll_dco = { @@ -58,7 +60,7 @@ static struct clk_regmap axg_fixed_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "fixed_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -113,7 +115,7 @@ static struct clk_regmap axg_sys_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "sys_pll_dco", .ops = &meson_clk_pll_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -214,7 +216,7 @@ static struct clk_regmap axg_gp0_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "gp0_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -283,7 +285,7 @@ static struct clk_regmap axg_hifi_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "hifi_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -701,7 +703,7 @@ static struct clk_regmap axg_pcie_pll_dco = { .hw.init = &(struct clk_init_data){ .name = "pcie_pll_dco", .ops = &meson_clk_pll_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -803,7 +805,7 @@ static struct clk_regmap axg_pcie_cml_en1 = { static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; static const char * const clk81_parent_names[] = { - "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", + IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", "fclk_div3", "fclk_div5" }; @@ -852,7 +854,7 @@ static struct clk_regmap axg_clk81 = { }; static const char * const axg_sd_emmc_clk0_parent_names[] = { - "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", /* * Following these parent clocks, we should also have had mpll2, mpll3 @@ -957,7 +959,7 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = { static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, }; static const char * const gen_clk_parent_names[] = { - "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3", + IN_PREFIX "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3", "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll", }; @@ -1263,6 +1265,7 @@ static const struct of_device_id clkc_match_table[] = { static int axg_clkc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct clk_hw *input; struct regmap *map; int ret, i; @@ -1273,6 +1276,14 @@ static int axg_clkc_probe(struct platform_device *pdev) return PTR_ERR(map); } + input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); + if (IS_ERR(input)) { + ret = PTR_ERR(input); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get input clock"); + return ret; + } + /* Populate regmap for the regmap backed clocks */ for (i = 0; i < ARRAY_SIZE(axg_clk_regmaps); i++) axg_clk_regmaps[i]->map = map; From 439a6bb5bfe775f9aa18f267a54878e0ac7f3d49 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 16 Jan 2019 18:54:35 +0100 Subject: [PATCH 0419/4277] clk: meson: ao-clkc: claim clock controller input clocks from DT Instead of relying on a fixed names for the differents input clocks of the controller, get them through DT. Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190116175435.4990-4-jbrunet@baylibre.com --- drivers/clk/meson/axg-aoclk.c | 22 +++++++++++++++----- drivers/clk/meson/gxbb-aoclk.c | 25 ++++++++++++++++------ drivers/clk/meson/meson-aoclk.c | 37 ++++++++++++++++++++++++++++++++- drivers/clk/meson/meson-aoclk.h | 12 +++++++++-- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c index 5f518be144ce..5701f5840b75 100644 --- a/drivers/clk/meson/axg-aoclk.c +++ b/drivers/clk/meson/axg-aoclk.c @@ -16,6 +16,8 @@ #include "meson-aoclk.h" #include "axg-aoclk.h" +#define IN_PREFIX "ao-in-" + /* * AO Configuration Clock registers offsets * Register offsets from the data sheet must be multiplied by 4. @@ -38,7 +40,7 @@ static struct clk_regmap axg_aoclk_##_name = { \ .hw.init = &(struct clk_init_data) { \ .name = "axg_ao_" #_name, \ .ops = &clk_regmap_gate_ops, \ - .parent_names = (const char *[]){ "clk81" }, \ + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \ .num_parents = 1, \ .flags = CLK_IGNORE_UNUSED, \ }, \ @@ -60,7 +62,7 @@ static struct clk_regmap axg_aoclk_cts_oscin = { .hw.init = &(struct clk_init_data){ .name = "cts_oscin", .ops = &clk_regmap_gate_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -167,7 +169,7 @@ static struct clk_regmap axg_aoclk_cts_rtc_oscin = { .name = "axg_ao_cts_rtc_oscin", .ops = &clk_regmap_mux_ops, .parent_names = (const char *[]){ "axg_ao_32k", - "axg_ext_32k" }, + IN_PREFIX "ext_32k-0" }, .num_parents = 2, .flags = CLK_SET_RATE_PARENT, }, @@ -183,7 +185,7 @@ static struct clk_regmap axg_aoclk_clk81 = { .hw.init = &(struct clk_init_data){ .name = "axg_ao_clk81", .ops = &clk_regmap_mux_ro_ops, - .parent_names = (const char *[]){ "clk81", + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk", "axg_ao_cts_rtc_oscin"}, .num_parents = 2, .flags = CLK_SET_RATE_PARENT, @@ -199,7 +201,8 @@ static struct clk_regmap axg_aoclk_saradc_mux = { .hw.init = &(struct clk_init_data){ .name = "axg_ao_saradc_mux", .ops = &clk_regmap_mux_ops, - .parent_names = (const char *[]){ "xtal", "axg_ao_clk81" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal", + "axg_ao_clk81" }, .num_parents = 2, }, }; @@ -285,6 +288,12 @@ static const struct clk_hw_onecell_data axg_aoclk_onecell_data = { .num = NR_CLKS, }; +static const struct meson_aoclk_input axg_aoclk_inputs[] = { + { .name = "xtal", .required = true }, + { .name = "mpeg-clk", .required = true }, + { .name = "ext-32k-0", .required = false }, +}; + static const struct meson_aoclk_data axg_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(axg_aoclk_reset), @@ -292,6 +301,9 @@ static const struct meson_aoclk_data axg_aoclkc_data = { .num_clks = ARRAY_SIZE(axg_aoclk_regmap), .clks = axg_aoclk_regmap, .hw_data = &axg_aoclk_onecell_data, + .inputs = axg_aoclk_inputs, + .num_inputs = ARRAY_SIZE(axg_aoclk_inputs), + .input_prefix = IN_PREFIX, }; static const struct of_device_id axg_aoclkc_match_table[] = { diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index 5fa57b623b8f..510b6a7d2f18 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -9,6 +9,8 @@ #include "meson-aoclk.h" #include "gxbb-aoclk.h" +#define IN_PREFIX "ao-in-" + /* AO Configuration Clock registers offsets */ #define AO_RTI_PWR_CNTL_REG1 0x0c #define AO_RTI_PWR_CNTL_REG0 0x10 @@ -27,7 +29,7 @@ static struct clk_regmap _name##_ao = { \ .hw.init = &(struct clk_init_data) { \ .name = #_name "_ao", \ .ops = &clk_regmap_gate_ops, \ - .parent_names = (const char *[]){ "clk81" }, \ + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \ .num_parents = 1, \ .flags = CLK_IGNORE_UNUSED, \ }, \ @@ -48,7 +50,7 @@ static struct clk_regmap ao_cts_oscin = { .hw.init = &(struct clk_init_data){ .name = "ao_cts_oscin", .ops = &clk_regmap_gate_ro_ops, - .parent_names = (const char *[]){ "xtal" }, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, .num_parents = 1, }, }; @@ -155,9 +157,9 @@ static struct clk_regmap ao_cts_rtc_oscin = { .hw.init = &(struct clk_init_data){ .name = "ao_cts_rtc_oscin", .ops = &clk_regmap_mux_ops, - .parent_names = (const char *[]){ "ext_32k_0", - "ext_32k_1", - "ext_32k_2", + .parent_names = (const char *[]){ IN_PREFIX "ext-32k-0", + IN_PREFIX "ext-32k-1", + IN_PREFIX "ext-32k-2", "ao_32k" }, .num_parents = 4, .flags = CLK_SET_RATE_PARENT, @@ -174,7 +176,7 @@ static struct clk_regmap ao_clk81 = { .hw.init = &(struct clk_init_data){ .name = "ao_clk81", .ops = &clk_regmap_mux_ro_ops, - .parent_names = (const char *[]){ "clk81", + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk", "ao_cts_rtc_oscin" }, .num_parents = 2, .flags = CLK_SET_RATE_PARENT, @@ -257,6 +259,14 @@ static const struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { .num = NR_CLKS, }; +static const struct meson_aoclk_input gxbb_aoclk_inputs[] = { + { .name = "xtal", .required = true, }, + { .name = "mpeg-clk", .required = true, }, + {. name = "ext-32k-0", .required = false, }, + {. name = "ext-32k-1", .required = false, }, + {. name = "ext-32k-2", .required = false, }, +}; + static const struct meson_aoclk_data gxbb_aoclkc_data = { .reset_reg = AO_RTI_GEN_CNTL_REG0, .num_reset = ARRAY_SIZE(gxbb_aoclk_reset), @@ -264,6 +274,9 @@ static const struct meson_aoclk_data gxbb_aoclkc_data = { .num_clks = ARRAY_SIZE(gxbb_aoclk), .clks = gxbb_aoclk, .hw_data = &gxbb_aoclk_onecell_data, + .inputs = gxbb_aoclk_inputs, + .num_inputs = ARRAY_SIZE(gxbb_aoclk_inputs), + .input_prefix = IN_PREFIX, }; static const struct of_device_id gxbb_aoclkc_match_table[] = { diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index 258c8d259ea1..7b9d194ccc3b 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -14,7 +14,7 @@ #include #include #include -#include "clk-regmap.h" +#include #include "meson-aoclk.h" static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev, @@ -31,6 +31,37 @@ static const struct reset_control_ops meson_aoclk_reset_ops = { .reset = meson_aoclk_do_reset, }; +static int meson_aoclkc_register_inputs(struct device *dev, + struct meson_aoclk_data *data) +{ + struct clk_hw *hw; + char *str; + int i; + + for (i = 0; i < data->num_inputs; i++) { + const struct meson_aoclk_input *in = &data->inputs[i]; + + str = kasprintf(GFP_KERNEL, "%s%s", data->input_prefix, + in->name); + if (!str) + return -ENOMEM; + + hw = meson_clk_hw_register_input(dev, in->name, str, 0); + kfree(str); + + if (IS_ERR(hw)) { + if (!in->required && PTR_ERR(hw) == -ENOENT) + continue; + else if (PTR_ERR(hw) != -EPROBE_DEFER) + dev_err(dev, "failed to register input %s\n", + in->name); + return PTR_ERR(hw); + } + } + + return 0; +} + int meson_aoclkc_probe(struct platform_device *pdev) { struct meson_aoclk_reset_controller *rstc; @@ -53,6 +84,10 @@ int meson_aoclkc_probe(struct platform_device *pdev) return PTR_ERR(regmap); } + ret = meson_aoclkc_register_inputs(dev, data); + if (ret) + return ret; + /* Reset Controller */ rstc->data = data; rstc->regmap = regmap; diff --git a/drivers/clk/meson/meson-aoclk.h b/drivers/clk/meson/meson-aoclk.h index ab2819e88922..0758b35d4427 100644 --- a/drivers/clk/meson/meson-aoclk.h +++ b/drivers/clk/meson/meson-aoclk.h @@ -13,14 +13,22 @@ #include #include -#include "clk-regmap.h" +#include "clkc.h" + +struct meson_aoclk_input { + const char *name; + bool required; +}; struct meson_aoclk_data { const unsigned int reset_reg; const int num_reset; const unsigned int *reset; - int num_clks; + const int num_clks; struct clk_regmap **clks; + const int num_inputs; + const struct meson_aoclk_input *inputs; + const char *input_prefix; const struct clk_hw_onecell_data *hw_data; }; From 9b7e6242ee4efcd7f9ef699bf1965e3a5343f216 Mon Sep 17 00:00:00 2001 From: Stefan M Schaeckeler Date: Thu, 17 Jan 2019 08:38:16 -0800 Subject: [PATCH 0420/4277] EDAC, aspeed: Add an Aspeed AST2500 EDAC driver Add support for the Aspeed AST2500 SoC. Signed-off-by: Stefan M Schaeckeler Signed-off-by: Borislav Petkov Cc: Andrew Jeffery Cc: Joel Stanley Cc: Mark Rutland Cc: Mauro Carvalho Chehab Cc: Rob Herring Cc: devicetree@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-aspeed@lists.ozlabs.org Cc: linux-edac Link: https://lkml.kernel.org/r/1547743097-5236-2-git-send-email-schaecsn@gmx.net --- MAINTAINERS | 6 + arch/arm/boot/dts/aspeed-g5.dtsi | 7 + drivers/edac/Kconfig | 9 + drivers/edac/Makefile | 1 + drivers/edac/aspeed_edac.c | 421 +++++++++++++++++++++++++++++++ 5 files changed, 444 insertions(+) create mode 100644 drivers/edac/aspeed_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..6ce25174fa02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5396,6 +5396,12 @@ L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/amd64_edac* +EDAC-AST2500 +M: Stefan Schaeckeler +S: Supported +F: drivers/edac/aspeed_edac.c +F: Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt + EDAC-CALXEDA M: Robert Richter L: linux-edac@vger.kernel.org diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index d107459fc0f8..b4e479ab5a2d 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -47,6 +47,13 @@ reg = <0x80000000 0>; }; + edac: sdram@1e6e0000 { + compatible = "aspeed,ast2500-sdram-edac"; + reg = <0x1e6e0000 0x174>; + interrupts = <0>; + status = "disabled"; + }; + ahb { compatible = "simple-bus"; #address-cells = <1>; diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index e286b5b99003..480fa7926d06 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -475,4 +475,13 @@ config EDAC_QCOM For debugging issues having to do with stability and overall system health, you should probably say 'Y' here. +config EDAC_ASPEED + tristate "Aspeed AST 2500 SoC" + depends on MACH_ASPEED_G5 + help + Support for error detection and correction on the Aspeed AST 2500 SoC. + + First, ECC must be configured in the bootloader. Then, this driver + will expose error counters via the EDAC kernel framework. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 716096d08ea0..e1f23d4ff860 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o obj-$(CONFIG_EDAC_TI) += ti_edac.o obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o +obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c new file mode 100644 index 000000000000..11833c0a5d07 --- /dev/null +++ b/drivers/edac/aspeed_edac.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018, 2019 Cisco Systems + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "edac_module.h" + + +#define DRV_NAME "aspeed-edac" + + +#define ASPEED_MCR_PROT 0x00 /* protection key register */ +#define ASPEED_MCR_CONF 0x04 /* configuration register */ +#define ASPEED_MCR_INTR_CTRL 0x50 /* interrupt control/status register */ +#define ASPEED_MCR_ADDR_UNREC 0x58 /* address of first un-recoverable error */ +#define ASPEED_MCR_ADDR_REC 0x5c /* address of last recoverable error */ +#define ASPEED_MCR_LAST ASPEED_MCR_ADDR_REC + + +#define ASPEED_MCR_PROT_PASSWD 0xfc600309 +#define ASPEED_MCR_CONF_DRAM_TYPE BIT(4) +#define ASPEED_MCR_CONF_ECC BIT(7) +#define ASPEED_MCR_INTR_CTRL_CLEAR BIT(31) +#define ASPEED_MCR_INTR_CTRL_CNT_REC GENMASK(23, 16) +#define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) +#define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) + + +static struct regmap *aspeed_regmap; + + +static int regmap_reg_write(void *context, unsigned int reg, unsigned int val) +{ + void __iomem *regs = (void __iomem *)context; + + /* enable write to MCR register set */ + writel(ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT); + + writel(val, regs + reg); + + /* disable write to MCR register set */ + writel(~ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT); + + return 0; +} + + +static int regmap_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + void __iomem *regs = (void __iomem *)context; + + *val = readl(regs + reg); + + return 0; +} + +static bool regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ASPEED_MCR_PROT: + case ASPEED_MCR_INTR_CTRL: + case ASPEED_MCR_ADDR_UNREC: + case ASPEED_MCR_ADDR_REC: + return true; + default: + return false; + } +} + + +static const struct regmap_config aspeed_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = ASPEED_MCR_LAST, + .reg_write = regmap_reg_write, + .reg_read = regmap_reg_read, + .volatile_reg = regmap_is_volatile, + .fast_io = true, +}; + + +static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) +{ + struct csrow_info *csrow = mci->csrows[0]; + u32 page, offset, syndrome; + + if (!rec_cnt) + return; + + /* report first few errors (if there are) */ + /* note: no addresses are recorded */ + if (rec_cnt > 1) { + /* page, offset and syndrome are not available */ + page = 0; + offset = 0; + syndrome = 0; + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, rec_cnt-1, + page, offset, syndrome, 0, 0, -1, + "address(es) not available", ""); + } + + /* report last error */ + /* note: rec_addr is the last recoverable error addr */ + page = rec_addr >> PAGE_SHIFT; + offset = rec_addr & ~PAGE_MASK; + /* syndrome is not available */ + syndrome = 0; + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, + csrow->first_page + page, offset, syndrome, + 0, 0, -1, "", ""); +} + + +static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt, + u32 un_rec_addr) +{ + struct csrow_info *csrow = mci->csrows[0]; + u32 page, offset, syndrome; + + if (!un_rec_cnt) + return; + + /* report 1. error */ + /* note: un_rec_addr is the first unrecoverable error addr */ + page = un_rec_addr >> PAGE_SHIFT; + offset = un_rec_addr & ~PAGE_MASK; + /* syndrome is not available */ + syndrome = 0; + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, + csrow->first_page + page, offset, syndrome, + 0, 0, -1, "", ""); + + /* report further errors (if there are) */ + /* note: no addresses are recorded */ + if (un_rec_cnt > 1) { + /* page, offset and syndrome are not available */ + page = 0; + offset = 0; + syndrome = 0; + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, un_rec_cnt-1, + page, offset, syndrome, 0, 0, -1, + "address(es) not available", ""); + } +} + + +static irqreturn_t mcr_isr(int irq, void *arg) +{ + struct mem_ctl_info *mci = arg; + u32 rec_addr, un_rec_addr; + u32 reg50, reg5c, reg58; + u8 rec_cnt, un_rec_cnt; + + regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ®50); + dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", + reg50); + + /* collect data about recoverable and unrecoverable errors */ + rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16; + un_rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_UNREC) >> 12; + + dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interrupts\n", + rec_cnt, un_rec_cnt); + + regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_UNREC, ®58); + un_rec_addr = reg58; + + regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_REC, ®5c); + rec_addr = reg5c; + + /* clear interrupt flags and error counters: */ + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, + ASPEED_MCR_INTR_CTRL_CLEAR, + ASPEED_MCR_INTR_CTRL_CLEAR); + + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, + ASPEED_MCR_INTR_CTRL_CLEAR, 0); + + /* process recoverable and unrecoverable errors */ + count_rec(mci, rec_cnt, rec_addr); + count_un_rec(mci, un_rec_cnt, un_rec_addr); + + if (!rec_cnt && !un_rec_cnt) + dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC counters\n"); + + regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ®50); + dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n", + reg50); + + return IRQ_HANDLED; +} + + +static int config_irq(void *ctx, struct platform_device *pdev) +{ + int irq; + int rc; + + /* register interrupt handler */ + irq = platform_get_irq(pdev, 0); + dev_dbg(&pdev->dev, "got irq %d\n", irq); + if (!irq) + return -ENODEV; + + rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH, + DRV_NAME, ctx); + if (rc) { + dev_err(&pdev->dev, "unable to request irq %d\n", irq); + return rc; + } + + /* enable interrupts */ + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, + ASPEED_MCR_INTR_CTRL_ENABLE, + ASPEED_MCR_INTR_CTRL_ENABLE); + + return 0; +} + + +static int init_csrows(struct mem_ctl_info *mci) +{ + struct csrow_info *csrow = mci->csrows[0]; + u32 nr_pages, dram_type; + struct dimm_info *dimm; + struct device_node *np; + struct resource r; + u32 reg04; + int rc; + + /* retrieve info about physical memory from device tree */ + np = of_find_node_by_path("/memory"); + if (!np) { + dev_err(mci->pdev, "dt: missing /memory node\n"); + return -ENODEV; + }; + + rc = of_address_to_resource(np, 0, &r); + + of_node_put(np); + + if (rc) { + dev_err(mci->pdev, "dt: failed requesting resource for /memory node\n"); + return rc; + }; + + dev_dbg(mci->pdev, "dt: /memory node resources: first page r.start=0x%x, resource_size=0x%x, PAGE_SHIFT macro=0x%x\n", + r.start, resource_size(&r), PAGE_SHIFT); + + csrow->first_page = r.start >> PAGE_SHIFT; + nr_pages = resource_size(&r) >> PAGE_SHIFT; + csrow->last_page = csrow->first_page + nr_pages - 1; + + regmap_read(aspeed_regmap, ASPEED_MCR_CONF, ®04); + dram_type = (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3; + + dimm = csrow->channels[0]->dimm; + dimm->mtype = dram_type; + dimm->edac_mode = EDAC_SECDED; + dimm->nr_pages = nr_pages / csrow->nr_channels; + + dev_dbg(mci->pdev, "initialized dimm with first_page=0x%lx and nr_pages=0x%x\n", + csrow->first_page, nr_pages); + + return 0; +} + + +static int aspeed_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + struct device_node *np; + struct resource *res; + void __iomem *regs; + u32 reg04; + int rc; + + /* setup regmap */ + np = dev->of_node; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + aspeed_regmap = devm_regmap_init(dev, NULL, (__force void *)regs, + &aspeed_regmap_config); + if (IS_ERR(aspeed_regmap)) + return PTR_ERR(aspeed_regmap); + + /* bail out if ECC mode is not configured */ + regmap_read(aspeed_regmap, ASPEED_MCR_CONF, ®04); + if (!(reg04 & ASPEED_MCR_CONF_ECC)) { + dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n"); + return -EPERM; + } + + edac_op_state = EDAC_OPSTATE_INT; + + /* allocate & init EDAC MC data structure */ + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = 1; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = 1; + layers[1].is_virt_csrow = false; + + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); + if (!mci) + return -ENOMEM; + + mci->pdev = &pdev->dev; + mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4; + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->scrub_cap = SCRUB_FLAG_HW_SRC; + mci->scrub_mode = SCRUB_HW_SRC; + mci->mod_name = DRV_NAME; + mci->ctl_name = "MIC"; + mci->dev_name = dev_name(&pdev->dev); + + rc = init_csrows(mci); + if (rc) { + dev_err(&pdev->dev, "failed to init csrows\n"); + goto probe_exit02; + } + + platform_set_drvdata(pdev, mci); + + /* register with edac core */ + rc = edac_mc_add_mc(mci); + if (rc) { + dev_err(&pdev->dev, "failed to register with EDAC core\n"); + goto probe_exit02; + } + + /* register interrupt handler and enable interrupts */ + rc = config_irq(mci, pdev); + if (rc) { + dev_err(&pdev->dev, "failed setting up irq\n"); + goto probe_exit01; + } + + return 0; + +probe_exit01: + edac_mc_del_mc(&pdev->dev); +probe_exit02: + edac_mc_free(mci); + return rc; +} + + +static int aspeed_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci; + + /* disable interrupts */ + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, + ASPEED_MCR_INTR_CTRL_ENABLE, 0); + + /* free resources */ + mci = edac_mc_del_mc(&pdev->dev); + if (mci) + edac_mc_free(mci); + + return 0; +} + + +static const struct of_device_id aspeed_of_match[] = { + { .compatible = "aspeed,ast2500-sdram-edac" }, + {}, +}; + + +static struct platform_driver aspeed_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = aspeed_of_match + }, + .probe = aspeed_probe, + .remove = aspeed_remove +}; + + +static int __init aspeed_init(void) +{ + return platform_driver_register(&aspeed_driver); +} + + +static void __exit aspeed_exit(void) +{ + platform_driver_unregister(&aspeed_driver); +} + + +module_init(aspeed_init); +module_exit(aspeed_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stefan Schaeckeler "); +MODULE_DESCRIPTION("Aspeed AST2500 EDAC driver"); +MODULE_VERSION("1.0"); From 5296bab33b6037897a1e32958ddbc2b3dab405be Mon Sep 17 00:00:00 2001 From: Stefan M Schaeckeler Date: Thu, 17 Jan 2019 08:38:17 -0800 Subject: [PATCH 0421/4277] dt-bindings, EDAC: Add Aspeed AST2500 Add support for EDAC on the Aspeed AST2500 SoC. Signed-off-by: Stefan M Schaeckeler Signed-off-by: Borislav Petkov Reviewed-by: Andrew Jeffery Cc: Joel Stanley Cc: Mark Rutland Cc: Mauro Carvalho Chehab Cc: Rob Herring Cc: devicetree@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-aspeed@lists.ozlabs.org Cc: linux-edac Link: https://lkml.kernel.org/r/1547743097-5236-3-git-send-email-schaecsn@gmx.net --- .../bindings/edac/aspeed-sdram-edac.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt diff --git a/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt b/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt new file mode 100644 index 000000000000..6a0f3d90d682 --- /dev/null +++ b/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt @@ -0,0 +1,25 @@ +Aspeed AST2500 SoC EDAC node + +The Aspeed AST2500 SoC supports DDR3 and DDR4 memory with and without ECC (error +correction check). + +The memory controller supports SECDED (single bit error correction, double bit +error detection) and single bit error auto scrubbing by reserving 8 bits for +every 64 bit word (effectively reducing available memory to 8/9). + +Note, the bootloader must configure ECC mode in the memory controller. + + +Required properties: +- compatible: should be "aspeed,ast2500-sdram-edac" +- reg: sdram controller register set should be <0x1e6e0000 0x174> +- interrupts: should be AVIC interrupt #0 + + +Example: + + edac: sdram@1e6e0000 { + compatible = "aspeed,ast2500-sdram-edac"; + reg = <0x1e6e0000 0x174>; + interrupts = <0>; + }; From de77503a59403e7045c18c6bb0a10c245a99b648 Mon Sep 17 00:00:00 2001 From: Lijun Ou Date: Sat, 12 Jan 2019 18:36:29 +0800 Subject: [PATCH 0422/4277] RDMA/hns: RDMA/hns: Assign rq head pointer when enable rq record db When flush cqe, it needs to get the pointer of rq and sq from db address space of user and update it into qp context by modified qp. if rq does not exist, it will not get the value from db address space of user. Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_qp.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index accf9ce1507d..507de9d50189 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -675,6 +675,10 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, dev_err(dev, "rq record doorbell map failed!\n"); goto err_sq_dbmap; } + + /* indicate kernel supports rq record db */ + resp.cap_flags |= HNS_ROCE_SUPPORT_RQ_RECORD_DB; + hr_qp->rdb_en = 1; } } else { if (init_attr->create_flags & @@ -783,16 +787,11 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, else hr_qp->doorbell_qpn = cpu_to_le64(hr_qp->qpn); - if (udata && (udata->outlen >= sizeof(resp)) && - (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB)) { - - /* indicate kernel supports rq record db */ - resp.cap_flags |= HNS_ROCE_SUPPORT_RQ_RECORD_DB; - ret = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (udata) { + ret = ib_copy_to_udata(udata, &resp, + min(udata->outlen, sizeof(resp))); if (ret) goto err_qp; - - hr_qp->rdb_en = 1; } hr_qp->event = hns_roce_ib_qp_event; @@ -969,7 +968,9 @@ int hns_roce_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, (attr_mask & IB_QP_STATE) && new_state == IB_QPS_ERR) { if (hr_qp->sdb_en == 1) { hr_qp->sq.head = *(int *)(hr_qp->sdb.virt_addr); - hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr); + + if (hr_qp->rdb_en == 1) + hr_qp->rq.head = *(int *)(hr_qp->rdb.virt_addr); } else { dev_warn(dev, "flush cqe is not supported in userspace!\n"); goto out; From 344684e6d02ff21ff2fce8cf1b2de3fd3cafcac7 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Sat, 12 Jan 2019 02:42:42 +0000 Subject: [PATCH 0423/4277] RDMA/device: Use __ib_device_get_by_name() in ib_device_rename() No reason to open code this loop. Signed-off-by: Jason Gunthorpe Reviewed-by: Steve Wise --- drivers/infiniband/core/device.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index f8180cf1a004..4a9aa6d10c5e 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -183,18 +183,15 @@ static struct ib_device *__ib_device_get_by_name(const char *name) int ib_device_rename(struct ib_device *ibdev, const char *name) { - struct ib_device *device; int ret = 0; if (!strcmp(name, dev_name(&ibdev->dev))) return ret; mutex_lock(&device_mutex); - list_for_each_entry(device, &device_list, core_list) { - if (!strcmp(name, dev_name(&device->dev))) { - ret = -EEXIST; - goto out; - } + if (__ib_device_get_by_name(name)) { + ret = -EEXIST; + goto out; } ret = device_rename(&ibdev->dev, name); From 8ea175f0056dd72e6a86f6852d0db542efbb08f6 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 17 Jan 2019 03:21:47 +0000 Subject: [PATCH 0424/4277] RDMA/qedr: remove set but not used variable 'ib_ctx' Fixes gcc '-Wunused-but-set-variable' warning: drivers/infiniband/hw/qedr/verbs.c: In function 'qedr_create_srq': drivers/infiniband/hw/qedr/verbs.c:1436:22: warning: variable 'ib_ctx' set but not used [-Wunused-but-set-variable] drivers/infiniband/hw/qedr/verbs.c: In function 'qedr_create_user_qp': drivers/infiniband/hw/qedr/verbs.c:1701:22: warning: variable 'ib_ctx' set but not used [-Wunused-but-set-variable] Fixes: b0ea0fa5435f ("IB/{core,hw}: Have ib_umem_get extract the ib_ucontext from ib_udata") Signed-off-by: YueHaibing Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qedr/verbs.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index b27ff9408507..8e6f9a0d1c70 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -1433,7 +1433,6 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd, struct qedr_pd *pd = get_qedr_pd(ibpd); struct qedr_create_srq_ureq ureq = {}; u64 pbl_base_addr, phy_prod_pair_addr; - struct ib_ucontext *ib_ctx = NULL; struct qedr_srq_hwq_info *hw_srq; u32 page_cnt, page_size; struct qedr_srq *srq; @@ -1459,8 +1458,6 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd, hw_srq->max_sges = init_attr->attr.max_sge; if (udata && ibpd->uobject && ibpd->uobject->context) { - ib_ctx = ibpd->uobject->context; - if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) { DP_ERR(dev, "create srq: problem copying data from user space\n"); @@ -1698,13 +1695,10 @@ static int qedr_create_user_qp(struct qedr_dev *dev, struct qed_rdma_create_qp_in_params in_params; struct qed_rdma_create_qp_out_params out_params; struct qedr_pd *pd = get_qedr_pd(ibpd); - struct ib_ucontext *ib_ctx = NULL; struct qedr_create_qp_ureq ureq; int alloc_and_init = rdma_protocol_roce(&dev->ibdev, 1); int rc = -EINVAL; - ib_ctx = ibpd->uobject->context; - memset(&ureq, 0, sizeof(ureq)); rc = ib_copy_from_udata(&ureq, udata, sizeof(ureq)); if (rc) { From 7527a7b157d1191b23562ed70154ae93bd65f845 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Thu, 17 Jan 2019 20:14:15 +0200 Subject: [PATCH 0425/4277] IB/core: Simplify rdma cgroup registration RDMA cgroup registration routine always returns success, so simplify function to be void and run clang formatter over whole CONFIG_CGROUP_RDMA art of core_priv.h. This reduces unwinding error path for regular registration and future net namespace change functionality for rdma device. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Acked-by: Tejun Heo Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cgroup.c | 5 ++--- drivers/infiniband/core/core_priv.h | 17 +++++++++++------ drivers/infiniband/core/device.c | 8 +------- include/linux/cgroup_rdma.h | 2 +- kernel/cgroup/rdma.c | 5 +---- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/core/cgroup.c b/drivers/infiniband/core/cgroup.c index 126ac5f99db7..388fd04e5f63 100644 --- a/drivers/infiniband/core/cgroup.c +++ b/drivers/infiniband/core/cgroup.c @@ -21,12 +21,11 @@ * Register with the rdma cgroup. Should be called before * exposing rdma device to user space applications to avoid * resource accounting leak. - * Returns 0 on success or otherwise failure code. */ -int ib_device_register_rdmacg(struct ib_device *device) +void ib_device_register_rdmacg(struct ib_device *device) { device->cg_device.name = device->name; - return rdmacg_register_device(&device->cg_device); + rdmacg_register_device(&device->cg_device); } /** diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index aca75c74e451..42a49982f66e 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -115,7 +115,7 @@ void ib_cache_cleanup_one(struct ib_device *device); void ib_cache_release_one(struct ib_device *device); #ifdef CONFIG_CGROUP_RDMA -int ib_device_register_rdmacg(struct ib_device *device); +void ib_device_register_rdmacg(struct ib_device *device); void ib_device_unregister_rdmacg(struct ib_device *device); int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj, @@ -126,21 +126,26 @@ void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj, struct ib_device *device, enum rdmacg_resource_type resource_index); #else -static inline int ib_device_register_rdmacg(struct ib_device *device) -{ return 0; } +static inline void ib_device_register_rdmacg(struct ib_device *device) +{ +} static inline void ib_device_unregister_rdmacg(struct ib_device *device) -{ } +{ +} static inline int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj, struct ib_device *device, enum rdmacg_resource_type resource_index) -{ return 0; } +{ + return 0; +} static inline void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj, struct ib_device *device, enum rdmacg_resource_type resource_index) -{ } +{ +} #endif static inline bool rdma_is_upper_dev_rcu(struct net_device *dev, diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 4a9aa6d10c5e..200431c540f2 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -599,12 +599,7 @@ int ib_register_device(struct ib_device *device, const char *name) device->index = __dev_new_index(); - ret = ib_device_register_rdmacg(device); - if (ret) { - dev_warn(&device->dev, - "Couldn't register device with rdma cgroup\n"); - goto dev_cleanup; - } + ib_device_register_rdmacg(device); ret = ib_device_register_sysfs(device); if (ret) { @@ -627,7 +622,6 @@ int ib_register_device(struct ib_device *device, const char *name) cg_cleanup: ib_device_unregister_rdmacg(device); -dev_cleanup: cleanup_device(device); out: mutex_unlock(&device_mutex); diff --git a/include/linux/cgroup_rdma.h b/include/linux/cgroup_rdma.h index e94290b29e99..ef1bae2983f3 100644 --- a/include/linux/cgroup_rdma.h +++ b/include/linux/cgroup_rdma.h @@ -39,7 +39,7 @@ struct rdmacg_device { * APIs for RDMA/IB stack to publish when a device wants to * participate in resource accounting */ -int rdmacg_register_device(struct rdmacg_device *device); +void rdmacg_register_device(struct rdmacg_device *device); void rdmacg_unregister_device(struct rdmacg_device *device); /* APIs for RDMA/IB stack to charge/uncharge pool specific resources */ diff --git a/kernel/cgroup/rdma.c b/kernel/cgroup/rdma.c index d3bbb757ee49..1d75ae7f1cb7 100644 --- a/kernel/cgroup/rdma.c +++ b/kernel/cgroup/rdma.c @@ -313,10 +313,8 @@ EXPORT_SYMBOL(rdmacg_try_charge); * If IB stack wish a device to participate in rdma cgroup resource * tracking, it must invoke this API to register with rdma cgroup before * any user space application can start using the RDMA resources. - * Returns 0 on success or EINVAL when table length given is beyond - * supported size. */ -int rdmacg_register_device(struct rdmacg_device *device) +void rdmacg_register_device(struct rdmacg_device *device) { INIT_LIST_HEAD(&device->dev_node); INIT_LIST_HEAD(&device->rpools); @@ -324,7 +322,6 @@ int rdmacg_register_device(struct rdmacg_device *device) mutex_lock(&rdmacg_mutex); list_add_tail(&device->dev_node, &rdmacg_devices); mutex_unlock(&rdmacg_mutex); - return 0; } EXPORT_SYMBOL(rdmacg_register_device); From 14e517e4b444a01d871893b1ea817790ee13dc0b Mon Sep 17 00:00:00 2001 From: Mike Marciniszyn Date: Thu, 17 Jan 2019 12:41:43 -0800 Subject: [PATCH 0426/4277] IB/rdmavt: Add wc_flags and wc_immdata to cq entry trace These fields were missing from the trace. Add them. Fixes: c6ad9482fcb8 ("IB/rdmavt: Add tracing for cq entry and poll") Reviewed-by: Michael J. Ruhl Signed-off-by: Mike Marciniszyn Signed-off-by: Dennis Dalessandro Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rdmavt/trace_cq.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/sw/rdmavt/trace_cq.h b/drivers/infiniband/sw/rdmavt/trace_cq.h index df8e1adbef9d..e3c416c6f900 100644 --- a/drivers/infiniband/sw/rdmavt/trace_cq.h +++ b/drivers/infiniband/sw/rdmavt/trace_cq.h @@ -105,7 +105,7 @@ DEFINE_EVENT(rvt_cq_template, rvt_create_cq, TP_ARGS(cq, attr)); #define CQ_PRN \ -"[%s] idx %u wr_id %llx status %u opcode %u,%s length %u qpn %x" +"[%s] idx %u wr_id %llx status %u opcode %u,%s length %u qpn %x flags %x imm %x" DECLARE_EVENT_CLASS( rvt_cq_entry_template, @@ -119,6 +119,8 @@ DECLARE_EVENT_CLASS( __field(u32, qpn) __field(u32, length) __field(u32, idx) + __field(u32, flags) + __field(u32, imm) ), TP_fast_assign( RDI_DEV_ASSIGN(cq->rdi) @@ -128,6 +130,8 @@ DECLARE_EVENT_CLASS( __entry->length = wc->byte_len; __entry->qpn = wc->qp->qp_num; __entry->idx = idx; + __entry->flags = wc->wc_flags; + __entry->imm = be32_to_cpu(wc->ex.imm_data); ), TP_printk( CQ_PRN, @@ -137,7 +141,9 @@ DECLARE_EVENT_CLASS( __entry->status, __entry->opcode, show_wc_opcode(__entry->opcode), __entry->length, - __entry->qpn + __entry->qpn, + __entry->flags, + __entry->imm ) ); From 790b57f686e29b93616b3d13b38043f5ec29fa0a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 18 Jan 2019 11:09:00 +0800 Subject: [PATCH 0427/4277] IB/hw: Remove unneeded semicolons Remove unneeded semicolons. Signed-off-by: YueHaibing Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- drivers/infiniband/hw/qedr/qedr_iw_cm.c | 2 +- drivers/infiniband/hw/qedr/verbs.c | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 9ed7e71dc437..d778457ec1e5 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4578,7 +4578,7 @@ static int hns_roce_v2_aeq_int(struct hns_roce_dev *hr_dev, dev_err(dev, "Unhandled event %d on EQ %d at idx %u.\n", event_type, eq->eqn, eq->cons_index); break; - }; + } eq->event_type = event_type; eq->sub_type = sub_type; diff --git a/drivers/infiniband/hw/qedr/qedr_iw_cm.c b/drivers/infiniband/hw/qedr/qedr_iw_cm.c index 93b16237b767..0555e5a8c9ed 100644 --- a/drivers/infiniband/hw/qedr/qedr_iw_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_iw_cm.c @@ -349,7 +349,7 @@ qedr_iw_event_handler(void *context, struct qed_iwarp_cm_event_params *params) default: DP_NOTICE(dev, "Unknown event received %d\n", params->event); break; - }; + } return 0; } diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 8e6f9a0d1c70..265a2646ad12 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -2110,7 +2110,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev, default: status = -EINVAL; break; - }; + } break; case QED_ROCE_QP_STATE_INIT: switch (new_state) { @@ -2131,7 +2131,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev, /* Invalid state change. */ status = -EINVAL; break; - }; + } break; case QED_ROCE_QP_STATE_RTR: /* RTR->XXX */ @@ -2144,7 +2144,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev, /* Invalid state change. */ status = -EINVAL; break; - }; + } break; case QED_ROCE_QP_STATE_RTS: /* RTS->XXX */ @@ -2157,7 +2157,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev, /* Invalid state change. */ status = -EINVAL; break; - }; + } break; case QED_ROCE_QP_STATE_SQD: /* SQD->XXX */ @@ -2169,7 +2169,7 @@ static int qedr_update_qp_state(struct qedr_dev *dev, /* Invalid state change. */ status = -EINVAL; break; - }; + } break; case QED_ROCE_QP_STATE_ERR: /* ERR->XXX */ @@ -2187,12 +2187,12 @@ static int qedr_update_qp_state(struct qedr_dev *dev, default: status = -EINVAL; break; - }; + } break; default: status = -EINVAL; break; - }; + } return status; } From 57b26497fabe1b9379b59fbc7e35e608e114df16 Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Thu, 17 Jan 2019 15:45:45 +0000 Subject: [PATCH 0428/4277] IB/iser: Pass the correct number of entries for dma mapped SGL ib_dma_map_sg() augments the SGL into a 'dma mapped SGL'. This process may change the number of entries and the lengths of each entry. Code that touches dma_address is iterating over the 'dma mapped SGL' and must use dma_nents which returned from ib_dma_map_sg(). ib_sg_to_pages() and ib_map_mr_sg() are using dma_address so they must use dma_nents. Fixes: 39405885005a ("IB/iser: Port to new fast registration API") Fixes: bfe066e256d5 ("IB/iser: Reuse ib_sg_to_pages") Signed-off-by: Israel Rukshin Reviewed-by: Max Gurtovoy Acked-by: Sagi Grimberg Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/iser/iser_memory.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index e9b7efc302d0..394d1b9c2ff7 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -240,8 +240,8 @@ int iser_fast_reg_fmr(struct iscsi_iser_task *iser_task, page_vec->npages = 0; page_vec->fake_mr.page_size = SIZE_4K; plen = ib_sg_to_pages(&page_vec->fake_mr, mem->sg, - mem->size, NULL, iser_set_page); - if (unlikely(plen < mem->size)) { + mem->dma_nents, NULL, iser_set_page); + if (unlikely(plen < mem->dma_nents)) { iser_err("page vec too short to hold this SG\n"); iser_data_buf_dump(mem, device->ib_device); iser_dump_page_vec(page_vec); @@ -448,10 +448,10 @@ static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task, ib_update_fast_reg_key(mr, ib_inc_rkey(mr->rkey)); - n = ib_map_mr_sg(mr, mem->sg, mem->size, NULL, SIZE_4K); - if (unlikely(n != mem->size)) { + n = ib_map_mr_sg(mr, mem->sg, mem->dma_nents, NULL, SIZE_4K); + if (unlikely(n != mem->dma_nents)) { iser_err("failed to map sg (%d/%d)\n", - n, mem->size); + n, mem->dma_nents); return n < 0 ? n : -EINVAL; } From b36506787cf19a3eb1116d5dcdef2e2ad5b2933a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 18 Jan 2019 14:02:27 +0000 Subject: [PATCH 0429/4277] arm64: perf: remove misleading comment The comment for the armv8pmu_set_event_filter function suggests that it only works for PMUv2 PMUs - this is incorrect. Let's remove the incorrect comment. Acked-by: Mark Rutland Signed-off-by: Andrew Murray Signed-off-by: Will Deacon --- arch/arm64/kernel/perf_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 1620a371b1f5..4addb38bc250 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -810,7 +810,7 @@ static void armv8pmu_clear_event_idx(struct pmu_hw_events *cpuc, } /* - * Add an event filter to a given event. This will only work for PMUv2 PMUs. + * Add an event filter to a given event. */ static int armv8pmu_set_event_filter(struct hw_perf_event *event, struct perf_event_attr *attr) From 875aac8a46424e5b73a9ff7f40b83311b609e407 Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Mon, 7 Jan 2019 09:32:14 +0800 Subject: [PATCH 0430/4277] dmaengine: qcom_hidma: initialize tx flags in hidma_prep_dma_* In async_tx_test_ack(), it uses flags in struct dma_async_tx_descriptor to check the ACK status. As hidma reuses the descriptor in a free list when hidma_prep_dma_*(memcpy/memset) is called, the flag will keep ACKed if the descriptor has been used before. This will cause a BUG_ON in async_tx_quiesce(). kernel BUG at crypto/async_tx/async_tx.c:282! Internal error: Oops - BUG: 0 1 SMP ... task: ffff8017dd3ec000 task.stack: ffff8017dd3e8000 PC is at async_tx_quiesce+0x54/0x78 [async_tx] LR is at async_trigger_callback+0x98/0x110 [async_tx] This patch initializes flags in dma_async_tx_descriptor by the flags passed from the caller when hidma_prep_dma_*(memcpy/memset) is called. Cc: Joey Zheng Reviewed-by: Sinan Kaya Signed-off-by: Shunyong Yang Signed-off-by: Vinod Koul --- drivers/dma/qcom/hidma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 43d4b00b8138..9d639ed1955a 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -415,6 +415,7 @@ hidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src, if (!mdesc) return NULL; + mdesc->desc.flags = flags; hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, src, dest, len, flags, HIDMA_TRE_MEMCPY); @@ -447,6 +448,7 @@ hidma_prep_dma_memset(struct dma_chan *dmach, dma_addr_t dest, int value, if (!mdesc) return NULL; + mdesc->desc.flags = flags; hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, value, dest, len, flags, HIDMA_TRE_MEMSET); From 546c0547555efca8ba8c120716c325435e29df1b Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Mon, 7 Jan 2019 09:34:02 +0800 Subject: [PATCH 0431/4277] dmaengine: qcom_hidma: assign channel cookie correctly When dma_cookie_complete() is called in hidma_process_completed(), dma_cookie_status() will return DMA_COMPLETE in hidma_tx_status(). Then, hidma_txn_is_success() will be called to use channel cookie mchan->last_success to do additional DMA status check. Current code assigns mchan->last_success after dma_cookie_complete(). This causes a race condition of dma_cookie_status() returns DMA_COMPLETE before mchan->last_success is assigned correctly. The race will cause hidma_tx_status() return DMA_ERROR but the transaction is actually a success. Moreover, in async_tx case, it will cause a timeout panic in async_tx_quiesce(). Kernel panic - not syncing: async_tx_quiesce: DMA error waiting for transaction ... Call trace: [] dump_backtrace+0x0/0x1f4 [] show_stack+0x24/0x2c [] dump_stack+0x84/0xa8 [] panic+0x12c/0x29c [] async_tx_quiesce+0xa4/0xc8 [async_tx] [] async_trigger_callback+0x70/0x1c0 [async_tx] [] raid_run_ops+0x86c/0x1540 [raid456] [] handle_stripe+0x5e8/0x1c7c [raid456] [] handle_active_stripes.isra.45+0x2d4/0x550 [raid456] [] raid5d+0x38c/0x5d0 [raid456] [] md_thread+0x108/0x168 [] kthread+0x10c/0x138 [] ret_from_fork+0x10/0x18 Cc: Joey Zheng Reviewed-by: Sinan Kaya Signed-off-by: Shunyong Yang Signed-off-by: Vinod Koul --- drivers/dma/qcom/hidma.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 9d639ed1955a..411f91fde734 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -138,24 +138,25 @@ static void hidma_process_completed(struct hidma_chan *mchan) desc = &mdesc->desc; last_cookie = desc->cookie; + llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch); + spin_lock_irqsave(&mchan->lock, irqflags); + if (llstat == DMA_COMPLETE) { + mchan->last_success = last_cookie; + result.result = DMA_TRANS_NOERROR; + } else { + result.result = DMA_TRANS_ABORTED; + } + dma_cookie_complete(desc); spin_unlock_irqrestore(&mchan->lock, irqflags); - llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch); dmaengine_desc_get_callback(desc, &cb); dma_run_dependencies(desc); spin_lock_irqsave(&mchan->lock, irqflags); list_move(&mdesc->node, &mchan->free); - - if (llstat == DMA_COMPLETE) { - mchan->last_success = last_cookie; - result.result = DMA_TRANS_NOERROR; - } else - result.result = DMA_TRANS_ABORTED; - spin_unlock_irqrestore(&mchan->lock, irqflags); dmaengine_desc_callback_invoke(&cb, &result); From 863326a6eec9c737fdd40bf9bebe135cba27ab97 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 7 Jan 2019 11:06:31 -0600 Subject: [PATCH 0432/4277] dmaengine: tegra210-adma: Use struct_size() in devm_kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = devm_kzalloc(dev, sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = devm_kzalloc(dev, struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/tegra210-adma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index b26256f23d67..5ec0dd97b397 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -678,8 +678,9 @@ static int tegra_adma_probe(struct platform_device *pdev) return -ENODEV; } - tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels * - sizeof(struct tegra_adma_chan), GFP_KERNEL); + tdma = devm_kzalloc(&pdev->dev, + struct_size(tdma, channels, cdata->nr_channels), + GFP_KERNEL); if (!tdma) return -ENOMEM; From 3c215fd868dd9fe077af63dbddca585e5116f598 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 8 Jan 2019 09:58:52 -0600 Subject: [PATCH 0433/4277] dmaengine: timb_dma: Use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/timb_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index fc0f9c8766a8..afbb1c95b721 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -643,8 +643,8 @@ static int td_probe(struct platform_device *pdev) DRIVER_NAME)) return -EBUSY; - td = kzalloc(sizeof(struct timb_dma) + - sizeof(struct timb_dma_chan) * pdata->nr_channels, GFP_KERNEL); + td = kzalloc(struct_size(td, channels, pdata->nr_channels), + GFP_KERNEL); if (!td) { err = -ENOMEM; goto err_release_region; From 48b02a85fe2e6a0927acaa754ba3bef4a20a5e37 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 9 Jan 2019 08:44:37 -0600 Subject: [PATCH 0434/4277] dmaengine: axi-dmac: Use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Vinod Koul --- drivers/dma/dma-axi-dmac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 15b2453d2647..ffc0adc2f6ce 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -367,8 +367,7 @@ static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs) struct axi_dmac_desc *desc; unsigned int i; - desc = kzalloc(sizeof(struct axi_dmac_desc) + - sizeof(struct axi_dmac_sg) * num_sgs, GFP_NOWAIT); + desc = kzalloc(struct_size(desc, sg, num_sgs), GFP_NOWAIT); if (!desc) return NULL; From c2be36ac2141b97f9a35ab560381317566bee357 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 9 Jan 2019 12:10:37 +0000 Subject: [PATCH 0435/4277] dmaengine: xilinx_dma: remove set but not used variable 'tail_segment' Fixes gcc '-Wunused-but-set-variable' warning: drivers/dma/xilinx/xilinx_dma.c: In function 'xilinx_vdma_start_transfer': drivers/dma/xilinx/xilinx_dma.c:1104:33: warning: variable 'tail_segment' set but not used [-Wunused-but-set-variable] It not used since commit b8349172b400 ("dmaengine: xilinx_dma: Drop SG support for VDMA IP") Signed-off-by: YueHaibing Reviewed-by: Radhey Shyam Pandey Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index d9431afa031b..c8acd34f1f70 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -1101,7 +1101,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) struct xilinx_vdma_config *config = &chan->config; struct xilinx_dma_tx_descriptor *desc, *tail_desc; u32 reg, j; - struct xilinx_vdma_tx_segment *tail_segment; struct xilinx_vdma_tx_segment *segment, *last = NULL; int i = 0; @@ -1120,9 +1119,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan) tail_desc = list_last_entry(&chan->pending_list, struct xilinx_dma_tx_descriptor, node); - tail_segment = list_last_entry(&tail_desc->segments, - struct xilinx_vdma_tx_segment, node); - /* Configure the hardware using info in the config structure */ if (chan->has_vflip) { reg = dma_read(chan, XILINX_VDMA_REG_ENABLE_VERTICAL_FLIP); From 9227ab5643cb8350449502dd9e3168a873ab0e3b Mon Sep 17 00:00:00 2001 From: Anders Roxell Date: Thu, 10 Jan 2019 12:15:35 +0100 Subject: [PATCH 0436/4277] dmaengine: imx-dma: fix warning comparison of distinct pointer types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The warning got introduced by commit 930507c18304 ("arm64: add basic Kconfig symbols for i.MX8"). Since it got enabled for arm64. The warning haven't been seen before since size_t was 'unsigned int' when built on arm32. ../drivers/dma/imx-dma.c: In function ‘imxdma_sg_next’: ../include/linux/kernel.h:846:29: warning: comparison of distinct pointer types lacks a cast (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1))) ^~ ../include/linux/kernel.h:860:4: note: in expansion of macro ‘__typecheck’ (__typecheck(x, y) && __no_side_effects(x, y)) ^~~~~~~~~~~ ../include/linux/kernel.h:870:24: note: in expansion of macro ‘__safe_cmp’ __builtin_choose_expr(__safe_cmp(x, y), \ ^~~~~~~~~~ ../include/linux/kernel.h:879:19: note: in expansion of macro ‘__careful_cmp’ #define min(x, y) __careful_cmp(x, y, <) ^~~~~~~~~~~~~ ../drivers/dma/imx-dma.c:288:8: note: in expansion of macro ‘min’ now = min(d->len, sg_dma_len(sg)); ^~~ Rework so that we use min_t and pass in the size_t that returns the minimum of two values, using the specified type. Signed-off-by: Anders Roxell Acked-by: Olof Johansson Reviewed-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index c2fff3f6c9ca..fa3ef43fe314 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -285,7 +285,7 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) struct scatterlist *sg = d->sg; unsigned long now; - now = min(d->len, sg_dma_len(sg)); + now = min_t(size_t, d->len, sg_dma_len(sg)); if (d->len != IMX_DMA_LENGTH_LOOP) d->len -= now; From da5035f3773632a7b11a2dc8ca15291b8e2817df Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 20 Jan 2019 11:42:44 +0530 Subject: [PATCH 0437/4277] dmaengine: imx-dma: change variable 'now' type to size_t now is used to keep size and it is better to change the variable type to size_t Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index fa3ef43fe314..aba1380c3321 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -283,7 +283,7 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_engine *imxdma = imxdmac->imxdma; struct scatterlist *sg = d->sg; - unsigned long now; + size_t now; now = min_t(size_t, d->len, sg_dma_len(sg)); if (d->len != IMX_DMA_LENGTH_LOOP) From 452fd6dc86209c23501c82183c1fbbbfa42c0a7a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 20 Jan 2019 11:42:44 +0530 Subject: [PATCH 0438/4277] dmaengine: imx-dma: change return of 'imxdma_sg_next' to void The return value of function 'imxdma_sg_next' is not checked anywhere, so make it void return type. Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index aba1380c3321..5a923490dc8b 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -278,7 +278,7 @@ static int imxdma_hw_chain(struct imxdma_channel *imxdmac) /* * imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation */ -static inline int imxdma_sg_next(struct imxdma_desc *d) +static inline void imxdma_sg_next(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_engine *imxdma = imxdmac->imxdma; @@ -303,8 +303,6 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) imx_dmav1_readl(imxdma, DMA_DAR(imxdmac->channel)), imx_dmav1_readl(imxdma, DMA_SAR(imxdmac->channel)), imx_dmav1_readl(imxdma, DMA_CNTR(imxdmac->channel))); - - return now; } static void imxdma_enable_hw(struct imxdma_desc *d) From ceaf52265148d3a5ca24237fd1c709caa5f46184 Mon Sep 17 00:00:00 2001 From: Andy Duan Date: Fri, 11 Jan 2019 14:29:49 +0000 Subject: [PATCH 0439/4277] dmaengine: imx-sdma: pass ->dev to dma_alloc_coherent() API Pass ->dev to dma_alloc_coherent() API. We need this because dma_alloc_coherent() makes use of dev parameter and receiving NULL will result in a crash. Signed-off-by: Andy Duan Signed-off-by: Daniel Baluta Reviewed-by: Robin Gong Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 015c17a3b826..af14a8d6efa8 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -678,7 +678,7 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, int ret; unsigned long flags; - buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); + buf_virt = dma_alloc_coherent(sdma->dev, size, &buf_phys, GFP_KERNEL); if (!buf_virt) { return -ENOMEM; } @@ -697,7 +697,7 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, spin_unlock_irqrestore(&sdma->channel_0_lock, flags); - dma_free_coherent(NULL, size, buf_virt, buf_phys); + dma_free_coherent(sdma->dev, size, buf_virt, buf_phys); return ret; } @@ -1189,7 +1189,7 @@ static int sdma_request_channel0(struct sdma_engine *sdma) { int ret = -EBUSY; - sdma->bd0 = dma_zalloc_coherent(NULL, PAGE_SIZE, &sdma->bd0_phys, + sdma->bd0 = dma_zalloc_coherent(sdma->dev, PAGE_SIZE, &sdma->bd0_phys, GFP_NOWAIT); if (!sdma->bd0) { ret = -ENOMEM; @@ -1212,8 +1212,8 @@ static int sdma_alloc_bd(struct sdma_desc *desc) u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor); int ret = 0; - desc->bd = dma_zalloc_coherent(NULL, bd_size, &desc->bd_phys, - GFP_NOWAIT); + desc->bd = dma_zalloc_coherent(desc->sdmac->sdma->dev, bd_size, + &desc->bd_phys, GFP_NOWAIT); if (!desc->bd) { ret = -ENOMEM; goto out; @@ -1226,7 +1226,8 @@ static void sdma_free_bd(struct sdma_desc *desc) { u32 bd_size = desc->num_bd * sizeof(struct sdma_buffer_descriptor); - dma_free_coherent(NULL, bd_size, desc->bd, desc->bd_phys); + dma_free_coherent(desc->sdmac->sdma->dev, bd_size, desc->bd, + desc->bd_phys); } static void sdma_desc_free(struct virt_dma_desc *vd) @@ -1849,7 +1850,7 @@ static int sdma_init(struct sdma_engine *sdma) /* Be sure SDMA has not started yet */ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); - sdma->channel_control = dma_alloc_coherent(NULL, + sdma->channel_control = dma_alloc_coherent(sdma->dev, MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + sizeof(struct sdma_context_data), &ccb_phys, GFP_KERNEL); From d9140a0da4a230a03426d175145989667758aa6a Mon Sep 17 00:00:00 2001 From: Phuong Nguyen Date: Thu, 17 Jan 2019 17:44:17 +0900 Subject: [PATCH 0440/4277] dmaengine: usb-dmac: Make DMAC system sleep callbacks explicit This commit fixes the issue that USB-DMAC hangs silently after system resumes on R-Car Gen3 hence renesas_usbhs will not work correctly when using USB-DMAC for bulk transfer e.g. ethernet or serial gadgets. The issue can be reproduced by these steps: 1. modprobe g_serial 2. Suspend and resume system. 3. connect a usb cable to host side 4. Transfer data from Host to Target 5. cat /dev/ttyGS0 (Target side) 6. echo "test" > /dev/ttyACM0 (Host side) The 'cat' will not result anything. However, system still can work normally. Currently, USB-DMAC driver does not have system sleep callbacks hence this driver relies on the PM core to force runtime suspend/resume to suspend and reinitialize USB-DMAC during system resume. After the commit 17218e0092f8 ("PM / genpd: Stop/start devices without pm_runtime_force_suspend/resume()"), PM core will not force runtime suspend/resume anymore so this issue happens. To solve this, make system suspend resume explicit by using pm_runtime_force_{suspend,resume}() as the system sleep callbacks. SET_NOIRQ_SYSTEM_SLEEP_PM_OPS() is used to make sure USB-DMAC suspended after and initialized before renesas_usbhs." Signed-off-by: Phuong Nguyen Signed-off-by: Hiroyuki Yokoyama Cc: # v4.16+ [shimoda: revise the commit log and add Cc tag] Signed-off-by: Yoshihiro Shimoda Signed-off-by: Vinod Koul --- drivers/dma/sh/usb-dmac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index 7f7184c3cf95..59403f6d008a 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -694,6 +694,8 @@ static int usb_dmac_runtime_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct dev_pm_ops usb_dmac_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume, NULL) }; From f5d782d46aa5d4dd369e6560ce5227136b58926f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 13 Dec 2018 02:38:58 +0100 Subject: [PATCH 0441/4277] power: supply: isp1704: switch to gpiod API This migrates isp1704 driver from old GPIO API to new descriptor based GPIO API and drops useless platform data as a side-effect. Migration is simple, since all mainline users are DT based and DT API does not change. Out of tree users of the platform data need to migrate to gpiod_lookup_table as described here: Documentation/driver-api/gpio/board.rst Reviewed-by: Linus Walleij Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/power/supply/isp1704_charger.c | 60 ++++++-------------------- include/linux/power/isp1704_charger.h | 30 ------------- 2 files changed, 12 insertions(+), 78 deletions(-) delete mode 100644 include/linux/power/isp1704_charger.h diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c index 95af5f305838..a63cb5dcfa08 100644 --- a/drivers/power/supply/isp1704_charger.c +++ b/drivers/power/supply/isp1704_charger.c @@ -30,13 +30,12 @@ #include #include #include -#include +#include #include #include #include #include -#include /* Vendor specific Power Control register */ #define ISP1704_PWR_CTRL 0x3d @@ -60,6 +59,7 @@ struct isp1704_charger { struct device *dev; struct power_supply *psy; struct power_supply_desc psy_desc; + struct gpio_desc *enable_gpio; struct usb_phy *phy; struct notifier_block nb; struct work_struct work; @@ -81,18 +81,9 @@ static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val) return usb_phy_io_write(isp->phy, val, reg); } -/* - * Disable/enable the power from the isp1704 if a function for it - * has been provided with platform data. - */ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on) { - struct isp1704_charger_data *board = isp->dev->platform_data; - - if (board && board->set_power) - board->set_power(on); - else if (board) - gpio_set_value(board->enable_gpio, on); + gpiod_set_value(isp->enable_gpio, on); } /* @@ -405,46 +396,19 @@ static int isp1704_charger_probe(struct platform_device *pdev) int ret = -ENODEV; struct power_supply_config psy_cfg = {}; - struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; - - if (np) { - int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0); - - if (gpio < 0) { - dev_err(&pdev->dev, "missing DT GPIO nxp,enable-gpio\n"); - return gpio; - } - - pdata = devm_kzalloc(&pdev->dev, - sizeof(struct isp1704_charger_data), GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - goto fail0; - } - pdata->enable_gpio = gpio; - - dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio); - - ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, - GPIOF_OUT_INIT_HIGH, "isp1704_reset"); - if (ret) { - dev_err(&pdev->dev, "gpio request failed\n"); - goto fail0; - } - } - - if (!pdata) { - dev_err(&pdev->dev, "missing platform data!\n"); - return -ENODEV; - } - - isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); if (!isp) return -ENOMEM; - if (np) + isp->enable_gpio = devm_gpiod_get(&pdev->dev, "nxp,enable", + GPIOD_OUT_HIGH); + if (IS_ERR(isp->enable_gpio)) { + ret = PTR_ERR(isp->enable_gpio); + dev_err(&pdev->dev, "Could not get reset gpio: %d\n", ret); + return ret; + } + + if (pdev->dev.of_node) isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); else isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); diff --git a/include/linux/power/isp1704_charger.h b/include/linux/power/isp1704_charger.h deleted file mode 100644 index 0105d9e7af85..000000000000 --- a/include/linux/power/isp1704_charger.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * ISP1704 USB Charger Detection driver - * - * Copyright (C) 2011 Nokia Corporation - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#ifndef __ISP1704_CHARGER_H -#define __ISP1704_CHARGER_H - -struct isp1704_charger_data { - void (*set_power)(bool on); - int enable_gpio; -}; - -#endif From 0367e23425afdffe1f5b94e56a214d62b35beb05 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Thu, 22 Nov 2018 09:25:51 -0500 Subject: [PATCH 0442/4277] power: supply: axp288_fuel_gauge: Change to use DEFINE_SHOW_ATTRIBUTE macro Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 084c8ba9749d..9ff2461820d8 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -307,22 +307,12 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data) return 0; } -static int debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, fuel_gauge_debug_show, inode->i_private); -} - -static const struct file_operations fg_debug_fops = { - .open = debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug); static void fuel_gauge_create_debugfs(struct axp288_fg_info *info) { info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL, - info, &fg_debug_fops); + info, &fuel_gauge_debug_fops); } static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) From e75e3a125b40ff847d5ffca293737fb7fc1816dc Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Dec 2018 16:31:34 -0800 Subject: [PATCH 0443/4277] drivers: power: supply: goldfish_battery: Put an SPDX tag There was a checkpatch complain: "Missing or malformed SPDX-License-Identifier tag". Signed-off-by: Roman Kiryanov Signed-off-by: Sebastian Reichel --- drivers/power/supply/goldfish_battery.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index f5c525e4482a..8af3e05f9dec 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL /* * Power supply driver for the goldfish emulator * @@ -5,15 +6,6 @@ * Copyright (C) 2012 Intel, Inc. * Copyright (C) 2013 Intel, Inc. * Author: Mike Lockwood - * - * 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 From 65ab18a1aaa884d4c9a94f66119b125aeff48351 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Dec 2018 16:31:35 -0800 Subject: [PATCH 0444/4277] drivers: power: supply: goldfish_battery: Fix "line over 80 characters" There was a line longer than 80 characters. Signed-off-by: Roman Kiryanov Signed-off-by: Sebastian Reichel --- drivers/power/supply/goldfish_battery.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index 8af3e05f9dec..6f6b19dbcc29 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -185,8 +185,9 @@ static int goldfish_battery_probe(struct platform_device *pdev) return -ENODEV; } - ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt, - IRQF_SHARED, pdev->name, data); + ret = devm_request_irq(&pdev->dev, data->irq, + goldfish_battery_interrupt, + IRQF_SHARED, pdev->name, data); if (ret) return ret; From 5fbde356cb09a418bb0838c863ab610439d3dac0 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Dec 2018 16:31:36 -0800 Subject: [PATCH 0445/4277] drivers: power: supply: goldfish_battery: Retire a redundant variable The battery_data static variable was not used. Signed-off-by: Roman Kiryanov Signed-off-by: Sebastian Reichel --- drivers/power/supply/goldfish_battery.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index 6f6b19dbcc29..f65ceb50d588 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -32,12 +32,6 @@ struct goldfish_battery_data { #define GOLDFISH_BATTERY_WRITE(data, addr, x) \ (writel(x, data->reg_base + addr)) -/* - * Temporary variable used between goldfish_battery_probe() and - * goldfish_battery_open(). - */ -static struct goldfish_battery_data *battery_data; - enum { /* status register */ BATTERY_INT_STATUS = 0x00, @@ -205,7 +199,6 @@ static int goldfish_battery_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, data); - battery_data = data; GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); return 0; @@ -217,7 +210,6 @@ static int goldfish_battery_remove(struct platform_device *pdev) power_supply_unregister(data->battery); power_supply_unregister(data->ac); - battery_data = NULL; return 0; } From 617ee863ed34c1c75c9d8d317b8b7f049420774b Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Dec 2018 16:31:37 -0800 Subject: [PATCH 0446/4277] drivers: power: supply: goldfish_battery: Fix alignment Align two values as other values below. Signed-off-by: Roman Kiryanov Signed-off-by: Sebastian Reichel --- drivers/power/supply/goldfish_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index f65ceb50d588..9937e164b467 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -34,9 +34,9 @@ struct goldfish_battery_data { enum { /* status register */ - BATTERY_INT_STATUS = 0x00, + BATTERY_INT_STATUS = 0x00, /* set this to enable IRQ */ - BATTERY_INT_ENABLE = 0x04, + BATTERY_INT_ENABLE = 0x04, BATTERY_AC_ONLINE = 0x08, BATTERY_STATUS = 0x0C, From 3dcdb68925b82a9fe89b28bd66a8c12d8c3804a0 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Dec 2018 16:31:38 -0800 Subject: [PATCH 0447/4277] drivers: power: supply: goldfish_battery: Use tabs for alignment Tabs are preferred. Signed-off-by: Roman Kiryanov Signed-off-by: Sebastian Reichel --- drivers/power/supply/goldfish_battery.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index 9937e164b467..382193fe63c4 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -38,15 +38,15 @@ enum { /* set this to enable IRQ */ BATTERY_INT_ENABLE = 0x04, - BATTERY_AC_ONLINE = 0x08, - BATTERY_STATUS = 0x0C, - BATTERY_HEALTH = 0x10, - BATTERY_PRESENT = 0x14, - BATTERY_CAPACITY = 0x18, + BATTERY_AC_ONLINE = 0x08, + BATTERY_STATUS = 0x0C, + BATTERY_HEALTH = 0x10, + BATTERY_PRESENT = 0x14, + BATTERY_CAPACITY = 0x18, BATTERY_STATUS_CHANGED = 1U << 0, AC_STATUS_CHANGED = 1U << 1, - BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, + BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, }; From 6f12e46eebf1a7d4fdd66df5e815df96b8f8b1b5 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Thu, 20 Dec 2018 13:51:24 -0600 Subject: [PATCH 0448/4277] power: twl4030: fix a missing check of return value If twl4030_bci_read() fails, the read data in "s" is incorrect, which is however used in the following execution. The fix checks the return value of twl4030_bci_read() and returns an error code upstream upon the failure of twl4030_bci_read(). Signed-off-by: Kangjie Lu Signed-off-by: Sebastian Reichel --- drivers/power/supply/twl4030_charger.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 0e202d4273fb..4299873a1118 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -809,7 +809,9 @@ static int twl4030_bci_get_property(struct power_supply *psy, is_charging = state & TWL4030_MSTATEC_AC; if (!is_charging) { u8 s; - twl4030_bci_read(TWL4030_BCIMDEN, &s); + ret = twl4030_bci_read(TWL4030_BCIMDEN, &s); + if (ret < 0) + return ret; if (psy->desc->type == POWER_SUPPLY_TYPE_USB) is_charging = s & 1; else From 19836a1b8e28f61109514fbe86d36f6aac38bb70 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 26 Dec 2018 11:53:57 -0500 Subject: [PATCH 0449/4277] power: supply: remove some duplicated includes We include of.h twice. It's unnecessary,so just remove them. Signed-off-by: Yangtao Li Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 569790ea6917..07a85e19615c 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -156,8 +156,6 @@ static void power_supply_deferred_register_work(struct work_struct *work) } #ifdef CONFIG_OF -#include - static int __power_supply_populate_supplied_from(struct device *dev, void *data) { From fd10606f93a149a9f3d37574e5385b083b4a7b32 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 27 Dec 2018 20:52:21 +0100 Subject: [PATCH 0450/4277] cpcap-charger: generate events for userspace The driver doesn't generate uevents on charger connect/disconnect. This leads to UPower not detecting when AC is on or off... and that is bad. Reported by Arthur D. on github ( https://github.com/maemo-leste/bugtracker/issues/206 ), thanks to Merlijn Wajer for suggesting a fix. Cc: stable@kernel.org Signed-off-by: Pavel Machek Acked-by: Tony Lindgren Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-charger.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index c843eaff8ad0..c3ed7b476676 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -458,6 +458,7 @@ static void cpcap_usb_detect(struct work_struct *work) goto out_err; } + power_supply_changed(ddata->usb); return; out_err: From 02276af2fcf809b00b1fc1aede7cae6f25849e4d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 8 Jan 2019 13:12:12 +0100 Subject: [PATCH 0451/4277] power: supply: charger-manager: Fix trivial language typos Fix few trivial language typos. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/charger-manager.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 38be91f21cc4..2e8db5e6de0b 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -4,7 +4,7 @@ * * This driver enables to monitor battery health and control charger * during suspend-to-mem. - * Charger manager depends on other devices. register this later than + * Charger manager depends on other devices. Register this later than * the depending devices. * * This program is free software; you can redistribute it and/or modify @@ -29,7 +29,7 @@ #include /* - * Default termperature threshold for charging. + * Default temperature threshold for charging. * Every temperature units are in tenth of centigrade. */ #define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 @@ -356,7 +356,7 @@ static bool is_polling_required(struct charger_manager *cm) * Note that Charger Manager keeps the charger enabled regardless whether * the charger is charging or not (because battery is full or no external * power source exists) except when CM needs to disable chargers forcibly - * bacause of emergency causes; when the battery is overheated or too cold. + * because of emergency causes; when the battery is overheated or too cold. */ static int try_charger_enable(struct charger_manager *cm, bool enable) { @@ -643,7 +643,7 @@ static int cm_check_thermal_status(struct charger_manager *cm) if (ret) { /* FIXME: * No information of battery temperature might - * occur hazadous result. We have to handle it + * occur hazardous result. We have to handle it * depending on battery type. */ dev_err(cm->dev, "Failed to get battery temperature\n"); @@ -693,7 +693,7 @@ static bool _cm_monitor(struct charger_manager *cm) uevent_notify(cm, default_event_names[temp_alrt]); /* - * Check whole charging duration and discharing duration + * Check whole charging duration and discharging duration * after full-batt. */ } else if (!cm->emergency_stop && check_charging_duration(cm)) { @@ -866,7 +866,7 @@ static void battout_handler(struct charger_manager *cm) } /** - * misc_event_handler - Handler for other evnets + * misc_event_handler - Handler for other events * @cm: the Charger Manager representing the battery. * @type: the Charger Manager representing the battery. */ @@ -1218,7 +1218,7 @@ static int charger_extcon_init(struct charger_manager *cm, } /** - * charger_manager_register_extcon - Register extcon device to recevie state + * charger_manager_register_extcon - Register extcon device to receive state * of charger cable. * @cm: the Charger Manager representing the battery. * @@ -1538,7 +1538,7 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) of_property_read_u32(np, "cm-discharging-max", &desc->discharging_max_duration_ms); - /* battery charger regualtors */ + /* battery charger regulators */ desc->num_charger_regulators = of_get_child_count(np); if (desc->num_charger_regulators) { struct charger_regulator *chg_regs; @@ -1801,7 +1801,7 @@ static int charger_manager_probe(struct platform_device *pdev) /* * Charger-manager have to check the charging state right after - * tialization of charger-manager and then update current charging + * initialization of charger-manager and then update current charging * state. */ cm_monitor(); From 1d2375f048b7da90402c7a40fb3606b320c3f76f Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Fri, 18 Jan 2019 22:58:04 +0100 Subject: [PATCH 0452/4277] doc:process: remove note from 'stable api nonsense' The link referred by the note can't be retrieved: this patch just remove that old note. Signed-off-by: Federico Vaga Acked-by: Greg Kroah-Hartman Signed-off-by: Jonathan Corbet --- Documentation/process/stable-api-nonsense.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/process/stable-api-nonsense.rst b/Documentation/process/stable-api-nonsense.rst index 24f5aeecee91..57d95a49c096 100644 --- a/Documentation/process/stable-api-nonsense.rst +++ b/Documentation/process/stable-api-nonsense.rst @@ -171,8 +171,7 @@ is also a rough job. Simple, get your kernel driver into the main kernel tree (remember we are talking about GPL released drivers here, if your code doesn't fall -under this category, good luck, you are on your own here, you leech -.) If your +under this category, good luck, you are on your own here, you leech). If your driver is in the tree, and a kernel interface changes, it will be fixed up by the person who did the kernel change in the first place. This ensures that your driver is always buildable, and works over time, with From 3d18f58621601c04f5c31e4f51c640dfd41bbaf0 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Sat, 19 Jan 2019 23:14:22 +0100 Subject: [PATCH 0453/4277] doc:it_IT: documentation alignment It aligns the italian translation with the latest changes: ae67ee6c5e1d docs: fix Co-Developed-by docs 3fe5dbfef47e Documentation/process/coding-style.rst: don't use "extern" with function prototypes Signed-off-by: Federico Vaga Signed-off-by: Jonathan Corbet --- Documentation/translations/it_IT/process/coding-style.rst | 3 +++ .../translations/it_IT/process/submitting-patches.rst | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/translations/it_IT/process/coding-style.rst b/Documentation/translations/it_IT/process/coding-style.rst index 67c6d9ea7257..dfdefb951b89 100644 --- a/Documentation/translations/it_IT/process/coding-style.rst +++ b/Documentation/translations/it_IT/process/coding-style.rst @@ -449,6 +449,9 @@ Nonostante questo non sia richiesto dal linguaggio C, in Linux viene preferito perché è un modo semplice per aggiungere informazioni importanti per il lettore. +Non usate la parola chiave ``extern`` coi prototipi di funzione perché +rende le righe più lunghe e non è strettamente necessario. + 7) Centralizzare il ritorno delle funzioni ------------------------------------------ diff --git a/Documentation/translations/it_IT/process/submitting-patches.rst b/Documentation/translations/it_IT/process/submitting-patches.rst index b6368a45a8e4..2ab9c1401aa1 100644 --- a/Documentation/translations/it_IT/process/submitting-patches.rst +++ b/Documentation/translations/it_IT/process/submitting-patches.rst @@ -534,7 +534,7 @@ alle persone che vogliono seguire i vostri sorgenti, e quelle che cercano dei bachi. -12) Quando utilizzare Acked-by:, Cc:, e Co-Developed-by: +12) Quando utilizzare Acked-by:, Cc:, e Co-developed-by: -------------------------------------------------------- L'etichetta Signed-off-by: indica che il firmatario è stato coinvolto nello @@ -567,7 +567,7 @@ alcunché - ma dovrebbe indicare che la persona ha ricevuto una copia della patch. Questa etichetta documenta che terzi potenzialmente interessati sono stati inclusi nella discussione. -L'etichetta Co-Developed-by: indica che la patch è stata scritta dall'autore in +L'etichetta Co-developed-by: indica che la patch è stata scritta dall'autore in collaborazione con un altro sviluppatore. Qualche volta questo è utile quando più persone lavorano sulla stessa patch. Notate, questa persona deve avere nella patch anche una riga Signed-off-by:. From b04c11c988f4fcfba037ce6c356ea5ac9e23df63 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Sun, 20 Jan 2019 12:16:29 +0100 Subject: [PATCH 0454/4277] doc:process: add missing internal link in stable-kernel-rules Keep consistent the document. In the document, option references are always linked, except for the one I fixed with this patch Signed-off-by: Federico Vaga Signed-off-by: Jonathan Corbet --- Documentation/process/stable-kernel-rules.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/process/stable-kernel-rules.rst b/Documentation/process/stable-kernel-rules.rst index 0de6f6145cc6..070e3f04d183 100644 --- a/Documentation/process/stable-kernel-rules.rst +++ b/Documentation/process/stable-kernel-rules.rst @@ -98,9 +98,9 @@ text, like this: commit upstream. -Additionally, some patches submitted via Option 1 may have additional patch -prerequisites which can be cherry-picked. This can be specified in the following -format in the sign-off area: +Additionally, some patches submitted via :ref:`option_1` may have additional +patch prerequisites which can be cherry-picked. This can be specified in the +following format in the sign-off area: .. code-block:: none From 7967656ffbfa493f5546c0f18bf8a28f702c4baa Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 18 Jan 2019 15:50:47 -0700 Subject: [PATCH 0455/4277] coding-style: Clarify the expectations around bool There has been some confusion since checkpatch started warning about bool use in structures, and people have been avoiding using it. Many people feel there is still a legitimate place for bool in structures, so provide some guidance on bool usage derived from the entire thread that spawned the checkpatch warning. Link: https://lkml.kernel.org/r/CA+55aFwVZk1OfB9T2v014PTAKFhtVan_Zj2dOjnCy3x6E4UJfA@mail.gmail.com Signed-off-by: Joe Perches Acked-by: Joe Perches Reviewed-by: Bart Van Assche Acked-by: Jani Nikula Reviewed-by: Joey Pabalinas Signed-off-by: Jason Gunthorpe Signed-off-by: Jonathan Corbet --- Documentation/process/coding-style.rst | 38 +++++++++++++++++++++++--- scripts/checkpatch.pl | 13 --------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index 4e611a01adc6..8ea913e99fa1 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -938,7 +938,37 @@ result. Typical examples would be functions that return pointers; they use NULL or the ERR_PTR mechanism to report failure. -17) Don't re-invent the kernel macros +17) Using bool +-------------- + +The Linux kernel bool type is an alias for the C99 _Bool type. bool values can +only evaluate to 0 or 1, and implicit or explicit conversion to bool +automatically converts the value to true or false. When using bool types the +!! construction is not needed, which eliminates a class of bugs. + +When working with bool values the true and false definitions should be used +instead of 1 and 0. + +bool function return types and stack variables are always fine to use whenever +appropriate. Use of bool is encouraged to improve readability and is often a +better option than 'int' for storing boolean values. + +Do not use bool if cache line layout or size of the value matters, as its size +and alignment varies based on the compiled architecture. Structures that are +optimized for alignment and size should not use bool. + +If a structure has many true/false values, consider consolidating them into a +bitfield with 1 bit members, or using an appropriate fixed width type, such as +u8. + +Similarly for function arguments, many true/false values can be consolidated +into a single bitwise 'flags' argument and 'flags' can often be a more +readable alternative if the call-sites have naked true/false constants. + +Otherwise limited use of bool in structures and arguments can improve +readability. + +18) Don't re-invent the kernel macros ------------------------------------- The header file include/linux/kernel.h contains a number of macros that @@ -961,7 +991,7 @@ need them. Feel free to peruse that header file to see what else is already defined that you shouldn't reproduce in your code. -18) Editor modelines and other cruft +19) Editor modelines and other cruft ------------------------------------ Some editors can interpret configuration information embedded in source files, @@ -995,7 +1025,7 @@ own custom mode, or may have some other magic method for making indentation work correctly. -19) Inline assembly +20) Inline assembly ------------------- In architecture-specific code, you may need to use inline assembly to interface @@ -1027,7 +1057,7 @@ the next instruction in the assembly output: : /* outputs */ : /* inputs */ : /* clobbers */); -20) Conditional Compilation +21) Conditional Compilation --------------------------- Wherever possible, don't use preprocessor conditionals (#if, #ifdef) in .c diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index b737ca9d7204..d62abd032885 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -6368,19 +6368,6 @@ sub process { } } -# check for bool bitfields - if ($sline =~ /^.\s+bool\s*$Ident\s*:\s*\d+\s*;/) { - WARN("BOOL_BITFIELD", - "Avoid using bool as bitfield. Prefer bool bitfields as unsigned int or u<8|16|32>\n" . $herecurr); - } - -# check for bool use in .h files - if ($realfile =~ /\.h$/ && - $sline =~ /^.\s+bool\s*$Ident\s*(?::\s*d+\s*)?;/) { - CHK("BOOL_MEMBER", - "Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384\n" . $herecurr); - } - # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", From e6e37f636815075c0055f33f42ebea4fb057def6 Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Fri, 18 Jan 2019 21:38:32 +0100 Subject: [PATCH 0456/4277] doc: networking: integrate scaling document into doc tree Convert scaling document into reStructuredText and add reference to scaling document into main table of contents in network documentation. There are no semantic changes. There are no references to "scaling.txt" file. Whole kernel tree was checked using: $ grep -r "scaling\.txt" Signed-off-by: Otto Sabart Signed-off-by: Jonathan Corbet --- Documentation/networking/index.rst | 1 + .../networking/{scaling.txt => scaling.rst} | 131 ++++++++++++------ 2 files changed, 86 insertions(+), 46 deletions(-) rename Documentation/networking/{scaling.txt => scaling.rst} (92%) diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 7034214cf87e..44a0f1939fcd 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -34,6 +34,7 @@ Contents: snmp_counter checksum-offloads segmentation-offloads + scaling .. only:: subproject diff --git a/Documentation/networking/scaling.txt b/Documentation/networking/scaling.rst similarity index 92% rename from Documentation/networking/scaling.txt rename to Documentation/networking/scaling.rst index b7056a8a0540..f78d7bf27ff5 100644 --- a/Documentation/networking/scaling.txt +++ b/Documentation/networking/scaling.rst @@ -1,4 +1,8 @@ +.. SPDX-License-Identifier: GPL-2.0 + +===================================== Scaling in the Linux Networking Stack +===================================== Introduction @@ -10,11 +14,11 @@ multi-processor systems. The following technologies are described: - RSS: Receive Side Scaling - RPS: Receive Packet Steering - RFS: Receive Flow Steering - Accelerated Receive Flow Steering - XPS: Transmit Packet Steering +- RSS: Receive Side Scaling +- RPS: Receive Packet Steering +- RFS: Receive Flow Steering +- Accelerated Receive Flow Steering +- XPS: Transmit Packet Steering RSS: Receive Side Scaling @@ -45,7 +49,9 @@ programmable filters. For example, webserver bound TCP port 80 packets can be directed to their own receive queue. Such “n-tuple” filters can be configured from ethtool (--config-ntuple). -==== RSS Configuration + +RSS Configuration +----------------- The driver for a multi-queue capable NIC typically provides a kernel module parameter for specifying the number of hardware queues to @@ -63,7 +69,9 @@ commands (--show-rxfh-indir and --set-rxfh-indir). Modifying the indirection table could be done to give different queues different relative weights. -== RSS IRQ Configuration + +RSS IRQ Configuration +~~~~~~~~~~~~~~~~~~~~~ Each receive queue has a separate IRQ associated with it. The NIC triggers this to notify a CPU when new packets arrive on the given queue. The @@ -77,7 +85,9 @@ affinity of each interrupt see Documentation/IRQ-affinity.txt. Some systems will be running irqbalance, a daemon that dynamically optimizes IRQ assignments and as a result may override any manual settings. -== Suggested Configuration + +Suggested Configuration +~~~~~~~~~~~~~~~~~~~~~~~ RSS should be enabled when latency is a concern or whenever receive interrupt processing forms a bottleneck. Spreading load between CPUs @@ -105,10 +115,12 @@ Whereas RSS selects the queue and hence CPU that will run the hardware interrupt handler, RPS selects the CPU to perform protocol processing above the interrupt handler. This is accomplished by placing the packet on the desired CPU’s backlog queue and waking up the CPU for processing. -RPS has some advantages over RSS: 1) it can be used with any NIC, -2) software filters can easily be added to hash over new protocols, +RPS has some advantages over RSS: + +1) it can be used with any NIC +2) software filters can easily be added to hash over new protocols 3) it does not increase hardware device interrupt rate (although it does -introduce inter-processor interrupts (IPIs)). + introduce inter-processor interrupts (IPIs)) RPS is called during bottom half of the receive interrupt handler, when a driver sends a packet up the network stack with netif_rx() or @@ -135,21 +147,25 @@ packets have been queued to their backlog queue. The IPI wakes backlog processing on the remote CPU, and any queued packets are then processed up the networking stack. -==== RPS Configuration + +RPS Configuration +----------------- RPS requires a kernel compiled with the CONFIG_RPS kconfig symbol (on by default for SMP). Even when compiled in, RPS remains disabled until explicitly configured. The list of CPUs to which RPS may forward traffic -can be configured for each receive queue using a sysfs file entry: +can be configured for each receive queue using a sysfs file entry:: - /sys/class/net//queues/rx-/rps_cpus + /sys/class/net//queues/rx-/rps_cpus This file implements a bitmap of CPUs. RPS is disabled when it is zero (the default), in which case packets are processed on the interrupting CPU. Documentation/IRQ-affinity.txt explains how CPUs are assigned to the bitmap. -== Suggested Configuration + +Suggested Configuration +~~~~~~~~~~~~~~~~~~~~~~~ For a single queue device, a typical RPS configuration would be to set the rps_cpus to the CPUs in the same memory domain of the interrupting @@ -163,7 +179,9 @@ and unnecessary. If there are fewer hardware queues than CPUs, then RPS might be beneficial if the rps_cpus for each queue are the ones that share the same memory domain as the interrupting CPU for that queue. -==== RPS Flow Limit + +RPS Flow Limit +-------------- RPS scales kernel receive processing across CPUs without introducing reordering. The trade-off to sending all packets from the same flow @@ -187,29 +205,33 @@ No packets are dropped when the input packet queue length is below the threshold, so flow limit does not sever connections outright: even large flows maintain connectivity. -== Interface + +Interface +~~~~~~~~~ Flow limit is compiled in by default (CONFIG_NET_FLOW_LIMIT), but not turned on. It is implemented for each CPU independently (to avoid lock and cache contention) and toggled per CPU by setting the relevant bit in sysctl net.core.flow_limit_cpu_bitmap. It exposes the same CPU -bitmap interface as rps_cpus (see above) when called from procfs: +bitmap interface as rps_cpus (see above) when called from procfs:: - /proc/sys/net/core/flow_limit_cpu_bitmap + /proc/sys/net/core/flow_limit_cpu_bitmap Per-flow rate is calculated by hashing each packet into a hashtable bucket and incrementing a per-bucket counter. The hash function is the same that selects a CPU in RPS, but as the number of buckets can be much larger than the number of CPUs, flow limit has finer-grained identification of large flows and fewer false positives. The default -table has 4096 buckets. This value can be modified through sysctl +table has 4096 buckets. This value can be modified through sysctl:: - net.core.flow_limit_table_len + net.core.flow_limit_table_len The value is only consulted when a new table is allocated. Modifying it does not update active tables. -== Suggested Configuration + +Suggested Configuration +~~~~~~~~~~~~~~~~~~~~~~~ Flow limit is useful on systems with many concurrent connections, where a single connection taking up 50% of a CPU indicates a problem. @@ -280,10 +302,10 @@ table), the packet is enqueued onto that CPU’s backlog. If they differ, the current CPU is updated to match the desired CPU if one of the following is true: -- The current CPU's queue head counter >= the recorded tail counter - value in rps_dev_flow[i] -- The current CPU is unset (>= nr_cpu_ids) -- The current CPU is offline + - The current CPU's queue head counter >= the recorded tail counter + value in rps_dev_flow[i] + - The current CPU is unset (>= nr_cpu_ids) + - The current CPU is offline After this check, the packet is sent to the (possibly updated) current CPU. These rules aim to ensure that a flow only moves to a new CPU when @@ -291,19 +313,23 @@ there are no packets outstanding on the old CPU, as the outstanding packets could arrive later than those about to be processed on the new CPU. -==== RFS Configuration + +RFS Configuration +----------------- RFS is only available if the kconfig symbol CONFIG_RPS is enabled (on by default for SMP). The functionality remains disabled until explicitly -configured. The number of entries in the global flow table is set through: +configured. The number of entries in the global flow table is set through:: - /proc/sys/net/core/rps_sock_flow_entries + /proc/sys/net/core/rps_sock_flow_entries -The number of entries in the per-queue flow table are set through: +The number of entries in the per-queue flow table are set through:: - /sys/class/net//queues/rx-/rps_flow_cnt + /sys/class/net//queues/rx-/rps_flow_cnt -== Suggested Configuration + +Suggested Configuration +~~~~~~~~~~~~~~~~~~~~~~~ Both of these need to be set before RFS is enabled for a receive queue. Values for both are rounded up to the nearest power of two. The @@ -347,7 +373,9 @@ functions in the cpu_rmap (“CPU affinity reverse map”) kernel library to populate the map. For each CPU, the corresponding queue in the map is set to be one whose processing CPU is closest in cache locality. -==== Accelerated RFS Configuration + +Accelerated RFS Configuration +----------------------------- Accelerated RFS is only available if the kernel is compiled with CONFIG_RFS_ACCEL and support is provided by the NIC device and driver. @@ -356,11 +384,14 @@ of CPU to queues is automatically deduced from the IRQ affinities configured for each receive queue by the driver, so no additional configuration should be necessary. -== Suggested Configuration + +Suggested Configuration +~~~~~~~~~~~~~~~~~~~~~~~ This technique should be enabled whenever one wants to use RFS and the NIC supports hardware acceleration. + XPS: Transmit Packet Steering ============================= @@ -430,20 +461,25 @@ transport layer is responsible for setting ooo_okay appropriately. TCP, for instance, sets the flag when all data for a connection has been acknowledged. -==== XPS Configuration +XPS Configuration +----------------- XPS is only available if the kconfig symbol CONFIG_XPS is enabled (on by default for SMP). The functionality remains disabled until explicitly configured. To enable XPS, the bitmap of CPUs/receive-queues that may use a transmit queue is configured using the sysfs file entry: -For selection based on CPUs map: -/sys/class/net//queues/tx-/xps_cpus +For selection based on CPUs map:: -For selection based on receive-queues map: -/sys/class/net//queues/tx-/xps_rxqs + /sys/class/net//queues/tx-/xps_cpus -== Suggested Configuration +For selection based on receive-queues map:: + + /sys/class/net//queues/tx-/xps_rxqs + + +Suggested Configuration +~~~~~~~~~~~~~~~~~~~~~~~ For a network device with a single transmission queue, XPS configuration has no effect, since there is no choice in this case. In a multi-queue @@ -460,16 +496,18 @@ explicitly configured mapping receive-queue(s) to transmit queue(s). If the user configuration for receive-queue map does not apply, then the transmit queue is selected based on the CPUs map. -Per TX Queue rate limitation: -============================= + +Per TX Queue rate limitation +============================ These are rate-limitation mechanisms implemented by HW, where currently -a max-rate attribute is supported, by setting a Mbps value to +a max-rate attribute is supported, by setting a Mbps value to:: -/sys/class/net//queues/tx-/tx_maxrate + /sys/class/net//queues/tx-/tx_maxrate A value of zero means disabled, and this is the default. + Further Information =================== RPS and RFS were introduced in kernel 2.6.35. XPS was incorporated into @@ -480,5 +518,6 @@ Accelerated RFS was introduced in 2.6.35. Original patches were submitted by Ben Hutchings (bwh@kernel.org) Authors: -Tom Herbert (therbert@google.com) -Willem de Bruijn (willemb@google.com) + +- Tom Herbert (therbert@google.com) +- Willem de Bruijn (willemb@google.com) From 31f433307043cf14ba1057ce4b8eaac17a2c617f Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 18 Jan 2019 13:38:22 +0000 Subject: [PATCH 0457/4277] Documentation: DMA-API: fix two typos This patch fixes two typos, a missing "e" and dma-api/driver_filter was incorrectly typed dma-api/driver-filter. Signed-off-by: Corentin Labbe [jc: fixed obvious language typos on the way in] Signed-off-by: Jonathan Corbet --- Documentation/DMA-API.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index e133ccd60228..607c1e75e5aa 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -530,8 +530,8 @@ that simply cannot make consistent memory. dma_free_attrs(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs) -Free memory allocated by the dma_alloc_attrs(). All parameters common -parameters must identical to those otherwise passed to dma_fre_coherent, +Free memory allocated by the dma_alloc_attrs(). All common +parameters must be identical to those otherwise passed to dma_free_coherent, and the attrs argument must be identical to the attrs passed to dma_alloc_attrs(). @@ -717,7 +717,7 @@ dma-api/num_free_entries The current number of free dma_debug_entries dma-api/nr_total_entries The total number of dma_debug_entries in the allocator, both free and used. -dma-api/driver-filter You can write a name of a driver into this file +dma-api/driver_filter You can write a name of a driver into this file to limit the debug output to requests from that particular driver. Write an empty string to that file to disable the filter and see From 699c7d1346fbef69e60ca7647c50bbebc483d1c8 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Sat, 1 Dec 2018 15:19:24 +0900 Subject: [PATCH 0458/4277] pinctrl: sh-pfc: r8a77990: Fix MOD_SEL0 bit3 when using TX0 According to the R-Car Gen3 Hardware Manual Errata for Rev 1.00 of Aug 24, 2018, the MOD_SEL0 bit3 is set to 0 when TX0_A pin function is selected, and the MOD_SEL0 bit3 is set to 1 when TX0_B pin function is selected. Fixes: 6d4036a1e3b3ac0f ("pinctrl: sh-pfc: Initial R8A77990 PFC support") Signed-off-by: Takeshi Kihara Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index e40908dc37e0..9a38b3ee9efe 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -1060,7 +1060,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_GPSR(IP11_11_8, RIF1_SYNC), PINMUX_IPSR_GPSR(IP11_11_8, TS_SCK1), - PINMUX_IPSR_GPSR(IP11_15_12, TX0_A), + PINMUX_IPSR_MSEL(IP11_15_12, TX0_A, SEL_SCIF0_0), PINMUX_IPSR_GPSR(IP11_15_12, HTX1_A), PINMUX_IPSR_MSEL(IP11_15_12, SSI_WS2_A, SEL_SSI2_0), PINMUX_IPSR_GPSR(IP11_15_12, RIF1_D0), @@ -1170,7 +1170,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP13_19_16, SIM0_D_A, SEL_SIMCARD_0), PINMUX_IPSR_GPSR(IP13_23_20, MLB_DAT), - PINMUX_IPSR_GPSR(IP13_23_20, TX0_B), + PINMUX_IPSR_MSEL(IP13_23_20, TX0_B, SEL_SCIF0_1), PINMUX_IPSR_MSEL(IP13_23_20, RIF0_SYNC_A, SEL_DRIF0_0), PINMUX_IPSR_GPSR(IP13_23_20, SIM0_CLK_A), From 7219a4b645208734d45b1d30a4c35b6f09a0e9e6 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Sat, 1 Dec 2018 16:02:20 +0900 Subject: [PATCH 0459/4277] pinctrl: sh-pfc: r8a77990: Fix MOD_SEL0 bit2 when using RX2, TX2 and SCK2 According to the R-Car Gen3 Hardware Manual Errata for Rev 1.00 of Aug 24, 2018, the MOD_SEL0 bit2 is set when RX2_{A,B}, TX2_{A,B} and SCK2_A pin functions are selected. Fixes: 6d4036a1e3b3ac0f ("pinctrl: sh-pfc: Initial R8A77990 PFC support") Signed-off-by: Takeshi Kihara Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index 9a38b3ee9efe..d50ed3c0f070 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -1099,7 +1099,7 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP12_3_0, SSI_WS9_B, SEL_SSI9_1), PINMUX_IPSR_GPSR(IP12_3_0, AUDIO_CLKOUT3_B), - PINMUX_IPSR_GPSR(IP12_7_4, SCK2_A), + PINMUX_IPSR_MSEL(IP12_7_4, SCK2_A, SEL_SCIF2_0), PINMUX_IPSR_MSEL(IP12_7_4, HSCK0_A, SEL_HSCIF0_0), PINMUX_IPSR_MSEL(IP12_7_4, AUDIO_CLKB_A, SEL_ADGB_0), PINMUX_IPSR_GPSR(IP12_7_4, CTS1_N), @@ -1107,14 +1107,14 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_MSEL(IP12_7_4, REMOCON_A, SEL_REMOCON_0), PINMUX_IPSR_MSEL(IP12_7_4, SCIF_CLK_B, SEL_SCIF_1), - PINMUX_IPSR_GPSR(IP12_11_8, TX2_A), + PINMUX_IPSR_MSEL(IP12_11_8, TX2_A, SEL_SCIF2_0), PINMUX_IPSR_MSEL(IP12_11_8, HRX0_A, SEL_HSCIF0_0), PINMUX_IPSR_GPSR(IP12_11_8, AUDIO_CLKOUT2_A), PINMUX_IPSR_MSEL(IP12_11_8, SCL1_A, SEL_I2C1_0), PINMUX_IPSR_MSEL(IP12_11_8, FSO_CFE_0_N_A, SEL_FSO_0), PINMUX_IPSR_GPSR(IP12_11_8, TS_SDEN1), - PINMUX_IPSR_GPSR(IP12_15_12, RX2_A), + PINMUX_IPSR_MSEL(IP12_15_12, RX2_A, SEL_SCIF2_0), PINMUX_IPSR_GPSR(IP12_15_12, HTX0_A), PINMUX_IPSR_GPSR(IP12_15_12, AUDIO_CLKOUT3_A), PINMUX_IPSR_MSEL(IP12_15_12, SDA1_A, SEL_I2C1_0), @@ -1126,11 +1126,11 @@ static const u16 pinmux_data[] = { PINMUX_IPSR_GPSR(IP12_23_20, MSIOF0_RXD), PINMUX_IPSR_GPSR(IP12_23_20, SSI_WS78), - PINMUX_IPSR_GPSR(IP12_23_20, TX2_B), + PINMUX_IPSR_MSEL(IP12_23_20, TX2_B, SEL_SCIF2_1), PINMUX_IPSR_GPSR(IP12_27_24, MSIOF0_TXD), PINMUX_IPSR_GPSR(IP12_27_24, SSI_SDATA7), - PINMUX_IPSR_GPSR(IP12_27_24, RX2_B), + PINMUX_IPSR_MSEL(IP12_27_24, RX2_B, SEL_SCIF2_1), PINMUX_IPSR_GPSR(IP12_31_28, MSIOF0_SYNC), PINMUX_IPSR_GPSR(IP12_31_28, AUDIO_CLKOUT_B), From 3e3eebeacad79bda8a9664c86c04f5201e86fece Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Wed, 12 Dec 2018 19:19:34 +0900 Subject: [PATCH 0460/4277] pinctrl: sh-pfc: r8a77990: Fix MOD_SEL bit numbering MOD_SEL register bit numbering was different from R-Car E3 SoC and R-Car H3/M3-[WN] SoCs. MOD_SEL 1-bit H3/M3-[WN] E3 =============== ========== ===== Set Value = H'0 b'0 b'0 Set Value = H'1 b'1 b'1 MOD_SEL 2-bits H3/M3-[WN] E3 =============== ========== ===== Set Value = H'0 b'00 b'00 Set Value = H'1 b'01 b'10 Set Value = H'2 b'10 b'01 Set Value = H'3 b'11 b'11 MOD_SEL 3-bits H3/M3-[WN] E3 =============== ========== ===== Set Value = H'0 b'000 b'000 Set Value = H'1 b'001 b'100 Set Value = H'2 b'010 b'010 Set Value = H'3 b'011 b'110 Set Value = H'4 b'100 b'001 Set Value = H'5 b'101 b'101 Set Value = H'6 b'110 b'011 Set Value = H'7 b'111 b'111 This patch replaces the #define name and value of MOD_SEL. Signed-off-by: Takeshi Kihara Fixes: 6d4036a1e3b3 ("pinctrl: sh-pfc: Initial R8A77990 PFC support") [shimoda: Split a patch per SoC and revise the commit log] Signed-off-by: Yoshihiro Shimoda [geert: Use macros to do the actual reordering] Signed-off-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 32 +++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index d50ed3c0f070..821a425e285a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -391,29 +391,33 @@ FM(IP12_23_20) IP12_23_20 FM(IP13_23_20) IP13_23_20 FM(IP14_23_20) IP14_23_20 FM FM(IP12_27_24) IP12_27_24 FM(IP13_27_24) IP13_27_24 FM(IP14_27_24) IP14_27_24 FM(IP15_27_24) IP15_27_24 \ FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM(IP15_31_28) IP15_31_28 +/* The bit numbering in MOD_SEL fields is reversed */ +#define REV4(f0, f1, f2, f3) f0 f2 f1 f3 +#define REV8(f0, f1, f2, f3, f4, f5, f6, f7) f0 f4 f2 f6 f1 f5 f3 f7 + /* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ -#define MOD_SEL0_30_29 FM(SEL_ADGB_0) FM(SEL_ADGB_1) FM(SEL_ADGB_2) F_(0, 0) +#define MOD_SEL0_30_29 REV4(FM(SEL_ADGB_0), FM(SEL_ADGB_1), FM(SEL_ADGB_2), F_(0, 0)) #define MOD_SEL0_28 FM(SEL_DRIF0_0) FM(SEL_DRIF0_1) -#define MOD_SEL0_27_26 FM(SEL_FM_0) FM(SEL_FM_1) FM(SEL_FM_2) F_(0, 0) +#define MOD_SEL0_27_26 REV4(FM(SEL_FM_0), FM(SEL_FM_1), FM(SEL_FM_2), F_(0, 0)) #define MOD_SEL0_25 FM(SEL_FSO_0) FM(SEL_FSO_1) #define MOD_SEL0_24 FM(SEL_HSCIF0_0) FM(SEL_HSCIF0_1) #define MOD_SEL0_23 FM(SEL_HSCIF1_0) FM(SEL_HSCIF1_1) #define MOD_SEL0_22 FM(SEL_HSCIF2_0) FM(SEL_HSCIF2_1) -#define MOD_SEL0_21_20 FM(SEL_I2C1_0) FM(SEL_I2C1_1) FM(SEL_I2C1_2) FM(SEL_I2C1_3) -#define MOD_SEL0_19_18_17 FM(SEL_I2C2_0) FM(SEL_I2C2_1) FM(SEL_I2C2_2) FM(SEL_I2C2_3) FM(SEL_I2C2_4) F_(0, 0) F_(0, 0) F_(0, 0) +#define MOD_SEL0_21_20 REV4(FM(SEL_I2C1_0), FM(SEL_I2C1_1), FM(SEL_I2C1_2), FM(SEL_I2C1_3)) +#define MOD_SEL0_19_18_17 REV8(FM(SEL_I2C2_0), FM(SEL_I2C2_1), FM(SEL_I2C2_2), FM(SEL_I2C2_3), FM(SEL_I2C2_4), F_(0, 0), F_(0, 0), F_(0, 0)) #define MOD_SEL0_16 FM(SEL_NDFC_0) FM(SEL_NDFC_1) #define MOD_SEL0_15 FM(SEL_PWM0_0) FM(SEL_PWM0_1) #define MOD_SEL0_14 FM(SEL_PWM1_0) FM(SEL_PWM1_1) -#define MOD_SEL0_13_12 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0) -#define MOD_SEL0_11_10 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0) +#define MOD_SEL0_13_12 REV4(FM(SEL_PWM2_0), FM(SEL_PWM2_1), FM(SEL_PWM2_2), F_(0, 0)) +#define MOD_SEL0_11_10 REV4(FM(SEL_PWM3_0), FM(SEL_PWM3_1), FM(SEL_PWM3_2), F_(0, 0)) #define MOD_SEL0_9 FM(SEL_PWM4_0) FM(SEL_PWM4_1) #define MOD_SEL0_8 FM(SEL_PWM5_0) FM(SEL_PWM5_1) #define MOD_SEL0_7 FM(SEL_PWM6_0) FM(SEL_PWM6_1) -#define MOD_SEL0_6_5 FM(SEL_REMOCON_0) FM(SEL_REMOCON_1) FM(SEL_REMOCON_2) F_(0, 0) +#define MOD_SEL0_6_5 REV4(FM(SEL_REMOCON_0), FM(SEL_REMOCON_1), FM(SEL_REMOCON_2), F_(0, 0)) #define MOD_SEL0_4 FM(SEL_SCIF_0) FM(SEL_SCIF_1) #define MOD_SEL0_3 FM(SEL_SCIF0_0) FM(SEL_SCIF0_1) #define MOD_SEL0_2 FM(SEL_SCIF2_0) FM(SEL_SCIF2_1) -#define MOD_SEL0_1_0 FM(SEL_SPEED_PULSE_IF_0) FM(SEL_SPEED_PULSE_IF_1) FM(SEL_SPEED_PULSE_IF_2) F_(0, 0) +#define MOD_SEL0_1_0 REV4(FM(SEL_SPEED_PULSE_IF_0), FM(SEL_SPEED_PULSE_IF_1), FM(SEL_SPEED_PULSE_IF_2), F_(0, 0)) /* MOD_SEL1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ #define MOD_SEL1_31 FM(SEL_SIMCARD_0) FM(SEL_SIMCARD_1) @@ -422,18 +426,18 @@ FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM #define MOD_SEL1_28 FM(SEL_USB_20_CH0_0) FM(SEL_USB_20_CH0_1) #define MOD_SEL1_26 FM(SEL_DRIF2_0) FM(SEL_DRIF2_1) #define MOD_SEL1_25 FM(SEL_DRIF3_0) FM(SEL_DRIF3_1) -#define MOD_SEL1_24_23_22 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) FM(SEL_HSCIF3_2) FM(SEL_HSCIF3_3) FM(SEL_HSCIF3_4) F_(0, 0) F_(0, 0) F_(0, 0) -#define MOD_SEL1_21_20_19 FM(SEL_HSCIF4_0) FM(SEL_HSCIF4_1) FM(SEL_HSCIF4_2) FM(SEL_HSCIF4_3) FM(SEL_HSCIF4_4) F_(0, 0) F_(0, 0) F_(0, 0) +#define MOD_SEL1_24_23_22 REV8(FM(SEL_HSCIF3_0), FM(SEL_HSCIF3_1), FM(SEL_HSCIF3_2), FM(SEL_HSCIF3_3), FM(SEL_HSCIF3_4), F_(0, 0), F_(0, 0), F_(0, 0)) +#define MOD_SEL1_21_20_19 REV8(FM(SEL_HSCIF4_0), FM(SEL_HSCIF4_1), FM(SEL_HSCIF4_2), FM(SEL_HSCIF4_3), FM(SEL_HSCIF4_4), F_(0, 0), F_(0, 0), F_(0, 0)) #define MOD_SEL1_18 FM(SEL_I2C6_0) FM(SEL_I2C6_1) #define MOD_SEL1_17 FM(SEL_I2C7_0) FM(SEL_I2C7_1) #define MOD_SEL1_16 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1) #define MOD_SEL1_15 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1) -#define MOD_SEL1_14_13 FM(SEL_SCIF3_0) FM(SEL_SCIF3_1) FM(SEL_SCIF3_2) F_(0, 0) -#define MOD_SEL1_12_11 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) FM(SEL_SCIF4_2) F_(0, 0) -#define MOD_SEL1_10_9 FM(SEL_SCIF5_0) FM(SEL_SCIF5_1) FM(SEL_SCIF5_2) F_(0, 0) +#define MOD_SEL1_14_13 REV4(FM(SEL_SCIF3_0), FM(SEL_SCIF3_1), FM(SEL_SCIF3_2), F_(0, 0)) +#define MOD_SEL1_12_11 REV4(FM(SEL_SCIF4_0), FM(SEL_SCIF4_1), FM(SEL_SCIF4_2), F_(0, 0)) +#define MOD_SEL1_10_9 REV4(FM(SEL_SCIF5_0), FM(SEL_SCIF5_1), FM(SEL_SCIF5_2), F_(0, 0)) #define MOD_SEL1_8 FM(SEL_VIN4_0) FM(SEL_VIN4_1) #define MOD_SEL1_7 FM(SEL_VIN5_0) FM(SEL_VIN5_1) -#define MOD_SEL1_6_5 FM(SEL_ADGC_0) FM(SEL_ADGC_1) FM(SEL_ADGC_2) F_(0, 0) +#define MOD_SEL1_6_5 REV4(FM(SEL_ADGC_0), FM(SEL_ADGC_1), FM(SEL_ADGC_2), F_(0, 0)) #define MOD_SEL1_4 FM(SEL_SSI9_0) FM(SEL_SSI9_1) #define PINMUX_MOD_SELS \ From 5219aa33caec2f7b68eda2b7e4ab8e276f323254 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Wed, 12 Dec 2018 19:19:35 +0900 Subject: [PATCH 0461/4277] pinctrl: sh-pfc: r8a77995: Fix MOD_SEL bit numbering MOD_SEL register bit numbering was different from R-Car D3 SoC and R-Car H3/M3-[WN] SoCs. MOD_SEL 1-bit H3/M3-[WN] D3 =============== ========== ===== Set Value = H'0 b'0 b'0 Set Value = H'1 b'1 b'1 MOD_SEL 2-bits H3/M3-[WN] D3 =============== ========== ===== Set Value = H'0 b'00 b'00 Set Value = H'1 b'01 b'10 Set Value = H'2 b'10 b'01 Set Value = H'3 b'11 b'11 MOD_SEL 3-bits H3/M3-[WN] D3 =============== ========== ===== Set Value = H'0 b'000 b'000 Set Value = H'1 b'001 b'100 Set Value = H'2 b'010 b'010 Set Value = H'3 b'011 b'110 Set Value = H'4 b'100 b'001 Set Value = H'5 b'101 b'101 Set Value = H'6 b'110 b'011 Set Value = H'7 b'111 b'111 This patch replaces the #define name and value of MOD_SEL. Signed-off-by: Takeshi Kihara Fixes: 794a67117646 ("pinctrl: sh-pfc: Initial R8A77995 PFC support") [shimoda: split a patch per SoC and revise the commit log] Signed-off-by: Yoshihiro Shimoda [geert: Use a macro to do the actual reordering] Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a77995.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c index 84d78db381e3..9e377e3b9cb3 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c @@ -381,6 +381,9 @@ FM(IP12_23_20) IP12_23_20 \ FM(IP12_27_24) IP12_27_24 \ FM(IP12_31_28) IP12_31_28 \ +/* The bit numbering in MOD_SEL fields is reversed */ +#define REV4(f0, f1, f2, f3) f0 f2 f1 f3 + /* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ #define MOD_SEL0_30 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1) #define MOD_SEL0_29 FM(SEL_I2C3_0) FM(SEL_I2C3_1) @@ -388,10 +391,10 @@ FM(IP12_31_28) IP12_31_28 \ #define MOD_SEL0_27 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1) #define MOD_SEL0_26 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) #define MOD_SEL0_25 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) -#define MOD_SEL0_24_23 FM(SEL_PWM0_0) FM(SEL_PWM0_1) FM(SEL_PWM0_2) F_(0, 0) -#define MOD_SEL0_22_21 FM(SEL_PWM1_0) FM(SEL_PWM1_1) FM(SEL_PWM1_2) F_(0, 0) -#define MOD_SEL0_20_19 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0) -#define MOD_SEL0_18_17 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0) +#define MOD_SEL0_24_23 REV4(FM(SEL_PWM0_0), FM(SEL_PWM0_1), FM(SEL_PWM0_2), F_(0, 0)) +#define MOD_SEL0_22_21 REV4(FM(SEL_PWM1_0), FM(SEL_PWM1_1), FM(SEL_PWM1_2), F_(0, 0)) +#define MOD_SEL0_20_19 REV4(FM(SEL_PWM2_0), FM(SEL_PWM2_1), FM(SEL_PWM2_2), F_(0, 0)) +#define MOD_SEL0_18_17 REV4(FM(SEL_PWM3_0), FM(SEL_PWM3_1), FM(SEL_PWM3_2), F_(0, 0)) #define MOD_SEL0_15 FM(SEL_IRQ_0_0) FM(SEL_IRQ_0_1) #define MOD_SEL0_14 FM(SEL_IRQ_1_0) FM(SEL_IRQ_1_1) #define MOD_SEL0_13 FM(SEL_IRQ_2_0) FM(SEL_IRQ_2_1) From f7d8b568e204d295bf0e780cea10cab1f84be31e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 26 Dec 2018 09:19:34 +0100 Subject: [PATCH 0462/4277] pinctrl: sh-pfc: r8a77990: GP6_9 does not have pull-down capability Hence remove the SH_PFC_PIN_CFG_PULL_DOWN flag from the GP6_9 GPIO description. Fixes: 83f6941a42a5e773 ("pinctrl: sh-pfc: r8a77990: Add bias pinconf support") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index 821a425e285a..33c154357262 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -30,7 +30,16 @@ PORT_GP_CFG_1(3, 15, fn, sfx, CFG_FLAGS), \ PORT_GP_CFG_11(4, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE), \ PORT_GP_CFG_20(5, fn, sfx, CFG_FLAGS), \ - PORT_GP_CFG_18(6, fn, sfx, CFG_FLAGS) + PORT_GP_CFG_9(6, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 9, fn, sfx, SH_PFC_PIN_CFG_PULL_UP), \ + PORT_GP_CFG_1(6, 10, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 11, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 12, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 13, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 14, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 15, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 16, fn, sfx, CFG_FLAGS), \ + PORT_GP_CFG_1(6, 17, fn, sfx, CFG_FLAGS) /* * F_() : just information * FM() : macro for FN_xxx / xxx_MARK From 85ccae133bde425bce8ebe0a5f64e41749a1885a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 26 Dec 2018 09:21:07 +0100 Subject: [PATCH 0463/4277] Revert "pinctrl: sh-pfc: r8a77990: Add support for pull-up only pins" This reverts commit f4caa6ee736fa8a46c806ba4382c16f7e5a20248. The same can be expressed better by dropping the SH_PFC_PIN_CFG_PULL_DOWN flag from the GPIO description, as it includes returning an error to the caller when trying to configure the pin for pull-down, causing: sh-pfc e6060000.pin-controller: pin_config_set op failed for pin 201 sh-pfc e6060000.pin-controller: Error applying setting, reverse things back sh-pfc e6060000.pin-controller: failed to select default state Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index 33c154357262..3ce1b24a9d6e 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -4927,17 +4927,6 @@ static const struct pinmux_bias_reg pinmux_bias_regs[] = { { /* sentinel */ }, }; -static bool pin_has_pud(unsigned int pin) -{ - /* Some pins are pull-up only */ - switch (pin) { - case RCAR_GP_PIN(6, 9): /* USB30_OVC */ - return false; - } - - return true; -} - static unsigned int r8a77990_pinmux_get_bias(struct sh_pfc *pfc, unsigned int pin) { @@ -4950,7 +4939,7 @@ static unsigned int r8a77990_pinmux_get_bias(struct sh_pfc *pfc, if (!(sh_pfc_read(pfc, reg->puen) & BIT(bit))) return PIN_CONFIG_BIAS_DISABLE; - else if (!pin_has_pud(pin) || (sh_pfc_read(pfc, reg->pud) & BIT(bit))) + else if (sh_pfc_read(pfc, reg->pud) & BIT(bit)) return PIN_CONFIG_BIAS_PULL_UP; else return PIN_CONFIG_BIAS_PULL_DOWN; @@ -4971,13 +4960,11 @@ static void r8a77990_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin, if (bias != PIN_CONFIG_BIAS_DISABLE) enable |= BIT(bit); - if (pin_has_pud(pin)) { - updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit); - if (bias == PIN_CONFIG_BIAS_PULL_UP) - updown |= BIT(bit); + updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit); + if (bias == PIN_CONFIG_BIAS_PULL_UP) + updown |= BIT(bit); - sh_pfc_write(pfc, reg->pud, updown); - } + sh_pfc_write(pfc, reg->pud, updown); sh_pfc_write(pfc, reg->puen, enable); } From d2ccdc11fd7e3b2fbcd1c76ffd24e07c59e21058 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 26 Dec 2018 09:23:27 +0100 Subject: [PATCH 0464/4277] pinctrl: sh-pfc: Print pin group when debugging Knowing which pin group is being configured is useful information when debugging pin configuration. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pinctrl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 274d5ff87078..c97d2ba7677c 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -347,6 +347,8 @@ static int sh_pfc_func_set_mux(struct pinctrl_dev *pctldev, unsigned selector, unsigned int i; int ret = 0; + dev_dbg(pctldev->dev, "Configuring pin group %s\n", grp->name); + spin_lock_irqsave(&pfc->lock, flags); for (i = 0; i < grp->nr_pins; ++i) { From 95c2d0efa0410831bf3aba76f42fe99cb0a27d92 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 7 Jan 2019 23:13:19 +0100 Subject: [PATCH 0465/4277] pinctrl: sh-pfc: r8a7790: Initialize TDSEL register for ES1.0 Documentation for ES1.0 says that some bits in TDSEL must be set (ch 5.3.39 in R-Car H2 v0.91). However, the reset value of the register is 0, so software has to do it. Add this to the kernel driver to ensure this is really done independent of firmware versions and use whitelisting for ES versions known to need this. This is needed for some SD cards supporting SDR104 transfer mode. For me, TDSEL was not initialized by the firmware and I had problems with the card when re-inserting it. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/sh-pfc/pfc-r8a7790.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index ab7a35392cd8..a84229cb8cd4 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -10,7 +10,9 @@ #include #include +#include +#include "core.h" #include "sh_pfc.h" /* @@ -5691,7 +5693,22 @@ static int r8a7790_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *poc return 31 - (pin & 0x1f); } +static const struct soc_device_attribute r8a7790_tdsel[] = { + { .soc_id = "r8a7790", .revision = "ES1.0" }, + { /* sentinel */ } +}; + +static int r8a7790_pinmux_soc_init(struct sh_pfc *pfc) +{ + /* Initialize TDSEL on old revisions */ + if (soc_device_match(r8a7790_tdsel)) + sh_pfc_write(pfc, 0xe6060088, 0x00155554); + + return 0; +} + static const struct sh_pfc_soc_operations r8a7790_pinmux_ops = { + .init = r8a7790_pinmux_soc_init, .pin_to_pocctrl = r8a7790_pin_to_pocctrl, }; From c54734e831d5080d8112162b77bf24773b665b02 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 7 Jan 2019 23:13:20 +0100 Subject: [PATCH 0466/4277] pinctrl: sh-pfc: r8a7794: Initialize TDSEL register for ES1.0 Documentation for ES1.0 says that some bits in TDSEL must be set (ch 5.3.35 in R-Car E2 v0.5). However, the reset value of the register is 0, so software has to do it. Add this to the kernel driver to ensure this is really done independent of firmware versions and use whitelisting for ES versions known to need this. This is needed for some SD cards supporting SDR104 transfer mode. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/sh-pfc/pfc-r8a7794.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c index fcf1339c4058..958a5f714c93 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c @@ -8,6 +8,7 @@ */ #include +#include #include "core.h" #include "sh_pfc.h" @@ -5560,7 +5561,22 @@ static int r8a7794_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *poc return -EINVAL; } +static const struct soc_device_attribute r8a7794_tdsel[] = { + { .soc_id = "r8a7794", .revision = "ES1.0" }, + { /* sentinel */ } +}; + +static int r8a7794_pinmux_soc_init(struct sh_pfc *pfc) +{ + /* Initialize TDSEL on old revisions */ + if (soc_device_match(r8a7794_tdsel)) + sh_pfc_write(pfc, 0xe6060068, 0x55555500); + + return 0; +} + static const struct sh_pfc_soc_operations r8a7794_pinmux_ops = { + .init = r8a7794_pinmux_soc_init, .pin_to_pocctrl = r8a7794_pin_to_pocctrl, }; From 99fdb920f5534d1c2f4b30c12eca4224ea260198 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jan 2019 15:51:22 +0100 Subject: [PATCH 0467/4277] pinctrl: sh-pfc: r8a7795: Deduplicate VIN5 pin definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use union vin_data16 and VIN_DATA_PIN_GROUP() to reduce redundancies in pin definitions for the VIN5 channel. This reduces kernel size by 240 bytes. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jacopo Mondi Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund --- drivers/pinctrl/sh-pfc/pfc-r8a7795.c | 90 ++++++++-------------------- 1 file changed, 26 insertions(+), 64 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c index 01105bb83598..db9add1405c5 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c @@ -4098,67 +4098,29 @@ static const unsigned int vin4_clk_mux[] = { }; /* - VIN5 ------------------------------------------------------------------- */ -static const unsigned int vin5_data8_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), +static const union vin_data16 vin5_data_pins = { + .data16 = { + RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), + RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), + RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), + RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), + RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), + RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), + RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), + }, }; -static const unsigned int vin5_data8_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, -}; -static const unsigned int vin5_data10_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), -}; -static const unsigned int vin5_data10_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, -}; -static const unsigned int vin5_data12_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), -}; -static const unsigned int vin5_data12_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, -}; -static const unsigned int vin5_data16_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), - RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), - RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), -}; -static const unsigned int vin5_data16_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, - VI5_DATA12_MARK, VI5_DATA13_MARK, - VI5_DATA14_MARK, VI5_DATA15_MARK, +static const union vin_data16 vin5_data_mux = { + .data16 = { + VI5_DATA0_MARK, VI5_DATA1_MARK, + VI5_DATA2_MARK, VI5_DATA3_MARK, + VI5_DATA4_MARK, VI5_DATA5_MARK, + VI5_DATA6_MARK, VI5_DATA7_MARK, + VI5_DATA8_MARK, VI5_DATA9_MARK, + VI5_DATA10_MARK, VI5_DATA11_MARK, + VI5_DATA12_MARK, VI5_DATA13_MARK, + VI5_DATA14_MARK, VI5_DATA15_MARK, + }, }; static const unsigned int vin5_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -4530,10 +4492,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(vin4_field), SH_PFC_PIN_GROUP(vin4_clkenb), SH_PFC_PIN_GROUP(vin4_clk), - SH_PFC_PIN_GROUP(vin5_data8), - SH_PFC_PIN_GROUP(vin5_data10), - SH_PFC_PIN_GROUP(vin5_data12), - SH_PFC_PIN_GROUP(vin5_data16), + VIN_DATA_PIN_GROUP(vin5_data, 8), + VIN_DATA_PIN_GROUP(vin5_data, 10), + VIN_DATA_PIN_GROUP(vin5_data, 12), + VIN_DATA_PIN_GROUP(vin5_data, 16), SH_PFC_PIN_GROUP(vin5_sync), SH_PFC_PIN_GROUP(vin5_field), SH_PFC_PIN_GROUP(vin5_clkenb), From 08b7e2112a9b19ccc583617cd81714d0e98b7e79 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jan 2019 15:55:28 +0100 Subject: [PATCH 0468/4277] pinctrl: sh-pfc: r8a7796: Deduplicate VIN5 pin definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use union vin_data16 and VIN_DATA_PIN_GROUP() to reduce redundancies in pin definitions for the VIN5 channel. This reduces kernel size by 240 bytes. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jacopo Mondi Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund --- drivers/pinctrl/sh-pfc/pfc-r8a7796.c | 90 ++++++++-------------------- 1 file changed, 26 insertions(+), 64 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c index 4b98ad8d93d9..72348a4f2ece 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c @@ -4070,67 +4070,29 @@ static const unsigned int vin4_clk_mux[] = { }; /* - VIN5 ------------------------------------------------------------------- */ -static const unsigned int vin5_data8_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), +static const union vin_data16 vin5_data_pins = { + .data16 = { + RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), + RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), + RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), + RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), + RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), + RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), + RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), + }, }; -static const unsigned int vin5_data8_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, -}; -static const unsigned int vin5_data10_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), -}; -static const unsigned int vin5_data10_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, -}; -static const unsigned int vin5_data12_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), -}; -static const unsigned int vin5_data12_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, -}; -static const unsigned int vin5_data16_pins[] = { - RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1), - RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3), - RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5), - RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7), - RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13), - RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15), - RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), - RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), -}; -static const unsigned int vin5_data16_mux[] = { - VI5_DATA0_MARK, VI5_DATA1_MARK, - VI5_DATA2_MARK, VI5_DATA3_MARK, - VI5_DATA4_MARK, VI5_DATA5_MARK, - VI5_DATA6_MARK, VI5_DATA7_MARK, - VI5_DATA8_MARK, VI5_DATA9_MARK, - VI5_DATA10_MARK, VI5_DATA11_MARK, - VI5_DATA12_MARK, VI5_DATA13_MARK, - VI5_DATA14_MARK, VI5_DATA15_MARK, +static const union vin_data16 vin5_data_mux = { + .data16 = { + VI5_DATA0_MARK, VI5_DATA1_MARK, + VI5_DATA2_MARK, VI5_DATA3_MARK, + VI5_DATA4_MARK, VI5_DATA5_MARK, + VI5_DATA6_MARK, VI5_DATA7_MARK, + VI5_DATA8_MARK, VI5_DATA9_MARK, + VI5_DATA10_MARK, VI5_DATA11_MARK, + VI5_DATA12_MARK, VI5_DATA13_MARK, + VI5_DATA14_MARK, VI5_DATA15_MARK, + }, }; static const unsigned int vin5_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -4468,10 +4430,10 @@ static const struct { SH_PFC_PIN_GROUP(vin4_field), SH_PFC_PIN_GROUP(vin4_clkenb), SH_PFC_PIN_GROUP(vin4_clk), - SH_PFC_PIN_GROUP(vin5_data8), - SH_PFC_PIN_GROUP(vin5_data10), - SH_PFC_PIN_GROUP(vin5_data12), - SH_PFC_PIN_GROUP(vin5_data16), + VIN_DATA_PIN_GROUP(vin5_data, 8), + VIN_DATA_PIN_GROUP(vin5_data, 10), + VIN_DATA_PIN_GROUP(vin5_data, 12), + VIN_DATA_PIN_GROUP(vin5_data, 16), SH_PFC_PIN_GROUP(vin5_sync), SH_PFC_PIN_GROUP(vin5_field), SH_PFC_PIN_GROUP(vin5_clkenb), From 81c585c96b7dd47caede1bf6d2920a81498385dc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jan 2019 15:56:12 +0100 Subject: [PATCH 0469/4277] pinctrl: sh-pfc: r8a77970: Deduplicate VIN[01] pin definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use union vin_data12 and VIN_DATA_PIN_GROUP() to reduce redundancies in pin definitions for the VIN0 and VIN1 channels. This reduces kernel size by 288 bytes. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jacopo Mondi Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund --- drivers/pinctrl/sh-pfc/pfc-r8a77970.c | 128 +++++++++----------------- 1 file changed, 42 insertions(+), 86 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c index 4a7ab84b366b..c5e67ba29f7c 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c @@ -1578,47 +1578,25 @@ static const unsigned int tmu_tclk2_b_mux[] = { }; /* - VIN0 ------------------------------------------------------------------- */ -static const unsigned int vin0_data8_pins[] = { - RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), - RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), - RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), - RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), +static const union vin_data12 vin0_data_pins = { + .data12 = { + RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), + RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), + RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), + RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), + RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15), + }, }; -static const unsigned int vin0_data8_mux[] = { - VI0_DATA0_MARK, VI0_DATA1_MARK, - VI0_DATA2_MARK, VI0_DATA3_MARK, - VI0_DATA4_MARK, VI0_DATA5_MARK, - VI0_DATA6_MARK, VI0_DATA7_MARK, -}; -static const unsigned int vin0_data10_pins[] = { - RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), - RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), - RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), - RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), - RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), -}; -static const unsigned int vin0_data10_mux[] = { - VI0_DATA0_MARK, VI0_DATA1_MARK, - VI0_DATA2_MARK, VI0_DATA3_MARK, - VI0_DATA4_MARK, VI0_DATA5_MARK, - VI0_DATA6_MARK, VI0_DATA7_MARK, - VI0_DATA8_MARK, VI0_DATA9_MARK, -}; -static const unsigned int vin0_data12_pins[] = { - RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), - RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), - RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), - RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), - RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13), - RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15), -}; -static const unsigned int vin0_data12_mux[] = { - VI0_DATA0_MARK, VI0_DATA1_MARK, - VI0_DATA2_MARK, VI0_DATA3_MARK, - VI0_DATA4_MARK, VI0_DATA5_MARK, - VI0_DATA6_MARK, VI0_DATA7_MARK, - VI0_DATA8_MARK, VI0_DATA9_MARK, - VI0_DATA10_MARK, VI0_DATA11_MARK, +static const union vin_data12 vin0_data_mux = { + .data12 = { + VI0_DATA0_MARK, VI0_DATA1_MARK, + VI0_DATA2_MARK, VI0_DATA3_MARK, + VI0_DATA4_MARK, VI0_DATA5_MARK, + VI0_DATA6_MARK, VI0_DATA7_MARK, + VI0_DATA8_MARK, VI0_DATA9_MARK, + VI0_DATA10_MARK, VI0_DATA11_MARK, + }, }; static const unsigned int vin0_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -1650,47 +1628,25 @@ static const unsigned int vin0_clk_mux[] = { }; /* - VIN1 ------------------------------------------------------------------- */ -static const unsigned int vin1_data8_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), +static const union vin_data12 vin1_data_pins = { + .data12 = { + RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), + RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), + RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), + RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), + }, }; -static const unsigned int vin1_data8_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, -}; -static const unsigned int vin1_data10_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), -}; -static const unsigned int vin1_data10_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, -}; -static const unsigned int vin1_data12_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), - RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), -}; -static const unsigned int vin1_data12_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, - VI1_DATA10_MARK, VI1_DATA11_MARK, +static const union vin_data12 vin1_data_mux = { + .data12 = { + VI1_DATA0_MARK, VI1_DATA1_MARK, + VI1_DATA2_MARK, VI1_DATA3_MARK, + VI1_DATA4_MARK, VI1_DATA5_MARK, + VI1_DATA6_MARK, VI1_DATA7_MARK, + VI1_DATA8_MARK, VI1_DATA9_MARK, + VI1_DATA10_MARK, VI1_DATA11_MARK, + }, }; static const unsigned int vin1_sync_pins[] = { /* HSYNC#, VSYNC# */ @@ -1831,16 +1787,16 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(tmu_tclk1_b), SH_PFC_PIN_GROUP(tmu_tclk2_a), SH_PFC_PIN_GROUP(tmu_tclk2_b), - SH_PFC_PIN_GROUP(vin0_data8), - SH_PFC_PIN_GROUP(vin0_data10), - SH_PFC_PIN_GROUP(vin0_data12), + VIN_DATA_PIN_GROUP(vin0_data, 8), + VIN_DATA_PIN_GROUP(vin0_data, 10), + VIN_DATA_PIN_GROUP(vin0_data, 12), SH_PFC_PIN_GROUP(vin0_sync), SH_PFC_PIN_GROUP(vin0_field), SH_PFC_PIN_GROUP(vin0_clkenb), SH_PFC_PIN_GROUP(vin0_clk), - SH_PFC_PIN_GROUP(vin1_data8), - SH_PFC_PIN_GROUP(vin1_data10), - SH_PFC_PIN_GROUP(vin1_data12), + VIN_DATA_PIN_GROUP(vin1_data, 8), + VIN_DATA_PIN_GROUP(vin1_data, 10), + VIN_DATA_PIN_GROUP(vin1_data, 12), SH_PFC_PIN_GROUP(vin1_sync), SH_PFC_PIN_GROUP(vin1_field), SH_PFC_PIN_GROUP(vin1_clkenb), From 9dd1731306d90f08d726987d3d7e39ba6e23a015 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jan 2019 15:57:57 +0100 Subject: [PATCH 0470/4277] pinctrl: sh-pfc: r8a77980: Deduplicate VIN1 pin definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use union vin_data12 and VIN_DATA_PIN_GROUP() to reduce redundancies in pin definitions for the VIN1 channel. This reduces kernel size by 144 bytes. Signed-off-by: Geert Uytterhoeven Reviewed-by: Jacopo Mondi Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund --- drivers/pinctrl/sh-pfc/pfc-r8a77980.c | 64 +++++++++------------------ 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c index 8bef24502f0c..b807b67ae143 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c @@ -1970,47 +1970,25 @@ static const unsigned int vin0_clk_mux[] = { }; /* - VIN1 ------------------------------------------------------------------- */ -static const unsigned int vin1_data8_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), +static const union vin_data12 vin1_data_pins = { + .data12 = { + RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), + RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), + RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), + RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), + RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), + }, }; -static const unsigned int vin1_data8_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, -}; -static const unsigned int vin1_data10_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), -}; -static const unsigned int vin1_data10_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, -}; -static const unsigned int vin1_data12_pins[] = { - RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5), - RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7), - RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), - RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), - RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13), - RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15), -}; -static const unsigned int vin1_data12_mux[] = { - VI1_DATA0_MARK, VI1_DATA1_MARK, - VI1_DATA2_MARK, VI1_DATA3_MARK, - VI1_DATA4_MARK, VI1_DATA5_MARK, - VI1_DATA6_MARK, VI1_DATA7_MARK, - VI1_DATA8_MARK, VI1_DATA9_MARK, - VI1_DATA10_MARK, VI1_DATA11_MARK, +static const union vin_data12 vin1_data_mux = { + .data12 = { + VI1_DATA0_MARK, VI1_DATA1_MARK, + VI1_DATA2_MARK, VI1_DATA3_MARK, + VI1_DATA4_MARK, VI1_DATA5_MARK, + VI1_DATA6_MARK, VI1_DATA7_MARK, + VI1_DATA8_MARK, VI1_DATA9_MARK, + VI1_DATA10_MARK, VI1_DATA11_MARK, + }, }; static const unsigned int vin1_sync_pins[] = { /* VI1_VSYNC#, VI1_HSYNC# */ @@ -2182,9 +2160,9 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(vin0_field), SH_PFC_PIN_GROUP(vin0_clkenb), SH_PFC_PIN_GROUP(vin0_clk), - SH_PFC_PIN_GROUP(vin1_data8), - SH_PFC_PIN_GROUP(vin1_data10), - SH_PFC_PIN_GROUP(vin1_data12), + VIN_DATA_PIN_GROUP(vin1_data, 8), + VIN_DATA_PIN_GROUP(vin1_data, 10), + VIN_DATA_PIN_GROUP(vin1_data, 12), SH_PFC_PIN_GROUP(vin1_sync), SH_PFC_PIN_GROUP(vin1_field), SH_PFC_PIN_GROUP(vin1_clkenb), From b8ba194ca5f4ca238b05e1d1579404d4ff3de522 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jan 2019 16:00:21 +0100 Subject: [PATCH 0471/4277] pinctrl: sh-pfc: r8a7791: Fix VIN1 versioned groups The naming of the "b" versions of the VIN1 pin groups is a bit odd, in that the "_b" appears in the middle of the names, instead of as a suffix. Increase consistency with other SoCs by making R-Car M2-W and M2-N, and RZ/G1M and RZ/G1N, use the recently added optional "version" argument of the VIN_DATA_PIN_GROUP() macro. Note that this breaks backwards compatibility with existing DTBs, but there are no upstream users of the "vin1_b_*" names. Fixes: 8e32c9671f84acd8 ("pinctrl: sh-pfc: r8a7791: Add VIN pins") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a7791.c | 68 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c index 2859231aaffc..bd363f2e196f 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -4317,7 +4317,7 @@ static const unsigned int vin1_clk_pins[] = { static const unsigned int vin1_clk_mux[] = { VI1_CLK_MARK, }; -static const union vin_data vin1_b_data_pins = { +static const union vin_data vin1_data_b_pins = { .data24 = { /* B */ RCAR_GP_PIN(3, 0), RCAR_GP_PIN(3, 1), @@ -4336,7 +4336,7 @@ static const union vin_data vin1_b_data_pins = { RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20), }, }; -static const union vin_data vin1_b_data_mux = { +static const union vin_data vin1_data_b_mux = { .data24 = { /* B */ VI1_DATA0_B_MARK, VI1_DATA1_B_MARK, @@ -4355,7 +4355,7 @@ static const union vin_data vin1_b_data_mux = { VI1_R6_B_MARK, VI1_R7_B_MARK, }, }; -static const unsigned int vin1_b_data18_pins[] = { +static const unsigned int vin1_data18_b_pins[] = { /* B */ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11), @@ -4369,7 +4369,7 @@ static const unsigned int vin1_b_data18_pins[] = { RCAR_GP_PIN(2, 17), RCAR_GP_PIN(2, 18), RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20), }; -static const unsigned int vin1_b_data18_mux[] = { +static const unsigned int vin1_data18_b_mux[] = { /* B */ VI1_DATA2_B_MARK, VI1_DATA3_B_MARK, VI1_DATA4_B_MARK, VI1_DATA5_B_MARK, @@ -4383,30 +4383,30 @@ static const unsigned int vin1_b_data18_mux[] = { VI1_R4_B_MARK, VI1_R5_B_MARK, VI1_R6_B_MARK, VI1_R7_B_MARK, }; -static const unsigned int vin1_b_sync_pins[] = { +static const unsigned int vin1_sync_b_pins[] = { RCAR_GP_PIN(3, 17), /* HSYNC */ RCAR_GP_PIN(3, 18), /* VSYNC */ }; -static const unsigned int vin1_b_sync_mux[] = { +static const unsigned int vin1_sync_b_mux[] = { VI1_HSYNC_N_B_MARK, VI1_VSYNC_N_B_MARK, }; -static const unsigned int vin1_b_field_pins[] = { +static const unsigned int vin1_field_b_pins[] = { RCAR_GP_PIN(3, 20), }; -static const unsigned int vin1_b_field_mux[] = { +static const unsigned int vin1_field_b_mux[] = { VI1_FIELD_B_MARK, }; -static const unsigned int vin1_b_clkenb_pins[] = { +static const unsigned int vin1_clkenb_b_pins[] = { RCAR_GP_PIN(3, 19), }; -static const unsigned int vin1_b_clkenb_mux[] = { +static const unsigned int vin1_clkenb_b_mux[] = { VI1_CLKENB_B_MARK, }; -static const unsigned int vin1_b_clk_pins[] = { +static const unsigned int vin1_clk_b_pins[] = { RCAR_GP_PIN(3, 16), }; -static const unsigned int vin1_b_clk_mux[] = { +static const unsigned int vin1_clk_b_mux[] = { VI1_CLK_B_MARK, }; /* - VIN2 ----------------------------------------------------------------- */ @@ -4784,17 +4784,17 @@ static const struct { SH_PFC_PIN_GROUP(vin1_field), SH_PFC_PIN_GROUP(vin1_clkenb), SH_PFC_PIN_GROUP(vin1_clk), - VIN_DATA_PIN_GROUP(vin1_b_data, 24), - VIN_DATA_PIN_GROUP(vin1_b_data, 20), - SH_PFC_PIN_GROUP(vin1_b_data18), - VIN_DATA_PIN_GROUP(vin1_b_data, 16), - VIN_DATA_PIN_GROUP(vin1_b_data, 12), - VIN_DATA_PIN_GROUP(vin1_b_data, 10), - VIN_DATA_PIN_GROUP(vin1_b_data, 8), - SH_PFC_PIN_GROUP(vin1_b_sync), - SH_PFC_PIN_GROUP(vin1_b_field), - SH_PFC_PIN_GROUP(vin1_b_clkenb), - SH_PFC_PIN_GROUP(vin1_b_clk), + VIN_DATA_PIN_GROUP(vin1_data, 24, _b), + VIN_DATA_PIN_GROUP(vin1_data, 20, _b), + SH_PFC_PIN_GROUP(vin1_data18_b), + VIN_DATA_PIN_GROUP(vin1_data, 16, _b), + VIN_DATA_PIN_GROUP(vin1_data, 12, _b), + VIN_DATA_PIN_GROUP(vin1_data, 10, _b), + VIN_DATA_PIN_GROUP(vin1_data, 8, _b), + SH_PFC_PIN_GROUP(vin1_sync_b), + SH_PFC_PIN_GROUP(vin1_field_b), + SH_PFC_PIN_GROUP(vin1_clkenb_b), + SH_PFC_PIN_GROUP(vin1_clk_b), SH_PFC_PIN_GROUP(vin2_data8), SH_PFC_PIN_GROUP(vin2_sync), SH_PFC_PIN_GROUP(vin2_field), @@ -5335,17 +5335,17 @@ static const char * const vin1_groups[] = { "vin1_field", "vin1_clkenb", "vin1_clk", - "vin1_b_data24", - "vin1_b_data20", - "vin1_b_data18", - "vin1_b_data16", - "vin1_b_data12", - "vin1_b_data10", - "vin1_b_data8", - "vin1_b_sync", - "vin1_b_field", - "vin1_b_clkenb", - "vin1_b_clk", + "vin1_data24_b", + "vin1_data20_b", + "vin1_data18_b", + "vin1_data16_b", + "vin1_data12_b", + "vin1_data10_b", + "vin1_data8_b", + "vin1_sync_b", + "vin1_field_b", + "vin1_clkenb_b", + "vin1_clk_b", }; static const char * const vin2_groups[] = { From 86c045c2e4201e94ef37458e5777fcb2dab33a11 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 15 Jan 2019 10:55:19 +0100 Subject: [PATCH 0472/4277] pinctrl: sh-pfc: r8a77965: Replace DU_DOTCLKIN2 by DU_DOTCLKIN3 Unlike R-Car M3-W, R-Car M3-N does not have DU_DOTCLKIN2, but the corresponding pin carries the DU_DOTCLKIN3 signal. Correct all references to DU_DOTCLKIN2 to fix this. This change does not have any runtime effect, as it only changes an internal enum name, and a comment. Fixes: 490e687eb8b274b5 ("pinctrl: sh-pfc: Initial R-Car M3-N support") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a77965.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c index a7c4882de09e..d937b1bc396a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c @@ -554,7 +554,7 @@ MOD_SEL0_4_3 MOD_SEL1_4 \ FM(AVB_RX_CTL) FM(AVB_RXC) FM(AVB_RD0) FM(AVB_RD1) FM(AVB_RD2) FM(AVB_RD3) \ FM(AVB_TXCREFCLK) FM(AVB_MDIO) \ FM(PRESETOUT) \ - FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN2) \ + FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN3) \ FM(TMS) FM(TDO) FM(ASEBRK) FM(MLB_REF) FM(TDI) FM(TCK) FM(TRST) FM(EXTALR) enum { @@ -1566,7 +1566,7 @@ static const struct sh_pfc_pin pinmux_pins[] = { SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('E'), 5, QSPI1_MISO_IO1, CFG_FLAGS), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 7, DU_DOTCLKIN0, CFG_FLAGS), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 8, DU_DOTCLKIN1, CFG_FLAGS), - SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN2, CFG_FLAGS), + SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN3, CFG_FLAGS), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 26, TRST#, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 29, TDI, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN), SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 30, TMS, CFG_FLAGS), @@ -5740,7 +5740,7 @@ static const struct pinmux_bias_reg pinmux_bias_regs[] = { [31] = PIN_A_NUMBER('P', 8), /* DU_DOTCLKIN1 */ } }, { PINMUX_BIAS_REG("PUEN3", 0xe606040c, "PUD3", 0xe606044c) { - [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN2 */ + [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN3 */ [ 1] = PIN_NONE, [ 2] = PIN_A_NUMBER('D', 38), /* FSCLKST */ [ 3] = PIN_A_NUMBER('D', 39), /* EXTALR*/ From 16978e7d40f73bed462ca991ce3565d133b0c6cd Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Tue, 15 Jan 2019 21:01:27 +0900 Subject: [PATCH 0473/4277] pinctrl: sh-pfc: r8a77990: Add TMU pins, groups and functions This patch adds TMU TCLK{1,2} pins, groups and functions to the R8A77990 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Yoshihiro Kaneko Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 53 ++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index 3ce1b24a9d6e..0a9aa62f50bb 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -3256,6 +3256,43 @@ static const unsigned int ssi9_ctrl_b_mux[] = { SSI_SCK9_B_MARK, SSI_WS9_B_MARK, }; +/* - TMU -------------------------------------------------------------------- */ +static const unsigned int tmu_tclk1_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(3, 12), +}; + +static const unsigned int tmu_tclk1_a_mux[] = { + TCLK1_A_MARK, +}; + +static const unsigned int tmu_tclk1_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(5, 17), +}; + +static const unsigned int tmu_tclk1_b_mux[] = { + TCLK1_B_MARK, +}; + +static const unsigned int tmu_tclk2_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(3, 13), +}; + +static const unsigned int tmu_tclk2_a_mux[] = { + TCLK2_A_MARK, +}; + +static const unsigned int tmu_tclk2_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(5, 18), +}; + +static const unsigned int tmu_tclk2_b_mux[] = { + TCLK2_B_MARK, +}; + /* - USB0 ------------------------------------------------------------------- */ static const unsigned int usb0_a_pins[] = { /* PWEN, OVC */ @@ -3536,7 +3573,7 @@ static const unsigned int vin5_clk_b_mux[] = { }; static const struct { - struct sh_pfc_pin_group common[241]; + struct sh_pfc_pin_group common[245]; struct sh_pfc_pin_group automotive[2]; } pinmux_groups = { .common = { @@ -3748,6 +3785,10 @@ static const struct { SH_PFC_PIN_GROUP(ssi9_data), SH_PFC_PIN_GROUP(ssi9_ctrl_a), SH_PFC_PIN_GROUP(ssi9_ctrl_b), + SH_PFC_PIN_GROUP(tmu_tclk1_a), + SH_PFC_PIN_GROUP(tmu_tclk1_b), + SH_PFC_PIN_GROUP(tmu_tclk2_a), + SH_PFC_PIN_GROUP(tmu_tclk2_b), SH_PFC_PIN_GROUP(usb0_a), SH_PFC_PIN_GROUP(usb0_b), SH_PFC_PIN_GROUP(usb0_id), @@ -4124,6 +4165,13 @@ static const char * const ssi_groups[] = { "ssi9_ctrl_b", }; +static const char * const tmu_groups[] = { + "tmu_tclk1_a", + "tmu_tclk1_b", + "tmu_tclk2_a", + "tmu_tclk2_b", +}; + static const char * const usb0_groups[] = { "usb0_a", "usb0_b", @@ -4170,7 +4218,7 @@ static const char * const vin5_groups[] = { }; static const struct { - struct sh_pfc_function common[44]; + struct sh_pfc_function common[45]; struct sh_pfc_function automotive[2]; } pinmux_functions = { .common = { @@ -4214,6 +4262,7 @@ static const struct { SH_PFC_FUNCTION(sdhi1), SH_PFC_FUNCTION(sdhi3), SH_PFC_FUNCTION(ssi), + SH_PFC_FUNCTION(tmu), SH_PFC_FUNCTION(usb0), SH_PFC_FUNCTION(usb30), SH_PFC_FUNCTION(vin4), From 8e32e881947be98abaa917157fefc4a3319e90af Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 16 Jan 2019 11:48:53 +0100 Subject: [PATCH 0474/4277] pinctrl: sh-pfc: r8a7778: Fix HSPI pin numbers and names When declaring the HSPI RX1_B and TX1_B pins, two mistakes were made: - the rows and columns in the BGA pin matrix, from which the pin numbers are derived, were exchanged, - it was not taken into account that pin row labelling skips characters I, O, Q, and S. Fix the order, and the corresponding pin names. Notes: - The actual values of the pin numbers don't really matter (they just have to be unique), so the wrong order didn't have any impact, - Changing the names of the pins is user-visible, but there are no users in (upstream) DTS files. Fixes: 4f82e3ee724f1712 ("sh-pfc: Support pins not associated with a GPIO port") Fixes: 09cc76a95802e87d ("sh-pfc: r8a7778: add HSPI pin groups") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a7778.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c index 6bcdb4b5e69e..068b5e6334d1 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c @@ -1264,8 +1264,8 @@ static const struct sh_pfc_pin pinmux_pins[] = { /* Pins not associated with a GPIO port */ SH_PFC_PIN_NAMED(3, 20, C20), - SH_PFC_PIN_NAMED(20, 1, T1), - SH_PFC_PIN_NAMED(25, 2, Y2), + SH_PFC_PIN_NAMED(1, 20, A20), + SH_PFC_PIN_NAMED(2, 25, B25), }; /* - macro */ @@ -1400,7 +1400,7 @@ HSPI_PFC_DAT(hspi1_a, HSPI_CLK1_A, HSPI_CS1_A, HSPI_RX1_A, HSPI_TX1_A); HSPI_PFC_PIN(hspi1_b, RCAR_GP_PIN(0, 27), RCAR_GP_PIN(0, 26), - PIN_NUMBER(20, 1), PIN_NUMBER(25, 2)); + PIN_NUMBER(1, 20), PIN_NUMBER(2, 25)); HSPI_PFC_DAT(hspi1_b, HSPI_CLK1_B, HSPI_CS1_B, HSPI_RX1_B, HSPI_TX1_B); From a3f1caeefed0731e64bef39859482e6c9de8f027 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 21 Jan 2019 13:05:39 +0800 Subject: [PATCH 0475/4277] gpio: sprd: Remove unused irq trigger setting The Spreadtrum PMIC interrupt controller has no registers to set irq trigger type, since it is always high level trigger. That means the PMIC EIC controller as a child device of PMIC INTC does not need to set the trigger type, so remove it. Signed-off-by: Baolin Wang Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pmic-eic-sprd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 29e044ff4b17..ac573da040bb 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -322,7 +322,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, pmic_eic->irq, NULL, sprd_pmic_eic_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND, dev_name(&pdev->dev), pmic_eic); if (ret) { From 697818f383fc548cdbfb1528c7067994739ace04 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:38 -0500 Subject: [PATCH 0476/4277] dt-bindings: pinctrl: qcom-pmic-gpio: add qcom,pmi8998-gpio binding Add support for the PMI8998 GPIO variant to the Qualcomm PMIC GPIO binding document. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Reviewed-by: Rob Herring Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt index 759aa1732e48..7f64a7e92c28 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt @@ -19,6 +19,7 @@ PMIC's from Qualcomm. "qcom,pm8998-gpio" "qcom,pma8084-gpio" "qcom,pmi8994-gpio" + "qcom,pmi8998-gpio" "qcom,pms405-gpio" And must contain either "qcom,spmi-gpio" or "qcom,ssbi-gpio" From d7ee4d0a67315736b402291ed48b77e701c76224 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:39 -0500 Subject: [PATCH 0477/4277] pinctrl: qcom: spmi-gpio: add support for three new variants Add support for qcom,pm8005-gpio, qcom,pm8998-gpio, and qcom,pmi8998-gpio. These three variants are already in use in some arm64 dtsi files. Those boards work since the generic binding qcom,spmi-gpio is also specified. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 4458d44dfcf6..5f2977982ef1 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -1063,10 +1063,13 @@ static int pmic_gpio_remove(struct platform_device *pdev) } static const struct of_device_id pmic_gpio_of_match[] = { + { .compatible = "qcom,pm8005-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */ + { .compatible = "qcom,pm8998-gpio" }, /* 26 GPIO's */ + { .compatible = "qcom,pmi8998-gpio" }, /* 14 GPIO's */ { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */ { .compatible = "qcom,spmi-gpio" }, /* Generic */ From cfacef373505dce8735c562a147d14611db259b9 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:40 -0500 Subject: [PATCH 0478/4277] pinctrl: qcom: spmi-gpio: hardcode IRQ counts The probing of this driver calls platform_irq_count, which will setup all of the IRQs that are configured in device tree. In preparation for converting this driver to be a hierarchical IRQ chip, hardcode the IRQ count based on the hardware type so that all the IRQs are not configured immediately and are configured on an as-needed basis later in the boot process. This change will also allow for the removal of the interrupts property later in this patch series once the hierarchical IRQ chip support is in. This patch also removes the generic qcom,spmi-gpio OF match since we don't know the number of pins. All of the existing upstream bindings already include the more-specific binding. The pm8941 code was tested on a LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 28 ++++++++++-------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 5f2977982ef1..b6fa2c7dbb26 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -951,13 +951,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) return ret; } - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - - BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); + npins = (uintptr_t) device_get_match_data(&pdev->dev); state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) @@ -1063,16 +1057,16 @@ static int pmic_gpio_remove(struct platform_device *pdev) } static const struct of_device_id pmic_gpio_of_match[] = { - { .compatible = "qcom,pm8005-gpio" }, /* 4 GPIO's */ - { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ - { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ - { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pmi8994-gpio" }, /* 10 GPIO's */ - { .compatible = "qcom,pm8998-gpio" }, /* 26 GPIO's */ - { .compatible = "qcom,pmi8998-gpio" }, /* 14 GPIO's */ - { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ - { .compatible = "qcom,pms405-gpio" }, /* 12 GPIO's, holes on 1 9 10 */ - { .compatible = "qcom,spmi-gpio" }, /* Generic */ + { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, + { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, + { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, + { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, + { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, + { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, + /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ + { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, { }, }; From 2a6efbc6da5d248c4fbe7fdb7de7f3eff1de95a4 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Wed, 16 Jan 2019 19:45:45 +0000 Subject: [PATCH 0479/4277] clk: renesas: r8a774c0: Add missing CANFD clock This patch adds the missing CANFD clock to the r8a774c0 specific clock driver. Signed-off-by: Fabrizio Castro Reviewed-by: Chris Paterson Reviewed-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a774c0-cpg-mssr.c | 4 ++++ include/dt-bindings/clock/r8a774c0-cpg-mssr.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/clk/renesas/r8a774c0-cpg-mssr.c b/drivers/clk/renesas/r8a774c0-cpg-mssr.c index 10b96895d452..28bcc8105d57 100644 --- a/drivers/clk/renesas/r8a774c0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774c0-cpg-mssr.c @@ -33,6 +33,7 @@ enum clk_ids { CLK_PLL1, CLK_PLL3, CLK_PLL0D4, + CLK_PLL0D6, CLK_PLL0D8, CLK_PLL0D20, CLK_PLL0D24, @@ -61,6 +62,7 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = { DEF_FIXED(".pll0", CLK_PLL0, CLK_MAIN, 1, 100), DEF_FIXED(".pll0d4", CLK_PLL0D4, CLK_PLL0, 4, 1), + DEF_FIXED(".pll0d6", CLK_PLL0D6, CLK_PLL0, 6, 1), DEF_FIXED(".pll0d8", CLK_PLL0D8, CLK_PLL0, 8, 1), DEF_FIXED(".pll0d20", CLK_PLL0D20, CLK_PLL0, 20, 1), DEF_FIXED(".pll0d24", CLK_PLL0D24, CLK_PLL0, 24, 1), @@ -112,6 +114,7 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = { DEF_GEN3_PE("s3d2c", R8A774C0_CLK_S3D2C, CLK_S3, 2, CLK_PE, 2), DEF_GEN3_PE("s3d4c", R8A774C0_CLK_S3D4C, CLK_S3, 4, CLK_PE, 4), + DEF_DIV6P1("canfd", R8A774C0_CLK_CANFD, CLK_PLL0D6, 0x244), DEF_DIV6P1("csi0", R8A774C0_CLK_CSI0, CLK_PLL1D2, 0x00c), DEF_DIV6P1("mso", R8A774C0_CLK_MSO, CLK_PLL1D2, 0x014), @@ -187,6 +190,7 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { DEF_MOD("gpio2", 910, R8A774C0_CLK_S3D4), DEF_MOD("gpio1", 911, R8A774C0_CLK_S3D4), DEF_MOD("gpio0", 912, R8A774C0_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A774C0_CLK_S3D2), DEF_MOD("can-if1", 915, R8A774C0_CLK_S3D4), DEF_MOD("can-if0", 916, R8A774C0_CLK_S3D4), DEF_MOD("i2c6", 918, R8A774C0_CLK_S3D2), diff --git a/include/dt-bindings/clock/r8a774c0-cpg-mssr.h b/include/dt-bindings/clock/r8a774c0-cpg-mssr.h index 8fe51b6aca28..8ad9cd6be8e9 100644 --- a/include/dt-bindings/clock/r8a774c0-cpg-mssr.h +++ b/include/dt-bindings/clock/r8a774c0-cpg-mssr.h @@ -56,5 +56,6 @@ #define R8A774C0_CLK_CSI0 45 #define R8A774C0_CLK_CP 46 #define R8A774C0_CLK_CPEX 47 +#define R8A774C0_CLK_CANFD 48 #endif /* __DT_BINDINGS_CLOCK_R8A774C0_CPG_MSSR_H__ */ From 9d034e151b407cbd2c66bc4c48b423f814533374 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 17 Jan 2019 14:54:14 +0000 Subject: [PATCH 0480/4277] clk: renesas: r8a774a1: Add missing CANFD clock This patch adds the missing CANFD clock to the r8a774a1 specific clock driver. Signed-off-by: Fabrizio Castro Reviewed-by: Chris Paterson Reviewed-by: Simon Horman Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a774a1-cpg-mssr.c | 2 ++ include/dt-bindings/clock/r8a774a1-cpg-mssr.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/clk/renesas/r8a774a1-cpg-mssr.c b/drivers/clk/renesas/r8a774a1-cpg-mssr.c index 10e852518870..e10374119a91 100644 --- a/drivers/clk/renesas/r8a774a1-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774a1-cpg-mssr.c @@ -102,6 +102,7 @@ static const struct cpg_core_clk r8a774a1_core_clks[] __initconst = { DEF_FIXED("cp", R8A774A1_CLK_CP, CLK_EXTAL, 2, 1), DEF_FIXED("cpex", R8A774A1_CLK_CPEX, CLK_EXTAL, 2, 1), + DEF_DIV6P1("canfd", R8A774A1_CLK_CANFD, CLK_PLL1_DIV4, 0x244), DEF_DIV6P1("csi0", R8A774A1_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), DEF_DIV6P1("mso", R8A774A1_CLK_MSO, CLK_PLL1_DIV4, 0x014), DEF_DIV6P1("hdmi", R8A774A1_CLK_HDMI, CLK_PLL1_DIV4, 0x250), @@ -191,6 +192,7 @@ static const struct mssr_mod_clk r8a774a1_mod_clks[] __initconst = { DEF_MOD("gpio2", 910, R8A774A1_CLK_S3D4), DEF_MOD("gpio1", 911, R8A774A1_CLK_S3D4), DEF_MOD("gpio0", 912, R8A774A1_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A774A1_CLK_S3D2), DEF_MOD("can-if1", 915, R8A774A1_CLK_S3D4), DEF_MOD("can-if0", 916, R8A774A1_CLK_S3D4), DEF_MOD("i2c6", 918, R8A774A1_CLK_S0D6), diff --git a/include/dt-bindings/clock/r8a774a1-cpg-mssr.h b/include/dt-bindings/clock/r8a774a1-cpg-mssr.h index 9bc5d45ff4b5..e355363f40c2 100644 --- a/include/dt-bindings/clock/r8a774a1-cpg-mssr.h +++ b/include/dt-bindings/clock/r8a774a1-cpg-mssr.h @@ -54,5 +54,6 @@ #define R8A774A1_CLK_CPEX 43 #define R8A774A1_CLK_R 44 #define R8A774A1_CLK_OSC 45 +#define R8A774A1_CLK_CANFD 46 #endif /* __DT_BINDINGS_CLOCK_R8A774A1_CPG_MSSR_H__ */ From 7341fa7a6bf3f8dc7466bdd340dc5a61efab8902 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2019 10:31:57 +0100 Subject: [PATCH 0481/4277] gpio: pca953x: reduce indentation level in pca953x_irq_setup() The current design of pca953x_irq_setup() is: if (all conditions to support IRQ are met) { lots of code to support IRQs, which goes to a serious indentation level. } return 0; It makes more sense to handle this like this: if (!all conditions to support IRQ are met) return 0; handle IRQ support This commit does just this change, reducing by one tab the indentation level of the IRQ setup code. Thanks to this reduced indentation level, we are less restricted by the 80-column limit, and we can have more function arguments on the same line. Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pca953x.c | 82 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 83617fdc661d..73ceea72fc0a 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -702,53 +702,53 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int reg_direction[MAX_BANK]; int ret, i; - if (client->irq && irq_base != -1 - && (chip->driver_data & PCA_INT)) { - ret = pca953x_read_regs(chip, - chip->regs->input, chip->irq_stat); - if (ret) - return ret; + if (!client->irq) + return 0; - /* - * There is no way to know which GPIO line generated the - * interrupt. We have to rely on the previous read for - * this purpose. - */ - regmap_bulk_read(chip->regmap, chip->regs->direction, - reg_direction, NBANK(chip)); - for (i = 0; i < NBANK(chip); i++) - chip->irq_stat[i] &= reg_direction[i]; - mutex_init(&chip->irq_lock); + if (irq_base == -1) + return 0; - ret = devm_request_threaded_irq(&client->dev, - client->irq, - NULL, - pca953x_irq_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT | - IRQF_SHARED, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - return ret; - } + if (!(chip->driver_data & PCA_INT)) + return 0; - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, - &pca953x_irq_chip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } + ret = pca953x_read_regs(chip, chip->regs->input, chip->irq_stat); + if (ret) + return ret; - gpiochip_set_nested_irqchip(&chip->gpio_chip, - &pca953x_irq_chip, - client->irq); + /* + * There is no way to know which GPIO line generated the + * interrupt. We have to rely on the previous read for + * this purpose. + */ + regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction, + NBANK(chip)); + for (i = 0; i < NBANK(chip); i++) + chip->irq_stat[i] &= reg_direction[i]; + mutex_init(&chip->irq_lock); + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pca953x_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | + IRQF_SHARED, + dev_name(&client->dev), chip); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + return ret; } + ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, &pca953x_irq_chip, + irq_base, handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&client->dev, + "could not connect irqchip to gpiochip\n"); + return ret; + } + + gpiochip_set_nested_irqchip(&chip->gpio_chip, &pca953x_irq_chip, + client->irq); + return 0; } From 5c4fee63c5ed8133e5a6011787a614c7dbc80c82 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2019 10:31:58 +0100 Subject: [PATCH 0482/4277] gpio: pca953x: use a per instance irq_chip structure When a system has two PCA953x GPIO expanders, the kernel complains with: gpio gpiochip2: (0-0021): detected irqchip that is shared with multiple gpiochips: please fix the driver. Indeed, there is a single instance of "struct irq_chip" that gets re-used for both PCA953x instance. This commit moves the "struct irq_chip" to be part of the "struct pca953x_chip", so that we have one "struct irq_chip" per PCA953X instance. As part of this, the name of the irq_chip is also made different on a per-instance basis, now using the dev_name() of the I2C device. This changes what is visible in /proc/interrupts. Before: 47: 0 0 pca953x 10 Edge e0100000.sdhci cd 48: 0 0 pca953x 6 Edge e0101000.sdhci cd After: 47: 0 0 0-0020 10 Edge e0100000.sdhci cd 48: 2 0 0-0020 6 Edge e0101000.sdhci cd Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pca953x.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 73ceea72fc0a..83d45f00e4d0 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -150,6 +150,7 @@ struct pca953x_chip { u8 irq_stat[MAX_BANK]; u8 irq_trig_raise[MAX_BANK]; u8 irq_trig_fall[MAX_BANK]; + struct irq_chip irq_chip; #endif struct i2c_client *client; @@ -594,16 +595,6 @@ static void pca953x_irq_shutdown(struct irq_data *d) chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask; } -static struct irq_chip pca953x_irq_chip = { - .name = "pca953x", - .irq_mask = pca953x_irq_mask, - .irq_unmask = pca953x_irq_unmask, - .irq_bus_lock = pca953x_irq_bus_lock, - .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock, - .irq_set_type = pca953x_irq_set_type, - .irq_shutdown = pca953x_irq_shutdown, -}; - static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) { u8 cur_stat[MAX_BANK]; @@ -699,6 +690,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) { struct i2c_client *client = chip->client; + struct irq_chip *irq_chip = &chip->irq_chip; int reg_direction[MAX_BANK]; int ret, i; @@ -737,7 +729,15 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, return ret; } - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, &pca953x_irq_chip, + irq_chip->name = dev_name(&chip->client->dev); + irq_chip->irq_mask = pca953x_irq_mask; + irq_chip->irq_unmask = pca953x_irq_unmask; + irq_chip->irq_bus_lock = pca953x_irq_bus_lock; + irq_chip->irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock; + irq_chip->irq_set_type = pca953x_irq_set_type; + irq_chip->irq_shutdown = pca953x_irq_shutdown; + + ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip, irq_base, handle_simple_irq, IRQ_TYPE_NONE); if (ret) { @@ -746,8 +746,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, return ret; } - gpiochip_set_nested_irqchip(&chip->gpio_chip, &pca953x_irq_chip, - client->irq); + gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq); return 0; } From f4f1b074691288d024a72601463011b6afd8ac75 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 8 Jan 2019 17:28:43 +0100 Subject: [PATCH 0483/4277] pinctrl: armada-37xx: change suspend/resume time Armada 3700 PCIe IP relies on the pinctrl IP managed by this driver. For reasons related to the PCI core's organization when suspending/resuming, PCI host controller drivers must reconfigure their register at suspend_noirq()/resume_noirq() which happens after suspend()/suspend_late() and before resume_early()/resume(). In the current state, after resuming from a suspend to RAM cycle the PCIe IP is reconfigured before the pinctrl one which produces an interrupt storm. The solution to support PCIe resume operation is to change the "priority" of this pinctrl driver PM callbacks to "_noirq()". Signed-off-by: Miquel Raynal Acked-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 4b9f2cf94431..6462d3ca7ceb 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -1107,8 +1107,8 @@ static int armada_3700_pinctrl_resume(struct device *dev) * to other IO drivers. */ static const struct dev_pm_ops armada_3700_pinctrl_pm_ops = { - .suspend_late = armada_3700_pinctrl_suspend, - .resume_early = armada_3700_pinctrl_resume, + .suspend_noirq = armada_3700_pinctrl_suspend, + .resume_noirq = armada_3700_pinctrl_resume, }; #define PINCTRL_ARMADA_37XX_DEV_PM_OPS (&armada_3700_pinctrl_pm_ops) From 19ab5ca9b77d8b30a00cd3b7474d9c1f87df5c0a Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Fri, 11 Jan 2019 17:25:16 +0100 Subject: [PATCH 0484/4277] pinctrl: mcp23s08: Allocate irq_chip dynamic Keeping the irq_chip definition static shares it with multiple instances of the mcp23s08 gpiochip in the system. This is bad and now we get this warning from gpiolib core: "detected irqchip that is shared with multiple gpiochips: please fix the driver." Hence, move the irq_chip definition from being driver static into the struct mcp23s08. So a unique irq_chip is used for each gpiochip instance. Signed-off-by: Lars Poeschel Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-mcp23s08.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index 6b4def13df8f..2047e03178a3 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -68,6 +68,7 @@ struct mcp23s08 { struct mutex lock; struct gpio_chip chip; + struct irq_chip irq_chip; struct regmap *regmap; struct device *dev; @@ -607,15 +608,6 @@ static void mcp23s08_irq_bus_unlock(struct irq_data *data) mutex_unlock(&mcp->lock); } -static struct irq_chip mcp23s08_irq_chip = { - .name = "gpio-mcp23xxx", - .irq_mask = mcp23s08_irq_mask, - .irq_unmask = mcp23s08_irq_unmask, - .irq_set_type = mcp23s08_irq_set_type, - .irq_bus_lock = mcp23s08_irq_bus_lock, - .irq_bus_sync_unlock = mcp23s08_irq_bus_unlock, -}; - static int mcp23s08_irq_setup(struct mcp23s08 *mcp) { struct gpio_chip *chip = &mcp->chip; @@ -645,7 +637,7 @@ static int mcp23s08_irqchip_setup(struct mcp23s08 *mcp) int err; err = gpiochip_irqchip_add_nested(chip, - &mcp23s08_irq_chip, + &mcp->irq_chip, 0, handle_simple_irq, IRQ_TYPE_NONE); @@ -656,7 +648,7 @@ static int mcp23s08_irqchip_setup(struct mcp23s08 *mcp) } gpiochip_set_nested_irqchip(chip, - &mcp23s08_irq_chip, + &mcp->irq_chip, mcp->irq); return 0; @@ -1042,6 +1034,13 @@ static int mcp230xx_probe(struct i2c_client *client, return -ENOMEM; mcp->irq = client->irq; + mcp->irq_chip.name = dev_name(&client->dev); + mcp->irq_chip.irq_mask = mcp23s08_irq_mask; + mcp->irq_chip.irq_unmask = mcp23s08_irq_unmask; + mcp->irq_chip.irq_set_type = mcp23s08_irq_set_type; + mcp->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock; + mcp->irq_chip.irq_bus_sync_unlock = mcp23s08_irq_bus_unlock; + status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, id->driver_data, pdata->base, 0); if (status) @@ -1151,6 +1150,13 @@ static int mcp23s08_probe(struct spi_device *spi) chips--; data->mcp[addr] = &data->chip[chips]; data->mcp[addr]->irq = spi->irq; + data->mcp[addr]->irq_chip.name = dev_name(&spi->dev); + data->mcp[addr]->irq_chip.irq_mask = mcp23s08_irq_mask; + data->mcp[addr]->irq_chip.irq_unmask = mcp23s08_irq_unmask; + data->mcp[addr]->irq_chip.irq_set_type = mcp23s08_irq_set_type; + data->mcp[addr]->irq_chip.irq_bus_lock = mcp23s08_irq_bus_lock; + data->mcp[addr]->irq_chip.irq_bus_sync_unlock = + mcp23s08_irq_bus_unlock; status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, 0x40 | (addr << 1), type, pdata->base, addr); From 588de43ca141d575042ea1b55bb8203f35b425c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Mon, 14 Jan 2019 20:14:49 +0100 Subject: [PATCH 0485/4277] gpio: hlwd: Add basic IRQ support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements level-triggered IRQs in the Hollywood GPIO driver. Edge triggered interrupts are not supported in this GPIO controller, so I moved their emulation into a separate patch. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-hlwd.c | 136 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b5a2845347ec..f77180dd87bd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -258,6 +258,7 @@ config GPIO_HLWD tristate "Nintendo Wii (Hollywood) GPIO" depends on OF_GPIO select GPIO_GENERIC + select GPIOLIB_IRQCHIP help Select this to support the GPIO controller of the Nintendo Wii. diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index a63136a68ba3..61983c115856 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -48,9 +48,107 @@ struct hlwd_gpio { struct gpio_chip gpioc; + struct irq_chip irqc; void __iomem *regs; + int irq; }; +static void hlwd_gpio_irqhandler(struct irq_desc *desc) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_desc_get_handler_data(desc)); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long flags; + unsigned long pending; + int hwirq; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); + pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + + chained_irq_enter(chip, desc); + + for_each_set_bit(hwirq, &pending, 32) { + int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq); + + generic_handle_irq(irq); + } + + chained_irq_exit(chip, desc); +} + +static void hlwd_gpio_irq_ack(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG); +} + +static void hlwd_gpio_irq_mask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask &= ~BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_unmask(struct irq_data *data) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + mask |= BIT(data->hwirq); + iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); +} + +static void hlwd_gpio_irq_enable(struct irq_data *data) +{ + hlwd_gpio_irq_ack(data); + hlwd_gpio_irq_unmask(data); +} + +static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct hlwd_gpio *hlwd = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + unsigned long flags; + u32 level; + + spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level |= BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + case IRQ_TYPE_LEVEL_LOW: + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + level &= ~BIT(data->hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + break; + default: + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); + return 0; +} + static int hlwd_gpio_probe(struct platform_device *pdev) { struct hlwd_gpio *hlwd; @@ -92,7 +190,43 @@ static int hlwd_gpio_probe(struct platform_device *pdev) ngpios = 32; hlwd->gpioc.ngpio = ngpios; - return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); + if (res) + return res; + + /* Mask and ack all interrupts */ + iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); + iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); + + /* + * If this GPIO controller is not marked as an interrupt controller in + * the DT, return. + */ + if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) + return 0; + + hlwd->irq = platform_get_irq(pdev, 0); + if (hlwd->irq < 0) { + dev_info(&pdev->dev, "platform_get_irq returned %d\n", + hlwd->irq); + return hlwd->irq; + } + + hlwd->irqc.name = dev_name(&pdev->dev); + hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; + hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; + hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; + hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; + + res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0, + handle_level_irq, IRQ_TYPE_NONE); + if (res) + return res; + + gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc, + hlwd->irq, hlwd_gpio_irqhandler); + + return 0; } static const struct of_device_id hlwd_gpio_match[] = { From a7241c1b188ebed18df1d8cef445ba0d881d2a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Mon, 14 Jan 2019 20:14:50 +0100 Subject: [PATCH 0486/4277] gpio: hlwd: Implement edge trigger emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like the Spreadtrum EIC driver[1], this driver needs to emulate edge triggered interrupts to support the generic gpio-keys driver. [1]: https://www.spinics.net/lists/kernel/msg2764576.html Signed-off-by: Jonathan Neuschäfer Signed-off-by: Linus Walleij --- drivers/gpio/gpio-hlwd.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index 61983c115856..a7b17897356e 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -51,6 +51,8 @@ struct hlwd_gpio { struct irq_chip irqc; void __iomem *regs; int irq; + u32 edge_emulation; + u32 rising_edge, falling_edge; }; static void hlwd_gpio_irqhandler(struct irq_desc *desc) @@ -61,10 +63,36 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc) unsigned long flags; unsigned long pending; int hwirq; + u32 emulated_pending; spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); + + /* Treat interrupts due to edge trigger emulation separately */ + emulated_pending = hlwd->edge_emulation & pending; + pending &= ~emulated_pending; + if (emulated_pending) { + u32 level, rising, falling; + + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + rising = level & emulated_pending; + falling = ~level & emulated_pending; + + /* Invert the levels */ + iowrite32be(level ^ emulated_pending, + hlwd->regs + HW_GPIOB_INTLVL); + + /* Ack all emulated-edge interrupts */ + iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); + + /* Signal interrupts only on the correct edge */ + rising &= hlwd->rising_edge; + falling &= hlwd->falling_edge; + + /* Mark emulated interrupts as pending */ + pending |= rising | falling; + } spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); chained_irq_enter(chip, desc); @@ -120,6 +148,27 @@ static void hlwd_gpio_irq_enable(struct irq_data *data) hlwd_gpio_irq_unmask(data); } +static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, + unsigned int flow_type) +{ + u32 level, state; + + /* Set the trigger level to the inactive level */ + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); + state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); + level &= ~BIT(hwirq); + level |= state ^ BIT(hwirq); + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); + + hlwd->edge_emulation |= BIT(hwirq); + hlwd->rising_edge &= ~BIT(hwirq); + hlwd->falling_edge &= ~BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_RISING) + hlwd->rising_edge |= BIT(hwirq); + if (flow_type & IRQ_TYPE_EDGE_FALLING) + hlwd->falling_edge |= BIT(hwirq); +} + static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) { struct hlwd_gpio *hlwd = @@ -129,6 +178,8 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); + hlwd->edge_emulation &= ~BIT(data->hwirq); + switch (flow_type) { case IRQ_TYPE_LEVEL_HIGH: level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); @@ -140,6 +191,11 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) level &= ~BIT(data->hwirq); iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); + break; default: spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); return -EINVAL; From 40e3795851ce50f879e8b54767354324650077b3 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Tue, 15 Jan 2019 10:20:59 +0200 Subject: [PATCH 0487/4277] pinctrl: ti: iodelay: Lower the priority of prints Dont print every single iodelay register configuration - this is just plain noise. Since this is useful debug information, just lower to debug Signed-off-by: Nishanth Menon Signed-off-by: Tero Kristo Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c index a4bc506a01a3..76f9083d9c63 100644 --- a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -263,9 +263,9 @@ static int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod, reg_val |= reg->unlock_val << __ffs(reg->lock_mask); r = regmap_update_bits(iod->regmap, cfg->offset, reg_mask, reg_val); - dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", - cfg->offset, cfg->a_delay, cfg->g_delay, c_elements, - f_elements, reg_val); + dev_dbg(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", + cfg->offset, cfg->a_delay, cfg->g_delay, c_elements, + f_elements, reg_val); return r; } From 341fe1d301f587c930509f6b9153436b957f649c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 17 Jan 2019 12:33:04 -0200 Subject: [PATCH 0488/4277] media: tw9910: Unregister subdevice with v4l2-async As the tw9910 subdevice is registered through the v4l2-async framework, use the v4l2-async provided function to register it. Fixes: 7b20f325a566 ("media: i2c: tw9910: Remove soc_camera dependencies") Signed-off-by: Jacopo Mondi Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tw9910.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 8d1138e13803..4d7cd736b930 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -1001,7 +1001,7 @@ static int tw9910_remove(struct i2c_client *client) if (priv->pdn_gpio) gpiod_put(priv->pdn_gpio); clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); + v4l2_async_unregister_subdev(&priv->subdev); return 0; } From 6f80a5e44ef2828fadd6bca67e5af564f533b6fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:27:00 -0200 Subject: [PATCH 0489/4277] media: soc_mt9t112: remove obsolete sensor driver This driver got converted to not depend on soc_camera in commit 6a26f141bf62 ("media: i2c: mt9t112: Remove soc_camera dependencies"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_mt9t112.c | 1157 -------------------- 3 files changed, 1164 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_mt9t112.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index f67499187bda..773d067c44a3 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -17,12 +17,6 @@ config SOC_CAMERA_MT9M111 This is the legacy configuration which shouldn't be used anymore, while VIDEO_MT9M111 should be used instead. -config SOC_CAMERA_MT9T112 - tristate "mt9t112 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9T112 cameras from Aptina. - config SOC_CAMERA_MT9V022 tristate "mt9v022 and mt9v024 support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 0d15864cfda8..ea8da4074a01 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o -obj-$(CONFIG_SOC_CAMERA_MT9T112) += soc_mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o diff --git a/drivers/media/i2c/soc_camera/soc_mt9t112.c b/drivers/media/i2c/soc_camera/soc_mt9t112.c deleted file mode 100644 index ea1ff270bc2d..000000000000 --- a/drivers/media/i2c/soc_camera/soc_mt9t112.c +++ /dev/null @@ -1,1157 +0,0 @@ -/* - * mt9t112 Camera Driver - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov772x driver, mt9m111 driver, - * - * Copyright (C) 2008 Kuninori Morimoto - * Copyright (C) 2008, Robert Jarzmik - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* you can check PLL/clock info */ -/* #define EXT_CLOCK 24000000 */ - -/************************************************************************ - macro -************************************************************************/ -/* - * frame size - */ -#define MAX_WIDTH 2048 -#define MAX_HEIGHT 1536 - -/* - * macro of read/write - */ -#define ECHECKER(ret, x) \ - do { \ - (ret) = (x); \ - if ((ret) < 0) \ - return (ret); \ - } while (0) - -#define mt9t112_reg_write(ret, client, a, b) \ - ECHECKER(ret, __mt9t112_reg_write(client, a, b)) -#define mt9t112_mcu_write(ret, client, a, b) \ - ECHECKER(ret, __mt9t112_mcu_write(client, a, b)) - -#define mt9t112_reg_mask_set(ret, client, a, b, c) \ - ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c)) -#define mt9t112_mcu_mask_set(ret, client, a, b, c) \ - ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c)) - -#define mt9t112_reg_read(ret, client, a) \ - ECHECKER(ret, __mt9t112_reg_read(client, a)) - -/* - * Logical address - */ -#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff)) -#define VAR(id, offset) _VAR(id, offset, 0x0000) -#define VAR8(id, offset) _VAR(id, offset, 0x8000) - -/************************************************************************ - struct -************************************************************************/ -struct mt9t112_format { - u32 code; - enum v4l2_colorspace colorspace; - u16 fmt; - u16 order; -}; - -struct mt9t112_priv { - struct v4l2_subdev subdev; - struct mt9t112_platform_data *info; - struct i2c_client *client; - struct v4l2_rect frame; - struct v4l2_clk *clk; - const struct mt9t112_format *format; - int num_formats; - u32 flags; -/* for flags */ -#define INIT_DONE (1 << 0) -#define PCLK_RISING (1 << 1) -}; - -/************************************************************************ - supported format -************************************************************************/ - -static const struct mt9t112_format mt9t112_cfmts[] = { - { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 0, - }, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 1, - }, { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 2, - }, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 3, - }, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 8, - .order = 2, - }, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 4, - .order = 2, - }, -}; - -/************************************************************************ - general function -************************************************************************/ -static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), - struct mt9t112_priv, - subdev); -} - -static int __mt9t112_reg_read(const struct i2c_client *client, u16 command) -{ - struct i2c_msg msg[2]; - u8 buf[2]; - int ret; - - command = swab16(command); - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 2; - msg[0].buf = (u8 *)&command; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 2; - msg[1].buf = buf; - - /* - * if return value of this function is < 0, - * it mean error. - * else, under 16bit is valid data. - */ - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) - return ret; - - memcpy(&ret, buf, 2); - return swab16(ret); -} - -static int __mt9t112_reg_write(const struct i2c_client *client, - u16 command, u16 data) -{ - struct i2c_msg msg; - u8 buf[4]; - int ret; - - command = swab16(command); - data = swab16(data); - - memcpy(buf + 0, &command, 2); - memcpy(buf + 2, &data, 2); - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 4; - msg.buf = buf; - - /* - * i2c_transfer return message length, - * but this function should return 0 if correct case - */ - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret >= 0) - ret = 0; - - return ret; -} - -static int __mt9t112_reg_mask_set(const struct i2c_client *client, - u16 command, - u16 mask, - u16 set) -{ - int val = __mt9t112_reg_read(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return __mt9t112_reg_write(client, command, val); -} - -/* mcu access */ -static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command) -{ - int ret; - - ret = __mt9t112_reg_write(client, 0x098E, command); - if (ret < 0) - return ret; - - return __mt9t112_reg_read(client, 0x0990); -} - -static int __mt9t112_mcu_write(const struct i2c_client *client, - u16 command, u16 data) -{ - int ret; - - ret = __mt9t112_reg_write(client, 0x098E, command); - if (ret < 0) - return ret; - - return __mt9t112_reg_write(client, 0x0990, data); -} - -static int __mt9t112_mcu_mask_set(const struct i2c_client *client, - u16 command, - u16 mask, - u16 set) -{ - int val = __mt9t112_mcu_read(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return __mt9t112_mcu_write(client, command, val); -} - -static int mt9t112_reset(const struct i2c_client *client) -{ - int ret; - - mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001); - msleep(1); - mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000); - - return ret; -} - -#ifndef EXT_CLOCK -#define CLOCK_INFO(a, b) -#else -#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b) -static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) -{ - int m, n, p1, p2, p3, p4, p5, p6, p7; - u32 vco, clk; - char *enable; - - ext /= 1000; /* kbyte order */ - - mt9t112_reg_read(n, client, 0x0012); - p1 = n & 0x000f; - n = n >> 4; - p2 = n & 0x000f; - n = n >> 4; - p3 = n & 0x000f; - - mt9t112_reg_read(n, client, 0x002a); - p4 = n & 0x000f; - n = n >> 4; - p5 = n & 0x000f; - n = n >> 4; - p6 = n & 0x000f; - - mt9t112_reg_read(n, client, 0x002c); - p7 = n & 0x000f; - - mt9t112_reg_read(n, client, 0x0010); - m = n & 0x00ff; - n = (n >> 8) & 0x003f; - - enable = ((6000 > ext) || (54000 < ext)) ? "X" : ""; - dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); - - vco = 2 * m * ext / (n+1); - enable = ((384000 > vco) || (768000 < vco)) ? "X" : ""; - dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); - - clk = vco / (p1+1) / (p2+1); - enable = (96000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); - - clk = vco / (p3+1); - enable = (768000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); - - clk = vco / (p6+1); - enable = (96000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); - - clk = vco / (p5+1); - enable = (54000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); - - clk = vco / (p4+1); - enable = (70000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); - - clk = vco / (p7+1); - dev_dbg(&client->dev, "External sensor : %10u K\n", clk); - - clk = ext / (n+1); - enable = ((2000 > clk) || (24000 < clk)) ? "X" : ""; - dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); - - return 0; -} -#endif - -static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top) -{ - soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH); - soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT); -} - -static int mt9t112_set_a_frame_size(const struct i2c_client *client, - u16 width, - u16 height) -{ - int ret; - u16 wstart = (MAX_WIDTH - width) / 2; - u16 hstart = (MAX_HEIGHT - height) / 2; - - /* (Context A) Image Width/Height */ - mt9t112_mcu_write(ret, client, VAR(26, 0), width); - mt9t112_mcu_write(ret, client, VAR(26, 2), height); - - /* (Context A) Output Width/Height */ - mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width); - mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height); - - /* (Context A) Start Row/Column */ - mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart); - mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart); - - /* (Context A) End Row/Column */ - mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart); - mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart); - - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); - - return ret; -} - -static int mt9t112_set_pll_dividers(const struct i2c_client *client, - u8 m, u8 n, - u8 p1, u8 p2, u8 p3, - u8 p4, u8 p5, u8 p6, - u8 p7) -{ - int ret; - u16 val; - - /* N/M */ - val = (n << 8) | - (m << 0); - mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val); - - /* P1/P2/P3 */ - val = ((p3 & 0x0F) << 8) | - ((p2 & 0x0F) << 4) | - ((p1 & 0x0F) << 0); - mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val); - - /* P4/P5/P6 */ - val = (0x7 << 12) | - ((p6 & 0x0F) << 8) | - ((p5 & 0x0F) << 4) | - ((p4 & 0x0F) << 0); - mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val); - - /* P7 */ - val = (0x1 << 12) | - ((p7 & 0x0F) << 0); - mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val); - - return ret; -} - -static int mt9t112_init_pll(const struct i2c_client *client) -{ - struct mt9t112_priv *priv = to_mt9t112(client); - int data, i, ret; - - mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001); - - /* PLL control: BYPASS PLL = 8517 */ - mt9t112_reg_write(ret, client, 0x0014, 0x2145); - - /* Replace these registers when new timing parameters are generated */ - mt9t112_set_pll_dividers(client, - priv->info->divider.m, - priv->info->divider.n, - priv->info->divider.p1, - priv->info->divider.p2, - priv->info->divider.p3, - priv->info->divider.p4, - priv->info->divider.p5, - priv->info->divider.p6, - priv->info->divider.p7); - - /* - * TEST_BYPASS on - * PLL_ENABLE on - * SEL_LOCK_DET on - * TEST_BYPASS off - */ - mt9t112_reg_write(ret, client, 0x0014, 0x2525); - mt9t112_reg_write(ret, client, 0x0014, 0x2527); - mt9t112_reg_write(ret, client, 0x0014, 0x3427); - mt9t112_reg_write(ret, client, 0x0014, 0x3027); - - mdelay(10); - - /* - * PLL_BYPASS off - * Reference clock count - * I2C Master Clock Divider - */ - mt9t112_reg_write(ret, client, 0x0014, 0x3046); - mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */ - mt9t112_reg_write(ret, client, 0x0022, 0x0190); - mt9t112_reg_write(ret, client, 0x3B84, 0x0212); - - /* External sensor clock is PLL bypass */ - mt9t112_reg_write(ret, client, 0x002E, 0x0500); - - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002); - mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004); - - /* MCU disabled */ - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004); - - /* out of standby */ - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0); - - mdelay(50); - - /* - * Standby Workaround - * Disable Secondary I2C Pads - */ - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - - /* poll to verify out of standby. Must Poll this bit */ - for (i = 0; i < 100; i++) { - mt9t112_reg_read(data, client, 0x0018); - if (!(0x4000 & data)) - break; - - mdelay(10); - } - - return ret; -} - -static int mt9t112_init_setting(const struct i2c_client *client) -{ - - int ret; - - /* Adaptive Output Clock (A) */ - mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000); - - /* Read Mode (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024); - - /* Fine Correction (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC); - - /* Fine IT Min (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1); - - /* Fine IT Max Margin (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF); - - /* Base Frame Lines (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D); - - /* Min Line Length (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a); - - /* Line Length (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0); - - /* Adaptive Output Clock (B) */ - mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000); - - /* Row Start (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004); - - /* Column Start (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004); - - /* Row End (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B); - - /* Column End (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B); - - /* Fine Correction (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C); - - /* Fine IT Min (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1); - - /* Fine IT Max Margin (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF); - - /* Base Frame Lines (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668); - - /* Min Line Length (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0); - - /* Line Length (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0); - - /* - * Flicker Dectection registers - * This section should be replaced whenever new Timing file is generated - * All the following registers need to be replaced - * Following registers are generated from Register Wizard but user can - * modify them. For detail see auto flicker detection tuning - */ - - /* FD_FDPERIOD_SELECT */ - mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01); - - /* PRI_B_CONFIG_FD_ALGO_RUN */ - mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003); - - /* PRI_A_CONFIG_FD_ALGO_RUN */ - mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003); - - /* - * AFD range detection tuning registers - */ - - /* search_f1_50 */ - mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25); - - /* search_f2_50 */ - mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28); - - /* search_f1_60 */ - mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C); - - /* search_f2_60 */ - mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F); - - /* period_50Hz (A) */ - mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA); - - /* secret register by aptina */ - /* period_50Hz (A MSB) */ - mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00); - - /* period_60Hz (A) */ - mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B); - - /* secret register by aptina */ - /* period_60Hz (A MSB) */ - mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00); - - /* period_50Hz (B) */ - mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82); - - /* secret register by aptina */ - /* period_50Hz (B) MSB */ - mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00); - - /* period_60Hz (B) */ - mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D); - - /* secret register by aptina */ - /* period_60Hz (B) MSB */ - mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00); - - /* FD Mode */ - mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10); - - /* Stat_min */ - mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02); - - /* Stat_max */ - mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03); - - /* Min_amplitude */ - mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A); - - /* RX FIFO Watermark (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014); - - /* RX FIFO Watermark (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014); - - /* MCLK: 16MHz - * PCLK: 73MHz - * CorePixCLK: 36.5 MHz - */ - mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133); - mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110); - mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130); - mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108); - - mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27); - mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30); - mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32); - mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35); - - return ret; -} - -static int mt9t112_auto_focus_setting(const struct i2c_client *client) -{ - int ret; - - mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F); - mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F); - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); - - mt9t112_reg_write(ret, client, 0x0614, 0x0000); - - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); - mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02); - mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002); - mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001); - mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025); - mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193); - mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18); - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); - - return ret; -} - -static int mt9t112_auto_focus_trigger(const struct i2c_client *client) -{ - int ret; - - mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01); - - return ret; -} - -static int mt9t112_init_camera(const struct i2c_client *client) -{ - int ret; - - ECHECKER(ret, mt9t112_reset(client)); - - ECHECKER(ret, mt9t112_init_pll(client)); - - ECHECKER(ret, mt9t112_init_setting(client)); - - ECHECKER(ret, mt9t112_auto_focus_setting(client)); - - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0); - - /* Analog setting B */ - mt9t112_reg_write(ret, client, 0x3084, 0x2409); - mt9t112_reg_write(ret, client, 0x3092, 0x0A49); - mt9t112_reg_write(ret, client, 0x3094, 0x4949); - mt9t112_reg_write(ret, client, 0x3096, 0x4950); - - /* - * Disable adaptive clock - * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR - * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR - */ - mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E); - mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E); - - /* Configure STatus in Status_before_length Format and enable header */ - /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */ - mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4); - - /* Enable JPEG in context B */ - /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */ - mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01); - - /* Disable Dac_TXLO */ - mt9t112_reg_write(ret, client, 0x316C, 0x350F); - - /* Set max slew rates */ - mt9t112_reg_write(ret, client, 0x1E, 0x777); - - return ret; -} - -/************************************************************************ - v4l2_subdev_core_ops -************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t112_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - reg->size = 2; - mt9t112_reg_read(ret, client, reg->reg); - - reg->val = (__u64)ret; - - return 0; -} - -static int mt9t112_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - mt9t112_reg_write(ret, client, reg->reg, reg->val); - - return ret; -} -#endif - -static int mt9t112_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9t112_priv *priv = to_mt9t112(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t112_g_register, - .s_register = mt9t112_s_register, -#endif - .s_power = mt9t112_s_power, -}; - - -/************************************************************************ - v4l2_subdev_video_ops -************************************************************************/ -static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - int ret = 0; - - if (!enable) { - /* FIXME - * - * If user selected large output size, - * and used it long time, - * mt9t112 camera will be very warm. - * - * But current driver can not stop mt9t112 camera. - * So, set small size here to solve this problem. - */ - mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT); - return ret; - } - - if (!(priv->flags & INIT_DONE)) { - u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000; - - ECHECKER(ret, mt9t112_init_camera(client)); - - /* Invert PCLK (Data sampled on falling edge of pixclk) */ - mt9t112_reg_write(ret, client, 0x3C20, param); - - mdelay(5); - - priv->flags |= INIT_DONE; - } - - mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt); - mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order); - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); - - mt9t112_set_a_frame_size(client, - priv->frame.width, - priv->frame.height); - - ECHECKER(ret, mt9t112_auto_focus_trigger(client)); - - dev_dbg(&client->dev, "format : %d\n", priv->format->code); - dev_dbg(&client->dev, "size : %d x %d\n", - priv->frame.width, - priv->frame.height); - - CLOCK_INFO(client, EXT_CLOCK); - - return ret; -} - -static int mt9t112_set_params(struct mt9t112_priv *priv, - const struct v4l2_rect *rect, - u32 code) -{ - int i; - - /* - * get color format - */ - for (i = 0; i < priv->num_formats; i++) - if (mt9t112_cfmts[i].code == code) - break; - - if (i == priv->num_formats) - return -EINVAL; - - priv->frame = *rect; - - /* - * frame size check - */ - mt9t112_frame_check(&priv->frame.width, &priv->frame.height, - &priv->frame.left, &priv->frame.top); - - priv->format = mt9t112_cfmts + i; - - return 0; -} - -static int mt9t112_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = MAX_WIDTH; - sel->r.height = MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = priv->frame; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t112_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - const struct v4l2_rect *rect = &sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - return mt9t112_set_params(priv, rect, priv->format->code); -} - -static int mt9t112_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (format->pad) - return -EINVAL; - - mf->width = priv->frame.width; - mf->height = priv->frame.height; - mf->colorspace = priv->format->colorspace; - mf->code = priv->format->code; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9t112_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - struct v4l2_rect rect = { - .width = mf->width, - .height = mf->height, - .left = priv->frame.left, - .top = priv->frame.top, - }; - int ret; - - ret = mt9t112_set_params(priv, &rect, mf->code); - - if (!ret) - mf->colorspace = priv->format->colorspace; - - return ret; -} - -static int mt9t112_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - unsigned int top, left; - int i; - - if (format->pad) - return -EINVAL; - - for (i = 0; i < priv->num_formats; i++) - if (mt9t112_cfmts[i].code == mf->code) - break; - - if (i == priv->num_formats) { - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_JPEG; - } else { - mf->colorspace = mt9t112_cfmts[i].colorspace; - } - - mt9t112_frame_check(&mf->width, &mf->height, &left, &top); - - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9t112_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (code->pad || code->index >= priv->num_formats) - return -EINVAL; - - code->code = mt9t112_cfmts[code->index].code; - - return 0; -} - -static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) - priv->flags |= PCLK_RISING; - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { - .s_stream = mt9t112_s_stream, - .g_mbus_config = mt9t112_g_mbus_config, - .s_mbus_config = mt9t112_s_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { - .enum_mbus_code = mt9t112_enum_mbus_code, - .get_selection = mt9t112_get_selection, - .set_selection = mt9t112_set_selection, - .get_fmt = mt9t112_get_fmt, - .set_fmt = mt9t112_set_fmt, -}; - -/************************************************************************ - i2c driver -************************************************************************/ -static const struct v4l2_subdev_ops mt9t112_subdev_ops = { - .core = &mt9t112_subdev_core_ops, - .video = &mt9t112_subdev_video_ops, - .pad = &mt9t112_subdev_pad_ops, -}; - -static int mt9t112_camera_probe(struct i2c_client *client) -{ - struct mt9t112_priv *priv = to_mt9t112(client); - const char *devname; - int chipid; - int ret; - - ret = mt9t112_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show chip ID - */ - mt9t112_reg_read(chipid, client, 0x0000); - - switch (chipid) { - case 0x2680: - devname = "mt9t111"; - priv->num_formats = 1; - break; - case 0x2682: - devname = "mt9t112"; - priv->num_formats = ARRAY_SIZE(mt9t112_cfmts); - break; - default: - dev_err(&client->dev, "Product ID error %04x\n", chipid); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid); - -done: - mt9t112_s_power(&priv->subdev, 0); - return ret; -} - -static int mt9t112_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t112_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct v4l2_rect rect = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .left = (MAX_WIDTH - VGA_WIDTH) / 2, - .top = (MAX_HEIGHT - VGA_HEIGHT) / 2, - }; - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "mt9t112: missing platform data!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = ssdd->drv_priv; - - v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = mt9t112_camera_probe(client); - - /* Cannot fail: using the default supported pixel code */ - if (!ret) - mt9t112_set_params(priv, &rect, MEDIA_BUS_FMT_UYVY8_2X8); - else - v4l2_clk_put(priv->clk); - - return ret; -} - -static int mt9t112_remove(struct i2c_client *client) -{ - struct mt9t112_priv *priv = to_mt9t112(client); - - v4l2_clk_put(priv->clk); - return 0; -} - -static const struct i2c_device_id mt9t112_id[] = { - { "mt9t112", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t112_id); - -static struct i2c_driver mt9t112_i2c_driver = { - .driver = { - .name = "mt9t112", - }, - .probe = mt9t112_probe, - .remove = mt9t112_remove, - .id_table = mt9t112_id, -}; - -module_i2c_driver(mt9t112_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for mt9t112"); -MODULE_AUTHOR("Kuninori Morimoto"); -MODULE_LICENSE("GPL v2"); From b597c3a939ecc58d69aadd188515fa9549be3144 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Mon, 7 Jan 2019 22:49:08 +0530 Subject: [PATCH 0490/4277] arm/mach-sa1100/simpad.c: Remove duplicate header Remove linux/gpio/machine.h which is included more than once. Signed-off-by: Souptick Joarder Signed-off-by: Linus Walleij --- arch/arm/mach-sa1100/simpad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index 406487e76a5c..c7fb9a73e4c5 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include From cf892f38e8568a20b853ec6efc2116c3e6e0215f Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Thu, 17 Jan 2019 11:23:13 +0100 Subject: [PATCH 0491/4277] dt-bindings: pinctrl: meson: update register descriptions like pull-enable, pull should be optional has this region is available on every controllers. Also, the g12a feature a new region "ds" for the drive-strength All this region thing is one big mess. I suspect that there is only one big GPIO region with holes in it. All registers between the current regions reads '0' so it is probably just spare space to handle more pins. Since we need to continue to handle the existing controllers, switching to one single region now would not simplify things. However, if more organisation layouts and features keep on being added, we may have to look at this again Fixes: 3cd3c83f6752 ("pinctrl: Add compatibles for Amlogic Meson G12A pin controllers") Signed-off-by: Jerome Brunet Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/meson,pinctrl.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt index 82ead40311f6..a47dd990a8d3 100644 --- a/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt @@ -23,11 +23,11 @@ The GPIO bank for the controller is represented as a sub-node and it acts as a GPIO controller. Required properties for sub-nodes are: - - reg: should contain address and size for mux, pull-enable, pull and - gpio register sets - - reg-names: an array of strings describing the "reg" entries. Must - contain "mux", "pull" and "gpio". "pull-enable" is optional and - when it is missing the "pull" registers are used instead + - reg: should contain a list of address and size, one tuple for each entry + in reg-names. + - reg-names: an array of strings describing the "reg" entries. + Must contain "mux" and "gpio". + May contain "pull", "pull-enable" and "ds" when appropriate. - gpio-controller: identifies the node as a gpio controller - #gpio-cells: must be 2 From e66dd48e8b0dee104d16417d30361074b08baca8 Mon Sep 17 00:00:00 2001 From: Xingyu Chen Date: Thu, 17 Jan 2019 11:23:14 +0100 Subject: [PATCH 0492/4277] pinctrl: meson: fix G12A ao pull registers base address Since Meson G12A SoC, Introduce new ao registers AO_RTI_PULL_UP_EN_REG and AO_GPIO_O. These bits of controlling output level are remapped to the new register AO_GPIO_O, and the AO_GPIO_O_EN_N support only controlling output enable. These bits of controlling pull enable are remapped to the new register AO_RTI_PULL_UP_EN_REG, and the AO_RTI_PULL_UP_REG support only controlling pull type(up/down). The new layout of ao gpio/pull registers is as follows: - AO_GPIO_O_EN_N [offset: 0x9 << 2] - AO_GPIO_I [offset: 0xa << 2] - AO_RTI_PULL_UP_REG [offset: 0xb << 2] - AO_RTI_PULL_UP_EN_REG [offset: 0xc << 2] - AO_GPIO_O [offset: 0xd << 2] From above, we can see ao GPIO registers region has been separated by the ao pull registers. In order to ensure the continuity of the region on software, the ao GPIO and ao pull registers use the same base address, but can be identified by the offset. Fixes: 29ae0952e85f ("pinctrl: meson-g12a: add pinctrl driver support") Signed-off-by: Xingyu Chen Signed-off-by: Jianxin Pan Signed-off-by: Jerome Brunet Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-meson.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index ea87d739f534..a4ae1ac5369e 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -31,6 +31,9 @@ * In some cases the register ranges for pull enable and pull * direction are the same and thus there are only 3 register ranges. * + * Since Meson G12A SoC, the ao register ranges for gpio, pull enable + * and pull direction are the same, so there are only 2 register ranges. + * * For the pull and GPIO configuration every bank uses a contiguous * set of bits in the register sets described above; the same register * can be shared by more banks with different offsets. @@ -488,23 +491,22 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc, return PTR_ERR(pc->reg_mux); } - pc->reg_pull = meson_map_resource(pc, gpio_np, "pull"); - if (IS_ERR(pc->reg_pull)) { - dev_err(pc->dev, "pull registers not found\n"); - return PTR_ERR(pc->reg_pull); - } - - pc->reg_pullen = meson_map_resource(pc, gpio_np, "pull-enable"); - /* Use pull region if pull-enable one is not present */ - if (IS_ERR(pc->reg_pullen)) - pc->reg_pullen = pc->reg_pull; - pc->reg_gpio = meson_map_resource(pc, gpio_np, "gpio"); if (IS_ERR(pc->reg_gpio)) { dev_err(pc->dev, "gpio registers not found\n"); return PTR_ERR(pc->reg_gpio); } + pc->reg_pull = meson_map_resource(pc, gpio_np, "pull"); + /* Use gpio region if pull one is not present */ + if (IS_ERR(pc->reg_pull)) + pc->reg_pull = pc->reg_gpio; + + pc->reg_pullen = meson_map_resource(pc, gpio_np, "pull-enable"); + /* Use pull region if pull-enable one is not present */ + if (IS_ERR(pc->reg_pullen)) + pc->reg_pullen = pc->reg_pull; + return 0; } From 64856974a36178a0b2a1081d4cf3e8c9dd72fc44 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Thu, 17 Jan 2019 11:23:15 +0100 Subject: [PATCH 0493/4277] pinctrl: meson: add optional region for drive strength On the G12a, there is a new 'region' to handle the drive-strength. This is optional since the older do not have this. Fixes: 29ae0952e85f ("pinctrl: meson-g12a: add pinctrl driver support") Signed-off-by: Jerome Brunet Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-meson.c | 6 ++++++ drivers/pinctrl/meson/pinctrl-meson.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index a4ae1ac5369e..96a4a72708e4 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -507,6 +507,12 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc, if (IS_ERR(pc->reg_pullen)) pc->reg_pullen = pc->reg_pull; + pc->reg_ds = meson_map_resource(pc, gpio_np, "ds"); + if (IS_ERR(pc->reg_ds)) { + dev_dbg(pc->dev, "ds registers not found - skipping\n"); + pc->reg_ds = NULL; + } + return 0; } diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h index eff61ea1c67e..5eaab925f427 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.h +++ b/drivers/pinctrl/meson/pinctrl-meson.h @@ -120,6 +120,7 @@ struct meson_pinctrl { struct regmap *reg_pullen; struct regmap *reg_pull; struct regmap *reg_gpio; + struct regmap *reg_ds; struct gpio_chip chip; struct device_node *of_node; }; From 3ae4f3aac05ab9cc7d93ef3e87bb0bd159cb6bfa Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 18 Jan 2019 10:53:43 +0200 Subject: [PATCH 0494/4277] gpio: rcar: select General Output Register to set output states R-Car GPIO controller provides two interfaces to set GPIO line output signal state, and for a particular GPIO line the selected interface is determined by OUTDTSEL bit value. At the moment the driver supports only one of two interfaces, namely OUTDT General Output Register is used to control the output signal. While this selection is the default one on reset, it is not explicitly configured on probe, thus it might be possible that kernel and userspace consumers of a GPIO won't be able to set the wanted GPIO output signal. Below is a simple test case to reproduce the described problem and verify this fix in the kernel on H3 ULCB by setting non-default OUTDTSEL configuration from a bootloader: u-boot > mw.l 0xe6055440 0x3000 1 ... userspace > echo default-on > /sys/devices/platform/leds/leds/led5/trigger userspace > echo default-on > /sys/devices/platform/leds/leds/led6/trigger Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-rcar.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 068ce25ffd28..500a3596aaf4 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -40,6 +40,7 @@ struct gpio_rcar_priv { struct irq_chip irq_chip; unsigned int irq_parent; atomic_t wakeup_path; + bool has_outdtsel; bool has_both_edge_trigger; struct gpio_rcar_bank_info bank_info; }; @@ -55,6 +56,7 @@ struct gpio_rcar_priv { #define POSNEG 0x20 /* Positive/Negative Logic Select Register */ #define EDGLEVEL 0x24 /* Edge/level Select Register */ #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ +#define OUTDTSEL 0x40 /* Output Data Select Register */ #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ #define RCAR_MAX_GPIO_PER_BANK 32 @@ -235,6 +237,10 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, /* Select Input Mode or Output Mode in INOUTSEL */ gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); + /* Select General Output Register to output data in OUTDTSEL */ + if (p->has_outdtsel && output) + gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); + spin_unlock_irqrestore(&p->lock, flags); } @@ -336,14 +342,17 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, } struct gpio_rcar_info { + bool has_outdtsel; bool has_both_edge_trigger; }; static const struct gpio_rcar_info gpio_rcar_info_gen1 = { + .has_outdtsel = false, .has_both_edge_trigger = false, }; static const struct gpio_rcar_info gpio_rcar_info_gen2 = { + .has_outdtsel = true, .has_both_edge_trigger = true, }; @@ -403,10 +412,11 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) int ret; info = of_device_get_match_data(p->dev); + p->has_outdtsel = info->has_outdtsel; + p->has_both_edge_trigger = info->has_both_edge_trigger; ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; - p->has_both_edge_trigger = info->has_both_edge_trigger; if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n", From 846a415bf4408ccd38d7c0b2a036249737a5ee56 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 14 Jan 2019 11:41:25 +0000 Subject: [PATCH 0495/4277] arm64: default NR_CPUS to 256 There are shipping arm64 platforms with 256 hardware threads. So that we can make use of these with defconfig, bump the arm64 default NR_CPUS to 256. At the same time, drop a redundant comment. We only have one default for NR_CPUS, so there's nothing to sort. Acked-by: Will Deacon Signed-off-by: Mark Rutland Signed-off-by: Catalin Marinas --- arch/arm64/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index a4168d366127..4cad67b9ec0a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -792,8 +792,7 @@ config SCHED_SMT config NR_CPUS int "Maximum number of CPUs (2-4096)" range 2 4096 - # These have to remain sorted largest to smallest - default "64" + default "256" config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" From 83a680dd97ad2d1ed7a6355aa5baddbc0c8ae2ae Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 18 Jan 2019 13:52:42 +0000 Subject: [PATCH 0496/4277] arm64: asm-offsets: remove unused offsets There are a number of offsets defined in asm-offsets.c which no longer have any users. Let's clean this up by removing them. All the remaining offsets are in use. Acked-by: Mark Rutland Signed-off-by: Andrew Murray Signed-off-by: Catalin Marinas --- arch/arm64/kernel/asm-offsets.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 65b8afc84466..0552b91d7666 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -53,13 +53,9 @@ int main(void) DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context)); BLANK(); DEFINE(S_X0, offsetof(struct pt_regs, regs[0])); - DEFINE(S_X1, offsetof(struct pt_regs, regs[1])); DEFINE(S_X2, offsetof(struct pt_regs, regs[2])); - DEFINE(S_X3, offsetof(struct pt_regs, regs[3])); DEFINE(S_X4, offsetof(struct pt_regs, regs[4])); - DEFINE(S_X5, offsetof(struct pt_regs, regs[5])); DEFINE(S_X6, offsetof(struct pt_regs, regs[6])); - DEFINE(S_X7, offsetof(struct pt_regs, regs[7])); DEFINE(S_X8, offsetof(struct pt_regs, regs[8])); DEFINE(S_X10, offsetof(struct pt_regs, regs[10])); DEFINE(S_X12, offsetof(struct pt_regs, regs[12])); @@ -73,12 +69,8 @@ int main(void) DEFINE(S_X28, offsetof(struct pt_regs, regs[28])); DEFINE(S_LR, offsetof(struct pt_regs, regs[30])); DEFINE(S_SP, offsetof(struct pt_regs, sp)); -#ifdef CONFIG_COMPAT - DEFINE(S_COMPAT_SP, offsetof(struct pt_regs, compat_sp)); -#endif DEFINE(S_PSTATE, offsetof(struct pt_regs, pstate)); DEFINE(S_PC, offsetof(struct pt_regs, pc)); - DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0)); DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno)); DEFINE(S_ORIG_ADDR_LIMIT, offsetof(struct pt_regs, orig_addr_limit)); DEFINE(S_STACKFRAME, offsetof(struct pt_regs, stackframe)); @@ -93,7 +85,6 @@ int main(void) BLANK(); DEFINE(PAGE_SZ, PAGE_SIZE); BLANK(); - DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL); DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE); DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE); BLANK(); @@ -110,25 +101,18 @@ int main(void) BLANK(); DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last)); DEFINE(VDSO_RAW_TIME_SEC, offsetof(struct vdso_data, raw_time_sec)); - DEFINE(VDSO_RAW_TIME_NSEC, offsetof(struct vdso_data, raw_time_nsec)); DEFINE(VDSO_XTIME_CLK_SEC, offsetof(struct vdso_data, xtime_clock_sec)); - DEFINE(VDSO_XTIME_CLK_NSEC, offsetof(struct vdso_data, xtime_clock_nsec)); DEFINE(VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec)); DEFINE(VDSO_XTIME_CRS_NSEC, offsetof(struct vdso_data, xtime_coarse_nsec)); DEFINE(VDSO_WTM_CLK_SEC, offsetof(struct vdso_data, wtm_clock_sec)); - DEFINE(VDSO_WTM_CLK_NSEC, offsetof(struct vdso_data, wtm_clock_nsec)); DEFINE(VDSO_TB_SEQ_COUNT, offsetof(struct vdso_data, tb_seq_count)); DEFINE(VDSO_CS_MONO_MULT, offsetof(struct vdso_data, cs_mono_mult)); - DEFINE(VDSO_CS_RAW_MULT, offsetof(struct vdso_data, cs_raw_mult)); DEFINE(VDSO_CS_SHIFT, offsetof(struct vdso_data, cs_shift)); DEFINE(VDSO_TZ_MINWEST, offsetof(struct vdso_data, tz_minuteswest)); - DEFINE(VDSO_TZ_DSTTIME, offsetof(struct vdso_data, tz_dsttime)); DEFINE(VDSO_USE_SYSCALL, offsetof(struct vdso_data, use_syscall)); BLANK(); DEFINE(TVAL_TV_SEC, offsetof(struct timeval, tv_sec)); - DEFINE(TVAL_TV_USEC, offsetof(struct timeval, tv_usec)); DEFINE(TSPEC_TV_SEC, offsetof(struct timespec, tv_sec)); - DEFINE(TSPEC_TV_NSEC, offsetof(struct timespec, tv_nsec)); BLANK(); DEFINE(TZ_MINWEST, offsetof(struct timezone, tz_minuteswest)); DEFINE(TZ_DSTTIME, offsetof(struct timezone, tz_dsttime)); @@ -142,13 +126,9 @@ int main(void) DEFINE(VCPU_WORKAROUND_FLAGS, offsetof(struct kvm_vcpu, arch.workaround_flags)); DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs)); DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_regs, regs)); - DEFINE(CPU_FP_REGS, offsetof(struct kvm_regs, fp_regs)); - DEFINE(VCPU_FPEXC32_EL2, offsetof(struct kvm_vcpu, arch.ctxt.sys_regs[FPEXC32_EL2])); - DEFINE(VCPU_HOST_CONTEXT, offsetof(struct kvm_vcpu, arch.host_cpu_context)); DEFINE(HOST_CONTEXT_VCPU, offsetof(struct kvm_cpu_context, __hyp_running_vcpu)); #endif #ifdef CONFIG_CPU_PM - DEFINE(CPU_SUSPEND_SZ, sizeof(struct cpu_suspend_ctx)); DEFINE(CPU_CTX_SP, offsetof(struct cpu_suspend_ctx, sp)); DEFINE(MPIDR_HASH_MASK, offsetof(struct mpidr_hash, mask)); DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff)); From 9a3a49f9246b2abf468bad43e7615b6bab907931 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:30:16 -0200 Subject: [PATCH 0497/4277] media: soc_ov772x: remove obsolete sensor driver This driver got converted to not depend on soc_camera in commit 762c28121d7c ("media: i2c: ov772x: Remove soc_camera dependencies"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_ov772x.c | 1123 --------------------- 3 files changed, 1130 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_ov772x.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 773d067c44a3..62a6562f4f0c 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -29,12 +29,6 @@ config SOC_CAMERA_OV5642 help This is a V4L2 camera driver for the OmniVision OV5642 sensor -config SOC_CAMERA_OV772X - tristate "ov772x camera support" - depends on SOC_CAMERA && I2C - help - This is a ov772x camera driver - config SOC_CAMERA_OV9740 tristate "ov9740 camera support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index ea8da4074a01..1e82912c5b05 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/soc_ov772x.c b/drivers/media/i2c/soc_camera/soc_ov772x.c deleted file mode 100644 index fafd372527b2..000000000000 --- a/drivers/media/i2c/soc_camera/soc_ov772x.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * ov772x Camera Driver - * - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov7670 and soc_camera_platform driver, - * - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * register offset - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define BLUE 0x01 /* AWB - Blue channel gain setting */ -#define RED 0x02 /* AWB - Red channel gain setting */ -#define GREEN 0x03 /* AWB - Green channel gain setting */ -#define COM1 0x04 /* Common control 1 */ -#define BAVG 0x05 /* U/B Average Level */ -#define GAVG 0x06 /* Y/Gb Average Level */ -#define RAVG 0x07 /* V/R Average Level */ -#define AECH 0x08 /* Exposure Value - AEC MSBs */ -#define COM2 0x09 /* Common control 2 */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM4 0x0D /* Common control 4 */ -#define COM5 0x0E /* Common control 5 */ -#define COM6 0x0F /* Common control 6 */ -#define AEC 0x10 /* Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define COM7 0x12 /* Common control 7 */ -#define COM8 0x13 /* Common control 8 */ -#define COM9 0x14 /* Common control 9 */ -#define COM10 0x15 /* Common control 10 */ -#define REG16 0x16 /* Register 16 */ -#define HSTART 0x17 /* Horizontal sensor size */ -#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ -#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ -#define VSIZE 0x1A /* Vertical sensor size */ -#define PSHFT 0x1B /* Data format - pixel delay select */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define LAEC 0x1F /* Fine AEC value */ -#define COM11 0x20 /* Common control 11 */ -#define BDBASE 0x22 /* Banding filter Minimum AEC value */ -#define DBSTEP 0x23 /* Banding filter Maximum Setp */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VPT 0x26 /* AGC/AEC Fast mode operating region */ -#define REG28 0x28 /* Register 28 */ -#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ -#define EXHCH 0x2A /* Dummy pixel insert MSB */ -#define EXHCL 0x2B /* Dummy pixel insert LSB */ -#define VOUTSIZE 0x2C /* Vertical data output size MSBs */ -#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ -#define YAVE 0x2F /* Y/G Channel Average value */ -#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ -#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ -#define HREF 0x32 /* Image start and size control */ -#define DM_LNL 0x33 /* Dummy line low 8 bits */ -#define DM_LNH 0x34 /* Dummy line high 8 bits */ -#define ADOFF_B 0x35 /* AD offset compensation value for B channel */ -#define ADOFF_R 0x36 /* AD offset compensation value for R channel */ -#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ -#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ -#define OFF_B 0x39 /* Analog process B channel offset value */ -#define OFF_R 0x3A /* Analog process R channel offset value */ -#define OFF_GB 0x3B /* Analog process Gb channel offset value */ -#define OFF_GR 0x3C /* Analog process Gr channel offset value */ -#define COM12 0x3D /* Common control 12 */ -#define COM13 0x3E /* Common control 13 */ -#define COM14 0x3F /* Common control 14 */ -#define COM15 0x40 /* Common control 15*/ -#define COM16 0x41 /* Common control 16 */ -#define TGT_B 0x42 /* BLC blue channel target value */ -#define TGT_R 0x43 /* BLC red channel target value */ -#define TGT_GB 0x44 /* BLC Gb channel target value */ -#define TGT_GR 0x45 /* BLC Gr channel target value */ -/* for ov7720 */ -#define LCC0 0x46 /* Lens correction control 0 */ -#define LCC1 0x47 /* Lens correction option 1 - X coordinate */ -#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ -#define LCC3 0x49 /* Lens correction option 3 */ -#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ -#define LCC5 0x4B /* Lens correction option 5 */ -#define LCC6 0x4C /* Lens correction option 6 */ -/* for ov7725 */ -#define LC_CTR 0x46 /* Lens correction control */ -#define LC_XC 0x47 /* X coordinate of lens correction center relative */ -#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ -#define LC_COEF 0x49 /* Lens correction coefficient */ -#define LC_RADI 0x4A /* Lens correction radius */ -#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ -#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ - -#define FIXGAIN 0x4D /* Analog fix gain amplifer */ -#define AREF0 0x4E /* Sensor reference control */ -#define AREF1 0x4F /* Sensor reference current control */ -#define AREF2 0x50 /* Analog reference control */ -#define AREF3 0x51 /* ADC reference control */ -#define AREF4 0x52 /* ADC reference control */ -#define AREF5 0x53 /* ADC reference control */ -#define AREF6 0x54 /* Analog reference control */ -#define AREF7 0x55 /* Analog reference control */ -#define UFIX 0x60 /* U channel fixed value output */ -#define VFIX 0x61 /* V channel fixed value output */ -#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ -#define AWB_CTRL0 0x63 /* AWB control byte 0 */ -#define DSP_CTRL1 0x64 /* DSP control byte 1 */ -#define DSP_CTRL2 0x65 /* DSP control byte 2 */ -#define DSP_CTRL3 0x66 /* DSP control byte 3 */ -#define DSP_CTRL4 0x67 /* DSP control byte 4 */ -#define AWB_BIAS 0x68 /* AWB BLC level clip */ -#define AWB_CTRL1 0x69 /* AWB control 1 */ -#define AWB_CTRL2 0x6A /* AWB control 2 */ -#define AWB_CTRL3 0x6B /* AWB control 3 */ -#define AWB_CTRL4 0x6C /* AWB control 4 */ -#define AWB_CTRL5 0x6D /* AWB control 5 */ -#define AWB_CTRL6 0x6E /* AWB control 6 */ -#define AWB_CTRL7 0x6F /* AWB control 7 */ -#define AWB_CTRL8 0x70 /* AWB control 8 */ -#define AWB_CTRL9 0x71 /* AWB control 9 */ -#define AWB_CTRL10 0x72 /* AWB control 10 */ -#define AWB_CTRL11 0x73 /* AWB control 11 */ -#define AWB_CTRL12 0x74 /* AWB control 12 */ -#define AWB_CTRL13 0x75 /* AWB control 13 */ -#define AWB_CTRL14 0x76 /* AWB control 14 */ -#define AWB_CTRL15 0x77 /* AWB control 15 */ -#define AWB_CTRL16 0x78 /* AWB control 16 */ -#define AWB_CTRL17 0x79 /* AWB control 17 */ -#define AWB_CTRL18 0x7A /* AWB control 18 */ -#define AWB_CTRL19 0x7B /* AWB control 19 */ -#define AWB_CTRL20 0x7C /* AWB control 20 */ -#define AWB_CTRL21 0x7D /* AWB control 21 */ -#define GAM1 0x7E /* Gamma Curve 1st segment input end point */ -#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ -#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ -#define GAM4 0x81 /* Gamma Curve 4th segment input end point */ -#define GAM5 0x82 /* Gamma Curve 5th segment input end point */ -#define GAM6 0x83 /* Gamma Curve 6th segment input end point */ -#define GAM7 0x84 /* Gamma Curve 7th segment input end point */ -#define GAM8 0x85 /* Gamma Curve 8th segment input end point */ -#define GAM9 0x86 /* Gamma Curve 9th segment input end point */ -#define GAM10 0x87 /* Gamma Curve 10th segment input end point */ -#define GAM11 0x88 /* Gamma Curve 11th segment input end point */ -#define GAM12 0x89 /* Gamma Curve 12th segment input end point */ -#define GAM13 0x8A /* Gamma Curve 13th segment input end point */ -#define GAM14 0x8B /* Gamma Curve 14th segment input end point */ -#define GAM15 0x8C /* Gamma Curve 15th segment input end point */ -#define SLOP 0x8D /* Gamma curve highest segment slope */ -#define DNSTH 0x8E /* De-noise threshold */ -#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ -#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ -#define DNSOFF 0x91 /* Auto De-noise threshold control */ -#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ -#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ -#define MTX1 0x94 /* Matrix coefficient 1 */ -#define MTX2 0x95 /* Matrix coefficient 2 */ -#define MTX3 0x96 /* Matrix coefficient 3 */ -#define MTX4 0x97 /* Matrix coefficient 4 */ -#define MTX5 0x98 /* Matrix coefficient 5 */ -#define MTX6 0x99 /* Matrix coefficient 6 */ -#define MTX_CTRL 0x9A /* Matrix control */ -#define BRIGHT 0x9B /* Brightness control */ -#define CNTRST 0x9C /* Contrast contrast */ -#define CNTRST_CTRL 0x9D /* Contrast contrast center */ -#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ -#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ -#define SCAL0 0xA0 /* Scaling control 0 */ -#define SCAL1 0xA1 /* Scaling control 1 */ -#define SCAL2 0xA2 /* Scaling control 2 */ -#define FIFODLYM 0xA3 /* FIFO manual mode delay control */ -#define FIFODLYA 0xA4 /* FIFO auto mode delay control */ -#define SDE 0xA6 /* Special digital effect control */ -#define USAT 0xA7 /* U component saturation control */ -#define VSAT 0xA8 /* V component saturation control */ -/* for ov7720 */ -#define HUE0 0xA9 /* Hue control 0 */ -#define HUE1 0xAA /* Hue control 1 */ -/* for ov7725 */ -#define HUECOS 0xA9 /* Cosine value */ -#define HUESIN 0xAA /* Sine value */ - -#define SIGN 0xAB /* Sign bit for Hue and contrast */ -#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ - -/* - * register detail - */ - -/* COM2 */ -#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define OCAP_1x 0x00 /* 1x */ -#define OCAP_2x 0x01 /* 2x */ -#define OCAP_3x 0x02 /* 3x */ -#define OCAP_4x 0x03 /* 4x */ - -/* COM3 */ -#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) -#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) - -#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ -#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ -#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ -#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ -#define SWAP_ML 0x08 /* Swap output MSB/LSB */ - /* Tri-state option for output clock */ -#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ - /* 1: No tri-state at this period */ - /* Tri-state option for output data */ -#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ - /* 1: No tri-state at this period */ -#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ - -/* COM4 */ - /* PLL frequency control */ -#define PLL_BYPASS 0x00 /* 00: Bypass PLL */ -#define PLL_4x 0x40 /* 01: PLL 4x */ -#define PLL_6x 0x80 /* 10: PLL 6x */ -#define PLL_8x 0xc0 /* 11: PLL 8x */ - /* AEC evaluate window */ -#define AEC_FULL 0x00 /* 00: Full window */ -#define AEC_1p2 0x10 /* 01: 1/2 window */ -#define AEC_1p4 0x20 /* 10: 1/4 window */ -#define AEC_2p3 0x30 /* 11: Low 2/3 window */ - -/* COM5 */ -#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ -#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ - /* Auto frame rate max rate control */ -#define AFR_NO_RATE 0x00 /* No reduction of frame rate */ -#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ -#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ -#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ - /* Auto frame rate active point control */ -#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ -#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ -#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ -#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ - /* AEC max step control */ -#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ - /* 1 : No limit to AEC increase step */ - -/* COM7 */ - /* SCCB Register Reset */ -#define SCCB_RESET 0x80 /* 0 : No change */ - /* 1 : Resets all registers to default */ - /* Resolution selection */ -#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ -#define SLCT_VGA 0x00 /* 0 : VGA */ -#define SLCT_QVGA 0x40 /* 1 : QVGA */ -#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ -#define SENSOR_RAW 0x10 /* Sensor RAW */ - /* RGB output format control */ -#define FMT_MASK 0x0c /* Mask of color format */ -#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ -#define FMT_RGB565 0x04 /* 01 : RGB 565 */ -#define FMT_RGB555 0x08 /* 10 : RGB 555 */ -#define FMT_RGB444 0x0c /* 11 : RGB 444 */ - /* Output format control */ -#define OFMT_MASK 0x03 /* Mask of output format */ -#define OFMT_YUV 0x00 /* 00 : YUV */ -#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ -#define OFMT_RGB 0x02 /* 10 : RGB */ -#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ - -/* COM8 */ -#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ - /* AEC Setp size limit */ -#define UNLMT_STEP 0x40 /* 0 : Step size is limited */ - /* 1 : Unlimited step size */ -#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ -#define AEC_BND 0x10 /* Enable AEC below banding value */ -#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ -#define AGC_ON 0x04 /* AGC Enable */ -#define AWB_ON 0x02 /* AWB Enable */ -#define AEC_ON 0x01 /* AEC Enable */ - -/* COM9 */ -#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ - /* Automatic gain ceiling - maximum AGC value */ -#define GAIN_2x 0x00 /* 000 : 2x */ -#define GAIN_4x 0x10 /* 001 : 4x */ -#define GAIN_8x 0x20 /* 010 : 8x */ -#define GAIN_16x 0x30 /* 011 : 16x */ -#define GAIN_32x 0x40 /* 100 : 32x */ -#define GAIN_64x 0x50 /* 101 : 64x */ -#define GAIN_128x 0x60 /* 110 : 128x */ -#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ -#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ - -/* COM11 */ -#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ -#define SGLF_TRIG 0x01 /* Single frame transfer trigger */ - -/* HREF */ -#define HREF_VSTART_SHIFT 6 /* VSTART LSB */ -#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ -#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ -#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ - -/* EXHCH */ -#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ -#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ - -/* DSP_CTRL1 */ -#define FIFO_ON 0x80 /* FIFO enable/disable selection */ -#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ -#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ -#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ -#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ -#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ -#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ -#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ - -/* DSP_CTRL3 */ -#define UV_MASK 0x80 /* UV output sequence option */ -#define UV_ON 0x80 /* ON */ -#define UV_OFF 0x00 /* OFF */ -#define CBAR_MASK 0x20 /* DSP Color bar mask */ -#define CBAR_ON 0x20 /* ON */ -#define CBAR_OFF 0x00 /* OFF */ - -/* DSP_CTRL4 */ -#define DSP_OFMT_YUV 0x00 -#define DSP_OFMT_RGB 0x00 -#define DSP_OFMT_RAW8 0x02 -#define DSP_OFMT_RAW10 0x03 - -/* DSPAUTO (DSP Auto Function ON/OFF Control) */ -#define AWB_ACTRL 0x80 /* AWB auto threshold control */ -#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ -#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ -#define UV_ACTRL 0x10 /* UV adjust auto slope control */ -#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ -#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ - -#define OV772X_MAX_WIDTH VGA_WIDTH -#define OV772X_MAX_HEIGHT VGA_HEIGHT - -/* - * ID - */ -#define OV7720 0x7720 -#define OV7725 0x7721 -#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF)) - -/* - * struct - */ - -struct ov772x_color_format { - u32 code; - enum v4l2_colorspace colorspace; - u8 dsp3; - u8 dsp4; - u8 com3; - u8 com7; -}; - -struct ov772x_win_size { - char *name; - unsigned char com7_bit; - struct v4l2_rect rect; -}; - -struct ov772x_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - struct ov772x_camera_info *info; - const struct ov772x_color_format *cfmt; - const struct ov772x_win_size *win; - unsigned short flag_vflip:1; - unsigned short flag_hflip:1; - /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ - unsigned short band_filter; -}; - -/* - * supported color format list - */ -static const struct ov772x_color_format ov772x_cfmts[] = { - { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_YUV, - .com7 = OFMT_YUV, - }, - { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .dsp3 = UV_ON, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_YUV, - .com7 = OFMT_YUV, - }, - { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = 0x0, - .com7 = OFMT_YUV, - }, - { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_RGB, - .com7 = FMT_RGB555 | OFMT_RGB, - }, - { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = 0x0, - .com7 = FMT_RGB555 | OFMT_RGB, - }, - { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_RGB, - .com7 = FMT_RGB565 | OFMT_RGB, - }, - { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = 0x0, - .com7 = FMT_RGB565 | OFMT_RGB, - }, - { - /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, - * regardless of the COM7 value. We can thus only support 10-bit - * Bayer until someone figures it out. - */ - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_RAW10, - .com3 = 0x0, - .com7 = SENSOR_RAW | OFMT_BRAW, - }, -}; - - -/* - * window size list - */ - -static const struct ov772x_win_size ov772x_win_sizes[] = { - { - .name = "VGA", - .com7_bit = SLCT_VGA, - .rect = { - .left = 140, - .top = 14, - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - }, - }, { - .name = "QVGA", - .com7_bit = SLCT_QVGA, - .rect = { - .left = 252, - .top = 6, - .width = QVGA_WIDTH, - .height = QVGA_HEIGHT, - }, - }, -}; - -/* - * general function - */ - -static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ov772x_priv, subdev); -} - -static inline int ov772x_read(struct i2c_client *client, u8 addr) -{ - return i2c_smbus_read_byte_data(client, addr); -} - -static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) -{ - return i2c_smbus_write_byte_data(client, addr, value); -} - -static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask, - u8 set) -{ - s32 val = ov772x_read(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return ov772x_write(client, command, val); -} - -static int ov772x_reset(struct i2c_client *client) -{ - int ret; - - ret = ov772x_write(client, COM7, SCCB_RESET); - if (ret < 0) - return ret; - - msleep(1); - - return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); -} - -/* - * soc_camera_ops function - */ - -static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov772x_priv *priv = to_ov772x(sd); - - if (!enable) { - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); - return 0; - } - - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); - - dev_dbg(&client->dev, "format %d, win %s\n", - priv->cfmt->code, priv->win->name); - - return 0; -} - -static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov772x_priv *priv = container_of(ctrl->handler, - struct ov772x_priv, hdl); - struct v4l2_subdev *sd = &priv->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - u8 val; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - val = ctrl->val ? VFLIP_IMG : 0x00; - priv->flag_vflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_VFLIP) - val ^= VFLIP_IMG; - return ov772x_mask_set(client, COM3, VFLIP_IMG, val); - case V4L2_CID_HFLIP: - val = ctrl->val ? HFLIP_IMG : 0x00; - priv->flag_hflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_HFLIP) - val ^= HFLIP_IMG; - return ov772x_mask_set(client, COM3, HFLIP_IMG, val); - case V4L2_CID_BAND_STOP_FILTER: - if (!ctrl->val) { - /* Switch the filter off, it is on now */ - ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); - if (!ret) - ret = ov772x_mask_set(client, COM8, - BNDF_ON_OFF, 0); - } else { - /* Switch the filter on, set AEC low limit */ - val = 256 - ctrl->val; - ret = ov772x_mask_set(client, COM8, - BNDF_ON_OFF, BNDF_ON_OFF); - if (!ret) - ret = ov772x_mask_set(client, BDBASE, - 0xff, val); - } - if (!ret) - priv->band_filter = ctrl->val; - return ret; - } - - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov772x_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - reg->size = 1; - if (reg->reg > 0xff) - return -EINVAL; - - ret = ov772x_read(client, reg->reg); - if (ret < 0) - return ret; - - reg->val = (__u64)ret; - - return 0; -} - -static int ov772x_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff || - reg->val > 0xff) - return -EINVAL; - - return ov772x_write(client, reg->reg, reg->val); -} -#endif - -static int ov772x_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov772x_priv *priv = to_ov772x(sd); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) -{ - const struct ov772x_win_size *win = &ov772x_win_sizes[0]; - u32 best_diff = UINT_MAX; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { - u32 diff = abs(width - ov772x_win_sizes[i].rect.width) - + abs(height - ov772x_win_sizes[i].rect.height); - if (diff < best_diff) { - best_diff = diff; - win = &ov772x_win_sizes[i]; - } - } - - return win; -} - -static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, - const struct ov772x_color_format **cfmt, - const struct ov772x_win_size **win) -{ - unsigned int i; - - /* Select a format. */ - *cfmt = &ov772x_cfmts[0]; - - for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { - if (mf->code == ov772x_cfmts[i].code) { - *cfmt = &ov772x_cfmts[i]; - break; - } - } - - /* Select a window size. */ - *win = ov772x_select_win(mf->width, mf->height); -} - -static int ov772x_set_params(struct ov772x_priv *priv, - const struct ov772x_color_format *cfmt, - const struct ov772x_win_size *win) -{ - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - int ret; - u8 val; - - /* - * reset hardware - */ - ov772x_reset(client); - - /* - * Edge Ctrl - */ - if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { - - /* - * Manual Edge Control Mode - * - * Edge auto strength bit is set by default. - * Remove it when manual mode. - */ - - ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); - if (ret < 0) - goto ov772x_set_fmt_error; - - ret = ov772x_mask_set(client, - EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, - priv->info->edgectrl.threshold); - if (ret < 0) - goto ov772x_set_fmt_error; - - ret = ov772x_mask_set(client, - EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, - priv->info->edgectrl.strength); - if (ret < 0) - goto ov772x_set_fmt_error; - - } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { - /* - * Auto Edge Control Mode - * - * set upper and lower limit - */ - ret = ov772x_mask_set(client, - EDGE_UPPER, OV772X_EDGE_UPPER_MASK, - priv->info->edgectrl.upper); - if (ret < 0) - goto ov772x_set_fmt_error; - - ret = ov772x_mask_set(client, - EDGE_LOWER, OV772X_EDGE_LOWER_MASK, - priv->info->edgectrl.lower); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - /* Format and window size */ - ret = ov772x_write(client, HSTART, win->rect.left >> 2); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, HSIZE, win->rect.width >> 2); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, VSTART, win->rect.top >> 1); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, VSIZE, win->rect.height >> 1); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, HREF, - ((win->rect.top & 1) << HREF_VSTART_SHIFT) | - ((win->rect.left & 3) << HREF_HSTART_SHIFT) | - ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | - ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, EXHCH, - ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | - ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); - if (ret < 0) - goto ov772x_set_fmt_error; - - /* - * set DSP_CTRL3 - */ - val = cfmt->dsp3; - if (val) { - ret = ov772x_mask_set(client, - DSP_CTRL3, UV_MASK, val); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - /* DSP_CTRL4: AEC reference point and DSP output format. */ - if (cfmt->dsp4) { - ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - /* - * set COM3 - */ - val = cfmt->com3; - if (priv->info->flags & OV772X_FLAG_VFLIP) - val |= VFLIP_IMG; - if (priv->info->flags & OV772X_FLAG_HFLIP) - val |= HFLIP_IMG; - if (priv->flag_vflip) - val ^= VFLIP_IMG; - if (priv->flag_hflip) - val ^= HFLIP_IMG; - - ret = ov772x_mask_set(client, - COM3, SWAP_MASK | IMG_MASK, val); - if (ret < 0) - goto ov772x_set_fmt_error; - - /* COM7: Sensor resolution and output format control. */ - ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7); - if (ret < 0) - goto ov772x_set_fmt_error; - - /* - * set COM8 - */ - if (priv->band_filter) { - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); - if (!ret) - ret = ov772x_mask_set(client, BDBASE, - 0xff, 256 - priv->band_filter); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - return ret; - -ov772x_set_fmt_error: - - ov772x_reset(client); - - return ret; -} - -static int ov772x_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.width = OV772X_MAX_WIDTH; - sel->r.height = OV772X_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r.width = VGA_WIDTH; - sel->r.height = VGA_HEIGHT; - return 0; - default: - return -EINVAL; - } -} - -static int ov772x_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct ov772x_priv *priv = to_ov772x(sd); - - if (format->pad) - return -EINVAL; - - mf->width = priv->win->rect.width; - mf->height = priv->win->rect.height; - mf->code = priv->cfmt->code; - mf->colorspace = priv->cfmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int ov772x_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct ov772x_priv *priv = to_ov772x(sd); - struct v4l2_mbus_framefmt *mf = &format->format; - const struct ov772x_color_format *cfmt; - const struct ov772x_win_size *win; - int ret; - - if (format->pad) - return -EINVAL; - - ov772x_select_params(mf, &cfmt, &win); - - mf->code = cfmt->code; - mf->width = win->rect.width; - mf->height = win->rect.height; - mf->field = V4L2_FIELD_NONE; - mf->colorspace = cfmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - ret = ov772x_set_params(priv, cfmt, win); - if (ret < 0) - return ret; - - priv->win = win; - priv->cfmt = cfmt; - return 0; -} - -static int ov772x_video_probe(struct ov772x_priv *priv) -{ - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - u8 pid, ver; - const char *devname; - int ret; - - ret = ov772x_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - pid = ov772x_read(client, PID); - ver = ov772x_read(client, VER); - - switch (VERSION(pid, ver)) { - case OV7720: - devname = "ov7720"; - break; - case OV7725: - devname = "ov7725"; - break; - default: - dev_err(&client->dev, - "Product ID error %x:%x\n", pid, ver); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, - "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", - devname, - pid, - ver, - ov772x_read(client, MIDH), - ov772x_read(client, MIDL)); - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov772x_s_power(&priv->subdev, 0); - return ret; -} - -static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { - .s_ctrl = ov772x_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov772x_g_register, - .s_register = ov772x_s_register, -#endif - .s_power = ov772x_s_power, -}; - -static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) - return -EINVAL; - - code->code = ov772x_cfmts[code->index].code; - return 0; -} - -static int ov772x_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { - .s_stream = ov772x_s_stream, - .g_mbus_config = ov772x_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { - .enum_mbus_code = ov772x_enum_mbus_code, - .get_selection = ov772x_get_selection, - .get_fmt = ov772x_get_fmt, - .set_fmt = ov772x_set_fmt, -}; - -static const struct v4l2_subdev_ops ov772x_subdev_ops = { - .core = &ov772x_subdev_core_ops, - .video = &ov772x_subdev_video_ops, - .pad = &ov772x_subdev_pad_ops, -}; - -/* - * i2c_driver function - */ - -static int ov772x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov772x_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "OV772X: missing platform data!\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_PROTOCOL_MANGLING)) { - dev_err(&adapter->dev, - "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n"); - return -EIO; - } - client->flags |= I2C_CLIENT_SCCB; - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = ssdd->drv_priv; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 3); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov772x_video_probe(priv); - if (ret < 0) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } else { - priv->cfmt = &ov772x_cfmts[0]; - priv->win = &ov772x_win_sizes[0]; - } - - return ret; -} - -static int ov772x_remove(struct i2c_client *client) -{ - struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov772x_id[] = { - { "ov772x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov772x_id); - -static struct i2c_driver ov772x_i2c_driver = { - .driver = { - .name = "ov772x", - }, - .probe = ov772x_probe, - .remove = ov772x_remove, - .id_table = ov772x_id, -}; - -module_i2c_driver(ov772x_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for ov772x"); -MODULE_AUTHOR("Kuninori Morimoto"); -MODULE_LICENSE("GPL v2"); From 386a35eb70569b3158392eb573fe42589a669da4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:33:42 -0200 Subject: [PATCH 0498/4277] media: tw9910.h: remove obsolete soc_camera.h include. This include isn't use anymore, so drop it. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/i2c/tw9910.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/media/i2c/tw9910.h b/include/media/i2c/tw9910.h index bec8f7bce745..2f93799d5a21 100644 --- a/include/media/i2c/tw9910.h +++ b/include/media/i2c/tw9910.h @@ -16,8 +16,6 @@ #ifndef __TW9910_H__ #define __TW9910_H__ -#include - /** * tw9910_mpout_pin - MPOUT (multi-purpose output) pin functions */ From 4e869529ab21583dcba935d8ac4c8250e5907da1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:37:44 -0200 Subject: [PATCH 0499/4277] media: soc_tw9910: remove obsolete sensor driver This driver got converted to not depend on soc_camera in commit 7b20f325a566 ("media: i2c: tw9910: Remove soc_camera dependencies"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_tw9910.c | 999 ---------------------- 3 files changed, 1006 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_tw9910.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 62a6562f4f0c..dea66ef1394d 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -40,9 +40,3 @@ config SOC_CAMERA_RJ54N1 depends on SOC_CAMERA && I2C help This is a rj54n1cb0c video driver - -config SOC_CAMERA_TW9910 - tristate "tw9910 support" - depends on SOC_CAMERA && I2C - help - This is a tw9910 video driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 1e82912c5b05..94659f7aa195 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -4,4 +4,3 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o -obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/soc_tw9910.c b/drivers/media/i2c/soc_camera/soc_tw9910.c deleted file mode 100644 index bdb5e0a431e9..000000000000 --- a/drivers/media/i2c/soc_camera/soc_tw9910.c +++ /dev/null @@ -1,999 +0,0 @@ -/* - * tw9910 Video Driver - * - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov772x driver, - * - * Copyright (C) 2008 Kuninori Morimoto - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define GET_ID(val) ((val & 0xF8) >> 3) -#define GET_REV(val) (val & 0x07) - -/* - * register offset - */ -#define ID 0x00 /* Product ID Code Register */ -#define STATUS1 0x01 /* Chip Status Register I */ -#define INFORM 0x02 /* Input Format */ -#define OPFORM 0x03 /* Output Format Control Register */ -#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ -#define OUTCTR1 0x05 /* Output Control I */ -#define ACNTL1 0x06 /* Analog Control Register 1 */ -#define CROP_HI 0x07 /* Cropping Register, High */ -#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ -#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ -#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ -#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ -#define CNTRL1 0x0C /* Control Register I */ -#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ -#define SCALE_HI 0x0E /* Scaling Register, High */ -#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ -#define BRIGHT 0x10 /* BRIGHTNESS Control Register */ -#define CONTRAST 0x11 /* CONTRAST Control Register */ -#define SHARPNESS 0x12 /* SHARPNESS Control Register I */ -#define SAT_U 0x13 /* Chroma (U) Gain Register */ -#define SAT_V 0x14 /* Chroma (V) Gain Register */ -#define HUE 0x15 /* Hue Control Register */ -#define CORING1 0x17 -#define CORING2 0x18 /* Coring and IF compensation */ -#define VBICNTL 0x19 /* VBI Control Register */ -#define ACNTL2 0x1A /* Analog Control 2 */ -#define OUTCTR2 0x1B /* Output Control 2 */ -#define SDT 0x1C /* Standard Selection */ -#define SDTR 0x1D /* Standard Recognition */ -#define TEST 0x1F /* Test Control Register */ -#define CLMPG 0x20 /* Clamping Gain */ -#define IAGC 0x21 /* Individual AGC Gain */ -#define AGCGAIN 0x22 /* AGC Gain */ -#define PEAKWT 0x23 /* White Peak Threshold */ -#define CLMPL 0x24 /* Clamp level */ -#define SYNCT 0x25 /* Sync Amplitude */ -#define MISSCNT 0x26 /* Sync Miss Count Register */ -#define PCLAMP 0x27 /* Clamp Position Register */ -#define VCNTL1 0x28 /* Vertical Control I */ -#define VCNTL2 0x29 /* Vertical Control II */ -#define CKILL 0x2A /* Color Killer Level Control */ -#define COMB 0x2B /* Comb Filter Control */ -#define LDLY 0x2C /* Luma Delay and H Filter Control */ -#define MISC1 0x2D /* Miscellaneous Control I */ -#define LOOP 0x2E /* LOOP Control Register */ -#define MISC2 0x2F /* Miscellaneous Control II */ -#define MVSN 0x30 /* Macrovision Detection */ -#define STATUS2 0x31 /* Chip STATUS II */ -#define HFREF 0x32 /* H monitor */ -#define CLMD 0x33 /* CLAMP MODE */ -#define IDCNTL 0x34 /* ID Detection Control */ -#define CLCNTL1 0x35 /* Clamp Control I */ -#define ANAPLLCTL 0x4C -#define VBIMIN 0x4D -#define HSLOWCTL 0x4E -#define WSS3 0x4F -#define FILLDATA 0x50 -#define SDID 0x51 -#define DID 0x52 -#define WSS1 0x53 -#define WSS2 0x54 -#define VVBI 0x55 -#define LCTL6 0x56 -#define LCTL7 0x57 -#define LCTL8 0x58 -#define LCTL9 0x59 -#define LCTL10 0x5A -#define LCTL11 0x5B -#define LCTL12 0x5C -#define LCTL13 0x5D -#define LCTL14 0x5E -#define LCTL15 0x5F -#define LCTL16 0x60 -#define LCTL17 0x61 -#define LCTL18 0x62 -#define LCTL19 0x63 -#define LCTL20 0x64 -#define LCTL21 0x65 -#define LCTL22 0x66 -#define LCTL23 0x67 -#define LCTL24 0x68 -#define LCTL25 0x69 -#define LCTL26 0x6A -#define HSBEGIN 0x6B -#define HSEND 0x6C -#define OVSDLY 0x6D -#define OVSEND 0x6E -#define VBIDELAY 0x6F - -/* - * register detail - */ - -/* INFORM */ -#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ -#define FC27_FF 0x00 /* 0 : Square pixel mode. */ - /* Must use 24.54MHz for 60Hz field rate */ - /* source or 29.5MHz for 50Hz field rate */ -#define IFSEL_S 0x10 /* 01 : S-video decoding */ -#define IFSEL_C 0x00 /* 00 : Composite video decoding */ - /* Y input video selection */ -#define YSEL_M0 0x00 /* 00 : Mux0 selected */ -#define YSEL_M1 0x04 /* 01 : Mux1 selected */ -#define YSEL_M2 0x08 /* 10 : Mux2 selected */ -#define YSEL_M3 0x10 /* 11 : Mux3 selected */ - -/* OPFORM */ -#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ - /* 1 : ITU-R-656 compatible data sequence format */ -#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ - /* 1 : 16-bit YCrCb 4:2:2 output format.*/ -#define LLCMODE 0x20 /* 1 : LLC output mode. */ - /* 0 : free-run output mode */ -#define AINC 0x10 /* Serial interface auto-indexing control */ - /* 0 : auto-increment */ - /* 1 : non-auto */ -#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ - /* 0 : Vertical out ctrl by HACTIVE and DVALID */ -#define OEN_TRI_SEL_MASK 0x07 -#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */ -#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */ -#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */ - -/* OUTCTR1 */ -#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ -#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ - /* VS pin output control */ -#define VSSL_VSYNC 0x00 /* 0 : VSYNC */ -#define VSSL_VACT 0x10 /* 1 : VACT */ -#define VSSL_FIELD 0x20 /* 2 : FIELD */ -#define VSSL_VVALID 0x30 /* 3 : VVALID */ -#define VSSL_ZERO 0x70 /* 7 : 0 */ -#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ -#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ - /* HS pin output control */ -#define HSSL_HACT 0x00 /* 0 : HACT */ -#define HSSL_HSYNC 0x01 /* 1 : HSYNC */ -#define HSSL_DVALID 0x02 /* 2 : DVALID */ -#define HSSL_HLOCK 0x03 /* 3 : HLOCK */ -#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ -#define HSSL_ZERO 0x07 /* 7 : 0 */ - -/* ACNTL1 */ -#define SRESET 0x80 /* resets the device to its default state - * but all register content remain unchanged. - * This bit is self-resetting. - */ -#define ACNTL1_PDN_MASK 0x0e -#define CLK_PDN 0x08 /* system clock power down */ -#define Y_PDN 0x04 /* Luma ADC power down */ -#define C_PDN 0x02 /* Chroma ADC power down */ - -/* ACNTL2 */ -#define ACNTL2_PDN_MASK 0x40 -#define PLL_PDN 0x40 /* PLL power down */ - -/* VBICNTL */ - -/* RTSEL : control the real time signal output from the MPOUT pin */ -#define RTSEL_MASK 0x07 -#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ -#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ -#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ -#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ -#define RTSEL_MONO 0x04 /* 0100 = MONO */ -#define RTSEL_DET50 0x05 /* 0101 = DET50 */ -#define RTSEL_FIELD 0x06 /* 0110 = FIELD */ -#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ - -/* HSYNC start and end are constant for now */ -#define HSYNC_START 0x0260 -#define HSYNC_END 0x0300 - -/* - * structure - */ - -struct regval_list { - unsigned char reg_num; - unsigned char value; -}; - -struct tw9910_scale_ctrl { - char *name; - unsigned short width; - unsigned short height; - u16 hscale; - u16 vscale; -}; - -struct tw9910_priv { - struct v4l2_subdev subdev; - struct v4l2_clk *clk; - struct tw9910_video_info *info; - const struct tw9910_scale_ctrl *scale; - v4l2_std_id norm; - u32 revision; -}; - -static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { - { - .name = "NTSC SQ", - .width = 640, - .height = 480, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "NTSC CCIR601", - .width = 720, - .height = 480, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "NTSC SQ (CIF)", - .width = 320, - .height = 240, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "NTSC CCIR601 (CIF)", - .width = 360, - .height = 240, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "NTSC SQ (QCIF)", - .width = 160, - .height = 120, - .hscale = 0x0400, - .vscale = 0x0400, - }, - { - .name = "NTSC CCIR601 (QCIF)", - .width = 180, - .height = 120, - .hscale = 0x0400, - .vscale = 0x0400, - }, -}; - -static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { - { - .name = "PAL SQ", - .width = 768, - .height = 576, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "PAL CCIR601", - .width = 720, - .height = 576, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "PAL SQ (CIF)", - .width = 384, - .height = 288, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "PAL CCIR601 (CIF)", - .width = 360, - .height = 288, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "PAL SQ (QCIF)", - .width = 192, - .height = 144, - .hscale = 0x0400, - .vscale = 0x0400, - }, - { - .name = "PAL CCIR601 (QCIF)", - .width = 180, - .height = 144, - .hscale = 0x0400, - .vscale = 0x0400, - }, -}; - -/* - * general function - */ -static struct tw9910_priv *to_tw9910(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct tw9910_priv, - subdev); -} - -static int tw9910_mask_set(struct i2c_client *client, u8 command, - u8 mask, u8 set) -{ - s32 val = i2c_smbus_read_byte_data(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return i2c_smbus_write_byte_data(client, command, val); -} - -static int tw9910_set_scale(struct i2c_client *client, - const struct tw9910_scale_ctrl *scale) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, SCALE_HI, - (scale->vscale & 0x0F00) >> 4 | - (scale->hscale & 0x0F00) >> 8); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, HSCALE_LO, - scale->hscale & 0x00FF); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, VSCALE_LO, - scale->vscale & 0x00FF); - - return ret; -} - -static int tw9910_set_hsync(struct i2c_client *client) -{ - struct tw9910_priv *priv = to_tw9910(client); - int ret; - - /* bit 10 - 3 */ - ret = i2c_smbus_write_byte_data(client, HSBEGIN, - (HSYNC_START & 0x07F8) >> 3); - if (ret < 0) - return ret; - - /* bit 10 - 3 */ - ret = i2c_smbus_write_byte_data(client, HSEND, - (HSYNC_END & 0x07F8) >> 3); - if (ret < 0) - return ret; - - /* So far only revisions 0 and 1 have been seen */ - /* bit 2 - 0 */ - if (1 == priv->revision) - ret = tw9910_mask_set(client, HSLOWCTL, 0x77, - (HSYNC_START & 0x0007) << 4 | - (HSYNC_END & 0x0007)); - - return ret; -} - -static void tw9910_reset(struct i2c_client *client) -{ - tw9910_mask_set(client, ACNTL1, SRESET, SRESET); - msleep(1); -} - -static int tw9910_power(struct i2c_client *client, int enable) -{ - int ret; - u8 acntl1; - u8 acntl2; - - if (enable) { - acntl1 = 0; - acntl2 = 0; - } else { - acntl1 = CLK_PDN | Y_PDN | C_PDN; - acntl2 = PLL_PDN; - } - - ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1); - if (ret < 0) - return ret; - - return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); -} - -static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, - u32 width, u32 height) -{ - const struct tw9910_scale_ctrl *scale; - const struct tw9910_scale_ctrl *ret = NULL; - __u32 diff = 0xffffffff, tmp; - int size, i; - - if (norm & V4L2_STD_NTSC) { - scale = tw9910_ntsc_scales; - size = ARRAY_SIZE(tw9910_ntsc_scales); - } else if (norm & V4L2_STD_PAL) { - scale = tw9910_pal_scales; - size = ARRAY_SIZE(tw9910_pal_scales); - } else { - return NULL; - } - - for (i = 0; i < size; i++) { - tmp = abs(width - scale[i].width) + - abs(height - scale[i].height); - if (tmp < diff) { - diff = tmp; - ret = scale + i; - } - } - - return ret; -} - -/* - * subdevice operations - */ -static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - u8 val; - int ret; - - if (!enable) { - switch (priv->revision) { - case 0: - val = OEN_TRI_SEL_ALL_OFF_r0; - break; - case 1: - val = OEN_TRI_SEL_ALL_OFF_r1; - break; - default: - dev_err(&client->dev, "un-supported revision\n"); - return -EINVAL; - } - } else { - val = OEN_TRI_SEL_ALL_ON; - - if (!priv->scale) { - dev_err(&client->dev, "norm select error\n"); - return -EPERM; - } - - dev_dbg(&client->dev, "%s %dx%d\n", - priv->scale->name, - priv->scale->width, - priv->scale->height); - } - - ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val); - if (ret < 0) - return ret; - - return tw9910_power(client, enable); -} - -static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - - *norm = priv->norm; - - return 0; -} - -static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - const unsigned hact = 720; - const unsigned hdelay = 15; - unsigned vact; - unsigned vdelay; - int ret; - - if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) - return -EINVAL; - - priv->norm = norm; - if (norm & V4L2_STD_525_60) { - vact = 240; - vdelay = 18; - ret = tw9910_mask_set(client, VVBI, 0x10, 0x10); - } else { - vact = 288; - vdelay = 24; - ret = tw9910_mask_set(client, VVBI, 0x10, 0x00); - } - if (!ret) - ret = i2c_smbus_write_byte_data(client, CROP_HI, - ((vdelay >> 2) & 0xc0) | - ((vact >> 4) & 0x30) | - ((hdelay >> 6) & 0x0c) | - ((hact >> 8) & 0x03)); - if (!ret) - ret = i2c_smbus_write_byte_data(client, VDELAY_LO, - vdelay & 0xff); - if (!ret) - ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, - vact & 0xff); - - return ret; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int tw9910_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - ret = i2c_smbus_read_byte_data(client, reg->reg); - if (ret < 0) - return ret; - - /* - * ret = int - * reg->val = __u64 - */ - reg->val = (__u64)ret; - - return 0; -} - -static int tw9910_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff || - reg->val > 0xff) - return -EINVAL; - - return i2c_smbus_write_byte_data(client, reg->reg, reg->val); -} -#endif - -static int tw9910_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct tw9910_priv *priv = to_tw9910(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - int ret = -EINVAL; - u8 val; - - /* - * select suitable norm - */ - priv->scale = tw9910_select_norm(priv->norm, *width, *height); - if (!priv->scale) - goto tw9910_set_fmt_error; - - /* - * reset hardware - */ - tw9910_reset(client); - - /* - * set bus width - */ - val = 0x00; - if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) - val = LEN; - - ret = tw9910_mask_set(client, OPFORM, LEN, val); - if (ret < 0) - goto tw9910_set_fmt_error; - - /* - * select MPOUT behavior - */ - switch (priv->info->mpout) { - case TW9910_MPO_VLOSS: - val = RTSEL_VLOSS; break; - case TW9910_MPO_HLOCK: - val = RTSEL_HLOCK; break; - case TW9910_MPO_SLOCK: - val = RTSEL_SLOCK; break; - case TW9910_MPO_VLOCK: - val = RTSEL_VLOCK; break; - case TW9910_MPO_MONO: - val = RTSEL_MONO; break; - case TW9910_MPO_DET50: - val = RTSEL_DET50; break; - case TW9910_MPO_FIELD: - val = RTSEL_FIELD; break; - case TW9910_MPO_RTCO: - val = RTSEL_RTCO; break; - default: - val = 0; - } - - ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); - if (ret < 0) - goto tw9910_set_fmt_error; - - /* - * set scale - */ - ret = tw9910_set_scale(client, priv->scale); - if (ret < 0) - goto tw9910_set_fmt_error; - - /* - * set hsync - */ - ret = tw9910_set_hsync(client); - if (ret < 0) - goto tw9910_set_fmt_error; - - *width = priv->scale->width; - *height = priv->scale->height; - - return ret; - -tw9910_set_fmt_error: - - tw9910_reset(client); - priv->scale = NULL; - - return ret; -} - -static int tw9910_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */ - if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - if (priv->norm & V4L2_STD_NTSC) { - sel->r.width = 640; - sel->r.height = 480; - } else { - sel->r.width = 768; - sel->r.height = 576; - } - return 0; -} - -static int tw9910_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - - if (format->pad) - return -EINVAL; - - if (!priv->scale) { - priv->scale = tw9910_select_norm(priv->norm, 640, 480); - if (!priv->scale) - return -EINVAL; - } - - mf->width = priv->scale->width; - mf->height = priv->scale->height; - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - mf->field = V4L2_FIELD_INTERLACED_BT; - - return 0; -} - -static int tw9910_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u32 width = mf->width, height = mf->height; - int ret; - - WARN_ON(mf->field != V4L2_FIELD_ANY && - mf->field != V4L2_FIELD_INTERLACED_BT); - - /* - * check color format - */ - if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8) - return -EINVAL; - - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - - ret = tw9910_set_frame(sd, &width, &height); - if (!ret) { - mf->width = width; - mf->height = height; - } - return ret; -} - -static int tw9910_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - const struct tw9910_scale_ctrl *scale; - - if (format->pad) - return -EINVAL; - - if (V4L2_FIELD_ANY == mf->field) { - mf->field = V4L2_FIELD_INTERLACED_BT; - } else if (V4L2_FIELD_INTERLACED_BT != mf->field) { - dev_err(&client->dev, "Field type %d invalid.\n", mf->field); - return -EINVAL; - } - - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - - /* - * select suitable norm - */ - scale = tw9910_select_norm(priv->norm, mf->width, mf->height); - if (!scale) - return -EINVAL; - - mf->width = scale->width; - mf->height = scale->height; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return tw9910_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int tw9910_video_probe(struct i2c_client *client) -{ - struct tw9910_priv *priv = to_tw9910(client); - s32 id; - int ret; - - /* - * tw9910 only use 8 or 16 bit bus width - */ - if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && - SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&client->dev, "bus width error\n"); - return -ENODEV; - } - - ret = tw9910_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show Product ID - * So far only revisions 0 and 1 have been seen - */ - id = i2c_smbus_read_byte_data(client, ID); - priv->revision = GET_REV(id); - id = GET_ID(id); - - if (0x0B != id || - 0x01 < priv->revision) { - dev_err(&client->dev, - "Product ID error %x:%x\n", - id, priv->revision); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, - "tw9910 Product ID %0x:%0x\n", id, priv->revision); - - priv->norm = V4L2_STD_NTSC; - priv->scale = &tw9910_ntsc_scales[0]; - -done: - tw9910_s_power(&priv->subdev, 0); - return ret; -} - -static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = tw9910_g_register, - .s_register = tw9910_s_register, -#endif - .s_power = tw9910_s_power, -}; - -static int tw9910_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_UYVY8_2X8; - return 0; -} - -static int tw9910_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int tw9910_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - u8 val = VSSL_VVALID | HSSL_DVALID; - unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); - - /* - * set OUTCTR1 - * - * We use VVALID and DVALID signals to control VSYNC and HSYNC - * outputs, in this mode their polarity is inverted. - */ - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - val |= HSP_HI; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - val |= VSP_HI; - - return i2c_smbus_write_byte_data(client, OUTCTR1, val); -} - -static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - *norm = V4L2_STD_NTSC | V4L2_STD_PAL; - return 0; -} - -static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { - .s_std = tw9910_s_std, - .g_std = tw9910_g_std, - .s_stream = tw9910_s_stream, - .g_mbus_config = tw9910_g_mbus_config, - .s_mbus_config = tw9910_s_mbus_config, - .g_tvnorms = tw9910_g_tvnorms, -}; - -static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { - .enum_mbus_code = tw9910_enum_mbus_code, - .get_selection = tw9910_get_selection, - .get_fmt = tw9910_get_fmt, - .set_fmt = tw9910_set_fmt, -}; - -static const struct v4l2_subdev_ops tw9910_subdev_ops = { - .core = &tw9910_subdev_core_ops, - .video = &tw9910_subdev_video_ops, - .pad = &tw9910_subdev_pad_ops, -}; - -/* - * i2c_driver function - */ - -static int tw9910_probe(struct i2c_client *client, - const struct i2c_device_id *did) - -{ - struct tw9910_priv *priv; - struct tw9910_video_info *info; - struct i2c_adapter *adapter = - to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "TW9910: missing platform data!\n"); - return -EINVAL; - } - - info = ssdd->drv_priv; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n"); - return -EIO; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = info; - - v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = tw9910_video_probe(client); - if (ret < 0) - v4l2_clk_put(priv->clk); - - return ret; -} - -static int tw9910_remove(struct i2c_client *client) -{ - struct tw9910_priv *priv = to_tw9910(client); - v4l2_clk_put(priv->clk); - return 0; -} - -static const struct i2c_device_id tw9910_id[] = { - { "tw9910", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tw9910_id); - -static struct i2c_driver tw9910_i2c_driver = { - .driver = { - .name = "tw9910", - }, - .probe = tw9910_probe, - .remove = tw9910_remove, - .id_table = tw9910_id, -}; - -module_i2c_driver(tw9910_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for tw9910"); -MODULE_AUTHOR("Kuninori Morimoto"); -MODULE_LICENSE("GPL v2"); From 43a445f188e1a4d58e1be052b23afc9aacccbdd8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:41:23 -0200 Subject: [PATCH 0500/4277] media: sh_mobile_ceu_camera: remove obsolete soc_camera driver This driver got converted to not depend on soc_camera in commit 32e5a70dc8f4 ("media: platform: Add Renesas CEU driver"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 9 - drivers/media/platform/soc_camera/Makefile | 3 - .../soc_camera/sh_mobile_ceu_camera.c | 1810 ----------------- include/media/drv-intf/sh_mobile_ceu.h | 29 - 4 files changed, 1851 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c delete mode 100644 include/media/drv-intf/sh_mobile_ceu.h diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 669d116b8f09..94907d0611d6 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -15,12 +15,3 @@ config SOC_CAMERA_PLATFORM depends on SOC_CAMERA help This is a generic SoC camera platform driver, useful for testing - -config VIDEO_SH_MOBILE_CEU - tristate "SuperH Mobile CEU Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK - depends on ARCH_SHMOBILE || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select SOC_CAMERA_SCALE_CROP - ---help--- - This is a v4l2 driver for the SuperH Mobile CEU Interface diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 07a451e8b228..aa4b855a0882 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -4,6 +4,3 @@ obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o # a platform subdevice driver stub, allowing to support cameras by adding a # couple of callback functions to the board code obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o - -# soc-camera host drivers have to be linked after camera drivers -obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c deleted file mode 100644 index 6803f744e307..000000000000 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ /dev/null @@ -1,1810 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * V4L2 Driver for SuperH Mobile CEU interface - * - * Copyright (C) 2008 Magnus Damm - * - * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", - * - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "soc_scale_crop.h" - -/* register offsets for sh7722 / sh7723 */ - -#define CAPSR 0x00 /* Capture start register */ -#define CAPCR 0x04 /* Capture control register */ -#define CAMCR 0x08 /* Capture interface control register */ -#define CMCYR 0x0c /* Capture interface cycle register */ -#define CAMOR 0x10 /* Capture interface offset register */ -#define CAPWR 0x14 /* Capture interface width register */ -#define CAIFR 0x18 /* Capture interface input format register */ -#define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */ -#define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */ -#define CRCNTR 0x28 /* CEU register control register */ -#define CRCMPR 0x2c /* CEU register forcible control register */ -#define CFLCR 0x30 /* Capture filter control register */ -#define CFSZR 0x34 /* Capture filter size clip register */ -#define CDWDR 0x38 /* Capture destination width register */ -#define CDAYR 0x3c /* Capture data address Y register */ -#define CDACR 0x40 /* Capture data address C register */ -#define CDBYR 0x44 /* Capture data bottom-field address Y register */ -#define CDBCR 0x48 /* Capture data bottom-field address C register */ -#define CBDSR 0x4c /* Capture bundle destination size register */ -#define CFWCR 0x5c /* Firewall operation control register */ -#define CLFCR 0x60 /* Capture low-pass filter control register */ -#define CDOCR 0x64 /* Capture data output control register */ -#define CDDCR 0x68 /* Capture data complexity level register */ -#define CDDAR 0x6c /* Capture data complexity level address register */ -#define CEIER 0x70 /* Capture event interrupt enable register */ -#define CETCR 0x74 /* Capture event flag clear register */ -#define CSTSR 0x7c /* Capture status register */ -#define CSRTR 0x80 /* Capture software reset register */ -#define CDSSR 0x84 /* Capture data size register */ -#define CDAYR2 0x90 /* Capture data address Y register 2 */ -#define CDACR2 0x94 /* Capture data address C register 2 */ -#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ -#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ - -#undef DEBUG_GEOMETRY -#ifdef DEBUG_GEOMETRY -#define dev_geo dev_info -#else -#define dev_geo dev_dbg -#endif - -/* per video frame buffer */ -struct sh_mobile_ceu_buffer { - struct vb2_v4l2_buffer vb; /* v4l buffer must be first */ - struct list_head queue; -}; - -struct sh_mobile_ceu_dev { - struct soc_camera_host ici; - - unsigned int irq; - void __iomem *base; - size_t video_limit; - size_t buf_total; - - spinlock_t lock; /* Protects video buffer lists */ - struct list_head capture; - struct vb2_v4l2_buffer *active; - - struct sh_mobile_ceu_info *pdata; - struct completion complete; - - u32 cflcr; - - /* static max sizes either from platform data or default */ - int max_width; - int max_height; - - enum v4l2_field field; - int sequence; - unsigned long flags; - - unsigned int image_mode:1; - unsigned int is_16bit:1; - unsigned int frozen:1; -}; - -struct sh_mobile_ceu_cam { - /* CEU offsets within the camera output, before the CEU scaler */ - unsigned int ceu_left; - unsigned int ceu_top; - /* Client output, as seen by the CEU */ - unsigned int width; - unsigned int height; - /* - * User window from S_SELECTION / G_SELECTION, produced by client cropping and - * scaling, CEU scaling and CEU cropping, mapped back onto the client - * input window - */ - struct v4l2_rect subrect; - /* Camera cropping rectangle */ - struct v4l2_rect rect; - const struct soc_mbus_pixelfmt *extra_fmt; - u32 code; -}; - -static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_v4l2_buffer *vbuf) -{ - return container_of(vbuf, struct sh_mobile_ceu_buffer, vb); -} - -static void ceu_write(struct sh_mobile_ceu_dev *priv, - unsigned long reg_offs, u32 data) -{ - iowrite32(data, priv->base + reg_offs); -} - -static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) -{ - return ioread32(priv->base + reg_offs); -} - -static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) -{ - int i, success = 0; - - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ - - /* wait CSTSR.CPTON bit */ - for (i = 0; i < 1000; i++) { - if (!(ceu_read(pcdev, CSTSR) & 1)) { - success++; - break; - } - udelay(1); - } - - /* wait CAPSR.CPKIL bit */ - for (i = 0; i < 1000; i++) { - if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) { - success++; - break; - } - udelay(1); - } - - if (2 != success) { - dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n"); - return -EIO; - } - - return 0; -} - -/* - * Videobuf operations - */ - -/* - * .queue_setup() is called to check, whether the driver can accept the - * requested number of buffers and to fill in plane sizes - * for the current frame format if required - */ -static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - if (!vq->num_buffers) - pcdev->sequence = 0; - - if (!*count) - *count = 2; - - /* Called from VIDIOC_REQBUFS or in compatibility mode */ - if (!*num_planes) - sizes[0] = icd->sizeimage; - else if (sizes[0] < icd->sizeimage) - return -EINVAL; - - /* If *num_planes != 0, we have already verified *count. */ - if (pcdev->video_limit) { - size_t size = PAGE_ALIGN(sizes[0]) * *count; - - if (size + pcdev->buf_total > pcdev->video_limit) - *count = (pcdev->video_limit - pcdev->buf_total) / - PAGE_ALIGN(sizes[0]); - } - - *num_planes = 1; - - dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); - - return 0; -} - -#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ -#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ -#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ -#define CEU_CEIER_VBP (1 << 20) /* vbp error */ -#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ -#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) - - -/* - * return value doesn't reflex the success/failure to queue the new buffer, - * but rather the status of the previous buffer. - */ -static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) -{ - struct soc_camera_device *icd = pcdev->ici.icd; - dma_addr_t phys_addr_top, phys_addr_bottom; - unsigned long top1, top2; - unsigned long bottom1, bottom2; - u32 status; - bool planar; - int ret = 0; - - /* - * The hardware is _very_ picky about this sequence. Especially - * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge - * several not-so-well documented interrupt sources in CETCR. - */ - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); - status = ceu_read(pcdev, CETCR); - ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); - if (!pcdev->frozen) - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); - ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); - ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); - - /* - * When a VBP interrupt occurs, a capture end interrupt does not occur - * and the image of that frame is not captured correctly. So, soft reset - * is needed here. - */ - if (status & CEU_CEIER_VBP) { - sh_mobile_ceu_soft_reset(pcdev); - ret = -EIO; - } - - if (pcdev->frozen) { - complete(&pcdev->complete); - return ret; - } - - if (!pcdev->active) - return ret; - - if (V4L2_FIELD_INTERLACED_BT == pcdev->field) { - top1 = CDBYR; - top2 = CDBCR; - bottom1 = CDAYR; - bottom2 = CDACR; - } else { - top1 = CDAYR; - top2 = CDACR; - bottom1 = CDBYR; - bottom2 = CDBCR; - } - - phys_addr_top = - vb2_dma_contig_plane_dma_addr(&pcdev->active->vb2_buf, 0); - - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - planar = true; - break; - default: - planar = false; - } - - ceu_write(pcdev, top1, phys_addr_top); - if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->bytesperline; - ceu_write(pcdev, bottom1, phys_addr_bottom); - } - - if (planar) { - phys_addr_top += icd->bytesperline * icd->user_height; - ceu_write(pcdev, top2, phys_addr_top); - if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->bytesperline; - ceu_write(pcdev, bottom2, phys_addr_bottom); - } - } - - ceu_write(pcdev, CAPSR, 0x1); /* start capture */ - - return ret; -} - -static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); - - /* Added list head initialization on alloc */ - WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); - - return 0; -} - -static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); - unsigned long size; - - size = icd->sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", - vb->index, vb2_plane_size(vb, 0), size); - goto error; - } - - vb2_set_plane_payload(vb, 0, size); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - if (vb2_plane_vaddr(vb, 0)) - memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - spin_lock_irq(&pcdev->lock); - list_add_tail(&buf->queue, &pcdev->capture); - - if (!pcdev->active) { - /* - * Because there were no active buffer at this moment, - * we are not interested in the return value of - * sh_mobile_ceu_capture here. - */ - pcdev->active = vbuf; - sh_mobile_ceu_capture(pcdev); - } - spin_unlock_irq(&pcdev->lock); - - return; - -error: - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - spin_lock_irq(&pcdev->lock); - - if (pcdev->active == vbuf) { - /* disable capture (release DMA buffer), reset */ - ceu_write(pcdev, CAPSR, 1 << 16); - pcdev->active = NULL; - } - - /* - * Doesn't hurt also if the list is empty, but it hurts, if queuing the - * buffer failed, and .buf_init() hasn't been called - */ - if (buf->queue.next) - list_del_init(&buf->queue); - - pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0)); - dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__, - pcdev->buf_total); - - spin_unlock_irq(&pcdev->lock); -} - -static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0)); - dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__, - pcdev->buf_total); - - /* This is for locking debugging only */ - INIT_LIST_HEAD(&to_ceu_vb(vbuf)->queue); - return 0; -} - -static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct list_head *buf_head, *tmp; - struct vb2_v4l2_buffer *vbuf; - - spin_lock_irq(&pcdev->lock); - - pcdev->active = NULL; - - list_for_each_safe(buf_head, tmp, &pcdev->capture) { - vbuf = &list_entry(buf_head, struct sh_mobile_ceu_buffer, - queue)->vb; - vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); - list_del_init(buf_head); - } - - spin_unlock_irq(&pcdev->lock); - - sh_mobile_ceu_soft_reset(pcdev); -} - -static const struct vb2_ops sh_mobile_ceu_videobuf_ops = { - .queue_setup = sh_mobile_ceu_videobuf_setup, - .buf_prepare = sh_mobile_ceu_videobuf_prepare, - .buf_queue = sh_mobile_ceu_videobuf_queue, - .buf_cleanup = sh_mobile_ceu_videobuf_release, - .buf_init = sh_mobile_ceu_videobuf_init, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = sh_mobile_ceu_stop_streaming, -}; - -static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) -{ - struct sh_mobile_ceu_dev *pcdev = data; - struct vb2_v4l2_buffer *vbuf; - int ret; - - spin_lock(&pcdev->lock); - - vbuf = pcdev->active; - if (!vbuf) - /* Stale interrupt from a released buffer */ - goto out; - - list_del_init(&to_ceu_vb(vbuf)->queue); - - if (!list_empty(&pcdev->capture)) - pcdev->active = &list_entry(pcdev->capture.next, - struct sh_mobile_ceu_buffer, queue)->vb; - else - pcdev->active = NULL; - - ret = sh_mobile_ceu_capture(pcdev); - vbuf->vb2_buf.timestamp = ktime_get_ns(); - if (!ret) { - vbuf->field = pcdev->field; - vbuf->sequence = pcdev->sequence++; - } - vb2_buffer_done(&vbuf->vb2_buf, - ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - -out: - spin_unlock(&pcdev->lock); - - return IRQ_HANDLED; -} - -static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, - "SuperH Mobile CEU driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, - "SuperH Mobile CEU driver detached from camera %d\n", - icd->devnum); -} - -/* Called with .host_lock held */ -static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici) -{ - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - pm_runtime_get_sync(ici->v4l2_dev.dev); - - pcdev->buf_total = 0; - - sh_mobile_ceu_soft_reset(pcdev); - - return 0; -} - -/* Called with .host_lock held */ -static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici) -{ - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - /* disable capture, disable interrupts */ - ceu_write(pcdev, CEIER, 0); - sh_mobile_ceu_soft_reset(pcdev); - - /* make sure active buffer is canceled */ - spin_lock_irq(&pcdev->lock); - if (pcdev->active) { - list_del_init(&to_ceu_vb(pcdev->active)->queue); - vb2_buffer_done(&pcdev->active->vb2_buf, VB2_BUF_STATE_ERROR); - pcdev->active = NULL; - } - spin_unlock_irq(&pcdev->lock); - - pm_runtime_put(ici->v4l2_dev.dev); -} - -/* - * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)" - * in SH7722 Hardware Manual - */ -static unsigned int size_dst(unsigned int src, unsigned int scale) -{ - unsigned int mant_pre = scale >> 12; - if (!src || !scale) - return src; - return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) * - mant_pre * 4096 / scale + 1; -} - -static u16 calc_scale(unsigned int src, unsigned int *dst) -{ - u16 scale; - - if (src == *dst) - return 0; - - scale = (src * 4096 / *dst) & ~7; - - while (scale > 4096 && size_dst(src, scale) < *dst) - scale -= 8; - - *dst = size_dst(src, scale); - - return scale; -} - -/* rect is guaranteed to not exceed the scaled camera rectangle */ -static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned int height, width, cdwdr_width, in_width, in_height; - unsigned int left_offset, top_offset; - u32 camor; - - dev_geo(icd->parent, "Crop %ux%u@%u:%u\n", - icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); - - left_offset = cam->ceu_left; - top_offset = cam->ceu_top; - - WARN_ON(icd->user_width & 3 || icd->user_height & 3); - - width = icd->user_width; - - if (pcdev->image_mode) { - in_width = cam->width; - if (!pcdev->is_16bit) { - in_width *= 2; - left_offset *= 2; - } - } else { - unsigned int w_factor; - - switch (icd->current_fmt->host_fmt->packing) { - case SOC_MBUS_PACKING_2X8_PADHI: - w_factor = 2; - break; - default: - w_factor = 1; - } - - in_width = cam->width * w_factor; - left_offset *= w_factor; - } - - cdwdr_width = icd->bytesperline; - - height = icd->user_height; - in_height = cam->height; - if (V4L2_FIELD_NONE != pcdev->field) { - height = (height / 2) & ~3; - in_height /= 2; - top_offset /= 2; - cdwdr_width *= 2; - } - - /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ - camor = left_offset | (top_offset << 16); - - dev_geo(icd->parent, - "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor, - (in_height << 16) | in_width, (height << 16) | width, - cdwdr_width); - - ceu_write(pcdev, CAMOR, camor); - ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); - /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */ - ceu_write(pcdev, CFSZR, (height << 16) | width); - ceu_write(pcdev, CDWDR, cdwdr_width); -} - -static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev) -{ - u32 capsr = ceu_read(pcdev, CAPSR); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */ - return capsr; -} - -static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) -{ - unsigned long timeout = jiffies + 10 * HZ; - - /* - * Wait until the end of the current frame. It can take a long time, - * but if it has been aborted by a CAPSR reset, it shoule exit sooner. - */ - while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout)) - msleep(1); - - if (time_after(jiffies, timeout)) { - dev_err(pcdev->ici.v4l2_dev.dev, - "Timeout waiting for frame end! Interface problem?\n"); - return; - } - - /* Wait until reset clears, this shall not hang... */ - while (ceu_read(pcdev, CAPSR) & (1 << 16)) - udelay(10); - - /* Anything to restore? */ - if (capsr & ~(1 << 16)) - ceu_write(pcdev, CAPSR, capsr); -} - -#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_DATA_ACTIVE_HIGH) - -/* Capture is not running, no interrupts, no locking needed */ -static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long value, common_flags = CEU_BUS_FLAGS; - u32 capsr = capture_save_reset(pcdev); - unsigned int yuv_lineskip; - int ret; - - /* - * If the client doesn't implement g_mbus_config, we just use our - * platform data - */ - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - common_flags); - if (!common_flags) - return -EINVAL; - } else if (ret != -ENOIOCTLCMD) { - return ret; - } - - /* Make choises, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - if (icd->current_fmt->host_fmt->bits_per_sample > 8) - pcdev->is_16bit = 1; - else - pcdev->is_16bit = 0; - - ceu_write(pcdev, CRCNTR, 0); - ceu_write(pcdev, CRCMPR, 0); - - value = 0x00000010; /* data fetch by default */ - yuv_lineskip = 0x10; - - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - /* convert 4:2:2 -> 4:2:0 */ - yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */ - /* fall-through */ - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - switch (cam->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */ - break; - case MEDIA_BUS_FMT_YUYV8_2X8: - value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */ - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */ - break; - default: - BUG(); - } - } - - if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 || - icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61) - value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */ - - value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; - value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - - if (pcdev->is_16bit) - value |= 1 << 12; - else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT) - value |= 2 << 12; - - ceu_write(pcdev, CAMCR, value); - - ceu_write(pcdev, CAPCR, 0x00300000); - - switch (pcdev->field) { - case V4L2_FIELD_INTERLACED_TB: - value = 0x101; - break; - case V4L2_FIELD_INTERLACED_BT: - value = 0x102; - break; - default: - value = 0; - break; - } - ceu_write(pcdev, CAIFR, value); - - sh_mobile_ceu_set_rect(icd); - mdelay(1); - - dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr); - ceu_write(pcdev, CFLCR, pcdev->cflcr); - - /* - * A few words about byte order (observed in Big Endian mode) - * - * In data fetch mode bytes are received in chunks of 8 bytes. - * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first) - * - * The data is however by default written to memory in reverse order: - * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte) - * - * The lowest three bits of CDOCR allows us to do swapping, - * using 7 we swap the data bytes to match the incoming order: - * D0, D1, D2, D3, D4, D5, D6, D7 - */ - value = 0x00000007 | yuv_lineskip; - - ceu_write(pcdev, CDOCR, value); - ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ - - capture_restore(pcdev, capsr); - - /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ - return 0; -} - -static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, - unsigned char buswidth) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - unsigned long common_flags = CEU_BUS_FLAGS; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) - common_flags = soc_mbus_config_compatible(&cfg, - common_flags); - else if (ret != -ENOIOCTLCMD) - return ret; - - if (!common_flags || buswidth > 16) - return -EINVAL; - - return 0; -} - -static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .name = "NV12", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .name = "NV21", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, - }, { - .fourcc = V4L2_PIX_FMT_NV16, - .name = "NV16", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .name = "NV61", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, - }, -}; - -/* This will be corrected as we get more formats */ -static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) -{ - return fmt->packing == SOC_MBUS_PACKING_NONE || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_1_5X8) || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || - (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); -} - -static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct soc_camera_device, - ctrl_handler); -} - -static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct soc_camera_device *icd = ctrl_to_icd(ctrl); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - ceu_write(pcdev, CLFCR, !ctrl->val); - return 0; - } - break; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = { - .s_ctrl = sh_mobile_ceu_s_ctrl, -}; - -static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret, k, n; - int formats = 0; - struct sh_mobile_ceu_cam *cam; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code); - return 0; - } - - ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - - if (!icd->host_priv) { - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - struct v4l2_rect rect; - int shift = 0; - - /* Add our control */ - v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 1, 1, 1); - if (icd->ctrl_handler.error) - return icd->ctrl_handler.error; - - /* FIXME: subwindow is lost between close / open */ - - /* Cache current client geometry */ - ret = soc_camera_client_g_rect(sd, &rect); - if (ret < 0) - return ret; - - /* First time */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - /* - * All currently existing CEU implementations support 2560x1920 - * or larger frames. If the sensor is proposing too big a frame, - * don't bother with possibly supportred by the CEU larger - * sizes, just try VGA multiples. If needed, this can be - * adjusted in the future. - */ - while ((mf->width > pcdev->max_width || - mf->height > pcdev->max_height) && shift < 4) { - /* Try 2560x1920, 1280x960, 640x480, 320x240 */ - mf->width = 2560 >> shift; - mf->height = 1920 >> shift; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - shift++; - } - - if (shift == 4) { - dev_err(dev, "Failed to configure the client below %ux%x\n", - mf->width, mf->height); - return -EIO; - } - - dev_geo(dev, "camera fmt %ux%u\n", mf->width, mf->height); - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - /* We are called with current camera crop, initialise subrect with it */ - cam->rect = rect; - cam->subrect = rect; - - cam->width = mf->width; - cam->height = mf->height; - - icd->host_priv = cam; - } else { - cam = icd->host_priv; - } - - /* Beginning of a pass */ - if (!idx) - cam->extra_fmt = NULL; - - switch (code.code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - if (cam->extra_fmt) - break; - - /* - * Our case is simple so far: for any of the above four camera - * formats we add all our four synthesized NV* formats, so, - * just marking the device with a single flag suffices. If - * the format generation rules are more complex, you would have - * to actually hang your already added / counted formats onto - * the host_priv pointer and check whether the format you're - * going to add now is already there. - */ - cam->extra_fmt = sh_mobile_ceu_formats; - - n = ARRAY_SIZE(sh_mobile_ceu_formats); - formats += n; - for (k = 0; xlate && k < n; k++) { - xlate->host_fmt = &sh_mobile_ceu_formats[k]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code %d\n", - sh_mobile_ceu_formats[k].name, code.code); - } - break; - default: - if (!sh_mobile_ceu_packing_supported(fmt)) - return 0; - } - - /* Generic pass-through */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s in pass-through mode\n", - fmt->name); - } - - return formats; -} - -static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) -{ - kfree(icd->host_priv); - icd->host_priv = NULL; -} - -#define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale) -#define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out) - -/* - * CEU can scale and crop, but we don't want to waste bandwidth and kill the - * framerate by always requesting the maximum image from the client. See - * Documentation/media/v4l-drivers/sh_mobile_ceu_camera.rst for a description of - * scaling and cropping algorithms and for the meaning of referenced here steps. - */ -static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_rect *rect = &sel->r; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_selection cam_sel; - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_sel.r; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, - out_width, out_height; - int interm_width, interm_height; - u32 capsr, cflcr; - int ret; - - dev_geo(dev, "S_SELECTION(%ux%u@%u:%u)\n", rect->width, rect->height, - rect->left, rect->top); - - /* During camera cropping its output window can change too, stop CEU */ - capsr = capture_save_reset(pcdev); - dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); - - /* - * 1. - 2. Apply iterative camera S_SELECTION for new input window, read back - * actual camera rectangle. - */ - ret = soc_camera_client_s_selection(sd, sel, &cam_sel, - &cam->rect, &cam->subrect); - if (ret < 0) - return ret; - - dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n", - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - - /* On success cam_crop contains current camera crop */ - - /* 3. Retrieve camera output window */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (mf->width > pcdev->max_width || mf->height > pcdev->max_height) - return -EINVAL; - - /* 4. Calculate camera scales */ - scale_cam_h = calc_generic_scale(cam_rect->width, mf->width); - scale_cam_v = calc_generic_scale(cam_rect->height, mf->height); - - /* Calculate intermediate window */ - interm_width = scale_down(rect->width, scale_cam_h); - interm_height = scale_down(rect->height, scale_cam_v); - - if (interm_width < icd->user_width) { - u32 new_scale_h; - - new_scale_h = calc_generic_scale(rect->width, icd->user_width); - - mf->width = scale_down(cam_rect->width, new_scale_h); - } - - if (interm_height < icd->user_height) { - u32 new_scale_v; - - new_scale_v = calc_generic_scale(rect->height, icd->user_height); - - mf->height = scale_down(cam_rect->height, new_scale_v); - } - - if (interm_width < icd->user_width || interm_height < icd->user_height) { - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - dev_geo(dev, "New camera output %ux%u\n", mf->width, mf->height); - scale_cam_h = calc_generic_scale(cam_rect->width, mf->width); - scale_cam_v = calc_generic_scale(cam_rect->height, mf->height); - interm_width = scale_down(rect->width, scale_cam_h); - interm_height = scale_down(rect->height, scale_cam_v); - } - - /* Cache camera output window */ - cam->width = mf->width; - cam->height = mf->height; - - if (pcdev->image_mode) { - out_width = min(interm_width, icd->user_width); - out_height = min(interm_height, icd->user_height); - } else { - out_width = interm_width; - out_height = interm_height; - } - - /* - * 5. Calculate CEU scales from camera scales from results of (5) and - * the user window - */ - scale_ceu_h = calc_scale(interm_width, &out_width); - scale_ceu_v = calc_scale(interm_height, &out_height); - - dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); - - /* Apply CEU scales. */ - cflcr = scale_ceu_h | (scale_ceu_v << 16); - if (cflcr != pcdev->cflcr) { - pcdev->cflcr = cflcr; - ceu_write(pcdev, CFLCR, cflcr); - } - - icd->user_width = out_width & ~3; - icd->user_height = out_height & ~3; - /* Offsets are applied at the CEU scaling filter input */ - cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1; - cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1; - - /* 6. Use CEU cropping to crop to the new window. */ - sh_mobile_ceu_set_rect(icd); - - cam->subrect = *rect; - - dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n", - icd->user_width, icd->user_height, - cam->ceu_left, cam->ceu_top); - - /* Restore capture. The CE bit can be cleared by the hardware */ - if (pcdev->active) - capsr |= 1; - capture_restore(pcdev, capsr); - - /* Even if only camera cropping succeeded */ - return ret; -} - -static int sh_mobile_ceu_get_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - - sel->r = cam->subrect; - - return 0; -} - -/* Similar to set_crop multistage iterative algorithm */ -static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; - __u32 pixfmt = pix->pixelformat; - const struct soc_camera_format_xlate *xlate; - unsigned int ceu_sub_width = pcdev->max_width, - ceu_sub_height = pcdev->max_height; - u16 scale_v, scale_h; - int ret; - bool image_mode; - enum v4l2_field field; - - switch (pix->field) { - default: - pix->field = V4L2_FIELD_NONE; - /* fall-through */ - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - case V4L2_FIELD_NONE: - field = pix->field; - break; - case V4L2_FIELD_INTERLACED: - field = V4L2_FIELD_INTERLACED_TB; - break; - } - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* 1.-4. Calculate desired client output geometry */ - soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, 12); - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; - - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - image_mode = true; - break; - default: - image_mode = false; - } - - dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code, - pix->width, pix->height); - - dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); - - /* 5. - 9. */ - ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, - &mf, &ceu_sub_width, &ceu_sub_height, - image_mode && V4L2_FIELD_NONE == field, 12); - - dev_geo(dev, "5-9: client scale return %d\n", ret); - - /* Done with the camera. Now see if we can improve the result */ - - dev_geo(dev, "fmt %ux%u, requested %ux%u\n", - mf.width, mf.height, pix->width, pix->height); - if (ret < 0) - return ret; - - if (mf.code != xlate->code) - return -EINVAL; - - /* 9. Prepare CEU crop */ - cam->width = mf.width; - cam->height = mf.height; - - /* 10. Use CEU scaling to scale to the requested user window. */ - - /* We cannot scale up */ - if (pix->width > ceu_sub_width) - ceu_sub_width = pix->width; - - if (pix->height > ceu_sub_height) - ceu_sub_height = pix->height; - - pix->colorspace = mf.colorspace; - - if (image_mode) { - /* Scale pix->{width x height} down to width x height */ - scale_h = calc_scale(ceu_sub_width, &pix->width); - scale_v = calc_scale(ceu_sub_height, &pix->height); - } else { - pix->width = ceu_sub_width; - pix->height = ceu_sub_height; - scale_h = 0; - scale_v = 0; - } - - pcdev->cflcr = scale_h | (scale_v << 16); - - /* - * We have calculated CFLCR, the actual configuration will be performed - * in sh_mobile_ceu_set_bus_param() - */ - - dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", - ceu_sub_width, scale_h, pix->width, - ceu_sub_height, scale_v, pix->height); - - cam->code = xlate->code; - icd->current_fmt = xlate; - - pcdev->field = field; - pcdev->image_mode = image_mode; - - /* CFSZR requirement */ - pix->width &= ~3; - pix->height &= ~3; - - return 0; -} - -#define CEU_CHDW_MAX 8188U /* Maximum line stride */ - -static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int width, height; - int ret; - - dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n", - pixfmt, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - xlate = icd->current_fmt; - dev_dbg(icd->parent, "Format %x not found, keeping %x\n", - pixfmt, xlate->host_fmt->fourcc); - pixfmt = xlate->host_fmt->fourcc; - pix->pixelformat = pixfmt; - pix->colorspace = icd->colorspace; - } - - /* FIXME: calculate using depth and bus width */ - - /* CFSZR requires height and width to be 4-pixel aligned */ - v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2, - &pix->height, 4, pcdev->max_height, 2, 0); - - width = pix->width; - height = pix->height; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->code = xlate->code; - mf->colorspace = pix->colorspace; - - ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - /* FIXME: check against rect_max after converting soc-camera */ - /* We can scale precisely, need a bigger image from camera */ - if (pix->width < width || pix->height < height) { - /* - * We presume, the sensor behaves sanely, i.e., if - * requested a bigger rectangle, it will not return a - * smaller one. - */ - mf->width = pcdev->max_width; - mf->height = pcdev->max_height; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, &pad_cfg, &format); - if (ret < 0) { - /* Shouldn't actually happen... */ - dev_err(icd->parent, - "FIXME: client try_fmt() = %d\n", ret); - return ret; - } - } - /* We will scale exactly */ - if (mf->width > width) - pix->width = width; - if (mf->height > height) - pix->height = height; - - pix->bytesperline = max(pix->bytesperline, pix->width); - pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX); - pix->bytesperline &= ~3; - break; - - default: - /* Configurable stride isn't supported in pass-through mode. */ - pix->bytesperline = 0; - } - - pix->width &= ~3; - pix->height &= ~3; - pix->sizeimage = 0; - - dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", - __func__, ret, pix->pixelformat, pix->width, pix->height); - - return ret; -} - -static int sh_mobile_ceu_set_liveselection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - u32 out_width = icd->user_width, out_height = icd->user_height; - int ret; - - /* Freeze queue */ - pcdev->frozen = 1; - /* Wait for frame */ - ret = wait_for_completion_interruptible(&pcdev->complete); - /* Stop the client */ - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret < 0) - dev_warn(icd->parent, - "Client failed to stop the stream: %d\n", ret); - else - /* Do the crop, if it fails, there's nothing more we can do */ - sh_mobile_ceu_set_selection(icd, sel); - - dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); - - if (icd->user_width != out_width || icd->user_height != out_height) { - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = out_width, - .height = out_height, - .pixelformat = icd->current_fmt->host_fmt->fourcc, - .field = pcdev->field, - .colorspace = icd->colorspace, - }, - }; - ret = sh_mobile_ceu_set_fmt(icd, &f); - if (!ret && (out_width != f.fmt.pix.width || - out_height != f.fmt.pix.height)) - ret = -EINVAL; - if (!ret) { - icd->user_width = out_width & ~3; - icd->user_height = out_height & ~3; - ret = sh_mobile_ceu_set_bus_param(icd); - } - } - - /* Thaw the queue */ - pcdev->frozen = 0; - spin_lock_irq(&pcdev->lock); - sh_mobile_ceu_capture(pcdev); - spin_unlock_irq(&pcdev->lock); - /* Start the client */ - ret = v4l2_subdev_call(sd, video, s_stream, 1); - return ret; -} - -static __poll_t sh_mobile_ceu_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - strscpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); - strscpy(cap->driver, "sh_mobile_ceu", sizeof(cap->driver)); - strscpy(cap->bus_info, "platform:sh_mobile_ceu", sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &sh_mobile_ceu_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ici->host_lock; - q->dev = ici->v4l2_dev.dev; - - return vb2_queue_init(q); -} - -static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { - .owner = THIS_MODULE, - .add = sh_mobile_ceu_add_device, - .remove = sh_mobile_ceu_remove_device, - .clock_start = sh_mobile_ceu_clock_start, - .clock_stop = sh_mobile_ceu_clock_stop, - .get_formats = sh_mobile_ceu_get_formats, - .put_formats = sh_mobile_ceu_put_formats, - .get_selection = sh_mobile_ceu_get_selection, - .set_selection = sh_mobile_ceu_set_selection, - .set_liveselection = sh_mobile_ceu_set_liveselection, - .set_fmt = sh_mobile_ceu_set_fmt, - .try_fmt = sh_mobile_ceu_try_fmt, - .poll = sh_mobile_ceu_poll, - .querycap = sh_mobile_ceu_querycap, - .set_bus_param = sh_mobile_ceu_set_bus_param, - .init_videobuf2 = sh_mobile_ceu_init_videobuf, -}; - -struct bus_wait { - struct notifier_block notifier; - struct completion completion; - struct device *dev; -}; - -static int bus_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct device *dev = data; - struct bus_wait *wait = container_of(nb, struct bus_wait, notifier); - - if (wait->dev != dev) - return NOTIFY_DONE; - - switch (action) { - case BUS_NOTIFY_UNBOUND_DRIVER: - /* Protect from module unloading */ - wait_for_completion(&wait->completion); - return NOTIFY_OK; - } - return NOTIFY_DONE; -} - -static int sh_mobile_ceu_probe(struct platform_device *pdev) -{ - struct sh_mobile_ceu_dev *pcdev; - struct resource *res; - void __iomem *base; - unsigned int irq; - int err; - struct bus_wait wait = { - .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), - .notifier.notifier_call = bus_notify, - }; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { - dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); - return -ENODEV; - } - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); - if (!pcdev) { - dev_err(&pdev->dev, "Could not allocate pcdev\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&pcdev->capture); - spin_lock_init(&pcdev->lock); - init_completion(&pcdev->complete); - - pcdev->pdata = pdev->dev.platform_data; - if (!pcdev->pdata && !pdev->dev.of_node) { - dev_err(&pdev->dev, "CEU platform data not set.\n"); - return -EINVAL; - } - - /* TODO: implement per-device bus flags */ - if (pcdev->pdata) { - pcdev->max_width = pcdev->pdata->max_width; - pcdev->max_height = pcdev->pdata->max_height; - pcdev->flags = pcdev->pdata->flags; - } - pcdev->field = V4L2_FIELD_NONE; - - if (!pcdev->max_width) { - unsigned int v; - err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v); - if (!err) - pcdev->max_width = v; - - if (!pcdev->max_width) - pcdev->max_width = 2560; - } - if (!pcdev->max_height) { - unsigned int v; - err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v); - if (!err) - pcdev->max_height = v; - - if (!pcdev->max_height) - pcdev->max_height = 1920; - } - - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - pcdev->irq = irq; - pcdev->base = base; - pcdev->video_limit = 0; /* only enabled if second resource exists */ - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - err = dma_declare_coherent_memory(&pdev->dev, res->start, - res->start, - resource_size(res), - DMA_MEMORY_EXCLUSIVE); - if (err) { - dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - return err; - } - - pcdev->video_limit = resource_size(res); - } - - /* request irq */ - err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, - 0, dev_name(&pdev->dev), pcdev); - if (err) { - dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); - goto exit_release_mem; - } - - pm_suspend_ignore_children(&pdev->dev, true); - pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&pdev->dev); - - pcdev->ici.priv = pcdev; - pcdev->ici.v4l2_dev.dev = &pdev->dev; - pcdev->ici.nr = pdev->id; - pcdev->ici.drv_name = dev_name(&pdev->dev); - pcdev->ici.ops = &sh_mobile_ceu_host_ops; - pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; - - if (pcdev->pdata && pcdev->pdata->asd_sizes) { - pcdev->ici.asd = pcdev->pdata->asd; - pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes; - } - - err = soc_camera_host_register(&pcdev->ici); - if (err) - goto exit_free_clk; - - return 0; - -exit_free_clk: - pm_runtime_disable(&pdev->dev); -exit_release_mem: - if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) - dma_release_declared_memory(&pdev->dev); - return err; -} - -static int sh_mobile_ceu_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - - soc_camera_host_unregister(soc_host); - pm_runtime_disable(&pdev->dev); - if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) - dma_release_declared_memory(&pdev->dev); - - return 0; -} - -static int sh_mobile_ceu_runtime_nop(struct device *dev) -{ - /* Runtime PM callback shared between ->runtime_suspend() - * and ->runtime_resume(). Simply returns success. - * - * This driver re-initializes all registers after - * pm_runtime_get_sync() anyway so there is no need - * to save and restore registers here. - */ - return 0; -} - -static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { - .runtime_suspend = sh_mobile_ceu_runtime_nop, - .runtime_resume = sh_mobile_ceu_runtime_nop, -}; - -static const struct of_device_id sh_mobile_ceu_of_match[] = { - { .compatible = "renesas,sh-mobile-ceu" }, - { } -}; -MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match); - -static struct platform_driver sh_mobile_ceu_driver = { - .driver = { - .name = "sh_mobile_ceu", - .pm = &sh_mobile_ceu_dev_pm_ops, - .of_match_table = sh_mobile_ceu_of_match, - }, - .probe = sh_mobile_ceu_probe, - .remove = sh_mobile_ceu_remove, -}; - -module_platform_driver(sh_mobile_ceu_driver); - -MODULE_DESCRIPTION("SuperH Mobile CEU driver"); -MODULE_AUTHOR("Magnus Damm"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1.0"); -MODULE_ALIAS("platform:sh_mobile_ceu"); diff --git a/include/media/drv-intf/sh_mobile_ceu.h b/include/media/drv-intf/sh_mobile_ceu.h deleted file mode 100644 index 555f0ecc0fde..000000000000 --- a/include/media/drv-intf/sh_mobile_ceu.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_SH_MOBILE_CEU_H__ -#define __ASM_SH_MOBILE_CEU_H__ - -#define SH_CEU_FLAG_USE_8BIT_BUS (1 << 0) /* use 8bit bus width */ -#define SH_CEU_FLAG_USE_16BIT_BUS (1 << 1) /* use 16bit bus width */ -#define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ -#define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ -#define SH_CEU_FLAG_LOWER_8BIT (1 << 4) /* default upper 8bit */ - -struct device; -struct resource; - -struct sh_mobile_ceu_companion { - u32 num_resources; - struct resource *resource; - int id; - void *platform_data; -}; - -struct sh_mobile_ceu_info { - unsigned long flags; - int max_width; - int max_height; - struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ - unsigned int *asd_sizes; /* 0-terminated array pf asd group sizes */ -}; - -#endif /* __ASM_SH_MOBILE_CEU_H__ */ From e48fef523e9bee354f27ae5d0b4927006a2a19c0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:44:21 -0200 Subject: [PATCH 0501/4277] media: soc_camera/soc_scale_crop: drop this unused code With the removal of sh_mobile_ceu_camera.c this code is no longer used and can be removed. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 3 - drivers/media/platform/soc_camera/Makefile | 1 - .../platform/soc_camera/soc_scale_crop.c | 426 ------------------ .../platform/soc_camera/soc_scale_crop.h | 47 -- 4 files changed, 477 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/soc_scale_crop.c delete mode 100644 drivers/media/platform/soc_camera/soc_scale_crop.h diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 94907d0611d6..d471d34b884c 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -7,9 +7,6 @@ config SOC_CAMERA over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. -config SOC_CAMERA_SCALE_CROP - tristate - config SOC_CAMERA_PLATFORM tristate "platform camera support" depends on SOC_CAMERA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index aa4b855a0882..2cb7022e073b 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o -obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o # a platform subdevice driver stub, allowing to support cameras by adding a # couple of callback functions to the board code diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c deleted file mode 100644 index 8d25ca0490f7..000000000000 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * soc-camera generic scaling-cropping manipulation functions - * - * Copyright (C) 2013 Guennadi Liakhovetski - * - * 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. - */ - -#include -#include - -#include -#include - -#include "soc_scale_crop.h" - -#ifdef DEBUG_GEOMETRY -#define dev_geo dev_info -#else -#define dev_geo dev_dbg -#endif - -/* Check if any dimension of r1 is smaller than respective one of r2 */ -static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->width < r2->width || r1->height < r2->height; -} - -/* Check if r1 fails to cover r2 */ -static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->left > r2->left || r1->top > r2->top || - r1->left + r1->width < r2->left + r2->width || - r1->top + r1->height < r2->top + r2->height; -} - -/* Get and store current client crop */ -int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) -{ - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (!ret) { - *rect = sdsel.r; - return ret; - } - - sdsel.target = V4L2_SEL_TGT_CROP_BOUNDS; - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (!ret) - *rect = sdsel.r; - - return ret; -} -EXPORT_SYMBOL(soc_camera_client_g_rect); - -/* Client crop has changed, update our sub-rectangle to remain within the area */ -static void move_and_crop_subrect(struct v4l2_rect *rect, - struct v4l2_rect *subrect) -{ - if (rect->width < subrect->width) - subrect->width = rect->width; - - if (rect->height < subrect->height) - subrect->height = rect->height; - - if (rect->left > subrect->left) - subrect->left = rect->left; - else if (rect->left + rect->width < - subrect->left + subrect->width) - subrect->left = rect->left + rect->width - - subrect->width; - - if (rect->top > subrect->top) - subrect->top = rect->top; - else if (rect->top + rect->height < - subrect->top + subrect->height) - subrect->top = rect->top + rect->height - - subrect->height; -} - -/* - * The common for both scaling and cropping iterative approach is: - * 1. try if the client can produce exactly what requested by the user - * 2. if (1) failed, try to double the client image until we get one big enough - * 3. if (2) failed, try to request the maximum image - */ -int soc_camera_client_s_selection(struct v4l2_subdev *sd, - struct v4l2_selection *sel, struct v4l2_selection *cam_sel, - struct v4l2_rect *target_rect, struct v4l2_rect *subrect) -{ - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - struct v4l2_subdev_selection bounds = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP_BOUNDS, - }; - struct v4l2_rect *rect = &sel->r, *cam_rect = &cam_sel->r; - struct device *dev = sd->v4l2_dev->dev; - int ret; - unsigned int width, height; - - v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - sel->r = sdsel.r; - ret = soc_camera_client_g_rect(sd, cam_rect); - if (ret < 0) - return ret; - - /* - * Now cam_crop contains the current camera input rectangle, and it must - * be within camera cropcap bounds - */ - if (!memcmp(rect, cam_rect, sizeof(*rect))) { - /* Even if camera S_SELECTION failed, but camera rectangle matches */ - dev_dbg(dev, "Camera S_SELECTION successful for %dx%d@%d:%d\n", - rect->width, rect->height, rect->left, rect->top); - *target_rect = *cam_rect; - return 0; - } - - /* Try to fix cropping, that camera hasn't managed to set */ - dev_geo(dev, "Fix camera S_SELECTION for %dx%d@%d:%d to %dx%d@%d:%d\n", - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top, - rect->width, rect->height, rect->left, rect->top); - - /* We need sensor maximum rectangle */ - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &bounds); - if (ret < 0) - return ret; - - /* Put user requested rectangle within sensor bounds */ - soc_camera_limit_side(&rect->left, &rect->width, sdsel.r.left, 2, - bounds.r.width); - soc_camera_limit_side(&rect->top, &rect->height, sdsel.r.top, 4, - bounds.r.height); - - /* - * Popular special case - some cameras can only handle fixed sizes like - * QVGA, VGA,... Take care to avoid infinite loop. - */ - width = max_t(unsigned int, cam_rect->width, 2); - height = max_t(unsigned int, cam_rect->height, 2); - - /* - * Loop as long as sensor is not covering the requested rectangle and - * is still within its bounds - */ - while (!ret && (is_smaller(cam_rect, rect) || - is_inside(cam_rect, rect)) && - (bounds.r.width > width || bounds.r.height > height)) { - - width *= 2; - height *= 2; - - cam_rect->width = width; - cam_rect->height = height; - - /* - * We do not know what capabilities the camera has to set up - * left and top borders. We could try to be smarter in iterating - * them, e.g., if camera current left is to the right of the - * target left, set it to the middle point between the current - * left and minimum left. But that would add too much - * complexity: we would have to iterate each border separately. - * Instead we just drop to the left and top bounds. - */ - if (cam_rect->left > rect->left) - cam_rect->left = bounds.r.left; - - if (cam_rect->left + cam_rect->width < rect->left + rect->width) - cam_rect->width = rect->left + rect->width - - cam_rect->left; - - if (cam_rect->top > rect->top) - cam_rect->top = bounds.r.top; - - if (cam_rect->top + cam_rect->height < rect->top + rect->height) - cam_rect->height = rect->top + rect->height - - cam_rect->top; - - sdsel.r = *cam_rect; - v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - *cam_rect = sdsel.r; - ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_SELECTION %d for %dx%d@%d:%d\n", ret, - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - } - - /* S_SELECTION must not modify the rectangle */ - if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { - /* - * The camera failed to configure a suitable cropping, - * we cannot use the current rectangle, set to max - */ - sdsel.r = bounds.r; - v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - *cam_rect = sdsel.r; - - ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_SELECTION %d for max %dx%d@%d:%d\n", ret, - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - } - - if (!ret) { - *target_rect = *cam_rect; - move_and_crop_subrect(target_rect, subrect); - } - - return ret; -} -EXPORT_SYMBOL(soc_camera_client_s_selection); - -/* Iterative set_fmt, also updates cached client crop on success */ -static int client_set_fmt(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - unsigned int max_width, unsigned int max_height, - struct v4l2_subdev_format *format, bool host_can_scale) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct v4l2_mbus_framefmt *mf = &format->format; - unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP_BOUNDS, - }; - bool host_1to1; - int ret; - - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, format); - if (ret < 0) - return ret; - - dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); - - if (width == mf->width && height == mf->height) { - /* Perfect! The client has done it all. */ - host_1to1 = true; - goto update_cache; - } - - host_1to1 = false; - if (!host_can_scale) - goto update_cache; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (ret < 0) - return ret; - - if (max_width > sdsel.r.width) - max_width = sdsel.r.width; - if (max_height > sdsel.r.height) - max_height = sdsel.r.height; - - /* Camera set a format, but geometry is not precise, try to improve */ - tmp_w = mf->width; - tmp_h = mf->height; - - /* width <= max_width && height <= max_height - guaranteed by try_fmt */ - while ((width > tmp_w || height > tmp_h) && - tmp_w < max_width && tmp_h < max_height) { - tmp_w = min(2 * tmp_w, max_width); - tmp_h = min(2 * tmp_h, max_height); - mf->width = tmp_w; - mf->height = tmp_h; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, format); - dev_geo(dev, "Camera scaled to %ux%u\n", - mf->width, mf->height); - if (ret < 0) { - /* This shouldn't happen */ - dev_err(dev, "Client failed to set format: %d\n", ret); - return ret; - } - } - -update_cache: - /* Update cache */ - ret = soc_camera_client_g_rect(sd, rect); - if (ret < 0) - return ret; - - if (host_1to1) - *subrect = *rect; - else - move_and_crop_subrect(rect, subrect); - - return 0; -} - -/** - * soc_camera_client_scale - * @icd: soc-camera device - * @rect: camera cropping window - * @subrect: part of rect, sent to the user - * @mf: in- / output camera output window - * @width: on input: max host input width; - * on output: user width, mapped back to input - * @height: on input: max host input height; - * on output: user height, mapped back to input - * @host_can_scale: host can scale this pixel format - * @shift: shift, used for scaling - */ -int soc_camera_client_scale(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - struct v4l2_mbus_framefmt *mf, - unsigned int *width, unsigned int *height, - bool host_can_scale, unsigned int shift) -{ - struct device *dev = icd->parent; - struct v4l2_subdev_format fmt_tmp = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .format = *mf, - }; - struct v4l2_mbus_framefmt *mf_tmp = &fmt_tmp.format; - unsigned int scale_h, scale_v; - int ret; - - /* - * 5. Apply iterative camera S_FMT for camera user window (also updates - * client crop cache and the imaginary sub-rectangle). - */ - ret = client_set_fmt(icd, rect, subrect, *width, *height, - &fmt_tmp, host_can_scale); - if (ret < 0) - return ret; - - dev_geo(dev, "5: camera scaled to %ux%u\n", - mf_tmp->width, mf_tmp->height); - - /* 6. Retrieve camera output window (g_fmt) */ - - /* unneeded - it is already in "mf_tmp" */ - - /* 7. Calculate new client scales. */ - scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp->width); - scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp->height); - - mf->width = mf_tmp->width; - mf->height = mf_tmp->height; - mf->colorspace = mf_tmp->colorspace; - - /* - * 8. Calculate new host crop - apply camera scales to previously - * updated "effective" crop. - */ - *width = soc_camera_shift_scale(subrect->width, shift, scale_h); - *height = soc_camera_shift_scale(subrect->height, shift, scale_v); - - dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); - - return 0; -} -EXPORT_SYMBOL(soc_camera_client_scale); - -/* - * Calculate real client output window by applying new scales to the current - * client crop. New scales are calculated from the requested output format and - * host crop, mapped backed onto the client input (subrect). - */ -void soc_camera_calc_client_output(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, - unsigned int shift) -{ - struct device *dev = icd->parent; - unsigned int scale_v, scale_h; - - if (subrect->width == rect->width && - subrect->height == rect->height) { - /* No sub-cropping */ - mf->width = pix->width; - mf->height = pix->height; - return; - } - - /* 1.-2. Current camera scales and subwin - cached. */ - - dev_geo(dev, "2: subwin %ux%u@%u:%u\n", - subrect->width, subrect->height, - subrect->left, subrect->top); - - /* - * 3. Calculate new combined scales from input sub-window to requested - * user window. - */ - - /* - * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF - * (128x96) or larger than VGA. This and similar limitations have to be - * taken into account here. - */ - scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width); - scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height); - - dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); - - /* - * 4. Calculate desired client output window by applying combined scales - * to client (real) input window. - */ - mf->width = soc_camera_shift_scale(rect->width, shift, scale_h); - mf->height = soc_camera_shift_scale(rect->height, shift, scale_v); -} -EXPORT_SYMBOL(soc_camera_calc_client_output); - -MODULE_DESCRIPTION("soc-camera scaling-cropping functions"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h deleted file mode 100644 index 9ca469312a1f..000000000000 --- a/drivers/media/platform/soc_camera/soc_scale_crop.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * soc-camera generic scaling-cropping manipulation functions - * - * Copyright (C) 2013 Guennadi Liakhovetski - * - * 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. - */ - -#ifndef SOC_SCALE_CROP_H -#define SOC_SCALE_CROP_H - -#include - -struct soc_camera_device; - -struct v4l2_selection; -struct v4l2_mbus_framefmt; -struct v4l2_pix_format; -struct v4l2_rect; -struct v4l2_subdev; - -static inline unsigned int soc_camera_shift_scale(unsigned int size, - unsigned int shift, unsigned int scale) -{ - return DIV_ROUND_CLOSEST(size << shift, scale); -} - -#define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out) - -int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); -int soc_camera_client_s_selection(struct v4l2_subdev *sd, - struct v4l2_selection *sel, struct v4l2_selection *cam_sel, - struct v4l2_rect *target_rect, struct v4l2_rect *subrect); -int soc_camera_client_scale(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - struct v4l2_mbus_framefmt *mf, - unsigned int *width, unsigned int *height, - bool host_can_scale, unsigned int shift); -void soc_camera_calc_client_output(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, - unsigned int shift); - -#endif From dc60a4cfb77c891f67f31953025208067b05883c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:47:55 -0200 Subject: [PATCH 0502/4277] media: soc_camera_platform: remove obsolete soc_camera test driver This is a test stub driver for soc_camera. Since soc_camera is being deprecated (and in fact, nobody is using it anymore) there's no sense in keeping this test driver. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 6 - drivers/media/platform/soc_camera/Makefile | 4 - .../platform/soc_camera/soc_camera_platform.c | 188 ------------------ .../platform_data/media/soc_camera_platform.h | 83 -------- 4 files changed, 281 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/soc_camera_platform.c delete mode 100644 include/linux/platform_data/media/soc_camera_platform.h diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index d471d34b884c..8f9b3bac5450 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -6,9 +6,3 @@ config SOC_CAMERA SoC Camera is a common API to several cameras, not connecting over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. - -config SOC_CAMERA_PLATFORM - tristate "platform camera support" - depends on SOC_CAMERA - help - This is a generic SoC camera platform driver, useful for testing diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 2cb7022e073b..85d5e74f3b2b 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -1,5 +1 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o - -# a platform subdevice driver stub, allowing to support cameras by adding a -# couple of callback functions to the board code -obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c deleted file mode 100644 index 79fbe1fea95f..000000000000 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Generic Platform Camera Driver - * - * Copyright (C) 2008 Magnus Damm - * Based on mt9m001 driver, - * Copyright (C) 2008, Guennadi Liakhovetski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct soc_camera_platform_priv { - struct v4l2_subdev subdev; -}; - -static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev) -{ - struct v4l2_subdev *subdev = platform_get_drvdata(pdev); - return container_of(subdev, struct soc_camera_platform_priv, subdev); -} - -static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - return p->set_capture(p, enable); -} - -static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf = &format->format; - - mf->width = p->format.width; - mf->height = p->format.height; - mf->code = p->format.code; - mf->colorspace = p->format.colorspace; - mf->field = p->format.field; - - return 0; -} - -static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on); -} - -static const struct v4l2_subdev_core_ops platform_subdev_core_ops = { - .s_power = soc_camera_platform_s_power, -}; - -static int soc_camera_platform_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - if (code->pad || code->index) - return -EINVAL; - - code->code = p->format.code; - return 0; -} - -static int soc_camera_platform_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = p->format.width; - sel->r.height = p->format.height; - return 0; - default: - return -EINVAL; - } -} - -static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - cfg->flags = p->mbus_param; - cfg->type = p->mbus_type; - - return 0; -} - -static const struct v4l2_subdev_video_ops platform_subdev_video_ops = { - .s_stream = soc_camera_platform_s_stream, - .g_mbus_config = soc_camera_platform_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops platform_subdev_pad_ops = { - .enum_mbus_code = soc_camera_platform_enum_mbus_code, - .get_selection = soc_camera_platform_get_selection, - .get_fmt = soc_camera_platform_fill_fmt, - .set_fmt = soc_camera_platform_fill_fmt, -}; - -static const struct v4l2_subdev_ops platform_subdev_ops = { - .core = &platform_subdev_core_ops, - .video = &platform_subdev_video_ops, - .pad = &platform_subdev_pad_ops, -}; - -static int soc_camera_platform_probe(struct platform_device *pdev) -{ - struct soc_camera_host *ici; - struct soc_camera_platform_priv *priv; - struct soc_camera_platform_info *p = pdev->dev.platform_data; - struct soc_camera_device *icd; - - if (!p) - return -EINVAL; - - if (!p->icd) { - dev_err(&pdev->dev, - "Platform has not set soc_camera_device pointer!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - icd = p->icd; - - /* soc-camera convention: control's drvdata points to the subdev */ - platform_set_drvdata(pdev, &priv->subdev); - /* Set the control device reference */ - icd->control = &pdev->dev; - - ici = to_soc_camera_host(icd->parent); - - v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); - v4l2_set_subdevdata(&priv->subdev, p); - strscpy(priv->subdev.name, dev_name(&pdev->dev), - sizeof(priv->subdev.name)); - - return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); -} - -static int soc_camera_platform_remove(struct platform_device *pdev) -{ - struct soc_camera_platform_priv *priv = get_priv(pdev); - struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev); - - p->icd->control = NULL; - v4l2_device_unregister_subdev(&priv->subdev); - return 0; -} - -static struct platform_driver soc_camera_platform_driver = { - .driver = { - .name = "soc_camera_platform", - }, - .probe = soc_camera_platform_probe, - .remove = soc_camera_platform_remove, -}; - -module_platform_driver(soc_camera_platform_driver); - -MODULE_DESCRIPTION("SoC Camera Platform driver"); -MODULE_AUTHOR("Magnus Damm"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:soc_camera_platform"); diff --git a/include/linux/platform_data/media/soc_camera_platform.h b/include/linux/platform_data/media/soc_camera_platform.h deleted file mode 100644 index 1e5065dab430..000000000000 --- a/include/linux/platform_data/media/soc_camera_platform.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Generic Platform Camera Driver Header - * - * Copyright (C) 2008 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __SOC_CAMERA_H__ -#define __SOC_CAMERA_H__ - -#include -#include -#include - -struct device; - -struct soc_camera_platform_info { - const char *format_name; - unsigned long format_depth; - struct v4l2_mbus_framefmt format; - unsigned long mbus_param; - enum v4l2_mbus_type mbus_type; - struct soc_camera_device *icd; - int (*set_capture)(struct soc_camera_platform_info *info, int enable); -}; - -static inline void soc_camera_platform_release(struct platform_device **pdev) -{ - *pdev = NULL; -} - -static inline int soc_camera_platform_add(struct soc_camera_device *icd, - struct platform_device **pdev, - struct soc_camera_link *plink, - void (*release)(struct device *dev), - int id) -{ - struct soc_camera_subdev_desc *ssdd = - (struct soc_camera_subdev_desc *)plink; - struct soc_camera_platform_info *info = ssdd->drv_priv; - int ret; - - if (&icd->sdesc->subdev_desc != ssdd) - return -ENODEV; - - if (*pdev) - return -EBUSY; - - *pdev = platform_device_alloc("soc_camera_platform", id); - if (!*pdev) - return -ENOMEM; - - info->icd = icd; - - (*pdev)->dev.platform_data = info; - (*pdev)->dev.release = release; - - ret = platform_device_add(*pdev); - if (ret < 0) { - platform_device_put(*pdev); - *pdev = NULL; - info->icd = NULL; - } - - return ret; -} - -static inline void soc_camera_platform_del(const struct soc_camera_device *icd, - struct platform_device *pdev, - const struct soc_camera_link *plink) -{ - const struct soc_camera_subdev_desc *ssdd = - (const struct soc_camera_subdev_desc *)plink; - if (&icd->sdesc->subdev_desc != ssdd || !pdev) - return; - - platform_device_unregister(pdev); -} - -#endif /* __SOC_CAMERA_H__ */ From 83504032e6ddcc8b0942aa24dfad5db849090c9f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 14 Jan 2019 14:22:24 +0000 Subject: [PATCH 0503/4277] arm64: Remove asm/memblock.h The arm64 asm/memblock.h header exists only to provide a function prototype for arm64_memblock_init(), which is called only from setup_arch(). Move the declaration into mmu.h, where it can live alongside other init functions such as paging_init() and bootmem_init() without the need for its own special header file. Acked-by: Mark Rutland Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/memblock.h | 21 --------------------- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/kernel/setup.c | 1 - arch/arm64/mm/mmu.c | 1 - 4 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 arch/arm64/include/asm/memblock.h diff --git a/arch/arm64/include/asm/memblock.h b/arch/arm64/include/asm/memblock.h deleted file mode 100644 index 6afeed2467f1..000000000000 --- a/arch/arm64/include/asm/memblock.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2012 ARM Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef __ASM_MEMBLOCK_H -#define __ASM_MEMBLOCK_H - -extern void arm64_memblock_init(void); - -#endif diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 3e8063f4f9d3..67ef25d037ea 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -129,6 +129,7 @@ static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void) static inline void arm64_apply_bp_hardening(void) { } #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ +extern void arm64_memblock_init(void); extern void paging_init(void); extern void bootmem_init(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 4b0e1231625c..71f5fbb12608 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -58,7 +58,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index b6f5aa52ac67..d6b6f1b169bb 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include From 526daee7301d8ec86d8de0698d4c9ada185c5470 Mon Sep 17 00:00:00 2001 From: "French, Nicholas A" Date: Sun, 11 Mar 2018 16:27:28 -0300 Subject: [PATCH 0504/4277] media: ivtv: add parameter to enable ivtvfb on x86 PAT systems ivtvfb was previously disabled for x86 PAT-enabled systems by commit 1bf1735b4780 ("x86/mm/pat, drivers/media/ivtv: Use arch_phys_wc_add() and require PAT disabled") as a workaround to abstract MTRR code away from device drivers. The driver is not easily upgradable to the PAT-aware ioremap_wc() API since the firmware hides the address ranges that should be marked write-combined from the driver. However, since a write-combined cache on the framebuffer is only a performance enhancement not a requirement for the framebuffer to function, completely disabling the driver in this configuration is not necessary. Add force_pat module parameter and a corresponding kernel configuration parameter to optionally force initialization on PAT-enabled x86 systems with a warning about the lack of write-combined caching, and document the reasons the driver cannot be easily updated to support wc caching on all systems. Signed-off-by: Nick French Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: fix typo, split long pr_ lines up] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/Kconfig | 23 ++++++++++++++++++++--- drivers/media/pci/ivtv/ivtvfb.c | 16 ++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig index c72cbbd2d40c..06ca4e23f9fb 100644 --- a/drivers/media/pci/ivtv/Kconfig +++ b/drivers/media/pci/ivtv/Kconfig @@ -70,8 +70,25 @@ config VIDEO_FB_IVTV This is used in the Hauppauge PVR-350 card. There is a driver homepage at . - In order to use this module, you will need to boot with PAT disabled - on x86 systems, using the nopat kernel parameter. - To compile this driver as a module, choose M here: the module will be called ivtvfb. + +config VIDEO_FB_IVTV_FORCE_PAT + bool "force cx23415 framebuffer init with x86 PAT enabled" + depends on VIDEO_FB_IVTV && X86_PAT + default n + ---help--- + With PAT enabled, the cx23415 framebuffer driver does not + utilize write-combined caching on the framebuffer memory. + For this reason, the driver will by default disable itself + when initializied on a kernel with PAT enabled (i.e. not + using the nopat kernel parameter). + + The driver is not easily upgradable to the PAT-aware + ioremap_wc() API since the firmware hides the address + ranges that should be marked write-combined from the driver. + + With this setting enabled, the framebuffer will initialize on + PAT-enabled systems but the framebuffer memory will be uncached. + + If unsure, say N. diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 8ec2525d8ef5..cfd21040d0e3 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -55,6 +55,7 @@ /* card parameters */ static int ivtvfb_card_id = -1; static int ivtvfb_debug = 0; +static bool ivtvfb_force_pat = IS_ENABLED(CONFIG_VIDEO_FB_IVTV_FORCE_PAT); static bool osd_laced; static int osd_depth; static int osd_upper; @@ -64,6 +65,7 @@ static int osd_xres; module_param(ivtvfb_card_id, int, 0444); module_param_named(debug,ivtvfb_debug, int, 0644); +module_param_named(force_pat, ivtvfb_force_pat, bool, 0644); module_param(osd_laced, bool, 0444); module_param(osd_depth, int, 0444); module_param(osd_upper, int, 0444); @@ -79,6 +81,9 @@ MODULE_PARM_DESC(debug, "Debug level (bitmask). Default: errors only\n" "\t\t\t(debug = 3 gives full debugging)"); +MODULE_PARM_DESC(force_pat, + "Force initialization on x86 PAT-enabled systems (bool).\n"); + /* Why upper, left, xres, yres, depth, laced ? To match terminology used by fbset. Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ @@ -1167,8 +1172,15 @@ static int ivtvfb_init_card(struct ivtv *itv) #ifdef CONFIG_X86_64 if (pat_enabled()) { - pr_warn("ivtvfb needs PAT disabled, boot with nopat kernel parameter\n"); - return -ENODEV; + if (ivtvfb_force_pat) { + pr_info("PAT is enabled. Write-combined framebuffer caching will be disabled.\n"); + pr_info("To enable caching, boot with nopat kernel parameter\n"); + } else { + pr_warn("ivtvfb needs PAT disabled for write-combined framebuffer caching.\n"); + pr_warn("Boot with nopat kernel parameter to use caching, or use the\n"); + pr_warn("force_pat module parameter to run with caching disabled\n"); + return -ENODEV; + } } #endif From 95f9db59d856010e5c28813263fbdd7d70159f77 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:09 -0200 Subject: [PATCH 0505/4277] media: si470x-i2c: Add device tree support This commit enables device tree support adding simple of_match table. Signed-off-by: Pawe? Chmiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 9751ea1d80be..250828ddb5fa 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -527,6 +527,13 @@ static int si470x_i2c_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); #endif +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id si470x_of_match[] = { + { .compatible = "silabs,si470x" }, + { }, +}; +MODULE_DEVICE_TABLE(of, si470x_of_match); +#endif /* * si470x_i2c_driver - i2c driver interface @@ -534,6 +541,7 @@ static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); static struct i2c_driver si470x_i2c_driver = { .driver = { .name = "si470x", + .of_match_table = of_match_ptr(si470x_of_match), #ifdef CONFIG_PM_SLEEP .pm = &si470x_i2c_pm, #endif From a2c801c53d1682871fba1e037c9d3b0c9fffee8a Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Wed, 9 Jan 2019 13:21:00 -0700 Subject: [PATCH 0506/4277] arm64: mm: make use of new memblocks_present() helper Cleanup the arm64_memory_present() function seeing it's very similar to other arches. memblocks_present() is a direct replacement of arm64_memory_present() Acked-by: Will Deacon Acked-by: Catalin Marinas Signed-off-by: Logan Gunthorpe Signed-off-by: Catalin Marinas --- arch/arm64/mm/init.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 7205a9085b4d..2302b4093a63 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -285,24 +285,6 @@ int pfn_valid(unsigned long pfn) } EXPORT_SYMBOL(pfn_valid); -#ifndef CONFIG_SPARSEMEM -static void __init arm64_memory_present(void) -{ -} -#else -static void __init arm64_memory_present(void) -{ - struct memblock_region *reg; - - for_each_memblock(memory, reg) { - int nid = memblock_get_region_node(reg); - - memory_present(nid, memblock_region_memory_base_pfn(reg), - memblock_region_memory_end_pfn(reg)); - } -} -#endif - static phys_addr_t memory_limit = PHYS_ADDR_MAX; /* @@ -489,7 +471,7 @@ void __init bootmem_init(void) * Sparsemem tries to allocate bootmem in memory_present(), so must be * done after the fixed reservations. */ - arm64_memory_present(); + memblocks_present(); sparse_init(); zone_sizes_init(min, max); From f86c51b66bf6a066190035594965a1a9c661b4e4 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:10 -0200 Subject: [PATCH 0507/4277] media: si470x-i2c: Use managed resource helpers Simplify cleanup of failures by using managed resource helpers Signed-off-by: Pawe? Chmiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 250828ddb5fa..a7ac09c55188 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -350,7 +350,7 @@ static int si470x_i2c_probe(struct i2c_client *client, unsigned char version_warning = 0; /* private data allocation and initialization */ - radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); + radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL); if (!radio) { retval = -ENOMEM; goto err_initial; @@ -370,7 +370,7 @@ static int si470x_i2c_probe(struct i2c_client *client, retval = v4l2_device_register(&client->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&client->dev, "couldn't register v4l2_device\n"); - goto err_radio; + goto err_initial; } v4l2_ctrl_handler_init(&radio->hdl, 2); @@ -396,14 +396,14 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { retval = -EIO; - goto err_ctrl; + goto err_all; } msleep(110); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_ctrl; + goto err_all; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[SI_CHIPID]); @@ -430,10 +430,10 @@ static int si470x_i2c_probe(struct i2c_client *client, /* rds buffer allocation */ radio->buf_size = rds_buf * 3; - radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); + radio->buffer = devm_kmalloc(&client->dev, radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_ctrl; + goto err_all; } /* rds buffer configuration */ @@ -441,12 +441,13 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->rd_index = 0; init_waitqueue_head(&radio->read_queue); - retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DRIVER_NAME, - radio); + retval = devm_request_threaded_irq(&client->dev, client->irq, NULL, + si470x_i2c_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRIVER_NAME, radio); if (retval) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_rds; + goto err_all; } /* register video device */ @@ -460,15 +461,9 @@ static int si470x_i2c_probe(struct i2c_client *client, return 0; err_all: - free_irq(client->irq, radio); -err_rds: - kfree(radio->buffer); -err_ctrl: v4l2_ctrl_handler_free(&radio->hdl); err_dev: v4l2_device_unregister(&radio->v4l2_dev); -err_radio: - kfree(radio); err_initial: return retval; } @@ -481,9 +476,7 @@ static int si470x_i2c_remove(struct i2c_client *client) { struct si470x_device *radio = i2c_get_clientdata(client); - free_irq(client->irq, radio); video_unregister_device(&radio->videodev); - kfree(radio); return 0; } From 1c64222be9ad0e56ad9a222b35e063d481f58cfc Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:11 -0200 Subject: [PATCH 0508/4277] media: si470x-i2c: Add optional reset-gpio support If reset-gpio is defined, use it to bring device out of reset. Without this, it's not possible to access si470x registers. Signed-off-by: Pawe? Chmiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 15 +++++++++++++++ drivers/media/radio/si470x/radio-si470x.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index a7ac09c55188..15eea2b2c90f 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "radio-si470x.h" @@ -392,6 +393,17 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->videodev.release = video_device_release_empty; video_set_drvdata(&radio->videodev, radio); + radio->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(radio->gpio_reset)) { + retval = PTR_ERR(radio->gpio_reset); + dev_err(&client->dev, "Failed to request gpio: %d\n", retval); + goto err_all; + } + + if (radio->gpio_reset) + gpiod_set_value(radio->gpio_reset, 1); + /* power up : need 110ms */ radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { @@ -478,6 +490,9 @@ static int si470x_i2c_remove(struct i2c_client *client) video_unregister_device(&radio->videodev); + if (radio->gpio_reset) + gpiod_set_value(radio->gpio_reset, 0); + return 0; } diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index 35fa0f3bbdd2..6fd6a399cb77 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -189,6 +189,7 @@ struct si470x_device { #if IS_ENABLED(CONFIG_I2C_SI470X) struct i2c_client *client; + struct gpio_desc *gpio_reset; #endif }; From 9b4fce216f538f848a46183650f29603c1ecf4e6 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:12 -0200 Subject: [PATCH 0509/4277] media: dt-bindings: Add binding for si470x radio Add device tree bindings for si470x family radio receiver driver. Signed-off-by: Pawe? Chmiel Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/si470x.txt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/si470x.txt diff --git a/Documentation/devicetree/bindings/media/si470x.txt b/Documentation/devicetree/bindings/media/si470x.txt new file mode 100644 index 000000000000..a9403558362e --- /dev/null +++ b/Documentation/devicetree/bindings/media/si470x.txt @@ -0,0 +1,26 @@ +* Silicon Labs FM Radio receiver + +The Silicon Labs Si470x is family of FM radio receivers with receive power scan +supporting 76-108 MHz, programmable through an I2C interface. +Some of them includes an RDS encoder. + +Required Properties: +- compatible: Should contain "silabs,si470x" +- reg: the I2C address of the device + +Optional Properties: +- interrupts : The interrupt number +- reset-gpios: GPIO specifier for the chips reset line + +Example: + +&i2c2 { + si470x@63 { + compatible = "silabs,si470x"; + reg = <0x63>; + + interrupt-parent = <&gpj2>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpj2 5 GPIO_ACTIVE_HIGH>; + }; +}; From 6372f01cddaa9d910d530ba3e389b9f86c7052e4 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 11 Dec 2018 13:17:00 -0200 Subject: [PATCH 0510/4277] media: dt-bindings: media: video-i2c: add melexis mlx90640 documentation Cc: devicetree@vger.kernel.org Signed-off-by: Matt Ranostay Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/melexis,mlx90640.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt diff --git a/Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt b/Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt new file mode 100644 index 000000000000..060d2b7a5893 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt @@ -0,0 +1,20 @@ +* Melexis MLX90640 FIR Sensor + +Melexis MLX90640 FIR sensor support which allows recording of thermal data +with 32x24 resolution excluding 2 lines of coefficient data that is used by +userspace to render processed frames. + +Required Properties: + - compatible : Must be "melexis,mlx90640" + - reg : i2c address of the device + +Example: + + i2c0@1c22000 { + ... + mlx90640@33 { + compatible = "melexis,mlx90640"; + reg = <0x33>; + }; + ... + }; From 8866cfbf655067237bb34a32a199f01eca9eeb5a Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 11 Dec 2018 13:17:01 -0200 Subject: [PATCH 0511/4277] media: video-i2c: add Melexis MLX90640 thermal camera Add initial support for MLX90640 thermal cameras which output an 32x24 greyscale pixel image along with 2 rows of coefficent data. Because of this the data outputed is really 32x26 and needs the two rows removed after using the coefficent information to generate processed images in userspace. Signed-off-by: Matt Ranostay Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/video-i2c.c | 110 +++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 119aaee5318b..e6cd5cf7c3d9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1107,6 +1107,7 @@ config VIDEO_I2C Enable the I2C transport video support which supports the following: * Panasonic AMG88xx Grid-Eye Sensors + * Melexis MLX90640 Thermal Cameras To compile this driver as a module, choose M here: the module will be called video-i2c diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 01dcf179f203..abd3152df7d0 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -6,6 +6,7 @@ * * Supported: * - Panasonic AMG88xx Grid-Eye Sensors + * - Melexis MLX90640 Thermal Cameras */ #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -66,12 +68,26 @@ static const struct v4l2_frmsize_discrete amg88xx_size = { .height = 8, }; +static const struct v4l2_fmtdesc mlx90640_format = { + .pixelformat = V4L2_PIX_FMT_Y16_BE, +}; + +static const struct v4l2_frmsize_discrete mlx90640_size = { + .width = 32, + .height = 26, /* 24 lines of pixel data + 2 lines of processing data */ +}; + static const struct regmap_config amg88xx_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff }; +static const struct regmap_config mlx90640_regmap_config = { + .reg_bits = 16, + .val_bits = 16, +}; + struct video_i2c_chip { /* video dimensions */ const struct v4l2_fmtdesc *format; @@ -88,6 +104,7 @@ struct video_i2c_chip { unsigned int bpp; const struct regmap_config *regmap_config; + struct nvmem_config *nvmem_config; /* setup function */ int (*setup)(struct video_i2c_data *data); @@ -102,6 +119,22 @@ struct video_i2c_chip { int (*hwmon_init)(struct video_i2c_data *data); }; +static int mlx90640_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct video_i2c_data *data = priv; + + return regmap_bulk_read(data->regmap, 0x2400 + offset, val, bytes); +} + +static struct nvmem_config mlx90640_nvram_config = { + .name = "mlx90640_nvram", + .word_size = 2, + .stride = 1, + .size = 1664, + .reg_read = mlx90640_nvram_read, +}; + /* Power control register */ #define AMG88XX_REG_PCTL 0x00 #define AMG88XX_PCTL_NORMAL 0x00 @@ -122,12 +155,23 @@ struct video_i2c_chip { /* Temperature register */ #define AMG88XX_REG_T01L 0x80 +/* Control register */ +#define MLX90640_REG_CTL1 0x800d +#define MLX90640_REG_CTL1_MASK 0x0380 +#define MLX90640_REG_CTL1_MASK_SHIFT 7 + static int amg88xx_xfer(struct video_i2c_data *data, char *buf) { return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf, data->chip->buffer_size); } +static int mlx90640_xfer(struct video_i2c_data *data, char *buf) +{ + return regmap_bulk_read(data->regmap, 0x400, buf, + data->chip->buffer_size); +} + static int amg88xx_setup(struct video_i2c_data *data) { unsigned int mask = AMG88XX_FPSC_1FPS; @@ -141,6 +185,27 @@ static int amg88xx_setup(struct video_i2c_data *data) return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val); } +static int mlx90640_setup(struct video_i2c_data *data) +{ + unsigned int n, idx; + + for (n = 0; n < data->chip->num_frame_intervals - 1; n++) { + if (data->frame_interval.numerator + != data->chip->frame_intervals[n].numerator) + continue; + + if (data->frame_interval.denominator + == data->chip->frame_intervals[n].denominator) + break; + } + + idx = data->chip->num_frame_intervals - n - 1; + + return regmap_update_bits(data->regmap, MLX90640_REG_CTL1, + MLX90640_REG_CTL1_MASK, + idx << MLX90640_REG_CTL1_MASK_SHIFT); +} + static int amg88xx_set_power_on(struct video_i2c_data *data) { int ret; @@ -274,13 +339,27 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data) #define amg88xx_hwmon_init NULL #endif -#define AMG88XX 0 +enum { + AMG88XX, + MLX90640, +}; static const struct v4l2_fract amg88xx_frame_intervals[] = { { 1, 10 }, { 1, 1 }, }; +static const struct v4l2_fract mlx90640_frame_intervals[] = { + { 1, 64 }, + { 1, 32 }, + { 1, 16 }, + { 1, 8 }, + { 1, 4 }, + { 1, 2 }, + { 1, 1 }, + { 2, 1 }, +}; + static const struct video_i2c_chip video_i2c_chip[] = { [AMG88XX] = { .size = &amg88xx_size, @@ -295,6 +374,18 @@ static const struct video_i2c_chip video_i2c_chip[] = { .set_power = amg88xx_set_power, .hwmon_init = amg88xx_hwmon_init, }, + [MLX90640] = { + .size = &mlx90640_size, + .format = &mlx90640_format, + .frame_intervals = mlx90640_frame_intervals, + .num_frame_intervals = ARRAY_SIZE(mlx90640_frame_intervals), + .buffer_size = 1664, + .bpp = 16, + .regmap_config = &mlx90640_regmap_config, + .nvmem_config = &mlx90640_nvram_config, + .setup = mlx90640_setup, + .xfer = mlx90640_xfer, + }, }; static const struct v4l2_file_operations video_i2c_fops = { @@ -756,6 +847,21 @@ static int video_i2c_probe(struct i2c_client *client, } } + if (data->chip->nvmem_config) { + struct nvmem_config *config = data->chip->nvmem_config; + struct nvmem_device *device; + + config->priv = data; + config->dev = &client->dev; + + device = devm_nvmem_register(&client->dev, config); + + if (IS_ERR(device)) { + dev_warn(&client->dev, + "failed to register nvmem device\n"); + } + } + ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) goto error_pm_disable; @@ -835,12 +941,14 @@ static const struct dev_pm_ops video_i2c_pm_ops = { static const struct i2c_device_id video_i2c_id_table[] = { { "amg88xx", AMG88XX }, + { "mlx90640", MLX90640 }, {} }; MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); static const struct of_device_id video_i2c_of_match[] = { { .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] }, + { .compatible = "melexis,mlx90640", .data = &video_i2c_chip[MLX90640] }, {} }; MODULE_DEVICE_TABLE(of, video_i2c_of_match); From 78a9f4a39bf43d41f0d91dc3cdbc1fdb752fc416 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 12 Dec 2018 14:20:14 -0200 Subject: [PATCH 0512/4277] media: exynos4-is: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-is.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index f5fc54de19da..02da0b06e56a 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -738,7 +738,7 @@ int fimc_is_hw_initialize(struct fimc_is *is) return 0; } -static int fimc_is_log_show(struct seq_file *s, void *data) +static int fimc_is_show(struct seq_file *s, void *data) { struct fimc_is *is = s->private; const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET; @@ -752,17 +752,7 @@ static int fimc_is_log_show(struct seq_file *s, void *data) return 0; } -static int fimc_is_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, fimc_is_log_show, inode->i_private); -} - -static const struct file_operations fimc_is_debugfs_fops = { - .open = fimc_is_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fimc_is); static void fimc_is_debugfs_remove(struct fimc_is *is) { @@ -777,7 +767,7 @@ static int fimc_is_debugfs_create(struct fimc_is *is) is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, - is, &fimc_is_debugfs_fops); + is, &fimc_is_fops); if (!dentry) fimc_is_debugfs_remove(is); From 5e22c19f7e66672966281ea6f557100805c222bc Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 12 Dec 2018 14:27:03 -0200 Subject: [PATCH 0513/4277] media: platform: sti: remove bdisp_dbg_declare() and hva_dbg_declare() We already have the DEFINE_SHOW_ATTRIBUTE. There is no need to define bdisp_dbg_declare and hva_dbg_declare, so remove them. Also use DEFINE_SHOW_ATTRIBUTE to simplify some code. Signed-off-by: Yangtao Li Reviewed-by: Fabien Dessenne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/sti/bdisp/bdisp-debug.c | 34 ++++++------------ drivers/media/platform/sti/hva/hva-debugfs.c | 36 +++++++------------ 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c index c6a4e2de5c0c..77ca7517fa3e 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c @@ -315,7 +315,7 @@ static void bdisp_dbg_dump_ivmx(struct seq_file *s, seq_puts(s, "Unknown conversion\n"); } -static int bdisp_dbg_last_nodes(struct seq_file *s, void *data) +static int last_nodes_show(struct seq_file *s, void *data) { /* Not dumping all fields, focusing on significant ones */ struct bdisp_dev *bdisp = s->private; @@ -388,7 +388,7 @@ static int bdisp_dbg_last_nodes(struct seq_file *s, void *data) return 0; } -static int bdisp_dbg_last_nodes_raw(struct seq_file *s, void *data) +static int last_nodes_raw_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_node *node; @@ -437,7 +437,7 @@ static const char *bdisp_fmt_to_str(struct bdisp_frame frame) } } -static int bdisp_dbg_last_request(struct seq_file *s, void *data) +static int last_request_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_request *request = &bdisp->dbg.copy_request; @@ -474,7 +474,7 @@ static int bdisp_dbg_last_request(struct seq_file *s, void *data) #define DUMP(reg) seq_printf(s, #reg " \t0x%08X\n", readl(bdisp->regs + reg)) -static int bdisp_dbg_regs(struct seq_file *s, void *data) +static int regs_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; int ret; @@ -582,7 +582,7 @@ static int bdisp_dbg_regs(struct seq_file *s, void *data) #define SECOND 1000000 -static int bdisp_dbg_perf(struct seq_file *s, void *data) +static int perf_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_request *request = &bdisp->dbg.copy_request; @@ -627,27 +627,15 @@ static int bdisp_dbg_perf(struct seq_file *s, void *data) return 0; } -#define bdisp_dbg_declare(name) \ - static int bdisp_dbg_##name##_open(struct inode *i, struct file *f) \ - { \ - return single_open(f, bdisp_dbg_##name, i->i_private); \ - } \ - static const struct file_operations bdisp_dbg_##name##_fops = { \ - .open = bdisp_dbg_##name##_open, \ - .read = seq_read, \ - .llseek = seq_lseek, \ - .release = single_release, \ - } - #define bdisp_dbg_create_entry(name) \ debugfs_create_file(#name, S_IRUGO, bdisp->dbg.debugfs_entry, bdisp, \ - &bdisp_dbg_##name##_fops) + &name##_fops) -bdisp_dbg_declare(regs); -bdisp_dbg_declare(last_nodes); -bdisp_dbg_declare(last_nodes_raw); -bdisp_dbg_declare(last_request); -bdisp_dbg_declare(perf); +DEFINE_SHOW_ATTRIBUTE(regs); +DEFINE_SHOW_ATTRIBUTE(last_nodes); +DEFINE_SHOW_ATTRIBUTE(last_nodes_raw); +DEFINE_SHOW_ATTRIBUTE(last_request); +DEFINE_SHOW_ATTRIBUTE(perf); int bdisp_debugfs_create(struct bdisp_dev *bdisp) { diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/sti/hva/hva-debugfs.c index 9f7e8ac875d1..7d12a5b5d914 100644 --- a/drivers/media/platform/sti/hva/hva-debugfs.c +++ b/drivers/media/platform/sti/hva/hva-debugfs.c @@ -271,7 +271,7 @@ static void hva_dbg_perf_compute(struct hva_ctx *ctx) * device debug info */ -static int hva_dbg_device(struct seq_file *s, void *data) +static int device_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; @@ -281,7 +281,7 @@ static int hva_dbg_device(struct seq_file *s, void *data) return 0; } -static int hva_dbg_encoders(struct seq_file *s, void *data) +static int encoders_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; unsigned int i = 0; @@ -299,7 +299,7 @@ static int hva_dbg_encoders(struct seq_file *s, void *data) return 0; } -static int hva_dbg_last(struct seq_file *s, void *data) +static int last_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; struct hva_ctx *last_ctx = &hva->dbg.last_ctx; @@ -316,7 +316,7 @@ static int hva_dbg_last(struct seq_file *s, void *data) return 0; } -static int hva_dbg_regs(struct seq_file *s, void *data) +static int regs_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; @@ -325,26 +325,14 @@ static int hva_dbg_regs(struct seq_file *s, void *data) return 0; } -#define hva_dbg_declare(name) \ - static int hva_dbg_##name##_open(struct inode *i, struct file *f) \ - { \ - return single_open(f, hva_dbg_##name, i->i_private); \ - } \ - static const struct file_operations hva_dbg_##name##_fops = { \ - .open = hva_dbg_##name##_open, \ - .read = seq_read, \ - .llseek = seq_lseek, \ - .release = single_release, \ - } - #define hva_dbg_create_entry(name) \ debugfs_create_file(#name, 0444, hva->dbg.debugfs_entry, hva, \ - &hva_dbg_##name##_fops) + &name##_fops) -hva_dbg_declare(device); -hva_dbg_declare(encoders); -hva_dbg_declare(last); -hva_dbg_declare(regs); +DEFINE_SHOW_ATTRIBUTE(device); +DEFINE_SHOW_ATTRIBUTE(encoders); +DEFINE_SHOW_ATTRIBUTE(last); +DEFINE_SHOW_ATTRIBUTE(regs); void hva_debugfs_create(struct hva_dev *hva) { @@ -380,7 +368,7 @@ void hva_debugfs_remove(struct hva_dev *hva) * context (instance) debug info */ -static int hva_dbg_ctx(struct seq_file *s, void *data) +static int ctx_show(struct seq_file *s, void *data) { struct hva_ctx *ctx = s->private; @@ -392,7 +380,7 @@ static int hva_dbg_ctx(struct seq_file *s, void *data) return 0; } -hva_dbg_declare(ctx); +DEFINE_SHOW_ATTRIBUTE(ctx); void hva_dbg_ctx_create(struct hva_ctx *ctx) { @@ -407,7 +395,7 @@ void hva_dbg_ctx_create(struct hva_ctx *ctx) ctx->dbg.debugfs_entry = debugfs_create_file(name, 0444, hva->dbg.debugfs_entry, - ctx, &hva_dbg_ctx_fops); + ctx, &ctx_fops); } void hva_dbg_ctx_remove(struct hva_ctx *ctx) From 8d19d5d03b4d09177b0ae87f964eb751e6f51b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 14 Dec 2018 04:18:21 -0200 Subject: [PATCH 0514/4277] media: rcar-vin: fix wrong return value in rvin_set_channel_routing() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the operation in rvin_set_channel_routing() is successful the 'ret' variable contains the runtime PM use count for the VIN master device. The intention is not to return the use count to the caller but to return 0 on success else none zero. Fix this by always returning 0 if the operation is successful. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 92323310f735..beb9248992a4 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1341,5 +1341,5 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) pm_runtime_put(vin->dev); - return ret; + return 0; } From 6f4b9d9a6c08f692f627700c2d0e250e406ac81f Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 9 Jan 2019 12:19:19 -0200 Subject: [PATCH 0515/4277] media: cedrus: Cleanup duplicate declarations from cedrus_dec header Some leftover declarations are still in the cedrus_dec header although they were moved to cedrus_video already. Clean them up. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index 4f423d3a1cad..d1ae7903677b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,12 +16,6 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ -extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; - -void cedrus_device_work(struct work_struct *work); void cedrus_device_run(void *priv); -int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - #endif From cf20ae1535eb690a87c29b9cc7af51881384e967 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 9 Jan 2019 12:19:20 -0200 Subject: [PATCH 0516/4277] media: cedrus: Allow using the current dst buffer as reference It was reported that some cases of interleaved video decoding require using the current destination buffer as a reference. However, this is no longer possible after the move to vb2_find_timestamp because only dequeued and done buffers are considered. Add a helper in our driver that also considers the current destination buffer before resorting to vb2_find_timestamp and use it in MPEG-2. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 13 +++++++++++++ drivers/staging/media/sunxi/cedrus/cedrus_dec.h | 2 ++ drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 10 ++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 443fb037e1cf..2c295286766c 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -22,6 +22,19 @@ #include "cedrus_dec.h" #include "cedrus_hw.h" +int cedrus_reference_index_find(struct vb2_queue *queue, + struct vb2_buffer *vb2_buf, u64 timestamp) +{ + /* + * Allow using the current capture buffer as reference, which can occur + * for field-coded pictures. + */ + if (vb2_buf->timestamp == timestamp) + return vb2_buf->index; + else + return vb2_find_timestamp(queue, timestamp, 0); +} + void cedrus_device_run(void *priv) { struct cedrus_ctx *ctx = priv; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index d1ae7903677b..8d0fc248220f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,6 +16,8 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ +int cedrus_reference_index_find(struct vb2_queue *queue, + struct vb2_buffer *vb2_buf, u64 timestamp); void cedrus_device_run(void *priv); #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index cb45fda9aaeb..81c66a8aa1ac 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -10,6 +10,7 @@ #include #include "cedrus.h" +#include "cedrus_dec.h" #include "cedrus_hw.h" #include "cedrus_regs.h" @@ -159,8 +160,8 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - forward_idx = vb2_find_timestamp(cap_q, - slice_params->forward_ref_ts, 0); + forward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, + slice_params->forward_ref_ts); fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); @@ -168,8 +169,9 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - backward_idx = vb2_find_timestamp(cap_q, - slice_params->backward_ref_ts, 0); + backward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, + slice_params->backward_ref_ts); + bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); From fb517583b3fe0bb6e64b8a861bfae5dc39f1cedb Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Fri, 11 Jan 2019 14:17:03 -0200 Subject: [PATCH 0517/4277] media: i2c: adv748x: Use devm to allocate the device struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to devm_kzalloc() when allocating the adv748x device struct. The sizeof() is updated to determine the correct allocation size from the dereferenced pointer type rather than hardcoding the struct type. [Kieran: Change sizeof() to dereference the pointer type] Signed-off-by: Steve Longerbeam Signed-off-by: Kieran Bingham Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index d94c63cb6a2e..bd8a8c8d6f8e 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -631,7 +631,7 @@ static int adv748x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - state = kzalloc(sizeof(struct adv748x_state), GFP_KERNEL); + state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; @@ -729,7 +729,6 @@ err_cleanup_dt: adv748x_dt_cleanup(state); err_free_mutex: mutex_destroy(&state->mutex); - kfree(state); return ret; } @@ -748,8 +747,6 @@ static int adv748x_remove(struct i2c_client *client) adv748x_dt_cleanup(state); mutex_destroy(&state->mutex); - kfree(state); - return 0; } From 04ee6d614c0dbe4350c61e8909c2ca01e364884d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 10:27:45 -0200 Subject: [PATCH 0518/4277] media: vimc: fill in correct driver name in querycap The driver name as returned in v4l2_capabilities must be vimc, not vimc_capture. Fix this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 2 +- drivers/media/platform/vimc/vimc-common.h | 2 ++ drivers/media/platform/vimc/vimc-core.c | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 3f7e9ed56633..aaeddf24b042 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -71,7 +71,7 @@ static int vimc_cap_querycap(struct file *file, void *priv, { struct vimc_cap_device *vcap = video_drvdata(file); - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vcap->vdev.v4l2_dev->name); diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 2e9981b18166..f491c33c7c14 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -22,6 +22,8 @@ #include #include +#define VIMC_PDEV_NAME "vimc" + /* VIMC-specific controls */ #define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000) #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index ce809d2e3d53..bf19f1f9795e 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -24,7 +24,6 @@ #include "vimc-common.h" -#define VIMC_PDEV_NAME "vimc" #define VIMC_MDEV_MODEL_NAME "VIMC MDEV" #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ From 07b8fd86556c64e8424f198f1b6a75ca194520f9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 09:46:26 -0200 Subject: [PATCH 0519/4277] media: vidioc-prepare-buf.rst: drop reference to NO_CACHE flags This was never implemented, so do not document this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/vidioc-prepare-buf.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst b/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst index 60986710967b..7c6b5f4e1011 100644 --- a/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst +++ b/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst @@ -43,10 +43,7 @@ Applications can optionally call the :ref:`VIDIOC_PREPARE_BUF` ioctl to pass ownership of the buffer to the driver before actually enqueuing it, using the :ref:`VIDIOC_QBUF ` ioctl, and to prepare it for future I/O. Such preparations may include cache invalidation or cleaning. Performing them -in advance saves time during the actual I/O. In case such cache -operations are not required, the application can use one of -``V4L2_BUF_FLAG_NO_CACHE_INVALIDATE`` and -``V4L2_BUF_FLAG_NO_CACHE_CLEAN`` flags to skip the respective step. +in advance saves time during the actual I/O. The struct :c:type:`v4l2_buffer` structure is specified in :ref:`buffer`. From 0a44baa4e9959d67b01b24a38b90dd903f89a0b6 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 16 Jan 2019 16:49:33 -0200 Subject: [PATCH 0520/4277] media: media/v4l2-core/videobuf-vmalloc.c: Remove dead code This code is commented since version 3.7. If there is no plan to use it in future, we can remove this dead code. Signed-off-by: Souptick Joarder Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf-vmalloc.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c index 45fe781aeeec..293213ad9ef7 100644 --- a/drivers/media/v4l2-core/videobuf-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf-vmalloc.c @@ -196,26 +196,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, } dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages); - -#if 0 - int rc; - /* Kernel userptr is used also by read() method. In this case, - there's no need to remap, since data will be copied to user - */ - if (!vb->baddr) - return 0; - - /* FIXME: to properly support USERPTR, remap should occur. - The code below won't work, since mem->vma = NULL - */ - /* Try to remap memory */ - rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); - if (rc < 0) { - printk(KERN_ERR "mmap: remap failed with error %d", rc); - return -ENOMEM; - } -#endif - break; case V4L2_MEMORY_OVERLAY: default: From fbf9aa6aeaebb10afb9d4fca013c4823ae0b5101 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 8 Jan 2019 15:20:16 -0200 Subject: [PATCH 0521/4277] media: coda: use macroblock tiling on CODA960 only Coda7541 and earlier do not support macroblock tiling. They do support the NV12 format, though. Enable macroblock tiling for NV12 only on CODA960. This fixes crashes when trying to use NV12 support on CodaHx4. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 390d1ce6ab32..fa0b22fb7991 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -728,7 +728,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; break; case V4L2_PIX_FMT_NV12: - if (!disable_tiling) { + if (!disable_tiling && ctx->dev->devtype->product == CODA_960) { ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; break; } From cd9f125cfaa5047899e1d39d6fc0a9ed5e40afa7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 8 Jan 2019 15:20:43 -0200 Subject: [PATCH 0522/4277] media: coda: fix decoder capture buffer payload It is not correct to calculate decoder capture payload dynamically from the decoded frame width and height reported by the firmware. These tell us what the decoder wrote into the internal framebuffers. The rotator or VDOA always write the full sizeimage when copying the previously decoded frame from the internal framebuffers into the capture queue. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 88065b07149c..b4f396c2e72c 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -2024,7 +2024,6 @@ static void coda_finish_decode(struct coda_ctx *ctx) struct coda_q_data *q_data_dst; struct vb2_v4l2_buffer *dst_buf; struct coda_buffer_meta *meta; - unsigned long payload; int width, height; int decoded_idx; int display_idx; @@ -2230,21 +2229,8 @@ static void coda_finish_decode(struct coda_ctx *ctx) trace_coda_dec_rot_done(ctx, dst_buf, meta); - switch (q_data_dst->fourcc) { - case V4L2_PIX_FMT_YUYV: - payload = width * height * 2; - break; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_NV12: - default: - payload = width * height * 3 / 2; - break; - case V4L2_PIX_FMT_YUV422P: - payload = width * height * 2; - break; - } - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + q_data_dst->sizeimage); if (ctx->frame_errors[ctx->display_idx] || err_vdoa) coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); From 439d8186fb235ab6d9bc08a23a8a9530a5f3ac95 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 17 Jan 2019 13:51:52 -0200 Subject: [PATCH 0523/4277] media: imx: add capture compose rectangle Allowing to compose captured images into larger memory buffers will let us lift alignment restrictions on CSI crop width. For now all compose rectangles are identical to the complete frame width / height. This will be changed in the next patches. Signed-off-by: Philipp Zabel Acked-by: Sakari Ailus Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 +-- drivers/staging/media/imx/imx-media-capture.c | 27 +++++++++++++++++++ drivers/staging/media/imx/imx-media-csi.c | 3 +-- drivers/staging/media/imx/imx-media-vdic.c | 4 +-- drivers/staging/media/imx/imx-media.h | 2 ++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 33ada6612fee..c7855fbc449d 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -371,8 +371,7 @@ static int prp_setup_channel(struct prp_priv *priv, memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect = vdev->compose; /* * If the field type at capture interface is interlaced, and diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 01ec9443de55..8b8ef4a11774 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -276,6 +276,10 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, priv->vdev.fmt.fmt.pix = f->fmt.pix; priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat, CS_SEL_ANY, true); + priv->vdev.compose.left = 0; + priv->vdev.compose.top = 0; + priv->vdev.compose.width = f->fmt.pix.width; + priv->vdev.compose.height = f->fmt.pix.height; return 0; } @@ -304,6 +308,25 @@ static int capture_s_std(struct file *file, void *fh, v4l2_std_id std) return v4l2_subdev_call(priv->src_sd, video, s_std, std); } +static int capture_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct capture_priv *priv = video_drvdata(file); + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + s->r = priv->vdev.compose; + break; + default: + return -EINVAL; + } + + return 0; +} + static int capture_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { @@ -364,6 +387,8 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_g_std = capture_g_std, .vidioc_s_std = capture_s_std, + .vidioc_g_selection = capture_g_selection, + .vidioc_g_parm = capture_g_parm, .vidioc_s_parm = capture_s_parm, @@ -701,6 +726,8 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &fmt_src.format, NULL); + vdev->compose.width = fmt_src.format.width; + vdev->compose.height = fmt_src.format.height; vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat, CS_SEL_ANY, false); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 555aa45e02e3..39fad3bfa6ca 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -419,8 +419,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect = vdev->compose; csi_idmac_setup_vb2_buf(priv, phys); diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 4a890714193e..297951d98ab5 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -255,10 +255,10 @@ static int setup_vdi_channel(struct vdic_priv *priv, memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; + image.rect = vdev->compose; /* one field to VDIC channels */ image.pix.height /= 2; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect.height /= 2; image.phys0 = phys0; image.phys1 = phys1; diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index bc7feb81937c..7a0e658753f0 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -80,6 +80,8 @@ struct imx_media_video_dev { /* the user format */ struct v4l2_format fmt; + /* the compose rectangle */ + struct v4l2_rect compose; const struct imx_media_pixfmt *cc; /* links this vdev to master list */ From 5b498e139fb55e179e841bb5e55168ce4ba631b5 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 11 Jan 2019 12:04:19 +0000 Subject: [PATCH 0524/4277] MAINTAINERS: Add LAKML list to ACPI for ARM64 entry Patches for ACPI for ARM64 must be posted and reviewed on the Linux ARM kernel mailing list (in addition to linux-acpi@vger) since they affect (and are merged through) the ARM64 tree. Make this explicit. Acked-by: Hanjun Guo Acked-by: Will Deacon Signed-off-by: Lorenzo Pieralisi Cc: Sudeep Holla Cc: "Rafael J. Wysocki" Signed-off-by: Catalin Marinas --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 51029a425dbe..f6a3b6c25494 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -365,6 +365,7 @@ M: Lorenzo Pieralisi M: Hanjun Guo M: Sudeep Holla L: linux-acpi@vger.kernel.org +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/acpi/arm64 From 20997568f9a621c54d5ffcf2b6973fd672148cb6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 17 Jan 2019 13:51:53 -0200 Subject: [PATCH 0525/4277] media: imx: set compose rectangle to mbus format Prepare for mbus format being smaller than the written rectangle due to burst size. Signed-off-by: Philipp Zabel Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-capture.c | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 8b8ef4a11774..a93bd7f388ef 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -203,21 +203,13 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, return 0; } -static int capture_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) +static int __capture_try_fmt_vid_cap(struct capture_priv *priv, + struct v4l2_subdev_format *fmt_src, + struct v4l2_format *f) { - struct capture_priv *priv = video_drvdata(file); - struct v4l2_subdev_format fmt_src; const struct imx_media_pixfmt *cc, *cc_src; - int ret; - fmt_src.pad = priv->src_sd_pad; - fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); - if (ret) - return ret; - - cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY); + cc_src = imx_media_find_ipu_format(fmt_src->format.code, CS_SEL_ANY); if (cc_src) { u32 fourcc, cs_sel; @@ -231,7 +223,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, cc = imx_media_find_format(fourcc, cs_sel, false); } } else { - cc_src = imx_media_find_mbus_format(fmt_src.format.code, + cc_src = imx_media_find_mbus_format(fmt_src->format.code, CS_SEL_ANY, true); if (WARN_ON(!cc_src)) return -EINVAL; @@ -241,27 +233,44 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, /* allow IDMAC interweave but enforce field order from source */ if (V4L2_FIELD_IS_INTERLACED(f->fmt.pix.field)) { - switch (fmt_src.format.field) { + switch (fmt_src->format.field) { case V4L2_FIELD_SEQ_TB: - fmt_src.format.field = V4L2_FIELD_INTERLACED_TB; + fmt_src->format.field = V4L2_FIELD_INTERLACED_TB; break; case V4L2_FIELD_SEQ_BT: - fmt_src.format.field = V4L2_FIELD_INTERLACED_BT; + fmt_src->format.field = V4L2_FIELD_INTERLACED_BT; break; default: break; } } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc); + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src->format, cc); return 0; } +static int capture_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct capture_priv *priv = video_drvdata(file); + struct v4l2_subdev_format fmt_src; + int ret; + + fmt_src.pad = priv->src_sd_pad; + fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); + if (ret) + return ret; + + return __capture_try_fmt_vid_cap(priv, &fmt_src, f); +} + static int capture_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { struct capture_priv *priv = video_drvdata(file); + struct v4l2_subdev_format fmt_src; int ret; if (vb2_is_busy(&priv->q)) { @@ -269,7 +278,13 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, return -EBUSY; } - ret = capture_try_fmt_vid_cap(file, priv, f); + fmt_src.pad = priv->src_sd_pad; + fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); + if (ret) + return ret; + + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f); if (ret) return ret; @@ -278,8 +293,8 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, CS_SEL_ANY, true); priv->vdev.compose.left = 0; priv->vdev.compose.top = 0; - priv->vdev.compose.width = f->fmt.pix.width; - priv->vdev.compose.height = f->fmt.pix.height; + priv->vdev.compose.width = fmt_src.format.width; + priv->vdev.compose.height = fmt_src.format.height; return 0; } @@ -317,9 +332,20 @@ static int capture_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: + /* The compose rectangle is fixed to the source format. */ s->r = priv->vdev.compose; break; + case V4L2_SEL_TGT_COMPOSE_PADDED: + /* + * The hardware writes with a configurable but fixed DMA burst + * size. If the source format width is not burst size aligned, + * the written frame contains padding to the right. + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = priv->vdev.fmt.fmt.pix.width; + s->r.height = priv->vdev.fmt.fmt.pix.height; + break; default: return -EINVAL; } From 451a7b7815d0b9c6e46d005a40d4b07b721fbef4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 17 Jan 2019 13:51:54 -0200 Subject: [PATCH 0526/4277] media: imx: lift CSI and PRP ENC/VF width alignment restriction The CSI, PRP ENC, and PRP VF subdevices shouldn't have to care about IDMAC line start address alignment. With compose rectangle support in the capture driver, they don't have to anymore. If the direct CSI -> IC path is enabled, the CSI output width must still be aligned to 8 pixels (IC burst length). Signed-off-by: Philipp Zabel Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 2 +- drivers/staging/media/imx/imx-media-csi.c | 11 +++++++++-- drivers/staging/media/imx/imx-media-utils.c | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index c7855fbc449d..053a911d477a 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -48,7 +48,7 @@ #define MAX_W_SRC 1024 #define MAX_H_SRC 1024 -#define W_ALIGN_SRC 4 /* multiple of 16 pixels */ +#define W_ALIGN_SRC 1 /* multiple of 2 pixels */ #define H_ALIGN_SRC 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 39fad3bfa6ca..8857994b2a16 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -41,7 +41,7 @@ #define MIN_H 144 #define MAX_W 4096 #define MAX_H 4096 -#define W_ALIGN 4 /* multiple of 16 pixels */ +#define W_ALIGN 1 /* multiple of 2 pixels */ #define H_ALIGN 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ @@ -1029,6 +1029,8 @@ static int csi_link_setup(struct media_entity *entity, v4l2_ctrl_handler_free(&priv->ctrl_hdlr); v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); priv->sink = NULL; + /* do not apply IC burst alignment in csi_try_crop */ + priv->active_output_pad = CSI_SRC_PAD_IDMAC; goto out; } @@ -1172,7 +1174,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left = infmt->width - crop->width; /* adjust crop left/width to h/w alignment restrictions */ crop->left &= ~0x3; - crop->width &= ~0x7; + if (priv->active_output_pad == CSI_SRC_PAD_DIRECT) + crop->width &= ~0x7; /* multiple of 8 pixels (IC burst) */ + else + crop->width &= ~0x1; /* multiple of 2 pixels */ in_height = infmt->height; if (infmt->field == V4L2_FIELD_ALTERNATE) @@ -1937,6 +1942,8 @@ static int imx_csi_probe(struct platform_device *pdev) priv->csi_id = pdata->csi; priv->smfc_id = (priv->csi_id == 0) ? 0 : 2; + priv->active_output_pad = CSI_SRC_PAD_IDMAC; + timer_setup(&priv->eof_timeout_timer, csi_idmac_eof_timeout, 0); spin_lock_init(&priv->irqlock); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 0eaa353d5cb3..5f110d90a4ef 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -580,6 +580,7 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc) { + u32 width; u32 stride; if (!cc) { @@ -602,9 +603,16 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false); } - stride = cc->planar ? mbus->width : (mbus->width * cc->bpp) >> 3; + /* Round up width for minimum burst size */ + width = round_up(mbus->width, 8); - pix->width = mbus->width; + /* Round up stride for IDMAC line start address alignment */ + if (cc->planar) + stride = round_up(width, 16); + else + stride = round_up((width * cc->bpp) >> 3, 8); + + pix->width = width; pix->height = mbus->height; pix->pixelformat = cc->fourcc; pix->colorspace = mbus->colorspace; @@ -613,7 +621,8 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, pix->quantization = mbus->quantization; pix->field = mbus->field; pix->bytesperline = stride; - pix->sizeimage = (pix->width * pix->height * cc->bpp) >> 3; + pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) : + stride * pix->height; return 0; } From 958f047a250ae9000308de050e4285ecff5089cf Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Tue, 15 Jan 2019 17:15:46 -0200 Subject: [PATCH 0527/4277] media: imx: queue subdev events to reachable video devices In order to receive events generated by subdevices on the video capture nodes, those events need to be forwarded to the subdevice's list of reachable video capture devices. Note this will queue the event to a video device even if there is no actual _enabled_ media path from the sub-device to the video device. So a future improvement is to skip the video device if there is no enabled path to it from the sub-device. The entity->pipe pointer can't be used for this check because in imx-media a sub-device can be a member to more than one streaming pipeline at a time. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-dev.c | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..25e916562c66 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -442,6 +442,29 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; +static void imx_media_notify(struct v4l2_subdev *sd, + unsigned int notification, + void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} + static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -462,6 +485,7 @@ static int imx_media_probe(struct platform_device *pdev) mutex_init(&imxmd->mutex); imxmd->v4l2_dev.mdev = &imxmd->md; + imxmd->v4l2_dev.notify = imx_media_notify; strscpy(imxmd->v4l2_dev.name, "imx-media", sizeof(imxmd->v4l2_dev.name)); From 54f4bc2f2aada71a681b79b64cf74ca6e8c5d26c Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Tue, 15 Jan 2019 17:16:08 -0200 Subject: [PATCH 0528/4277] media: imx: capture: Allow event subscribe/unsubscribe Implement the vidioc_(un)subscribe_event operations. Imx will allow subscribing to the imx-specific frame interval error events, events from subdevices (V4L2_EVENT_SOURCE_CHANGE), and control events. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-capture.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index a93bd7f388ef..f92edee63aa6 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -398,6 +398,21 @@ static int capture_s_parm(struct file *file, void *fh, return 0; } +static int capture_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_querycap = vidioc_querycap, @@ -427,6 +442,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = capture_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* From 337e90ed028643c7acdfd0d31e3224d05ca03d66 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Thu, 17 Jan 2019 18:58:37 -0200 Subject: [PATCH 0529/4277] media: imx-csi: Input connections to CSI should be optional Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected CSI input port as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Tim Harvey Cc: stable@vger.kernel.org Tested-by: Fabio Estevam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8857994b2a16..7abfe0aa1418 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1865,7 +1865,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) From bf72e42d400ace273931151b3a9bf1f8b846ec45 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Thu, 10 Jan 2019 16:30:12 +0100 Subject: [PATCH 0530/4277] dt-bindings: serial: sh-sci: Document r8a7778/9 HSCIF bindings Document support for the HSCIF serial ports in the Renesas R-Car M1A (R8A7778) and H1 (R8A7779) SoCs. No driver update is needed. Signed-off-by: Ulrich Hecht Reviewed-by: Simon Horman [geert: Patch description] Signed-off-by: Geert Uytterhoeven Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 20232ad05d89..dd63151dc8b6 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -29,7 +29,9 @@ Required properties: - "renesas,scif-r8a774c0" for R8A774C0 (RZ/G2E) SCIF compatible UART. - "renesas,hscif-r8a774c0" for R8A774C0 (RZ/G2E) HSCIF compatible UART. - "renesas,scif-r8a7778" for R8A7778 (R-Car M1) SCIF compatible UART. + - "renesas,hscif-r8a7778" for R8A7778 (R-Car M1) HSCIF compatible UART. - "renesas,scif-r8a7779" for R8A7779 (R-Car H1) SCIF compatible UART. + - "renesas,hscif-r8a7779" for R8A7779 (R-Car H1) HSCIF compatible UART. - "renesas,scif-r8a7790" for R8A7790 (R-Car H2) SCIF compatible UART. - "renesas,scifa-r8a7790" for R8A7790 (R-Car H2) SCIFA compatible UART. - "renesas,scifb-r8a7790" for R8A7790 (R-Car H2) SCIFB compatible UART. From 2a7b0a29b3752a08751ae723abf40c19435de387 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 18 Dec 2018 16:31:39 -0800 Subject: [PATCH 0531/4277] drivers: power: supply: goldfish_battery: Add support for reading more properties To comply with tests we need to support more power supply properties: POWER_SUPPLY_PROP_VOLTAGE_NOW POWER_SUPPLY_PROP_TEMP POWER_SUPPLY_PROP_CHARGE_COUNTER POWER_SUPPLY_PROP_CURRENT_NOW POWER_SUPPLY_PROP_CURRENT_AVG POWER_SUPPLY_PROP_CHARGE_FULL POWER_SUPPLY_PROP_CYCLE_COUNT POWER_SUPPLY_PROP_VOLTAGE_MAX POWER_SUPPLY_PROP_CURRENT_MAX Signed-off-by: Roman Kiryanov Signed-off-by: Sebastian Reichel --- drivers/power/supply/goldfish_battery.c | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index 382193fe63c4..ad969d9fc981 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -43,6 +43,15 @@ enum { BATTERY_HEALTH = 0x10, BATTERY_PRESENT = 0x14, BATTERY_CAPACITY = 0x18, + BATTERY_VOLTAGE = 0x1C, + BATTERY_TEMP = 0x20, + BATTERY_CHARGE_COUNTER = 0x24, + BATTERY_VOLTAGE_MAX = 0x28, + BATTERY_CURRENT_MAX = 0x2C, + BATTERY_CURRENT_NOW = 0x30, + BATTERY_CURRENT_AVG = 0x34, + BATTERY_CHARGE_FULL_UAH = 0x38, + BATTERY_CYCLE_COUNT = 0x40, BATTERY_STATUS_CHANGED = 1U << 0, AC_STATUS_CHANGED = 1U << 1, @@ -61,6 +70,12 @@ static int goldfish_ac_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE); break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE_MAX); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_MAX); + break; default: ret = -EINVAL; break; @@ -91,6 +106,29 @@ static int goldfish_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_TEMP); + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = GOLDFISH_BATTERY_READ(data, + BATTERY_CHARGE_COUNTER); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_NOW); + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_AVG); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = GOLDFISH_BATTERY_READ(data, + BATTERY_CHARGE_FULL_UAH); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CYCLE_COUNT); + break; default: ret = -EINVAL; break; @@ -105,10 +143,19 @@ static enum power_supply_property goldfish_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CYCLE_COUNT, }; static enum power_supply_property goldfish_ac_props[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_MAX, }; static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) From 9802c335e7c5a5dc6e34028835d2cd7a2b6c149a Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Sun, 20 Jan 2019 08:21:40 -0500 Subject: [PATCH 0532/4277] IB/rxe: Remove unnecessary rxe variable The variable rxe in the function is not used. So it is removed. Signed-off-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_comp.c | 6 +++--- drivers/infiniband/sw/rxe/rxe_loc.h | 6 ++---- drivers/infiniband/sw/rxe/rxe_recv.c | 12 +++++------- drivers/infiniband/sw/rxe/rxe_resp.c | 3 +-- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c index e996da67a851..00eb99d3df86 100644 --- a/drivers/infiniband/sw/rxe/rxe_comp.c +++ b/drivers/infiniband/sw/rxe/rxe_comp.c @@ -146,8 +146,7 @@ void retransmit_timer(struct timer_list *t) } } -void rxe_comp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp, - struct sk_buff *skb) +void rxe_comp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb) { int must_sched; @@ -155,7 +154,8 @@ void rxe_comp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp, must_sched = skb_queue_len(&qp->resp_pkts) > 1; if (must_sched != 0) - rxe_counter_inc(rxe, RXE_CNT_COMPLETER_SCHED); + rxe_counter_inc(SKB_TO_PKT(skb)->rxe, RXE_CNT_COMPLETER_SCHED); + rxe_run_task(&qp->comp.task, must_sched); } diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index 01b74597b36a..a65fbd0ff42e 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -239,11 +239,9 @@ int rxe_responder(void *arg); u32 rxe_icrc_hdr(struct rxe_pkt_info *pkt, struct sk_buff *skb); -void rxe_resp_queue_pkt(struct rxe_dev *rxe, - struct rxe_qp *qp, struct sk_buff *skb); +void rxe_resp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb); -void rxe_comp_queue_pkt(struct rxe_dev *rxe, - struct rxe_qp *qp, struct sk_buff *skb); +void rxe_comp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb); static inline unsigned int wr_opcode_mask(int opcode, struct rxe_qp *qp) { diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c index 5c29a1bb575a..f9a492ed900b 100644 --- a/drivers/infiniband/sw/rxe/rxe_recv.c +++ b/drivers/infiniband/sw/rxe/rxe_recv.c @@ -266,14 +266,12 @@ err1: return -EINVAL; } -static inline void rxe_rcv_pkt(struct rxe_dev *rxe, - struct rxe_pkt_info *pkt, - struct sk_buff *skb) +static inline void rxe_rcv_pkt(struct rxe_pkt_info *pkt, struct sk_buff *skb) { if (pkt->mask & RXE_REQ_MASK) - rxe_resp_queue_pkt(rxe, pkt->qp, skb); + rxe_resp_queue_pkt(pkt->qp, skb); else - rxe_comp_queue_pkt(rxe, pkt->qp, skb); + rxe_comp_queue_pkt(pkt->qp, skb); } static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb) @@ -319,7 +317,7 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb) pkt->qp = qp; rxe_add_ref(qp); - rxe_rcv_pkt(rxe, pkt, skb); + rxe_rcv_pkt(pkt, skb); } spin_unlock_bh(&mcg->mcg_lock); @@ -411,7 +409,7 @@ void rxe_rcv(struct sk_buff *skb) if (unlikely(bth_qpn(pkt) == IB_MULTICAST_QPN)) rxe_rcv_mcast_pkt(rxe, skb); else - rxe_rcv_pkt(rxe, pkt, skb); + rxe_rcv_pkt(pkt, skb); return; diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 231528188250..aca9f60f9b21 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -104,8 +104,7 @@ static char *resp_state_name[] = { }; /* rxe_recv calls here to add a request packet to the input queue */ -void rxe_resp_queue_pkt(struct rxe_dev *rxe, struct rxe_qp *qp, - struct sk_buff *skb) +void rxe_resp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb) { int must_sched; struct rxe_pkt_info *pkt = SKB_TO_PKT(skb); From 2b9acb9a97fe9b4101ca020643760c4a090b4cb4 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Sat, 19 Jan 2019 14:23:29 +0800 Subject: [PATCH 0533/4277] RDMA/hns: Add the process of AEQ overflow for hip08 AEQ overflow will be reported by hardware when too many asynchronous events occurred but not be handled in time. Normally, AEQ overflow error is not easy to occur. Once happened, we have to do physical function reset to recover. PF reset is implemented in two steps. Firstly, set reset level with ae_dev->ops->set_default_reset_request. Secondly, run reset with ae_dev->ops->reset_event. Signed-off-by: Xiaofei Tan Signed-off-by: Yixian Liu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index d778457ec1e5..fb990ff0c127 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4702,11 +4702,22 @@ static irqreturn_t hns_roce_v2_msix_interrupt_abn(int irq, void *dev_id) int_en = roce_read(hr_dev, ROCEE_VF_ABN_INT_EN_REG); if (roce_get_bit(int_st, HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S)) { + struct pci_dev *pdev = hr_dev->pci_dev; + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); + const struct hnae3_ae_ops *ops = ae_dev->ops; + dev_err(dev, "AEQ overflow!\n"); roce_set_bit(int_st, HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S, 1); roce_write(hr_dev, ROCEE_VF_ABN_INT_ST_REG, int_st); + /* Set reset level for reset_event() */ + if (ops->set_default_reset_request) + ops->set_default_reset_request(ae_dev, + HNAE3_FUNC_RESET); + if (ops->reset_event) + ops->reset_event(pdev, NULL); + roce_set_bit(int_en, HNS_ROCE_V2_VF_ABN_INT_EN_S, 1); roce_write(hr_dev, ROCEE_VF_ABN_INT_EN_REG, int_en); From 42e45a9449f1376d4decd751612a48f6db1f6e37 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 14 Jan 2019 15:14:14 +0000 Subject: [PATCH 0534/4277] media: s5p-mfc: Fix memdev DMA configuration Having of_reserved_mem_device_init() forcibly reconfigure DMA for all callers, potentially overriding the work done by a bus-specific .dma_configure method earlier, is at best a bad idea and at worst actively harmful. If drivers really need virtual devices to own dma-coherent memory, they should explicitly configure those devices based on the appropriate firmware node as they create them. It looks like the only driver not passing in a proper OF platform device is s5p-mfc, so move the rogue of_dma_configure() call into that driver where it logically belongs. Reviewed-by: Marek Szyprowski Acked-by: Mauro Carvalho Chehab Signed-off-by: Robin Murphy Signed-off-by: Rob Herring --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 7 +++++++ drivers/of/of_reserved_mem.c | 4 ---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8a5ba3bec3af..6db33704b1a8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1094,6 +1094,13 @@ static struct device *s5p_mfc_alloc_memdev(struct device *dev, child->dma_mask = dev->dma_mask; child->release = s5p_mfc_memdev_release; + /* + * The memdevs are not proper OF platform devices, so in order for them + * to be treated as valid DMA masters we need a bit of a hack to force + * them to inherit the MFC node's DMA configuration. + */ + of_dma_configure(child, dev->of_node, true); + if (device_add(child) == 0) { ret = of_reserved_mem_device_init_by_idx(child, dev->of_node, idx); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 1977ee0adcb1..9e02a5d80225 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -340,10 +340,6 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, mutex_lock(&of_rmem_assigned_device_mutex); list_add(&rd->list, &of_rmem_assigned_device_list); mutex_unlock(&of_rmem_assigned_device_mutex); - /* ensure that dma_ops is set for virtual devices - * using reserved memory - */ - of_dma_configure(dev, np, true); dev_info(dev, "assigned reserved memory node %s\n", rmem->name); } else { From fa31f14380bea60ccc9d7a314e0cb61f6984627f Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Sun, 13 Jan 2019 16:01:16 +0200 Subject: [PATCH 0535/4277] IB/mlx5: DEVX handling for indirection MKEY Once an indirection MKEY is created umem valid bit shouldn't be set as this MKEY doesn't really hold a umem. Signed-off-by: Yishai Hadas Reviewed-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 38 +++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 8bb711da7ee1..b7ff2138ac2a 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1011,6 +1011,32 @@ static void devx_obj_build_destroy_cmd(void *in, void *out, void *din, } } +static int devx_handle_mkey_create(struct mlx5_ib_dev *dev, + struct devx_obj *obj, + void *in, int in_len) +{ + int min_len = MLX5_BYTE_OFF(create_mkey_in, memory_key_mkey_entry) + + MLX5_FLD_SZ_BYTES(create_mkey_in, + memory_key_mkey_entry); + void *mkc; + u8 access_mode; + + if (in_len < min_len) + return -EINVAL; + + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + + access_mode = MLX5_GET(mkc, mkc, access_mode_1_0); + access_mode |= MLX5_GET(mkc, mkc, access_mode_4_2) << 2; + + if (access_mode == MLX5_MKC_ACCESS_MODE_KLMS || + access_mode == MLX5_MKC_ACCESS_MODE_KSM) + return 0; + + MLX5_SET(create_mkey_in, in, mkey_umem_valid, 1); + return 0; +} + static int devx_obj_cleanup(struct ib_uobject *uobject, enum rdma_remove_reason why) { @@ -1032,6 +1058,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( void *cmd_in = uverbs_attr_get_alloced_ptr(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_IN); int cmd_out_len = uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT); + int cmd_in_len = uverbs_attr_get_len(attrs, + MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_IN); void *cmd_out; struct ib_uobject *uobj = uverbs_attr_get_uobject( attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_HANDLE); @@ -1060,10 +1088,16 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( return -ENOMEM; MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid); - devx_set_umem_valid(cmd_in); + if (opcode == MLX5_CMD_OP_CREATE_MKEY) { + err = devx_handle_mkey_create(dev, obj, cmd_in, cmd_in_len); + if (err) + goto obj_free; + } else { + devx_set_umem_valid(cmd_in); + } err = mlx5_cmd_exec(dev->mdev, cmd_in, - uverbs_attr_get_len(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_IN), + cmd_in_len, cmd_out, cmd_out_len); if (err) goto obj_free; From 534fd7aac56a7994d16032f32123def9923e339f Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Sun, 13 Jan 2019 16:01:17 +0200 Subject: [PATCH 0536/4277] IB/mlx5: Manage indirection mkey upon DEVX flow for ODP Manage indirection mkey upon DEVX flow to support ODP. To support a page fault event on the indirection mkey it needs to be part of the device mkey radix tree. Both the creation and the deletion flows for a DEVX object which is indirection mkey were adapted to handle that. Signed-off-by: Yishai Hadas Reviewed-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 89 +++++++++++++++++++++++++++- drivers/infiniband/hw/mlx5/main.c | 1 + drivers/infiniband/hw/mlx5/mlx5_ib.h | 6 ++ include/linux/mlx5/driver.h | 1 + 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index b7ff2138ac2a..bbf9a26d8fa6 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -17,12 +17,18 @@ #define UVERBS_MODULE_NAME mlx5_ib #include +enum devx_obj_flags { + DEVX_OBJ_FLAGS_INDIRECT_MKEY = 1 << 0, +}; + #define MLX5_MAX_DESTROY_INBOX_SIZE_DW MLX5_ST_SZ_DW(delete_fte_in) struct devx_obj { struct mlx5_core_dev *mdev; u64 obj_id; u32 dinlen; /* destroy inbox length */ u32 dinbox[MLX5_MAX_DESTROY_INBOX_SIZE_DW]; + u32 flags; + struct mlx5_ib_devx_mr devx_mr; }; struct devx_umem { @@ -1011,6 +1017,36 @@ static void devx_obj_build_destroy_cmd(void *in, void *out, void *din, } } +static int devx_handle_mkey_indirect(struct devx_obj *obj, + struct mlx5_ib_dev *dev, + void *in, void *out) +{ + struct mlx5_mkey_table *table = &dev->mdev->priv.mkey_table; + struct mlx5_ib_devx_mr *devx_mr = &obj->devx_mr; + unsigned long flags; + struct mlx5_core_mkey *mkey; + void *mkc; + u8 key; + int err; + + mkey = &devx_mr->mmkey; + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + key = MLX5_GET(mkc, mkc, mkey_7_0); + mkey->key = mlx5_idx_to_mkey( + MLX5_GET(create_mkey_out, out, mkey_index)) | key; + mkey->type = MLX5_MKEY_INDIRECT_DEVX; + mkey->iova = MLX5_GET64(mkc, mkc, start_addr); + mkey->size = MLX5_GET64(mkc, mkc, len); + mkey->pd = MLX5_GET(mkc, mkc, pd); + devx_mr->ndescs = MLX5_GET(mkc, mkc, translations_octword_size); + + write_lock_irqsave(&table->lock, flags); + err = radix_tree_insert(&table->tree, mlx5_base_mkey(mkey->key), + mkey); + write_unlock_irqrestore(&table->lock, flags); + return err; +} + static int devx_handle_mkey_create(struct mlx5_ib_dev *dev, struct devx_obj *obj, void *in, int in_len) @@ -1030,13 +1066,45 @@ static int devx_handle_mkey_create(struct mlx5_ib_dev *dev, access_mode |= MLX5_GET(mkc, mkc, access_mode_4_2) << 2; if (access_mode == MLX5_MKC_ACCESS_MODE_KLMS || - access_mode == MLX5_MKC_ACCESS_MODE_KSM) + access_mode == MLX5_MKC_ACCESS_MODE_KSM) { + if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) + obj->flags |= DEVX_OBJ_FLAGS_INDIRECT_MKEY; return 0; + } MLX5_SET(create_mkey_in, in, mkey_umem_valid, 1); return 0; } +static void devx_free_indirect_mkey(struct rcu_head *rcu) +{ + kfree(container_of(rcu, struct devx_obj, devx_mr.rcu)); +} + +/* This function to delete from the radix tree needs to be called before + * destroying the underlying mkey. Otherwise a race might occur in case that + * other thread will get the same mkey before this one will be deleted, + * in that case it will fail via inserting to the tree its own data. + * + * Note: + * An error in the destroy is not expected unless there is some other indirect + * mkey which points to this one. In a kernel cleanup flow it will be just + * destroyed in the iterative destruction call. In a user flow, in case + * the application didn't close in the expected order it's its own problem, + * the mkey won't be part of the tree, in both cases the kernel is safe. + */ +static void devx_cleanup_mkey(struct devx_obj *obj) +{ + struct mlx5_mkey_table *table = &obj->mdev->priv.mkey_table; + struct mlx5_core_mkey *del_mkey; + unsigned long flags; + + write_lock_irqsave(&table->lock, flags); + del_mkey = radix_tree_delete(&table->tree, + mlx5_base_mkey(obj->devx_mr.mmkey.key)); + write_unlock_irqrestore(&table->lock, flags); +} + static int devx_obj_cleanup(struct ib_uobject *uobject, enum rdma_remove_reason why) { @@ -1044,10 +1112,21 @@ static int devx_obj_cleanup(struct ib_uobject *uobject, struct devx_obj *obj = uobject->object; int ret; + if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) + devx_cleanup_mkey(obj); + ret = mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out)); if (ib_is_destroy_retryable(ret, why, uobject)) return ret; + if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) { + struct mlx5_ib_dev *dev = to_mdev(uobject->context->device); + + call_srcu(&dev->mr_srcu, &obj->devx_mr.rcu, + devx_free_indirect_mkey); + return ret; + } + kfree(obj); return ret; } @@ -1108,6 +1187,12 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( &obj_id); WARN_ON(obj->dinlen > MLX5_MAX_DESTROY_INBOX_SIZE_DW * sizeof(u32)); + if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) { + err = devx_handle_mkey_indirect(obj, dev, cmd_in, cmd_out); + if (err) + goto obj_destroy; + } + err = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT, cmd_out, cmd_out_len); if (err) goto obj_destroy; @@ -1116,6 +1201,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( return 0; obj_destroy: + if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) + devx_cleanup_mkey(obj); mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out)); obj_free: kfree(obj); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 61064b7171fc..ae00f994673b 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -5724,6 +5724,7 @@ void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev) { mlx5_ib_cleanup_multiport_master(dev); if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { + srcu_barrier(&dev->mr_srcu); cleanup_srcu_struct(&dev->mr_srcu); drain_workqueue(dev->advise_mr_wq); destroy_workqueue(dev->advise_mr_wq); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index b0a37ca2a714..819207190343 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -602,6 +602,12 @@ struct mlx5_ib_mw { int ndescs; }; +struct mlx5_ib_devx_mr { + struct mlx5_core_mkey mmkey; + int ndescs; + struct rcu_head rcu; +}; + struct mlx5_ib_umr_context { struct ib_cqe cqe; enum ib_wc_status status; diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index b6f5839f129a..619d6fee96a1 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -364,6 +364,7 @@ struct mlx5_core_sig_ctx { enum { MLX5_MKEY_MR = 1, MLX5_MKEY_MW, + MLX5_MKEY_INDIRECT_DEVX, }; struct mlx5_core_mkey { From 414556af5f27c1f40b2afb18c3a1fee4b9535d40 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Sun, 13 Jan 2019 16:01:18 +0200 Subject: [PATCH 0537/4277] IB/mlx5: Add support for ODP for DEVX indirection mkey Add support for ODP for DEVX indirection mkey, it includes: - Recognizing its type as part of the radix tree lookup. - Use similar flow as done for the MW MKEY type. Signed-off-by: Yishai Hadas Reviewed-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 8d46b1dc5658..82ac6cdc7130 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -685,6 +685,21 @@ struct pf_frame { int depth; }; +static int get_indirect_num_descs(struct mlx5_core_mkey *mmkey) +{ + struct mlx5_ib_mw *mw; + struct mlx5_ib_devx_mr *devx_mr; + + if (mmkey->type == MLX5_MKEY_MW) { + mw = container_of(mmkey, struct mlx5_ib_mw, mmkey); + return mw->ndescs; + } + + devx_mr = container_of(mmkey, struct mlx5_ib_devx_mr, + mmkey); + return devx_mr->ndescs; +} + /* * Handle a single data segment in a page-fault WQE or RDMA region. * @@ -705,11 +720,11 @@ static int pagefault_single_data_segment(struct mlx5_ib_dev *dev, u32 key, bool prefetch = flags & MLX5_PF_FLAGS_PREFETCH; struct pf_frame *head = NULL, *frame; struct mlx5_core_mkey *mmkey; - struct mlx5_ib_mw *mw; struct mlx5_ib_mr *mr; struct mlx5_klm *pklm; u32 *out = NULL; size_t offset; + int ndescs; srcu_key = srcu_read_lock(&dev->mr_srcu); @@ -762,7 +777,8 @@ next_mr: break; case MLX5_MKEY_MW: - mw = container_of(mmkey, struct mlx5_ib_mw, mmkey); + case MLX5_MKEY_INDIRECT_DEVX: + ndescs = get_indirect_num_descs(mmkey); if (depth >= MLX5_CAP_GEN(dev->mdev, max_indirection)) { mlx5_ib_dbg(dev, "indirection level exceeded\n"); @@ -771,7 +787,7 @@ next_mr: } outlen = MLX5_ST_SZ_BYTES(query_mkey_out) + - sizeof(*pklm) * (mw->ndescs - 2); + sizeof(*pklm) * (ndescs - 2); if (outlen > cur_outlen) { kfree(out); @@ -786,14 +802,14 @@ next_mr: pklm = (struct mlx5_klm *)MLX5_ADDR_OF(query_mkey_out, out, bsf0_klm0_pas_mtt0_1); - ret = mlx5_core_query_mkey(dev->mdev, &mw->mmkey, out, outlen); + ret = mlx5_core_query_mkey(dev->mdev, mmkey, out, outlen); if (ret) goto srcu_unlock; offset = io_virt - MLX5_GET64(query_mkey_out, out, memory_key_mkey_entry.start_addr); - for (i = 0; bcnt && i < mw->ndescs; i++, pklm++) { + for (i = 0; bcnt && i < ndescs; i++, pklm++) { if (offset >= be32_to_cpu(pklm->bcount)) { offset -= be32_to_cpu(pklm->bcount); continue; From 6113cc44015b41ca51c0a76fed82522b68108dac Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 17 Jan 2019 20:08:15 +0200 Subject: [PATCH 0538/4277] IB/mlx5: Don't override existing ip_protocol Two flow specifications can set the ip protocol field in the flow table entry: 1) IB_FLOW_SPEC_TCP/UDP/GRE - set the ip protocol accordingly. 2) IB_FLOW_SPEC_IPV4/6 - has ip_protocol field for users who want to receive specific L4 packets. We need to avoid overriding of the ip_protocol with zeros, in case that the user first put the L4 specification and only then the L3. Fixes: ca0d47538528b ('IB/mlx5: Add support in TOS and protocol to flow steering') Signed-off-by: Maor Gottlieb Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 53 +++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index ae00f994673b..b32b5c7f1cfd 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2333,10 +2333,29 @@ static u8 get_match_criteria_enable(u32 *match_criteria) return match_criteria_enable; } -static void set_proto(void *outer_c, void *outer_v, u8 mask, u8 val) +static int set_proto(void *outer_c, void *outer_v, u8 mask, u8 val) { - MLX5_SET(fte_match_set_lyr_2_4, outer_c, ip_protocol, mask); - MLX5_SET(fte_match_set_lyr_2_4, outer_v, ip_protocol, val); + u8 entry_mask; + u8 entry_val; + int err = 0; + + if (!mask) + goto out; + + entry_mask = MLX5_GET(fte_match_set_lyr_2_4, outer_c, + ip_protocol); + entry_val = MLX5_GET(fte_match_set_lyr_2_4, outer_v, + ip_protocol); + if (!entry_mask) { + MLX5_SET(fte_match_set_lyr_2_4, outer_c, ip_protocol, mask); + MLX5_SET(fte_match_set_lyr_2_4, outer_v, ip_protocol, val); + goto out; + } + /* Don't override existing ip protocol */ + if (mask != entry_mask || val != entry_val) + err = -EINVAL; +out: + return err; } static void set_flow_label(void *misc_c, void *misc_v, u32 mask, u32 val, @@ -2570,8 +2589,10 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, set_tos(headers_c, headers_v, ib_spec->ipv4.mask.tos, ib_spec->ipv4.val.tos); - set_proto(headers_c, headers_v, - ib_spec->ipv4.mask.proto, ib_spec->ipv4.val.proto); + if (set_proto(headers_c, headers_v, + ib_spec->ipv4.mask.proto, + ib_spec->ipv4.val.proto)) + return -EINVAL; break; case IB_FLOW_SPEC_IPV6: if (FIELDS_NOT_SUPPORTED(ib_spec->ipv6.mask, LAST_IPV6_FIELD)) @@ -2610,9 +2631,10 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, ib_spec->ipv6.mask.traffic_class, ib_spec->ipv6.val.traffic_class); - set_proto(headers_c, headers_v, - ib_spec->ipv6.mask.next_hdr, - ib_spec->ipv6.val.next_hdr); + if (set_proto(headers_c, headers_v, + ib_spec->ipv6.mask.next_hdr, + ib_spec->ipv6.val.next_hdr)) + return -EINVAL; set_flow_label(misc_params_c, misc_params_v, ntohl(ib_spec->ipv6.mask.flow_label), @@ -2633,10 +2655,8 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, LAST_TCP_UDP_FIELD)) return -EOPNOTSUPP; - MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, - 0xff); - MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, - IPPROTO_TCP); + if (set_proto(headers_c, headers_v, 0xff, IPPROTO_TCP)) + return -EINVAL; MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_sport, ntohs(ib_spec->tcp_udp.mask.src_port)); @@ -2653,10 +2673,8 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, LAST_TCP_UDP_FIELD)) return -EOPNOTSUPP; - MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, - 0xff); - MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, - IPPROTO_UDP); + if (set_proto(headers_c, headers_v, 0xff, IPPROTO_UDP)) + return -EINVAL; MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport, ntohs(ib_spec->tcp_udp.mask.src_port)); @@ -2672,6 +2690,9 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, if (ib_spec->gre.mask.c_ks_res0_ver) return -EOPNOTSUPP; + if (set_proto(headers_c, headers_v, 0xff, IPPROTO_GRE)) + return -EINVAL; + MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, 0xff); MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, From 182ca6e0ae23550cf98d05daeab8dcb7fdc0a928 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Mon, 21 Jan 2019 18:20:41 +0800 Subject: [PATCH 0539/4277] pstore/ram: Replace dummy_data heap memory with stack memory In ramoops_register_dummy() dummy_data is allocated via kzalloc() then it will always occupy the heap space after register platform device via platform_device_register_data(), but it will not be used any more. So let's free it for system usage, replace it with stack memory is better due to small size. Signed-off-by: Yue Hu [kees: add required memset and adjust sizeof() argument] Signed-off-by: Kees Cook --- fs/pstore/ram.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 898c8321b343..1adb5e35c004 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -110,7 +110,6 @@ struct ramoops_context { }; static struct platform_device *dummy; -static struct ramoops_platform_data *dummy_data; static int ramoops_pstore_open(struct pstore_info *psi) { @@ -892,13 +891,12 @@ static inline void ramoops_unregister_dummy(void) { platform_device_unregister(dummy); dummy = NULL; - - kfree(dummy_data); - dummy_data = NULL; } static void __init ramoops_register_dummy(void) { + struct ramoops_platform_data pdata; + /* * Prepare a dummy platform data structure to carry the module * parameters. If mem_size isn't set, then there are no module @@ -909,30 +907,25 @@ static void __init ramoops_register_dummy(void) pr_info("using module parameters\n"); - dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL); - if (!dummy_data) { - pr_info("could not allocate pdata\n"); - return; - } - - dummy_data->mem_size = mem_size; - dummy_data->mem_address = mem_address; - dummy_data->mem_type = mem_type; - dummy_data->record_size = record_size; - dummy_data->console_size = ramoops_console_size; - dummy_data->ftrace_size = ramoops_ftrace_size; - dummy_data->pmsg_size = ramoops_pmsg_size; - dummy_data->dump_oops = dump_oops; - dummy_data->flags = RAMOOPS_FLAG_FTRACE_PER_CPU; + memset(&pdata, 0, sizeof(pdata)); + pdata.mem_size = mem_size; + pdata.mem_address = mem_address; + pdata.mem_type = mem_type; + pdata.record_size = record_size; + pdata.console_size = ramoops_console_size; + pdata.ftrace_size = ramoops_ftrace_size; + pdata.pmsg_size = ramoops_pmsg_size; + pdata.dump_oops = dump_oops; + pdata.flags = RAMOOPS_FLAG_FTRACE_PER_CPU; /* * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC * (using 1 byte for ECC isn't much of use anyway). */ - dummy_data->ecc_info.ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc; + pdata.ecc_info.ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc; dummy = platform_device_register_data(NULL, "ramoops", -1, - dummy_data, sizeof(struct ramoops_platform_data)); + &pdata, sizeof(pdata)); if (IS_ERR(dummy)) { pr_info("could not create platform device: %ld\n", PTR_ERR(dummy)); From 05f9f57500e214e7a82bad768c41911393d63881 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Tue, 8 Jan 2019 15:53:42 +0530 Subject: [PATCH 0540/4277] dt-bindings: remoteproc: qcom: Introduce shutdown-ack irq for Q6V5 Introduce shutdown-irq binding required for sysmon shutdown for Q6V5 MSS on SDM845/MSM8996 SoCs and for WCSS Q6V5 on QCS404 SoC. Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- .../bindings/remoteproc/qcom,adsp.txt | 17 ++++++++++++++--- .../bindings/remoteproc/qcom,q6v5.txt | 15 ++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt index 60ee0f73071a..292dfda9770d 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt @@ -19,13 +19,24 @@ on the Qualcomm ADSP Hexagon core. - interrupts-extended: Usage: required Value type: - Definition: must list the watchdog, fatal IRQs ready, handover and - stop-ack IRQs + Definition: reference to the interrupts that match interrupt-names - interrupt-names: Usage: required Value type: - Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" + Definition: The interrupts needed depends on the compatible + string: + qcom,msm8974-adsp-pil: + qcom,msm8996-adsp-pil: + qcom,msm8996-slpi-pil: + qcom,qcs404-adsp-pas: + qcom,qcs404-cdsp-pas: + qcom,sdm845-adsp-pas: + qcom,sdm845-cdsp-pas: + must be "wdog", "fatal", "ready", "handover", "stop-ack" + qcom,qcs404-wcss-pas: + must be "wdog", "fatal", "ready", "handover", "stop-ack", + "shutdown-ack" - firmware-name: Usage: optional diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 401e49ebee39..41ca5df5be5a 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -28,13 +28,22 @@ on the Qualcomm Hexagon core. - interrupts-extended: Usage: required Value type: - Definition: must list the watchdog, fatal IRQs ready, handover and - stop-ack IRQs + Definition: reference to the interrupts that match interrupt-names - interrupt-names: Usage: required Value type: - Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" + Definition: The interrupts needed depends on the the compatible + string: + qcom,q6v5-pil: + qcom,ipq8074-wcss-pil: + qcom,msm8916-mss-pil: + qcom,msm8974-mss-pil: + must be "wdog", "fatal", "ready", "handover", "stop-ack" + qcom,msm8996-mss-pil: + qcom,sdm845-mss-pil: + must be "wdog", "fatal", "ready", "handover", "stop-ack", + "shutdown-ack" - firmware-name: Usage: optional From 027045a6e2b7cd81216e8a559534a30fb0782702 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Tue, 8 Jan 2019 15:53:43 +0530 Subject: [PATCH 0541/4277] remoteproc: qcom: Add shutdown-ack irq Add shutdown-ack irq handling required for sysmon shutdown for Q6V5 MSS on SDM845/MSM8996 and for WCSS Q6V5 on QCS404 SoC. Signed-off-by: Sibi Sankar [bjorn: Revert back to qcom_add_sysmon_subdev returning a sysmon object] Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_adsp.c | 4 +++ drivers/remoteproc/qcom_q6v5_mss.c | 4 +++ drivers/remoteproc/qcom_q6v5_pas.c | 4 +++ drivers/remoteproc/qcom_sysmon.c | 39 +++++++++++++++++++++++++++-- drivers/remoteproc/qcom_wcnss.c | 4 +++ 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 79374d1de311..91bb99668253 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -439,6 +439,10 @@ static int adsp_probe(struct platform_device *pdev) adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(adsp->sysmon)) { + ret = PTR_ERR(adsp->sysmon); + goto disable_pm; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index c86dc40cfb8c..07d1cc52a647 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1341,6 +1341,10 @@ static int q6v5_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); + if (IS_ERR(qproc->sysmon)) { + ret = PTR_ERR(qproc->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index b1e63fcd5fdf..c529a04c14ed 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -304,6 +304,10 @@ static int adsp_probe(struct platform_device *pdev) adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(adsp->sysmon)) { + ret = PTR_ERR(adsp->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 603b813151f2..610f31f43cbf 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -24,6 +26,7 @@ struct qcom_sysmon { const char *name; + int shutdown_irq; int ssctl_version; int ssctl_instance; @@ -33,6 +36,7 @@ struct qcom_sysmon { struct rpmsg_endpoint *ept; struct completion comp; + struct completion shutdown_comp; struct mutex lock; bool ssr_ack; @@ -431,6 +435,15 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; } +static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) +{ + struct qcom_sysmon *sysmon = data; + + complete(&sysmon->shutdown_comp); + + return IRQ_HANDLED; +} + /** * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc * @rproc: rproc context to associate the subdev with @@ -448,7 +461,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); if (!sysmon) - return NULL; + return ERR_PTR(-ENOMEM); sysmon->dev = rproc->dev.parent; sysmon->rproc = rproc; @@ -457,13 +470,35 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon->ssctl_instance = ssctl_instance; init_completion(&sysmon->comp); + init_completion(&sysmon->shutdown_comp); mutex_init(&sysmon->lock); + sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, + "shutdown-ack"); + if (sysmon->shutdown_irq < 0) { + if (sysmon->shutdown_irq != -ENODATA) { + dev_err(sysmon->dev, + "failed to retrieve shutdown-ack IRQ\n"); + return ERR_PTR(sysmon->shutdown_irq); + } + } else { + ret = devm_request_threaded_irq(sysmon->dev, + sysmon->shutdown_irq, + NULL, sysmon_shutdown_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 shutdown-ack", sysmon); + if (ret) { + dev_err(sysmon->dev, + "failed to acquire shutdown-ack IRQ\n"); + return ERR_PTR(ret); + } + } + ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); if (ret < 0) { dev_err(sysmon->dev, "failed to initialize qmi handle\n"); kfree(sysmon); - return NULL; + return ERR_PTR(ret); } qmi_add_lookup(&sysmon->qmi, 43, 0, 0); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 1152da49a18a..adcce523971e 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -553,6 +553,10 @@ static int wcnss_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); + if (IS_ERR(wcnss->sysmon)) { + ret = PTR_ERR(wcnss->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) From 74f2720265541070e2d3721c5261148b4084952e Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Tue, 8 Jan 2019 15:53:44 +0530 Subject: [PATCH 0542/4277] remoteproc: qcom: Wait for shutdown-ack/ind on sysmon shutdown After sending a sysmon shutdown request to the SSCTL service on the subsystem, wait for the service to send shutdown-ack interrupt or an indication message to signal the completion of graceful shutdown. Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_sysmon.c | 42 +++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 610f31f43cbf..c231314eab66 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -36,6 +36,7 @@ struct qcom_sysmon { struct rpmsg_endpoint *ept; struct completion comp; + struct completion ind_comp; struct completion shutdown_comp; struct mutex lock; @@ -140,6 +141,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, } #define SSCTL_SHUTDOWN_REQ 0x21 +#define SSCTL_SHUTDOWN_READY_IND 0x21 #define SSCTL_SUBSYS_EVENT_REQ 0x23 #define SSCTL_MAX_MSG_LEN 7 @@ -255,6 +257,29 @@ static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { {} }; +static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { + {} +}; + +static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *data) +{ + struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); + + complete(&sysmon->ind_comp); +} + +static struct qmi_msg_handler qmi_indication_handler[] = { + { + .type = QMI_INDICATION, + .msg_id = SSCTL_SHUTDOWN_READY_IND, + .ei = ssctl_shutdown_ind_ei, + .decoded_size = 0, + .fn = sysmon_ind_cb + }, + {} +}; + /** * ssctl_request_shutdown() - request shutdown via SSCTL QMI service * @sysmon: sysmon context @@ -265,6 +290,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) struct qmi_txn txn; int ret; + reinit_completion(&sysmon->ind_comp); + reinit_completion(&sysmon->shutdown_comp); ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); if (ret < 0) { dev_err(sysmon->dev, "failed to allocate QMI txn\n"); @@ -286,6 +313,17 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) dev_err(sysmon->dev, "shutdown request failed\n"); else dev_dbg(sysmon->dev, "shutdown request completed\n"); + + if (sysmon->shutdown_irq > 0) { + ret = wait_for_completion_timeout(&sysmon->shutdown_comp, + 10 * HZ); + if (!ret) { + ret = try_wait_for_completion(&sysmon->ind_comp); + if (!ret) + dev_err(sysmon->dev, + "timeout waiting for shutdown ack\n"); + } + } } /** @@ -470,6 +508,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon->ssctl_instance = ssctl_instance; init_completion(&sysmon->comp); + init_completion(&sysmon->ind_comp); init_completion(&sysmon->shutdown_comp); mutex_init(&sysmon->lock); @@ -494,7 +533,8 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, } } - ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); + ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, + qmi_indication_handler); if (ret < 0) { dev_err(sysmon->dev, "failed to initialize qmi handle\n"); kfree(sysmon); From 794a48cec678486b63cfe8218f0fe693711d683f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 21 Jan 2019 13:37:30 +0100 Subject: [PATCH 0543/4277] EDAC: Add James Morse as a reviewer With the growing amount of ARM[,64] EDAC enablement happening, add James as a reviewer for the ARM bits. Signed-off-by: Borislav Petkov Acked-by: James Morse Cc: linux-edac Link: https://lkml.kernel.org/r/20190121123941.28247-1-bp@alien8.de --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6ce25174fa02..0f5ef1d6299a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5426,6 +5426,7 @@ F: drivers/edac/thunderx_edac* EDAC-CORE M: Borislav Petkov M: Mauro Carvalho Chehab +R: James Morse L: linux-edac@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git for-next T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git linux_next From b0c3e54e2496eb36cd3f28215992c4d6d708c921 Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Wed, 16 Jan 2019 08:31:25 +0100 Subject: [PATCH 0544/4277] gpio-f7188x: add support Fintek F81804 & F81966 Basic implementation of driver is used to support Fintek F81804 & F81966 gpios with custom register set. Signed-off-by: Steffen Kothe Signed-off-by: Linus Walleij --- drivers/gpio/gpio-f7188x.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 13350c9d7f5e..0896c825b312 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -39,8 +39,10 @@ #define SIO_F71889_ID 0x0909 /* F71889 chipset ID */ #define SIO_F71889A_ID 0x1005 /* F71889A chipset ID */ #define SIO_F81866_ID 0x1010 /* F81866 chipset ID */ +#define SIO_F81804_ID 0x1502 /* F81804 chipset ID, same for f81966 */ -enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866 }; + +enum chips { f71869, f71869a, f71882fg, f71889a, f71889f, f81866, f81804 }; static const char * const f7188x_names[] = { "f71869", @@ -49,6 +51,7 @@ static const char * const f7188x_names[] = { "f71889a", "f71889f", "f81866", + "f81804", }; struct f7188x_sio { @@ -223,6 +226,18 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = { F7188X_GPIO_BANK(80, 8, 0x88), }; + +static struct f7188x_gpio_bank f81804_gpio_bank[] = { + F7188X_GPIO_BANK(0, 8, 0xF0), + F7188X_GPIO_BANK(10, 8, 0xE0), + F7188X_GPIO_BANK(20, 8, 0xD0), + F7188X_GPIO_BANK(50, 8, 0xA0), + F7188X_GPIO_BANK(60, 8, 0x90), + F7188X_GPIO_BANK(70, 8, 0x80), + F7188X_GPIO_BANK(90, 8, 0x98), +}; + + static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { int err; @@ -407,6 +422,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev) data->nr_bank = ARRAY_SIZE(f81866_gpio_bank); data->bank = f81866_gpio_bank; break; + case f81804: + data->nr_bank = ARRAY_SIZE(f81804_gpio_bank); + data->bank = f81804_gpio_bank; + break; default: return -ENODEV; } @@ -469,6 +488,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81866_ID: sio->type = f81866; break; + case SIO_F81804_ID: + sio->type = f81804; + break; default: pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid); goto err; From f724c6bee162be3a9cd3b9168414ca106e0cdfe2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 16 Jan 2019 03:01:21 +0000 Subject: [PATCH 0545/4277] rtc: sd3078: make symbol 'sd3078_driver' static Fixes the following sparse warning: drivers/rtc/rtc-sd3078.c:218:19: warning: symbol 'sd3078_driver' was not declared. Should it be static? Fixes: 1d67a232105b ("rtc: sd3078: new driver.") Signed-off-by: Wei Yongjun Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sd3078.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index a70d7311d039..704844f32b45 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -215,7 +215,7 @@ static const struct of_device_id rtc_dt_match[] = { }; MODULE_DEVICE_TABLE(of, rtc_dt_match); -struct i2c_driver sd3078_driver = { +static struct i2c_driver sd3078_driver = { .driver = { .name = "sd3078", .owner = THIS_MODULE, From 5a5b614ba61cc2a89ad0dffc63d913a1a6ba1f9f Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 21 Jan 2019 12:09:30 +0100 Subject: [PATCH 0546/4277] rtc: s3c: Rewrite clock handling s3c_rtc_enable/disable_clk() functions were designed to be called multiple times without reference counting, because they were initially only used in alarm setting/clearing functions, which can be called both when alarm is already set or not. Later however, calls to those functions have been added to other places in the driver - like time and /proc reading callbacks, what results in broken alarm if any of such events happens after the alarm has been set. Fix this by simplifying s3c_rtc_enable/disable_clk() functions to rely on proper reference counting in clock core and move alarm enable counter to s3c_rtc_setaie() function. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 76 ++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 04c68178c42d..ea82efba6f94 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -39,7 +39,7 @@ struct s3c_rtc { void __iomem *base; struct clk *rtc_clk; struct clk *rtc_src_clk; - bool clk_disabled; + bool alarm_enabled; const struct s3c_rtc_data *data; @@ -47,7 +47,7 @@ struct s3c_rtc { int irq_tick; spinlock_t pie_lock; - spinlock_t alarm_clk_lock; + spinlock_t alarm_lock; int ticnt_save; int ticnt_en_save; @@ -70,44 +70,27 @@ struct s3c_rtc_data { static int s3c_rtc_enable_clk(struct s3c_rtc *info) { - unsigned long irq_flags; - int ret = 0; + int ret; - spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); + ret = clk_enable(info->rtc_clk); + if (ret) + return ret; - if (info->clk_disabled) { - ret = clk_enable(info->rtc_clk); - if (ret) - goto out; - - if (info->data->needs_src_clk) { - ret = clk_enable(info->rtc_src_clk); - if (ret) { - clk_disable(info->rtc_clk); - goto out; - } + if (info->data->needs_src_clk) { + ret = clk_enable(info->rtc_src_clk); + if (ret) { + clk_disable(info->rtc_clk); + return ret; } - info->clk_disabled = false; } - -out: - spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); - - return ret; + return 0; } static void s3c_rtc_disable_clk(struct s3c_rtc *info) { - unsigned long irq_flags; - - spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); - if (!info->clk_disabled) { - if (info->data->needs_src_clk) - clk_disable(info->rtc_src_clk); - clk_disable(info->rtc_clk); - info->clk_disabled = true; - } - spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); } /* IRQ Handlers */ @@ -135,6 +118,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { struct s3c_rtc *info = dev_get_drvdata(dev); + unsigned long flags; unsigned int tmp; int ret; @@ -151,17 +135,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) writeb(tmp, info->base + S3C2410_RTCALM); + spin_lock_irqsave(&info->alarm_lock, flags); + + if (info->alarm_enabled && !enabled) + s3c_rtc_disable_clk(info); + else if (!info->alarm_enabled && enabled) + ret = s3c_rtc_enable_clk(info); + + info->alarm_enabled = enabled; + spin_unlock_irqrestore(&info->alarm_lock, flags); + s3c_rtc_disable_clk(info); - if (enabled) { - ret = s3c_rtc_enable_clk(info); - if (ret) - return ret; - } else { - s3c_rtc_disable_clk(info); - } - - return 0; + return ret; } /* Set RTC frequency */ @@ -357,10 +343,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) writeb(alrm_en, info->base + S3C2410_RTCALM); - s3c_rtc_disable_clk(info); - s3c_rtc_setaie(dev, alrm->enabled); + s3c_rtc_disable_clk(info); + return 0; } @@ -491,7 +477,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) return -EINVAL; } spin_lock_init(&info->pie_lock); - spin_lock_init(&info->alarm_clk_lock); + spin_lock_init(&info->alarm_lock); platform_set_drvdata(pdev, info); @@ -591,6 +577,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) s3c_rtc_setfreq(info, 1); + s3c_rtc_disable_clk(info); + return 0; err_nortc: From 64704c92fd19c599f20433aae1372a7ccab79a57 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 18 Jan 2019 14:28:37 +0100 Subject: [PATCH 0547/4277] rtc: s3c: Use generic helper to get driver data Replace of_match_node() with of_device_get_match_data(), which removes a few lines of code from the driver. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index ea82efba6f94..e81a2b22a5c3 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -442,16 +443,6 @@ static int s3c_rtc_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id s3c_rtc_dt_match[]; - -static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) -{ - const struct of_device_id *match; - - match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); - return match->data; -} - static int s3c_rtc_probe(struct platform_device *pdev) { struct s3c_rtc *info = NULL; @@ -471,7 +462,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) } info->dev = &pdev->dev; - info->data = s3c_rtc_get_data(pdev); + info->data = of_device_get_match_data(&pdev->dev); if (!info->data) { dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); return -EINVAL; From 5eca99917cb73086908cdb262ca54dff1fce4f6e Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:26 +0100 Subject: [PATCH 0548/4277] devicetree: property-units: Add femtofarads unit When dealing with capacitance of 0.5 pF then a smaller unit is preferred. Add femtofarads to deal with this. Signed-off-by: Sam Ravnborg Reviewed-by: Rob Herring Cc: Mark Rutland Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/property-units.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/property-units.txt b/Documentation/devicetree/bindings/property-units.txt index 45ce054d844d..bfd33734faca 100644 --- a/Documentation/devicetree/bindings/property-units.txt +++ b/Documentation/devicetree/bindings/property-units.txt @@ -31,6 +31,7 @@ Electricity -microwatt-hours: micro Watt-hours -microvolt : micro volts -picofarads : picofarads +-femtofarads : femtofarads Temperature ---------------------------------------- From d08fd7c1b9bd78b10345d2f9b115c5fd362b75cd Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:27 +0100 Subject: [PATCH 0549/4277] dt-bindings: rtc: Add quartz-load-femtofarads property The quartz-load-femtofarads are relevant for several users. Add it as a common property in rtc. Note that valid values and default values must be documented. Signed-off-by: Sam Ravnborg Suggested-by: Rob Herring Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Rob Herring Cc: Mark Rutland Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index b79a4794a928..0cacbc0edd20 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -21,12 +21,16 @@ Optional properties The following properties may not be supported by all drivers. However, if a driver wants to support one of the below features, it should adapt the bindings below. -- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given - if trickle charger should be enabled -- trickle-diode-disable : Do not use internal trickle charger diode Should be - given if internal trickle charger diode should be - disabled -- wakeup-source : Enables wake up of host system on alarm +- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given + if trickle charger should be enabled +- trickle-diode-disable : Do not use internal trickle charger diode Should be + given if internal trickle charger diode should be + disabled +- wakeup-source : Enables wake up of host system on alarm +- quartz-load-femtofarads : The capacitive load of the quartz(x-tal), + expressed in femto Farad (fF). + The default value shall be listed (if optional), + and likewise all valid values. Trivial RTCs ------------ From 8c12af15e7340a14e2978814ef28df58962283d6 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:28 +0100 Subject: [PATCH 0550/4277] dt-binding: pcf8523: add xtal load capacitance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NXP pcf8523 supports two different xtal load capacitance - 7000fF (7pF) HW default - 12500fF (12.5pF) Minimum power consumption, driver default To obtain a precise RTC the pcf8523 must be configured with the correct capacitance load of the xtal. Add a property to specify the xtal capacitance load. The default value matches that of the current Linux driver. With a dedicated binding remove the entry in rtc.txt Signed-off-by: Søren Andersen Signed-off-by: Sam Ravnborg Reviewed-by: Rob Herring Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Mark Rutland Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/nxp,pcf8523.txt | 18 ++++++++++++++++++ Documentation/devicetree/bindings/rtc/rtc.txt | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt new file mode 100644 index 000000000000..0b1080c60f63 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt @@ -0,0 +1,18 @@ +* NXP PCF8523 Real Time Clock + +Required properties: +- compatible: Should contain "nxp,pcf8523". +- reg: I2C address for chip. + +Optional property: +- quartz-load-femtofarads: The capacitive load of the quartz(x-tal), + expressed in femto Farad (fF). Valid values are 7000 and 12500. + Default value (if no value is specified) is 12500fF. + +Example: + +pcf8523: rtc@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + quartz-load-femtofarads = <7000>; +}; diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 0cacbc0edd20..27a7c1578f6e 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -56,7 +56,6 @@ microcrystal,rv3029 Real Time Clock Module with I2C-Bus microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock nxp,pcf2129 Real-time clock -nxp,pcf8523 Real-time Clock nxp,pcf8563 Real-time clock/calendar nxp,pcf85063 Tiny Real-Time Clock pericom,pt7c4338 Real-time Clock Module From e4a604cbee5470fc116ac9a4f852152070984286 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:29 +0100 Subject: [PATCH 0551/4277] dt-binding: pcf85063: add xtal load capacitance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NXP pcf85063 supports two different xtal load capacitance - 7000fF (7pF) HW default, Linux driver default - 12500fF (12.5pF) Minimum power consumption To obtain a precise RTC the pcf85063 must be configured with the correct capacitance load of the xtal. Add a property to specify the xtal capacitance load. The default value matches that of the current Linux driver. With a dedicated binding remove the entry in rtc.txt Signed-off-by: Søren Andersen Signed-off-by: Sam Ravnborg Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Rob Herring Cc: Mark Rutland Cc: Urs Fässler Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/nxp,pcf85063.txt | 18 ++++++++++++++++++ Documentation/devicetree/bindings/rtc/rtc.txt | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt new file mode 100644 index 000000000000..d3e380ad712d --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt @@ -0,0 +1,18 @@ +* NXP PCF85063 Real Time Clock + +Required properties: +- compatible: Should contain "nxp,pcf85063". +- reg: I2C address for chip. + +Optional property: +- quartz-load-femtofarads: The capacitive load of the quartz(x-tal), + expressed in femto Farad (fF). Valid values are 7000 and 12500. + Default value (if no value is specified) is 7000fF. + +Example: + +pcf85063: rtc@51 { + compatible = "nxp,pcf85063"; + reg = <0x51>; + quartz-load-femtofarads = <12500>; +}; diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 27a7c1578f6e..d86e2850fe1c 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -57,7 +57,6 @@ microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock nxp,pcf2129 Real-time clock nxp,pcf8563 Real-time clock/calendar -nxp,pcf85063 Tiny Real-Time Clock pericom,pt7c4338 Real-time Clock Module ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC From 189927e719e36ceefbb8037f23d3849e47833aef Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:30 +0100 Subject: [PATCH 0552/4277] rtc: pcf8523: set xtal load capacitance from DT Add support for specifying the xtal load capacitance in the DT node. The pcf8523 supports xtal load capacitance of 7pF or 12.5pF. If the rtc has the wrong configuration the time will drift several hours/week. The driver use the default value 12.5pF. The DT may specify either 7000fF or 12500fF. (The DT uses femto Farad to avoid decimal numbers). Other values are warned and the driver uses the default value. Signed-off-by: Sam Ravnborg Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf8523.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index fb13933f4006..b5c61a70b5df 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -97,8 +97,9 @@ static int pcf8523_voltage_low(struct i2c_client *client) return !!(value & REG_CONTROL3_BLF); } -static int pcf8523_select_capacitance(struct i2c_client *client, bool high) +static int pcf8523_load_capacitance(struct i2c_client *client) { + u32 load; u8 value; int err; @@ -106,14 +107,24 @@ static int pcf8523_select_capacitance(struct i2c_client *client, bool high) if (err < 0) return err; - if (!high) - value &= ~REG_CONTROL1_CAP_SEL; - else + load = 12500; + of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads", + &load); + + switch (load) { + default: + dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500", + load); + /* fall through */ + case 12500: value |= REG_CONTROL1_CAP_SEL; + break; + case 7000: + value &= ~REG_CONTROL1_CAP_SEL; + break; + } err = pcf8523_write(client, REG_CONTROL1, value); - if (err < 0) - return err; return err; } @@ -347,9 +358,10 @@ static int pcf8523_probe(struct i2c_client *client, if (!pcf) return -ENOMEM; - err = pcf8523_select_capacitance(client, true); + err = pcf8523_load_capacitance(client); if (err < 0) - return err; + dev_warn(&client->dev, "failed to set xtal load capacitance: %d", + err); err = pcf8523_set_pm(client, 0); if (err < 0) From bbb4383826b20cdc9efbd8ffdd19a5fa58320175 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 19 Jan 2019 10:00:31 +0100 Subject: [PATCH 0553/4277] rtc: pcf85063: set xtal load capacitance from DT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for specifying the xtal load capacitance in the DT node. The pcf85063 supports xtal load capacitance of 7pF or 12.5pF. If the rtc has the wrong configuration the time will drift several hours/week. The driver use the default value 7pF. The DT may specify either 7000fF or 12500fF. (The DT uses femto Farad to avoid decimal numbers). Other values are warned and the driver uses the default value. Signed-off-by: Sam Ravnborg Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Urs Fässler Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf85063.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index 283c2335b01b..b79af6e96292 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -27,6 +27,7 @@ */ #define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL1_CAP_SEL BIT(0) #define PCF85063_REG_CTRL1_STOP BIT(5) #define PCF85063_REG_CTRL2 0x01 @@ -180,6 +181,39 @@ static const struct rtc_class_ops pcf85063_rtc_ops = { .set_time = pcf85063_rtc_set_time }; +static int pcf85063_load_capacitance(struct i2c_client *client) +{ + u32 load; + int rc; + u8 reg; + + rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1); + if (rc < 0) + return rc; + + reg = rc; + load = 7000; + of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads", + &load); + + switch (load) { + default: + dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000", + load); + /* fall through */ + case 7000: + reg &= ~PCF85063_REG_CTRL1_CAP_SEL; + break; + case 12500: + reg |= PCF85063_REG_CTRL1_CAP_SEL; + break; + } + + rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg); + + return rc; +} + static int pcf85063_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -197,6 +231,11 @@ static int pcf85063_probe(struct i2c_client *client, return err; } + err = pcf85063_load_capacitance(client); + if (err < 0) + dev_warn(&client->dev, "failed to set xtal load capacitance: %d", + err); + rtc = devm_rtc_device_register(&client->dev, pcf85063_driver.driver.name, &pcf85063_rtc_ops, THIS_MODULE); From 5910fa0d0d9809c4d8fbf1fb905a8c86fbf2eccb Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 20:43:33 -0600 Subject: [PATCH 0554/4277] rtc: coh901331: fix a missing check of clk_prepare clk_prepare() could fail, so let's check its status and if it fails return its error code upstream. Signed-off-by: Kangjie Lu Acked-by: Linus Walleij Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-coh901331.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index fc5cf5c44ae7..0b232c84f674 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -235,9 +235,13 @@ static int coh901331_suspend(struct device *dev) static int coh901331_resume(struct device *dev) { + int ret; struct coh901331_port *rtap = dev_get_drvdata(dev); - clk_prepare(rtap->clk); + ret = clk_prepare(rtap->clk); + if (ret) + return ret; + if (device_may_wakeup(dev)) { disable_irq_wake(rtap->irq); } else { From 9a20b5e35a536d6bb4b2d4a3b14a0457e205356c Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 21:09:11 -0600 Subject: [PATCH 0555/4277] rtc: hym8563: fix a missing check of block data read When i2c_smbus_read_i2c_block_data() fails, the read data in "buf" could be incorrect and should not be used. The fix checks if i2c_smbus_read_i2c_block_data fails, and if so, return its error code upstream. Signed-off-by: Kangjie Lu Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-hym8563.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index e5ad527cb75e..d03f5d212eea 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -109,6 +109,8 @@ static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm) } ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf); + if (ret < 0) + return ret; tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK); tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK); From 2a4daadd4d3e507138f8937926e6a4df49c6bfdc Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 6 Jan 2019 08:21:03 +0000 Subject: [PATCH 0556/4277] rtc: cmos: ignore bogus century byte Older versions of Libreboot and Coreboot had an invalid value (`3' in my case) in the century byte affecting the GM45 in the Thinkpad X200. Not everybody's updated their firmwares, and Linux <= 4.2 was able to read the RTC without problems, so workaround this by ignoring invalid values. Fixes: 3c217e51d8a272b9 ("rtc: cmos: century support") Cc: Alexandre Belloni Cc: Alessandro Zummo Cc: Sylvain Chouleur Cc: Patrick McDermott Cc: linux-rtc@vger.kernel.org Signed-off-by: Eric Wong Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mc146818-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index 2f1772a358ca..18a6f15e313d 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -82,7 +82,7 @@ unsigned int mc146818_get_time(struct rtc_time *time) time->tm_year += real_year - 72; #endif - if (century) + if (century > 20) time->tm_year += (century - 19) * 100; /* From 1e47704e15f0907d2d8482ec939e3cb500bae558 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 22 Jan 2019 10:42:15 +0000 Subject: [PATCH 0557/4277] dt-bindings: rtc: Add bindings for Cadence RTC This patch adds a DT binding documentation for Cadence RTC controller. Signed-off-by: Jan Kotas Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/cdns,rtc.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/cdns,rtc.txt diff --git a/Documentation/devicetree/bindings/rtc/cdns,rtc.txt b/Documentation/devicetree/bindings/rtc/cdns,rtc.txt new file mode 100644 index 000000000000..14a04487b432 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/cdns,rtc.txt @@ -0,0 +1,25 @@ +Cadence Real Time Clock + +The Cadence RTC controller with date, time and alarm capabilities. +The alarm may wake the system from low-power state. + +Required properties: +- compatible: Should be "cdns,rtc-r109v3" +- reg: Specifies base physical address and size of the register area. +- interrupts: A single interrupt specifier. +- clocks: Must contain two entries: + - pclk: APB registers clock + - ref_clk: reference 1Hz or 100Hz clock, depending on IP configuration + See ../clocks/clock-bindings.txt for details. + +Example: + rtc0: rtc@fd080000 { + compatible = "cdns,rtc-r109v3"; + reg = <0xfd080000 0x1000>; + + clock-names = "pclk", "ref_clk"; + clocks = <&sysclock>, <&refclock>; + + interrupt-parent = <&gic>; + interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>; + }; From 42986fb8119928d32e585c73181f39506937a3b2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 22 Jan 2019 10:42:16 +0000 Subject: [PATCH 0558/4277] rtc: Add Cadence RTC driver This patch adds a driver for Cadence RTC controller. It can be enabled with RTC_DRV_CADENCE Kconfig option. It supports waking system from sleep modes. Signed-off-by: Jan Kotas Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-cadence.c | 423 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 434 insertions(+) create mode 100644 drivers/rtc/rtc-cadence.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a24fe8b7b98a..faa9ae1a3062 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1517,6 +1517,16 @@ config RTC_DRV_ARMADA38X This driver can also be built as a module. If so, the module will be called armada38x-rtc. +config RTC_DRV_CADENCE + tristate "Cadence RTC driver" + depends on OF && HAS_IOMEM + help + If you say Y here you will get access to Cadence RTC IP + found on certain SOCs. + + To compile this driver as a module, choose M here: the + module will be called rtc-cadence. + config RTC_DRV_FTRTC010 tristate "Faraday Technology FTRTC010 RTC" depends on HAS_IOMEM diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 39eb6de4ad74..faca02109aaa 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o +obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c new file mode 100644 index 000000000000..3b7d643c8a63 --- /dev/null +++ b/drivers/rtc/rtc-cadence.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2019 Cadence + * + * Authors: + * Jan Kotas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define CDNS_RTC_CTLR 0x00 +#define CDNS_RTC_HMR 0x04 +#define CDNS_RTC_TIMR 0x08 +#define CDNS_RTC_CALR 0x0C +#define CDNS_RTC_TIMAR 0x10 +#define CDNS_RTC_CALAR 0x14 +#define CDNS_RTC_AENR 0x18 +#define CDNS_RTC_EFLR 0x1C +#define CDNS_RTC_IENR 0x20 +#define CDNS_RTC_IDISR 0x24 +#define CDNS_RTC_IMSKR 0x28 +#define CDNS_RTC_STSR 0x2C +#define CDNS_RTC_KRTCR 0x30 + +/* Control */ +#define CDNS_RTC_CTLR_TIME BIT(0) +#define CDNS_RTC_CTLR_CAL BIT(1) +#define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL) + +/* Status */ +#define CDNS_RTC_STSR_VT BIT(0) +#define CDNS_RTC_STSR_VC BIT(1) +#define CDNS_RTC_STSR_VTA BIT(2) +#define CDNS_RTC_STSR_VCA BIT(3) +#define CDNS_RTC_STSR_VT_VC (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC) +#define CDNS_RTC_STSR_VTA_VCA (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA) + +/* Keep RTC */ +#define CDNS_RTC_KRTCR_KRTC BIT(0) + +/* Alarm, Event, Interrupt */ +#define CDNS_RTC_AEI_HOS BIT(0) +#define CDNS_RTC_AEI_SEC BIT(1) +#define CDNS_RTC_AEI_MIN BIT(2) +#define CDNS_RTC_AEI_HOUR BIT(3) +#define CDNS_RTC_AEI_DATE BIT(4) +#define CDNS_RTC_AEI_MNTH BIT(5) +#define CDNS_RTC_AEI_ALRM BIT(6) + +/* Time */ +#define CDNS_RTC_TIME_H GENMASK(7, 0) +#define CDNS_RTC_TIME_S GENMASK(14, 8) +#define CDNS_RTC_TIME_M GENMASK(22, 16) +#define CDNS_RTC_TIME_HR GENMASK(29, 24) +#define CDNS_RTC_TIME_PM BIT(30) +#define CDNS_RTC_TIME_CH BIT(31) + +/* Calendar */ +#define CDNS_RTC_CAL_DAY GENMASK(2, 0) +#define CDNS_RTC_CAL_M GENMASK(7, 3) +#define CDNS_RTC_CAL_D GENMASK(13, 8) +#define CDNS_RTC_CAL_Y GENMASK(23, 16) +#define CDNS_RTC_CAL_C GENMASK(29, 24) +#define CDNS_RTC_CAL_CH BIT(31) + +#define CDNS_RTC_MAX_REGS_TRIES 3 + +struct cdns_rtc { + struct rtc_device *rtc_dev; + struct clk *pclk; + struct clk *ref_clk; + void __iomem *regs; + int irq; +}; + +static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled) +{ + u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL; + + writel(reg, crtc->regs + CDNS_RTC_CTLR); +} + +static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc) +{ + return !(readl(crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL); +} + +static irqreturn_t cdns_rtc_irq_handler(int irq, void *id) +{ + struct device *dev = id; + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + /* Reading the register clears it */ + if (!(readl(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM)) + return IRQ_NONE; + + rtc_update_irq(crtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static u32 cdns_rtc_time2reg(struct rtc_time *tm) +{ + return FIELD_PREP(CDNS_RTC_TIME_S, bin2bcd(tm->tm_sec)) + | FIELD_PREP(CDNS_RTC_TIME_M, bin2bcd(tm->tm_min)) + | FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour)); +} + +static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg)); + tm->tm_min = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg)); + tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg)); +} + +static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 reg; + + /* If the RTC is disabled, assume the values are invalid */ + if (!cdns_rtc_get_enabled(crtc)) + return -EINVAL; + + cdns_rtc_set_enabled(crtc, false); + + reg = readl(crtc->regs + CDNS_RTC_TIMR); + cdns_rtc_reg2time(reg, tm); + + reg = readl(crtc->regs + CDNS_RTC_CALR); + tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); + tm->tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; + tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg)) + + bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900; + tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1; + + cdns_rtc_set_enabled(crtc, true); + return 0; +} + +static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 timr, calr, stsr; + int ret = -EIO; + int year = tm->tm_year + 1900; + int tries; + + cdns_rtc_set_enabled(crtc, false); + + timr = cdns_rtc_time2reg(tm); + + calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday)) + | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1)) + | FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100)) + | FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100)) + | FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1); + + /* Update registers, check valid flags */ + for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { + writel(timr, crtc->regs + CDNS_RTC_TIMR); + writel(calr, crtc->regs + CDNS_RTC_CALR); + stsr = readl(crtc->regs + CDNS_RTC_STSR); + + if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) { + ret = 0; + break; + } + } + + cdns_rtc_set_enabled(crtc, true); + return ret; +} + +static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (enabled) { + writel((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR + | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH), + crtc->regs + CDNS_RTC_AENR); + writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR); + } else { + writel(0, crtc->regs + CDNS_RTC_AENR); + writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IDISR); + } + + return 0; +} + +static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 reg; + + reg = readl(crtc->regs + CDNS_RTC_TIMAR); + cdns_rtc_reg2time(reg, &alarm->time); + + reg = readl(crtc->regs + CDNS_RTC_CALAR); + alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); + alarm->time.tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; + + return 0; +} + +static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + int ret = -EIO; + int tries; + u32 timar, calar, stsr; + + cdns_rtc_alarm_irq_enable(dev, 0); + + timar = cdns_rtc_time2reg(&alarm->time); + calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday)) + | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1)); + + /* Update registers, check valid alarm flags */ + for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { + writel(timar, crtc->regs + CDNS_RTC_TIMAR); + writel(calar, crtc->regs + CDNS_RTC_CALAR); + stsr = readl(crtc->regs + CDNS_RTC_STSR); + + if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) { + ret = 0; + break; + } + } + + if (!ret) + cdns_rtc_alarm_irq_enable(dev, alarm->enabled); + return ret; +} + +static const struct rtc_class_ops cdns_rtc_ops = { + .read_time = cdns_rtc_read_time, + .set_time = cdns_rtc_set_time, + .read_alarm = cdns_rtc_read_alarm, + .set_alarm = cdns_rtc_set_alarm, + .alarm_irq_enable = cdns_rtc_alarm_irq_enable, +}; + +static int cdns_rtc_probe(struct platform_device *pdev) +{ + struct cdns_rtc *crtc; + struct resource *res; + int ret; + unsigned long ref_clk_freq; + + crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL); + if (!crtc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + crtc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(crtc->regs)) + return PTR_ERR(crtc->regs); + + crtc->irq = platform_get_irq(pdev, 0); + if (crtc->irq < 0) + return -EINVAL; + + crtc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(crtc->pclk)) { + ret = PTR_ERR(crtc->pclk); + dev_err(&pdev->dev, + "Failed to retrieve the peripheral clock, %d\n", ret); + return ret; + } + + crtc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(crtc->ref_clk)) { + ret = PTR_ERR(crtc->ref_clk); + dev_err(&pdev->dev, + "Failed to retrieve the reference clock, %d\n", ret); + return ret; + } + + crtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(crtc->rtc_dev)) { + ret = PTR_ERR(crtc->rtc_dev); + dev_err(&pdev->dev, + "Failed to allocate the RTC device, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, crtc); + + ret = clk_prepare_enable(crtc->pclk); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable the peripheral clock, %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(crtc->ref_clk); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable the reference clock, %d\n", ret); + goto err_disable_pclk; + } + + ref_clk_freq = clk_get_rate(crtc->ref_clk); + if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) { + dev_err(&pdev->dev, + "Invalid reference clock frequency %lu Hz.\n", + ref_clk_freq); + ret = -EINVAL; + goto err_disable_ref_clk; + } + + ret = devm_request_irq(&pdev->dev, crtc->irq, + cdns_rtc_irq_handler, 0, + dev_name(&pdev->dev), &pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to request interrupt for the device, %d\n", + ret); + goto err_disable_ref_clk; + } + + /* The RTC supports 01.01.1900 - 31.12.2999 */ + crtc->rtc_dev->range_min = mktime64(1900, 1, 1, 0, 0, 0); + crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59); + + crtc->rtc_dev->ops = &cdns_rtc_ops; + device_init_wakeup(&pdev->dev, true); + + /* Always use 24-hour mode and keep the RTC values */ + writel(0, crtc->regs + CDNS_RTC_HMR); + writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR); + + ret = rtc_register_device(crtc->rtc_dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to register the RTC device, %d\n", ret); + goto err_disable_wakeup; + } + + return 0; + +err_disable_wakeup: + device_init_wakeup(&pdev->dev, false); + +err_disable_ref_clk: + clk_disable_unprepare(crtc->ref_clk); + +err_disable_pclk: + clk_disable_unprepare(crtc->pclk); + + return ret; +} + +static int cdns_rtc_remove(struct platform_device *pdev) +{ + struct cdns_rtc *crtc = platform_get_drvdata(pdev); + + cdns_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, 0); + + clk_disable_unprepare(crtc->pclk); + clk_disable_unprepare(crtc->ref_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cdns_rtc_suspend(struct device *dev) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(crtc->irq); + + return 0; +} + +static int cdns_rtc_resume(struct device *dev) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(crtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume); + +static const struct of_device_id cdns_rtc_of_match[] = { + { .compatible = "cdns,rtc-r109v3" }, + { }, +}; +MODULE_DEVICE_TABLE(of, cdns_rtc_of_match); + +static struct platform_driver cdns_rtc_driver = { + .driver = { + .name = "cdns-rtc", + .of_match_table = cdns_rtc_of_match, + .pm = &cdns_rtc_pm_ops, + }, + .probe = cdns_rtc_probe, + .remove = cdns_rtc_remove, +}; +module_platform_driver(cdns_rtc_driver); + +MODULE_AUTHOR("Jan Kotas "); +MODULE_DESCRIPTION("Cadence RTC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cdns-rtc"); From 51c48b310183ab6ba5419edfc6a8de889cc04521 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 19 Jan 2019 11:35:04 -0600 Subject: [PATCH 0559/4277] PCI: Probe bridge window attributes once at enumeration-time pci_bridge_check_ranges() determines whether a bridge supports the optional I/O and prefetchable memory windows and sets the flag bits in the bridge resources. This *could* be done once during enumeration except that the resource allocation code completely clears the flag bits, e.g., in the pci_assign_unassigned_bridge_resources() path. The problem with pci_bridge_check_ranges() in the resource allocation path is that we may allocate resources after devices have been claimed by drivers, and pci_bridge_check_ranges() *changes* the window registers to determine whether they're writable. This may break concurrent accesses to devices behind the bridge. Add a new pci_read_bridge_windows() to determine whether a bridge supports the optional windows, call it once during enumeration, remember the results, and change pci_bridge_check_ranges() so it doesn't touch the bridge windows but sets the flag bits based on those remembered results. Link: https://lore.kernel.org/linux-pci/1506151482-113560-1-git-send-email-wangzhou1@hisilicon.com Link: https://lists.gnu.org/archive/html/qemu-devel/2018-12/msg02082.html Reported-by: Yandong Xu Tested-by: Yandong Xu Signed-off-by: Bjorn Helgaas Cc: Michael S. Tsirkin Cc: Sagi Grimberg Cc: Ofer Hayut Cc: Roy Shterman Cc: Keith Busch Cc: Zhou Wang --- drivers/pci/probe.c | 52 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/setup-bus.c | 45 ++++------------------------------- include/linux/pci.h | 3 +++ 3 files changed, 59 insertions(+), 41 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..2ef8b954c65a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -348,6 +348,57 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) } } +static void pci_read_bridge_windows(struct pci_dev *bridge) +{ + u16 io; + u32 pmem, tmp; + + pci_read_config_word(bridge, PCI_IO_BASE, &io); + if (!io) { + pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); + pci_read_config_word(bridge, PCI_IO_BASE, &io); + pci_write_config_word(bridge, PCI_IO_BASE, 0x0); + } + if (io) + bridge->io_window = 1; + + /* + * DECchip 21050 pass 2 errata: the bridge may miss an address + * disconnect boundary by one PCI data phase. Workaround: do not + * use prefetching on this device. + */ + if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) + return; + + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); + if (!pmem) { + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, + 0xffe0fff0); + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); + } + if (!pmem) + return; + + bridge->pref_window = 1; + + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + + /* + * Bridge claims to have a 64-bit prefetchable memory + * window; verify that the upper bits are actually + * writable. + */ + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, + 0xffffffff); + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem); + if (tmp) + bridge->pref_64_window = 1; + } +} + static void pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; @@ -1739,6 +1790,7 @@ int pci_setup_device(struct pci_dev *dev) pci_read_irq(dev); dev->transparent = ((dev->class & 0xff) == 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); + pci_read_bridge_windows(dev); set_pcie_hotplug_bridge(dev); pos = pci_find_capability(dev, PCI_CAP_ID_SSVID); if (pos) { diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ed960436df5e..1941bb0a6c13 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -735,58 +735,21 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i) base/limit registers must be read-only and read as 0. */ static void pci_bridge_check_ranges(struct pci_bus *bus) { - u16 io; - u32 pmem; struct pci_dev *bridge = bus->self; - struct resource *b_res; + struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; - b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; b_res[1].flags |= IORESOURCE_MEM; - pci_read_config_word(bridge, PCI_IO_BASE, &io); - if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); - pci_read_config_word(bridge, PCI_IO_BASE, &io); - pci_write_config_word(bridge, PCI_IO_BASE, 0x0); - } - if (io) + if (bridge->io_window) b_res[0].flags |= IORESOURCE_IO; - /* DECchip 21050 pass 2 errata: the bridge may miss an address - disconnect boundary by one PCI data phase. - Workaround: do not use prefetching on this device. */ - if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) - return; - - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - if (!pmem) { - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xffe0fff0); - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); - } - if (pmem) { + if (bridge->pref_window) { b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == - PCI_PREF_RANGE_TYPE_64) { + if (bridge->pref_64_window) { b_res[2].flags |= IORESOURCE_MEM_64; b_res[2].flags |= PCI_PREF_RANGE_TYPE_64; } } - - /* double check if bridge does support 64 bit pref */ - if (b_res[2].flags & IORESOURCE_MEM_64) { - u32 mem_base_hi, tmp; - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, - &mem_base_hi); - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - 0xffffffff); - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); - if (!tmp) - b_res[2].flags &= ~IORESOURCE_MEM_64; - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - mem_base_hi); - } } /* Helper function for sizing routines: find first available diff --git a/include/linux/pci.h b/include/linux/pci.h index 65f1d8c2f082..40b327b814aa 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -373,6 +373,9 @@ struct pci_dev { bool match_driver; /* Skip attaching driver */ unsigned int transparent:1; /* Subtractive decode bridge */ + unsigned int io_window:1; /* Bridge has I/O window */ + unsigned int pref_window:1; /* Bridge has pref mem window */ + unsigned int pref_64_window:1; /* Pref mem window is 64-bit */ unsigned int multifunction:1; /* Multi-function device */ unsigned int is_busmaster:1; /* Is busmaster */ From 83a50d3a235b9c074f30af105b9d9751b55edb70 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:34 -0600 Subject: [PATCH 0560/4277] PCI: Use of_node_name_eq() for node name comparisons Convert string compares of DT node names to use of_node_name_eq() helper instead. This removes direct access to the node name pointer. Signed-off-by: Rob Herring [bhelgaas: drop similar rpaphp_core.c change to avoid merge conflict] Signed-off-by: Bjorn Helgaas --- drivers/pci/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 4c4217d0c3f1..3d32da15c215 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -113,7 +113,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, * a fake root for all functions of a multi-function * device we go down them as well. */ - if (!strcmp(node->name, "multifunc-device")) { + if (of_node_name_eq(node, "multifunc-device")) { for_each_child_of_node(node, node2) { if (__of_pci_pci_compare(node2, devfn)) { of_node_put(node); From 3d5b38684951564d07e3f4bb482ef9491f49ced1 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 17 Jan 2019 03:14:54 +0000 Subject: [PATCH 0561/4277] i2c: imx: Fix inconsistent IS_ERR and PTR_ERR in i2c_imx_dma_request() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the call to PTR_ERR to access the value just tested by IS_ERR. Fixes: 5b3a23a3cc94 ("i2c: imx: notify about real errors on dma i2c_imx_dma_request") Signed-off-by: YueHaibing Reviewed-by: Oleksij Rempel Reviewed-by: Uwe Kleine-König Acked-by: Esben Haabendal Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 09b124547669..42fed40198a0 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -287,7 +287,7 @@ static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma->chan_tx = dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { - ret = PTR_ERR(dma->chan_rx); + ret = PTR_ERR(dma->chan_tx); if (ret != -ENODEV && ret != -EPROBE_DEFER) dev_err(dev, "can't request DMA tx channel (%d)\n", ret); goto fail_al; From 81d696c7c4ff8c981333159d072da65268bfe6d5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Jan 2019 11:03:57 +0100 Subject: [PATCH 0562/4277] i2c: rcar: Fix clients using i2c from suspend callback When doing s2idle/s2ram on Salvator-X(S): WARNING: CPU: 2 PID: 971 at drivers/i2c/i2c-core-base.c:1869 __i2c_transfer+0x608/0x910 [...] Call trace: __i2c_transfer+0x608/0x910 i2c_smbus_xfer_emulated+0x158/0x5b0 __i2c_smbus_xfer+0x17c/0x818 i2c_smbus_xfer+0x64/0x98 i2c_smbus_read_byte_data+0x40/0x70 cs2000_bset.isra.1+0x2c/0x68 __cs2000_set_rate.constprop.7+0x80/0x148 cs2000_resume+0x18/0x20 dpm_run_callback+0x74/0x330 device_resume_early+0xd4/0x120 dpm_resume_early+0x158/0x4f8 suspend_devices_and_enter+0x36c/0xd98 [...] On second resume, the sound driver fails with: cs2000-cp 2-004f: pll lock failed rcar_sound ec500000.sound: can't use clk 1 As the CS2000 clock driver needs to send I2C messages during suspend, the I2C controller driver should be suspended later, and resumed earlier. Fix this by using the noirq sleep ops instead of the normal sleep ops, which are called after the late sleep ops, as used by the CS2000 clock driver. Fixes: 18569fa89a4db9ed ("i2c: rcar: add suspend/resume support") Signed-off-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 1d6390eed861..d6c0f50a6dab 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1034,7 +1034,9 @@ static int rcar_i2c_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rcar_i2c_pm_ops, rcar_i2c_suspend, rcar_i2c_resume); +static const struct dev_pm_ops rcar_i2c_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume) +}; #define DEV_PM_OPS (&rcar_i2c_pm_ops) #else From 5b3e3b43b03bf84f1656c2f7afda6b73f945cc82 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:49 +0100 Subject: [PATCH 0563/4277] i2c: sh_mobile: simplify sending address for RX pd->pos won't be smaller than -1, so we can simplify the logic. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index a64f2ff3cb49..e18e3cedf817 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -392,13 +392,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) int real_pos; do { - if (pd->pos <= -1) { + if (sh_mobile_i2c_is_first_byte(pd)) { sh_mobile_i2c_get_data(pd, &data); - - if (sh_mobile_i2c_is_first_byte(pd)) - i2c_op(pd, OP_TX_FIRST, data); - else - i2c_op(pd, OP_TX, data); + i2c_op(pd, OP_TX_FIRST, data); break; } From 1f32fbdfc1e0af25c8f8572284c5d17d1cbcf9d7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:50 +0100 Subject: [PATCH 0564/4277] i2c: sh_mobile: remove get_data function It makes the code much easier comprehensible to explicitly code that the first byte will be client address and all the following bytes are the actual data. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index e18e3cedf817..27b57b376549 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -358,18 +358,6 @@ static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) return pd->pos == -1; } -static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd, - unsigned char *buf) -{ - switch (pd->pos) { - case -1: - *buf = i2c_8bit_addr_from_msg(pd->msg); - break; - default: - *buf = pd->msg->buf[pd->pos]; - } -} - static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { unsigned char data; @@ -379,8 +367,13 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) return 1; } - sh_mobile_i2c_get_data(pd, &data); - i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data); + if (sh_mobile_i2c_is_first_byte(pd)) { + data = i2c_8bit_addr_from_msg(pd->msg); + i2c_op(pd, OP_TX_FIRST, data); + } else { + data = pd->msg->buf[pd->pos]; + i2c_op(pd, OP_TX, data); + } pd->pos++; return 0; @@ -393,7 +386,7 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) do { if (sh_mobile_i2c_is_first_byte(pd)) { - sh_mobile_i2c_get_data(pd, &data); + data = i2c_8bit_addr_from_msg(pd->msg); i2c_op(pd, OP_TX_FIRST, data); break; } From d16f2bfea3cc3ebc8d1d45bc24d4f9f2c852dddd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:51 +0100 Subject: [PATCH 0565/4277] i2c: sh_mobile: drop 'data' argument from i2c_op function It is clear that we always send the address in TX_FIRST and data in TX. No need to pass it from the caller. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 41 +++++++++++++----------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 27b57b376549..ae5804c12998 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -303,13 +303,12 @@ static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd) return sh_mobile_i2c_check_timing(pd); } -static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, - enum sh_mobile_i2c_op op, unsigned char data) +static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op op) { unsigned char ret = 0; unsigned long flags; - dev_dbg(pd->dev, "op %d, data in 0x%02x\n", op, data); + dev_dbg(pd->dev, "op %d\n", op); spin_lock_irqsave(&pd->lock, flags); @@ -317,12 +316,12 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, case OP_START: /* issue start and trigger DTE interrupt */ iic_wr(pd, ICCR, ICCR_ICE | ICCR_TRS | ICCR_BBSY); break; - case OP_TX_FIRST: /* disable DTE interrupt and write data */ + case OP_TX_FIRST: /* disable DTE interrupt and write client address */ iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE); - iic_wr(pd, ICDR, data); + iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg)); break; case OP_TX: /* write data */ - iic_wr(pd, ICDR, data); + iic_wr(pd, ICDR, pd->msg->buf[pd->pos]); break; case OP_TX_STOP: /* issue a stop (or rep_start) */ iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS @@ -360,20 +359,15 @@ static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { - unsigned char data; - if (pd->pos == pd->msg->len) { - i2c_op(pd, OP_TX_STOP, 0); + i2c_op(pd, OP_TX_STOP); return 1; } - if (sh_mobile_i2c_is_first_byte(pd)) { - data = i2c_8bit_addr_from_msg(pd->msg); - i2c_op(pd, OP_TX_FIRST, data); - } else { - data = pd->msg->buf[pd->pos]; - i2c_op(pd, OP_TX, data); - } + if (sh_mobile_i2c_is_first_byte(pd)) + i2c_op(pd, OP_TX_FIRST); + else + i2c_op(pd, OP_TX); pd->pos++; return 0; @@ -386,13 +380,12 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) do { if (sh_mobile_i2c_is_first_byte(pd)) { - data = i2c_8bit_addr_from_msg(pd->msg); - i2c_op(pd, OP_TX_FIRST, data); + i2c_op(pd, OP_TX_FIRST); break; } if (pd->pos == 0) { - i2c_op(pd, OP_TX_TO_RX, 0); + i2c_op(pd, OP_TX_TO_RX); break; } @@ -401,18 +394,18 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) if (pd->pos == pd->msg->len) { if (pd->stop_after_dma) { /* Simulate PIO end condition after DMA transfer */ - i2c_op(pd, OP_RX_STOP, 0); + i2c_op(pd, OP_RX_STOP); pd->pos++; break; } if (real_pos < 0) { - i2c_op(pd, OP_RX_STOP, 0); + i2c_op(pd, OP_RX_STOP); break; } - data = i2c_op(pd, OP_RX_STOP_DATA, 0); + data = i2c_op(pd, OP_RX_STOP_DATA); } else if (real_pos >= 0) { - data = i2c_op(pd, OP_RX, 0); + data = i2c_op(pd, OP_RX); } if (real_pos >= 0) @@ -687,7 +680,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, start_ch(pd, msg, do_start); if (do_start) - i2c_op(pd, OP_START, 0); + i2c_op(pd, OP_START); /* The interrupt handler takes care of the rest... */ timeout = wait_event_timeout(pd->wait, From e659f36d46904b5e0e860401c0454fcf6025f419 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:52 +0100 Subject: [PATCH 0566/4277] i2c: sh_mobile: remove is_first_byte function All state machines deal with pd->pos values. This helper function is an exception and makes it only more confusing. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ae5804c12998..ef9101cde29f 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -352,11 +352,6 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op return ret; } -static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) -{ - return pd->pos == -1; -} - static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) { if (pd->pos == pd->msg->len) { @@ -364,7 +359,7 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) return 1; } - if (sh_mobile_i2c_is_first_byte(pd)) + if (pd->pos == -1) i2c_op(pd, OP_TX_FIRST); else i2c_op(pd, OP_TX); @@ -379,7 +374,7 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) int real_pos; do { - if (sh_mobile_i2c_is_first_byte(pd)) { + if (pd->pos == -1) { i2c_op(pd, OP_TX_FIRST); break; } From 0130e3bfa9fa102252ad318d0f5608cc09822baa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:53 +0100 Subject: [PATCH 0567/4277] i2c: sh_mobile: replace break; with if-block In preparation to remove the do-while-loop. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ef9101cde29f..ab6969ed7eff 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -394,11 +394,10 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) break; } - if (real_pos < 0) { + if (real_pos < 0) i2c_op(pd, OP_RX_STOP); - break; - } - data = i2c_op(pd, OP_RX_STOP_DATA); + else + data = i2c_op(pd, OP_RX_STOP_DATA); } else if (real_pos >= 0) { data = i2c_op(pd, OP_RX); } From 63c524600353a9c2d097041863a9ab36ed2db79b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:54 +0100 Subject: [PATCH 0568/4277] i2c: sh_mobile: refactor rx isr Remove the do_while loop which was just there to have an easy exit with "break;" and replace it with if-else-blocks which should make the state machine clearer. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 52 +++++++++++++----------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index ab6969ed7eff..dd4df961d8eb 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -373,39 +373,31 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) unsigned char data; int real_pos; - do { - if (pd->pos == -1) { - i2c_op(pd, OP_TX_FIRST); - break; + real_pos = pd->pos - 2; + + if (pd->pos == -1) { + i2c_op(pd, OP_TX_FIRST); + } else if (pd->pos == 0) { + i2c_op(pd, OP_TX_TO_RX); + } else if (pd->pos == pd->msg->len) { + if (pd->stop_after_dma) { + /* Simulate PIO end condition after DMA transfer */ + i2c_op(pd, OP_RX_STOP); + pd->pos++; + goto done; } - if (pd->pos == 0) { - i2c_op(pd, OP_TX_TO_RX); - break; - } - - real_pos = pd->pos - 2; - - if (pd->pos == pd->msg->len) { - if (pd->stop_after_dma) { - /* Simulate PIO end condition after DMA transfer */ - i2c_op(pd, OP_RX_STOP); - pd->pos++; - break; - } - - if (real_pos < 0) - i2c_op(pd, OP_RX_STOP); - else - data = i2c_op(pd, OP_RX_STOP_DATA); - } else if (real_pos >= 0) { - data = i2c_op(pd, OP_RX); - } - - if (real_pos >= 0) - pd->msg->buf[real_pos] = data; - } while (0); + if (real_pos < 0) + i2c_op(pd, OP_RX_STOP); + else + data = i2c_op(pd, OP_RX_STOP_DATA); + } else if (real_pos >= 0) { + data = i2c_op(pd, OP_RX); + } + if (real_pos >= 0) + pd->msg->buf[real_pos] = data; + done: pd->pos++; return pd->pos == (pd->msg->len + 2); } From 9d899ed4009976c5cb26196afa6d28b82d78010b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 16 Jan 2019 22:05:55 +0100 Subject: [PATCH 0569/4277] i2c: sh_mobile: update copyright and comments Update copyright years and add Renesas to it. Add/update comments to make driver easier to understand. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index dd4df961d8eb..554bd79e0891 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -2,8 +2,7 @@ /* * SuperH Mobile I2C Controller * - * Copyright (C) 2014 Wolfram Sang - * + * Copyright (C) 2014-19 Wolfram Sang * Copyright (C) 2008 Magnus Damm * * Portions of the code based on out-of-tree driver i2c-sh7343.c @@ -373,6 +372,7 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) unsigned char data; int real_pos; + /* switch from TX (address) to RX (data) adds two interrupts */ real_pos = pd->pos - 2; if (pd->pos == -1) { @@ -717,8 +717,7 @@ static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = { }; /* - * r8a7740 chip has lasting errata on I2C I/O pad reset. - * this is work-around for it. + * r8a7740 has an errata regarding I2C I/O pad reset needing this workaround. */ static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd) { From 44783efdfed190088066d4c3470766c28da38a21 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Wed, 16 Jan 2019 11:39:43 -0800 Subject: [PATCH 0570/4277] i2c: aspeed: Remove hard-coded bus timeout value setting This commit removes hard-coded bus timeout value setting so that it can be set by i2c-core-base. Signed-off-by: Jae Hyun Yoo Reviewed-by: Joel Stanley Reviewed-by: Brendan Higgins Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-aspeed.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 8dc9161ced38..833b6b6a4c7e 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -930,7 +930,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) init_completion(&bus->cmd_complete); bus->adap.owner = THIS_MODULE; bus->adap.retries = 0; - bus->adap.timeout = 5 * HZ; bus->adap.algo = &aspeed_i2c_algo; bus->adap.dev.parent = &pdev->dev; bus->adap.dev.of_node = pdev->dev.of_node; From ddf06b753a8573eb0323d91efeaffe397f7b673d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 8 Jan 2019 10:21:24 +0800 Subject: [PATCH 0571/4277] f2fs: fix to trigger fsck if dirent.name_len is zero While traversing dirents in f2fs_fill_dentries(), if bitmap is valid, filename length should not be zero, otherwise, directory structure consistency could be corrupted, in this case, let's print related info and set SBI_NEED_FSCK to trigger fsck for repairing. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 50d0d36280fa..926166528cd4 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -800,6 +800,10 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, if (de->name_len == 0) { bit_pos++; ctx->pos = start_pos + bit_pos; + printk_ratelimited( + "%s, invalid namelen(0), ino:%u, run fsck to fix.", + KERN_WARNING, le32_to_cpu(de->ino)); + set_sbi_flag(sbi, SBI_NEED_FSCK); continue; } From 720db068634c91553a8e1d9a0fcd8c7050e06d2b Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Mon, 7 Jan 2019 15:02:34 +0800 Subject: [PATCH 0572/4277] f2fs: check if file namelen exceeds max value Dentry bitmap is not enough to detect incorrect dentries. So this patch also checks the namelen value of a dentry. Signed-off-by: Gong Chen Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 926166528cd4..ba7535399d95 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -814,7 +814,8 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, /* check memory boundary before moving forward */ bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); - if (unlikely(bit_pos > d->max)) { + if (unlikely(bit_pos > d->max || + le16_to_cpu(de->name_len) > F2FS_NAME_LEN)) { f2fs_msg(sbi->sb, KERN_WARNING, "%s: corrupted namelen=%d, run fsck to fix.", __func__, le16_to_cpu(de->name_len)); From 2f84babfe5eb270e13d54afebc26b6d19f49cba2 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Mon, 14 Jan 2019 22:05:14 +0800 Subject: [PATCH 0573/4277] f2fs: add brackets for macros Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7df41cd1eb35..f71b44f21ffb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -254,7 +254,7 @@ struct discard_entry { /* max discard pend list number */ #define MAX_PLIST_NUM 512 #define plist_idx(blk_num) ((blk_num) >= MAX_PLIST_NUM ? \ - (MAX_PLIST_NUM - 1) : (blk_num - 1)) + (MAX_PLIST_NUM - 1) : ((blk_num) - 1)) enum { D_PREP, /* initial */ @@ -2763,9 +2763,9 @@ static inline int get_inline_xattr_addrs(struct inode *inode) #define F2FS_OLD_ATTRIBUTE_SIZE (offsetof(struct f2fs_inode, i_addr)) #define F2FS_FITS_IN_INODE(f2fs_inode, extra_isize, field) \ - ((offsetof(typeof(*f2fs_inode), field) + \ + ((offsetof(typeof(*(f2fs_inode)), field) + \ sizeof((f2fs_inode)->field)) \ - <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize)) \ + <= (F2FS_OLD_ATTRIBUTE_SIZE + (extra_isize))) \ static inline void f2fs_reset_iostat(struct f2fs_sb_info *sbi) { @@ -2794,8 +2794,8 @@ static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi, #define __is_large_section(sbi) ((sbi)->segs_per_sec > 1) -#define __is_meta_io(fio) (PAGE_TYPE_OF_BIO(fio->type) == META && \ - (!is_read_io(fio->op) || fio->is_meta)) +#define __is_meta_io(fio) (PAGE_TYPE_OF_BIO((fio)->type) == META && \ + (!is_read_io((fio)->op) || (fio)->is_meta)) bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type); From ac92985864e187a1735502f6a02f54eaa655b2aa Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Tue, 15 Jan 2019 20:02:15 +0000 Subject: [PATCH 0574/4277] f2fs: UBSAN: set boolean value iostat_enable correctly When setting /sys/fs/f2fs//iostat_enable with non-bool value, UBSAN reports the following warning. [ 7562.295484] ================================================================================ [ 7562.296531] UBSAN: Undefined behaviour in fs/f2fs/f2fs.h:2776:10 [ 7562.297651] load of value 64 is not a valid value for type '_Bool' [ 7562.298642] CPU: 1 PID: 7487 Comm: dd Not tainted 4.20.0-rc4+ #79 [ 7562.298653] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 7562.298662] Call Trace: [ 7562.298760] dump_stack+0x46/0x5b [ 7562.298811] ubsan_epilogue+0x9/0x40 [ 7562.298830] __ubsan_handle_load_invalid_value+0x72/0x90 [ 7562.298863] f2fs_file_write_iter+0x29f/0x3f0 [ 7562.298905] __vfs_write+0x115/0x160 [ 7562.298922] vfs_write+0xa7/0x190 [ 7562.298934] ksys_write+0x50/0xc0 [ 7562.298973] do_syscall_64+0x4a/0xe0 [ 7562.298992] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 7562.299001] RIP: 0033:0x7fa45ec19c00 [ 7562.299004] Code: 73 01 c3 48 8b 0d 88 92 2c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 83 3d dd eb 2c 00 00 75 10 b8 01 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 ce 8f 01 00 48 89 04 24 [ 7562.299044] RSP: 002b:00007ffca52b49e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 7562.299052] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fa45ec19c00 [ 7562.299059] RDX: 0000000000000400 RSI: 000000000093f000 RDI: 0000000000000001 [ 7562.299065] RBP: 000000000093f000 R08: 0000000000000004 R09: 0000000000000000 [ 7562.299071] R10: 00007ffca52b47b0 R11: 0000000000000246 R12: 0000000000000400 [ 7562.299077] R13: 000000000093f000 R14: 000000000093f400 R15: 0000000000000000 [ 7562.299091] ================================================================================ So, if iostat_enable is enabled, set its value as true. Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 02d4012a9183..5b83e1d66cb3 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -280,10 +280,16 @@ out: return count; } - *ui = t; - if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0) - f2fs_reset_iostat(sbi); + if (!strcmp(a->attr.name, "iostat_enable")) { + sbi->iostat_enable = !!t; + if (!sbi->iostat_enable) + f2fs_reset_iostat(sbi); + return count; + } + + *ui = (unsigned int)t; + return count; } From f9aa52a8cbe09fe25244d59c29660bbe635df613 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 16 Jan 2019 09:51:28 +0800 Subject: [PATCH 0575/4277] f2fs: fix to initialize variable to avoid UBSAN/smatch warning As Dan Carpenter as below: The patch df634f444ee9: "f2fs: use rb_*_cached friends" from Oct 4, 2018, leads to the following static checker warning: fs/f2fs/extent_cache.c:606 f2fs_update_extent_tree_range() error: uninitialized symbol 'leftmost'. And also Eric Biggers, and Kyungtae Kim reported, there is an UBSAN warning described as below: We report a bug in linux-4.20.2: "UBSAN: Undefined behaviour in fs/f2fs/extent_cache.c" kernel config: https://kt0755.github.io/etc/config_v4.20_stable repro: https://kt0755.github.io/etc/repro.4a3e7.c (f2fs is mounted on /mnt/f2fs/) This arose in f2fs_update_extent_tree_range (fs/f2fs/extent_cache.c:605). It seems that, for some reason, its last argument became "24" although that was supposed to be bool type. ========================================= UBSAN: Undefined behaviour in fs/f2fs/extent_cache.c:605:4 load of value 24 is not a valid value for type '_Bool' CPU: 0 PID: 6774 Comm: syz-executor5 Not tainted 4.20.2 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xb1/0x118 lib/dump_stack.c:113 ubsan_epilogue+0x12/0x94 lib/ubsan.c:159 __ubsan_handle_load_invalid_value+0x17a/0x1be lib/ubsan.c:457 f2fs_update_extent_tree_range+0x1d4a/0x1d50 fs/f2fs/extent_cache.c:605 f2fs_update_extent_cache+0x2b6/0x350 fs/f2fs/extent_cache.c:804 f2fs_update_data_blkaddr+0x61/0x70 fs/f2fs/data.c:656 f2fs_outplace_write_data+0x1d6/0x4b0 fs/f2fs/segment.c:3140 f2fs_convert_inline_page+0x86d/0x2060 fs/f2fs/inline.c:163 f2fs_convert_inline_inode+0x6b5/0xad0 fs/f2fs/inline.c:208 f2fs_preallocate_blocks+0x78b/0xb00 fs/f2fs/data.c:982 f2fs_file_write_iter+0x31b/0xf40 fs/f2fs/file.c:3062 call_write_iter include/linux/fs.h:1857 [inline] new_sync_write fs/read_write.c:474 [inline] __vfs_write+0x538/0x6e0 fs/read_write.c:487 vfs_write+0x1b3/0x520 fs/read_write.c:549 ksys_write+0xde/0x1c0 fs/read_write.c:598 __do_sys_write fs/read_write.c:610 [inline] __se_sys_write fs/read_write.c:607 [inline] __x64_sys_write+0x7e/0xc0 fs/read_write.c:607 do_syscall_64+0xbe/0x4f0 arch/x86/entry/common.c:290 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x4497b9 Code: e8 8c 9f 02 00 48 83 c4 18 c3 0f 1f 80 00 00 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 9b 6b fc ff c3 66 2e 0f 1f 84 00 00 00 00 RSP: 002b:00007f1ea15edc68 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 00007f1ea15ee6cc RCX: 00000000004497b9 RDX: 0000000000001000 RSI: 0000000020000140 RDI: 0000000000000013 RBP: 000000000071bea0 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00000000ffffffff R13: 000000000000bb50 R14: 00000000006f4bf0 R15: 00007f1ea15ee700 ========================================= As I checked, this uninitialized variable won't cause extent cache corruption, but in order to avoid such kind of warning of both UBSAN and smatch, fix to initialize related variable. Reported-by: Dan Carpenter Reported-by: Eric Biggers Reported-by: Kyungtae Kim Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 1cb0fcc67d2d..caf77fe8ac07 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -506,7 +506,7 @@ static void f2fs_update_extent_tree_range(struct inode *inode, unsigned int end = fofs + len; unsigned int pos = (unsigned int)fofs; bool updated = false; - bool leftmost; + bool leftmost = false; if (!et) return; From 2010987365ab3b381df20bb6f175b59551cfe67d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 10 Jan 2019 16:40:12 +0800 Subject: [PATCH 0576/4277] f2fs: fix to set sbi dirty correctly In order to record direct IO count, we add two additional type in enum count_type: F2FS_DIO_{WRITE,READ}, but those IO won't dirty filesystem metadata, so we don't need to set filesystem dirty in inc_page_count(), fix it. Fixes: 02b16d0a34a1 ("f2fs: add to account direct IO") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f71b44f21ffb..0f564883e078 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1799,13 +1799,12 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) { atomic_inc(&sbi->nr_pages[count_type]); - if (count_type == F2FS_DIRTY_DATA || count_type == F2FS_INMEM_PAGES || - count_type == F2FS_WB_CP_DATA || count_type == F2FS_WB_DATA || - count_type == F2FS_RD_DATA || count_type == F2FS_RD_NODE || - count_type == F2FS_RD_META) - return; - - set_sbi_flag(sbi, SBI_IS_DIRTY); + if (count_type == F2FS_DIRTY_DENTS || + count_type == F2FS_DIRTY_NODES || + count_type == F2FS_DIRTY_META || + count_type == F2FS_DIRTY_QDATA || + count_type == F2FS_DIRTY_IMETA) + set_sbi_flag(sbi, SBI_IS_DIRTY); } static inline void inode_inc_dirty_pages(struct inode *inode) From 84f7a9de0602704bbec774a6c7f7c8c4994bee9c Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 15 Jan 2019 16:49:57 -0800 Subject: [PATCH 0577/4277] scsi: sd: Remove a local variable This patch does not change any functionality. Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen [ bvanassche: extracted this patch from a larger patch ] Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c6ca3d915925..a60b98681f15 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1015,7 +1015,6 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) struct bio *bio = rq->bio; sector_t sector = blk_rq_pos(rq); unsigned int nr_sectors = blk_rq_sectors(rq); - unsigned int nr_bytes = blk_rq_bytes(rq); blk_status_t ret; if (sdkp->device->no_write_same) @@ -1055,7 +1054,7 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) */ rq->__data_len = sdp->sector_size; ret = scsi_init_io(cmd); - rq->__data_len = nr_bytes; + rq->__data_len = blk_rq_bytes(rq); return ret; } From c6c93fdd3451cc0de393dad891d6b0be71e50888 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 15 Jan 2019 16:49:58 -0800 Subject: [PATCH 0578/4277] scsi: sd: Be consistent about blocks vs. sectors We have had several bugs due mixing sector and logical block size terminology. In the block layer, a sector is a 512-byte unit regardless of the logical block size of the underlying device. But the term "sector" is still widely used in sd.c when referring to logical block sized units. We previously introduced helper functions such as sectors_to_logical() and logical_to_sectors() to make the distinction clear. Use these to make the code in sd.c consistent wrt. logical blocks and block layer sectors. Use "lba" to describe a logical block address and "nr_blocks" when counting logical blocks. SBC uses "TRANSFER LENGTH" to describe the latter but this term was avoided to prevent confusion with the very similar DMA transfer size (->transfersize) which is counted in bytes. Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen [ bvanassche: ported this patch from kernel v4.11 to kernel v5.0 ] Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 169 +++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index a60b98681f15..4d14208fe6db 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -817,8 +817,8 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) { struct scsi_device *sdp = cmd->device; struct request *rq = cmd->request; - u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9); - u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9); + u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); + u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); unsigned int data_len = 24; char *buf; @@ -837,8 +837,8 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) buf = page_address(rq->special_vec.bv_page); put_unaligned_be16(6 + 16, &buf[0]); put_unaligned_be16(16, &buf[2]); - put_unaligned_be64(sector, &buf[8]); - put_unaligned_be32(nr_sectors, &buf[16]); + put_unaligned_be64(lba, &buf[8]); + put_unaligned_be32(nr_blocks, &buf[16]); cmd->allowed = SD_MAX_RETRIES; cmd->transfersize = data_len; @@ -853,8 +853,8 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, { struct scsi_device *sdp = cmd->device; struct request *rq = cmd->request; - u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9); - u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9); + u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); + u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); u32 data_len = sdp->sector_size; rq->special_vec.bv_page = mempool_alloc(sd_page_pool, GFP_ATOMIC); @@ -869,8 +869,8 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, cmd->cmnd[0] = WRITE_SAME_16; if (unmap) cmd->cmnd[1] = 0x8; /* UNMAP */ - put_unaligned_be64(sector, &cmd->cmnd[2]); - put_unaligned_be32(nr_sectors, &cmd->cmnd[10]); + put_unaligned_be64(lba, &cmd->cmnd[2]); + put_unaligned_be32(nr_blocks, &cmd->cmnd[10]); cmd->allowed = SD_MAX_RETRIES; cmd->transfersize = data_len; @@ -885,8 +885,8 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, { struct scsi_device *sdp = cmd->device; struct request *rq = cmd->request; - u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9); - u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9); + u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); + u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); u32 data_len = sdp->sector_size; rq->special_vec.bv_page = mempool_alloc(sd_page_pool, GFP_ATOMIC); @@ -901,8 +901,8 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, cmd->cmnd[0] = WRITE_SAME; if (unmap) cmd->cmnd[1] = 0x8; /* UNMAP */ - put_unaligned_be32(sector, &cmd->cmnd[2]); - put_unaligned_be16(nr_sectors, &cmd->cmnd[7]); + put_unaligned_be32(lba, &cmd->cmnd[2]); + put_unaligned_be16(nr_blocks, &cmd->cmnd[7]); cmd->allowed = SD_MAX_RETRIES; cmd->transfersize = data_len; @@ -917,8 +917,8 @@ static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd) struct request *rq = cmd->request; struct scsi_device *sdp = cmd->device; struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); - u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9); - u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9); + u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); + u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); if (!(rq->cmd_flags & REQ_NOUNMAP)) { switch (sdkp->zeroing_mode) { @@ -932,7 +932,7 @@ static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd) if (sdp->no_write_same) return BLK_STS_TARGET; - if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) + if (sdkp->ws16 || lba > 0xffffffff || nr_blocks > 0xffff) return sd_setup_write_same16_cmnd(cmd, false); return sd_setup_write_same10_cmnd(cmd, false); @@ -1013,8 +1013,8 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) struct scsi_device *sdp = cmd->device; struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); struct bio *bio = rq->bio; - sector_t sector = blk_rq_pos(rq); - unsigned int nr_sectors = blk_rq_sectors(rq); + u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); + u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); blk_status_t ret; if (sdkp->device->no_write_same) @@ -1022,21 +1022,18 @@ static blk_status_t sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size); - sector >>= ilog2(sdp->sector_size) - 9; - nr_sectors >>= ilog2(sdp->sector_size) - 9; - rq->timeout = SD_WRITE_SAME_TIMEOUT; - if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) { + if (sdkp->ws16 || lba > 0xffffffff || nr_blocks > 0xffff) { cmd->cmd_len = 16; cmd->cmnd[0] = WRITE_SAME_16; - put_unaligned_be64(sector, &cmd->cmnd[2]); - put_unaligned_be32(nr_sectors, &cmd->cmnd[10]); + put_unaligned_be64(lba, &cmd->cmnd[2]); + put_unaligned_be32(nr_blocks, &cmd->cmnd[10]); } else { cmd->cmd_len = 10; cmd->cmnd[0] = WRITE_SAME; - put_unaligned_be32(sector, &cmd->cmnd[2]); - put_unaligned_be16(nr_sectors, &cmd->cmnd[7]); + put_unaligned_be32(lba, &cmd->cmnd[2]); + put_unaligned_be16(nr_blocks, &cmd->cmnd[7]); } cmd->transfersize = sdp->sector_size; @@ -1081,9 +1078,9 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) struct scsi_device *sdp = SCpnt->device; struct gendisk *disk = rq->rq_disk; struct scsi_disk *sdkp = scsi_disk(disk); - sector_t block = blk_rq_pos(rq); + sector_t lba = blk_rq_pos(rq); sector_t threshold; - unsigned int this_count = blk_rq_sectors(rq); + unsigned int nr_blocks = blk_rq_sectors(rq); unsigned int dif, dix; unsigned char protect; blk_status_t ret; @@ -1096,10 +1093,10 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt, "%s: block=%llu, count=%d\n", - __func__, (unsigned long long)block, this_count)); + __func__, (unsigned long long)lba, nr_blocks)); if (!sdp || !scsi_device_online(sdp) || - block + blk_rq_sectors(rq) > get_capacity(disk)) { + lba + blk_rq_sectors(rq) > get_capacity(disk)) { SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "Finishing %u sectors\n", blk_rq_sectors(rq))); @@ -1124,18 +1121,18 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS * (sdp->sector_size / 512); - if (unlikely(sdp->last_sector_bug && block + this_count > threshold)) { - if (block < threshold) { + if (unlikely(sdp->last_sector_bug && lba + nr_blocks > threshold)) { + if (lba < threshold) { /* Access up to the threshold but not beyond */ - this_count = threshold - block; + nr_blocks = threshold - lba; } else { /* Access only a single hardware sector */ - this_count = sdp->sector_size / 512; + nr_blocks = sdp->sector_size / 512; } } SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", - (unsigned long long)block)); + (unsigned long long)lba)); /* * If we have a 1K hardware sectorsize, prevent access to single @@ -1149,31 +1146,31 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) * for this. */ if (sdp->sector_size == 1024) { - if ((block & 1) || (blk_rq_sectors(rq) & 1)) { + if ((lba & 1) || (blk_rq_sectors(rq) & 1)) { scmd_printk(KERN_ERR, SCpnt, "Bad block number requested\n"); return BLK_STS_IOERR; } - block = block >> 1; - this_count = this_count >> 1; + lba = lba >> 1; + nr_blocks = nr_blocks >> 1; } if (sdp->sector_size == 2048) { - if ((block & 3) || (blk_rq_sectors(rq) & 3)) { + if ((lba & 3) || (blk_rq_sectors(rq) & 3)) { scmd_printk(KERN_ERR, SCpnt, "Bad block number requested\n"); return BLK_STS_IOERR; } - block = block >> 2; - this_count = this_count >> 2; + lba = lba >> 2; + nr_blocks = nr_blocks >> 2; } if (sdp->sector_size == 4096) { - if ((block & 7) || (blk_rq_sectors(rq) & 7)) { + if ((lba & 7) || (blk_rq_sectors(rq) & 7)) { scmd_printk(KERN_ERR, SCpnt, "Bad block number requested\n"); return BLK_STS_IOERR; } - block = block >> 3; - this_count = this_count >> 3; + lba = lba >> 3; + nr_blocks = nr_blocks >> 3; } if (rq_data_dir(rq) == WRITE) { SCpnt->cmnd[0] = WRITE_6; @@ -1191,7 +1188,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "%s %d/%u 512 byte blocks.\n", (rq_data_dir(rq) == WRITE) ? - "writing" : "reading", this_count, + "writing" : "reading", nr_blocks, blk_rq_sectors(rq))); dix = scsi_prot_sg_count(SCpnt); @@ -1216,54 +1213,54 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) SCpnt->cmnd[10] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); /* LBA */ - SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; - SCpnt->cmnd[13] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; - SCpnt->cmnd[14] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; - SCpnt->cmnd[15] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0; - SCpnt->cmnd[16] = (unsigned char) (block >> 24) & 0xff; - SCpnt->cmnd[17] = (unsigned char) (block >> 16) & 0xff; - SCpnt->cmnd[18] = (unsigned char) (block >> 8) & 0xff; - SCpnt->cmnd[19] = (unsigned char) block & 0xff; + SCpnt->cmnd[12] = sizeof(lba) > 4 ? (unsigned char) (lba >> 56) & 0xff : 0; + SCpnt->cmnd[13] = sizeof(lba) > 4 ? (unsigned char) (lba >> 48) & 0xff : 0; + SCpnt->cmnd[14] = sizeof(lba) > 4 ? (unsigned char) (lba >> 40) & 0xff : 0; + SCpnt->cmnd[15] = sizeof(lba) > 4 ? (unsigned char) (lba >> 32) & 0xff : 0; + SCpnt->cmnd[16] = (unsigned char) (lba >> 24) & 0xff; + SCpnt->cmnd[17] = (unsigned char) (lba >> 16) & 0xff; + SCpnt->cmnd[18] = (unsigned char) (lba >> 8) & 0xff; + SCpnt->cmnd[19] = (unsigned char) lba & 0xff; /* Expected Indirect LBA */ - SCpnt->cmnd[20] = (unsigned char) (block >> 24) & 0xff; - SCpnt->cmnd[21] = (unsigned char) (block >> 16) & 0xff; - SCpnt->cmnd[22] = (unsigned char) (block >> 8) & 0xff; - SCpnt->cmnd[23] = (unsigned char) block & 0xff; + SCpnt->cmnd[20] = (unsigned char) (lba >> 24) & 0xff; + SCpnt->cmnd[21] = (unsigned char) (lba >> 16) & 0xff; + SCpnt->cmnd[22] = (unsigned char) (lba >> 8) & 0xff; + SCpnt->cmnd[23] = (unsigned char) lba & 0xff; /* Transfer length */ - SCpnt->cmnd[28] = (unsigned char) (this_count >> 24) & 0xff; - SCpnt->cmnd[29] = (unsigned char) (this_count >> 16) & 0xff; - SCpnt->cmnd[30] = (unsigned char) (this_count >> 8) & 0xff; - SCpnt->cmnd[31] = (unsigned char) this_count & 0xff; - } else if (sdp->use_16_for_rw || (this_count > 0xffff)) { + SCpnt->cmnd[28] = (unsigned char) (nr_blocks >> 24) & 0xff; + SCpnt->cmnd[29] = (unsigned char) (nr_blocks >> 16) & 0xff; + SCpnt->cmnd[30] = (unsigned char) (nr_blocks >> 8) & 0xff; + SCpnt->cmnd[31] = (unsigned char) nr_blocks & 0xff; + } else if (sdp->use_16_for_rw || (nr_blocks > 0xffff)) { SCpnt->cmnd[0] += READ_16 - READ_6; SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); - SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; - SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; - SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; - SCpnt->cmnd[5] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0; - SCpnt->cmnd[6] = (unsigned char) (block >> 24) & 0xff; - SCpnt->cmnd[7] = (unsigned char) (block >> 16) & 0xff; - SCpnt->cmnd[8] = (unsigned char) (block >> 8) & 0xff; - SCpnt->cmnd[9] = (unsigned char) block & 0xff; - SCpnt->cmnd[10] = (unsigned char) (this_count >> 24) & 0xff; - SCpnt->cmnd[11] = (unsigned char) (this_count >> 16) & 0xff; - SCpnt->cmnd[12] = (unsigned char) (this_count >> 8) & 0xff; - SCpnt->cmnd[13] = (unsigned char) this_count & 0xff; + SCpnt->cmnd[2] = sizeof(lba) > 4 ? (unsigned char) (lba >> 56) & 0xff : 0; + SCpnt->cmnd[3] = sizeof(lba) > 4 ? (unsigned char) (lba >> 48) & 0xff : 0; + SCpnt->cmnd[4] = sizeof(lba) > 4 ? (unsigned char) (lba >> 40) & 0xff : 0; + SCpnt->cmnd[5] = sizeof(lba) > 4 ? (unsigned char) (lba >> 32) & 0xff : 0; + SCpnt->cmnd[6] = (unsigned char) (lba >> 24) & 0xff; + SCpnt->cmnd[7] = (unsigned char) (lba >> 16) & 0xff; + SCpnt->cmnd[8] = (unsigned char) (lba >> 8) & 0xff; + SCpnt->cmnd[9] = (unsigned char) lba & 0xff; + SCpnt->cmnd[10] = (unsigned char) (nr_blocks >> 24) & 0xff; + SCpnt->cmnd[11] = (unsigned char) (nr_blocks >> 16) & 0xff; + SCpnt->cmnd[12] = (unsigned char) (nr_blocks >> 8) & 0xff; + SCpnt->cmnd[13] = (unsigned char) nr_blocks & 0xff; SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0; - } else if ((this_count > 0xff) || (block > 0x1fffff) || + } else if ((nr_blocks > 0xff) || (lba > 0x1fffff) || scsi_device_protection(SCpnt->device) || SCpnt->device->use_10_for_rw) { SCpnt->cmnd[0] += READ_10 - READ_6; SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); - SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; - SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff; - SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff; - SCpnt->cmnd[5] = (unsigned char) block & 0xff; + SCpnt->cmnd[2] = (unsigned char) (lba >> 24) & 0xff; + SCpnt->cmnd[3] = (unsigned char) (lba >> 16) & 0xff; + SCpnt->cmnd[4] = (unsigned char) (lba >> 8) & 0xff; + SCpnt->cmnd[5] = (unsigned char) lba & 0xff; SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0; - SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff; - SCpnt->cmnd[8] = (unsigned char) this_count & 0xff; + SCpnt->cmnd[7] = (unsigned char) (nr_blocks >> 8) & 0xff; + SCpnt->cmnd[8] = (unsigned char) nr_blocks & 0xff; } else { if (unlikely(rq->cmd_flags & REQ_FUA)) { /* @@ -1277,13 +1274,13 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) return BLK_STS_IOERR; } - SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f); - SCpnt->cmnd[2] = (unsigned char) ((block >> 8) & 0xff); - SCpnt->cmnd[3] = (unsigned char) block & 0xff; - SCpnt->cmnd[4] = (unsigned char) this_count; + SCpnt->cmnd[1] |= (unsigned char) ((lba >> 16) & 0x1f); + SCpnt->cmnd[2] = (unsigned char) ((lba >> 8) & 0xff); + SCpnt->cmnd[3] = (unsigned char) lba & 0xff; + SCpnt->cmnd[4] = (unsigned char) nr_blocks; SCpnt->cmnd[5] = 0; } - SCpnt->sdb.length = this_count * sdp->sector_size; + SCpnt->sdb.length = nr_blocks * sdp->sector_size; /* * We shouldn't disconnect in the middle of a sector, so with a dumb @@ -1291,7 +1288,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) * this many bytes between each connect / disconnect. */ SCpnt->transfersize = sdp->sector_size; - SCpnt->underflow = this_count << 9; + SCpnt->underflow = nr_blocks << 9; SCpnt->allowed = SD_MAX_RETRIES; /* From ec029758a10095c66fd24398f742c695c2e6ec2c Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 15 Jan 2019 16:49:59 -0800 Subject: [PATCH 0579/4277] scsi: sd: Simplify misaligned I/O check Avoid open coding the checks for the supported logical block sizes and use a mask to check for misaligned I/O. Use our helper functions to scale lba and block count. Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen [ bvanassche: ported this patch from kernel v4.11 to kernel v5.0 ] Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 57 ++++++++++------------------------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 4d14208fe6db..13d2137b94b1 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1078,10 +1078,11 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) struct scsi_device *sdp = SCpnt->device; struct gendisk *disk = rq->rq_disk; struct scsi_disk *sdkp = scsi_disk(disk); - sector_t lba = blk_rq_pos(rq); + sector_t lba = sectors_to_logical(sdp, blk_rq_pos(rq)); sector_t threshold; - unsigned int nr_blocks = blk_rq_sectors(rq); + unsigned int nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); unsigned int dif, dix; + unsigned int mask = logical_to_sectors(sdp, 1) - 1; unsigned char protect; blk_status_t ret; @@ -1115,63 +1116,29 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) } /* - * Some SD card readers can't handle multi-sector accesses which touch - * the last one or two hardware sectors. Split accesses as needed. + * Some SD card readers can't handle accesses which touch the + * last one or two logical blocks. Split accesses as needed. */ - threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS * - (sdp->sector_size / 512); + threshold = sdkp->capacity - SD_LAST_BUGGY_SECTORS; if (unlikely(sdp->last_sector_bug && lba + nr_blocks > threshold)) { if (lba < threshold) { /* Access up to the threshold but not beyond */ nr_blocks = threshold - lba; } else { - /* Access only a single hardware sector */ - nr_blocks = sdp->sector_size / 512; + /* Access only a single logical block */ + nr_blocks = 1; } } SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", (unsigned long long)lba)); - /* - * If we have a 1K hardware sectorsize, prevent access to single - * 512 byte sectors. In theory we could handle this - in fact - * the scsi cdrom driver must be able to handle this because - * we typically use 1K blocksizes, and cdroms typically have - * 2K hardware sectorsizes. Of course, things are simpler - * with the cdrom, since it is read-only. For performance - * reasons, the filesystems should be able to handle this - * and not force the scsi disk driver to use bounce buffers - * for this. - */ - if (sdp->sector_size == 1024) { - if ((lba & 1) || (blk_rq_sectors(rq) & 1)) { - scmd_printk(KERN_ERR, SCpnt, - "Bad block number requested\n"); - return BLK_STS_IOERR; - } - lba = lba >> 1; - nr_blocks = nr_blocks >> 1; - } - if (sdp->sector_size == 2048) { - if ((lba & 3) || (blk_rq_sectors(rq) & 3)) { - scmd_printk(KERN_ERR, SCpnt, - "Bad block number requested\n"); - return BLK_STS_IOERR; - } - lba = lba >> 2; - nr_blocks = nr_blocks >> 2; - } - if (sdp->sector_size == 4096) { - if ((lba & 7) || (blk_rq_sectors(rq) & 7)) { - scmd_printk(KERN_ERR, SCpnt, - "Bad block number requested\n"); - return BLK_STS_IOERR; - } - lba = lba >> 3; - nr_blocks = nr_blocks >> 3; + if ((blk_rq_pos(rq) & mask) || (blk_rq_sectors(rq) & mask)) { + scmd_printk(KERN_ERR, SCpnt, "request not aligned to the logical block size\n"); + return BLK_STS_IOERR; } + if (rq_data_dir(rq) == WRITE) { SCpnt->cmnd[0] = WRITE_6; From 78a02f49d0eb978b5eb85ffdf5f11582ac83c754 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 15 Jan 2019 16:50:00 -0800 Subject: [PATCH 0580/4277] scsi: sd: Create helper functions for read/write commands Create a helper function for each of the 6, 10, 16 and 32-byte READ/WRITE variants and use those when setting up reads and writes. Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen [ bvanassche: ported this patch from kernel v4.11 to kernel v5.0 and made function names shorter. ] Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 167 +++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 75 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 13d2137b94b1..53093a34b1fc 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1072,6 +1072,83 @@ static blk_status_t sd_setup_flush_cmnd(struct scsi_cmnd *cmd) return BLK_STS_OK; } +static blk_status_t sd_setup_rw32_cmnd(struct scsi_cmnd *cmd, bool write, + sector_t lba, unsigned int nr_blocks, + unsigned char flags) +{ + cmd->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); + if (unlikely(cmd->cmnd == NULL)) + return BLK_STS_RESOURCE; + + cmd->cmd_len = SD_EXT_CDB_SIZE; + memset(cmd->cmnd, 0, cmd->cmd_len); + + cmd->cmnd[0] = VARIABLE_LENGTH_CMD; + cmd->cmnd[7] = 0x18; /* Additional CDB len */ + cmd->cmnd[9] = write ? WRITE_32 : READ_32; + cmd->cmnd[10] = flags; + put_unaligned_be64(lba, &cmd->cmnd[12]); + put_unaligned_be32(lba, &cmd->cmnd[20]); /* Expected Indirect LBA */ + put_unaligned_be32(nr_blocks, &cmd->cmnd[28]); + + return BLK_STS_OK; +} + +static blk_status_t sd_setup_rw16_cmnd(struct scsi_cmnd *cmd, bool write, + sector_t lba, unsigned int nr_blocks, + unsigned char flags) +{ + cmd->cmd_len = 16; + cmd->cmnd[0] = write ? WRITE_16 : READ_16; + cmd->cmnd[1] = flags; + cmd->cmnd[14] = 0; + cmd->cmnd[15] = 0; + put_unaligned_be64(lba, &cmd->cmnd[2]); + put_unaligned_be32(nr_blocks, &cmd->cmnd[10]); + + return BLK_STS_OK; +} + +static blk_status_t sd_setup_rw10_cmnd(struct scsi_cmnd *cmd, bool write, + sector_t lba, unsigned int nr_blocks, + unsigned char flags) +{ + cmd->cmd_len = 10; + cmd->cmnd[0] = write ? WRITE_10 : READ_10; + cmd->cmnd[1] = flags; + cmd->cmnd[6] = 0; + cmd->cmnd[9] = 0; + put_unaligned_be32(lba, &cmd->cmnd[2]); + put_unaligned_be16(nr_blocks, &cmd->cmnd[7]); + + return BLK_STS_OK; +} + +static blk_status_t sd_setup_rw6_cmnd(struct scsi_cmnd *cmd, bool write, + sector_t lba, unsigned int nr_blocks, + unsigned char flags) +{ + if (unlikely(flags & 0x8)) { + /* + * This happens only if this drive failed 10byte rw + * command with ILLEGAL_REQUEST during operation and + * thus turned off use_10_for_rw. + */ + scmd_printk(KERN_ERR, cmd, "FUA write on READ/WRITE(6) drive\n"); + return BLK_STS_IOERR; + } + + cmd->cmd_len = 6; + cmd->cmnd[0] = write ? WRITE_6 : READ_6; + cmd->cmnd[1] = (lba >> 16) & 0x1f; + cmd->cmnd[2] = (lba >> 8) & 0xff; + cmd->cmnd[3] = lba & 0xff; + cmd->cmnd[4] = nr_blocks; + cmd->cmnd[5] = 0; + + return BLK_STS_OK; +} + static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) { struct request *rq = SCpnt->request; @@ -1083,7 +1160,8 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) unsigned int nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); unsigned int dif, dix; unsigned int mask = logical_to_sectors(sdp, 1) - 1; - unsigned char protect; + bool write = rq_data_dir(rq) == WRITE; + unsigned char protect, fua; blk_status_t ret; ret = scsi_init_io(SCpnt); @@ -1158,6 +1236,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) "writing" : "reading", nr_blocks, blk_rq_sectors(rq))); + fua = rq->cmd_flags & REQ_FUA ? 0x8 : 0; dix = scsi_prot_sg_count(SCpnt); dif = scsi_host_dif_capable(SCpnt->device->host, sdkp->protection_type); @@ -1167,86 +1246,24 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) protect = 0; if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) { - SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); - - if (unlikely(!SCpnt->cmnd)) - return BLK_STS_RESOURCE; - - SCpnt->cmd_len = SD_EXT_CDB_SIZE; - memset(SCpnt->cmnd, 0, SCpnt->cmd_len); - SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD; - SCpnt->cmnd[7] = 0x18; - SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32; - SCpnt->cmnd[10] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); - - /* LBA */ - SCpnt->cmnd[12] = sizeof(lba) > 4 ? (unsigned char) (lba >> 56) & 0xff : 0; - SCpnt->cmnd[13] = sizeof(lba) > 4 ? (unsigned char) (lba >> 48) & 0xff : 0; - SCpnt->cmnd[14] = sizeof(lba) > 4 ? (unsigned char) (lba >> 40) & 0xff : 0; - SCpnt->cmnd[15] = sizeof(lba) > 4 ? (unsigned char) (lba >> 32) & 0xff : 0; - SCpnt->cmnd[16] = (unsigned char) (lba >> 24) & 0xff; - SCpnt->cmnd[17] = (unsigned char) (lba >> 16) & 0xff; - SCpnt->cmnd[18] = (unsigned char) (lba >> 8) & 0xff; - SCpnt->cmnd[19] = (unsigned char) lba & 0xff; - - /* Expected Indirect LBA */ - SCpnt->cmnd[20] = (unsigned char) (lba >> 24) & 0xff; - SCpnt->cmnd[21] = (unsigned char) (lba >> 16) & 0xff; - SCpnt->cmnd[22] = (unsigned char) (lba >> 8) & 0xff; - SCpnt->cmnd[23] = (unsigned char) lba & 0xff; - - /* Transfer length */ - SCpnt->cmnd[28] = (unsigned char) (nr_blocks >> 24) & 0xff; - SCpnt->cmnd[29] = (unsigned char) (nr_blocks >> 16) & 0xff; - SCpnt->cmnd[30] = (unsigned char) (nr_blocks >> 8) & 0xff; - SCpnt->cmnd[31] = (unsigned char) nr_blocks & 0xff; + ret = sd_setup_rw32_cmnd(SCpnt, write, lba, nr_blocks, + protect | fua); } else if (sdp->use_16_for_rw || (nr_blocks > 0xffff)) { - SCpnt->cmnd[0] += READ_16 - READ_6; - SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); - SCpnt->cmnd[2] = sizeof(lba) > 4 ? (unsigned char) (lba >> 56) & 0xff : 0; - SCpnt->cmnd[3] = sizeof(lba) > 4 ? (unsigned char) (lba >> 48) & 0xff : 0; - SCpnt->cmnd[4] = sizeof(lba) > 4 ? (unsigned char) (lba >> 40) & 0xff : 0; - SCpnt->cmnd[5] = sizeof(lba) > 4 ? (unsigned char) (lba >> 32) & 0xff : 0; - SCpnt->cmnd[6] = (unsigned char) (lba >> 24) & 0xff; - SCpnt->cmnd[7] = (unsigned char) (lba >> 16) & 0xff; - SCpnt->cmnd[8] = (unsigned char) (lba >> 8) & 0xff; - SCpnt->cmnd[9] = (unsigned char) lba & 0xff; - SCpnt->cmnd[10] = (unsigned char) (nr_blocks >> 24) & 0xff; - SCpnt->cmnd[11] = (unsigned char) (nr_blocks >> 16) & 0xff; - SCpnt->cmnd[12] = (unsigned char) (nr_blocks >> 8) & 0xff; - SCpnt->cmnd[13] = (unsigned char) nr_blocks & 0xff; - SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0; + ret = sd_setup_rw16_cmnd(SCpnt, write, lba, nr_blocks, + protect | fua); } else if ((nr_blocks > 0xff) || (lba > 0x1fffff) || scsi_device_protection(SCpnt->device) || SCpnt->device->use_10_for_rw) { - SCpnt->cmnd[0] += READ_10 - READ_6; - SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); - SCpnt->cmnd[2] = (unsigned char) (lba >> 24) & 0xff; - SCpnt->cmnd[3] = (unsigned char) (lba >> 16) & 0xff; - SCpnt->cmnd[4] = (unsigned char) (lba >> 8) & 0xff; - SCpnt->cmnd[5] = (unsigned char) lba & 0xff; - SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0; - SCpnt->cmnd[7] = (unsigned char) (nr_blocks >> 8) & 0xff; - SCpnt->cmnd[8] = (unsigned char) nr_blocks & 0xff; + ret = sd_setup_rw10_cmnd(SCpnt, write, lba, nr_blocks, + protect | fua); } else { - if (unlikely(rq->cmd_flags & REQ_FUA)) { - /* - * This happens only if this drive failed - * 10byte rw command with ILLEGAL_REQUEST - * during operation and thus turned off - * use_10_for_rw. - */ - scmd_printk(KERN_ERR, SCpnt, - "FUA write on READ/WRITE(6) drive\n"); - return BLK_STS_IOERR; - } - - SCpnt->cmnd[1] |= (unsigned char) ((lba >> 16) & 0x1f); - SCpnt->cmnd[2] = (unsigned char) ((lba >> 8) & 0xff); - SCpnt->cmnd[3] = (unsigned char) lba & 0xff; - SCpnt->cmnd[4] = (unsigned char) nr_blocks; - SCpnt->cmnd[5] = 0; + ret = sd_setup_rw6_cmnd(SCpnt, write, lba, nr_blocks, + protect | fua); } + + if (unlikely(ret != BLK_STS_OK)) + return ret; + SCpnt->sdb.length = nr_blocks * sdp->sector_size; /* From e249e42d277ef4993230b193880fbe9a307084eb Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 15 Jan 2019 16:50:01 -0800 Subject: [PATCH 0581/4277] scsi: sd: Clean up sd_setup_read_write_cmnd() Rework sd_setup_read_write_cmnd() so it becomes more readable. Put all the sanity checking at the head of the function and sanitize the logged error messages. Move the legacy SCSI logging calls to the end of the functions and reduce conditional nesting. Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen [ bvanassche: ported this patch from kernel v4.11 to kernel v5.0 ] Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 80 +++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 53093a34b1fc..e2bd39670362 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1153,12 +1153,11 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) { struct request *rq = SCpnt->request; struct scsi_device *sdp = SCpnt->device; - struct gendisk *disk = rq->rq_disk; - struct scsi_disk *sdkp = scsi_disk(disk); + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); sector_t lba = sectors_to_logical(sdp, blk_rq_pos(rq)); sector_t threshold; unsigned int nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); - unsigned int dif, dix; + bool dif, dix; unsigned int mask = logical_to_sectors(sdp, 1) - 1; bool write = rq_data_dir(rq) == WRITE; unsigned char protect, fua; @@ -1167,29 +1166,21 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) ret = scsi_init_io(SCpnt); if (ret != BLK_STS_OK) return ret; + WARN_ON_ONCE(SCpnt != rq->special); - SCSI_LOG_HLQUEUE(1, - scmd_printk(KERN_INFO, SCpnt, - "%s: block=%llu, count=%d\n", - __func__, (unsigned long long)lba, nr_blocks)); - - if (!sdp || !scsi_device_online(sdp) || - lba + blk_rq_sectors(rq) > get_capacity(disk)) { - SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, - "Finishing %u sectors\n", - blk_rq_sectors(rq))); - SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, - "Retry with 0x%p\n", SCpnt)); + if (!scsi_device_online(sdp) || sdp->changed) { + scmd_printk(KERN_ERR, SCpnt, "device offline or changed\n"); return BLK_STS_IOERR; } - if (sdp->changed) { - /* - * quietly refuse to do anything to a changed disc until - * the changed bit has been reset - */ - /* printk("SCSI disk has been changed or is not present. Prohibiting further I/O.\n"); */ + if (blk_rq_pos(rq) + blk_rq_sectors(rq) > get_capacity(rq->rq_disk)) { + scmd_printk(KERN_ERR, SCpnt, "access beyond end of device\n"); + return BLK_STS_IOERR; + } + + if ((blk_rq_pos(rq) & mask) || (blk_rq_sectors(rq) & mask)) { + scmd_printk(KERN_ERR, SCpnt, "request not aligned to the logical block size\n"); return BLK_STS_IOERR; } @@ -1209,37 +1200,13 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) } } - SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", - (unsigned long long)lba)); - - if ((blk_rq_pos(rq) & mask) || (blk_rq_sectors(rq) & mask)) { - scmd_printk(KERN_ERR, SCpnt, "request not aligned to the logical block size\n"); - return BLK_STS_IOERR; - } - - if (rq_data_dir(rq) == WRITE) { - SCpnt->cmnd[0] = WRITE_6; - - if (blk_integrity_rq(rq)) - t10_pi_prepare(SCpnt->request, sdkp->protection_type); - - } else if (rq_data_dir(rq) == READ) { - SCpnt->cmnd[0] = READ_6; - } else { - scmd_printk(KERN_ERR, SCpnt, "Unknown command %d\n", req_op(rq)); - return BLK_STS_IOERR; - } - - SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, - "%s %d/%u 512 byte blocks.\n", - (rq_data_dir(rq) == WRITE) ? - "writing" : "reading", nr_blocks, - blk_rq_sectors(rq))); - fua = rq->cmd_flags & REQ_FUA ? 0x8 : 0; dix = scsi_prot_sg_count(SCpnt); dif = scsi_host_dif_capable(SCpnt->device->host, sdkp->protection_type); + if (write && dix) + t10_pi_prepare(SCpnt->request, sdkp->protection_type); + if (dif || dix) protect = sd_setup_protect_cmnd(SCpnt, dix, dif); else @@ -1252,8 +1219,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) ret = sd_setup_rw16_cmnd(SCpnt, write, lba, nr_blocks, protect | fua); } else if ((nr_blocks > 0xff) || (lba > 0x1fffff) || - scsi_device_protection(SCpnt->device) || - SCpnt->device->use_10_for_rw) { + sdp->use_10_for_rw || protect) { ret = sd_setup_rw10_cmnd(SCpnt, write, lba, nr_blocks, protect | fua); } else { @@ -1264,8 +1230,6 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) if (unlikely(ret != BLK_STS_OK)) return ret; - SCpnt->sdb.length = nr_blocks * sdp->sector_size; - /* * We shouldn't disconnect in the middle of a sector, so with a dumb * host adapter, it's safe to assume that we can at least transfer @@ -1274,6 +1238,18 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) SCpnt->transfersize = sdp->sector_size; SCpnt->underflow = nr_blocks << 9; SCpnt->allowed = SD_MAX_RETRIES; + SCpnt->sdb.length = nr_blocks * sdp->sector_size; + + SCSI_LOG_HLQUEUE(1, + scmd_printk(KERN_INFO, SCpnt, + "%s: block=%llu, count=%d\n", __func__, + (unsigned long long)blk_rq_pos(rq), + blk_rq_sectors(rq))); + SCSI_LOG_HLQUEUE(2, + scmd_printk(KERN_INFO, SCpnt, + "%s %d/%u 512 byte blocks.\n", + write ? "writing" : "reading", nr_blocks, + blk_rq_sectors(rq))); /* * This indicates that the command is ready from our end to be From cf64e5a5f6a5f602f7ef46897da4f70968e11fbc Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 15 Jan 2019 16:50:02 -0800 Subject: [PATCH 0582/4277] scsi: sd: Rename 'SCpnt' into 'cmd' This patch makes the source code more uniform and does not change any functionality. Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen [ bvanassche: extracted this patch from a larger patch ] Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index e2bd39670362..7a1cf6c80f6a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1149,10 +1149,10 @@ static blk_status_t sd_setup_rw6_cmnd(struct scsi_cmnd *cmd, bool write, return BLK_STS_OK; } -static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) +static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd) { - struct request *rq = SCpnt->request; - struct scsi_device *sdp = SCpnt->device; + struct request *rq = cmd->request; + struct scsi_device *sdp = cmd->device; struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); sector_t lba = sectors_to_logical(sdp, blk_rq_pos(rq)); sector_t threshold; @@ -1163,24 +1163,24 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) unsigned char protect, fua; blk_status_t ret; - ret = scsi_init_io(SCpnt); + ret = scsi_init_io(cmd); if (ret != BLK_STS_OK) return ret; - WARN_ON_ONCE(SCpnt != rq->special); + WARN_ON_ONCE(cmd != rq->special); if (!scsi_device_online(sdp) || sdp->changed) { - scmd_printk(KERN_ERR, SCpnt, "device offline or changed\n"); + scmd_printk(KERN_ERR, cmd, "device offline or changed\n"); return BLK_STS_IOERR; } if (blk_rq_pos(rq) + blk_rq_sectors(rq) > get_capacity(rq->rq_disk)) { - scmd_printk(KERN_ERR, SCpnt, "access beyond end of device\n"); + scmd_printk(KERN_ERR, cmd, "access beyond end of device\n"); return BLK_STS_IOERR; } if ((blk_rq_pos(rq) & mask) || (blk_rq_sectors(rq) & mask)) { - scmd_printk(KERN_ERR, SCpnt, "request not aligned to the logical block size\n"); + scmd_printk(KERN_ERR, cmd, "request not aligned to the logical block size\n"); return BLK_STS_IOERR; } @@ -1201,29 +1201,29 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) } fua = rq->cmd_flags & REQ_FUA ? 0x8 : 0; - dix = scsi_prot_sg_count(SCpnt); - dif = scsi_host_dif_capable(SCpnt->device->host, sdkp->protection_type); + dix = scsi_prot_sg_count(cmd); + dif = scsi_host_dif_capable(cmd->device->host, sdkp->protection_type); if (write && dix) - t10_pi_prepare(SCpnt->request, sdkp->protection_type); + t10_pi_prepare(cmd->request, sdkp->protection_type); if (dif || dix) - protect = sd_setup_protect_cmnd(SCpnt, dix, dif); + protect = sd_setup_protect_cmnd(cmd, dix, dif); else protect = 0; if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) { - ret = sd_setup_rw32_cmnd(SCpnt, write, lba, nr_blocks, + ret = sd_setup_rw32_cmnd(cmd, write, lba, nr_blocks, protect | fua); } else if (sdp->use_16_for_rw || (nr_blocks > 0xffff)) { - ret = sd_setup_rw16_cmnd(SCpnt, write, lba, nr_blocks, + ret = sd_setup_rw16_cmnd(cmd, write, lba, nr_blocks, protect | fua); } else if ((nr_blocks > 0xff) || (lba > 0x1fffff) || sdp->use_10_for_rw || protect) { - ret = sd_setup_rw10_cmnd(SCpnt, write, lba, nr_blocks, + ret = sd_setup_rw10_cmnd(cmd, write, lba, nr_blocks, protect | fua); } else { - ret = sd_setup_rw6_cmnd(SCpnt, write, lba, nr_blocks, + ret = sd_setup_rw6_cmnd(cmd, write, lba, nr_blocks, protect | fua); } @@ -1235,18 +1235,18 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) * host adapter, it's safe to assume that we can at least transfer * this many bytes between each connect / disconnect. */ - SCpnt->transfersize = sdp->sector_size; - SCpnt->underflow = nr_blocks << 9; - SCpnt->allowed = SD_MAX_RETRIES; - SCpnt->sdb.length = nr_blocks * sdp->sector_size; + cmd->transfersize = sdp->sector_size; + cmd->underflow = nr_blocks << 9; + cmd->allowed = SD_MAX_RETRIES; + cmd->sdb.length = nr_blocks * sdp->sector_size; SCSI_LOG_HLQUEUE(1, - scmd_printk(KERN_INFO, SCpnt, + scmd_printk(KERN_INFO, cmd, "%s: block=%llu, count=%d\n", __func__, (unsigned long long)blk_rq_pos(rq), blk_rq_sectors(rq))); SCSI_LOG_HLQUEUE(2, - scmd_printk(KERN_INFO, SCpnt, + scmd_printk(KERN_INFO, cmd, "%s %d/%u 512 byte blocks.\n", write ? "writing" : "reading", nr_blocks, blk_rq_sectors(rq))); From cd464d838041b3a69464c0dd3938c1f912487bd3 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 15 Jan 2019 16:50:03 -0800 Subject: [PATCH 0583/4277] scsi: core: Remove an atomic instruction from the hot path From scsi_init_command(), a function called by scsi_mq_prep_fn(): /* zero out the cmd, except for the embedded scsi_request */ memset((char *)cmd + sizeof(cmd->req), 0, sizeof(*cmd) - sizeof(cmd->req) + dev->host->hostt->cmd_size); In other words, scsi_mq_prep_fn() clears scsi_cmnd.flags. Hence move the clear_bit() call into the else branch, the only branch in which this code is necessary. See also commit f1342709d18a ("scsi: Do not rely on blk-mq for double completions"). Cc: Keith Busch Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Reviewed-by: Keith Busch Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index b13cc9288ba0..00cd365fb7d2 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1712,13 +1712,13 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx, if (!scsi_host_queue_ready(q, shost, sdev)) goto out_dec_target_busy; - clear_bit(SCMD_STATE_COMPLETE, &cmd->state); if (!(req->rq_flags & RQF_DONTPREP)) { ret = scsi_mq_prep_fn(req); if (ret != BLK_STS_OK) goto out_dec_host_busy; req->rq_flags |= RQF_DONTPREP; } else { + clear_bit(SCMD_STATE_COMPLETE, &cmd->state); blk_mq_start_request(req); } From c01461a6d7b35c9ccc4621332d4db6b029ffb006 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:20 -0800 Subject: [PATCH 0584/4277] scsi: fnic: support to display 20G port speed This patch is to add fnic 20G port speed display in sysfs. [mkp: typo] Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_fcs.c | 4 ++++ drivers/scsi/fnic/fnic_io.h | 3 ++- drivers/scsi/fnic/fnic_main.c | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 844ef688fa91..9bc1ece372a7 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -70,6 +70,10 @@ void fnic_handle_link(struct work_struct *work) fc_host_speed(fnic->lport->host) = FC_PORTSPEED_10GBIT; fnic->lport->link_supported_speeds = FC_PORTSPEED_10GBIT; break; + case DCEM_PORTSPEED_20G: + fc_host_speed(fnic->lport->host) = FC_PORTSPEED_20GBIT; + fnic->lport->link_supported_speeds = FC_PORTSPEED_20GBIT; + break; case DCEM_PORTSPEED_25G: fc_host_speed(fnic->lport->host) = FC_PORTSPEED_25GBIT; fnic->lport->link_supported_speeds = FC_PORTSPEED_25GBIT; diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h index e0bc659ed71f..1cb6a68c8e4e 100644 --- a/drivers/scsi/fnic/fnic_io.h +++ b/drivers/scsi/fnic/fnic_io.h @@ -70,9 +70,10 @@ enum fnic_port_speeds { DCEM_PORTSPEED_NONE = 0, DCEM_PORTSPEED_1G = 1000, DCEM_PORTSPEED_10G = 10000, + DCEM_PORTSPEED_20G = 20000, + DCEM_PORTSPEED_25G = 25000, DCEM_PORTSPEED_40G = 40000, DCEM_PORTSPEED_4x10G = 41000, - DCEM_PORTSPEED_25G = 25000, DCEM_PORTSPEED_100G = 100000, }; #endif /* _FNIC_IO_H_ */ diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 5b3534b0deda..6aefedd1e669 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -178,6 +178,9 @@ static void fnic_get_host_speed(struct Scsi_Host *shost) case DCEM_PORTSPEED_10G: fc_host_speed(shost) = FC_PORTSPEED_10GBIT; break; + case DCEM_PORTSPEED_20G: + fc_host_speed(shost) = FC_PORTSPEED_20GBIT; + break; case DCEM_PORTSPEED_25G: fc_host_speed(shost) = FC_PORTSPEED_25GBIT; break; From 1c55262649a823a1d1c9ea37e7403ea3edd2b9c3 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:21 -0800 Subject: [PATCH 0585/4277] scsi: fnic: change fnic queue depth to 256 This patch changes the default lun queuedepth for fnic to 256. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index d094ba59ed15..5c9402c811c9 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -49,7 +49,7 @@ #define FNIC_MAX_IO_REQ 1024 /* scsi_cmnd tag map entries */ #define FNIC_DFLT_IO_REQ 256 /* Default scsi_cmnd tag map entries */ #define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */ -#define FNIC_DFLT_QUEUE_DEPTH 32 +#define FNIC_DFLT_QUEUE_DEPTH 256 #define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */ /* From 5271d1cad8187b7c33685201a3799fc3385007e9 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:22 -0800 Subject: [PATCH 0586/4277] scsi: fnic: vnic_rq_clean change BUG_ON to WARN_ON rq->ctrl not enabled when this is called is bad but not fatal and can continue. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_rq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c index fd2068f5ae16..74d2f2cd6370 100644 --- a/drivers/scsi/fnic/vnic_rq.c +++ b/drivers/scsi/fnic/vnic_rq.c @@ -171,7 +171,7 @@ void vnic_rq_clean(struct vnic_rq *rq, struct vnic_rq_buf *buf; u32 fetch_index; - BUG_ON(ioread32(&rq->ctrl->enable)); + WARN_ON(ioread32(&rq->ctrl->enable)); buf = rq->to_clean; From 68f03bd1eec6b064644e4b20dbc4a0f54a27b6b1 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:23 -0800 Subject: [PATCH 0587/4277] scsi: fnic: use fnic_lock to guard fnic->state_flags Need to use fnic_lock as well as host lock in that order to set state flags. [mkp: typos] Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_scsi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index cafbcfb85bfa..a49b0f36b2f8 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -180,20 +180,19 @@ void __fnic_set_state_flags(struct fnic *fnic, unsigned long st_flags, unsigned long clearbits) { - struct Scsi_Host *host = fnic->lport->host; - int sh_locked = spin_is_locked(host->host_lock); unsigned long flags = 0; + unsigned long host_lock_flags = 0; - if (!sh_locked) - spin_lock_irqsave(host->host_lock, flags); + spin_lock_irqsave(&fnic->fnic_lock, flags); + spin_lock_irqsave(fnic->lport->host->host_lock, host_lock_flags); if (clearbits) fnic->state_flags &= ~st_flags; else fnic->state_flags |= st_flags; - if (!sh_locked) - spin_unlock_irqrestore(host->host_lock, flags); + spin_unlock_irqrestore(fnic->lport->host->host_lock, host_lock_flags); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); return; } From d948e6383ec372da488cb553d56e100787a32ace Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:24 -0800 Subject: [PATCH 0588/4277] scsi: fnic: Add port speed stat to fnic debug stats This patch adds the current fnic port speed stat to fnic debug stats. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_fcs.c | 6 ++++++ drivers/scsi/fnic/fnic_stats.h | 1 + drivers/scsi/fnic/fnic_trace.c | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 9bc1ece372a7..911a5adc289c 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -65,6 +65,12 @@ void fnic_handle_link(struct work_struct *work) fnic->link_status = vnic_dev_link_status(fnic->vdev); fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev); + atomic64_set(&fnic->fnic_stats.misc_stats.current_port_speed, + vnic_dev_port_speed(fnic->vdev)); + shost_printk(KERN_INFO, fnic->lport->host, "Current vnic speed set to : %llu\n", + (u64)atomic64_read( + &fnic->fnic_stats.misc_stats.current_port_speed)); + switch (vnic_dev_port_speed(fnic->vdev)) { case DCEM_PORTSPEED_10G: fc_host_speed(fnic->lport->host) = FC_PORTSPEED_10GBIT; diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h index 9daa6ada6fa0..299e925921b1 100644 --- a/drivers/scsi/fnic/fnic_stats.h +++ b/drivers/scsi/fnic/fnic_stats.h @@ -113,6 +113,7 @@ struct misc_stats { atomic64_t queue_fulls; atomic64_t rport_not_ready; atomic64_t frame_errors; + atomic64_t current_port_speed; }; struct fnic_stats { diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c index bf0fd2aeb92e..8c0d4ee824db 100644 --- a/drivers/scsi/fnic/fnic_trace.c +++ b/drivers/scsi/fnic/fnic_trace.c @@ -446,6 +446,11 @@ int fnic_get_stats_data(struct stats_debug_info *debug, (u64)atomic64_read(&stats->misc_stats.rport_not_ready), (u64)atomic64_read(&stats->misc_stats.frame_errors)); + len += snprintf(debug->debug_buffer + len, buf_size - len, + "Firmware reported port seed: %llu\n", + (u64)atomic64_read( + &stats->misc_stats.current_port_speed)); + return len; } From 3567dca1bac619d8da4145602495faf9340a4688 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:25 -0800 Subject: [PATCH 0589/4277] scsi: fnic: fnic stats for max CQs processed and ISR time This change is to add fnic stats for the max number of CQs (corresponding to copy WQ) processed in a given interrupt, max time taken by the ISR. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_scsi.c | 19 +++++++++++++++++++ drivers/scsi/fnic/fnic_stats.h | 3 +++ drivers/scsi/fnic/fnic_trace.c | 6 ++++++ 3 files changed, 28 insertions(+) diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index a49b0f36b2f8..4140f07415dd 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1325,13 +1325,32 @@ int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do) unsigned int wq_work_done = 0; unsigned int i, cq_index; unsigned int cur_work_done; + struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats; + u64 start_jiffies = 0; + u64 end_jiffies = 0; + u64 delta_jiffies = 0; + u64 delta_ms = 0; for (i = 0; i < fnic->wq_copy_count; i++) { cq_index = i + fnic->raw_wq_count + fnic->rq_count; + + start_jiffies = jiffies; cur_work_done = vnic_cq_copy_service(&fnic->cq[cq_index], fnic_fcpio_cmpl_handler, copy_work_to_do); + end_jiffies = jiffies; + wq_work_done += cur_work_done; + delta_jiffies = end_jiffies - start_jiffies; + if (delta_jiffies > + (u64) atomic64_read(&misc_stats->max_isr_jiffies)) { + atomic64_set(&misc_stats->max_isr_jiffies, + delta_jiffies); + delta_ms = jiffies_to_msecs(delta_jiffies); + atomic64_set(&misc_stats->max_isr_time_ms, delta_ms); + atomic64_set(&misc_stats->corr_work_done, + cur_work_done); + } } return wq_work_done; } diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h index 299e925921b1..1b1006a882e9 100644 --- a/drivers/scsi/fnic/fnic_stats.h +++ b/drivers/scsi/fnic/fnic_stats.h @@ -97,6 +97,9 @@ struct vlan_stats { struct misc_stats { u64 last_isr_time; u64 last_ack_time; + atomic64_t max_isr_jiffies; + atomic64_t max_isr_time_ms; + atomic64_t corr_work_done; atomic64_t isr_count; atomic64_t max_cq_entries; atomic64_t ack_index_out_of_range; diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c index 8c0d4ee824db..ce32380a182e 100644 --- a/drivers/scsi/fnic/fnic_trace.c +++ b/drivers/scsi/fnic/fnic_trace.c @@ -409,6 +409,9 @@ int fnic_get_stats_data(struct stats_debug_info *debug, len += snprintf(debug->debug_buffer + len, buf_size - len, "Last ISR time: %llu (%8llu.%09lu)\n" "Last ACK time: %llu (%8llu.%09lu)\n" + "Max ISR jiffies: %llu\n" + "Max ISR time (ms) (0 denotes < 1 ms): %llu\n" + "Corr. work done: %llu\n" "Number of ISRs: %lld\n" "Maximum CQ Entries: %lld\n" "Number of ACK index out of range: %lld\n" @@ -428,6 +431,9 @@ int fnic_get_stats_data(struct stats_debug_info *debug, (s64)val1.tv_sec, val1.tv_nsec, (u64)stats->misc_stats.last_ack_time, (s64)val2.tv_sec, val2.tv_nsec, + (u64)atomic64_read(&stats->misc_stats.max_isr_jiffies), + (u64)atomic64_read(&stats->misc_stats.max_isr_time_ms), + (u64)atomic64_read(&stats->misc_stats.corr_work_done), (u64)atomic64_read(&stats->misc_stats.isr_count), (u64)atomic64_read(&stats->misc_stats.max_cq_entries), (u64)atomic64_read(&stats->misc_stats.ack_index_out_of_range), From e8bfe3e7ffc380540deeecd8565b9912d6ed87c2 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:26 -0800 Subject: [PATCH 0590/4277] scsi: fnic: Warn when calling done for IO not issued to fw The change is to print warning when scsi done is called for an IO that has not yet been issued to the fw. Also adding sc and tag to debug print when IO is cleaned up. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_scsi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 4140f07415dd..80608b53897b 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1415,8 +1415,9 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id) cleanup_scsi_cmd: sc->result = DID_TRANSPORT_DISRUPTED << 16; FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, - "%s: sc duration = %lu DID_TRANSPORT_DISRUPTED\n", - __func__, (jiffies - start_time)); + "%s: tag:0x%x : sc:0x%p duration = %lu DID_TRANSPORT_DISRUPTED\n", + __func__, sc->request->tag, sc, + (jiffies - start_time)); if (atomic64_read(&fnic->io_cmpl_skip)) atomic64_dec(&fnic->io_cmpl_skip); @@ -1425,6 +1426,11 @@ cleanup_scsi_cmd: /* Complete the command to SCSI */ if (sc->scsi_done) { + if (!(CMD_FLAGS(sc) & FNIC_IO_ISSUED)) + shost_printk(KERN_ERR, fnic->lport->host, + "Calling done for IO not issued to fw: tag:0x%x sc:0x%p\n", + sc->request->tag, sc); + FNIC_TRACE(fnic_cleanup_io, sc->device->host->host_no, i, sc, jiffies_to_msecs(jiffies - start_time), From 120dbfd94c4d5c9634d1f8fb12aa92520eee117c Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:27 -0800 Subject: [PATCH 0591/4277] scsi: fnic: delaying vnic dev enable till after req intr Doing vnic_device_enable before this could cause interrupts to happen before they are setup. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 6aefedd1e669..7a5665acf280 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -880,8 +880,6 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) fc_fabric_login(lp); - vnic_dev_enable(fnic->vdev); - err = fnic_request_intr(fnic); if (err) { shost_printk(KERN_ERR, fnic->lport->host, @@ -889,6 +887,8 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_exch_mgr; } + vnic_dev_enable(fnic->vdev); + for (i = 0; i < fnic->intr_count; i++) vnic_intr_unmask(&fnic->intr[i]); From 68e3cc02bcfc4f55a47333ee7d8fe1a9b41d72c3 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:28 -0800 Subject: [PATCH 0592/4277] scsi: fnic: RQ enable and then post descriptors Do RQ enable before posting descriptor. This is needed for later hw revisions. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_main.c | 5 ++--- drivers/scsi/fnic/vnic_devcmd.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 7a5665acf280..25e0414b6cb0 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -654,7 +654,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } err = fnic_dev_wait(fnic->vdev, vnic_dev_open, - vnic_dev_open_done, 0); + vnic_dev_open_done, CMD_OPENF_RQ_ENABLE_THEN_POST); if (err) { shost_printk(KERN_ERR, fnic->lport->host, "vNIC dev open failed, aborting.\n"); @@ -799,6 +799,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* allocate RQ buffers and post them to RQ*/ for (i = 0; i < fnic->rq_count; i++) { + vnic_rq_enable(&fnic->rq[i]); err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame); if (err) { shost_printk(KERN_ERR, fnic->lport->host, @@ -873,8 +874,6 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Enable all queues */ for (i = 0; i < fnic->raw_wq_count; i++) vnic_wq_enable(&fnic->wq[i]); - for (i = 0; i < fnic->rq_count; i++) - vnic_rq_enable(&fnic->rq[i]); for (i = 0; i < fnic->wq_copy_count; i++) vnic_wq_copy_enable(&fnic->wq_copy[i]); diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h index 3e2fcbda6aed..1a2a13bd6442 100644 --- a/drivers/scsi/fnic/vnic_devcmd.h +++ b/drivers/scsi/fnic/vnic_devcmd.h @@ -268,6 +268,8 @@ enum vnic_devcmd_cmd { /* flags for CMD_OPEN */ #define CMD_OPENF_OPROM 0x1 /* open coming from option rom */ +#define CMD_OPENF_RQ_ENABLE_THEN_POST 0x2 + /* flags for CMD_INIT */ #define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */ From 18244e9445fef9eef7f3afd3b70a42b87be0f8fd Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 14 Jan 2019 17:09:29 -0800 Subject: [PATCH 0593/4277] scsi: fnic: Impose upper limit on max. # of CQs processed per intr Impose an upper limit on the max number of CQ entries (corresponding to the copy wq) processed in an interrupt. Use module parameter to set the limit. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic.h | 2 ++ drivers/scsi/fnic/fnic_isr.c | 6 +++--- drivers/scsi/fnic/fnic_main.c | 7 ++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 5c9402c811c9..a3f20ae0add5 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -128,6 +128,7 @@ __fnic_set_state_flags(fnicp, st_flags, 1) extern unsigned int fnic_log_level; +extern unsigned int io_completions; #define FNIC_MAIN_LOGGING 0x01 #define FNIC_FCS_LOGGING 0x02 @@ -196,6 +197,7 @@ enum fnic_state { #define FNIC_WQ_MAX 1 #define FNIC_RQ_MAX 1 #define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX) +#define FNIC_DFLT_IO_COMPLETIONS 256 struct mempool; diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c index 4e3a50202e8c..da4602b63495 100644 --- a/drivers/scsi/fnic/fnic_isr.c +++ b/drivers/scsi/fnic/fnic_isr.c @@ -51,7 +51,7 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data) } if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) { - work_done += fnic_wq_copy_cmpl_handler(fnic, -1); + work_done += fnic_wq_copy_cmpl_handler(fnic, io_completions); work_done += fnic_wq_cmpl_handler(fnic, -1); work_done += fnic_rq_cmpl_handler(fnic, -1); @@ -72,7 +72,7 @@ static irqreturn_t fnic_isr_msi(int irq, void *data) fnic->fnic_stats.misc_stats.last_isr_time = jiffies; atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); - work_done += fnic_wq_copy_cmpl_handler(fnic, -1); + work_done += fnic_wq_copy_cmpl_handler(fnic, io_completions); work_done += fnic_wq_cmpl_handler(fnic, -1); work_done += fnic_rq_cmpl_handler(fnic, -1); @@ -125,7 +125,7 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data) fnic->fnic_stats.misc_stats.last_isr_time = jiffies; atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); - wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1); + wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, io_completions); vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY], wq_copy_work_done, 1 /* unmask intr */, diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 25e0414b6cb0..1433ba94ea71 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -69,6 +69,11 @@ unsigned int fnic_log_level; module_param(fnic_log_level, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels"); + +unsigned int io_completions = FNIC_DFLT_IO_COMPLETIONS; +module_param(io_completions, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(io_completions, "Max CQ entries to process at a time"); + unsigned int fnic_trace_max_pages = 16; module_param(fnic_trace_max_pages, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(fnic_trace_max_pages, "Total allocated memory pages " @@ -503,7 +508,7 @@ static int fnic_cleanup(struct fnic *fnic) } /* Clean up completed IOs and FCS frames */ - fnic_wq_copy_cmpl_handler(fnic, -1); + fnic_wq_copy_cmpl_handler(fnic, io_completions); fnic_wq_cmpl_handler(fnic, -1); fnic_rq_cmpl_handler(fnic, -1); From 5ed827a62f12c1b1ab0e4644a1fec7de3352787a Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Fri, 18 Jan 2019 14:51:40 -0800 Subject: [PATCH 0594/4277] scsi: fnic: fnic devcmd2 interface definitions This patch adds the fnic devcmd2 command structre and the command result structure definitions. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_devcmd.h | 158 +++++++++++++++++++++++++++++- drivers/scsi/fnic/vnic_resource.h | 7 ++ 2 files changed, 163 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h index 1a2a13bd6442..c5dde556dc7c 100644 --- a/drivers/scsi/fnic/vnic_devcmd.h +++ b/drivers/scsi/fnic/vnic_devcmd.h @@ -170,7 +170,8 @@ enum vnic_devcmd_cmd { /* variant of CMD_INIT, with provisioning info * (u64)a0=paddr of vnic_devcmd_provinfo - * (u32)a1=sizeof provision info */ + * (u32)a1=sizeof provision info + */ CMD_INIT_PROV_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 27), /* enable virtual link */ @@ -262,7 +263,125 @@ enum vnic_devcmd_cmd { * non-zero for resetting vlan to the default * out: (u16)a0=old default vlan */ - CMD_SET_DEFAULT_VLAN = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 46) + CMD_SET_DEFAULT_VLAN = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 46), + + /* init_prov_info2: + * Variant of CMD_INIT_PROV_INFO, where it will not try to enable + * the vnic until CMD_ENABLE2 is issued. + * (u64)a0=paddr of vnic_devcmd_provinfo + * (u32)a1=sizeof provision info + */ + CMD_INIT_PROV_INFO2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 47), + + /* enable2: + * (u32)a0=0 ==> standby + * =CMD_ENABLE2_ACTIVE ==> active + */ + CMD_ENABLE2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 48), + + /* + * cmd_status: + * Returns the status of the specified command + * Input: + * a0 = command for which status is being queried. + * Possible values are: + * CMD_SOFT_RESET + * CMD_HANG_RESET + * CMD_OPEN + * CMD_INIT + * CMD_INIT_PROV_INFO + * CMD_DEINIT + * CMD_INIT_PROV_INFO2 + * CMD_ENABLE2 + * Output: + * if status == STAT_ERROR + * a0 = ERR_ENOTSUPPORTED - status for command in a0 is + * not supported + * if status == STAT_NONE + * a0 = status of the devcmd specified in a0 as follows. + * ERR_SUCCESS - command in a0 completed successfully + * ERR_EINPROGRESS - command in a0 is still in progress + */ + CMD_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 49), + + /* + * Returns interrupt coalescing timer conversion factors. + * After calling this devcmd, ENIC driver can convert + * interrupt coalescing timer in usec into CPU cycles as follows: + * + * intr_timer_cycles = intr_timer_usec * multiplier / divisor + * + * Interrupt coalescing timer in usecs can be be converted/obtained + * from CPU cycles as follows: + * + * intr_timer_usec = intr_timer_cycles * divisor / multiplier + * + * in: none + * out: (u32)a0 = multiplier + * (u32)a1 = divisor + * (u32)a2 = maximum timer value in usec + */ + CMD_INTR_COAL_CONVERT = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 50), + + /* + * ISCSI DUMP API: + * in: (u64)a0=paddr of the param or param itself + * (u32)a1=ISCSI_CMD_xxx + */ + CMD_ISCSI_DUMP_REQ = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 51), + + /* + * ISCSI DUMP STATUS API: + * in: (u32)a0=cmd tag + * in: (u32)a1=ISCSI_CMD_xxx + * out: (u32)a0=cmd status + */ + CMD_ISCSI_DUMP_STATUS = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 52), + + /* + * Subvnic migration from MQ <--> VF. + * Enable the LIF migration from MQ to VF and vice versa. MQ and VF + * indexes are statically bound at the time of initialization. + * Based on the + * direction of migration, the resources of either MQ or the VF shall + * be attached to the LIF. + * in: (u32)a0=Direction of Migration + * 0=> Migrate to VF + * 1=> Migrate to MQ + * (u32)a1=VF index (MQ index) + */ + CMD_MIGRATE_SUBVNIC = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 53), + + /* + * Register / Deregister the notification block for MQ subvnics + * in: + * (u64)a0=paddr to notify (set paddr=0 to unset) + * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify) + * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr) + * out: + * (u32)a1 = effective size + */ + CMD_SUBVNIC_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 54), + + /* + * Set the predefined mac address as default + * in: + * (u48)a0=mac addr + */ + CMD_SET_MAC_ADDR = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 55), + + /* Update the provisioning info of the given VIF + * (u64)a0=paddr of vnic_devcmd_provinfo + * (u32)a1=sizeof provision info + */ + CMD_PROV_INFO_UPDATE = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 56), + + /* + * Initialization for the devcmd2 interface. + * in: (u64) a0=host result buffer physical address + * in: (u16) a1=number of entries in result buffer + */ + CMD_INITIALIZE_DEVCMD2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 57) }; /* flags for CMD_OPEN */ @@ -347,4 +466,39 @@ struct vnic_devcmd { u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */ }; +/* + * Version 2 of the interface. + * + * Some things are carried over, notably the vnic_devcmd_cmd enum. + */ + +/* + * Flags for vnic_devcmd2.flags + */ + +#define DEVCMD2_FNORESULT 0x1 /* Don't copy result to host */ + +#define VNIC_DEVCMD2_NARGS VNIC_DEVCMD_NARGS + +struct vnic_devcmd2 { + u16 pad; + u16 flags; + u32 cmd; /* same command #defines as original */ + u64 args[VNIC_DEVCMD2_NARGS]; +}; + +#define VNIC_DEVCMD2_NRESULTS VNIC_DEVCMD_NARGS +struct devcmd2_result { + u64 results[VNIC_DEVCMD2_NRESULTS]; + u32 pad; + u16 completed_index; /* into copy WQ */ + u8 error; /* same error codes as original */ + u8 color; /* 0 or 1 as with completion queues */ +}; + +#define DEVCMD2_RING_SIZE 32 +#define DEVCMD2_DESC_SIZE 128 + +#define DEVCMD2_RESULTS_SIZE_MAX ((1 << 16) - 1) + #endif /* _VNIC_DEVCMD_H_ */ diff --git a/drivers/scsi/fnic/vnic_resource.h b/drivers/scsi/fnic/vnic_resource.h index 2d842f79d41a..7c6163f73bd3 100644 --- a/drivers/scsi/fnic/vnic_resource.h +++ b/drivers/scsi/fnic/vnic_resource.h @@ -41,6 +41,13 @@ enum vnic_res_type { RES_TYPE_RSVD7, RES_TYPE_DEVCMD, /* Device command region */ RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */ + RES_TYPE_SUBVNIC, /* subvnic resource type */ + RES_TYPE_MQ_WQ, /* MQ Work queues */ + RES_TYPE_MQ_RQ, /* MQ Receive queues */ + RES_TYPE_MQ_CQ, /* MQ Completion queues */ + RES_TYPE_DEPRECATED1, /* Old version of devcmd 2 */ + RES_TYPE_DEPRECATED2, /* Old version of devcmd 2 */ + RES_TYPE_DEVCMD2, /* Device control region */ RES_TYPE_MAX, /* Count of resource types */ }; From 6c10cb44eaadbd13adee60d4cb8a2b8462e79fe2 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Fri, 18 Jan 2019 14:51:41 -0800 Subject: [PATCH 0595/4277] scsi: fnic: fnic devcmd2 controller definitions This patch adds the fnic devcmd2 controller definitions. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_dev.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c index 434447ea24b8..648f3fda68ab 100644 --- a/drivers/scsi/fnic/vnic_dev.c +++ b/drivers/scsi/fnic/vnic_dev.c @@ -27,6 +27,24 @@ #include "vnic_devcmd.h" #include "vnic_dev.h" #include "vnic_stats.h" +#include "vnic_wq.h" + +struct devcmd2_controller { + struct vnic_wq_ctrl *wq_ctrl; + struct vnic_dev_ring results_ring; + struct vnic_wq wq; + struct vnic_devcmd2 *cmd_ring; + struct devcmd2_result *result; + u16 next_result; + u16 result_size; + int color; +}; + +enum vnic_proxy_type { + PROXY_NONE, + PROXY_BY_BDF, + PROXY_BY_INDEX, +}; struct vnic_res { void __iomem *vaddr; @@ -48,6 +66,12 @@ struct vnic_dev { dma_addr_t stats_pa; struct vnic_devcmd_fw_info *fw_info; dma_addr_t fw_info_pa; + enum vnic_proxy_type proxy; + u32 proxy_index; + u64 args[VNIC_DEVCMD_NARGS]; + struct devcmd2_controller *devcmd2; + int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + int wait); }; #define VNIC_MAX_RES_HDR_SIZE \ @@ -119,6 +143,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev, } break; case RES_TYPE_INTR_PBA_LEGACY: + case RES_TYPE_DEVCMD2: case RES_TYPE_DEVCMD: len = count; break; @@ -229,8 +254,7 @@ void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring) } } -int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, - u64 *a0, u64 *a1, int wait) +int vnic_dev_cmd1(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, int wait) { struct vnic_devcmd __iomem *devcmd = vdev->devcmd; int delay; @@ -244,6 +268,8 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, EBUSY, /* ERR_EBUSY */ }; int err; + u64 *a0 = &vdev->args[0]; + u64 *a1 = &vdev->args[1]; status = ioread32(&devcmd->status); if (status & STAT_BUSY) { From 9d699c6280684c862afb4433d276d4fdce4349b0 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Fri, 18 Jan 2019 14:51:42 -0800 Subject: [PATCH 0596/4277] scsi: fnic: Add devcmd2 initialization helpers This patch adds the devcmd2 wq initalization and devcmd2 ring allocation helper interfaces used by devcmd2 init. [mkp: typos] Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_wq.c | 66 +++++++++++++++++++++++++++++++++++++ drivers/scsi/fnic/vnic_wq.h | 8 +++++ 2 files changed, 74 insertions(+) diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c index a414135460db..74cfc567f657 100644 --- a/drivers/scsi/fnic/vnic_wq.c +++ b/drivers/scsi/fnic/vnic_wq.c @@ -24,6 +24,26 @@ #include "vnic_dev.h" #include "vnic_wq.h" + +int vnic_wq_get_ctrl(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int index, enum vnic_res_type res_type) +{ + wq->ctrl = vnic_dev_get_res(vdev, res_type, index); + + if (!wq->ctrl) + return -EINVAL; + + return 0; +} + + +int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int desc_count, unsigned int desc_size) +{ + return vnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, desc_size); +} + + static int vnic_wq_alloc_bufs(struct vnic_wq *wq) { struct vnic_wq_buf *buf; @@ -111,6 +131,52 @@ int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, return 0; } + +int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int desc_count, unsigned int desc_size) +{ + int err; + + wq->index = 0; + wq->vdev = vdev; + + err = vnic_wq_get_ctrl(vdev, wq, 0, RES_TYPE_DEVCMD2); + if (err) { + pr_err("Failed to get devcmd2 resource\n"); + return err; + } + vnic_wq_disable(wq); + + err = vnic_wq_alloc_ring(vdev, wq, desc_count, desc_size); + if (err) + return err; + return 0; +} + +void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, + unsigned int fetch_index, unsigned int posted_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset) +{ + u64 paddr; + unsigned int count = wq->ring.desc_count; + + paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET; + writeq(paddr, &wq->ctrl->ring_base); + iowrite32(count, &wq->ctrl->ring_size); + iowrite32(fetch_index, &wq->ctrl->fetch_index); + iowrite32(posted_index, &wq->ctrl->posted_index); + iowrite32(cq_index, &wq->ctrl->cq_index); + iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable); + iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset); + iowrite32(0, &wq->ctrl->error_status); + + wq->to_use = wq->to_clean = + &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES] + [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES]; +} + + void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, unsigned int error_interrupt_enable, unsigned int error_interrupt_offset) diff --git a/drivers/scsi/fnic/vnic_wq.h b/drivers/scsi/fnic/vnic_wq.h index 5cd094f79281..5d1e0a44d94a 100644 --- a/drivers/scsi/fnic/vnic_wq.h +++ b/drivers/scsi/fnic/vnic_wq.h @@ -33,6 +33,8 @@ #define vnic_wq_service fnic_wq_service #define vnic_wq_free fnic_wq_free #define vnic_wq_alloc fnic_wq_alloc +#define vnic_wq_devcmd2_alloc fnic_wq_devcmd2_alloc +#define vnic_wq_init_start fnic_wq_init_start #define vnic_wq_init fnic_wq_init #define vnic_wq_error_status fnic_wq_error_status #define vnic_wq_enable fnic_wq_enable @@ -163,6 +165,12 @@ static inline void vnic_wq_service(struct vnic_wq *wq, void vnic_wq_free(struct vnic_wq *wq); int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, unsigned int desc_count, unsigned int desc_size); +int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int desc_count, unsigned int desc_size); +void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, + unsigned int fetch_index, unsigned int posted_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset); void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, unsigned int error_interrupt_enable, unsigned int error_interrupt_offset); From 0a2fdd2215e1fa3b417792bd6e9cb719822cbfb6 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Fri, 18 Jan 2019 14:51:43 -0800 Subject: [PATCH 0597/4277] scsi: fnic: Adding devcmd2 init and posting interfaces This patch adds fnic devcmd2 interfaces for initialization and posting commands to fw. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_dev.c | 223 ++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c index 648f3fda68ab..741bf6c1a2c2 100644 --- a/drivers/scsi/fnic/vnic_dev.c +++ b/drivers/scsi/fnic/vnic_dev.c @@ -316,6 +316,223 @@ int vnic_dev_cmd1(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, int wait) return -ETIMEDOUT; } +int vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + int wait) +{ + struct devcmd2_controller *dc2c = vdev->devcmd2; + struct devcmd2_result *result; + u8 color; + unsigned int i; + int delay; + int err; + u32 fetch_index; + u32 posted; + u32 new_posted; + + posted = ioread32(&dc2c->wq_ctrl->posted_index); + fetch_index = ioread32(&dc2c->wq_ctrl->fetch_index); + + if (posted == 0xFFFFFFFF || fetch_index == 0xFFFFFFFF) { + /* Hardware surprise removal: return error */ + pr_err("%s: devcmd2 invalid posted or fetch index on cmd %d\n", + pci_name(vdev->pdev), _CMD_N(cmd)); + pr_err("%s: fetch index: %u, posted index: %u\n", + pci_name(vdev->pdev), fetch_index, posted); + + return -ENODEV; + + } + + new_posted = (posted + 1) % DEVCMD2_RING_SIZE; + + if (new_posted == fetch_index) { + pr_err("%s: devcmd2 wq full while issuing cmd %d\n", + pci_name(vdev->pdev), _CMD_N(cmd)); + pr_err("%s: fetch index: %u, posted index: %u\n", + pci_name(vdev->pdev), fetch_index, posted); + return -EBUSY; + + } + dc2c->cmd_ring[posted].cmd = cmd; + dc2c->cmd_ring[posted].flags = 0; + + if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT)) + dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT; + if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) { + for (i = 0; i < VNIC_DEVCMD_NARGS; i++) + dc2c->cmd_ring[posted].args[i] = vdev->args[i]; + + } + + /* Adding write memory barrier prevents compiler and/or CPU + * reordering, thus avoiding descriptor posting before + * descriptor is initialized. Otherwise, hardware can read + * stale descriptor fields. + */ + wmb(); + iowrite32(new_posted, &dc2c->wq_ctrl->posted_index); + + if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT) + return 0; + + result = dc2c->result + dc2c->next_result; + color = dc2c->color; + + dc2c->next_result++; + if (dc2c->next_result == dc2c->result_size) { + dc2c->next_result = 0; + dc2c->color = dc2c->color ? 0 : 1; + } + + for (delay = 0; delay < wait; delay++) { + udelay(100); + if (result->color == color) { + if (result->error) { + err = -(int) result->error; + if (err != ERR_ECMDUNKNOWN || + cmd != CMD_CAPABILITY) + pr_err("%s:Error %d devcmd %d\n", + pci_name(vdev->pdev), + err, _CMD_N(cmd)); + return err; + } + if (_CMD_DIR(cmd) & _CMD_DIR_READ) { + rmb(); /*prevent reorder while reding result*/ + for (i = 0; i < VNIC_DEVCMD_NARGS; i++) + vdev->args[i] = result->results[i]; + } + return 0; + } + } + + pr_err("%s:Timed out devcmd %d\n", pci_name(vdev->pdev), _CMD_N(cmd)); + + return -ETIMEDOUT; +} + + +int vnic_dev_init_devcmd1(struct vnic_dev *vdev) +{ + vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0); + if (!vdev->devcmd) + return -ENODEV; + + vdev->devcmd_rtn = &vnic_dev_cmd1; + return 0; +} + + +int vnic_dev_init_devcmd2(struct vnic_dev *vdev) +{ + int err; + unsigned int fetch_index; + + if (vdev->devcmd2) + return 0; + + vdev->devcmd2 = kzalloc(sizeof(*vdev->devcmd2), GFP_ATOMIC); + if (!vdev->devcmd2) + return -ENOMEM; + + vdev->devcmd2->color = 1; + vdev->devcmd2->result_size = DEVCMD2_RING_SIZE; + err = vnic_wq_devcmd2_alloc(vdev, &vdev->devcmd2->wq, + DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE); + if (err) + goto err_free_devcmd2; + + fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index); + if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */ + pr_err("error in devcmd2 init"); + return -ENODEV; + } + + /* + * Don't change fetch_index ever and + * set posted_index same as fetch_index + * when setting up the WQ for devcmd2. + */ + vnic_wq_init_start(&vdev->devcmd2->wq, 0, fetch_index, + fetch_index, 0, 0); + + vnic_wq_enable(&vdev->devcmd2->wq); + + err = vnic_dev_alloc_desc_ring(vdev, &vdev->devcmd2->results_ring, + DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE); + if (err) + goto err_free_wq; + + vdev->devcmd2->result = + (struct devcmd2_result *) vdev->devcmd2->results_ring.descs; + vdev->devcmd2->cmd_ring = + (struct vnic_devcmd2 *) vdev->devcmd2->wq.ring.descs; + vdev->devcmd2->wq_ctrl = vdev->devcmd2->wq.ctrl; + vdev->args[0] = (u64) vdev->devcmd2->results_ring.base_addr | + VNIC_PADDR_TARGET; + vdev->args[1] = DEVCMD2_RING_SIZE; + + err = vnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, 1000); + if (err) + goto err_free_desc_ring; + + vdev->devcmd_rtn = &vnic_dev_cmd2; + + return 0; + +err_free_desc_ring: + vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring); +err_free_wq: + vnic_wq_disable(&vdev->devcmd2->wq); + vnic_wq_free(&vdev->devcmd2->wq); +err_free_devcmd2: + kfree(vdev->devcmd2); + vdev->devcmd2 = NULL; + + return err; +} + + +void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev) +{ + vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring); + vnic_wq_disable(&vdev->devcmd2->wq); + vnic_wq_free(&vdev->devcmd2->wq); + kfree(vdev->devcmd2); + vdev->devcmd2 = NULL; + vdev->devcmd_rtn = &vnic_dev_cmd1; +} + + +int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev, + enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait) +{ + int err; + + vdev->args[0] = *a0; + vdev->args[1] = *a1; + + err = (*vdev->devcmd_rtn)(vdev, cmd, wait); + + *a0 = vdev->args[0]; + *a1 = vdev->args[1]; + + return err; +} + + +int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + u64 *a0, u64 *a1, int wait) +{ + memset(vdev->args, 0, sizeof(vdev->args)); + + switch (vdev->proxy) { + case PROXY_NONE: + default: + return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait); + } +} + + int vnic_dev_fw_info(struct vnic_dev *vdev, struct vnic_devcmd_fw_info **fw_info) { @@ -690,6 +907,8 @@ void vnic_dev_unregister(struct vnic_dev *vdev) dma_free_coherent(&vdev->pdev->dev, sizeof(struct vnic_devcmd_fw_info), vdev->fw_info, vdev->fw_info_pa); + if (vdev->devcmd2) + vnic_dev_deinit_devcmd2(vdev); kfree(vdev); } } @@ -709,10 +928,6 @@ struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev, if (vnic_dev_discover_res(vdev, bar)) goto err_out; - vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0); - if (!vdev->devcmd) - goto err_out; - return vdev; err_out: From e119d14ca38c146846e400182252c22df002db56 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Fri, 18 Jan 2019 14:51:44 -0800 Subject: [PATCH 0598/4277] scsi: fnic: Enable fnic devcmd2 interface This patch adds changes to check if fnic devcmd2 interface is exported by the firmware. If devcmd2 interfaces is exported, driver starts using it else falls back to fnic devcmd1 interface. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_main.c | 11 ++++++++++- drivers/scsi/fnic/vnic_dev.c | 17 +++++++++++++++++ drivers/scsi/fnic/vnic_dev.h | 2 ++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 1433ba94ea71..0f42114be2d1 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -658,12 +658,20 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_iounmap; } + err = vnic_dev_cmd_init(fnic->vdev); + if (err) { + shost_printk(KERN_ERR, fnic->lport->host, + "vnic_dev_cmd_init() returns %d, aborting\n", + err); + goto err_out_vnic_unregister; + } + err = fnic_dev_wait(fnic->vdev, vnic_dev_open, vnic_dev_open_done, CMD_OPENF_RQ_ENABLE_THEN_POST); if (err) { shost_printk(KERN_ERR, fnic->lport->host, "vNIC dev open failed, aborting.\n"); - goto err_out_vnic_unregister; + goto err_out_dev_cmd_deinit; } err = vnic_dev_init(fnic->vdev, 0); @@ -921,6 +929,7 @@ err_out_clear_intr: fnic_clear_intr_mode(fnic); err_out_dev_close: vnic_dev_close(fnic->vdev); +err_out_dev_cmd_deinit: err_out_vnic_unregister: vnic_dev_unregister(fnic->vdev); err_out_iounmap: diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c index 741bf6c1a2c2..78af9cc2009b 100644 --- a/drivers/scsi/fnic/vnic_dev.c +++ b/drivers/scsi/fnic/vnic_dev.c @@ -934,3 +934,20 @@ err_out: vnic_dev_unregister(vdev); return NULL; } + +int vnic_dev_cmd_init(struct vnic_dev *vdev) +{ + int err; + void *p; + + p = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0); + if (p) { + pr_err("fnic: DEVCMD2 resource found!\n"); + err = vnic_dev_init_devcmd2(vdev); + } else { + pr_err("fnic: DEVCMD2 not found, fall back to Devcmd\n"); + err = vnic_dev_init_devcmd1(vdev); + } + + return err; +} diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h index 40d4195f562b..ef5309a5df5d 100644 --- a/drivers/scsi/fnic/vnic_dev.h +++ b/drivers/scsi/fnic/vnic_dev.h @@ -36,6 +36,7 @@ #define vnic_dev_fw_info fnic_dev_fw_info #define vnic_dev_spec fnic_dev_spec #define vnic_dev_stats_clear fnic_dev_stats_clear +#define vnic_dev_cmd_init fnic_dev_cmd_init #define vnic_dev_stats_dump fnic_dev_stats_dump #define vnic_dev_hang_notify fnic_dev_hang_notify #define vnic_dev_packet_filter fnic_dev_packet_filter @@ -128,6 +129,7 @@ int vnic_dev_fw_info(struct vnic_dev *vdev, int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, void *value); int vnic_dev_stats_clear(struct vnic_dev *vdev); +int vnic_dev_cmd_init(struct vnic_dev *vdev); int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats); int vnic_dev_hang_notify(struct vnic_dev *vdev); void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, From f03dc9cfc86f7b6a1bb403adeab5543349db1ffc Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Fri, 18 Jan 2019 14:51:45 -0800 Subject: [PATCH 0599/4277] scsi: fnic: Update fnic driver version to 1.6.0.47 Update fnic driver to version 1.6.0.47. Signed-off-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index a3f20ae0add5..477513dc23b7 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -39,7 +39,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.6.0.34" +#define DRV_VERSION "1.6.0.47" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " From 3e3153b050fc33f3e56763fd237701e7683609a7 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Fri, 18 Jan 2019 10:53:41 +0800 Subject: [PATCH 0600/4277] scsi: arcmsr: Use dma_alloc_coherent to replace dma_zalloc_coherent From Ching Huang dma_zalloc_coherent will be phased out. Use dma_alloc_coherent instead. Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 9f85d5abbb0c..57364347ecd6 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -642,7 +642,7 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_B: { acb->ioqueue_size = roundup(sizeof(struct MessageUnit_B), 32); - dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, + dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); if (!dma_coherent) { pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no); @@ -656,7 +656,7 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) break; case ACB_ADAPTER_TYPE_D: { acb->ioqueue_size = roundup(sizeof(struct MessageUnit_D), 32); - dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, + dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); if (!dma_coherent) { pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no); @@ -672,7 +672,7 @@ static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb) uint32_t completeQ_size; completeQ_size = sizeof(struct deliver_completeQ) * ARCMSR_MAX_HBE_DONEQUEUE + 128; acb->ioqueue_size = roundup(completeQ_size, 32); - dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->ioqueue_size, + dma_coherent = dma_alloc_coherent(&pdev->dev, acb->ioqueue_size, &dma_coherent_handle, GFP_KERNEL); if (!dma_coherent){ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no); From 317d0e027ce42ef197f24435023736ba4e5983a2 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Fri, 18 Jan 2019 10:58:51 +0800 Subject: [PATCH 0601/4277] scsi: arcmsr: Fix suspend/resume of ACB_ADAPTER_TYPE_B part 2 From Ching Huang For ACB_ADAPTER_TYPE_B controller, the read/write after hibernate and resume may sometimes result in 'isr get an illegal ccb command' in /var/log/messages. This patch fixes it. Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 57364347ecd6..88053b15c363 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1113,7 +1113,11 @@ static int arcmsr_resume(struct pci_dev *pdev) switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_B: { struct MessageUnit_B *reg = acb->pmuB; - reg->post_qbuffer[0] = 0; + uint32_t i; + for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) { + reg->post_qbuffer[i] = 0; + reg->done_qbuffer[i] = 0; + } reg->postq_index = 0; reg->doneq_index = 0; break; From dfeba64dd66b674ff6ada5793b0ac00c6ffea587 Mon Sep 17 00:00:00 2001 From: Ching Huang Date: Fri, 18 Jan 2019 11:01:49 +0800 Subject: [PATCH 0602/4277] scsi: arcmsr: Update driver version to v1.40.00.10-20190116 From Ching Huang Update driver version to v1.40.00.10-20190116. Signed-off-by: Ching Huang Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h index a94c513a57d8..9220bcf8388f 100644 --- a/drivers/scsi/arcmsr/arcmsr.h +++ b/drivers/scsi/arcmsr/arcmsr.h @@ -49,7 +49,7 @@ struct device_attribute; #define ARCMSR_MAX_OUTSTANDING_CMD 1024 #define ARCMSR_DEFAULT_OUTSTANDING_CMD 128 #define ARCMSR_MIN_OUTSTANDING_CMD 32 -#define ARCMSR_DRIVER_VERSION "v1.40.00.10-20181217" +#define ARCMSR_DRIVER_VERSION "v1.40.00.10-20190116" #define ARCMSR_SCSI_INITIATOR_ID 255 #define ARCMSR_MAX_XFER_SECTORS 512 #define ARCMSR_MAX_XFER_SECTORS_B 4096 From 3bbe8b1a4ae9585e9cf15e7036bf9e5374a482df Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 23 Jan 2019 01:16:32 -0600 Subject: [PATCH 0603/4277] 9p: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: net/9p/trans_xen.c:514:6: warning: this statement may fall through [-Wimplicit-fallthrough=] Warning level 3 was used: -Wimplicit-fallthrough=3 This patch is part of the ongoing efforts to enabling -Wimplicit-fallthrough Link: http://lkml.kernel.org/r/20190123071632.GA8039@embeddedor Signed-off-by: Gustavo A. R. Silva Signed-off-by: Dominique Martinet --- net/9p/trans_xen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index e2fbf3677b9b..29420ebb8f07 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -513,7 +513,7 @@ static void xen_9pfs_front_changed(struct xenbus_device *dev, case XenbusStateClosed: if (dev->state == XenbusStateClosed) break; - /* Missed the backend's CLOSING state -- fallthrough */ + /* fall through - Missed the backend's CLOSING state */ case XenbusStateClosing: xenbus_frontend_closed(dev); break; From 912ebd99edfa1d1695083fa6d34ac878346ac66b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:21:16 +0100 Subject: [PATCH 0604/4277] EDAC: Do not check return value of debugfs_create() functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. [ bp: Make edac_debugfs_init() return void too, while at it. ] Signed-off-by: Greg Kroah-Hartman Signed-off-by: Borislav Petkov Cc: James Morse Cc: Mauro Carvalho Chehab Cc: linux-edac Link: https://lkml.kernel.org/r/20190122152151.16139-17-gregkh@linuxfoundation.org --- drivers/edac/debugfs.c | 48 ++++++++++---------------------------- drivers/edac/edac_module.h | 8 +++---- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/drivers/edac/debugfs.c b/drivers/edac/debugfs.c index 92dbb7e2320c..0a9277228c50 100644 --- a/drivers/edac/debugfs.c +++ b/drivers/edac/debugfs.c @@ -41,14 +41,9 @@ static const struct file_operations debug_fake_inject_fops = { .llseek = generic_file_llseek, }; -int __init edac_debugfs_init(void) +void __init edac_debugfs_init(void) { edac_debugfs = debugfs_create_dir("edac", NULL); - if (IS_ERR(edac_debugfs)) { - edac_debugfs = NULL; - return -ENOMEM; - } - return 0; } void edac_debugfs_exit(void) @@ -56,50 +51,31 @@ void edac_debugfs_exit(void) debugfs_remove_recursive(edac_debugfs); } -int edac_create_debugfs_nodes(struct mem_ctl_info *mci) +void edac_create_debugfs_nodes(struct mem_ctl_info *mci) { - struct dentry *d, *parent; + struct dentry *parent; char name[80]; int i; - if (!edac_debugfs) - return -ENODEV; - - d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); - if (!d) - return -ENOMEM; - parent = d; + parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); for (i = 0; i < mci->n_layers; i++) { sprintf(name, "fake_inject_%s", edac_layer_name[mci->layers[i].type]); - d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_layer[i]); - if (!d) - goto nomem; + debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_layer[i]); } - d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_ue); - if (!d) - goto nomem; + debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_ue); - d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_count); - if (!d) - goto nomem; + debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_count); - d = debugfs_create_file("fake_inject", S_IWUSR, parent, - &mci->dev, - &debug_fake_inject_fops); - if (!d) - goto nomem; + debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev, + &debug_fake_inject_fops); mci->debugfs = parent; - return 0; -nomem: - edac_debugfs_remove_recursive(mci->debugfs); - return -ENOMEM; } /* Create a toplevel dir under EDAC's debugfs hierarchy */ diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index dec88dcea036..dd7d0b509aa3 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -69,9 +69,9 @@ extern void *edac_align_ptr(void **p, unsigned size, int n_elems); #define edac_debugfs_remove_recursive debugfs_remove_recursive #define edac_debugfs_remove debugfs_remove #ifdef CONFIG_EDAC_DEBUG -int edac_debugfs_init(void); +void edac_debugfs_init(void); void edac_debugfs_exit(void); -int edac_create_debugfs_nodes(struct mem_ctl_info *mci); +void edac_create_debugfs_nodes(struct mem_ctl_info *mci); struct dentry *edac_debugfs_create_dir(const char *dirname); struct dentry * edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent); @@ -83,9 +83,9 @@ edac_debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8 struct dentry * edac_debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value); #else -static inline int edac_debugfs_init(void) { return -ENODEV; } +static inline void edac_debugfs_init(void) { } static inline void edac_debugfs_exit(void) { } -static inline int edac_create_debugfs_nodes(struct mem_ctl_info *mci) { return 0; } +static inline void edac_create_debugfs_nodes(struct mem_ctl_info *mci) { } static inline struct dentry *edac_debugfs_create_dir(const char *dirname) { return NULL; } static inline struct dentry * edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) { return NULL; } From 2abbf9a4d262511999ac11b4ddc8521c9ee72b88 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:21:51 +0100 Subject: [PATCH 0605/4277] gfs: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. There is no need to save the dentries for the debugfs files, so drop those variables to save a bit of space and make the code simpler. Cc: Bob Peterson Cc: Andreas Gruenbacher Cc: cluster-devel@redhat.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 64 +++++++++--------------------------------------- fs/gfs2/glock.h | 4 +-- fs/gfs2/incore.h | 3 --- fs/gfs2/main.c | 6 +---- 4 files changed, 14 insertions(+), 63 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index b92740edc416..f66773c71bcd 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2131,71 +2131,29 @@ static const struct file_operations gfs2_sbstats_fops = { .release = seq_release, }; -int gfs2_create_debugfs_file(struct gfs2_sbd *sdp) +void gfs2_create_debugfs_file(struct gfs2_sbd *sdp) { - struct dentry *dent; + sdp->debugfs_dir = debugfs_create_dir(sdp->sd_table_name, gfs2_root); - dent = debugfs_create_dir(sdp->sd_table_name, gfs2_root); - if (IS_ERR_OR_NULL(dent)) - goto fail; - sdp->debugfs_dir = dent; + debugfs_create_file("glocks", S_IFREG | S_IRUGO, sdp->debugfs_dir, sdp, + &gfs2_glocks_fops); - dent = debugfs_create_file("glocks", - S_IFREG | S_IRUGO, - sdp->debugfs_dir, sdp, - &gfs2_glocks_fops); - if (IS_ERR_OR_NULL(dent)) - goto fail; - sdp->debugfs_dentry_glocks = dent; + debugfs_create_file("glstats", S_IFREG | S_IRUGO, sdp->debugfs_dir, sdp, + &gfs2_glstats_fops); - dent = debugfs_create_file("glstats", - S_IFREG | S_IRUGO, - sdp->debugfs_dir, sdp, - &gfs2_glstats_fops); - if (IS_ERR_OR_NULL(dent)) - goto fail; - sdp->debugfs_dentry_glstats = dent; - - dent = debugfs_create_file("sbstats", - S_IFREG | S_IRUGO, - sdp->debugfs_dir, sdp, - &gfs2_sbstats_fops); - if (IS_ERR_OR_NULL(dent)) - goto fail; - sdp->debugfs_dentry_sbstats = dent; - - return 0; -fail: - gfs2_delete_debugfs_file(sdp); - return dent ? PTR_ERR(dent) : -ENOMEM; + debugfs_create_file("sbstats", S_IFREG | S_IRUGO, sdp->debugfs_dir, sdp, + &gfs2_sbstats_fops); } void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp) { - if (sdp->debugfs_dir) { - if (sdp->debugfs_dentry_glocks) { - debugfs_remove(sdp->debugfs_dentry_glocks); - sdp->debugfs_dentry_glocks = NULL; - } - if (sdp->debugfs_dentry_glstats) { - debugfs_remove(sdp->debugfs_dentry_glstats); - sdp->debugfs_dentry_glstats = NULL; - } - if (sdp->debugfs_dentry_sbstats) { - debugfs_remove(sdp->debugfs_dentry_sbstats); - sdp->debugfs_dentry_sbstats = NULL; - } - debugfs_remove(sdp->debugfs_dir); - sdp->debugfs_dir = NULL; - } + debugfs_remove_recursive(sdp->debugfs_dir); + sdp->debugfs_dir = NULL; } -int gfs2_register_debugfs(void) +void gfs2_register_debugfs(void) { gfs2_root = debugfs_create_dir("gfs2", NULL); - if (IS_ERR(gfs2_root)) - return PTR_ERR(gfs2_root); - return gfs2_root ? 0 : -ENOMEM; } void gfs2_unregister_debugfs(void) diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 8949bf28b249..936b3295839c 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -243,9 +243,9 @@ extern void gfs2_glock_free(struct gfs2_glock *gl); extern int __init gfs2_glock_init(void); extern void gfs2_glock_exit(void); -extern int gfs2_create_debugfs_file(struct gfs2_sbd *sdp); +extern void gfs2_create_debugfs_file(struct gfs2_sbd *sdp); extern void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp); -extern int gfs2_register_debugfs(void); +extern void gfs2_register_debugfs(void); extern void gfs2_unregister_debugfs(void); extern const struct lm_lockops gfs2_dlm_ops; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index e10e0b0a7cd5..cdf07b408f54 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -853,9 +853,6 @@ struct gfs2_sbd { unsigned long sd_last_warning; struct dentry *debugfs_dir; /* debugfs directory */ - struct dentry *debugfs_dentry_glocks; - struct dentry *debugfs_dentry_glstats; - struct dentry *debugfs_dentry_sbstats; }; static inline void gfs2_glstats_inc(struct gfs2_glock *gl, int which) diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index c7603063f861..136484ef35d3 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -178,16 +178,12 @@ static int __init init_gfs2_fs(void) if (!gfs2_page_pool) goto fail_mempool; - error = gfs2_register_debugfs(); - if (error) - goto fail_debugfs; + gfs2_register_debugfs(); pr_info("GFS2 installed\n"); return 0; -fail_debugfs: - mempool_destroy(gfs2_page_pool); fail_mempool: destroy_workqueue(gfs2_freeze_wq); fail_wq3: From a3b8bcbafa45b6cdcbca6beb8684c862a5a2ca61 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 23 Jan 2019 12:44:32 +0000 Subject: [PATCH 0606/4277] rtc: sd3078: fix platform_no_drv_owner.cocci warnings Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: YueHaibing Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sd3078.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index 704844f32b45..42cb90db7f94 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -218,7 +218,6 @@ MODULE_DEVICE_TABLE(of, rtc_dt_match); static struct i2c_driver sd3078_driver = { .driver = { .name = "sd3078", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(rtc_dt_match), }, .probe = sd3078_probe, From ee848721f60d87c5fd27f0a29d5fc570ff0780be Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Tue, 22 Jan 2019 08:33:00 +0200 Subject: [PATCH 0607/4277] IB/umad: Avoid additional device reference during open()/close() ib_umad_init_port_dev() holds the reference of a ib_umad_device instance. ib_umad_device contains standard core device and cdev. cdev holds the reference of its parent core device. file ops holds the reference to cdev using core kernel. Therefore, there is no need to hold additional reference while opening umad related char devices. While at it, add comments to bring clarity on releasing references to ib_umd_device. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/user_mad.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index de8d31ab8945..097f153f0f22 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -989,7 +989,6 @@ static int ib_umad_open(struct inode *inode, struct file *filp) goto out; } - ib_umad_dev_get(port->umad_dev); out: mutex_unlock(&port->file_mutex); return ret; @@ -998,7 +997,6 @@ out: static int ib_umad_close(struct inode *inode, struct file *filp) { struct ib_umad_file *file = filp->private_data; - struct ib_umad_device *dev = file->port->umad_dev; struct ib_umad_packet *packet, *tmp; int already_dead; int i; @@ -1027,7 +1025,6 @@ static int ib_umad_close(struct inode *inode, struct file *filp) mutex_unlock(&file->port->file_mutex); kfree(file); - ib_umad_dev_put(dev); return 0; } @@ -1077,7 +1074,6 @@ static int ib_umad_sm_open(struct inode *inode, struct file *filp) if (ret) goto err_clr_sm_cap; - ib_umad_dev_get(port->umad_dev); return 0; err_clr_sm_cap: @@ -1106,7 +1102,6 @@ static int ib_umad_sm_close(struct inode *inode, struct file *filp) up(&port->sm_sem); - ib_umad_dev_put(port->umad_dev); return ret; } @@ -1283,8 +1278,10 @@ static void ib_umad_kill_port(struct ib_umad_port *port) mutex_unlock(&port->file_mutex); cdev_device_del(&port->sm_cdev, &port->sm_dev); + /* balances device_initialize() */ put_device(&port->sm_dev); cdev_device_del(&port->cdev, &port->dev); + /* balances device_initialize() */ put_device(&port->dev); ida_free(&umad_ida, port->dev_num); } @@ -1329,6 +1326,7 @@ err: ib_umad_kill_port(&umad_dev->ports[i - s]); } free: + /* balances kref_init */ ib_umad_dev_put(umad_dev); } @@ -1344,6 +1342,7 @@ static void ib_umad_remove_one(struct ib_device *device, void *client_data) if (rdma_cap_ib_mad(device, i + rdma_start_port(device))) ib_umad_kill_port(&umad_dev->ports[i]); } + /* balances kref_init() */ ib_umad_dev_put(umad_dev); } From 257afa58e608d5f790b4c0f6e0334f69881f6de4 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 15 Jan 2019 18:32:34 +0800 Subject: [PATCH 0608/4277] dt-bindings: power: sc27xx: Add one IIO channel to read charge voltage Add one IIO channel named "charge_vol" to read the charge voltage for the SC27XX fuel gauge controller. Signed-off-by: Baolin Wang Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/power/supply/sc27xx-fg.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt index fc35ac577401..0a5705b8b592 100644 --- a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt +++ b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.txt @@ -9,8 +9,8 @@ Required properties: "sprd,sc2731-fgu". - reg: The address offset of fuel gauge unit. - battery-detect-gpios: GPIO for battery detection. -- io-channels: Specify the IIO ADC channel to get temperature. -- io-channel-names: Should be "bat-temp". +- io-channels: Specify the IIO ADC channels to get temperature and charge voltage. +- io-channel-names: Should be "bat-temp" or "charge-vol". - nvmem-cells: A phandle to the calibration cells provided by eFuse device. - nvmem-cell-names: Should be "fgu_calib". - monitored-battery: Phandle of battery characteristics devicetree node. @@ -47,8 +47,8 @@ Example: compatible = "sprd,sc2731-fgu"; reg = <0xa00>; battery-detect-gpios = <&pmic_eic 9 GPIO_ACTIVE_HIGH>; - io-channels = <&pmic_adc 5>; - io-channel-names = "bat-temp"; + io-channels = <&pmic_adc 5>, <&pmic_adc 14>; + io-channel-names = "bat-temp", "charge-vol"; nvmem-cells = <&fgu_calib>; nvmem-cell-names = "fgu_calib"; monitored-battery = <&bat>; From 0a4f97a171888594435a019711b719b32233b7c9 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 15 Jan 2019 18:32:35 +0800 Subject: [PATCH 0609/4277] power: supply: sc27xx: Add one property to read charge voltage Add POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE property to get charge voltage sampling by ADC controller, which is used to validate if the charge voltage is in normal range or not in charger manager. Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 76da1895b782..4926b8a7459a 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -72,6 +72,7 @@ * @lock: protect the structure * @gpiod: GPIO for battery detection * @channel: IIO channel to get battery temperature + * @charge_chan: IIO channel to get charge voltage * @internal_resist: the battery internal resistance in mOhm * @total_cap: the total capacity of the battery in mAh * @init_cap: the initial capacity of the battery in mAh @@ -92,6 +93,7 @@ struct sc27xx_fgu_data { struct mutex lock; struct gpio_desc *gpiod; struct iio_channel *channel; + struct iio_channel *charge_chan; bool bat_present; int internal_resist; int total_cap; @@ -391,6 +393,18 @@ static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) return 0; } +static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) +{ + int ret, vol; + + ret = iio_read_channel_processed(data->charge_chan, &vol); + if (ret < 0) + return ret; + + *val = vol * 1000; + return 0; +} + static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) { return iio_read_channel_processed(data->channel, temp); @@ -502,6 +516,14 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, val->intval = value; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = sc27xx_fgu_get_charge_vol(data, &value); + if (ret) + goto error; + + val->intval = value; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: ret = sc27xx_fgu_get_current(data, &value); @@ -567,6 +589,7 @@ static enum power_supply_property sc27xx_fgu_props[] = { POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; static const struct power_supply_desc sc27xx_fgu_desc = { @@ -907,6 +930,12 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) return PTR_ERR(data->channel); } + data->charge_chan = devm_iio_channel_get(&pdev->dev, "charge-vol"); + if (IS_ERR(data->charge_chan)) { + dev_err(&pdev->dev, "failed to get charge IIO channel\n"); + return PTR_ERR(data->charge_chan); + } + data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN); if (IS_ERR(data->gpiod)) { dev_err(&pdev->dev, "failed to get battery detection GPIO\n"); From 3a28f203c57944ac98a3adcbfacdbfa385966972 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 15 Jan 2019 18:32:36 +0800 Subject: [PATCH 0610/4277] power: supply: sc27xx: Fix the incorrect formula when converting capacity to coulomb counter We should multiply the calibrated current data (cur_1000ma_adc) when converting current capacity (mAh) to coulomb counter, which can get an accurate coulomb counter from the fuel gauge controller. Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 4926b8a7459a..ea1349fe55a5 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -731,7 +731,7 @@ static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) * Convert current capacity (mAh) to coulomb counter according to the * formula: 1 mAh =3.6 coulomb. */ - return DIV_ROUND_CLOSEST(cur_cap * 36, 10); + return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc, 10); } static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) From d3e67c94e8f607ee90097d296deecf030af9b8ab Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Tue, 15 Jan 2019 18:32:37 +0800 Subject: [PATCH 0611/4277] power: supply: sc27xx: Fix capacity saving function We found sometimes we can not get the saving capacity to initialize the battery capacity, the reason is the user area registers are put on power always-on region, so we need delay some time to wait until values are updated successfully. Moreover we also should clear the USER_AREA_CLEAR register after setting the USER_AREA_SET register, otherwise we can not save the values in the USER_AREA_SET register. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 64 ++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index ea1349fe55a5..24895cc3b41e 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -171,10 +171,37 @@ static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, if (ret) return ret; + /* + * Since the user area registers are put on power always-on region, + * then these registers changing time will be a little long. Thus + * here we should delay 200us to wait until values are updated + * successfully according to the datasheet. + */ + udelay(200); + + ret = regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_USER_AREA_SET, + SC27XX_FGU_MODE_AREA_MASK, + boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); + if (ret) + return ret; + + /* + * Since the user area registers are put on power always-on region, + * then these registers changing time will be a little long. Thus + * here we should delay 200us to wait until values are updated + * successfully according to the datasheet. + */ + udelay(200); + + /* + * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to + * make the user area data available, otherwise we can not save the user + * area data. + */ return regmap_update_bits(data->regmap, - data->base + SC27XX_FGU_USER_AREA_SET, - SC27XX_FGU_MODE_AREA_MASK, - boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); + data->base + SC27XX_FGU_USER_AREA_CLEAR, + SC27XX_FGU_MODE_AREA_MASK, 0); } static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) @@ -188,9 +215,36 @@ static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) if (ret) return ret; + /* + * Since the user area registers are put on power always-on region, + * then these registers changing time will be a little long. Thus + * here we should delay 200us to wait until values are updated + * successfully according to the datasheet. + */ + udelay(200); + + ret = regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_USER_AREA_SET, + SC27XX_FGU_CAP_AREA_MASK, cap); + if (ret) + return ret; + + /* + * Since the user area registers are put on power always-on region, + * then these registers changing time will be a little long. Thus + * here we should delay 200us to wait until values are updated + * successfully according to the datasheet. + */ + udelay(200); + + /* + * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to + * make the user area data available, otherwise we can not save the user + * area data. + */ return regmap_update_bits(data->regmap, - data->base + SC27XX_FGU_USER_AREA_SET, - SC27XX_FGU_CAP_AREA_MASK, cap); + data->base + SC27XX_FGU_USER_AREA_CLEAR, + SC27XX_FGU_CAP_AREA_MASK, 0); } static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) From f83a6ece6997fe64ce9b02e8169c3fb43c68ea55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Mon, 17 Dec 2018 20:28:16 +0100 Subject: [PATCH 0612/4277] power: supply: bq25890: show max charge current/voltage as configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use firmware-provided limits for POWER_SUPPLY_*_MAX properties instead of chip max values. This will reflect the battery limits as those are the important ones. Signed-off-by: Michał Mirosław Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 3f6fb49c956c..66991e6f75d9 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -436,7 +436,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - val->intval = bq25890_tables[TBL_ICHG].rt.max; + val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: @@ -454,7 +454,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: - val->intval = bq25890_tables[TBL_VREG].rt.max; + val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG); break; case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: From 1058ef0dcb7116fc49270131fda3037fddcc6595 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 5 Sep 2018 12:24:00 -0700 Subject: [PATCH 0613/4277] fscrypt: remove CRYPTO_CTR dependency fscrypt doesn't use the CTR mode of operation for anything, so there's no need to select CRYPTO_CTR. It was added by commit 71dea01ea2ed ("ext4 crypto: require CONFIG_CRYPTO_CTR if ext4 encryption is enabled"). But, I've been unable to identify the arm64 crypto bug it was supposedly working around. I suspect the issue was seen only on some old Android device kernel (circa 3.10?). So if the fix wasn't mistaken, the real bug is probably already fixed. Or maybe it was actually a bug in a non-upstream crypto driver. So, remove the dependency. If it turns out there's actually still a bug, we'll fix it properly. Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index 02b7d91c9231..284b589b4774 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -6,7 +6,6 @@ config FS_ENCRYPTION select CRYPTO_ECB select CRYPTO_XTS select CRYPTO_CTS - select CRYPTO_CTR select CRYPTO_SHA256 select KEYS help From 592ddec7578a33fb6b3e2ba78aed91614bbb30e2 Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Wed, 12 Dec 2018 15:20:10 +0530 Subject: [PATCH 0614/4277] ext4: use IS_ENCRYPTED() to check encryption status This commit removes the ext4 specific ext4_encrypted_inode() and makes use of the generic IS_ENCRYPTED() macro to check for the encryption status of an inode. Reviewed-by: Eric Biggers Signed-off-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/ext4/dir.c | 8 ++++---- fs/ext4/ext4.h | 5 ----- fs/ext4/ext4_jbd2.h | 2 +- fs/ext4/extents.c | 4 ++-- fs/ext4/ialloc.c | 2 +- fs/ext4/inode.c | 16 +++++++--------- fs/ext4/move_extent.c | 3 +-- fs/ext4/namei.c | 8 ++++---- fs/ext4/page-io.c | 3 +-- fs/ext4/readpage.c | 3 +-- 10 files changed, 22 insertions(+), 32 deletions(-) diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index f93f9881ec18..fb7a64ea5679 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -111,7 +111,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) int dir_has_error = 0; struct fscrypt_str fstr = FSTR_INIT(NULL, 0); - if (ext4_encrypted_inode(inode)) { + if (IS_ENCRYPTED(inode)) { err = fscrypt_get_encryption_info(inode); if (err && err != -ENOKEY) return err; @@ -138,7 +138,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) return err; } - if (ext4_encrypted_inode(inode)) { + if (IS_ENCRYPTED(inode)) { err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr); if (err < 0) return err; @@ -245,7 +245,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); if (le32_to_cpu(de->inode)) { - if (!ext4_encrypted_inode(inode)) { + if (!IS_ENCRYPTED(inode)) { if (!dir_emit(ctx, de->name, de->name_len, le32_to_cpu(de->inode), @@ -613,7 +613,7 @@ finished: static int ext4_dir_open(struct inode * inode, struct file * filp) { - if (ext4_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) return fscrypt_get_encryption_info(inode) ? -EACCES : 0; return 0; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 185a05d3257e..afdb9ad8be0e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2279,11 +2279,6 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb, struct ext4_group_desc *gdp); ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); -static inline bool ext4_encrypted_inode(struct inode *inode) -{ - return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT); -} - #ifdef CONFIG_EXT4_FS_ENCRYPTION static inline int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname, diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 15b6dd733780..a1ac7e9245ec 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -411,7 +411,7 @@ static inline int ext4_inode_journal_mode(struct inode *inode) (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) && !test_opt(inode->i_sb, DELALLOC))) { /* We do not support data journalling for encrypted data */ - if (S_ISREG(inode->i_mode) && ext4_encrypted_inode(inode)) + if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode)) return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */ return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */ } diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 240b6dea5441..79d986dbf5af 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3631,7 +3631,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, max_zeroout = sbi->s_extent_max_zeroout_kb >> (inode->i_sb->s_blocksize_bits - 10); - if (ext4_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) max_zeroout = 0; /* @@ -4818,7 +4818,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) * leave it disabled for encrypted inodes for now. This is a * bug we should fix.... */ - if (ext4_encrypted_inode(inode) && + if (IS_ENCRYPTED(inode) && (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE | FALLOC_FL_ZERO_RANGE))) return -EOPNOTSUPP; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 7ff14a1adba3..f3e17a8c84b4 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -771,7 +771,7 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, if (unlikely(ext4_forced_shutdown(sbi))) return ERR_PTR(-EIO); - if ((ext4_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) && + if ((IS_ENCRYPTED(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) && !(i_flags & EXT4_EA_INODE_FL)) { err = fscrypt_get_encryption_info(dir); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 34d7e0703cc6..71bd2d28f58d 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -415,7 +415,7 @@ int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk, { int ret; - if (ext4_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) return fscrypt_zeroout_range(inode, lblk, pblk, len); ret = sb_issue_zeroout(inode->i_sb, pblk, len, GFP_NOFS); @@ -1217,8 +1217,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, (block_start < from || block_end > to)) { ll_rw_block(REQ_OP_READ, 0, 1, &bh); *wait_bh++ = bh; - decrypt = ext4_encrypted_inode(inode) && - S_ISREG(inode->i_mode); + decrypt = IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode); } } /* @@ -3881,7 +3880,7 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ssize_t ret; #ifdef CONFIG_EXT4_FS_ENCRYPTION - if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) return 0; #endif @@ -4065,8 +4064,7 @@ static int __ext4_block_zero_page_range(handle_t *handle, /* Uhhuh. Read error. Complain and punt. */ if (!buffer_uptodate(bh)) goto unlock; - if (S_ISREG(inode->i_mode) && - ext4_encrypted_inode(inode)) { + if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode)) { /* We expect the key to be set. */ BUG_ON(!fscrypt_has_encryption_key(inode)); BUG_ON(blocksize != PAGE_SIZE); @@ -4142,7 +4140,7 @@ static int ext4_block_truncate_page(handle_t *handle, struct inode *inode = mapping->host; /* If we are processing an encrypted inode during orphan list handling */ - if (ext4_encrypted_inode(inode) && !fscrypt_has_encryption_key(inode)) + if (IS_ENCRYPTED(inode) && !fscrypt_has_encryption_key(inode)) return 0; blocksize = inode->i_sb->s_blocksize; @@ -4722,7 +4720,7 @@ static bool ext4_should_use_dax(struct inode *inode) return false; if (ext4_has_inline_data(inode)) return false; - if (ext4_encrypted_inode(inode)) + if (ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT)) return false; return true; } @@ -5072,7 +5070,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, ret = -EFSCORRUPTED; goto bad_inode; } - if (ext4_encrypted_inode(inode)) { + if (IS_ENCRYPTED(inode)) { inode->i_op = &ext4_encrypted_symlink_inode_operations; ext4_set_aops(inode); } else if (ext4_inode_is_fast_symlink(inode)) { diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 2f5be02fc6f6..1083a9f3f16a 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -592,8 +592,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk, return -EOPNOTSUPP; } - if (ext4_encrypted_inode(orig_inode) || - ext4_encrypted_inode(donor_inode)) { + if (IS_ENCRYPTED(orig_inode) || IS_ENCRYPTED(donor_inode)) { ext4_msg(orig_inode->i_sb, KERN_ERR, "Online defrag not supported for encrypted files"); return -EOPNOTSUPP; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 2b928eb07fa2..be6cb69beb12 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -621,7 +621,7 @@ static struct stats dx_show_leaf(struct inode *dir, name = de->name; len = de->name_len; - if (ext4_encrypted_inode(dir)) + if (IS_ENCRYPTED(dir)) res = fscrypt_get_encryption_info(dir); if (res) { printk(KERN_WARNING "Error setting up" @@ -986,7 +986,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, EXT4_DIR_REC_LEN(0)); #ifdef CONFIG_EXT4_FS_ENCRYPTION /* Check if the directory is encrypted */ - if (ext4_encrypted_inode(dir)) { + if (IS_ENCRYPTED(dir)) { err = fscrypt_get_encryption_info(dir); if (err < 0) { brelse(bh); @@ -1015,7 +1015,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, continue; if (de->inode == 0) continue; - if (!ext4_encrypted_inode(dir)) { + if (!IS_ENCRYPTED(dir)) { tmp_str.name = de->name; tmp_str.len = de->name_len; err = ext4_htree_store_dirent(dir_file, @@ -1578,7 +1578,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi ino); return ERR_PTR(-EFSCORRUPTED); } - if (!IS_ERR(inode) && ext4_encrypted_inode(dir) && + if (!IS_ERR(inode) && IS_ENCRYPTED(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && !fscrypt_has_permitted_context(dir, inode)) { ext4_warning(inode->i_sb, diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 2aa62d58d8dd..c398b55da854 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -477,8 +477,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, bh = head = page_buffers(page); - if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode) && - nr_to_submit) { + if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode) && nr_to_submit) { gfp_t gfp_flags = GFP_NOFS; retry_encrypt: diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 6aa282ee455a..52d3ff5a9db1 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -242,8 +242,7 @@ int ext4_mpage_readpages(struct address_space *mapping, if (bio == NULL) { struct fscrypt_ctx *ctx = NULL; - if (ext4_encrypted_inode(inode) && - S_ISREG(inode->i_mode)) { + if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) { ctx = fscrypt_get_ctx(inode, GFP_NOFS); if (IS_ERR(ctx)) goto set_error_page; From 62230e0d702f613e2f93e9c3ffd2893b36eff2db Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Wed, 12 Dec 2018 15:20:11 +0530 Subject: [PATCH 0615/4277] f2fs: use IS_ENCRYPTED() to check encryption status This commit removes the f2fs specific f2fs_encrypted_inode() and makes use of the generic IS_ENCRYPTED() macro to check for the encryption status of an inode. Acked-by: Chao Yu Reviewed-by: Eric Biggers Signed-off-by: Chandan Rajendra Signed-off-by: Eric Biggers --- fs/f2fs/data.c | 4 ++-- fs/f2fs/dir.c | 10 +++++----- fs/f2fs/f2fs.h | 7 +------ fs/f2fs/file.c | 10 +++++----- fs/f2fs/inode.c | 4 ++-- fs/f2fs/namei.c | 6 +++--- 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f91d8630c9a2..a36eb260656e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1465,7 +1465,7 @@ next: } if (size) { - if (f2fs_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) flags |= FIEMAP_EXTENT_DATA_ENCRYPTED; ret = fiemap_fill_next_extent(fieinfo, logical, @@ -1736,7 +1736,7 @@ static inline bool check_inplace_update_policy(struct inode *inode, if (policy & (0x1 << F2FS_IPU_ASYNC) && fio && fio->op == REQ_OP_WRITE && !(fio->op_flags & REQ_SYNC) && - !f2fs_encrypted_inode(inode)) + !IS_ENCRYPTED(inode)) return true; /* this is only set during fdatasync */ diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 50d0d36280fa..713b36a10a79 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -385,7 +385,7 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir, if (err) goto put_error; - if ((f2fs_encrypted_inode(dir) || dummy_encrypt) && + if ((IS_ENCRYPTED(dir) || dummy_encrypt) && f2fs_may_encrypt(inode)) { err = fscrypt_inherit_context(dir, inode, page, false); if (err) @@ -399,7 +399,7 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir, if (new_name) { init_dent_inode(new_name, page); - if (f2fs_encrypted_inode(dir)) + if (IS_ENCRYPTED(dir)) file_set_enc_name(inode); } @@ -819,7 +819,7 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, goto out; } - if (f2fs_encrypted_inode(d->inode)) { + if (IS_ENCRYPTED(d->inode)) { int save_len = fstr->len; err = fscrypt_fname_disk_to_usr(d->inode, @@ -862,7 +862,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) struct fscrypt_str fstr = FSTR_INIT(NULL, 0); int err = 0; - if (f2fs_encrypted_inode(inode)) { + if (IS_ENCRYPTED(inode)) { err = fscrypt_get_encryption_info(inode); if (err && err != -ENOKEY) goto out; @@ -924,7 +924,7 @@ out: static int f2fs_dir_open(struct inode *inode, struct file *filp) { - if (f2fs_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) return fscrypt_get_encryption_info(inode) ? -EACCES : 0; return 0; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 12fabd6735dd..9ef6f38e51cc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3463,14 +3463,9 @@ void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi); /* * crypto support */ -static inline bool f2fs_encrypted_inode(struct inode *inode) -{ - return file_is_encrypt(inode); -} - static inline bool f2fs_encrypted_file(struct inode *inode) { - return f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode); + return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode); } static inline void f2fs_set_encrypted_inode(struct inode *inode) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bba56b39dcc5..ba5954f41e14 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -582,7 +582,7 @@ truncate_out: zero_user(page, offset, PAGE_SIZE - offset); /* An encrypted inode should have a key and truncate the last page. */ - f2fs_bug_on(F2FS_I_SB(inode), cache_only && f2fs_encrypted_inode(inode)); + f2fs_bug_on(F2FS_I_SB(inode), cache_only && IS_ENCRYPTED(inode)); if (!cache_only) set_page_dirty(page); f2fs_put_page(page, 1); @@ -711,7 +711,7 @@ int f2fs_getattr(const struct path *path, struct kstat *stat, stat->attributes |= STATX_ATTR_APPEND; if (flags & F2FS_COMPR_FL) stat->attributes |= STATX_ATTR_COMPRESSED; - if (f2fs_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) stat->attributes |= STATX_ATTR_ENCRYPTED; if (flags & F2FS_IMMUTABLE_FL) stat->attributes |= STATX_ATTR_IMMUTABLE; @@ -1563,7 +1563,7 @@ static long f2fs_fallocate(struct file *file, int mode, if (!S_ISREG(inode->i_mode)) return -EINVAL; - if (f2fs_encrypted_inode(inode) && + if (IS_ENCRYPTED(inode) && (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE))) return -EOPNOTSUPP; @@ -1647,7 +1647,7 @@ static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) struct f2fs_inode_info *fi = F2FS_I(inode); unsigned int flags = fi->i_flags; - if (f2fs_encrypted_inode(inode)) + if (IS_ENCRYPTED(inode)) flags |= F2FS_ENCRYPT_FL; if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) flags |= F2FS_INLINE_DATA_FL; @@ -2414,7 +2414,7 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, if (!S_ISREG(src->i_mode) || !S_ISREG(dst->i_mode)) return -EINVAL; - if (f2fs_encrypted_inode(src) || f2fs_encrypted_inode(dst)) + if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst)) return -EOPNOTSUPP; if (src == dst) { diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index bec52961630b..d910a820ae67 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -43,7 +43,7 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_NOATIME; if (flags & F2FS_DIRSYNC_FL) new_fl |= S_DIRSYNC; - if (f2fs_encrypted_inode(inode)) + if (file_is_encrypt(inode)) new_fl |= S_ENCRYPTED; inode_set_flags(inode, new_fl, S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC| @@ -453,7 +453,7 @@ make_now: inode->i_mapping->a_ops = &f2fs_dblock_aops; inode_nohighmem(inode); } else if (S_ISLNK(inode->i_mode)) { - if (f2fs_encrypted_inode(inode)) + if (file_is_encrypt(inode)) inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 62d9829f3a6a..e967d27c1a89 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -75,7 +75,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) set_inode_flag(inode, FI_NEW_INODE); /* If the directory encrypted, then we should encrypt the inode. */ - if ((f2fs_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) && + if ((IS_ENCRYPTED(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) && f2fs_may_encrypt(inode)) f2fs_set_encrypted_inode(inode); @@ -476,7 +476,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (err) goto out_iput; } - if (f2fs_encrypted_inode(dir) && + if (IS_ENCRYPTED(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && !fscrypt_has_permitted_context(dir, inode)) { f2fs_msg(inode->i_sb, KERN_WARNING, @@ -803,7 +803,7 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if (f2fs_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) { + if (IS_ENCRYPTED(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) { int err = fscrypt_get_encryption_info(dir); if (err) return err; From 643fa9612bf1a29153eee46fd398117632f93cbe Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Wed, 12 Dec 2018 15:20:12 +0530 Subject: [PATCH 0616/4277] fscrypt: remove filesystem specific build config option In order to have a common code base for fscrypt "post read" processing for all filesystems which support encryption, this commit removes filesystem specific build config option (e.g. CONFIG_EXT4_FS_ENCRYPTION) and replaces it with a build option (i.e. CONFIG_FS_ENCRYPTION) whose value affects all the filesystems making use of fscrypt. Reviewed-by: Eric Biggers Signed-off-by: Chandan Rajendra Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 4 +- arch/mips/configs/generic_defconfig | 2 +- arch/nds32/configs/defconfig | 2 +- arch/s390/configs/debug_defconfig | 2 +- arch/s390/configs/performance_defconfig | 2 +- fs/crypto/Kconfig | 5 +- fs/crypto/fscrypt_private.h | 1 - fs/ext4/Kconfig | 15 - fs/ext4/dir.c | 2 - fs/ext4/ext4.h | 7 +- fs/ext4/inode.c | 8 +- fs/ext4/ioctl.c | 4 +- fs/ext4/namei.c | 10 +- fs/ext4/page-io.c | 6 +- fs/ext4/readpage.c | 2 +- fs/ext4/super.c | 6 +- fs/ext4/sysfs.c | 4 +- fs/f2fs/Kconfig | 12 +- fs/f2fs/f2fs.h | 7 +- fs/f2fs/super.c | 8 +- fs/f2fs/sysfs.c | 4 +- fs/ubifs/Kconfig | 12 +- fs/ubifs/Makefile | 2 +- fs/ubifs/ioctl.c | 4 +- fs/ubifs/sb.c | 2 +- fs/ubifs/super.c | 2 +- fs/ubifs/ubifs.h | 5 +- include/linux/fs.h | 4 +- include/linux/fscrypt.h | 416 +++++++++++++++++++++++- include/linux/fscrypt_notsupp.h | 231 ------------- include/linux/fscrypt_supp.h | 204 ------------ 31 files changed, 460 insertions(+), 535 deletions(-) delete mode 100644 include/linux/fscrypt_notsupp.h delete mode 100644 include/linux/fscrypt_supp.h diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 3a7b60521b94..43dd989e2a3f 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -343,9 +343,9 @@ FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors: - ``ENOTEMPTY``: the file is unencrypted and is a nonempty directory - ``ENOTTY``: this type of filesystem does not implement encryption - ``EOPNOTSUPP``: the kernel was not configured with encryption - support for this filesystem, or the filesystem superblock has not + support for filesystems, or the filesystem superblock has not had encryption enabled on it. (For example, to use encryption on an - ext4 filesystem, CONFIG_EXT4_ENCRYPTION must be enabled in the + ext4 filesystem, CONFIG_FS_ENCRYPTION must be enabled in the kernel config, and the superblock must have had the "encrypt" feature flag enabled using ``tune2fs -O encrypt`` or ``mkfs.ext4 -O encrypt``.) diff --git a/arch/mips/configs/generic_defconfig b/arch/mips/configs/generic_defconfig index 7c138dab87df..5d80521e5d5a 100644 --- a/arch/mips/configs/generic_defconfig +++ b/arch/mips/configs/generic_defconfig @@ -59,7 +59,7 @@ CONFIG_HID_MONTEREY=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y -CONFIG_EXT4_ENCRYPTION=y +CONFIG_FS_ENCRYPTION=y CONFIG_FANOTIFY=y CONFIG_FUSE_FS=y CONFIG_CUSE=y diff --git a/arch/nds32/configs/defconfig b/arch/nds32/configs/defconfig index 2546d8770785..65ce9259081b 100644 --- a/arch/nds32/configs/defconfig +++ b/arch/nds32/configs/defconfig @@ -74,7 +74,7 @@ CONFIG_GENERIC_PHY=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y -CONFIG_EXT4_ENCRYPTION=y +CONFIG_FS_ENCRYPTION=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index c69cb04b7a59..9824c7bad9d4 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -500,7 +500,6 @@ CONFIG_S390_AP_IOMMU=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y -CONFIG_EXT4_ENCRYPTION=y CONFIG_JBD2_DEBUG=y CONFIG_JFS_FS=m CONFIG_JFS_POSIX_ACL=y @@ -520,6 +519,7 @@ CONFIG_BTRFS_DEBUG=y CONFIG_NILFS2_FS=m CONFIG_FS_DAX=y CONFIG_EXPORTFS_BLOCK_OPS=y +CONFIG_FS_ENCRYPTION=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA_NETLINK_INTERFACE=y diff --git a/arch/s390/configs/performance_defconfig b/arch/s390/configs/performance_defconfig index 32f539dc9c19..4fcbe5792744 100644 --- a/arch/s390/configs/performance_defconfig +++ b/arch/s390/configs/performance_defconfig @@ -497,7 +497,6 @@ CONFIG_S390_AP_IOMMU=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y -CONFIG_EXT4_ENCRYPTION=y CONFIG_JBD2_DEBUG=y CONFIG_JFS_FS=m CONFIG_JFS_POSIX_ACL=y @@ -515,6 +514,7 @@ CONFIG_BTRFS_FS_POSIX_ACL=y CONFIG_NILFS2_FS=m CONFIG_FS_DAX=y CONFIG_EXPORTFS_BLOCK_OPS=y +CONFIG_FS_ENCRYPTION=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA_NETLINK_INTERFACE=y diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index 284b589b4774..f0de238000c0 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -1,5 +1,5 @@ config FS_ENCRYPTION - tristate "FS Encryption (Per-file encryption)" + bool "FS Encryption (Per-file encryption)" select CRYPTO select CRYPTO_AES select CRYPTO_CBC @@ -12,4 +12,5 @@ config FS_ENCRYPTION Enable encryption of files and directories. This feature is similar to ecryptfs, but it is more memory efficient since it avoids caching the encrypted and - decrypted pages in the page cache. + decrypted pages in the page cache. Currently Ext4, + F2FS and UBIFS make use of this feature. diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 7424f851eb5c..7da276159593 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -12,7 +12,6 @@ #ifndef _FSCRYPT_PRIVATE_H #define _FSCRYPT_PRIVATE_H -#define __FS_HAS_ENCRYPTION 1 #include #include diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index a453cc87082b..031e5a82d556 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -96,21 +96,6 @@ config EXT4_FS_SECURITY If you are not using a security module that requires using extended attributes for file security labels, say N. -config EXT4_ENCRYPTION - bool "Ext4 Encryption" - depends on EXT4_FS - select FS_ENCRYPTION - help - Enable encryption of ext4 files and directories. This - feature is similar to ecryptfs, but it is more memory - efficient since it avoids caching the encrypted and - decrypted pages in the page cache. - -config EXT4_FS_ENCRYPTION - bool - default y - depends on EXT4_ENCRYPTION - config EXT4_DEBUG bool "EXT4 debugging support" depends on EXT4_FS diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index fb7a64ea5679..0ccd51f72048 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -283,9 +283,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) done: err = 0; errout: -#ifdef CONFIG_EXT4_FS_ENCRYPTION fscrypt_fname_free_buffer(&fstr); -#endif brelse(bh); return err; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index afdb9ad8be0e..5012ddb6daf9 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -40,7 +40,6 @@ #include #endif -#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) #include #include @@ -1326,7 +1325,7 @@ struct ext4_super_block { #define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */ #define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004 -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION #define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \ EXT4_MF_TEST_DUMMY_ENCRYPTION)) #else @@ -2051,7 +2050,7 @@ struct ext4_filename { const struct qstr *usr_fname; struct fscrypt_str disk_name; struct dx_hash_info hinfo; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION struct fscrypt_str crypto_buf; #endif }; @@ -2279,7 +2278,7 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb, struct ext4_group_desc *gdp); ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION static inline int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname, int lookup, struct ext4_filename *fname) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 71bd2d28f58d..4356ef6d728e 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1150,7 +1150,7 @@ int do_journal_get_write_access(handle_t *handle, return ret; } -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, get_block_t *get_block) { @@ -1302,7 +1302,7 @@ retry_journal: /* In case writeback began while the page was unlocked */ wait_for_stable_page(page); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION if (ext4_should_dioread_nolock(inode)) ret = ext4_block_write_begin(page, pos, len, ext4_get_block_unwritten); @@ -3104,7 +3104,7 @@ retry_journal: /* In case writeback began while the page was unlocked */ wait_for_stable_page(page); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION ret = ext4_block_write_begin(page, pos, len, ext4_da_get_block_prep); #else @@ -3879,7 +3879,7 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) loff_t offset = iocb->ki_pos; ssize_t ret; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) return 0; #endif diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index d37dafa1d133..d26bcac291bb 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -210,7 +210,7 @@ journal_err_out: return err; } -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION static int uuid_is_zero(__u8 u[16]) { int i; @@ -978,7 +978,7 @@ resizefs_out: return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); case EXT4_IOC_GET_ENCRYPTION_PWSALT: { -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION int err, err2; struct ext4_sb_info *sbi = EXT4_SB(sb); handle_t *handle; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index be6cb69beb12..980166a8122a 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -612,7 +612,7 @@ static struct stats dx_show_leaf(struct inode *dir, { if (show_names) { -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION int len; char *name; struct fscrypt_str fname_crypto_str = @@ -984,7 +984,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, top = (struct ext4_dir_entry_2 *) ((char *) de + dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0)); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION /* Check if the directory is encrypted */ if (IS_ENCRYPTED(dir)) { err = fscrypt_get_encryption_info(dir); @@ -1047,7 +1047,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, } errout: brelse(bh); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION fscrypt_fname_free_buffer(&fname_crypto_str); #endif return count; @@ -1267,7 +1267,7 @@ static inline bool ext4_match(const struct ext4_filename *fname, f.usr_fname = fname->usr_fname; f.disk_name = fname->disk_name; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION f.crypto_buf = fname->crypto_buf; #endif return fscrypt_match_name(&f, de->name, de->name_len); @@ -1498,7 +1498,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, ext4_lblk_t block; int retval; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION *res_dir = NULL; #endif frame = dx_probe(fname, dir, NULL, frames); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index c398b55da854..b9d6cabe2ea8 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -66,7 +66,7 @@ static void ext4_finish_bio(struct bio *bio) bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION struct page *data_page = NULL; #endif struct buffer_head *bh, *head; @@ -78,7 +78,7 @@ static void ext4_finish_bio(struct bio *bio) if (!page) continue; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION if (!page->mapping) { /* The bounce data pages are unmapped. */ data_page = page; @@ -111,7 +111,7 @@ static void ext4_finish_bio(struct bio *bio) bit_spin_unlock(BH_Uptodate_Lock, &head->b_state); local_irq_restore(flags); if (!under_io) { -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION if (data_page) fscrypt_restore_control_page(data_page); #endif diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 52d3ff5a9db1..b18881eb8da6 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -49,7 +49,7 @@ static inline bool ext4_bio_encrypted(struct bio *bio) { -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION return unlikely(bio->bi_private != NULL); #else return false; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index fb12d3c17c1b..60da0a6e4d86 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1232,7 +1232,7 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page, return try_to_free_buffers(page); } -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION static int ext4_get_context(struct inode *inode, void *ctx, size_t len) { return ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, @@ -1922,7 +1922,7 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, *journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg); } else if (token == Opt_test_dummy_encryption) { -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION; ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled"); @@ -4167,7 +4167,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sb->s_op = &ext4_sops; sb->s_export_op = &ext4_export_ops; sb->s_xattr = ext4_xattr_handlers; -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION sb->s_cop = &ext4_cryptops; #endif #ifdef CONFIG_QUOTA diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c index 9212a026a1f1..5e4e78fc0b3a 100644 --- a/fs/ext4/sysfs.c +++ b/fs/ext4/sysfs.c @@ -224,7 +224,7 @@ static struct attribute *ext4_attrs[] = { EXT4_ATTR_FEATURE(lazy_itable_init); EXT4_ATTR_FEATURE(batched_discard); EXT4_ATTR_FEATURE(meta_bg_resize); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION EXT4_ATTR_FEATURE(encryption); #endif EXT4_ATTR_FEATURE(metadata_csum_seed); @@ -233,7 +233,7 @@ static struct attribute *ext4_feat_attrs[] = { ATTR_LIST(lazy_itable_init), ATTR_LIST(batched_discard), ATTR_LIST(meta_bg_resize), -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION ATTR_LIST(encryption), #endif ATTR_LIST(metadata_csum_seed), diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index 9a20ef42fadd..e57cc754d543 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -3,6 +3,7 @@ config F2FS_FS depends on BLOCK select CRYPTO select CRYPTO_CRC32 + select F2FS_FS_XATTR if FS_ENCRYPTION help F2FS is based on Log-structured File System (LFS), which supports versatile "flash-friendly" features. The design has been focused on @@ -70,17 +71,6 @@ config F2FS_CHECK_FS If you want to improve the performance, say N. -config F2FS_FS_ENCRYPTION - bool "F2FS Encryption" - depends on F2FS_FS - depends on F2FS_FS_XATTR - select FS_ENCRYPTION - help - Enable encryption of f2fs files and directories. This - feature is similar to ecryptfs, but it is more memory - efficient since it avoids caching the encrypted and - decrypted pages in the page cache. - config F2FS_IO_TRACE bool "F2FS IO tracer" depends on F2FS_FS diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9ef6f38e51cc..95cc885ccb2f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -24,7 +24,6 @@ #include #include -#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION) #include #ifdef CONFIG_F2FS_CHECK_FS @@ -1137,7 +1136,7 @@ enum fsync_mode { FSYNC_MODE_NOBARRIER, /* fsync behaves nobarrier based on posix */ }; -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION #define DUMMY_ENCRYPTION_ENABLED(sbi) \ (unlikely(F2FS_OPTION(sbi).test_dummy_encryption)) #else @@ -3470,7 +3469,7 @@ static inline bool f2fs_encrypted_file(struct inode *inode) static inline void f2fs_set_encrypted_inode(struct inode *inode) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION file_set_encrypt(inode); f2fs_set_inode_flags(inode); #endif @@ -3549,7 +3548,7 @@ static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) static inline bool f2fs_may_encrypt(struct inode *inode) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION umode_t mode = inode->i_mode; return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c46a1d4318d4..0f3db3a8e5cb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -757,7 +757,7 @@ static int parse_options(struct super_block *sb, char *options) kvfree(name); break; case Opt_test_dummy_encryption: -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION if (!f2fs_sb_has_encrypt(sbi)) { f2fs_msg(sb, KERN_ERR, "Encrypt feature is off"); return -EINVAL; @@ -1390,7 +1390,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",whint_mode=%s", "user-based"); else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS) seq_printf(seq, ",whint_mode=%s", "fs-based"); -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION if (F2FS_OPTION(sbi).test_dummy_encryption) seq_puts(seq, ",test_dummy_encryption"); #endif @@ -2154,7 +2154,7 @@ static const struct super_operations f2fs_sops = { .remount_fs = f2fs_remount, }; -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION static int f2fs_get_context(struct inode *inode, void *ctx, size_t len) { return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, @@ -3116,7 +3116,7 @@ try_onemore: #endif sb->s_op = &f2fs_sops; -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION sb->s_cop = &f2fs_cryptops; #endif sb->s_xattr = f2fs_xattr_handlers; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 0575edbe3ed6..70da6801c86f 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -431,7 +431,7 @@ F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(features); F2FS_GENERAL_RO_ATTR(current_reserved_blocks); -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); #endif #ifdef CONFIG_BLK_DEV_ZONED @@ -492,7 +492,7 @@ static struct attribute *f2fs_attrs[] = { }; static struct attribute *f2fs_feat_attrs[] = { -#ifdef CONFIG_F2FS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION ATTR_LIST(encryption), #endif #ifdef CONFIG_BLK_DEV_ZONED diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig index bc1e082d921d..9da2f135121b 100644 --- a/fs/ubifs/Kconfig +++ b/fs/ubifs/Kconfig @@ -8,6 +8,7 @@ config UBIFS_FS select CRYPTO_LZO if UBIFS_FS_LZO select CRYPTO_DEFLATE if UBIFS_FS_ZLIB select CRYPTO_HASH_INFO + select UBIFS_FS_XATTR if FS_ENCRYPTION depends on MTD_UBI help UBIFS is a file system for flash devices which works on top of UBI. @@ -60,17 +61,6 @@ config UBIFS_FS_XATTR If unsure, say Y. -config UBIFS_FS_ENCRYPTION - bool "UBIFS Encryption" - depends on UBIFS_FS_XATTR && BLOCK - select FS_ENCRYPTION - default n - help - Enable encryption of UBIFS files and directories. This - feature is similar to ecryptfs, but it is more memory - efficient since it avoids caching the encrypted and - decrypted pages in the page cache. - config UBIFS_FS_SECURITY bool "UBIFS Security Labels" depends on UBIFS_FS_XATTR diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile index 5f838319c8d5..5c4b845754a7 100644 --- a/fs/ubifs/Makefile +++ b/fs/ubifs/Makefile @@ -6,6 +6,6 @@ ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o debug.o ubifs-y += misc.o -ubifs-$(CONFIG_UBIFS_FS_ENCRYPTION) += crypto.o +ubifs-$(CONFIG_FS_ENCRYPTION) += crypto.o ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 0164bcc827f8..0f9c362a3402 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -185,7 +185,7 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return err; } case FS_IOC_SET_ENCRYPTION_POLICY: { -#ifdef CONFIG_UBIFS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION struct ubifs_info *c = inode->i_sb->s_fs_info; err = ubifs_enable_encryption(c); @@ -198,7 +198,7 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #endif } case FS_IOC_GET_ENCRYPTION_POLICY: { -#ifdef CONFIG_UBIFS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION return fscrypt_ioctl_get_policy(file, (void __user *)arg); #else return -EOPNOTSUPP; diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c index 3da90c951c23..67fac1e8adfb 100644 --- a/fs/ubifs/sb.c +++ b/fs/ubifs/sb.c @@ -748,7 +748,7 @@ int ubifs_read_superblock(struct ubifs_info *c) goto out; } -#ifndef CONFIG_UBIFS_FS_ENCRYPTION +#ifndef CONFIG_FS_ENCRYPTION if (c->encrypted) { ubifs_err(c, "file system contains encrypted files but UBIFS" " was built without crypto support."); diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 1fac1133dadd..8dc2818fdd84 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -2146,7 +2146,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) #ifdef CONFIG_UBIFS_FS_XATTR sb->s_xattr = ubifs_xattr_handlers; #endif -#ifdef CONFIG_UBIFS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION sb->s_cop = &ubifs_crypt_operations; #endif diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 38401adaa00d..1ae12900e01d 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -43,7 +43,6 @@ #include #include -#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_UBIFS_FS_ENCRYPTION) #include #include "ubifs-media.h" @@ -142,7 +141,7 @@ */ #define WORST_COMPR_FACTOR 2 -#ifdef CONFIG_UBIFS_FS_ENCRYPTION +#ifdef CONFIG_FS_ENCRYPTION #define UBIFS_CIPHER_BLOCK_SIZE FS_CRYPTO_BLOCK_SIZE #else #define UBIFS_CIPHER_BLOCK_SIZE 0 @@ -2072,7 +2071,7 @@ int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len, #include "misc.h" #include "key.h" -#ifndef CONFIG_UBIFS_FS_ENCRYPTION +#ifndef CONFIG_FS_ENCRYPTION static inline int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn, unsigned int in_len, unsigned int *out_len, diff --git a/include/linux/fs.h b/include/linux/fs.h index 811c77743dad..ba7889bb9ef6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -698,7 +698,7 @@ struct inode { struct fsnotify_mark_connector __rcu *i_fsnotify_marks; #endif -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +#ifdef CONFIG_FS_ENCRYPTION struct fscrypt_info *i_crypt_info; #endif @@ -1403,7 +1403,7 @@ struct super_block { void *s_security; #endif const struct xattr_handler **s_xattr; -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +#ifdef CONFIG_FS_ENCRYPTION const struct fscrypt_operations *s_cop; #endif struct hlist_bl_head s_roots; /* alternate root dentries for NFS */ diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 952ab97af325..eec604840568 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -2,9 +2,8 @@ /* * fscrypt.h: declarations for per-file encryption * - * Filesystems that implement per-file encryption include this header - * file with the __FS_HAS_ENCRYPTION set according to whether that filesystem - * is being built with encryption support or not. + * Filesystems that implement per-file encryption must include this header + * file. * * Copyright (C) 2015, Google, Inc. * @@ -15,6 +14,8 @@ #define _LINUX_FSCRYPT_H #include +#include +#include #define FS_CRYPTO_BLOCK_SIZE 16 @@ -42,11 +43,410 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 -#if __FS_HAS_ENCRYPTION -#include -#else -#include -#endif +#ifdef CONFIG_FS_ENCRYPTION +/* + * fscrypt superblock flags + */ +#define FS_CFLG_OWN_PAGES (1U << 1) + +/* + * crypto operations for filesystems + */ +struct fscrypt_operations { + unsigned int flags; + const char *key_prefix; + int (*get_context)(struct inode *, void *, size_t); + int (*set_context)(struct inode *, const void *, size_t, void *); + bool (*dummy_context)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned int max_namelen; +}; + +struct fscrypt_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ +}; + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return (inode->i_crypt_info != NULL); +} + +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + return inode->i_sb->s_cop->dummy_context && + inode->i_sb->s_cop->dummy_context(inode); +} + +/* crypto.c */ +extern void fscrypt_enqueue_decrypt_work(struct work_struct *); +extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); +extern void fscrypt_release_ctx(struct fscrypt_ctx *); +extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, + unsigned int, unsigned int, + u64, gfp_t); +extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, + unsigned int, u64); + +static inline struct page *fscrypt_control_page(struct page *page) +{ + return ((struct fscrypt_ctx *)page_private(page))->w.control_page; +} + +extern void fscrypt_restore_control_page(struct page *); + +/* policy.c */ +extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); +extern int fscrypt_ioctl_get_policy(struct file *, void __user *); +extern int fscrypt_has_permitted_context(struct inode *, struct inode *); +extern int fscrypt_inherit_context(struct inode *, struct inode *, + void *, bool); +/* keyinfo.c */ +extern int fscrypt_get_encryption_info(struct inode *); +extern void fscrypt_put_encryption_info(struct inode *); + +/* fname.c */ +extern int fscrypt_setup_filename(struct inode *, const struct qstr *, + int lookup, struct fscrypt_name *); + +static inline void fscrypt_free_filename(struct fscrypt_name *fname) +{ + kfree(fname->crypto_buf.name); +} + +extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, + struct fscrypt_str *); +extern void fscrypt_fname_free_buffer(struct fscrypt_str *); +extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, + const struct fscrypt_str *, struct fscrypt_str *); + +#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 + +/* Extracts the second-to-last ciphertext block; see explanation below */ +#define FSCRYPT_FNAME_DIGEST(name, len) \ + ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \ + FS_CRYPTO_BLOCK_SIZE)) + +#define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE + +/** + * fscrypt_digested_name - alternate identifier for an on-disk filename + * + * When userspace lists an encrypted directory without access to the key, + * filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE + * bytes are shown in this abbreviated form (base64-encoded) rather than as the + * full ciphertext (base64-encoded). This is necessary to allow supporting + * filenames up to NAME_MAX bytes, since base64 encoding expands the length. + * + * To make it possible for filesystems to still find the correct directory entry + * despite not knowing the full on-disk name, we encode any filesystem-specific + * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups, + * followed by the second-to-last ciphertext block of the filename. Due to the + * use of the CBC-CTS encryption mode, the second-to-last ciphertext block + * depends on the full plaintext. (Note that ciphertext stealing causes the + * last two blocks to appear "flipped".) This makes accidental collisions very + * unlikely: just a 1 in 2^128 chance for two filenames to collide even if they + * share the same filesystem-specific hashes. + * + * However, this scheme isn't immune to intentional collisions, which can be + * created by anyone able to create arbitrary plaintext filenames and view them + * without the key. Making the "digest" be a real cryptographic hash like + * SHA-256 over the full ciphertext would prevent this, although it would be + * less efficient and harder to implement, especially since the filesystem would + * need to calculate it for each directory entry examined during a search. + */ +struct fscrypt_digested_name { + u32 hash; + u32 minor_hash; + u8 digest[FSCRYPT_FNAME_DIGEST_SIZE]; +}; + +/** + * fscrypt_match_name() - test whether the given name matches a directory entry + * @fname: the name being searched for + * @de_name: the name from the directory entry + * @de_name_len: the length of @de_name in bytes + * + * Normally @fname->disk_name will be set, and in that case we simply compare + * that to the name stored in the directory entry. The only exception is that + * if we don't have the key for an encrypted directory and a filename in it is + * very long, then we won't have the full disk_name and we'll instead need to + * match against the fscrypt_digested_name. + * + * Return: %true if the name matches, otherwise %false. + */ +static inline bool fscrypt_match_name(const struct fscrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + if (unlikely(!fname->disk_name.name)) { + const struct fscrypt_digested_name *n = + (const void *)fname->crypto_buf.name; + if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_')) + return false; + if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) + return false; + return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len), + n->digest, FSCRYPT_FNAME_DIGEST_SIZE); + } + + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); +} + +/* bio.c */ +extern void fscrypt_decrypt_bio(struct bio *); +extern void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, + struct bio *bio); +extern void fscrypt_pullback_bio_page(struct page **, bool); +extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, + unsigned int); + +/* hooks.c */ +extern int fscrypt_file_open(struct inode *inode, struct file *filp); +extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); +extern int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags); +extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry); +extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, + unsigned int max_len, + struct fscrypt_str *disk_link); +extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, + unsigned int len, + struct fscrypt_str *disk_link); +extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, + unsigned int max_size, + struct delayed_call *done); +#else /* !CONFIG_FS_ENCRYPTION */ + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return false; +} + +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + return false; +} + +/* crypto.c */ +static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work) +{ +} + +static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, + gfp_t gfp_flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx) +{ + return; +} + +static inline struct page *fscrypt_encrypt_page(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, + u64 lblk_num, gfp_t gfp_flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int fscrypt_decrypt_page(const struct inode *inode, + struct page *page, + unsigned int len, unsigned int offs, + u64 lblk_num) +{ + return -EOPNOTSUPP; +} + +static inline struct page *fscrypt_control_page(struct page *page) +{ + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +} + +static inline void fscrypt_restore_control_page(struct page *page) +{ + return; +} + +/* policy.c */ +static inline int fscrypt_ioctl_set_policy(struct file *filp, + const void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_has_permitted_context(struct inode *parent, + struct inode *child) +{ + return 0; +} + +static inline int fscrypt_inherit_context(struct inode *parent, + struct inode *child, + void *fs_data, bool preload) +{ + return -EOPNOTSUPP; +} + +/* keyinfo.c */ +static inline int fscrypt_get_encryption_info(struct inode *inode) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_put_encryption_info(struct inode *inode) +{ + return; +} + + /* fname.c */ +static inline int fscrypt_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct fscrypt_name *fname) +{ + if (IS_ENCRYPTED(dir)) + return -EOPNOTSUPP; + + memset(fname, 0, sizeof(struct fscrypt_name)); + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; +} + +static inline void fscrypt_free_filename(struct fscrypt_name *fname) +{ + return; +} + +static inline int fscrypt_fname_alloc_buffer(const struct inode *inode, + u32 max_encrypted_len, + struct fscrypt_str *crypto_str) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str) +{ + return; +} + +static inline int fscrypt_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) +{ + return -EOPNOTSUPP; +} + +static inline bool fscrypt_match_name(const struct fscrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + /* Encryption support disabled; use standard comparison */ + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); +} + +/* bio.c */ +static inline void fscrypt_decrypt_bio(struct bio *bio) +{ +} + +static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, + struct bio *bio) +{ +} + +static inline void fscrypt_pullback_bio_page(struct page **page, bool restore) +{ + return; +} + +static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + return -EOPNOTSUPP; +} + +/* hooks.c */ + +static inline int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + +static inline int __fscrypt_prepare_link(struct inode *inode, + struct inode *dir) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_prepare_symlink(struct inode *dir, + unsigned int len, + unsigned int max_len, + struct fscrypt_str *disk_link) +{ + return -EOPNOTSUPP; +} + + +static inline int __fscrypt_encrypt_symlink(struct inode *inode, + const char *target, + unsigned int len, + struct fscrypt_str *disk_link) +{ + return -EOPNOTSUPP; +} + +static inline const char *fscrypt_get_symlink(struct inode *inode, + const void *caddr, + unsigned int max_size, + struct delayed_call *done) +{ + return ERR_PTR(-EOPNOTSUPP); +} +#endif /* !CONFIG_FS_ENCRYPTION */ /** * fscrypt_require_key - require an inode's encryption key diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h deleted file mode 100644 index ee8b43e4c15a..000000000000 --- a/include/linux/fscrypt_notsupp.h +++ /dev/null @@ -1,231 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * fscrypt_notsupp.h - * - * This stubs out the fscrypt functions for filesystems configured without - * encryption support. - * - * Do not include this file directly. Use fscrypt.h instead! - */ -#ifndef _LINUX_FSCRYPT_H -#error "Incorrect include of linux/fscrypt_notsupp.h!" -#endif - -#ifndef _LINUX_FSCRYPT_NOTSUPP_H -#define _LINUX_FSCRYPT_NOTSUPP_H - -static inline bool fscrypt_has_encryption_key(const struct inode *inode) -{ - return false; -} - -static inline bool fscrypt_dummy_context_enabled(struct inode *inode) -{ - return false; -} - -/* crypto.c */ -static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work) -{ -} - -static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, - gfp_t gfp_flags) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx) -{ - return; -} - -static inline struct page *fscrypt_encrypt_page(const struct inode *inode, - struct page *page, - unsigned int len, - unsigned int offs, - u64 lblk_num, gfp_t gfp_flags) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline int fscrypt_decrypt_page(const struct inode *inode, - struct page *page, - unsigned int len, unsigned int offs, - u64 lblk_num) -{ - return -EOPNOTSUPP; -} - -static inline struct page *fscrypt_control_page(struct page *page) -{ - WARN_ON_ONCE(1); - return ERR_PTR(-EINVAL); -} - -static inline void fscrypt_restore_control_page(struct page *page) -{ - return; -} - -/* policy.c */ -static inline int fscrypt_ioctl_set_policy(struct file *filp, - const void __user *arg) -{ - return -EOPNOTSUPP; -} - -static inline int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) -{ - return -EOPNOTSUPP; -} - -static inline int fscrypt_has_permitted_context(struct inode *parent, - struct inode *child) -{ - return 0; -} - -static inline int fscrypt_inherit_context(struct inode *parent, - struct inode *child, - void *fs_data, bool preload) -{ - return -EOPNOTSUPP; -} - -/* keyinfo.c */ -static inline int fscrypt_get_encryption_info(struct inode *inode) -{ - return -EOPNOTSUPP; -} - -static inline void fscrypt_put_encryption_info(struct inode *inode) -{ - return; -} - - /* fname.c */ -static inline int fscrypt_setup_filename(struct inode *dir, - const struct qstr *iname, - int lookup, struct fscrypt_name *fname) -{ - if (IS_ENCRYPTED(dir)) - return -EOPNOTSUPP; - - memset(fname, 0, sizeof(struct fscrypt_name)); - fname->usr_fname = iname; - fname->disk_name.name = (unsigned char *)iname->name; - fname->disk_name.len = iname->len; - return 0; -} - -static inline void fscrypt_free_filename(struct fscrypt_name *fname) -{ - return; -} - -static inline int fscrypt_fname_alloc_buffer(const struct inode *inode, - u32 max_encrypted_len, - struct fscrypt_str *crypto_str) -{ - return -EOPNOTSUPP; -} - -static inline void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str) -{ - return; -} - -static inline int fscrypt_fname_disk_to_usr(struct inode *inode, - u32 hash, u32 minor_hash, - const struct fscrypt_str *iname, - struct fscrypt_str *oname) -{ - return -EOPNOTSUPP; -} - -static inline bool fscrypt_match_name(const struct fscrypt_name *fname, - const u8 *de_name, u32 de_name_len) -{ - /* Encryption support disabled; use standard comparison */ - if (de_name_len != fname->disk_name.len) - return false; - return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); -} - -/* bio.c */ -static inline void fscrypt_decrypt_bio(struct bio *bio) -{ -} - -static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, - struct bio *bio) -{ -} - -static inline void fscrypt_pullback_bio_page(struct page **page, bool restore) -{ - return; -} - -static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, - sector_t pblk, unsigned int len) -{ - return -EOPNOTSUPP; -} - -/* hooks.c */ - -static inline int fscrypt_file_open(struct inode *inode, struct file *filp) -{ - if (IS_ENCRYPTED(inode)) - return -EOPNOTSUPP; - return 0; -} - -static inline int __fscrypt_prepare_link(struct inode *inode, - struct inode *dir) -{ - return -EOPNOTSUPP; -} - -static inline int __fscrypt_prepare_rename(struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, - unsigned int flags) -{ - return -EOPNOTSUPP; -} - -static inline int __fscrypt_prepare_lookup(struct inode *dir, - struct dentry *dentry) -{ - return -EOPNOTSUPP; -} - -static inline int __fscrypt_prepare_symlink(struct inode *dir, - unsigned int len, - unsigned int max_len, - struct fscrypt_str *disk_link) -{ - return -EOPNOTSUPP; -} - -static inline int __fscrypt_encrypt_symlink(struct inode *inode, - const char *target, - unsigned int len, - struct fscrypt_str *disk_link) -{ - return -EOPNOTSUPP; -} - -static inline const char *fscrypt_get_symlink(struct inode *inode, - const void *caddr, - unsigned int max_size, - struct delayed_call *done) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -#endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h deleted file mode 100644 index 6456c6b2005f..000000000000 --- a/include/linux/fscrypt_supp.h +++ /dev/null @@ -1,204 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * fscrypt_supp.h - * - * Do not include this file directly. Use fscrypt.h instead! - */ -#ifndef _LINUX_FSCRYPT_H -#error "Incorrect include of linux/fscrypt_supp.h!" -#endif - -#ifndef _LINUX_FSCRYPT_SUPP_H -#define _LINUX_FSCRYPT_SUPP_H - -#include -#include - -/* - * fscrypt superblock flags - */ -#define FS_CFLG_OWN_PAGES (1U << 1) - -/* - * crypto operations for filesystems - */ -struct fscrypt_operations { - unsigned int flags; - const char *key_prefix; - int (*get_context)(struct inode *, void *, size_t); - int (*set_context)(struct inode *, const void *, size_t, void *); - bool (*dummy_context)(struct inode *); - bool (*empty_dir)(struct inode *); - unsigned int max_namelen; -}; - -struct fscrypt_ctx { - union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; - struct { - struct bio *bio; - struct work_struct work; - } r; - struct list_head free_list; /* Free list */ - }; - u8 flags; /* Flags */ -}; - -static inline bool fscrypt_has_encryption_key(const struct inode *inode) -{ - return (inode->i_crypt_info != NULL); -} - -static inline bool fscrypt_dummy_context_enabled(struct inode *inode) -{ - return inode->i_sb->s_cop->dummy_context && - inode->i_sb->s_cop->dummy_context(inode); -} - -/* crypto.c */ -extern void fscrypt_enqueue_decrypt_work(struct work_struct *); -extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); -extern void fscrypt_release_ctx(struct fscrypt_ctx *); -extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, - unsigned int, unsigned int, - u64, gfp_t); -extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, - unsigned int, u64); - -static inline struct page *fscrypt_control_page(struct page *page) -{ - return ((struct fscrypt_ctx *)page_private(page))->w.control_page; -} - -extern void fscrypt_restore_control_page(struct page *); - -/* policy.c */ -extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); -extern int fscrypt_ioctl_get_policy(struct file *, void __user *); -extern int fscrypt_has_permitted_context(struct inode *, struct inode *); -extern int fscrypt_inherit_context(struct inode *, struct inode *, - void *, bool); -/* keyinfo.c */ -extern int fscrypt_get_encryption_info(struct inode *); -extern void fscrypt_put_encryption_info(struct inode *); - -/* fname.c */ -extern int fscrypt_setup_filename(struct inode *, const struct qstr *, - int lookup, struct fscrypt_name *); - -static inline void fscrypt_free_filename(struct fscrypt_name *fname) -{ - kfree(fname->crypto_buf.name); -} - -extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, - struct fscrypt_str *); -extern void fscrypt_fname_free_buffer(struct fscrypt_str *); -extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, - const struct fscrypt_str *, struct fscrypt_str *); - -#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 - -/* Extracts the second-to-last ciphertext block; see explanation below */ -#define FSCRYPT_FNAME_DIGEST(name, len) \ - ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \ - FS_CRYPTO_BLOCK_SIZE)) - -#define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE - -/** - * fscrypt_digested_name - alternate identifier for an on-disk filename - * - * When userspace lists an encrypted directory without access to the key, - * filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE - * bytes are shown in this abbreviated form (base64-encoded) rather than as the - * full ciphertext (base64-encoded). This is necessary to allow supporting - * filenames up to NAME_MAX bytes, since base64 encoding expands the length. - * - * To make it possible for filesystems to still find the correct directory entry - * despite not knowing the full on-disk name, we encode any filesystem-specific - * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups, - * followed by the second-to-last ciphertext block of the filename. Due to the - * use of the CBC-CTS encryption mode, the second-to-last ciphertext block - * depends on the full plaintext. (Note that ciphertext stealing causes the - * last two blocks to appear "flipped".) This makes accidental collisions very - * unlikely: just a 1 in 2^128 chance for two filenames to collide even if they - * share the same filesystem-specific hashes. - * - * However, this scheme isn't immune to intentional collisions, which can be - * created by anyone able to create arbitrary plaintext filenames and view them - * without the key. Making the "digest" be a real cryptographic hash like - * SHA-256 over the full ciphertext would prevent this, although it would be - * less efficient and harder to implement, especially since the filesystem would - * need to calculate it for each directory entry examined during a search. - */ -struct fscrypt_digested_name { - u32 hash; - u32 minor_hash; - u8 digest[FSCRYPT_FNAME_DIGEST_SIZE]; -}; - -/** - * fscrypt_match_name() - test whether the given name matches a directory entry - * @fname: the name being searched for - * @de_name: the name from the directory entry - * @de_name_len: the length of @de_name in bytes - * - * Normally @fname->disk_name will be set, and in that case we simply compare - * that to the name stored in the directory entry. The only exception is that - * if we don't have the key for an encrypted directory and a filename in it is - * very long, then we won't have the full disk_name and we'll instead need to - * match against the fscrypt_digested_name. - * - * Return: %true if the name matches, otherwise %false. - */ -static inline bool fscrypt_match_name(const struct fscrypt_name *fname, - const u8 *de_name, u32 de_name_len) -{ - if (unlikely(!fname->disk_name.name)) { - const struct fscrypt_digested_name *n = - (const void *)fname->crypto_buf.name; - if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_')) - return false; - if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) - return false; - return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len), - n->digest, FSCRYPT_FNAME_DIGEST_SIZE); - } - - if (de_name_len != fname->disk_name.len) - return false; - return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); -} - -/* bio.c */ -extern void fscrypt_decrypt_bio(struct bio *); -extern void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, - struct bio *bio); -extern void fscrypt_pullback_bio_page(struct page **, bool); -extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, - unsigned int); - -/* hooks.c */ -extern int fscrypt_file_open(struct inode *inode, struct file *filp); -extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); -extern int __fscrypt_prepare_rename(struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, - unsigned int flags); -extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry); -extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, - unsigned int max_len, - struct fscrypt_str *disk_link); -extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, - unsigned int len, - struct fscrypt_str *disk_link); -extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, - unsigned int max_size, - struct delayed_call *done); - -#endif /* _LINUX_FSCRYPT_SUPP_H */ From f5e55e777cc93eae1416f0fa4908e8846b6d7825 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 22 Jan 2019 16:20:21 -0800 Subject: [PATCH 0617/4277] fscrypt: return -EXDEV for incompatible rename or link into encrypted dir Currently, trying to rename or link a regular file, directory, or symlink into an encrypted directory fails with EPERM when the source file is unencrypted or is encrypted with a different encryption policy, and is on the same mountpoint. It is correct for the operation to fail, but the choice of EPERM breaks tools like 'mv' that know to copy rather than rename if they see EXDEV, but don't know what to do with EPERM. Our original motivation for EPERM was to encourage users to securely handle their data. Encrypting files by "moving" them into an encrypted directory can be insecure because the unencrypted data may remain in free space on disk, where it can later be recovered by an attacker. It's much better to encrypt the data from the start, or at least try to securely delete the source data e.g. using the 'shred' program. However, the current behavior hasn't been effective at achieving its goal because users tend to be confused, hack around it, and complain; see e.g. https://github.com/google/fscrypt/issues/76. And in some cases it's actually inconsistent or unnecessary. For example, 'mv'-ing files between differently encrypted directories doesn't work even in cases where it can be secure, such as when in userspace the same passphrase protects both directories. Yet, you *can* already 'mv' unencrypted files into an encrypted directory if the source files are on a different mountpoint, even though doing so is often insecure. There are probably better ways to teach users to securely handle their files. For example, the 'fscrypt' userspace tool could provide a command that migrates unencrypted files into an encrypted directory, acting like 'shred' on the source files and providing appropriate warnings depending on the type of the source filesystem and disk. Receiving errors on unimportant files might also force some users to disable encryption, thus making the behavior counterproductive. It's desirable to make encryption as unobtrusive as possible. Therefore, change the error code from EPERM to EXDEV so that tools looking for EXDEV will fall back to a copy. This, of course, doesn't prevent users from still doing the right things to securely manage their files. Note that this also matches the behavior when a file is renamed between two project quota hierarchies; so there's precedent for using EXDEV for things other than mountpoints. xfstests generic/398 will require an update with this change. [Rewritten from an earlier patch series by Michael Halcrow.] Cc: Michael Halcrow Cc: Joe Richey Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 12 ++++++++++-- fs/crypto/hooks.c | 6 +++--- fs/crypto/policy.c | 3 +-- include/linux/fscrypt.h | 4 ++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 43dd989e2a3f..08c23b60e016 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -451,10 +451,18 @@ astute users may notice some differences in behavior: - Unencrypted files, or files encrypted with a different encryption policy (i.e. different key, modes, or flags), cannot be renamed or linked into an encrypted directory; see `Encryption policy - enforcement`_. Attempts to do so will fail with EPERM. However, + enforcement`_. Attempts to do so will fail with EXDEV. However, encrypted files can be renamed within an encrypted directory, or into an unencrypted directory. + Note: "moving" an unencrypted file into an encrypted directory, e.g. + with the `mv` program, is implemented in userspace by a copy + followed by a delete. Be aware that the original unencrypted data + may remain recoverable from free space on the disk; prefer to keep + all files encrypted from the very beginning. The `shred` program + may be used to overwrite the source files but isn't guaranteed to be + effective on all filesystems and storage devices. + - Direct I/O is not supported on encrypted files. Attempts to use direct I/O on such files will fall back to buffered I/O. @@ -541,7 +549,7 @@ not be encrypted. Except for those special files, it is forbidden to have unencrypted files, or files encrypted with a different encryption policy, in an encrypted directory tree. Attempts to link or rename such a file into -an encrypted directory will fail with EPERM. This is also enforced +an encrypted directory will fail with EXDEV. This is also enforced during ->lookup() to provide limited protection against offline attacks that try to disable or downgrade encryption in known locations where applications may later write sensitive data. It is recommended diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 926e5df20ec3..56debb1fcf5e 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -58,7 +58,7 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) return err; if (!fscrypt_has_permitted_context(dir, inode)) - return -EPERM; + return -EXDEV; return 0; } @@ -82,13 +82,13 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, if (IS_ENCRYPTED(new_dir) && !fscrypt_has_permitted_context(new_dir, d_inode(old_dentry))) - return -EPERM; + return -EXDEV; if ((flags & RENAME_EXCHANGE) && IS_ENCRYPTED(old_dir) && !fscrypt_has_permitted_context(old_dir, d_inode(new_dentry))) - return -EPERM; + return -EXDEV; } return 0; } diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index f490de921ce8..bd7eaf9b3f00 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -151,8 +151,7 @@ EXPORT_SYMBOL(fscrypt_ioctl_get_policy); * malicious offline violations of this constraint, while the link and rename * checks are needed to prevent online violations of this constraint. * - * Return: 1 if permitted, 0 if forbidden. If forbidden, the caller must fail - * the filesystem operation with EPERM. + * Return: 1 if permitted, 0 if forbidden. */ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index eec604840568..e5194fc3983e 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -489,7 +489,7 @@ static inline int fscrypt_require_key(struct inode *inode) * in an encrypted directory tree use the same encryption policy. * * Return: 0 on success, -ENOKEY if the directory's encryption key is missing, - * -EPERM if the link would result in an inconsistent encryption policy, or + * -EXDEV if the link would result in an inconsistent encryption policy, or * another -errno code. */ static inline int fscrypt_prepare_link(struct dentry *old_dentry, @@ -519,7 +519,7 @@ static inline int fscrypt_prepare_link(struct dentry *old_dentry, * We also verify that the rename will not violate the constraint that all files * in an encrypted directory tree use the same encryption policy. * - * Return: 0 on success, -ENOKEY if an encryption key is missing, -EPERM if the + * Return: 0 on success, -ENOKEY if an encryption key is missing, -EXDEV if the * rename would cause inconsistent encryption policies, or another -errno code. */ static inline int fscrypt_prepare_rename(struct inode *old_dir, From c61a4633a56aaa85bd61645c4188340f8bb2f7f1 Mon Sep 17 00:00:00 2001 From: Shaokun Zhang Date: Thu, 24 Jan 2019 15:10:02 +0800 Subject: [PATCH 0618/4277] iommu/dma: Remove unused variable end_pfn is never used after commit ('iommu/iova: Make dma 32bit pfn implicit'), cleanup it. Cc: Joerg Roedel Cc: Robin Murphy Cc: Zhen Lei Signed-off-by: Shaokun Zhang Signed-off-by: Joerg Roedel --- drivers/iommu/dma-iommu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index d19f3d6b43c1..77aabe637a60 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -289,7 +289,7 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, { struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iova_domain *iovad = &cookie->iovad; - unsigned long order, base_pfn, end_pfn; + unsigned long order, base_pfn; int attr; if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE) @@ -298,7 +298,6 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, /* Use the smallest supported page size for IOVA granularity */ order = __ffs(domain->pgsize_bitmap); base_pfn = max_t(unsigned long, 1, base >> order); - end_pfn = (base + size - 1) >> order; /* Check the domain allows at least some access to the device... */ if (domain->geometry.force_aperture) { From 12a9eeaebba34f4f0abe1548ecb460414e285c49 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:41 -0500 Subject: [PATCH 0619/4277] spmi: pmic-arb: convert to v2 irq interfaces to support hierarchical IRQ chips Convert the spmi-pmic-arb IRQ code to use the version 2 IRQ interface in order to support hierarchical IRQ chips. This is necessary so that spmi-gpio can be setup as a hierarchical IRQ chip with pmic-arb as the parent. IRQ chips in device tree should be usable from the start without the consumer having to make an additional call to gpio[d]_to_irq() to get the proper IRQ on the parent. The old qpnpint_irq_domain_map function would hardcode the handler as handle_level_irq, however qpnpint_irq_set_type would later override the handler. Properly set the handler when the IRQ is mapped. This new code doesn't return an error for IRQ_TYPE_NONE and preserves the existing behavior of using handle_level_irq since there are some broken device tree bindings that need to be corrected first. Driver was tested on a LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Marc Zyngier Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 67 +++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 360b8218f322..928759242e42 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, return 0; } -static int qpnpint_irq_request_resources(struct irq_data *d) +static int qpnpint_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool reserve) { struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); u16 periph = hwirq_to_per(d->hwirq); @@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = { .irq_set_type = qpnpint_irq_set_type, .irq_set_wake = qpnpint_irq_set_wake, .irq_get_irqchip_state = qpnpint_get_irqchip_state, - .irq_request_resources = qpnpint_irq_request_resources, .flags = IRQCHIP_MASK_ON_SUSPEND, }; -static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, - unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) +static int qpnpint_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) { struct spmi_pmic_arb *pmic_arb = d->host_data; + u32 *intspec = fwspec->param; u16 apid, ppid; int rc; dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", intspec[0], intspec[1], intspec[2]); - if (irq_domain_get_of_node(d) != controller) + if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node) return -EINVAL; - if (intsize != 4) + if (fwspec->param_count != 4) return -EINVAL; if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) return -EINVAL; @@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, return 0; } -static int qpnpint_irq_domain_map(struct irq_domain *d, - unsigned int virq, - irq_hw_number_t hwirq) + +static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { - struct spmi_pmic_arb *pmic_arb = d->host_data; + irq_flow_handler_t handler; - dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); + dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", + virq, hwirq, type); + + if (type & IRQ_TYPE_EDGE_BOTH) + handler = handle_edge_irq; + else + handler = handle_level_irq; + + irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, + handler, NULL, NULL); +} + +static int qpnpint_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + struct spmi_pmic_arb *pmic_arb = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, + type); - irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); - irq_set_chip_data(virq, d->host_data); - irq_set_noprobe(virq); return 0; } @@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { - .map = qpnpint_irq_domain_map, - .xlate = qpnpint_irq_domain_dt_translate, + .activate = qpnpint_irq_domain_activate, + .alloc = qpnpint_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = qpnpint_irq_domain_translate, }; static int spmi_pmic_arb_probe(struct platform_device *pdev) From ef74f70e5a10cc2a78cc5529e564170cabcda9af Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:42 -0500 Subject: [PATCH 0620/4277] gpio: add irq domain activate/deactivate functions This adds the two new functions gpiochip_irq_domain_activate and gpiochip_irq_domain_deactivate that can be used as the activate and deactivate functions in the struct irq_domain_ops. This is for situations where only gpiochip_{lock,unlock}_as_irq needs to be called. SPMI and SSBI GPIO are two users that will initially use these functions. Signed-off-by: Brian Masney Suggested-by: Stephen Boyd Reviewed-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/gpio/driver.h | 5 +++++ 2 files changed, 42 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1651d7f0a303..361a09c8138a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1775,6 +1775,43 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/** + * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * @reserve: If set, only reserve an interrupt vector instead of assigning one + * + * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be + * used as the activate function for the &struct irq_domain_ops. The host_data + * for the IRQ domain must be the &struct gpio_chip. + */ +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_lock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate); + +/** + * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * + * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to + * be used as the deactivate function for the &struct irq_domain_ops. The + * host_data for the IRQ domain must be the &struct gpio_chip. + */ +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + struct gpio_chip *chip = domain->host_data; + + return gpiochip_unlock_as_irq(chip, data->hwirq); +} +EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); + static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { if (!gpiochip_irqchip_irq_valid(chip, offset)) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 07cddbf45186..01497910f023 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -472,6 +472,11 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq); void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq); +int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve); +void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data); + void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq, From 682aefaa81e6c9b76bf45391f92a2b9c6ec96a0c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:43 -0500 Subject: [PATCH 0621/4277] spmi: pmic-arb: disassociate old virq if hwirq mapping already exists Check to see if the hwirq is already associated with another virq on this IRQ domain. If so, then disassociate it before associating the hwirq with the new virq. This is a temporary hack that is needed in order to not break git bisect for existing boards. The next patch in this series converts spmi-gpio to be a hierarchical IRQ chip, then there are several patches to update all of the device tree files, and finally this patch will be reverted within the same patch series. IRQs for spmi-gpio are all initially setup without an IRQ hierarchy on pmic-arb when mfd/qcom-spmi-pmic.c is probed (via the devm_of_platform_populate call) due to the interrupts property in device tree. Once spmi-gpio is converted to be a hierarchical IRQ chip in the next patch, existing users of gpio[d]_to_irq() will call pmic_gpio_to_irq(), and that will use the new IRQ chip code in spmi-gpio that sets up the IRQ in an IRQ hierarchy. The hwirq is now associated with two Linux virqs and interrupts will not work as expected. This patch corrects that issue. Driver was tested using gpio-keys and iadc/vadc on the LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Acked-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 928759242e42..bc7dc1d6e4b2 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -745,10 +745,15 @@ static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; + unsigned int old_virq; dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", virq, hwirq, type); + old_virq = irq_find_mapping(domain, hwirq); + if (old_virq) + irq_domain_disassociate(domain, old_virq); + if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; else From ca69e2d165eb3d060cc9ad70a745e27a2cf4310b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:44 -0500 Subject: [PATCH 0622/4277] qcom: spmi-gpio: add support for hierarchical IRQ chip spmi-gpio did not have any irqchip support so consumers of this in device tree would need to call gpio[d]_to_irq() in order to get the proper IRQ on the underlying PMIC. IRQ chips in device tree should be usable from the start without the consumer having to make an additional call to get the proper IRQ on the parent. This patch adds hierarchical IRQ chip support to the spmi-gpio code to correct this issue. Driver was tested using the volume buttons (via gpio-keys) on the LG Nexus 5 (hammerhead) phone with the following two configurations. volume-up { interrupts-extended = <&pm8941_gpios 2 IRQ_TYPE_EDGE_BOTH>; ... }; volume-up { gpios = <&pm8941_gpios 2 GPIO_ACTIVE_LOW>; ... }; Both configurations now show that spmi-gpio is the IRQ domain and that the IRQ is setup in a hierarchy. $ grep volume_up /proc/interrupts 72: 6 0 spmi-gpio 1 Edge volume_up $ cat /sys/kernel/debug/irq/irqs/72 handler: handle_edge_irq device: (null) status: 0x00000403 _IRQ_NOPROBE istate: 0x00000000 ddepth: 0 wdepth: 0 dstate: 0x02400203 IRQ_TYPE_EDGE_RISING IRQ_TYPE_EDGE_FALLING IRQD_ACTIVATED IRQD_IRQ_STARTED node: 0 affinity: 0-3 effectiv: domain: :soc:spmi@fc4cf000:pm8941@0:gpios@c000 hwirq: 0x1 chip: spmi-gpio flags: 0x4 IRQCHIP_MASK_ON_SUSPEND parent: domain: :soc:spmi@fc4cf000 hwirq: 0xc100057 chip: pmic_arb flags: 0x4 IRQCHIP_MASK_ON_SUSPEND Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Marc Zyngier Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 115 +++++++++++++++++++++-- 1 file changed, 105 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index b6fa2c7dbb26..e01a556ac586 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -136,7 +137,6 @@ enum pmic_gpio_func_index { /** * struct pmic_gpio_pad - keep current GPIO settings * @base: Address base in SPMI device. - * @irq: IRQ number which this GPIO generate. * @is_enabled: Set to false when GPIO should be put in high Z state. * @out_value: Cached pin output value * @have_buffer: Set to true if GPIO output could be configured in push-pull, @@ -156,7 +156,6 @@ enum pmic_gpio_func_index { */ struct pmic_gpio_pad { u16 base; - int irq; bool is_enabled; bool out_value; bool have_buffer; @@ -179,6 +178,8 @@ struct pmic_gpio_state { struct regmap *map; struct pinctrl_dev *ctrl; struct gpio_chip chip; + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pmic_gpio_bindings[] = { @@ -761,11 +762,18 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip, static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) { struct pmic_gpio_state *state = gpiochip_get_data(chip); - struct pmic_gpio_pad *pad; + struct irq_fwspec fwspec; - pad = state->ctrl->desc->pins[pin].drv_data; + fwspec.fwnode = state->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET; + /* + * Set the type to a safe value temporarily. This will be overwritten + * later with the proper value by irq_set_type. + */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - return pad->irq; + return irq_create_fwspec_mapping(&fwspec); } static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -935,8 +943,78 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, return 0; } +static struct irq_chip pmic_gpio_irq_chip = { + .name = "spmi-gpio", + .irq_ack = irq_chip_ack_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = irq_chip_set_wake_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, +}; + +static int pmic_gpio_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + + if (fwspec->param_count != 2 || fwspec->param[0] >= state->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pmic_gpio_state *state = container_of(domain->host_data, + struct pmic_gpio_state, + chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pmic_gpio_irq_chip, state, + handle_level_irq, NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 4; + parent_fwspec.param[0] = 0; + parent_fwspec.param[1] = hwirq + 0xc0; + parent_fwspec.param[2] = 0; + parent_fwspec.param[3] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pmic_gpio_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pmic_gpio_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pmic_gpio_domain_translate, +}; + static int pmic_gpio_probe(struct platform_device *pdev) { + struct irq_domain *parent_domain; + struct device_node *parent_node; struct device *dev = &pdev->dev; struct pinctrl_pin_desc *pindesc; struct pinctrl_desc *pctrldesc; @@ -993,10 +1071,6 @@ static int pmic_gpio_probe(struct platform_device *pdev) pindesc->number = i; pindesc->name = pmic_gpio_groups[i]; - pad->irq = platform_get_irq(pdev, i); - if (pad->irq < 0) - return pad->irq; - pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; ret = pmic_gpio_populate(state, pad); @@ -1016,10 +1090,28 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (IS_ERR(state->ctrl)) return PTR_ERR(state->ctrl); + parent_node = of_irq_find_parent(state->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + state->fwnode = of_node_to_fwnode(state->dev->of_node); + state->domain = irq_domain_create_hierarchy(parent_domain, 0, + state->chip.ngpio, + state->fwnode, + &pmic_gpio_domain_ops, + &state->chip); + if (!state->domain) + return -ENODEV; + ret = gpiochip_add_data(&state->chip, state); if (ret) { dev_err(state->dev, "can't add gpio chip\n"); - return ret; + goto err_chip_add_data; } /* @@ -1045,6 +1137,8 @@ static int pmic_gpio_probe(struct platform_device *pdev) err_range: gpiochip_remove(&state->chip); +err_chip_add_data: + irq_domain_remove(state->domain); return ret; } @@ -1053,6 +1147,7 @@ static int pmic_gpio_remove(struct platform_device *pdev) struct pmic_gpio_state *state = platform_get_drvdata(pdev); gpiochip_remove(&state->chip); + irq_domain_remove(state->domain); return 0; } From 5f540fb4821a5444350ab3311fff60013d755d8f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:45 -0500 Subject: [PATCH 0623/4277] ARM: dts: qcom: pm8941: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. Code was tested on the LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-pm8941.dtsi | 38 ++---------------------------- 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/arch/arm/boot/dts/qcom-pm8941.dtsi b/arch/arm/boot/dts/qcom-pm8941.dtsi index 2515c5c217ac..e65049e0fb45 100644 --- a/arch/arm/boot/dts/qcom-pm8941.dtsi +++ b/arch/arm/boot/dts/qcom-pm8941.dtsi @@ -64,42 +64,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>, - <0 0xda 0 IRQ_TYPE_NONE>, - <0 0xdb 0 IRQ_TYPE_NONE>, - <0 0xdc 0 IRQ_TYPE_NONE>, - <0 0xdd 0 IRQ_TYPE_NONE>, - <0 0xde 0 IRQ_TYPE_NONE>, - <0 0xdf 0 IRQ_TYPE_NONE>, - <0 0xe0 0 IRQ_TYPE_NONE>, - <0 0xe1 0 IRQ_TYPE_NONE>, - <0 0xe2 0 IRQ_TYPE_NONE>, - <0 0xe3 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; boost_bypass_n_pin: boost-bypass { pins = "gpio21"; From c9a0ef552894ebdd6e4efc064ef10ddcb578a42f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:46 -0500 Subject: [PATCH 0624/4277] ARM: dts: qcom: pma8084: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-pma8084.dtsi | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/arch/arm/boot/dts/qcom-pma8084.dtsi b/arch/arm/boot/dts/qcom-pma8084.dtsi index aac7e73b6872..8f5ea7add20f 100644 --- a/arch/arm/boot/dts/qcom-pma8084.dtsi +++ b/arch/arm/boot/dts/qcom-pma8084.dtsi @@ -32,28 +32,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; pma8084_mpps: mpps@a000 { From a61326c076f25b7b808cccb827576808ec1cfa9d Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:47 -0500 Subject: [PATCH 0625/4277] arm64: dts: qcom: pm8005: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pm8005.dtsi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pm8005.dtsi b/arch/arm64/boot/dts/qcom/pm8005.dtsi index 4d5aca3eeb69..c0ddf128136c 100644 --- a/arch/arm64/boot/dts/qcom/pm8005.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8005.dtsi @@ -16,10 +16,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From a1738363e41a65733a0d277d8189efa98315fdff Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:48 -0500 Subject: [PATCH 0626/4277] arm64: dts: qcom: pm8998: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pm8998.dtsi | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi index f1025a50c227..43cb5ea14089 100644 --- a/arch/arm64/boot/dts/qcom/pm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi @@ -94,32 +94,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>, - <0 0xce 0 IRQ_TYPE_NONE>, - <0 0xcf 0 IRQ_TYPE_NONE>, - <0 0xd0 0 IRQ_TYPE_NONE>, - <0 0xd1 0 IRQ_TYPE_NONE>, - <0 0xd2 0 IRQ_TYPE_NONE>, - <0 0xd3 0 IRQ_TYPE_NONE>, - <0 0xd4 0 IRQ_TYPE_NONE>, - <0 0xd5 0 IRQ_TYPE_NONE>, - <0 0xd6 0 IRQ_TYPE_NONE>, - <0 0xd7 0 IRQ_TYPE_NONE>, - <0 0xd8 0 IRQ_TYPE_NONE>, - <0 0xd9 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From 8cff9c8a78810ca09fd7c4d6d5d4f86a8b113059 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:49 -0500 Subject: [PATCH 0627/4277] arm64: dts: qcom: pmi8994: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pmi8994.dtsi | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pmi8994.dtsi b/arch/arm64/boot/dts/qcom/pmi8994.dtsi index dae1cdc23f54..3aee10e3f921 100644 --- a/arch/arm64/boot/dts/qcom/pmi8994.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8994.dtsi @@ -15,16 +15,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <2 0xc0 0 IRQ_TYPE_NONE>, - <2 0xc1 0 IRQ_TYPE_NONE>, - <2 0xc2 0 IRQ_TYPE_NONE>, - <2 0xc3 0 IRQ_TYPE_NONE>, - <2 0xc4 0 IRQ_TYPE_NONE>, - <2 0xc5 0 IRQ_TYPE_NONE>, - <2 0xc6 0 IRQ_TYPE_NONE>, - <2 0xc7 0 IRQ_TYPE_NONE>, - <2 0xc8 0 IRQ_TYPE_NONE>, - <2 0xc9 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From f14a5e6da4a50e78a18384faf81eef6171909d3f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:50 -0500 Subject: [PATCH 0628/4277] arm64: dts: qcom: pmi8998: add interrupt controller properties Add interrupt controller properties now that spmi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on qcom-pm8941.dtsi using a LG Nexus 5 (hammerhead) phone with no issues. Signed-off-by: Brian Masney Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index da3285e216e2..051f57e7d6ac 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -14,20 +14,8 @@ reg = <0xc000>; gpio-controller; #gpio-cells = <2>; - interrupts = <0 0xc0 0 IRQ_TYPE_NONE>, - <0 0xc1 0 IRQ_TYPE_NONE>, - <0 0xc2 0 IRQ_TYPE_NONE>, - <0 0xc3 0 IRQ_TYPE_NONE>, - <0 0xc4 0 IRQ_TYPE_NONE>, - <0 0xc5 0 IRQ_TYPE_NONE>, - <0 0xc6 0 IRQ_TYPE_NONE>, - <0 0xc7 0 IRQ_TYPE_NONE>, - <0 0xc8 0 IRQ_TYPE_NONE>, - <0 0xc9 0 IRQ_TYPE_NONE>, - <0 0xca 0 IRQ_TYPE_NONE>, - <0 0xcb 0 IRQ_TYPE_NONE>, - <0 0xcc 0 IRQ_TYPE_NONE>, - <0 0xcd 0 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; }; }; From 135ef21ab064dd9811b51d5fa225829011adb10c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:51 -0500 Subject: [PATCH 0629/4277] spmi: pmic-arb: validate type when mapping IRQ qpnpint_irq_domain_map did not validate the IRQ type and this can cause IRQs to not work as expected if an unsupported type (such as IRQ_TYPE_NONE) is passed in. Now that spmi-gpio is a hierarchical IRQ controller, and all device tree bindings have been updated, add additional validation to the type field. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index bc7dc1d6e4b2..5666b99c9a92 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -740,9 +740,9 @@ static int qpnpint_irq_domain_translate(struct irq_domain *d, } -static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, - struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq, unsigned int type) +static int qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; unsigned int old_virq; @@ -756,11 +756,15 @@ static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; - else + else if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) handler = handle_level_irq; + else + return -EINVAL; irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, handler, NULL, NULL); + + return 0; } static int qpnpint_irq_domain_alloc(struct irq_domain *domain, @@ -777,9 +781,12 @@ static int qpnpint_irq_domain_alloc(struct irq_domain *domain, if (ret) return ret; - for (i = 0; i < nr_irqs; i++) - qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, - type); + for (i = 0; i < nr_irqs; i++) { + ret = qpnpint_irq_domain_map(pmic_arb, domain, virq + i, + hwirq + i, type); + if (ret) + return ret; + } return 0; } From e7dc6af82c284b8c79a0be5d2c9b555c3d793a3e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sat, 19 Jan 2019 15:42:52 -0500 Subject: [PATCH 0630/4277] spmi: pmic-arb: revert "disassociate old virq if hwirq mapping already exists" Now that spmi-gpio is a proper hierarchical IRQ chip, and all in-tree users of device tree have been updated, we can now drop the hack that was introduced to disassociate the old Linux virq if a hwirq mapping already exists. That patch was introduced to not break git bisect for any existing boards. Driver was tested using gpio-keys and iadc/vadc on the LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 5666b99c9a92..316e2708f3ba 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -745,15 +745,10 @@ static int qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; - unsigned int old_virq; dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n", virq, hwirq, type); - old_virq = irq_find_mapping(domain, hwirq); - if (old_virq) - irq_domain_disassociate(domain, old_virq); - if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; else if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) From d9286d9743b6e8cfbf1a13f8db84e9c65c0b09c7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 21 Jan 2019 14:07:39 +0100 Subject: [PATCH 0631/4277] clk: renesas: r8a774c0: Correct parent clock of DU According to the RZ/G Series, 2nd Generation Hardware Manual Rev 0.61, the parent clock of the DU module clocks on RZ/G2E is S1D1. Fixes: 906e0a4a6d1ef2d3 ("clk: renesas: cpg-mssr: Add r8a774c0 support") Signed-off-by: Geert Uytterhoeven Reviewed-by: Chris Paterson Reviewed-by: Simon Horman --- drivers/clk/renesas/r8a774c0-cpg-mssr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/renesas/r8a774c0-cpg-mssr.c b/drivers/clk/renesas/r8a774c0-cpg-mssr.c index 28bcc8105d57..4f3111b3113e 100644 --- a/drivers/clk/renesas/r8a774c0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774c0-cpg-mssr.c @@ -175,8 +175,8 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { DEF_MOD("ehci0", 703, R8A774C0_CLK_S3D4), DEF_MOD("hsusb", 704, R8A774C0_CLK_S3D4), DEF_MOD("csi40", 716, R8A774C0_CLK_CSI0), - DEF_MOD("du1", 723, R8A774C0_CLK_S2D1), - DEF_MOD("du0", 724, R8A774C0_CLK_S2D1), + DEF_MOD("du1", 723, R8A774C0_CLK_S1D1), + DEF_MOD("du0", 724, R8A774C0_CLK_S1D1), DEF_MOD("lvds", 727, R8A774C0_CLK_S2D1), DEF_MOD("vin5", 806, R8A774C0_CLK_S1D2), From 039d713a59c8fcfdf0f89efcce131e15a34284e8 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Tue, 22 Jan 2019 08:31:20 +0200 Subject: [PATCH 0632/4277] IB/umad: Do not check status of nonseekable_open() As the comment block of nonseekable_open() describes, nonseekable_open() can never fail. Several places in kernel depend on this behavior. Therefore, simplify the umad module to depend on this basic kernel functionality. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Reviewed-by: Ira Weiny Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/user_mad.c | 36 +++++++++++------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index 097f153f0f22..3ebd211a87ed 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -957,19 +957,22 @@ static int ib_umad_open(struct inode *inode, struct file *filp) { struct ib_umad_port *port; struct ib_umad_file *file; - int ret = -ENXIO; + int ret = 0; port = container_of(inode->i_cdev, struct ib_umad_port, cdev); mutex_lock(&port->file_mutex); - if (!port->ib_dev) + if (!port->ib_dev) { + ret = -ENXIO; goto out; + } - ret = -ENOMEM; - file = kzalloc(sizeof *file, GFP_KERNEL); - if (!file) + file = kzalloc(sizeof(*file), GFP_KERNEL); + if (!file) { + ret = -ENOMEM; goto out; + } mutex_init(&file->mutex); spin_lock_init(&file->send_lock); @@ -982,13 +985,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp) list_add_tail(&file->port_list, &port->file_list); - ret = nonseekable_open(inode, filp); - if (ret) { - list_del(&file->port_list); - kfree(file); - goto out; - } - + nonseekable_open(inode, filp); out: mutex_unlock(&port->file_mutex); return ret; @@ -1070,16 +1067,9 @@ static int ib_umad_sm_open(struct inode *inode, struct file *filp) filp->private_data = port; - ret = nonseekable_open(inode, filp); - if (ret) - goto err_clr_sm_cap; - + nonseekable_open(inode, filp); return 0; -err_clr_sm_cap: - swap(props.set_port_cap_mask, props.clr_port_cap_mask); - ib_modify_port(port->ib_dev, port->port_num, 0, &props); - err_up_sem: up(&port->sm_sem); @@ -1278,12 +1268,12 @@ static void ib_umad_kill_port(struct ib_umad_port *port) mutex_unlock(&port->file_mutex); cdev_device_del(&port->sm_cdev, &port->sm_dev); + cdev_device_del(&port->cdev, &port->dev); + ida_free(&umad_ida, port->dev_num); + /* balances device_initialize() */ put_device(&port->sm_dev); - cdev_device_del(&port->cdev, &port->dev); - /* balances device_initialize() */ put_device(&port->dev); - ida_free(&umad_ida, port->dev_num); } static void ib_umad_add_one(struct ib_device *device) From 8283d7872576784c49446532cc87143b145d537b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:53 +0100 Subject: [PATCH 0633/4277] infiniband: cxgb4: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Acked-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/device.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index c13c0ba30f63..9c10fff6dcfb 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -720,11 +720,8 @@ static const struct file_operations ep_debugfs_fops = { .read = debugfs_read, }; -static int setup_debugfs(struct c4iw_dev *devp) +static void setup_debugfs(struct c4iw_dev *devp) { - if (!devp->debugfs_root) - return -1; - debugfs_create_file_size("qps", S_IWUSR, devp->debugfs_root, (void *)devp, &qp_debugfs_fops, 4096); @@ -740,7 +737,6 @@ static int setup_debugfs(struct c4iw_dev *devp) if (c4iw_wr_log) debugfs_create_file_size("wr_log", S_IWUSR, devp->debugfs_root, (void *)devp, &wr_log_debugfs_fops, 4096); - return 0; } void c4iw_release_dev_ucontext(struct c4iw_rdev *rdev, @@ -1553,8 +1549,6 @@ static int __init c4iw_init_module(void) return err; c4iw_debugfs_root = debugfs_create_dir(DRV_NAME, NULL); - if (!c4iw_debugfs_root) - pr_warn("could not create debugfs entry, continuing\n"); reg_workq = create_singlethread_workqueue("Register_iWARP_device"); if (!reg_workq) { From 5c43276499f912ae7aec06737a3c6e0f0f3ba74b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:54 +0100 Subject: [PATCH 0634/4277] infiniband: hfi1: drop crazy DEBUGFS_SEQ_FILE_CREATE() macro The macro was just making things harder to follow, and audit, so remove it and call debugfs_create_file() directly. Also, the macro did not need to warn about the call failing as no one should ever care about any debugfs functions failing. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hfi1/debugfs.c | 56 ++++++++++++++-------------- drivers/infiniband/hw/hfi1/debugfs.h | 12 ------ drivers/infiniband/hw/hfi1/fault.c | 3 +- 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/debugfs.c b/drivers/infiniband/hw/hfi1/debugfs.c index 0a557795563c..aeb0f07103de 100644 --- a/drivers/infiniband/hw/hfi1/debugfs.c +++ b/drivers/infiniband/hw/hfi1/debugfs.c @@ -1167,6 +1167,7 @@ void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd) char link[10]; struct hfi1_devdata *dd = dd_from_dev(ibd); struct hfi1_pportdata *ppd; + struct dentry *root; int unit = dd->unit; int i, j; @@ -1174,31 +1175,29 @@ void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd) return; snprintf(name, sizeof(name), "%s_%d", class_name(), unit); snprintf(link, sizeof(link), "%d", unit); - ibd->hfi1_ibdev_dbg = debugfs_create_dir(name, hfi1_dbg_root); - if (!ibd->hfi1_ibdev_dbg) { - pr_warn("create of %s failed\n", name); - return; - } + root = debugfs_create_dir(name, hfi1_dbg_root); + ibd->hfi1_ibdev_dbg = root; + ibd->hfi1_ibdev_link = debugfs_create_symlink(link, hfi1_dbg_root, name); - if (!ibd->hfi1_ibdev_link) { - pr_warn("create of %s symlink failed\n", name); - return; - } - DEBUGFS_SEQ_FILE_CREATE(opcode_stats, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(tx_opcode_stats, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(ctx_stats, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(qp_stats, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(sdes, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(rcds, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(pios, ibd->hfi1_ibdev_dbg, ibd); - DEBUGFS_SEQ_FILE_CREATE(sdma_cpu_list, ibd->hfi1_ibdev_dbg, ibd); + + debugfs_create_file("opcode_stats", 0444, root, ibd, + &_opcode_stats_file_ops); + debugfs_create_file("tx_opcode_stats", 0444, root, ibd, + &_tx_opcode_stats_file_ops); + debugfs_create_file("ctx_stats", 0444, root, ibd, &_ctx_stats_file_ops); + debugfs_create_file("qp_stats", 0444, root, ibd, &_qp_stats_file_ops); + debugfs_create_file("sdes", 0444, root, ibd, &_sdes_file_ops); + debugfs_create_file("rcds", 0444, root, ibd, &_rcds_file_ops); + debugfs_create_file("pios", 0444, root, ibd, &_pios_file_ops); + debugfs_create_file("sdma_cpu_list", 0444, root, ibd, + &_sdma_cpu_list_file_ops); + /* dev counter files */ for (i = 0; i < ARRAY_SIZE(cntr_ops); i++) - DEBUGFS_FILE_CREATE(cntr_ops[i].name, - ibd->hfi1_ibdev_dbg, - dd, - &cntr_ops[i].ops, S_IRUGO); + debugfs_create_file(cntr_ops[i].name, 0444, root, dd, + &cntr_ops[i].ops); + /* per port files */ for (ppd = dd->pport, j = 0; j < dd->num_pports; j++, ppd++) for (i = 0; i < ARRAY_SIZE(port_cntr_ops); i++) { @@ -1206,12 +1205,11 @@ void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd) sizeof(name), port_cntr_ops[i].name, j + 1); - DEBUGFS_FILE_CREATE(name, - ibd->hfi1_ibdev_dbg, - ppd, - &port_cntr_ops[i].ops, + debugfs_create_file(name, !port_cntr_ops[i].ops.write ? - S_IRUGO : S_IRUGO | S_IWUSR); + S_IRUGO : + S_IRUGO | S_IWUSR, + root, ppd, &port_cntr_ops[i].ops); } hfi1_fault_init_debugfs(ibd); @@ -1343,8 +1341,10 @@ void hfi1_dbg_init(void) hfi1_dbg_root = debugfs_create_dir(DRIVER_NAME, NULL); if (!hfi1_dbg_root) pr_warn("init of debugfs failed\n"); - DEBUGFS_SEQ_FILE_CREATE(driver_stats_names, hfi1_dbg_root, NULL); - DEBUGFS_SEQ_FILE_CREATE(driver_stats, hfi1_dbg_root, NULL); + debugfs_create_file("driver_stats_names", 0444, hfi1_dbg_root, NULL, + &_driver_stats_names_file_ops); + debugfs_create_file("driver_stats", 0444, hfi1_dbg_root, NULL, + &_driver_stats_file_ops); } void hfi1_dbg_exit(void) diff --git a/drivers/infiniband/hw/hfi1/debugfs.h b/drivers/infiniband/hw/hfi1/debugfs.h index d5d824459fcc..57e582caa5eb 100644 --- a/drivers/infiniband/hw/hfi1/debugfs.h +++ b/drivers/infiniband/hw/hfi1/debugfs.h @@ -49,16 +49,6 @@ struct hfi1_ibdev; -#define DEBUGFS_FILE_CREATE(name, parent, data, ops, mode) \ -do { \ - struct dentry *ent; \ - const char *__name = name; \ - ent = debugfs_create_file(__name, mode, parent, \ - data, ops); \ - if (!ent) \ - pr_warn("create of %s failed\n", __name); \ -} while (0) - #define DEBUGFS_SEQ_FILE_OPS(name) \ static const struct seq_operations _##name##_seq_ops = { \ .start = _##name##_seq_start, \ @@ -89,8 +79,6 @@ static const struct file_operations _##name##_file_ops = { \ .release = seq_release \ } -#define DEBUGFS_SEQ_FILE_CREATE(name, parent, data) \ - DEBUGFS_FILE_CREATE(#name, parent, data, &_##name##_file_ops, 0444) ssize_t hfi1_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos); diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c index e2290f32c8d9..dd09b8544568 100644 --- a/drivers/infiniband/hw/hfi1/fault.c +++ b/drivers/infiniband/hw/hfi1/fault.c @@ -278,7 +278,8 @@ int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd) return -ENOENT; } - DEBUGFS_SEQ_FILE_CREATE(fault_stats, ibd->fault->dir, ibd); + debugfs_create_file("fault_stats", 0444, ibd->fault->dir, ibd, + &_fault_stats_file_ops); if (!debugfs_create_bool("enable", 0600, ibd->fault->dir, &ibd->fault->enable)) goto fail; From e77511802560728eadfa87f3a1ac92be42af6f96 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:55 +0100 Subject: [PATCH 0635/4277] infiniband: hfi1: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hfi1/debugfs.c | 2 -- drivers/infiniband/hw/hfi1/fault.c | 50 ++++++++++------------------ 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/debugfs.c b/drivers/infiniband/hw/hfi1/debugfs.c index aeb0f07103de..427ba0ce74a5 100644 --- a/drivers/infiniband/hw/hfi1/debugfs.c +++ b/drivers/infiniband/hw/hfi1/debugfs.c @@ -1339,8 +1339,6 @@ DEBUGFS_FILE_OPS(driver_stats); void hfi1_dbg_init(void) { hfi1_dbg_root = debugfs_create_dir(DRIVER_NAME, NULL); - if (!hfi1_dbg_root) - pr_warn("init of debugfs failed\n"); debugfs_create_file("driver_stats_names", 0444, hfi1_dbg_root, NULL, &_driver_stats_names_file_ops); debugfs_create_file("driver_stats", 0444, hfi1_dbg_root, NULL, diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c index dd09b8544568..3fd3315d0fb0 100644 --- a/drivers/infiniband/hw/hfi1/fault.c +++ b/drivers/infiniband/hw/hfi1/fault.c @@ -250,6 +250,7 @@ void hfi1_fault_exit_debugfs(struct hfi1_ibdev *ibd) int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd) { struct dentry *parent = ibd->hfi1_ibdev_dbg; + struct dentry *fault_dir; ibd->fault = kzalloc(sizeof(*ibd->fault), GFP_KERNEL); if (!ibd->fault) @@ -269,46 +270,31 @@ int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd) bitmap_zero(ibd->fault->opcodes, sizeof(ibd->fault->opcodes) * BITS_PER_BYTE); - ibd->fault->dir = - fault_create_debugfs_attr("fault", parent, - &ibd->fault->attr); - if (IS_ERR(ibd->fault->dir)) { + fault_dir = + fault_create_debugfs_attr("fault", parent, &ibd->fault->attr); + if (IS_ERR(fault_dir)) { kfree(ibd->fault); ibd->fault = NULL; return -ENOENT; } + ibd->fault->dir = fault_dir; - debugfs_create_file("fault_stats", 0444, ibd->fault->dir, ibd, + debugfs_create_file("fault_stats", 0444, fault_dir, ibd, &_fault_stats_file_ops); - if (!debugfs_create_bool("enable", 0600, ibd->fault->dir, - &ibd->fault->enable)) - goto fail; - if (!debugfs_create_bool("suppress_err", 0600, - ibd->fault->dir, - &ibd->fault->suppress_err)) - goto fail; - if (!debugfs_create_bool("opcode_mode", 0600, ibd->fault->dir, - &ibd->fault->opcode)) - goto fail; - if (!debugfs_create_file("opcodes", 0600, ibd->fault->dir, - ibd->fault, &__fault_opcodes_fops)) - goto fail; - if (!debugfs_create_u64("skip_pkts", 0600, - ibd->fault->dir, - &ibd->fault->fault_skip)) - goto fail; - if (!debugfs_create_u64("skip_usec", 0600, - ibd->fault->dir, - &ibd->fault->fault_skip_usec)) - goto fail; - if (!debugfs_create_u8("direction", 0600, ibd->fault->dir, - &ibd->fault->direction)) - goto fail; + debugfs_create_bool("enable", 0600, fault_dir, &ibd->fault->enable); + debugfs_create_bool("suppress_err", 0600, fault_dir, + &ibd->fault->suppress_err); + debugfs_create_bool("opcode_mode", 0600, fault_dir, + &ibd->fault->opcode); + debugfs_create_file("opcodes", 0600, fault_dir, ibd->fault, + &__fault_opcodes_fops); + debugfs_create_u64("skip_pkts", 0600, fault_dir, + &ibd->fault->fault_skip); + debugfs_create_u64("skip_usec", 0600, fault_dir, + &ibd->fault->fault_skip_usec); + debugfs_create_u8("direction", 0600, fault_dir, &ibd->fault->direction); return 0; -fail: - hfi1_fault_exit_debugfs(ibd); - return -ENOMEM; } bool hfi1_dbg_fault_suppress_err(struct hfi1_ibdev *ibd) From 0d0336cf54343bcd160644cdb7c3b986390433d8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:56 +0100 Subject: [PATCH 0636/4277] infiniband: qib: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qib/qib_debugfs.c | 27 ++++++++----------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib_debugfs.c b/drivers/infiniband/hw/qib/qib_debugfs.c index 5ed1ed93380f..caeb77d07a58 100644 --- a/drivers/infiniband/hw/qib/qib_debugfs.c +++ b/drivers/infiniband/hw/qib/qib_debugfs.c @@ -66,15 +66,6 @@ static const struct file_operations _##name##_file_ops = { \ .release = seq_release \ }; -#define DEBUGFS_FILE_CREATE(name) \ -do { \ - struct dentry *ent; \ - ent = debugfs_create_file(#name , 0400, ibd->qib_ibdev_dbg, \ - ibd, &_##name##_file_ops); \ - if (!ent) \ - pr_warn("create of " #name " failed\n"); \ -} while (0) - static void *_opcode_stats_seq_start(struct seq_file *s, loff_t *pos) { struct qib_opcode_stats_perctx *opstats; @@ -249,17 +240,17 @@ DEBUGFS_FILE(qp_stats) void qib_dbg_ibdev_init(struct qib_ibdev *ibd) { + struct dentry *root; char name[10]; snprintf(name, sizeof(name), "qib%d", dd_from_dev(ibd)->unit); - ibd->qib_ibdev_dbg = debugfs_create_dir(name, qib_dbg_root); - if (!ibd->qib_ibdev_dbg) { - pr_warn("create of %s failed\n", name); - return; - } - DEBUGFS_FILE_CREATE(opcode_stats); - DEBUGFS_FILE_CREATE(ctx_stats); - DEBUGFS_FILE_CREATE(qp_stats); + root = debugfs_create_dir(name, qib_dbg_root); + ibd->qib_ibdev_dbg = root; + + debugfs_create_file("opcode_stats", 0400, root, ibd, + &_opcode_stats_file_ops); + debugfs_create_file("ctx_stats", 0400, root, ibd, &_ctx_stats_file_ops); + debugfs_create_file("qp_stats", 0400, root, ibd, &_qp_stats_file_ops); } void qib_dbg_ibdev_exit(struct qib_ibdev *ibd) @@ -274,8 +265,6 @@ out: void qib_dbg_init(void) { qib_dbg_root = debugfs_create_dir(QIB_DRV_NAME, NULL); - if (!qib_dbg_root) - pr_warn("init of debugfs failed\n"); } void qib_dbg_exit(void) From 73eb8f03f0ecfcb45ed1995990229f5a81e8e38f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:57 +0100 Subject: [PATCH 0637/4277] infiniband: mlx5: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/cong.c | 15 +++----- drivers/infiniband/hw/mlx5/main.c | 9 +++-- drivers/infiniband/hw/mlx5/mlx5_ib.h | 8 +---- drivers/infiniband/hw/mlx5/mr.c | 51 +++++----------------------- 4 files changed, 19 insertions(+), 64 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/cong.c b/drivers/infiniband/hw/mlx5/cong.c index 7e4e358a4fd8..8ba439fabf7f 100644 --- a/drivers/infiniband/hw/mlx5/cong.c +++ b/drivers/infiniband/hw/mlx5/cong.c @@ -389,19 +389,19 @@ void mlx5_ib_cleanup_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num) dev->port[port_num].dbg_cc_params = NULL; } -int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num) +void mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num) { struct mlx5_ib_dbg_cc_params *dbg_cc_params; struct mlx5_core_dev *mdev; int i; if (!mlx5_debugfs_root) - goto out; + return; /* Takes a 1-based port number */ mdev = mlx5_ib_get_native_port_mdev(dev, port_num + 1, NULL); if (!mdev) - goto out; + return; if (!MLX5_CAP_GEN(mdev, cc_query_allowed) || !MLX5_CAP_GEN(mdev, cc_modify_allowed)) @@ -415,8 +415,6 @@ int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num) dbg_cc_params->root = debugfs_create_dir("cc_params", mdev->priv.dbg_root); - if (!dbg_cc_params->root) - goto err; for (i = 0; i < MLX5_IB_DBG_CC_MAX; i++) { dbg_cc_params->params[i].offset = i; @@ -427,14 +425,11 @@ int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num) 0600, dbg_cc_params->root, &dbg_cc_params->params[i], &dbg_cc_fops); - if (!dbg_cc_params->params[i].dentry) - goto err; } put_mdev: mlx5_ib_put_native_port_mdev(dev, port_num + 1); -out: - return 0; + return; err: mlx5_ib_warn(dev, "cong debugfs failure\n"); @@ -445,5 +440,5 @@ err: * We don't want to fail driver if debugfs failed to initialize, * so we are not forwarding error to the user. */ - return 0; + return; } diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index b32b5c7f1cfd..2aaa196c2653 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -5530,9 +5530,7 @@ static bool mlx5_ib_bind_slave_port(struct mlx5_ib_dev *ibdev, mpi->mdev_events.notifier_call = mlx5_ib_event_slave_port; mlx5_notifier_register(mpi->mdev, &mpi->mdev_events); - err = mlx5_ib_init_cong_debugfs(ibdev, port_num); - if (err) - goto unbind; + mlx5_ib_init_cong_debugfs(ibdev, port_num); return true; @@ -6207,8 +6205,9 @@ void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev) static int mlx5_ib_stage_cong_debugfs_init(struct mlx5_ib_dev *dev) { - return mlx5_ib_init_cong_debugfs(dev, - mlx5_core_native_port_num(dev->mdev) - 1); + mlx5_ib_init_cong_debugfs(dev, + mlx5_core_native_port_num(dev->mdev) - 1); + return 0; } static void mlx5_ib_stage_cong_debugfs_cleanup(struct mlx5_ib_dev *dev) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 819207190343..ea124af56dce 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -636,7 +636,6 @@ struct mlx5_cache_ent { spinlock_t lock; - struct dentry *dir; char name[4]; u32 order; u32 xlt; @@ -648,11 +647,6 @@ struct mlx5_cache_ent { u32 miss; u32 limit; - struct dentry *fsize; - struct dentry *fcur; - struct dentry *fmiss; - struct dentry *flimit; - struct mlx5_ib_dev *dev; struct work_struct work; struct delayed_work dwork; @@ -1270,7 +1264,7 @@ __be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev, const struct ib_gid_attr *attr); void mlx5_ib_cleanup_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num); -int mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num); +void mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u8 port_num); /* GSI QP helper functions */ struct ib_qp *mlx5_ib_gsi_create_qp(struct ib_pd *pd, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 6d763d6a189f..d78a94803e8c 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -606,52 +606,27 @@ static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) dev->cache.root = NULL; } -static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) +static void mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) { struct mlx5_mr_cache *cache = &dev->cache; struct mlx5_cache_ent *ent; + struct dentry *dir; int i; if (!mlx5_debugfs_root || dev->rep) - return 0; + return; cache->root = debugfs_create_dir("mr_cache", dev->mdev->priv.dbg_root); - if (!cache->root) - return -ENOMEM; for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { ent = &cache->ent[i]; sprintf(ent->name, "%d", ent->order); - ent->dir = debugfs_create_dir(ent->name, cache->root); - if (!ent->dir) - goto err; - - ent->fsize = debugfs_create_file("size", 0600, ent->dir, ent, - &size_fops); - if (!ent->fsize) - goto err; - - ent->flimit = debugfs_create_file("limit", 0600, ent->dir, ent, - &limit_fops); - if (!ent->flimit) - goto err; - - ent->fcur = debugfs_create_u32("cur", 0400, ent->dir, - &ent->cur); - if (!ent->fcur) - goto err; - - ent->fmiss = debugfs_create_u32("miss", 0600, ent->dir, - &ent->miss); - if (!ent->fmiss) - goto err; + dir = debugfs_create_dir(ent->name, cache->root); + debugfs_create_file("size", 0600, dir, ent, &size_fops); + debugfs_create_file("limit", 0600, dir, ent, &limit_fops); + debugfs_create_u32("cur", 0400, dir, &ent->cur); + debugfs_create_u32("miss", 0600, dir, &ent->miss); } - - return 0; -err: - mlx5_mr_cache_debugfs_cleanup(dev); - - return -ENOMEM; } static void delay_time_func(struct timer_list *t) @@ -665,7 +640,6 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) { struct mlx5_mr_cache *cache = &dev->cache; struct mlx5_cache_ent *ent; - int err; int i; mutex_init(&dev->slow_path_mutex); @@ -709,14 +683,7 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) queue_work(cache->wq, &ent->work); } - err = mlx5_mr_cache_debugfs_init(dev); - if (err) - mlx5_ib_warn(dev, "cache debugfs failure\n"); - - /* - * We don't want to fail driver if debugfs failed to initialize, - * so we are not forwarding error to the user. - */ + mlx5_mr_cache_debugfs_init(dev); return 0; } From 25376729661113857b8b07632c26d50eaef56dfa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:58 +0100 Subject: [PATCH 0638/4277] infiniband: ocrdma: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/ocrdma/ocrdma_stats.c | 67 +++++++-------------- 1 file changed, 22 insertions(+), 45 deletions(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c index dd15474b19b7..d8ab396593b7 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c @@ -767,88 +767,65 @@ void ocrdma_add_port_stats(struct ocrdma_dev *dev) /* Create post stats base dir */ dev->dir = debugfs_create_dir(pci_name(pdev), ocrdma_dbgfs_dir); - if (!dev->dir) - goto err; dev->rsrc_stats.type = OCRDMA_RSRC_STATS; dev->rsrc_stats.dev = dev; - if (!debugfs_create_file("resource_stats", S_IRUSR, dev->dir, - &dev->rsrc_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("resource_stats", S_IRUSR, dev->dir, + &dev->rsrc_stats, &ocrdma_dbg_ops); dev->rx_stats.type = OCRDMA_RXSTATS; dev->rx_stats.dev = dev; - if (!debugfs_create_file("rx_stats", S_IRUSR, dev->dir, - &dev->rx_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("rx_stats", S_IRUSR, dev->dir, &dev->rx_stats, + &ocrdma_dbg_ops); dev->wqe_stats.type = OCRDMA_WQESTATS; dev->wqe_stats.dev = dev; - if (!debugfs_create_file("wqe_stats", S_IRUSR, dev->dir, - &dev->wqe_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("wqe_stats", S_IRUSR, dev->dir, &dev->wqe_stats, + &ocrdma_dbg_ops); dev->tx_stats.type = OCRDMA_TXSTATS; dev->tx_stats.dev = dev; - if (!debugfs_create_file("tx_stats", S_IRUSR, dev->dir, - &dev->tx_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("tx_stats", S_IRUSR, dev->dir, &dev->tx_stats, + &ocrdma_dbg_ops); dev->db_err_stats.type = OCRDMA_DB_ERRSTATS; dev->db_err_stats.dev = dev; - if (!debugfs_create_file("db_err_stats", S_IRUSR, dev->dir, - &dev->db_err_stats, &ocrdma_dbg_ops)) - goto err; - + debugfs_create_file("db_err_stats", S_IRUSR, dev->dir, + &dev->db_err_stats, &ocrdma_dbg_ops); dev->tx_qp_err_stats.type = OCRDMA_TXQP_ERRSTATS; dev->tx_qp_err_stats.dev = dev; - if (!debugfs_create_file("tx_qp_err_stats", S_IRUSR, dev->dir, - &dev->tx_qp_err_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("tx_qp_err_stats", S_IRUSR, dev->dir, + &dev->tx_qp_err_stats, &ocrdma_dbg_ops); dev->rx_qp_err_stats.type = OCRDMA_RXQP_ERRSTATS; dev->rx_qp_err_stats.dev = dev; - if (!debugfs_create_file("rx_qp_err_stats", S_IRUSR, dev->dir, - &dev->rx_qp_err_stats, &ocrdma_dbg_ops)) - goto err; - + debugfs_create_file("rx_qp_err_stats", S_IRUSR, dev->dir, + &dev->rx_qp_err_stats, &ocrdma_dbg_ops); dev->tx_dbg_stats.type = OCRDMA_TX_DBG_STATS; dev->tx_dbg_stats.dev = dev; - if (!debugfs_create_file("tx_dbg_stats", S_IRUSR, dev->dir, - &dev->tx_dbg_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("tx_dbg_stats", S_IRUSR, dev->dir, + &dev->tx_dbg_stats, &ocrdma_dbg_ops); dev->rx_dbg_stats.type = OCRDMA_RX_DBG_STATS; dev->rx_dbg_stats.dev = dev; - if (!debugfs_create_file("rx_dbg_stats", S_IRUSR, dev->dir, - &dev->rx_dbg_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("rx_dbg_stats", S_IRUSR, dev->dir, + &dev->rx_dbg_stats, &ocrdma_dbg_ops); dev->driver_stats.type = OCRDMA_DRV_STATS; dev->driver_stats.dev = dev; - if (!debugfs_create_file("driver_dbg_stats", S_IRUSR, dev->dir, - &dev->driver_stats, &ocrdma_dbg_ops)) - goto err; + debugfs_create_file("driver_dbg_stats", S_IRUSR, dev->dir, + &dev->driver_stats, &ocrdma_dbg_ops); dev->reset_stats.type = OCRDMA_RESET_STATS; dev->reset_stats.dev = dev; - if (!debugfs_create_file("reset_stats", 0200, dev->dir, - &dev->reset_stats, &ocrdma_dbg_ops)) - goto err; - - - return; -err: - debugfs_remove_recursive(dev->dir); - dev->dir = NULL; + debugfs_create_file("reset_stats", 0200, dev->dir, &dev->reset_stats, + &ocrdma_dbg_ops); } void ocrdma_rem_port_stats(struct ocrdma_dev *dev) { - if (!dev->dir) - return; debugfs_remove_recursive(dev->dir); } From 316bcda81d29059751daf9a81d8ccc1c041d08c8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:17:59 +0100 Subject: [PATCH 0639/4277] infiniband: usnic: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Acked-by: Parvi Kaustubhi Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/usnic/usnic_debugfs.c | 26 --------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/infiniband/hw/usnic/usnic_debugfs.c b/drivers/infiniband/hw/usnic/usnic_debugfs.c index a3115709fb03..e5a3f02fb078 100644 --- a/drivers/infiniband/hw/usnic/usnic_debugfs.c +++ b/drivers/infiniband/hw/usnic/usnic_debugfs.c @@ -113,42 +113,21 @@ static const struct file_operations flowinfo_ops = { void usnic_debugfs_init(void) { debugfs_root = debugfs_create_dir(DRV_NAME, NULL); - if (IS_ERR(debugfs_root)) { - usnic_err("Failed to create debugfs root dir, check if debugfs is enabled in kernel configuration\n"); - goto out_clear_root; - } flows_dentry = debugfs_create_dir("flows", debugfs_root); - if (IS_ERR_OR_NULL(flows_dentry)) { - usnic_err("Failed to create debugfs flow dir with err %ld\n", - PTR_ERR(flows_dentry)); - goto out_free_root; - } debugfs_create_file("build-info", S_IRUGO, debugfs_root, NULL, &usnic_debugfs_buildinfo_ops); - return; - -out_free_root: - debugfs_remove_recursive(debugfs_root); -out_clear_root: - debugfs_root = NULL; } void usnic_debugfs_exit(void) { - if (!debugfs_root) - return; - debugfs_remove_recursive(debugfs_root); debugfs_root = NULL; } void usnic_debugfs_flow_add(struct usnic_ib_qp_grp_flow *qp_flow) { - if (IS_ERR_OR_NULL(flows_dentry)) - return; - scnprintf(qp_flow->dentry_name, sizeof(qp_flow->dentry_name), "%u", qp_flow->flow->flow_id); qp_flow->dbgfs_dentry = debugfs_create_file(qp_flow->dentry_name, @@ -156,11 +135,6 @@ void usnic_debugfs_flow_add(struct usnic_ib_qp_grp_flow *qp_flow) flows_dentry, qp_flow, &flowinfo_ops); - if (IS_ERR_OR_NULL(qp_flow->dbgfs_dentry)) { - usnic_err("Failed to create dbg fs entry for flow %u with error %ld\n", - qp_flow->flow->flow_id, - PTR_ERR(qp_flow->dbgfs_dentry)); - } } void usnic_debugfs_flow_remove(struct usnic_ib_qp_grp_flow *qp_flow) From 2e061c691c3f46a4932a40f7bf440bacd011e1e2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:18:00 +0100 Subject: [PATCH 0640/4277] infiniband: ipoib: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/ipoib/ipoib.h | 4 ++-- drivers/infiniband/ulp/ipoib/ipoib_fs.c | 7 +------ drivers/infiniband/ulp/ipoib/ipoib_main.c | 4 +--- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 1da119d901a9..5941d660add1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -781,12 +781,12 @@ static inline void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *w #ifdef CONFIG_INFINIBAND_IPOIB_DEBUG void ipoib_create_debug_files(struct net_device *dev); void ipoib_delete_debug_files(struct net_device *dev); -int ipoib_register_debugfs(void); +void ipoib_register_debugfs(void); void ipoib_unregister_debugfs(void); #else static inline void ipoib_create_debug_files(struct net_device *dev) { } static inline void ipoib_delete_debug_files(struct net_device *dev) { } -static inline int ipoib_register_debugfs(void) { return 0; } +static inline void ipoib_register_debugfs(void) { } static inline void ipoib_unregister_debugfs(void) { } #endif diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c index 178488028734..64c19f6fa931 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c @@ -267,14 +267,10 @@ void ipoib_create_debug_files(struct net_device *dev) snprintf(name, sizeof(name), "%s_mcg", dev->name); priv->mcg_dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, ipoib_root, dev, &ipoib_mcg_fops); - if (!priv->mcg_dentry) - ipoib_warn(priv, "failed to create mcg debug file\n"); snprintf(name, sizeof(name), "%s_path", dev->name); priv->path_dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, ipoib_root, dev, &ipoib_path_fops); - if (!priv->path_dentry) - ipoib_warn(priv, "failed to create path debug file\n"); } void ipoib_delete_debug_files(struct net_device *dev) @@ -286,10 +282,9 @@ void ipoib_delete_debug_files(struct net_device *dev) priv->mcg_dentry = priv->path_dentry = NULL; } -int ipoib_register_debugfs(void) +void ipoib_register_debugfs(void) { ipoib_root = debugfs_create_dir("ipoib", NULL); - return ipoib_root ? 0 : -ENOMEM; } void ipoib_unregister_debugfs(void) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index d932f99201d1..45ef3b0f03c5 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -2577,9 +2577,7 @@ static int __init ipoib_init_module(void) */ BUILD_BUG_ON(IPOIB_CM_COPYBREAK > IPOIB_CM_HEAD_SIZE); - ret = ipoib_register_debugfs(); - if (ret) - return ret; + ipoib_register_debugfs(); /* * We create a global workqueue here that is used for all flush From f37385919045ad611bcf40bbb899591ec8aac86c Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 22 Jan 2019 10:24:23 -0800 Subject: [PATCH 0641/4277] IB/core: Declare local functions 'static' This patch avoids that sparse complains about missing function declarations. Fixes: f27a0d50a4bc ("RDMA/umem: Use umem->owning_mm inside ODP") Signed-off-by: Bart Van Assche Reviewed-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/umem_odp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index a4ec43093cb3..dc1d7cb15cfa 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -299,7 +299,7 @@ static void free_per_mm(struct rcu_head *rcu) kfree(container_of(rcu, struct ib_ucontext_per_mm, rcu)); } -void put_per_mm(struct ib_umem_odp *umem_odp) +static void put_per_mm(struct ib_umem_odp *umem_odp) { struct ib_ucontext_per_mm *per_mm = umem_odp->per_mm; struct ib_ucontext *ctx = umem_odp->umem.context; From 0a353c2e9435ecea39f8ffbe09dc88fd931e88fd Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 22 Jan 2019 10:24:39 -0800 Subject: [PATCH 0642/4277] IB/mlx5: Declare local functions 'static' This patch avoids that sparse complains about missing function declarations. Fixes: c9990ab39b6e ("RDMA/umem: Move all the ODP related stuff out of ucontext and into per_mm") Signed-off-by: Bart Van Assche Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 82ac6cdc7130..3abdfc3584c0 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -101,7 +101,7 @@ static int check_parent(struct ib_umem_odp *odp, return mr && mr->parent == parent && !odp->dying; } -struct ib_ucontext_per_mm *mr_to_per_mm(struct mlx5_ib_mr *mr) +static struct ib_ucontext_per_mm *mr_to_per_mm(struct mlx5_ib_mr *mr) { if (WARN_ON(!mr || !is_odp_mr(mr))) return NULL; From 8ba0ddd09406e35782259f2ac18187ec77d8c093 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Sun, 20 Jan 2019 02:27:13 +0100 Subject: [PATCH 0643/4277] RDMA/iw_cxgb4: Drop __GFP_NOFAIL There is no reason for this __GFP_NOFAIL, none of the other routines in this file use it, and there is an error unwind here. NOFAIL should be reserved for special cases, not used by network drivers. Fixes: 6a0b6174d35a ("rdma/cxgb4: Add support for kernel mode SRQ's") Reported-by: Nicholas Mc Guire Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/qp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 03f4c66c2659..c00a41144126 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -2597,7 +2597,7 @@ static int alloc_srq_queue(struct c4iw_srq *srq, struct c4iw_dev_ucontext *uctx, /* build fw_ri_res_wr */ wr_len = sizeof(*res_wr) + sizeof(*res); - skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); + skb = alloc_skb(wr_len, GFP_KERNEL); if (!skb) goto err_free_queue; set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); From da6a496a34f2fdcab14362cdc5068aac385e7b47 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 09:16:08 +0200 Subject: [PATCH 0644/4277] IB/mlx5: Ranges in implicit ODP MR inherit its write access A sub-range in ODP implicit MR should take its write permission from the MR and not be set always to allow. Fixes: d07d1d70ce1a ("IB/umem: Update on demand page (ODP) support") Signed-off-by: Moni Shoua Reviewed-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/umem_odp.c | 5 +++-- drivers/infiniband/hw/mlx5/odp.c | 2 +- include/rdma/ib_umem_odp.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index dc1d7cb15cfa..eb8a5eb65bfa 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -332,9 +332,10 @@ static void put_per_mm(struct ib_umem_odp *umem_odp) mmu_notifier_call_srcu(&per_mm->rcu, free_per_mm); } -struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm, +struct ib_umem_odp *ib_alloc_odp_umem(struct ib_umem_odp *root, unsigned long addr, size_t size) { + struct ib_ucontext_per_mm *per_mm = root->per_mm; struct ib_ucontext *ctx = per_mm->context; struct ib_umem_odp *odp_data; struct ib_umem *umem; @@ -349,7 +350,7 @@ struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm, umem->length = size; umem->address = addr; umem->page_shift = PAGE_SHIFT; - umem->writable = 1; + umem->writable = root->umem.writable; umem->is_odp = 1; odp_data->per_mm = per_mm; diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 3abdfc3584c0..3e0d5885c026 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -439,7 +439,7 @@ next_mr: if (nentries) nentries++; } else { - odp = ib_alloc_odp_umem(odp_mr->per_mm, addr, + odp = ib_alloc_odp_umem(odp_mr, addr, MLX5_IMR_MTT_SIZE); if (IS_ERR(odp)) { mutex_unlock(&odp_mr->umem_mutex); diff --git a/include/rdma/ib_umem_odp.h b/include/rdma/ib_umem_odp.h index d3725cf13ecd..d0024f53626e 100644 --- a/include/rdma/ib_umem_odp.h +++ b/include/rdma/ib_umem_odp.h @@ -116,7 +116,7 @@ struct ib_ucontext_per_mm { }; int ib_umem_odp_get(struct ib_umem_odp *umem_odp, int access); -struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm, +struct ib_umem_odp *ib_alloc_odp_umem(struct ib_umem_odp *root_umem, unsigned long addr, size_t size); void ib_umem_odp_release(struct ib_umem_odp *umem_odp); From 61b2fe3c62e5269408e264b2348f96467246d537 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 09:16:09 +0200 Subject: [PATCH 0645/4277] IB/mlx5: Remove dead code When CONFIG_INFINIBAND_ON_DEMAND_PAGING is not set there is no caller to ib_alloc_odp_umem() so let's remove it. Signed-off-by: Moni Shoua Reviewed-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- include/rdma/ib_umem_odp.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/rdma/ib_umem_odp.h b/include/rdma/ib_umem_odp.h index d0024f53626e..dadc96dea39c 100644 --- a/include/rdma/ib_umem_odp.h +++ b/include/rdma/ib_umem_odp.h @@ -169,12 +169,6 @@ static inline int ib_umem_odp_get(struct ib_umem_odp *umem_odp, int access) return -EINVAL; } -static inline struct ib_umem_odp * -ib_alloc_odp_umem(struct ib_ucontext *context, unsigned long addr, size_t size) -{ - return ERR_PTR(-EINVAL); -} - static inline void ib_umem_odp_release(struct ib_umem_odp *umem_odp) {} #endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */ From 6a157f7d1b14eb88d89fbd396cfea15ac4bded2d Mon Sep 17 00:00:00 2001 From: Yangyang Li Date: Tue, 18 Dec 2018 21:21:53 +0800 Subject: [PATCH 0646/4277] RDMA/hns: Add SCC context allocation support for hip08 This patch adds SCC context allocation and initialization support for DCQCN in kernel space driver. Signed-off-by: Yangyang Li Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_cmd.h | 4 ++ drivers/infiniband/hw/hns/hns_roce_device.h | 6 +++ drivers/infiniband/hw/hns/hns_roce_hem.c | 26 ++++++++++-- drivers/infiniband/hw/hns/hns_roce_hem.h | 1 + drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 46 ++++++++++++++++++++- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 33 +++++++++++++-- drivers/infiniband/hw/hns/hns_roce_main.c | 18 ++++++++ drivers/infiniband/hw/hns/hns_roce_qp.c | 20 ++++++++- 8 files changed, 146 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_cmd.h b/drivers/infiniband/hw/hns/hns_roce_cmd.h index 927701df5eff..f682cb4b954b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cmd.h +++ b/drivers/infiniband/hw/hns/hns_roce_cmd.h @@ -98,6 +98,10 @@ enum { HNS_ROCE_CMD_MODIFY_CEQC = 0x91, HNS_ROCE_CMD_QUERY_CEQC = 0x92, HNS_ROCE_CMD_DESTROY_CEQC = 0x93, + + /* SCC CTX BT commands */ + HNS_ROCE_CMD_READ_SCCC_BT0 = 0xa4, + HNS_ROCE_CMD_WRITE_SCCC_BT0 = 0xa5, }; enum { diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 9a4be70936e0..7c93be4379e6 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -482,6 +482,7 @@ struct hns_roce_qp_table { struct hns_roce_hem_table qp_table; struct hns_roce_hem_table irrl_table; struct hns_roce_hem_table trrl_table; + struct hns_roce_hem_table sccc_table; }; struct hns_roce_cq_table { @@ -768,6 +769,7 @@ struct hns_roce_caps { int irrl_entry_sz; int trrl_entry_sz; int cqc_entry_sz; + int sccc_entry_sz; int srqc_entry_sz; int idx_entry_sz; u32 pbl_ba_pg_sz; @@ -780,6 +782,7 @@ struct hns_roce_caps { u32 srqc_bt_num; u32 cqc_bt_num; u32 mpt_bt_num; + u32 sccc_bt_num; u32 qpc_ba_pg_sz; u32 qpc_buf_pg_sz; u32 qpc_hop_num; @@ -795,6 +798,9 @@ struct hns_roce_caps { u32 mtt_ba_pg_sz; u32 mtt_buf_pg_sz; u32 mtt_hop_num; + u32 sccc_ba_pg_sz; + u32 sccc_buf_pg_sz; + u32 sccc_hop_num; u32 cqe_ba_pg_sz; u32 cqe_buf_pg_sz; u32 cqe_hop_num; diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 4cdbcafa5915..fc7b69e1b1f9 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -45,6 +45,7 @@ bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type) (hr_dev->caps.mpt_hop_num && type == HEM_TYPE_MTPT) || (hr_dev->caps.cqc_hop_num && type == HEM_TYPE_CQC) || (hr_dev->caps.srqc_hop_num && type == HEM_TYPE_SRQC) || + (hr_dev->caps.sccc_hop_num && type == HEM_TYPE_SCCC) || (hr_dev->caps.cqe_hop_num && type == HEM_TYPE_CQE) || (hr_dev->caps.mtt_hop_num && type == HEM_TYPE_MTT) || (hr_dev->caps.srqwqe_hop_num && type == HEM_TYPE_SRQWQE) || @@ -125,6 +126,14 @@ int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev, mhop->ba_l0_num = hr_dev->caps.cqc_bt_num; mhop->hop_num = hr_dev->caps.cqc_hop_num; break; + case HEM_TYPE_SCCC: + mhop->buf_chunk_size = 1 << (hr_dev->caps.sccc_buf_pg_sz + + PAGE_SHIFT); + mhop->bt_chunk_size = 1 << (hr_dev->caps.sccc_ba_pg_sz + + PAGE_SHIFT); + mhop->ba_l0_num = hr_dev->caps.sccc_bt_num; + mhop->hop_num = hr_dev->caps.sccc_hop_num; + break; case HEM_TYPE_SRQC: mhop->buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz + PAGE_SHIFT); @@ -175,7 +184,7 @@ int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev, return 0; /* - * QPC/MTPT/CQC/SRQC alloc hem for buffer pages. + * QPC/MTPT/CQC/SRQC/SCCC alloc hem for buffer pages. * MTT/CQE alloc hem for bt pages. */ bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num); @@ -486,7 +495,7 @@ static int hns_roce_table_mhop_get(struct hns_roce_dev *hr_dev, } /* - * alloc buffer space chunk for QPC/MTPT/CQC/SRQC. + * alloc buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC. * alloc bt space chunk for MTT/CQE. */ size = table->type < HEM_TYPE_MTT ? buf_chunk_size : bt_chunk_size; @@ -658,7 +667,7 @@ static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev, } /* - * free buffer space chunk for QPC/MTPT/CQC/SRQC. + * free buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC. * free bt space chunk for MTT/CQE. */ hns_roce_free_hem(hr_dev, table->hem[hem_idx]); @@ -904,6 +913,14 @@ int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev, num_bt_l0 = hr_dev->caps.cqc_bt_num; hop_num = hr_dev->caps.cqc_hop_num; break; + case HEM_TYPE_SCCC: + buf_chunk_size = 1 << (hr_dev->caps.sccc_buf_pg_sz + + PAGE_SHIFT); + bt_chunk_size = 1 << (hr_dev->caps.sccc_ba_pg_sz + + PAGE_SHIFT); + num_bt_l0 = hr_dev->caps.sccc_bt_num; + hop_num = hr_dev->caps.sccc_hop_num; + break; case HEM_TYPE_SRQC: buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz + PAGE_SHIFT); @@ -1081,6 +1098,9 @@ void hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev) hns_roce_cleanup_hem_table(hr_dev, &hr_dev->srq_table.table); hns_roce_cleanup_hem_table(hr_dev, &hr_dev->cq_table.table); + if (hr_dev->caps.sccc_entry_sz) + hns_roce_cleanup_hem_table(hr_dev, + &hr_dev->qp_table.sccc_table); if (hr_dev->caps.trrl_entry_sz) hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.trrl_table); diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.h b/drivers/infiniband/hw/hns/hns_roce_hem.h index a650278c6fbd..b2aeb7422eee 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.h +++ b/drivers/infiniband/hw/hns/hns_roce_hem.h @@ -44,6 +44,7 @@ enum { HEM_TYPE_MTPT, HEM_TYPE_CQC, HEM_TYPE_SRQC, + HEM_TYPE_SCCC, /* UNMAP HEM */ HEM_TYPE_MTT, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index fb990ff0c127..549a60113766 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -1078,6 +1078,9 @@ static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev) hr_dev->caps.sl_num = roce_get_field(req_b->qid_idx_sl_num, PF_RES_DATA_3_PF_SL_NUM_M, PF_RES_DATA_3_PF_SL_NUM_S); + hr_dev->caps.sccc_bt_num = roce_get_field(req_b->sccc_bt_idx_num, + PF_RES_DATA_4_PF_SCCC_BT_NUM_M, + PF_RES_DATA_4_PF_SCCC_BT_NUM_S); return 0; } @@ -1193,6 +1196,14 @@ static int hns_roce_alloc_vf_resource(struct hns_roce_dev *hr_dev) VF_RES_B_DATA_3_VF_SL_NUM_M, VF_RES_B_DATA_3_VF_SL_NUM_S, HNS_ROCE_VF_SL_NUM); + + roce_set_field(req_b->vf_sccc_idx_num, + VF_RES_B_DATA_4_VF_SCCC_BT_IDX_M, + VF_RES_B_DATA_4_VF_SCCC_BT_IDX_S, 0); + roce_set_field(req_b->vf_sccc_idx_num, + VF_RES_B_DATA_4_VF_SCCC_BT_NUM_M, + VF_RES_B_DATA_4_VF_SCCC_BT_NUM_S, + HNS_ROCE_VF_SCCC_BT_NUM); } } @@ -1205,6 +1216,7 @@ static int hns_roce_v2_set_bt(struct hns_roce_dev *hr_dev) u8 qpc_hop_num = hr_dev->caps.qpc_hop_num; u8 cqc_hop_num = hr_dev->caps.cqc_hop_num; u8 mpt_hop_num = hr_dev->caps.mpt_hop_num; + u8 sccc_hop_num = hr_dev->caps.sccc_hop_num; struct hns_roce_cfg_bt_attr *req; struct hns_roce_cmq_desc desc; @@ -1252,6 +1264,20 @@ static int hns_roce_v2_set_bt(struct hns_roce_dev *hr_dev) CFG_BT_ATTR_DATA_3_VF_MPT_HOPNUM_S, mpt_hop_num == HNS_ROCE_HOP_NUM_0 ? 0 : mpt_hop_num); + roce_set_field(req->vf_sccc_cfg, + CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_M, + CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_S, + hr_dev->caps.sccc_ba_pg_sz + PG_SHIFT_OFFSET); + roce_set_field(req->vf_sccc_cfg, + CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_M, + CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_S, + hr_dev->caps.sccc_buf_pg_sz + PG_SHIFT_OFFSET); + roce_set_field(req->vf_sccc_cfg, + CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_M, + CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_S, + sccc_hop_num == + HNS_ROCE_HOP_NUM_0 ? 0 : sccc_hop_num); + return hns_roce_cmq_send(hr_dev, &desc, 1); } @@ -1408,9 +1434,14 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev) caps->max_srq_wrs = HNS_ROCE_V2_MAX_SRQ_WR; caps->max_srq_sges = HNS_ROCE_V2_MAX_SRQ_SGE; - if (hr_dev->pci_dev->revision == 0x21) + if (hr_dev->pci_dev->revision == 0x21) { caps->flags |= HNS_ROCE_CAP_FLAG_ATOMIC | HNS_ROCE_CAP_FLAG_SRQ; + caps->sccc_entry_sz = HNS_ROCE_V2_SCCC_ENTRY_SZ; + caps->sccc_ba_pg_sz = 0; + caps->sccc_buf_pg_sz = 0; + caps->sccc_hop_num = HNS_ROCE_SCCC_HOP_NUM; + } ret = hns_roce_v2_set_bt(hr_dev); if (ret) @@ -2663,11 +2694,18 @@ static int hns_roce_v2_set_hem(struct hns_roce_dev *hr_dev, case HEM_TYPE_SRQC: op = HNS_ROCE_CMD_WRITE_SRQC_BT0; break; + case HEM_TYPE_SCCC: + op = HNS_ROCE_CMD_WRITE_SCCC_BT0; + break; default: dev_warn(dev, "Table %d not to be written by mailbox!\n", table->type); return 0; } + + if (table->type == HEM_TYPE_SCCC && step_idx) + return 0; + op += step_idx; mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); @@ -2722,6 +2760,8 @@ static int hns_roce_v2_clear_hem(struct hns_roce_dev *hr_dev, case HEM_TYPE_CQC: op = HNS_ROCE_CMD_DESTROY_CQC_BT0; break; + case HEM_TYPE_SCCC: + break; case HEM_TYPE_SRQC: op = HNS_ROCE_CMD_DESTROY_SRQC_BT0; break; @@ -2730,6 +2770,10 @@ static int hns_roce_v2_clear_hem(struct hns_roce_dev *hr_dev, table->type); return 0; } + + if (table->type == HEM_TYPE_SCCC) + return 0; + op += step_idx; mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index b72d0443c835..007caa21195b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -36,6 +36,7 @@ #include #define HNS_ROCE_VF_QPC_BT_NUM 256 +#define HNS_ROCE_VF_SCCC_BT_NUM 64 #define HNS_ROCE_VF_SRQC_BT_NUM 64 #define HNS_ROCE_VF_CQC_BT_NUM 64 #define HNS_ROCE_VF_MPT_BT_NUM 64 @@ -83,6 +84,7 @@ #define HNS_ROCE_V2_MTPT_ENTRY_SZ 64 #define HNS_ROCE_V2_MTT_ENTRY_SZ 64 #define HNS_ROCE_V2_CQE_ENTRY_SIZE 32 +#define HNS_ROCE_V2_SCCC_ENTRY_SZ 32 #define HNS_ROCE_V2_PAGE_SIZE_SUPPORTED 0xFFFFF000 #define HNS_ROCE_V2_MAX_INNER_MTPT_NUM 2 #define HNS_ROCE_INVALID_LKEY 0x100 @@ -91,6 +93,7 @@ #define HNS_ROCE_V2_RSV_QPS 8 #define HNS_ROCE_CONTEXT_HOP_NUM 1 +#define HNS_ROCE_SCCC_HOP_NUM 1 #define HNS_ROCE_MTT_HOP_NUM 1 #define HNS_ROCE_CQE_HOP_NUM 1 #define HNS_ROCE_SRQWQE_HOP_NUM 1 @@ -1300,7 +1303,8 @@ struct hns_roce_pf_res_b { __le32 smac_idx_num; __le32 sgid_idx_num; __le32 qid_idx_sl_num; - __le32 rsv[2]; + __le32 sccc_bt_idx_num; + __le32 rsv; }; #define PF_RES_DATA_1_PF_SMAC_IDX_S 0 @@ -1321,6 +1325,12 @@ struct hns_roce_pf_res_b { #define PF_RES_DATA_3_PF_SL_NUM_S 16 #define PF_RES_DATA_3_PF_SL_NUM_M GENMASK(26, 16) +#define PF_RES_DATA_4_PF_SCCC_BT_IDX_S 0 +#define PF_RES_DATA_4_PF_SCCC_BT_IDX_M GENMASK(8, 0) + +#define PF_RES_DATA_4_PF_SCCC_BT_NUM_S 9 +#define PF_RES_DATA_4_PF_SCCC_BT_NUM_M GENMASK(17, 9) + struct hns_roce_vf_res_a { __le32 vf_id; __le32 vf_qpc_bt_idx_num; @@ -1365,7 +1375,8 @@ struct hns_roce_vf_res_b { __le32 vf_smac_idx_num; __le32 vf_sgid_idx_num; __le32 vf_qid_idx_sl_num; - __le32 rsv[2]; + __le32 vf_sccc_idx_num; + __le32 rsv1; }; #define VF_RES_B_DATA_0_VF_ID_S 0 @@ -1389,6 +1400,12 @@ struct hns_roce_vf_res_b { #define VF_RES_B_DATA_3_VF_SL_NUM_S 16 #define VF_RES_B_DATA_3_VF_SL_NUM_M GENMASK(19, 16) +#define VF_RES_B_DATA_4_VF_SCCC_BT_IDX_S 0 +#define VF_RES_B_DATA_4_VF_SCCC_BT_IDX_M GENMASK(8, 0) + +#define VF_RES_B_DATA_4_VF_SCCC_BT_NUM_S 9 +#define VF_RES_B_DATA_4_VF_SCCC_BT_NUM_M GENMASK(17, 9) + struct hns_roce_vf_switch { __le32 rocee_sel; __le32 fun_id; @@ -1424,7 +1441,8 @@ struct hns_roce_cfg_bt_attr { __le32 vf_srqc_cfg; __le32 vf_cqc_cfg; __le32 vf_mpt_cfg; - __le32 rsv[2]; + __le32 vf_sccc_cfg; + __le32 rsv; }; #define CFG_BT_ATTR_DATA_0_VF_QPC_BA_PGSZ_S 0 @@ -1463,6 +1481,15 @@ struct hns_roce_cfg_bt_attr { #define CFG_BT_ATTR_DATA_3_VF_MPT_HOPNUM_S 8 #define CFG_BT_ATTR_DATA_3_VF_MPT_HOPNUM_M GENMASK(9, 8) +#define CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_S 0 +#define CFG_BT_ATTR_DATA_4_VF_SCCC_BA_PGSZ_M GENMASK(3, 0) + +#define CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_S 4 +#define CFG_BT_ATTR_DATA_4_VF_SCCC_BUF_PGSZ_M GENMASK(7, 4) + +#define CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_S 8 +#define CFG_BT_ATTR_DATA_4_VF_SCCC_HOPNUM_M GENMASK(9, 8) + struct hns_roce_cfg_sgid_tb { __le32 table_idx_rsv; __le32 vf_sgid_l; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 46ede58ef3b8..314586690f6e 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -702,8 +702,26 @@ static int hns_roce_init_hem(struct hns_roce_dev *hr_dev) } } + if (hr_dev->caps.sccc_entry_sz) { + ret = hns_roce_init_hem_table(hr_dev, + &hr_dev->qp_table.sccc_table, + HEM_TYPE_SCCC, + hr_dev->caps.sccc_entry_sz, + hr_dev->caps.num_qps, 1); + if (ret) { + dev_err(dev, + "Failed to init SCC context memory, aborting.\n"); + goto err_unmap_idx; + } + } + return 0; +err_unmap_idx: + if (hr_dev->caps.num_idx_segs) + hns_roce_cleanup_hem_table(hr_dev, + &hr_dev->mr_table.mtt_idx_table); + err_unmap_srqwqe: if (hr_dev->caps.num_srqwqe_segs) hns_roce_cleanup_hem_table(hr_dev, diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 507de9d50189..dc9a30e3b7ce 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -209,13 +209,23 @@ static int hns_roce_qp_alloc(struct hns_roce_dev *hr_dev, unsigned long qpn, } } + if (hr_dev->caps.sccc_entry_sz) { + /* Alloc memory for SCC CTX */ + ret = hns_roce_table_get(hr_dev, &qp_table->sccc_table, + hr_qp->qpn); + if (ret) { + dev_err(dev, "SCC CTX table get failed\n"); + goto err_put_trrl; + } + } + spin_lock_irq(&qp_table->lock); ret = radix_tree_insert(&hr_dev->qp_table_tree, hr_qp->qpn & (hr_dev->caps.num_qps - 1), hr_qp); spin_unlock_irq(&qp_table->lock); if (ret) { dev_err(dev, "QPC radix_tree_insert failed\n"); - goto err_put_trrl; + goto err_put_sccc; } atomic_set(&hr_qp->refcount, 1); @@ -223,6 +233,11 @@ static int hns_roce_qp_alloc(struct hns_roce_dev *hr_dev, unsigned long qpn, return 0; +err_put_sccc: + if (hr_dev->caps.sccc_entry_sz) + hns_roce_table_put(hr_dev, &qp_table->sccc_table, + hr_qp->qpn); + err_put_trrl: if (hr_dev->caps.trrl_entry_sz) hns_roce_table_put(hr_dev, &qp_table->trrl_table, hr_qp->qpn); @@ -258,6 +273,9 @@ void hns_roce_qp_free(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) wait_for_completion(&hr_qp->free); if ((hr_qp->ibqp.qp_type) != IB_QPT_GSI) { + if (hr_dev->caps.sccc_entry_sz) + hns_roce_table_put(hr_dev, &qp_table->sccc_table, + hr_qp->qpn); if (hr_dev->caps.trrl_entry_sz) hns_roce_table_put(hr_dev, &qp_table->trrl_table, hr_qp->qpn); From aa84fa18741b83daf0f8f160c46ae92f4d6f1343 Mon Sep 17 00:00:00 2001 From: Yangyang Li Date: Tue, 18 Dec 2018 21:21:54 +0800 Subject: [PATCH 0647/4277] RDMA/hns: Add SCC context clr support for hip08 This patch adds SCC context clear support for DCQCN in kernel space driver. Signed-off-by: Yangyang Li Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_device.h | 4 ++ drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 59 ++++++++++++++++++++- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 15 ++++++ drivers/infiniband/hw/hns/hns_roce_qp.c | 8 +++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 7c93be4379e6..bbcc56f8fc76 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -202,6 +202,7 @@ enum { HNS_ROCE_CAP_FLAG_SRQ = BIT(5), HNS_ROCE_CAP_FLAG_MW = BIT(7), HNS_ROCE_CAP_FLAG_FRMR = BIT(8), + HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL = BIT(9), HNS_ROCE_CAP_FLAG_ATOMIC = BIT(10), }; @@ -483,6 +484,7 @@ struct hns_roce_qp_table { struct hns_roce_hem_table irrl_table; struct hns_roce_hem_table trrl_table; struct hns_roce_hem_table sccc_table; + struct mutex scc_mutex; }; struct hns_roce_cq_table { @@ -867,6 +869,8 @@ struct hns_roce_hw { int attr_mask, enum ib_qp_state cur_state, enum ib_qp_state new_state); int (*destroy_qp)(struct ib_qp *ibqp); + int (*qp_flow_control_init)(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp); int (*post_send)(struct ib_qp *ibqp, const struct ib_send_wr *wr, const struct ib_send_wr **bad_wr); int (*post_recv)(struct ib_qp *qp, const struct ib_recv_wr *recv_wr, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 549a60113766..5e57f5f93d49 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -1436,7 +1436,9 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev) if (hr_dev->pci_dev->revision == 0x21) { caps->flags |= HNS_ROCE_CAP_FLAG_ATOMIC | - HNS_ROCE_CAP_FLAG_SRQ; + HNS_ROCE_CAP_FLAG_SRQ | + HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL; + caps->sccc_entry_sz = HNS_ROCE_V2_SCCC_ENTRY_SZ; caps->sccc_ba_pg_sz = 0; caps->sccc_buf_pg_sz = 0; @@ -4277,6 +4279,60 @@ static int hns_roce_v2_destroy_qp(struct ib_qp *ibqp) return 0; } +static int hns_roce_v2_qp_flow_control_init(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp) +{ + struct hns_roce_sccc_clr_done *rst, *resp; + struct hns_roce_sccc_clr *clr; + struct hns_roce_cmq_desc desc; + int ret, i; + + mutex_lock(&hr_dev->qp_table.scc_mutex); + + /* set scc ctx clear done flag */ + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_RESET_SCCC, false); + rst = (struct hns_roce_sccc_clr_done *)desc.data; + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) { + dev_err(hr_dev->dev, "Reset SCC ctx failed(%d)\n", ret); + goto out; + } + + /* clear scc context */ + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_CLR_SCCC, false); + clr = (struct hns_roce_sccc_clr *)desc.data; + clr->qpn = cpu_to_le32(hr_qp->qpn); + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) { + dev_err(hr_dev->dev, "Clear SCC ctx failed(%d)\n", ret); + goto out; + } + + /* query scc context clear is done or not */ + resp = (struct hns_roce_sccc_clr_done *)desc.data; + for (i = 0; i <= HNS_ROCE_CMQ_SCC_CLR_DONE_CNT; i++) { + hns_roce_cmq_setup_basic_desc(&desc, + HNS_ROCE_OPC_QUERY_SCCC, true); + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) { + dev_err(hr_dev->dev, "Query clr cmq failed(%d)\n", ret); + goto out; + } + + if (resp->clr_done) + goto out; + + msleep(20); + } + + dev_err(hr_dev->dev, "Query SCC clr done flag overtime.\n"); + ret = -ETIMEDOUT; + +out: + mutex_unlock(&hr_dev->qp_table.scc_mutex); + return ret; +} + static int hns_roce_v2_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period) { struct hns_roce_dev *hr_dev = to_hr_dev(cq->device); @@ -5835,6 +5891,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .modify_qp = hns_roce_v2_modify_qp, .query_qp = hns_roce_v2_query_qp, .destroy_qp = hns_roce_v2_destroy_qp, + .qp_flow_control_init = hns_roce_v2_qp_flow_control_init, .modify_cq = hns_roce_v2_modify_cq, .post_send = hns_roce_v2_post_send, .post_recv = hns_roce_v2_post_recv, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 007caa21195b..4da247d5ea18 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -123,6 +123,8 @@ #define HNS_ROCE_CMQ_EN_B 16 #define HNS_ROCE_CMQ_ENABLE BIT(HNS_ROCE_CMQ_EN_B) +#define HNS_ROCE_CMQ_SCC_CLR_DONE_CNT 5 + #define check_whether_last_step(hop_num, step_idx) \ ((step_idx == 0 && hop_num == HNS_ROCE_HOP_NUM_0) || \ (step_idx == 1 && hop_num == 1) || \ @@ -232,6 +234,9 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_POST_MB = 0x8504, HNS_ROCE_OPC_QUERY_MB_ST = 0x8505, HNS_ROCE_OPC_CFG_BT_ATTR = 0x8506, + HNS_ROCE_OPC_CLR_SCCC = 0x8509, + HNS_ROCE_OPC_QUERY_SCCC = 0x850a, + HNS_ROCE_OPC_RESET_SCCC = 0x850b, HNS_SWITCH_PARAMETER_CFG = 0x1033, }; @@ -1757,4 +1762,14 @@ struct hns_roce_wqe_atomic_seg { __le64 cmp_data; }; +struct hns_roce_sccc_clr { + __le32 qpn; + __le32 rsv[5]; +}; + +struct hns_roce_sccc_clr_done { + __le32 clr_done; + __le32 rsv[5]; +}; + #endif diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index dc9a30e3b7ce..73066bf38e47 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -811,6 +811,13 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, if (ret) goto err_qp; } + + if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL) { + ret = hr_dev->hw->qp_flow_control_init(hr_dev, hr_qp); + if (ret) + goto err_qp; + } + hr_qp->event = hns_roce_ib_qp_event; return 0; @@ -1152,6 +1159,7 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev) int reserved_from_bot; int ret; + mutex_init(&qp_table->scc_mutex); spin_lock_init(&qp_table->lock); INIT_RADIX_TREE(&hr_dev->qp_table_tree, GFP_ATOMIC); From 0e40dc2f70cda099e13392a26bd37aed24bcd25d Mon Sep 17 00:00:00 2001 From: Yangyang Li Date: Tue, 18 Dec 2018 21:21:55 +0800 Subject: [PATCH 0648/4277] RDMA/hns: Add timer allocation support for hip08 This patch adds qpc timer and cqc timer allocation support for hardware timeout retransmission in kernel space driver. Signed-off-by: Yangyang Li Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_cmd.h | 8 ++ drivers/infiniband/hw/hns/hns_roce_device.h | 14 +++ drivers/infiniband/hw/hns/hns_roce_hem.c | 42 ++++++++ drivers/infiniband/hw/hns/hns_roce_hem.h | 2 + drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 103 +++++++++++++++++++- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 24 +++++ drivers/infiniband/hw/hns/hns_roce_main.c | 36 +++++++ 7 files changed, 227 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_cmd.h b/drivers/infiniband/hw/hns/hns_roce_cmd.h index f682cb4b954b..059fd1da493e 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cmd.h +++ b/drivers/infiniband/hw/hns/hns_roce_cmd.h @@ -75,6 +75,10 @@ enum { HNS_ROCE_CMD_DESTROY_MPT_BT1 = 0x29, HNS_ROCE_CMD_DESTROY_MPT_BT2 = 0x2a, + /* CQC TIMER commands */ + HNS_ROCE_CMD_WRITE_CQC_TIMER_BT0 = 0x23, + HNS_ROCE_CMD_READ_CQC_TIMER_BT0 = 0x27, + /* MPT commands */ HNS_ROCE_CMD_QUERY_MPT = 0x62, @@ -89,6 +93,10 @@ enum { HNS_ROCE_CMD_DESTROY_SRQC_BT1 = 0x39, HNS_ROCE_CMD_DESTROY_SRQC_BT2 = 0x3a, + /* QPC TIMER commands */ + HNS_ROCE_CMD_WRITE_QPC_TIMER_BT0 = 0x33, + HNS_ROCE_CMD_READ_QPC_TIMER_BT0 = 0x37, + /* EQC commands */ HNS_ROCE_CMD_CREATE_AEQC = 0x80, HNS_ROCE_CMD_MODIFY_AEQC = 0x81, diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index bbcc56f8fc76..6fde434b22fd 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -732,6 +732,8 @@ struct hns_roce_caps { u32 max_extend_sg; int num_qps; /* 256k */ int reserved_qps; + int num_qpc_timer; + int num_cqc_timer; u32 max_srq_sg; int num_srqs; u32 max_wqes; /* 16k */ @@ -772,6 +774,8 @@ struct hns_roce_caps { int trrl_entry_sz; int cqc_entry_sz; int sccc_entry_sz; + int qpc_timer_entry_sz; + int cqc_timer_entry_sz; int srqc_entry_sz; int idx_entry_sz; u32 pbl_ba_pg_sz; @@ -781,8 +785,10 @@ struct hns_roce_caps { int ceqe_depth; enum ib_mtu max_mtu; u32 qpc_bt_num; + u32 qpc_timer_bt_num; u32 srqc_bt_num; u32 cqc_bt_num; + u32 cqc_timer_bt_num; u32 mpt_bt_num; u32 sccc_bt_num; u32 qpc_ba_pg_sz; @@ -803,6 +809,12 @@ struct hns_roce_caps { u32 sccc_ba_pg_sz; u32 sccc_buf_pg_sz; u32 sccc_hop_num; + u32 qpc_timer_ba_pg_sz; + u32 qpc_timer_buf_pg_sz; + u32 qpc_timer_hop_num; + u32 cqc_timer_ba_pg_sz; + u32 cqc_timer_buf_pg_sz; + u32 cqc_timer_hop_num; u32 cqe_ba_pg_sz; u32 cqe_buf_pg_sz; u32 cqe_hop_num; @@ -932,6 +944,8 @@ struct hns_roce_dev { struct hns_roce_srq_table srq_table; struct hns_roce_qp_table qp_table; struct hns_roce_eq_table eq_table; + struct hns_roce_hem_table qpc_timer_table; + struct hns_roce_hem_table cqc_timer_table; int cmd_mod; int loop_idc; diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index fc7b69e1b1f9..f1fec56f3ff4 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -46,6 +46,8 @@ bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type) (hr_dev->caps.cqc_hop_num && type == HEM_TYPE_CQC) || (hr_dev->caps.srqc_hop_num && type == HEM_TYPE_SRQC) || (hr_dev->caps.sccc_hop_num && type == HEM_TYPE_SCCC) || + (hr_dev->caps.qpc_timer_hop_num && type == HEM_TYPE_QPC_TIMER) || + (hr_dev->caps.cqc_timer_hop_num && type == HEM_TYPE_CQC_TIMER) || (hr_dev->caps.cqe_hop_num && type == HEM_TYPE_CQE) || (hr_dev->caps.mtt_hop_num && type == HEM_TYPE_MTT) || (hr_dev->caps.srqwqe_hop_num && type == HEM_TYPE_SRQWQE) || @@ -134,6 +136,22 @@ int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev, mhop->ba_l0_num = hr_dev->caps.sccc_bt_num; mhop->hop_num = hr_dev->caps.sccc_hop_num; break; + case HEM_TYPE_QPC_TIMER: + mhop->buf_chunk_size = 1 << (hr_dev->caps.qpc_timer_buf_pg_sz + + PAGE_SHIFT); + mhop->bt_chunk_size = 1 << (hr_dev->caps.qpc_timer_ba_pg_sz + + PAGE_SHIFT); + mhop->ba_l0_num = hr_dev->caps.qpc_timer_bt_num; + mhop->hop_num = hr_dev->caps.qpc_timer_hop_num; + break; + case HEM_TYPE_CQC_TIMER: + mhop->buf_chunk_size = 1 << (hr_dev->caps.cqc_timer_buf_pg_sz + + PAGE_SHIFT); + mhop->bt_chunk_size = 1 << (hr_dev->caps.cqc_timer_ba_pg_sz + + PAGE_SHIFT); + mhop->ba_l0_num = hr_dev->caps.cqc_timer_bt_num; + mhop->hop_num = hr_dev->caps.cqc_timer_hop_num; + break; case HEM_TYPE_SRQC: mhop->buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz + PAGE_SHIFT); @@ -602,6 +620,7 @@ out: mutex_unlock(&table->mutex); return ret; } +EXPORT_SYMBOL_GPL(hns_roce_table_get); static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev, struct hns_roce_hem_table *table, @@ -744,6 +763,7 @@ void hns_roce_table_put(struct hns_roce_dev *hr_dev, mutex_unlock(&table->mutex); } +EXPORT_SYMBOL_GPL(hns_roce_table_put); void *hns_roce_table_find(struct hns_roce_dev *hr_dev, struct hns_roce_hem_table *table, @@ -921,6 +941,22 @@ int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev, num_bt_l0 = hr_dev->caps.sccc_bt_num; hop_num = hr_dev->caps.sccc_hop_num; break; + case HEM_TYPE_QPC_TIMER: + buf_chunk_size = 1 << (hr_dev->caps.qpc_timer_buf_pg_sz + + PAGE_SHIFT); + bt_chunk_size = 1 << (hr_dev->caps.qpc_timer_ba_pg_sz + + PAGE_SHIFT); + num_bt_l0 = hr_dev->caps.qpc_timer_bt_num; + hop_num = hr_dev->caps.qpc_timer_hop_num; + break; + case HEM_TYPE_CQC_TIMER: + buf_chunk_size = 1 << (hr_dev->caps.cqc_timer_buf_pg_sz + + PAGE_SHIFT); + bt_chunk_size = 1 << (hr_dev->caps.cqc_timer_ba_pg_sz + + PAGE_SHIFT); + num_bt_l0 = hr_dev->caps.cqc_timer_bt_num; + hop_num = hr_dev->caps.cqc_timer_hop_num; + break; case HEM_TYPE_SRQC: buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz + PAGE_SHIFT); @@ -1098,6 +1134,12 @@ void hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev) hns_roce_cleanup_hem_table(hr_dev, &hr_dev->srq_table.table); hns_roce_cleanup_hem_table(hr_dev, &hr_dev->cq_table.table); + if (hr_dev->caps.qpc_timer_entry_sz) + hns_roce_cleanup_hem_table(hr_dev, + &hr_dev->qpc_timer_table); + if (hr_dev->caps.cqc_timer_entry_sz) + hns_roce_cleanup_hem_table(hr_dev, + &hr_dev->cqc_timer_table); if (hr_dev->caps.sccc_entry_sz) hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.sccc_table); diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.h b/drivers/infiniband/hw/hns/hns_roce_hem.h index b2aeb7422eee..d9d668992e49 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.h +++ b/drivers/infiniband/hw/hns/hns_roce_hem.h @@ -45,6 +45,8 @@ enum { HEM_TYPE_CQC, HEM_TYPE_SRQC, HEM_TYPE_SCCC, + HEM_TYPE_QPC_TIMER, + HEM_TYPE_CQC_TIMER, /* UNMAP HEM */ HEM_TYPE_MTT, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 5e57f5f93d49..d63dd155cd33 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -1085,6 +1085,41 @@ static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev) return 0; } +static int hns_roce_query_pf_timer_resource(struct hns_roce_dev *hr_dev) +{ + struct hns_roce_pf_timer_res_a *req_a; + struct hns_roce_cmq_desc desc[2]; + int ret, i; + + for (i = 0; i < 2; i++) { + hns_roce_cmq_setup_basic_desc(&desc[i], + HNS_ROCE_OPC_QUERY_PF_TIMER_RES, + true); + + if (i == 0) + desc[i].flag |= cpu_to_le16(HNS_ROCE_CMD_FLAG_NEXT); + else + desc[i].flag &= ~cpu_to_le16(HNS_ROCE_CMD_FLAG_NEXT); + } + + ret = hns_roce_cmq_send(hr_dev, desc, 2); + if (ret) + return ret; + + req_a = (struct hns_roce_pf_timer_res_a *)desc[0].data; + + hr_dev->caps.qpc_timer_bt_num = + roce_get_field(req_a->qpc_timer_bt_idx_num, + PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_M, + PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_S); + hr_dev->caps.cqc_timer_bt_num = + roce_get_field(req_a->cqc_timer_bt_idx_num, + PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_M, + PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_S); + + return 0; +} + static int hns_roce_set_vf_switch_param(struct hns_roce_dev *hr_dev, int vf_id) { @@ -1315,6 +1350,16 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev) return ret; } + if (hr_dev->pci_dev->revision == 0x21) { + ret = hns_roce_query_pf_timer_resource(hr_dev); + if (ret) { + dev_err(hr_dev->dev, + "Query pf timer resource fail, ret = %d.\n", + ret); + return ret; + } + } + ret = hns_roce_alloc_vf_resource(hr_dev); if (ret) { dev_err(hr_dev->dev, "Allocate vf resource fail, ret = %d.\n", @@ -1439,6 +1484,17 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev) HNS_ROCE_CAP_FLAG_SRQ | HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL; + caps->num_qpc_timer = HNS_ROCE_V2_MAX_QPC_TIMER_NUM; + caps->qpc_timer_entry_sz = HNS_ROCE_V2_QPC_TIMER_ENTRY_SZ; + caps->qpc_timer_ba_pg_sz = 0; + caps->qpc_timer_buf_pg_sz = 0; + caps->qpc_timer_hop_num = HNS_ROCE_HOP_NUM_0; + caps->num_cqc_timer = HNS_ROCE_V2_MAX_CQC_TIMER_NUM; + caps->cqc_timer_entry_sz = HNS_ROCE_V2_CQC_TIMER_ENTRY_SZ; + caps->cqc_timer_ba_pg_sz = 0; + caps->cqc_timer_buf_pg_sz = 0; + caps->cqc_timer_hop_num = HNS_ROCE_HOP_NUM_0; + caps->sccc_entry_sz = HNS_ROCE_V2_SCCC_ENTRY_SZ; caps->sccc_ba_pg_sz = 0; caps->sccc_buf_pg_sz = 0; @@ -1644,7 +1700,8 @@ static void hns_roce_free_link_table(struct hns_roce_dev *hr_dev, static int hns_roce_v2_init(struct hns_roce_dev *hr_dev) { struct hns_roce_v2_priv *priv = hr_dev->priv; - int ret; + int qpc_count, cqc_count; + int ret, i; /* TSQ includes SQ doorbell and ack doorbell */ ret = hns_roce_init_link_table(hr_dev, TSQ_LINK_TABLE); @@ -1659,8 +1716,40 @@ static int hns_roce_v2_init(struct hns_roce_dev *hr_dev) goto err_tpq_init_failed; } + /* Alloc memory for QPC Timer buffer space chunk*/ + for (qpc_count = 0; qpc_count < hr_dev->caps.qpc_timer_bt_num; + qpc_count++) { + ret = hns_roce_table_get(hr_dev, &hr_dev->qpc_timer_table, + qpc_count); + if (ret) { + dev_err(hr_dev->dev, "QPC Timer get failed\n"); + goto err_qpc_timer_failed; + } + } + + /* Alloc memory for CQC Timer buffer space chunk*/ + for (cqc_count = 0; cqc_count < hr_dev->caps.cqc_timer_bt_num; + cqc_count++) { + ret = hns_roce_table_get(hr_dev, &hr_dev->cqc_timer_table, + cqc_count); + if (ret) { + dev_err(hr_dev->dev, "CQC Timer get failed\n"); + goto err_cqc_timer_failed; + } + } + return 0; +err_cqc_timer_failed: + for (i = 0; i < cqc_count; i++) + hns_roce_table_put(hr_dev, &hr_dev->cqc_timer_table, i); + +err_qpc_timer_failed: + for (i = 0; i < qpc_count; i++) + hns_roce_table_put(hr_dev, &hr_dev->qpc_timer_table, i); + + hns_roce_free_link_table(hr_dev, &priv->tpq); + err_tpq_init_failed: hns_roce_free_link_table(hr_dev, &priv->tsq); @@ -2699,6 +2788,12 @@ static int hns_roce_v2_set_hem(struct hns_roce_dev *hr_dev, case HEM_TYPE_SCCC: op = HNS_ROCE_CMD_WRITE_SCCC_BT0; break; + case HEM_TYPE_QPC_TIMER: + op = HNS_ROCE_CMD_WRITE_QPC_TIMER_BT0; + break; + case HEM_TYPE_CQC_TIMER: + op = HNS_ROCE_CMD_WRITE_CQC_TIMER_BT0; + break; default: dev_warn(dev, "Table %d not to be written by mailbox!\n", table->type); @@ -2763,6 +2858,8 @@ static int hns_roce_v2_clear_hem(struct hns_roce_dev *hr_dev, op = HNS_ROCE_CMD_DESTROY_CQC_BT0; break; case HEM_TYPE_SCCC: + case HEM_TYPE_QPC_TIMER: + case HEM_TYPE_CQC_TIMER: break; case HEM_TYPE_SRQC: op = HNS_ROCE_CMD_DESTROY_SRQC_BT0; @@ -2773,7 +2870,9 @@ static int hns_roce_v2_clear_hem(struct hns_roce_dev *hr_dev, return 0; } - if (table->type == HEM_TYPE_SCCC) + if (table->type == HEM_TYPE_SCCC || + table->type == HEM_TYPE_QPC_TIMER || + table->type == HEM_TYPE_CQC_TIMER) return 0; op += step_idx; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 4da247d5ea18..242eeaeba761 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -46,11 +46,13 @@ #define HNS_ROCE_VF_SL_NUM 8 #define HNS_ROCE_V2_MAX_QP_NUM 0x2000 +#define HNS_ROCE_V2_MAX_QPC_TIMER_NUM 0x200 #define HNS_ROCE_V2_MAX_WQE_NUM 0x8000 #define HNS_ROCE_V2_MAX_SRQ 0x100000 #define HNS_ROCE_V2_MAX_SRQ_WR 0x8000 #define HNS_ROCE_V2_MAX_SRQ_SGE 0x100 #define HNS_ROCE_V2_MAX_CQ_NUM 0x8000 +#define HNS_ROCE_V2_MAX_CQC_TIMER_NUM 0x100 #define HNS_ROCE_V2_MAX_SRQ_NUM 0x100000 #define HNS_ROCE_V2_MAX_CQE_NUM 0x10000 #define HNS_ROCE_V2_MAX_SRQWQE_NUM 0x8000 @@ -85,6 +87,8 @@ #define HNS_ROCE_V2_MTT_ENTRY_SZ 64 #define HNS_ROCE_V2_CQE_ENTRY_SIZE 32 #define HNS_ROCE_V2_SCCC_ENTRY_SZ 32 +#define HNS_ROCE_V2_QPC_TIMER_ENTRY_SZ 4096 +#define HNS_ROCE_V2_CQC_TIMER_ENTRY_SZ 4096 #define HNS_ROCE_V2_PAGE_SIZE_SUPPORTED 0xFFFFF000 #define HNS_ROCE_V2_MAX_INNER_MTPT_NUM 2 #define HNS_ROCE_INVALID_LKEY 0x100 @@ -229,6 +233,7 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_ALLOC_VF_RES = 0x8401, HNS_ROCE_OPC_CFG_EXT_LLM = 0x8403, HNS_ROCE_OPC_CFG_TMOUT_LLM = 0x8404, + HNS_ROCE_OPC_QUERY_PF_TIMER_RES = 0x8406, HNS_ROCE_OPC_CFG_SGID_TB = 0x8500, HNS_ROCE_OPC_CFG_SMAC_TB = 0x8501, HNS_ROCE_OPC_POST_MB = 0x8504, @@ -1336,6 +1341,25 @@ struct hns_roce_pf_res_b { #define PF_RES_DATA_4_PF_SCCC_BT_NUM_S 9 #define PF_RES_DATA_4_PF_SCCC_BT_NUM_M GENMASK(17, 9) +struct hns_roce_pf_timer_res_a { + __le32 rsv0; + __le32 qpc_timer_bt_idx_num; + __le32 cqc_timer_bt_idx_num; + __le32 rsv[3]; +}; + +#define PF_RES_DATA_1_PF_QPC_TIMER_BT_IDX_S 0 +#define PF_RES_DATA_1_PF_QPC_TIMER_BT_IDX_M GENMASK(11, 0) + +#define PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_S 16 +#define PF_RES_DATA_1_PF_QPC_TIMER_BT_NUM_M GENMASK(28, 16) + +#define PF_RES_DATA_2_PF_CQC_TIMER_BT_IDX_S 0 +#define PF_RES_DATA_2_PF_CQC_TIMER_BT_IDX_M GENMASK(10, 0) + +#define PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_S 16 +#define PF_RES_DATA_2_PF_CQC_TIMER_BT_NUM_M GENMASK(27, 16) + struct hns_roce_vf_res_a { __le32 vf_id; __le32 vf_qpc_bt_idx_num; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 314586690f6e..67a8c4333f4f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -715,8 +715,44 @@ static int hns_roce_init_hem(struct hns_roce_dev *hr_dev) } } + if (hr_dev->caps.qpc_timer_entry_sz) { + ret = hns_roce_init_hem_table(hr_dev, + &hr_dev->qpc_timer_table, + HEM_TYPE_QPC_TIMER, + hr_dev->caps.qpc_timer_entry_sz, + hr_dev->caps.num_qpc_timer, 1); + if (ret) { + dev_err(dev, + "Failed to init QPC timer memory, aborting.\n"); + goto err_unmap_ctx; + } + } + + if (hr_dev->caps.cqc_timer_entry_sz) { + ret = hns_roce_init_hem_table(hr_dev, + &hr_dev->cqc_timer_table, + HEM_TYPE_CQC_TIMER, + hr_dev->caps.cqc_timer_entry_sz, + hr_dev->caps.num_cqc_timer, 1); + if (ret) { + dev_err(dev, + "Failed to init CQC timer memory, aborting.\n"); + goto err_unmap_qpc_timer; + } + } + return 0; +err_unmap_qpc_timer: + if (hr_dev->caps.qpc_timer_entry_sz) + hns_roce_cleanup_hem_table(hr_dev, + &hr_dev->qpc_timer_table); + +err_unmap_ctx: + if (hr_dev->caps.sccc_entry_sz) + hns_roce_cleanup_hem_table(hr_dev, + &hr_dev->qp_table.sccc_table); + err_unmap_idx: if (hr_dev->caps.num_idx_segs) hns_roce_cleanup_hem_table(hr_dev, From ff0244bb5924e4b94145df177976d4ed17fe5f29 Mon Sep 17 00:00:00 2001 From: Ira Weiny Date: Wed, 23 Jan 2019 14:02:24 -0500 Subject: [PATCH 0649/4277] RDMA/qib: Use GUP longterm for PSM page pining Similar to the core change commit 5f1d43de5416 ("IB/core: disable memory registration of filesystem-dax vmas") PSM should be prevented from using filesystem DAX pages. Signed-off-by: Ira Weiny Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qib/qib_user_pages.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c index 16543d5e80c3..075f09fb7ce3 100644 --- a/drivers/infiniband/hw/qib/qib_user_pages.c +++ b/drivers/infiniband/hw/qib/qib_user_pages.c @@ -67,10 +67,10 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages, } for (got = 0; got < num_pages; got += ret) { - ret = get_user_pages(start_page + got * PAGE_SIZE, - num_pages - got, - FOLL_WRITE | FOLL_FORCE, - p + got, NULL); + ret = get_user_pages_longterm(start_page + got * PAGE_SIZE, + num_pages - got, + FOLL_WRITE | FOLL_FORCE, + p + got, NULL); if (ret < 0) goto bail_release; } From 535005ca8e5e71918d64074032f4b9d4fef8981e Mon Sep 17 00:00:00 2001 From: Yuval Avnery Date: Tue, 22 Jan 2019 09:02:05 +0200 Subject: [PATCH 0650/4277] IB/core: Destroy QP if XRC QP fails The open-coded variant missed destroy of SELinux created QP, reuse already existing ib_detroy_qp() call and use this opportunity to clean ib_create_qp() from double prints and unclear exit paths. Reported-by: Parav Pandit Fixes: d291f1a65232 ("IB/core: Enforce PKey security on QPs") Signed-off-by: Yuval Avnery Reviewed-by: Parav Pandit Reviewed-by: Daniel Jurgens Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/verbs.c | 41 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index ac011836bb54..3220fb42ecce 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1106,8 +1106,8 @@ struct ib_qp *ib_open_qp(struct ib_xrcd *xrcd, } EXPORT_SYMBOL(ib_open_qp); -static struct ib_qp *ib_create_xrc_qp(struct ib_qp *qp, - struct ib_qp_init_attr *qp_init_attr) +static struct ib_qp *create_xrc_qp(struct ib_qp *qp, + struct ib_qp_init_attr *qp_init_attr) { struct ib_qp *real_qp = qp; @@ -1122,10 +1122,10 @@ static struct ib_qp *ib_create_xrc_qp(struct ib_qp *qp, qp = __ib_open_qp(real_qp, qp_init_attr->event_handler, qp_init_attr->qp_context); - if (!IS_ERR(qp)) - __ib_insert_xrcd_qp(qp_init_attr->xrcd, real_qp); - else - real_qp->device->ops.destroy_qp(real_qp); + if (IS_ERR(qp)) + return qp; + + __ib_insert_xrcd_qp(qp_init_attr->xrcd, real_qp); return qp; } @@ -1156,10 +1156,8 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, return qp; ret = ib_create_qp_security(qp, device); - if (ret) { - ib_destroy_qp(qp); - return ERR_PTR(ret); - } + if (ret) + goto err; qp->real_qp = qp; qp->qp_type = qp_init_attr->qp_type; @@ -1172,8 +1170,15 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, INIT_LIST_HEAD(&qp->sig_mrs); qp->port = 0; - if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) - return ib_create_xrc_qp(qp, qp_init_attr); + if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) { + struct ib_qp *xrc_qp = create_xrc_qp(qp, qp_init_attr); + + if (IS_ERR(xrc_qp)) { + ret = PTR_ERR(xrc_qp); + goto err; + } + return xrc_qp; + } qp->event_handler = qp_init_attr->event_handler; qp->qp_context = qp_init_attr->qp_context; @@ -1200,11 +1205,8 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, if (qp_init_attr->cap.max_rdma_ctxs) { ret = rdma_rw_init_mrs(qp, qp_init_attr); - if (ret) { - pr_err("failed to init MR pool ret= %d\n", ret); - ib_destroy_qp(qp); - return ERR_PTR(ret); - } + if (ret) + goto err; } /* @@ -1217,6 +1219,11 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, device->attrs.max_sge_rd); return qp; + +err: + ib_destroy_qp(qp); + return ERR_PTR(ret); + } EXPORT_SYMBOL(ib_create_qp); From 73052b0daee0b750b39af18460dfec683e4f5887 Mon Sep 17 00:00:00 2001 From: Varad Gautam Date: Thu, 24 Jan 2019 14:03:06 +0100 Subject: [PATCH 0651/4277] fs/devpts: always delete dcache dentry-s in dput() d_delete only unhashes an entry if it is reached with dentry->d_lockref.count != 1. Prior to commit 8ead9dd54716 ("devpts: more pty driver interface cleanups"), d_delete was called on a dentry from devpts_pty_kill with two references held, which would trigger the unhashing, and the subsequent dputs would release it. Commit 8ead9dd54716 reworked devpts_pty_kill to stop acquiring the second reference from d_find_alias, and the d_delete call left the dentries still on the hashed list without actually ever being dropped from dcache before explicit cleanup. This causes the number of negative dentries for devpts to pile up, and an `ls /dev/pts` invocation can take seconds to return. Provide always_delete_dentry() from simple_dentry_operations as .d_delete for devpts, to make the dentry be dropped from dcache. Without this cleanup, the number of dentries in /dev/pts/ can be grown arbitrarily as: `python -c 'import pty; pty.spawn(["ls", "/dev/pts"])'` A systemtap probe on dcache_readdir to count d_subdirs shows this count to increase with each pty spawn invocation above: probe kernel.function("dcache_readdir") { subdirs = &@cast($file->f_path->dentry, "dentry")->d_subdirs; p = subdirs; p = @cast(p, "list_head")->next; i = 0 while (p != subdirs) { p = @cast(p, "list_head")->next; i = i+1; } printf("number of dentries: %d\n", i); } Fixes: 8ead9dd54716 ("devpts: more pty driver interface cleanups") Signed-off-by: Varad Gautam Reported-by: Zheng Wang Reported-by: Brandon Schwartz Root-caused-by: Maximilian Heyne Root-caused-by: Nicolas Pernas Maradei CC: David Woodhouse CC: Maximilian Heyne CC: Stefan Nuernberger CC: Amit Shah CC: Linus Torvalds CC: Greg Kroah-Hartman CC: Al Viro CC: Christian Brauner CC: Eric W. Biederman CC: Matthew Wilcox CC: Eric Biggers CC: # 4.9+ Signed-off-by: Al Viro --- fs/devpts/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index c53814539070..553a3f3300ae 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -455,6 +455,7 @@ devpts_fill_super(struct super_block *s, void *data, int silent) s->s_blocksize_bits = 10; s->s_magic = DEVPTS_SUPER_MAGIC; s->s_op = &devpts_sops; + s->s_d_op = &simple_dentry_operations; s->s_time_gran = 1; error = -ENOMEM; From a30a67be7b6e1117e0c0f5bcf84328ccdb8d6205 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 4 Dec 2018 11:14:02 -0800 Subject: [PATCH 0652/4277] clk: socfpga: Don't have get_parent for single parent ops This driver creates a gate clk with the possibility to have multiple parents. That can cause problems if the common clk framework tries to call the get_parent() op and gets back a number that's larger than the number of parents the clk says it supports in clk_init_data::num_parents. Let's duplicate the clk_ops structure each time this function is called and drop the get/set parent ops when there is only one parent. This allows the framework to consider a number larger than clk_init_data::num_parents as an error condition of the get_parent() clk op, clearing the way for proper code. Cc: Jerome Brunet Cc: Masahiro Yamada Cc: Dinh Nguyen Tested-by: Dinh Nguyen Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-gate.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index aa7a6e6a15b6..73e03328d5c5 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -176,8 +176,7 @@ static struct clk_ops gateclk_ops = { .set_parent = socfpga_clk_set_parent, }; -static void __init __socfpga_gate_init(struct device_node *node, - const struct clk_ops *ops) +void __init socfpga_gate_init(struct device_node *node) { u32 clk_gate[2]; u32 div_reg[3]; @@ -188,12 +187,17 @@ static void __init __socfpga_gate_init(struct device_node *node, const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; + struct clk_ops *ops; int rc; socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); if (WARN_ON(!socfpga_clk)) return; + ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL); + if (WARN_ON(!ops)) + return; + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); if (rc) clk_gate[0] = 0; @@ -202,8 +206,8 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; socfpga_clk->hw.bit_idx = clk_gate[1]; - gateclk_ops.enable = clk_gate_ops.enable; - gateclk_ops.disable = clk_gate_ops.disable; + ops->enable = clk_gate_ops.enable; + ops->disable = clk_gate_ops.disable; } rc = of_property_read_u32(node, "fixed-divider", &fixed_div); @@ -234,6 +238,11 @@ static void __init __socfpga_gate_init(struct device_node *node, init.flags = 0; init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); + if (init.num_parents < 2) { + ops->get_parent = NULL; + ops->set_parent = NULL; + } + init.parent_names = parent_name; socfpga_clk->hw.hw.init = &init; @@ -246,8 +255,3 @@ static void __init __socfpga_gate_init(struct device_node *node, if (WARN_ON(rc)) return; } - -void __init socfpga_gate_init(struct device_node *node) -{ - __socfpga_gate_init(node, &gateclk_ops); -} From 921e88a4f9649cbd44b4995ebe9a52b384e2fef4 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 24 Dec 2018 00:40:25 -0600 Subject: [PATCH 0653/4277] clk: imx: imx7ulp: use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This issue was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Dong Aisheng Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx7ulp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index 4e18f629f823..ce306631e844 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -48,8 +48,8 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_SCG1_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SCG1_END), + GFP_KERNEL); if (!clk_data) return; @@ -136,8 +136,8 @@ static void __init imx7ulp_clk_pcc2_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_PCC2_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC2_END), + GFP_KERNEL); if (!clk_data) return; @@ -183,8 +183,8 @@ static void __init imx7ulp_clk_pcc3_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_PCC3_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC3_END), + GFP_KERNEL); if (!clk_data) return; @@ -228,8 +228,8 @@ static void __init imx7ulp_clk_smc1_init(struct device_node *np) struct clk_hw **clks; void __iomem *base; - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * - IMX7ULP_CLK_SMC1_END, GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SMC1_END), + GFP_KERNEL); if (!clk_data) return; From 664525b2d84abca1074c9546654ae9689de8a818 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 24 Jan 2019 13:12:04 -0800 Subject: [PATCH 0654/4277] device-dax: Auto-bind device after successful new_id The typical 'new_id' attribute behavior is to immediately attach a device to its driver after a new device-id is added. Implement this behavior for the dax bus. Reported-by: Alexander Duyck Reported-by: Brice Goglin Cc: Dave Hansen Signed-off-by: Dan Williams --- drivers/dax/bus.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index c620ad52d7e5..a410154d75fb 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -57,8 +57,13 @@ static int dax_match_id(struct dax_device_driver *dax_drv, struct device *dev) return match; } +enum id_action { + ID_REMOVE, + ID_ADD, +}; + static ssize_t do_id_store(struct device_driver *drv, const char *buf, - size_t count, bool add) + size_t count, enum id_action action) { struct dax_device_driver *dax_drv = to_dax_drv(drv); unsigned int region_id, id; @@ -77,7 +82,7 @@ static ssize_t do_id_store(struct device_driver *drv, const char *buf, mutex_lock(&dax_bus_lock); dax_id = __dax_match_id(dax_drv, buf); if (!dax_id) { - if (add) { + if (action == ID_ADD) { dax_id = kzalloc(sizeof(*dax_id), GFP_KERNEL); if (dax_id) { strncpy(dax_id->dev_name, buf, DAX_NAME_LEN); @@ -86,26 +91,33 @@ static ssize_t do_id_store(struct device_driver *drv, const char *buf, rc = -ENOMEM; } else /* nothing to remove */; - } else if (!add) { + } else if (action == ID_REMOVE) { list_del(&dax_id->list); kfree(dax_id); } else /* dax_id already added */; mutex_unlock(&dax_bus_lock); - return rc; + + if (rc < 0) + return rc; + if (action == ID_ADD) + rc = driver_attach(drv); + if (rc) + return rc; + return count; } static ssize_t new_id_store(struct device_driver *drv, const char *buf, size_t count) { - return do_id_store(drv, buf, count, true); + return do_id_store(drv, buf, count, ID_ADD); } static DRIVER_ATTR_WO(new_id); static ssize_t remove_id_store(struct device_driver *drv, const char *buf, size_t count) { - return do_id_store(drv, buf, count, false); + return do_id_store(drv, buf, count, ID_REMOVE); } static DRIVER_ATTR_WO(remove_id); From 463a554baa9feb31eb31281ff18f08d139f58acb Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Fri, 4 Jan 2019 22:48:26 +0100 Subject: [PATCH 0655/4277] clk: samsung: fix typo Fix spelling mistake: "lenght" -> "length" Signed-off-by: Matteo Croce Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index c3f309d7100d..9cfaca5fbcdb 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -26,7 +26,7 @@ struct samsung_clk_provider { void __iomem *reg_base; struct device *dev; spinlock_t lock; - /* clk_data must be the last entry due to variable lenght 'hws' array */ + /* clk_data must be the last entry due to variable length 'hws' array */ struct clk_hw_onecell_data clk_data; }; From 420601d25c0c8dd0749ccfb9bd59526c64b4cc35 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 10 Jan 2019 16:19:01 -0600 Subject: [PATCH 0656/4277] dt-bindings: clock: Convert fixed-clock binding to json-schema Convert the fixed-clock binding to DT schema format using json-schema. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Signed-off-by: Rob Herring [sboyd@kernel.org: Drop full stop on title] Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/fixed-clock.txt | 23 ---------- .../bindings/clock/fixed-clock.yaml | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 23 deletions(-) delete mode 100644 Documentation/devicetree/bindings/clock/fixed-clock.txt create mode 100644 Documentation/devicetree/bindings/clock/fixed-clock.yaml diff --git a/Documentation/devicetree/bindings/clock/fixed-clock.txt b/Documentation/devicetree/bindings/clock/fixed-clock.txt deleted file mode 100644 index 0641a663ad69..000000000000 --- a/Documentation/devicetree/bindings/clock/fixed-clock.txt +++ /dev/null @@ -1,23 +0,0 @@ -Binding for simple fixed-rate clock sources. - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be "fixed-clock". -- #clock-cells : from common clock binding; shall be set to 0. -- clock-frequency : frequency of clock in Hz. Should be a single cell. - -Optional properties: -- clock-accuracy : accuracy of clock in ppb (parts per billion). - Should be a single cell. -- clock-output-names : From common clock binding. - -Example: - clock { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <1000000000>; - clock-accuracy = <100>; - }; diff --git a/Documentation/devicetree/bindings/clock/fixed-clock.yaml b/Documentation/devicetree/bindings/clock/fixed-clock.yaml new file mode 100644 index 000000000000..b657ecd0ef1c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/fixed-clock.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/fixed-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Binding for simple fixed-rate clock sources + +maintainers: + - Michael Turquette + - Stephen Boyd + +properties: + compatible: + const: fixed-clock + + "#clock-cells": + const: 0 + + clock-frequency: true + + clock-accuracy: + description: accuracy of clock in ppb (parts per billion). + $ref: /schemas/types.yaml#/definitions/uint32 + + clock-output-names: + maxItems: 1 + +required: + - compatible + - "#clock-cells" + - clock-frequency + +additionalProperties: false + +examples: + - | + clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000000>; + clock-accuracy = <100>; + }; +... From f79bae1666f8af6ce0c2da0b2057675ba6185a25 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 10 Jan 2019 16:19:02 -0600 Subject: [PATCH 0657/4277] dt-bindings: clock: Convert fixed-factor-clock to json-schema Convert the fixed-factor-clock binding to DT schema format using json-schema. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Signed-off-by: Rob Herring [sboyd@kernel.org: Drop full stop on title] Signed-off-by: Stephen Boyd --- .../bindings/clock/fixed-factor-clock.txt | 28 ---------- .../bindings/clock/fixed-factor-clock.yaml | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 28 deletions(-) delete mode 100644 Documentation/devicetree/bindings/clock/fixed-factor-clock.txt create mode 100644 Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml diff --git a/Documentation/devicetree/bindings/clock/fixed-factor-clock.txt b/Documentation/devicetree/bindings/clock/fixed-factor-clock.txt deleted file mode 100644 index 189467a7188a..000000000000 --- a/Documentation/devicetree/bindings/clock/fixed-factor-clock.txt +++ /dev/null @@ -1,28 +0,0 @@ -Binding for simple fixed factor rate clock sources. - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be "fixed-factor-clock". -- #clock-cells : from common clock binding; shall be set to 0. -- clock-div: fixed divider. -- clock-mult: fixed multiplier. -- clocks: parent clock. - -Optional properties: -- clock-output-names : From common clock binding. - -Some clocks that require special treatments are also handled by that -driver, with the compatibles: - - allwinner,sun4i-a10-pll3-2x-clk - -Example: - clock { - compatible = "fixed-factor-clock"; - clocks = <&parentclk>; - #clock-cells = <0>; - clock-div = <2>; - clock-mult = <1>; - }; diff --git a/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml b/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml new file mode 100644 index 000000000000..b567f8092f8c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/fixed-factor-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Binding for simple fixed factor rate clock sources + +maintainers: + - Michael Turquette + - Stephen Boyd + +properties: + compatible: + enum: + - allwinner,sun4i-a10-pll3-2x-clk + - fixed-factor-clock + + "#clock-cells": + const: 0 + + clocks: + maxItems: 1 + + clock-div: + description: Fixed divider + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - minimum: 1 + + clock-mult: + description: Fixed multiplier + $ref: /schemas/types.yaml#/definitions/uint32 + + clock-output-names: + maxItems: 1 + +required: + - compatible + - clocks + - "#clock-cells" + - clock-div + - clock-mult + +additionalProperties: false + +examples: + - | + clock { + compatible = "fixed-factor-clock"; + clocks = <&parentclk>; + #clock-cells = <0>; + clock-div = <2>; + clock-mult = <1>; + }; +... From 108a459ef4cd17a28711d81092044e597b5c7618 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 25 Jan 2019 11:23:04 +0800 Subject: [PATCH 0658/4277] clk: sunxi-ng: sun8i-a23: Enable PLL-MIPI LDOs when ungating it The PLL-MIPI clock is somewhat special as it has its own LDOs which need to be turned on for this PLL to actually work and output a clock signal. Add the 2 LDO enable bits to the gate bits. Fixes: 5690879d93e8 ("clk: sunxi-ng: Add A23 CCU") Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun8i-a23.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index a4fa2945f230..4b5f8f4e4ab8 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -144,7 +144,7 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi", 8, 4, /* N */ 4, 2, /* K */ 0, 4, /* M */ - BIT(31), /* gate */ + BIT(31) | BIT(23) | BIT(22), /* gate */ BIT(28), /* lock */ CLK_SET_RATE_UNGATE); From 8cb8f16c62e5ea9c77ca7d25af761f4eaea670ba Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 22 Jan 2019 22:57:38 +0300 Subject: [PATCH 0659/4277] clk: renesas: rcar-gen3: Factor out cpg_reg_modify() There's quite often repeated sequence of a CPG register read-modify-write, so it seems worth factoring it out into a function -- this saves 68 bytes of the object code (AArch64 gcc 4.8.5) and simplifies protecting all such sequences with a spinlock in the next patch... Signed-off-by: Sergei Shtylyov Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rcar-gen3-cpg.c | 38 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index be2ccbd6d623..80d0808a7fe8 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -30,6 +30,16 @@ #define CPG_RCKCR_CKSEL BIT(15) /* RCLK Clock Source Select */ +static void cpg_reg_modify(void __iomem *reg, u32 clear, u32 set) +{ + u32 val; + + val = readl(reg); + val &= ~clear; + val |= set; + writel(val, reg); +}; + struct cpg_simple_notifier { struct notifier_block nb; void __iomem *reg; @@ -118,7 +128,6 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, struct cpg_z_clk *zclk = to_z_clk(hw); unsigned int mult; unsigned int i; - u32 val, kick; /* Factor of 2 is for fixed divider */ mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL * 2, parent_rate); @@ -127,17 +136,14 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK) return -EBUSY; - val = readl(zclk->reg) & ~zclk->mask; - val |= ((32 - mult) << __ffs(zclk->mask)) & zclk->mask; - writel(val, zclk->reg); + cpg_reg_modify(zclk->reg, zclk->mask, + ((32 - mult) << __ffs(zclk->mask)) & zclk->mask); /* * Set KICK bit in FRQCRB to update hardware setting and wait for * clock change completion. */ - kick = readl(zclk->kick_reg); - kick |= CPG_FRQCRB_KICK; - writel(kick, zclk->kick_reg); + cpg_reg_modify(zclk->kick_reg, 0, CPG_FRQCRB_KICK); /* * Note: There is no HW information about the worst case latency. @@ -266,12 +272,10 @@ static const struct sd_div_table cpg_sd_div_table[] = { static int cpg_sd_clock_enable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - u32 val = readl(clock->csn.reg); - val &= ~(CPG_SD_STP_MASK); - val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK; - - writel(val, clock->csn.reg); + cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK, + clock->div_table[clock->cur_div_idx].val & + CPG_SD_STP_MASK); return 0; } @@ -280,7 +284,7 @@ static void cpg_sd_clock_disable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - writel(readl(clock->csn.reg) | CPG_SD_STP_MASK, clock->csn.reg); + cpg_reg_modify(clock->csn.reg, 0, CPG_SD_STP_MASK); } static int cpg_sd_clock_is_enabled(struct clk_hw *hw) @@ -327,7 +331,6 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, { struct sd_clock *clock = to_sd_clock(hw); unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate); - u32 val; unsigned int i; for (i = 0; i < clock->div_num; i++) @@ -339,10 +342,9 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, clock->cur_div_idx = i; - val = readl(clock->csn.reg); - val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); - val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); - writel(val, clock->csn.reg); + cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK | CPG_SD_FC_MASK, + clock->div_table[i].val & + (CPG_SD_STP_MASK | CPG_SD_FC_MASK)); return 0; } From 875e8f6b0156c0ad56fd0c29c78e3f2f67ec0b16 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 22 Jan 2019 22:58:38 +0300 Subject: [PATCH 0660/4277] clk: renesas: rcar-gen3: Add spinlock Protect the CPG register read-modify-write sequence with a spinlock. Signed-off-by: Sergei Shtylyov Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rcar-gen3-cpg.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 80d0808a7fe8..0818691253af 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -30,14 +30,19 @@ #define CPG_RCKCR_CKSEL BIT(15) /* RCLK Clock Source Select */ +static spinlock_t cpg_lock; + static void cpg_reg_modify(void __iomem *reg, u32 clear, u32 set) { + unsigned long flags; u32 val; + spin_lock_irqsave(&cpg_lock, flags); val = readl(reg); val &= ~clear; val |= set; writel(val, reg); + spin_unlock_irqrestore(&cpg_lock, flags); }; struct cpg_simple_notifier { @@ -615,5 +620,8 @@ int __init rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config, if (attr) cpg_quirks = (uintptr_t)attr->data; pr_debug("%s: mode = 0x%x quirks = 0x%x\n", __func__, mode, cpg_quirks); + + spin_lock_init(&cpg_lock); + return 0; } From 13e4cdd785867a632516f9cb5dfbe5ba20822820 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Tue, 15 Jan 2019 13:58:26 +0000 Subject: [PATCH 0661/4277] arm64: uaccess: Cleanup get/put_user() __get/put_user_check() macro is made to return a value but this is never used. Get rid of them and just use directly __get/put_user_error() as a statement, reducing macro indirection. Also, take this opportunity to rename __get/put_user_err() as it gets a bit confusing having them along __get/put_user_error(). Signed-off-by: Julien Thierry Cc: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/uaccess.h | 36 +++++++++++--------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 547d7a0c9d05..8e408084b8c3 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -268,7 +268,7 @@ static inline void __user *__uaccess_mask_ptr(const void __user *ptr) : "+r" (err), "=&r" (x) \ : "r" (addr), "i" (-EFAULT)) -#define __get_user_err(x, ptr, err) \ +#define __raw_get_user(x, ptr, err) \ do { \ unsigned long __gu_val; \ __chk_user_ptr(ptr); \ @@ -297,28 +297,22 @@ do { \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ } while (0) -#define __get_user_check(x, ptr, err) \ -({ \ +#define __get_user_error(x, ptr, err) \ +do { \ __typeof__(*(ptr)) __user *__p = (ptr); \ might_fault(); \ if (access_ok(__p, sizeof(*__p))) { \ __p = uaccess_mask_ptr(__p); \ - __get_user_err((x), __p, (err)); \ + __raw_get_user((x), __p, (err)); \ } else { \ (x) = 0; (err) = -EFAULT; \ } \ -}) - -#define __get_user_error(x, ptr, err) \ -({ \ - __get_user_check((x), (ptr), (err)); \ - (void)0; \ -}) +} while (0) #define __get_user(x, ptr) \ ({ \ int __gu_err = 0; \ - __get_user_check((x), (ptr), __gu_err); \ + __get_user_error((x), (ptr), __gu_err); \ __gu_err; \ }) @@ -338,7 +332,7 @@ do { \ : "+r" (err) \ : "r" (x), "r" (addr), "i" (-EFAULT)) -#define __put_user_err(x, ptr, err) \ +#define __raw_put_user(x, ptr, err) \ do { \ __typeof__(*(ptr)) __pu_val = (x); \ __chk_user_ptr(ptr); \ @@ -366,28 +360,22 @@ do { \ uaccess_disable_not_uao(); \ } while (0) -#define __put_user_check(x, ptr, err) \ -({ \ +#define __put_user_error(x, ptr, err) \ +do { \ __typeof__(*(ptr)) __user *__p = (ptr); \ might_fault(); \ if (access_ok(__p, sizeof(*__p))) { \ __p = uaccess_mask_ptr(__p); \ - __put_user_err((x), __p, (err)); \ + __raw_put_user((x), __p, (err)); \ } else { \ (err) = -EFAULT; \ } \ -}) - -#define __put_user_error(x, ptr, err) \ -({ \ - __put_user_check((x), (ptr), (err)); \ - (void)0; \ -}) +} while (0) #define __put_user(x, ptr) \ ({ \ int __pu_err = 0; \ - __put_user_check((x), (ptr), __pu_err); \ + __put_user_error((x), (ptr), __pu_err); \ __pu_err; \ }) From 0bd3ef34d2a8dd4056560567073d8bfc5da92e39 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Tue, 15 Jan 2019 13:58:27 +0000 Subject: [PATCH 0662/4277] arm64: uaccess: Implement unsafe accessors Current implementation of get/put_user_unsafe default to get/put_user which toggle PAN before each access, despite having been told by the caller that multiple accesses to user memory were about to happen. Provide implementations for user_access_begin/end to turn PAN off/on and implement unsafe accessors that assume PAN was already turned off. Signed-off-by: Julien Thierry Cc: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/uaccess.h | 79 ++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 8e408084b8c3..6a70c75ed9f4 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -270,31 +270,26 @@ static inline void __user *__uaccess_mask_ptr(const void __user *ptr) #define __raw_get_user(x, ptr, err) \ do { \ - unsigned long __gu_val; \ - __chk_user_ptr(ptr); \ - uaccess_enable_not_uao(); \ switch (sizeof(*(ptr))) { \ case 1: \ - __get_user_asm("ldrb", "ldtrb", "%w", __gu_val, (ptr), \ + __get_user_asm("ldrb", "ldtrb", "%w", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ case 2: \ - __get_user_asm("ldrh", "ldtrh", "%w", __gu_val, (ptr), \ + __get_user_asm("ldrh", "ldtrh", "%w", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ case 4: \ - __get_user_asm("ldr", "ldtr", "%w", __gu_val, (ptr), \ + __get_user_asm("ldr", "ldtr", "%w", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ case 8: \ - __get_user_asm("ldr", "ldtr", "%x", __gu_val, (ptr), \ + __get_user_asm("ldr", "ldtr", "%x", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ default: \ BUILD_BUG(); \ } \ - uaccess_disable_not_uao(); \ - (x) = (__force __typeof__(*(ptr)))__gu_val; \ } while (0) #define __get_user_error(x, ptr, err) \ @@ -302,8 +297,13 @@ do { \ __typeof__(*(ptr)) __user *__p = (ptr); \ might_fault(); \ if (access_ok(__p, sizeof(*__p))) { \ + unsigned long __gu_val; \ + __chk_user_ptr(__p); \ __p = uaccess_mask_ptr(__p); \ - __raw_get_user((x), __p, (err)); \ + uaccess_enable_not_uao(); \ + __raw_get_user(__gu_val, __p, (err)); \ + uaccess_disable_not_uao(); \ + (x) = (__force __typeof__(*__p)) __gu_val; \ } else { \ (x) = 0; (err) = -EFAULT; \ } \ @@ -334,30 +334,26 @@ do { \ #define __raw_put_user(x, ptr, err) \ do { \ - __typeof__(*(ptr)) __pu_val = (x); \ - __chk_user_ptr(ptr); \ - uaccess_enable_not_uao(); \ switch (sizeof(*(ptr))) { \ case 1: \ - __put_user_asm("strb", "sttrb", "%w", __pu_val, (ptr), \ + __put_user_asm("strb", "sttrb", "%w", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ case 2: \ - __put_user_asm("strh", "sttrh", "%w", __pu_val, (ptr), \ + __put_user_asm("strh", "sttrh", "%w", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ case 4: \ - __put_user_asm("str", "sttr", "%w", __pu_val, (ptr), \ + __put_user_asm("str", "sttr", "%w", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ case 8: \ - __put_user_asm("str", "sttr", "%x", __pu_val, (ptr), \ + __put_user_asm("str", "sttr", "%x", (x), (ptr), \ (err), ARM64_HAS_UAO); \ break; \ default: \ BUILD_BUG(); \ } \ - uaccess_disable_not_uao(); \ } while (0) #define __put_user_error(x, ptr, err) \ @@ -365,9 +361,13 @@ do { \ __typeof__(*(ptr)) __user *__p = (ptr); \ might_fault(); \ if (access_ok(__p, sizeof(*__p))) { \ + __typeof__(*(ptr)) __pu_val = (x); \ + __chk_user_ptr(__p); \ __p = uaccess_mask_ptr(__p); \ - __raw_put_user((x), __p, (err)); \ - } else { \ + uaccess_enable_not_uao(); \ + __raw_put_user(__pu_val, __p, (err)); \ + uaccess_disable_not_uao(); \ + } else { \ (err) = -EFAULT; \ } \ } while (0) @@ -381,6 +381,45 @@ do { \ #define put_user __put_user +static __must_check inline bool user_access_begin(const void __user *ptr, + size_t len) +{ + if (unlikely(!access_ok(ptr, len))) + return false; + + uaccess_enable_not_uao(); + return true; +} +#define user_access_begin(ptr, len) user_access_begin(ptr, len) +#define user_access_end() uaccess_disable_not_uao() + +#define unsafe_get_user(x, ptr, err) \ +do { \ + __typeof__(*(ptr)) __user *__p = (ptr); \ + unsigned long __gu_val; \ + int __gu_err = 0; \ + might_fault(); \ + __chk_user_ptr(__p); \ + __p = uaccess_mask_ptr(__p); \ + __raw_get_user(__gu_val, __p, __gu_err); \ + (x) = (__force __typeof__(*__p)) __gu_val; \ + if (__gu_err != 0) \ + goto err; \ +} while (0) + +#define unsafe_put_user(x, ptr, err) \ +do { \ + __typeof__(*(ptr)) __user *__p = (ptr); \ + __typeof__(*(ptr)) __pu_val = (x); \ + int __pu_err = 0; \ + might_fault(); \ + __chk_user_ptr(__p); \ + __p = uaccess_mask_ptr(__p); \ + __raw_put_user(__pu_val, __p, __pu_err); \ + if (__pu_err != 0) \ + goto err; \ +} while (0) + extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n); #define raw_copy_from_user(to, from, n) \ ({ \ From 1455dddc74bd0951a4ea7e77917ebf3a43cc46d8 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 11 Jan 2019 14:30:42 -0200 Subject: [PATCH 0663/4277] media: imx274: remote unused function imx274_read_reg imx274_read_reg() is not used since commit ca017467c78b ("media: imx274: add helper to read multibyte registers"). Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 95e3d90309e8..c2ac2fd1b96b 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -617,24 +617,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) return 0; } -static inline int imx274_read_reg(struct stimx274 *priv, u16 addr, u8 *val) -{ - unsigned int uint_val; - int err; - - err = regmap_read(priv->regmap, addr, &uint_val); - if (err) - dev_err(&priv->client->dev, - "%s : i2c read failed, addr = %x\n", __func__, addr); - else - dev_dbg(&priv->client->dev, - "%s : addr 0x%x, val=0x%x\n", __func__, - addr, uint_val); - - *val = uint_val; - return err; -} - static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) { int err; From 879347f0c258cffe27b9e28bd4f1f55fc019f857 Mon Sep 17 00:00:00 2001 From: Ben Kao Date: Tue, 15 Jan 2019 06:30:29 -0200 Subject: [PATCH 0664/4277] media: ov8856: Add support for OV8856 sensor This patch adds driver for Omnivision's ov8856 sensor, the driver supports following features: - manual exposure/gain(analog and digital) control support - two link frequencies - VBLANK/HBLANK support - test pattern support - media controller support - runtime PM support - enable Vsync signal output - supported resolutions + 3280x2464 at 30FPS + 1640x1232 at 30FPS Signed-off-by: Ben Kao Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov8856.c | 1267 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1287 insertions(+) create mode 100644 drivers/media/i2c/ov8856.c diff --git a/MAINTAINERS b/MAINTAINERS index 17ad1d7b5510..3e211916d2bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11234,6 +11234,13 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/ov9640.* +OMNIVISION OV8856 SENSOR DRIVER +M: Ben Kao +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/ov8856.c + OMNIVISION OV9650 SENSOR DRIVER M: Sakari Ailus R: Akinobu Mita diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e6cd5cf7c3d9..54a26e24baf4 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -820,6 +820,18 @@ config VIDEO_OV7740 This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. +config VIDEO_OV8856 + tristate "OmniVision OV8856 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV8856 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called ov8856. + config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5639cb2f20aa..29bf3fa81113 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +obj-$(CONFIG_VIDEO_OV8856) += ov8856.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c new file mode 100644 index 000000000000..c0d44082c003 --- /dev/null +++ b/drivers/media/i2c/ov8856.c @@ -0,0 +1,1267 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV8856_REG_VALUE_08BIT 1 +#define OV8856_REG_VALUE_16BIT 2 +#define OV8856_REG_VALUE_24BIT 3 + +#define OV8856_LINK_FREQ_360MHZ 360000000ULL +#define OV8856_LINK_FREQ_180MHZ 180000000ULL +#define OV8856_SCLK 144000000ULL +#define OV8856_MCLK 19200000 +#define OV8856_DATA_LANES 4 +#define OV8856_RGB_DEPTH 10 + +#define OV8856_REG_CHIP_ID 0x300a +#define OV8856_CHIP_ID 0x00885a + +#define OV8856_REG_MODE_SELECT 0x0100 +#define OV8856_MODE_STANDBY 0x00 +#define OV8856_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV8856_REG_VTS 0x380e +#define OV8856_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV8856_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OV8856_REG_EXPOSURE 0x3500 +#define OV8856_EXPOSURE_MIN 6 +#define OV8856_EXPOSURE_MAX_MARGIN 6 +#define OV8856_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV8856_REG_ANALOG_GAIN 0x3508 +#define OV8856_ANAL_GAIN_MIN 128 +#define OV8856_ANAL_GAIN_MAX 2047 +#define OV8856_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV8856_REG_MWB_R_GAIN 0x5019 +#define OV8856_REG_MWB_G_GAIN 0x501b +#define OV8856_REG_MWB_B_GAIN 0x501d +#define OV8856_DGTL_GAIN_MIN 0 +#define OV8856_DGTL_GAIN_MAX 4095 +#define OV8856_DGTL_GAIN_STEP 1 +#define OV8856_DGTL_GAIN_DEFAULT 1024 + +/* Test Pattern Control */ +#define OV8856_REG_TEST_PATTERN 0x5e00 +#define OV8856_TEST_PATTERN_ENABLE BIT(7) +#define OV8856_TEST_PATTERN_BAR_SHIFT 2 + +#define to_ov8856(_sd) container_of(_sd, struct ov8856, sd) + +enum { + OV8856_LINK_FREQ_720MBPS, + OV8856_LINK_FREQ_360MBPS, +}; + +struct ov8856_reg { + u16 address; + u8 val; +}; + +struct ov8856_reg_list { + u32 num_of_regs; + const struct ov8856_reg *regs; +}; + +struct ov8856_link_freq_config { + const struct ov8856_reg_list reg_list; +}; + +struct ov8856_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov8856_reg_list reg_list; +}; + +static const struct ov8856_reg mipi_data_rate_720mbps[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0302, 0x4b}, + {0x0303, 0x01}, + {0x030b, 0x02}, + {0x030d, 0x4b}, + {0x031e, 0x0c}, +}; + +static const struct ov8856_reg mipi_data_rate_360mbps[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0302, 0x4b}, + {0x0303, 0x03}, + {0x030b, 0x02}, + {0x030d, 0x4b}, + {0x031e, 0x0c}, +}; + +static const struct ov8856_reg mode_3280x2464_regs[] = { + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x20}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x20}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366e, 0x10}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x23}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x04}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x07}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa6}, + {0x3808, 0x0c}, + {0x3809, 0xd0}, + {0x380a, 0x09}, + {0x380b, 0xa0}, + {0x380c, 0x07}, + {0x380d, 0x88}, + {0x380e, 0x09}, + {0x380f, 0xb8}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x00}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x0b}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x58}, + {0x481f, 0x27}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x57}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5795, 0x02}, + {0x5796, 0x20}, + {0x5797, 0x20}, + {0x5798, 0xd5}, + {0x5799, 0xd5}, + {0x579a, 0x00}, + {0x579b, 0x50}, + {0x579c, 0x00}, + {0x579d, 0x2c}, + {0x579e, 0x0c}, + {0x579f, 0x40}, + {0x57a0, 0x09}, + {0x57a1, 0x40}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00} +}; + +static const struct ov8856_reg mode_1640x1232_regs[] = { + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x20}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366e, 0x08}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x27}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x14}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x07}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa6}, + {0x3808, 0x06}, + {0x3809, 0x68}, + {0x380a, 0x04}, + {0x380b, 0xd0}, + {0x380c, 0x0e}, + {0x380d, 0xec}, + {0x380e, 0x04}, + {0x380f, 0xe8}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x00}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x90}, + {0x3821, 0x67}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x58}, + {0x481f, 0x27}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x57}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579e, 0x06}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00} +}; + +static const char * const ov8856_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar" +}; + +static const s64 link_freq_menu_items[] = { + OV8856_LINK_FREQ_360MHZ, + OV8856_LINK_FREQ_180MHZ +}; + +static const struct ov8856_link_freq_config link_freq_configs[] = { + [OV8856_LINK_FREQ_720MBPS] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps), + .regs = mipi_data_rate_720mbps, + } + }, + [OV8856_LINK_FREQ_360MBPS] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps), + .regs = mipi_data_rate_360mbps, + } + } +}; + +static const struct ov8856_mode supported_modes[] = { + { + .width = 3280, + .height = 2464, + .hts = 1928, + .vts_def = 2488, + .vts_min = 2488, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_720MBPS, + }, + { + .width = 1640, + .height = 1232, + .hts = 3820, + .vts_def = 1256, + .vts_min = 1256, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), + .regs = mode_1640x1232_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_360MBPS, + } +}; + +struct ov8856 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov8856_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV8856_DATA_LANES; + + do_div(pixel_rate, OV8856_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV8856_SCLK); + + return ppl; +} + +static int ov8856_read_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] = {reg >> 8, reg & 0xff}; + u8 data_buf[4] = {0, }; + int ret; + + if (len > 4) + return -EINVAL; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int ov8856_write_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov8856_write_reg_list(struct ov8856 *ov8856, + const struct ov8856_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov8856_write_reg(ov8856, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov8856_update_digital_gain(struct ov8856 *ov8856, u32 d_gain) +{ + int ret; + + ret = ov8856_write_reg(ov8856, OV8856_REG_MWB_R_GAIN, + OV8856_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = ov8856_write_reg(ov8856, OV8856_REG_MWB_G_GAIN, + OV8856_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + return ov8856_write_reg(ov8856, OV8856_REG_MWB_B_GAIN, + OV8856_REG_VALUE_16BIT, d_gain); +} + +static int ov8856_test_pattern(struct ov8856 *ov8856, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OV8856_TEST_PATTERN_BAR_SHIFT | + OV8856_TEST_PATTERN_ENABLE; + + return ov8856_write_reg(ov8856, OV8856_REG_TEST_PATTERN, + OV8856_REG_VALUE_08BIT, pattern); +} + +static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov8856 *ov8856 = container_of(ctrl->handler, + struct ov8856, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov8856->cur_mode->height + ctrl->val - + OV8856_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov8856->exposure, + ov8856->exposure->minimum, + exposure_max, ov8856->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov8856_write_reg(ov8856, OV8856_REG_ANALOG_GAIN, + OV8856_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov8856_update_digital_gain(ov8856, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov8856_write_reg(ov8856, OV8856_REG_EXPOSURE, + OV8856_REG_VALUE_24BIT, ctrl->val << 4); + break; + + case V4L2_CID_VBLANK: + ret = ov8856_write_reg(ov8856, OV8856_REG_VTS, + OV8856_REG_VALUE_16BIT, + ov8856->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov8856_test_pattern(ov8856, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov8856_ctrl_ops = { + .s_ctrl = ov8856_set_ctrl, +}; + +static int ov8856_init_controls(struct ov8856 *ov8856) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &ov8856->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov8856->mutex; + ov8856->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (ov8856->link_freq) + ov8856->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov8856->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(OV8856_LINK_FREQ_720MBPS), + 1, + to_pixel_rate(OV8856_LINK_FREQ_720MBPS)); + ov8856->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_VBLANK, + ov8856->cur_mode->vts_min - ov8856->cur_mode->height, + OV8856_VTS_MAX - ov8856->cur_mode->height, 1, + ov8856->cur_mode->vts_def - ov8856->cur_mode->height); + h_blank = to_pixels_per_line(ov8856->cur_mode->hts, + ov8856->cur_mode->link_freq_index) - ov8856->cur_mode->width; + ov8856->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (ov8856->hblank) + ov8856->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV8856_ANAL_GAIN_MIN, OV8856_ANAL_GAIN_MAX, + OV8856_ANAL_GAIN_STEP, OV8856_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV8856_DGTL_GAIN_MIN, OV8856_DGTL_GAIN_MAX, + OV8856_DGTL_GAIN_STEP, OV8856_DGTL_GAIN_DEFAULT); + exposure_max = ov8856->cur_mode->vts_def - OV8856_EXPOSURE_MAX_MARGIN; + ov8856->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_EXPOSURE, + OV8856_EXPOSURE_MIN, exposure_max, + OV8856_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov8856_test_pattern_menu) - 1, + 0, 0, ov8856_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov8856->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov8856_update_pad_format(const struct ov8856_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov8856_start_streaming(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + const struct ov8856_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = ov8856->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = ov8856_write_reg_list(ov8856, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov8856->cur_mode->reg_list; + ret = ov8856_write_reg_list(ov8856, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov8856->sd.ctrl_handler); + if (ret) + return ret; + + ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, + OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void ov8856_stop_streaming(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + + if (ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, + OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int ov8856_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov8856->streaming == enable) + return 0; + + mutex_lock(&ov8856->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov8856->mutex); + return ret; + } + + ret = ov8856_start_streaming(ov8856); + if (ret) { + enable = 0; + ov8856_stop_streaming(ov8856); + pm_runtime_put(&client->dev); + } + } else { + ov8856_stop_streaming(ov8856); + pm_runtime_put(&client->dev); + } + + ov8856->streaming = enable; + mutex_unlock(&ov8856->mutex); + + return ret; +} + +static int __maybe_unused ov8856_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + + mutex_lock(&ov8856->mutex); + if (ov8856->streaming) + ov8856_stop_streaming(ov8856); + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int __maybe_unused ov8856_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + int ret; + + mutex_lock(&ov8856->mutex); + if (ov8856->streaming) { + ret = ov8856_start_streaming(ov8856); + if (ret) { + ov8856->streaming = false; + ov8856_stop_streaming(ov8856); + mutex_unlock(&ov8856->mutex); + return ret; + } + } + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int ov8856_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + const struct ov8856_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov8856->mutex); + ov8856_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + ov8856->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov8856->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov8856->vblank, + mode->vts_min - mode->height, + OV8856_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov8856->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov8856->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int ov8856_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + mutex_lock(&ov8856->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, cfg, + fmt->pad); + else + ov8856_update_pad_format(ov8856->cur_mode, &fmt->format); + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int ov8856_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* Only one bayer order GRBG is supported */ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov8856_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + mutex_lock(&ov8856->mutex); + ov8856_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov8856_video_ops = { + .s_stream = ov8856_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov8856_pad_ops = { + .set_fmt = ov8856_set_format, + .get_fmt = ov8856_get_format, + .enum_mbus_code = ov8856_enum_mbus_code, + .enum_frame_size = ov8856_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov8856_subdev_ops = { + .video = &ov8856_video_ops, + .pad = &ov8856_pad_ops, +}; + +static const struct media_entity_operations ov8856_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov8856_internal_ops = { + .open = ov8856_open, +}; + +static int ov8856_identify_module(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + int ret; + u32 val; + + ret = ov8856_read_reg(ov8856, OV8856_REG_CHIP_ID, + OV8856_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OV8856_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV8856_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int ov8856_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (mclk != OV8856_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV8856_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov8856_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov8856->mutex); + + return 0; +} + +static int ov8856_probe(struct i2c_client *client) +{ + struct ov8856 *ov8856; + int ret; + + ret = ov8856_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL); + if (!ov8856) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops); + ret = ov8856_identify_module(ov8856); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&ov8856->mutex); + ov8856->cur_mode = &supported_modes[0]; + ret = ov8856_init_controls(ov8856); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov8856->sd.internal_ops = &ov8856_internal_ops; + ov8856->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov8856->sd.entity.ops = &ov8856_subdev_entity_ops; + ov8856->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov8856->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov8856->sd.entity, 1, &ov8856->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&ov8856->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov8856->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov8856->sd.ctrl_handler); + mutex_destroy(&ov8856->mutex); + + return ret; +} + +static const struct dev_pm_ops ov8856_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov8856_suspend, ov8856_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov8856_acpi_ids[] = { + {"OVTI8856"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov8856_acpi_ids); +#endif + +static struct i2c_driver ov8856_i2c_driver = { + .driver = { + .name = "ov8856", + .pm = &ov8856_pm_ops, + .acpi_match_table = ACPI_PTR(ov8856_acpi_ids), + }, + .probe_new = ov8856_probe, + .remove = ov8856_remove, +}; + +module_i2c_driver(ov8856_i2c_driver); + +MODULE_AUTHOR("Ben Kao "); +MODULE_DESCRIPTION("OmniVision OV8856 sensor driver"); +MODULE_LICENSE("GPL v2"); From f1fb085562411f71ab9259fc131b78d93406c907 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 Jan 2019 06:54:44 -0200 Subject: [PATCH 0665/4277] media: ov7670: Remove useless use of a ret variable Instead of assigning the return value to ret and then checking and returning it, just return the value to the caller directly. The success value is always 0. Signed-off-by: Sakari Ailus Acked-by: Lubomir Rintel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 4939a83b50e4..61c47c61c693 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -859,11 +859,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, /* Recalculate frame rate */ ov7675_get_framerate(sd, tpf); - ret = ov7670_write(sd, REG_CLKRC, info->clkrc); - if (ret < 0) - return ret; - - return 0; + return ov7670_write(sd, REG_CLKRC, info->clkrc); } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, From 5556ab2a3f2c91f09b53c761972bce8af72174bc Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:47 -0200 Subject: [PATCH 0666/4277] media: ov7670: split register setting from set_fmt() logic This will allow us to restore the last set format after the device returns from a power off. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 80 ++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 61c47c61c693..386c493362f3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -241,6 +241,7 @@ struct ov7670_info { }; struct v4l2_mbus_framefmt format; struct ov7670_format_struct *fmt; /* Current format */ + struct ov7670_win_size *wsize; struct clk *clk; struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; @@ -1000,48 +1001,20 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, return 0; } -/* - * Set a format. - */ -static int ov7670_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) +static int ov7670_apply_fmt(struct v4l2_subdev *sd) { - struct ov7670_format_struct *ovfmt; - struct ov7670_win_size *wsize; struct ov7670_info *info = to_state(sd); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - struct v4l2_mbus_framefmt *mbus_fmt; -#endif + struct ov7670_win_size *wsize = info->wsize; unsigned char com7, com10 = 0; int ret; - if (format->pad) - return -EINVAL; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); - if (ret) - return ret; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); - *mbus_fmt = format->format; - return 0; -#else - return -ENOTTY; -#endif - } - - ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize); - if (ret) - return ret; /* * COM7 is a pain in the ass, it doesn't like to be read then * quickly written afterward. But we have everything we need * to set it absolutely here, as long as the format-specific * register sets list it first. */ - com7 = ovfmt->regs[0].value; + com7 = info->fmt->regs[0].value; com7 |= wsize->com7_bit; ret = ov7670_write(sd, REG_COM7, com7); if (ret) @@ -1063,7 +1036,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, /* * Now write the rest of the array. Also store start/stops */ - ret = ov7670_write_array(sd, ovfmt->regs + 1); + ret = ov7670_write_array(sd, info->fmt->regs + 1); if (ret) return ret; @@ -1078,8 +1051,6 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, return ret; } - info->fmt = ovfmt; - /* * If we're running RGB565, we must rewrite clkrc after setting * the other parameters or the image looks poor. If we're *not* @@ -1097,6 +1068,46 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, return 0; } +/* + * Set a format. + */ +static int ov7670_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov7670_info *info = to_state(sd); +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *mbus_fmt; +#endif + int ret; + + if (format->pad) + return -EINVAL; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); + if (ret) + return ret; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + *mbus_fmt = format->format; + return 0; +#else + return -ENOTTY; +#endif + } + + ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize); + if (ret) + return ret; + + ret = ov7670_apply_fmt(sd); + if (ret) + return ret; + + return 0; +} + static int ov7670_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -1843,6 +1854,7 @@ static int ov7670_probe(struct i2c_client *client, info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; + info->wsize = &info->devtype->win_sizes[0]; ov7670_get_default_format(sd, &info->format); From 40012cd5ecaa48ad7bf36cb477d4d6f033639d0e Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:48 -0200 Subject: [PATCH 0667/4277] media: ov7670: split register setting from set_framerate() logic This will allow us to restore the last set frame rate after the device returns from a power off. [sakari.ailus@linux.intel.com>: Wrap a line over 80 characters] Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 386c493362f3..6ab88626c760 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -811,13 +811,25 @@ static void ov7675_get_framerate(struct v4l2_subdev *sd, (4 * clkrc); } +static int ov7675_apply_framerate(struct v4l2_subdev *sd) +{ + struct ov7670_info *info = to_state(sd); + int ret; + + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + if (ret < 0) + return ret; + + return ov7670_write(sd, REG_DBLV, + info->pll_bypass ? DBLV_BYPASS : DBLV_X4); +} + static int ov7675_set_framerate(struct v4l2_subdev *sd, struct v4l2_fract *tpf) { struct ov7670_info *info = to_state(sd); u32 clkrc; int pll_factor; - int ret; /* * The formula is fps = 5/4*pixclk for YUV/RGB and @@ -826,19 +838,10 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, * pixclk = clock_speed / (clkrc + 1) * PLLfactor * */ - if (info->pll_bypass) { - pll_factor = 1; - ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); - } else { - pll_factor = PLL_FACTOR; - ret = ov7670_write(sd, REG_DBLV, DBLV_X4); - } - if (ret < 0) - return ret; - if (tpf->numerator == 0 || tpf->denominator == 0) { clkrc = 0; } else { + pll_factor = info->pll_bypass ? 1 : PLL_FACTOR; clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / (4 * tpf->denominator); if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) @@ -860,7 +863,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, /* Recalculate frame rate */ ov7675_get_framerate(sd, tpf); - return ov7670_write(sd, REG_CLKRC, info->clkrc); + return ov7675_apply_framerate(sd); } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, From 3d6a8fe256059034ddd592f1e169da5ff9d28d7e Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:45 -0200 Subject: [PATCH 0668/4277] media: ov7670: hook s_power onto v4l2 core The commit 71862f63f351 ("media: ov7670: Add the ov7670_s_power function") added a power control routing. However, it was not good enough to use as a s_power() callback: it merely flipped on the power GPIOs without restoring the register settings. Fix this now and register an actual power callback. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 50 +++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 6ab88626c760..a5b591dde10c 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -243,6 +243,7 @@ struct ov7670_info { struct ov7670_format_struct *fmt; /* Current format */ struct ov7670_win_size *wsize; struct clk *clk; + int on; struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; unsigned int mbus_config; /* Media bus configuration flags */ @@ -1617,19 +1618,54 @@ static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regis } #endif -static int ov7670_s_power(struct v4l2_subdev *sd, int on) +static void ov7670_power_on(struct v4l2_subdev *sd) { struct ov7670_info *info = to_state(sd); + if (info->on) + return; + if (info->pwdn_gpio) - gpiod_set_value(info->pwdn_gpio, !on); - if (on && info->resetb_gpio) { + gpiod_set_value(info->pwdn_gpio, 0); + if (info->resetb_gpio) { gpiod_set_value(info->resetb_gpio, 1); usleep_range(500, 1000); gpiod_set_value(info->resetb_gpio, 0); usleep_range(3000, 5000); } + info->on = true; +} + +static void ov7670_power_off(struct v4l2_subdev *sd) +{ + struct ov7670_info *info = to_state(sd); + + if (!info->on) + return; + + if (info->pwdn_gpio) + gpiod_set_value(info->pwdn_gpio, 1); + + info->on = false; +} + +static int ov7670_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov7670_info *info = to_state(sd); + + if (info->on == on) + return 0; + + if (on) { + ov7670_power_on (sd); + ov7670_apply_fmt(sd); + ov7675_apply_framerate(sd); + v4l2_ctrl_handler_setup(&info->hdl); + } else { + ov7670_power_off (sd); + } + return 0; } @@ -1662,6 +1698,7 @@ static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_core_ops ov7670_core_ops = { .reset = ov7670_reset, .init = ov7670_init, + .s_power = ov7670_s_power, .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, @@ -1826,6 +1863,7 @@ static int ov7670_probe(struct i2c_client *client, else return ret; } + if (info->clk) { ret = clk_prepare_enable(info->clk); if (ret) @@ -1842,7 +1880,7 @@ static int ov7670_probe(struct i2c_client *client, if (ret) goto clk_disable; - ov7670_s_power(sd, 1); + ov7670_power_on(sd); /* Make sure it's an ov7670 */ ret = ov7670_detect(sd); @@ -1930,7 +1968,7 @@ entity_cleanup: hdl_free: v4l2_ctrl_handler_free(&info->hdl); power_off: - ov7670_s_power(sd, 0); + ov7670_power_off(sd); clk_disable: clk_disable_unprepare(info->clk); return ret; @@ -1946,7 +1984,7 @@ static int ov7670_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&info->hdl); clk_disable_unprepare(info->clk); media_entity_cleanup(&info->sd.entity); - ov7670_s_power(sd, 0); + ov7670_power_off(sd); return 0; } From 030f9f682e66ea3be214f673ca8ba53294eeecd2 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:46 -0200 Subject: [PATCH 0669/4277] media: ov7670: control clock along with power This provides more power saving when the sensor is off. While at that, do the delay on power/clock enable even if the sensor driver itself doesn't control the GPIOs. This is required for the OLPC XO-1 platform, that lacks the proper power/reset properties in its DT, but needs the delay after the sensor is clocked up. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index a5b591dde10c..a7d26b294eb5 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1625,14 +1625,17 @@ static void ov7670_power_on(struct v4l2_subdev *sd) if (info->on) return; + clk_prepare_enable(info->clk); + if (info->pwdn_gpio) gpiod_set_value(info->pwdn_gpio, 0); if (info->resetb_gpio) { gpiod_set_value(info->resetb_gpio, 1); usleep_range(500, 1000); gpiod_set_value(info->resetb_gpio, 0); - usleep_range(3000, 5000); } + if (info->pwdn_gpio || info->resetb_gpio || info->clk) + usleep_range(3000, 5000); info->on = true; } @@ -1644,6 +1647,8 @@ static void ov7670_power_off(struct v4l2_subdev *sd) if (!info->on) return; + clk_disable_unprepare(info->clk); + if (info->pwdn_gpio) gpiod_set_value(info->pwdn_gpio, 1); @@ -1864,24 +1869,20 @@ static int ov7670_probe(struct i2c_client *client, return ret; } - if (info->clk) { - ret = clk_prepare_enable(info->clk); - if (ret) - return ret; + ret = ov7670_init_gpio(client, info); + if (ret) + return ret; + ov7670_power_on(sd); + + if (info->clk) { info->clock_speed = clk_get_rate(info->clk) / 1000000; if (info->clock_speed < 10 || info->clock_speed > 48) { ret = -EINVAL; - goto clk_disable; + goto power_off; } } - ret = ov7670_init_gpio(client, info); - if (ret) - goto clk_disable; - - ov7670_power_on(sd); - /* Make sure it's an ov7670 */ ret = ov7670_detect(sd); if (ret) { @@ -1961,6 +1962,7 @@ static int ov7670_probe(struct i2c_client *client, if (ret < 0) goto entity_cleanup; + ov7670_power_off(sd); return 0; entity_cleanup: @@ -1969,12 +1971,9 @@ hdl_free: v4l2_ctrl_handler_free(&info->hdl); power_off: ov7670_power_off(sd); -clk_disable: - clk_disable_unprepare(info->clk); return ret; } - static int ov7670_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -1982,7 +1981,6 @@ static int ov7670_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&info->hdl); - clk_disable_unprepare(info->clk); media_entity_cleanup(&info->sd.entity); ov7670_power_off(sd); return 0; From fa6527340141bfec061db2f2a9cca7f82584f94e Mon Sep 17 00:00:00 2001 From: Ben Kao Date: Tue, 15 Jan 2019 07:36:07 -0200 Subject: [PATCH 0670/4277] media: ov8856: Modify ov8856 register reading function to be simplified We use put_unaligned_be16() to be simplified for setting register address in ov8856_read_reg() and use sizeof() to be better suited for bytes copying. Signed-off-by: Ben Kao Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov8856.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index c0d44082c003..dbf1095b9440 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -605,16 +605,17 @@ static int ov8856_read_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 *val) { struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); struct i2c_msg msgs[2]; - u8 addr_buf[2] = {reg >> 8, reg & 0xff}; - u8 data_buf[4] = {0, }; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; int ret; if (len > 4) return -EINVAL; + put_unaligned_be16(reg, addr_buf); msgs[0].addr = client->addr; msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].len = sizeof(addr_buf); msgs[0].buf = addr_buf; msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; From f8ce7c35e6aef69d9110e2e24756146e3c486cbb Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:38 -0200 Subject: [PATCH 0671/4277] media: i2c: mt9m001: copy mt9m001 soc_camera sensor driver Copy the soc_camera based driver in v4l2 sensor driver directory. This commit just copies the original file without modifying it. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/mt9m001.c | 757 ++++++++++++++++++++++++++++++++++++ 3 files changed, 765 insertions(+) create mode 100644 drivers/media/i2c/mt9m001.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 54a26e24baf4..7629e03ebcc9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -867,6 +867,13 @@ config VIDEO_VS6624 To compile this driver as a module, choose M here: the module will be called vs6624. +config VIDEO_MT9M001 + tristate "mt9m001 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9M001 cameras from Micron, monochrome + and colour models. + config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 29bf3fa81113..2e5e4b0bf7f3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_VIDEO_OV8856) += ov8856.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o +obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c new file mode 100644 index 000000000000..a1a85ff838c5 --- /dev/null +++ b/drivers/media/i2c/mt9m001.c @@ -0,0 +1,757 @@ +/* + * Driver for MT9M001 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * mt9m001 i2c address 0x5d + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc + */ + +/* mt9m001 selected register addresses */ +#define MT9M001_CHIP_VERSION 0x00 +#define MT9M001_ROW_START 0x01 +#define MT9M001_COLUMN_START 0x02 +#define MT9M001_WINDOW_HEIGHT 0x03 +#define MT9M001_WINDOW_WIDTH 0x04 +#define MT9M001_HORIZONTAL_BLANKING 0x05 +#define MT9M001_VERTICAL_BLANKING 0x06 +#define MT9M001_OUTPUT_CONTROL 0x07 +#define MT9M001_SHUTTER_WIDTH 0x09 +#define MT9M001_FRAME_RESTART 0x0b +#define MT9M001_SHUTTER_DELAY 0x0c +#define MT9M001_RESET 0x0d +#define MT9M001_READ_OPTIONS1 0x1e +#define MT9M001_READ_OPTIONS2 0x20 +#define MT9M001_GLOBAL_GAIN 0x35 +#define MT9M001_CHIP_ENABLE 0xF1 + +#define MT9M001_MAX_WIDTH 1280 +#define MT9M001_MAX_HEIGHT 1024 +#define MT9M001_MIN_WIDTH 48 +#define MT9M001_MIN_HEIGHT 32 +#define MT9M001_COLUMN_SKIP 20 +#define MT9M001_ROW_SKIP 12 + +/* MT9M001 has only one fixed colorspace per pixelcode */ +struct mt9m001_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +/* Find a data format by a pixel code in an array */ +static const struct mt9m001_datafmt *mt9m001_find_datafmt( + u32 code, const struct mt9m001_datafmt *fmt, + int n) +{ + int i; + for (i = 0; i < n; i++) + if (fmt[i].code == code) + return fmt + i; + + return NULL; +} + +static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { + /* + * Order important: first natively supported, + * second supported with a GPIO extender + */ + {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, +}; + +static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { + /* Order important - see above */ + {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, +}; + +struct mt9m001 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct v4l2_rect rect; /* Sensor window */ + struct v4l2_clk *clk; + const struct mt9m001_datafmt *fmt; + const struct mt9m001_datafmt *fmts; + int num_fmts; + unsigned int total_h; + unsigned short y_skip_top; /* Lines to skip at the top */ +}; + +static struct mt9m001 *to_mt9m001(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); +} + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret | data); +} + +static int reg_clear(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret & ~data); +} + +static int mt9m001_init(struct i2c_client *client) +{ + int ret; + + dev_dbg(&client->dev, "%s\n", __func__); + + /* + * We don't know, whether platform provides reset, issue a soft reset + * too. This returns all registers to their default values. + */ + ret = reg_write(client, MT9M001_RESET, 1); + if (!ret) + ret = reg_write(client, MT9M001_RESET, 0); + + /* Disable chip, synchronous option update */ + if (!ret) + ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); + + return ret; +} + +static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Switch to master "normal" mode or stop sensor readout */ + if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) + return -EIO; + return 0; +} + +static int mt9m001_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_rect rect = sel->r; + const u16 hblank = 9, vblank = 25; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (mt9m001->fmts == mt9m001_colour_fmts) + /* + * Bayer format - even number of rows for simplicity, + * but let the user play with the top row. + */ + rect.height = ALIGN(rect.height, 2); + + /* Datasheet requirement: see register description */ + rect.width = ALIGN(rect.width, 2); + rect.left = ALIGN(rect.left, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + + mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; + + /* Blanking and start values - default... */ + ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); + if (!ret) + ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); + + /* + * The caller provides a supported format, as verified per + * call to .set_fmt(FORMAT_TRY). + */ + if (!ret) + ret = reg_write(client, MT9M001_COLUMN_START, rect.left); + if (!ret) + ret = reg_write(client, MT9M001_ROW_START, rect.top); + if (!ret) + ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); + if (!ret) + ret = reg_write(client, MT9M001_WINDOW_HEIGHT, + rect.height + mt9m001->y_skip_top - 1); + if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); + + if (!ret) + mt9m001->rect = rect; + + return ret; +} + +static int mt9m001_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = MT9M001_COLUMN_SKIP; + sel->r.top = MT9M001_ROW_SKIP; + sel->r.width = MT9M001_MAX_WIDTH; + sel->r.height = MT9M001_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9m001->rect; + return 0; + default: + return -EINVAL; + } +} + +static int mt9m001_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + mf->width = mt9m001->rect.width; + mf->height = mt9m001->rect.height; + mf->code = mt9m001->fmt->code; + mf->colorspace = mt9m001->fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int mt9m001_s_fmt(struct v4l2_subdev *sd, + const struct mt9m001_datafmt *fmt, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = mt9m001->rect.left, + .r.top = mt9m001->rect.top, + .r.width = mf->width, + .r.height = mf->height, + }; + int ret; + + /* No support for scaling so far, just crop. TODO: use skipping */ + ret = mt9m001_set_selection(sd, NULL, &sel); + if (!ret) { + mf->width = mt9m001->rect.width; + mf->height = mt9m001->rect.height; + mt9m001->fmt = fmt; + mf->colorspace = fmt->colorspace; + } + + return ret; +} + +static int mt9m001_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + const struct mt9m001_datafmt *fmt; + + if (format->pad) + return -EINVAL; + + v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH, 1, + &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top, + MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0); + + if (mt9m001->fmts == mt9m001_colour_fmts) + mf->height = ALIGN(mf->height - 1, 2); + + fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts, + mt9m001->num_fmts); + if (!fmt) { + fmt = mt9m001->fmt; + mf->code = fmt->code; + } + + mf->colorspace = fmt->colorspace; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9m001_s_fmt(sd, fmt, mf); + cfg->try_fmt = *mf; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m001_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + reg->size = 2; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9m001_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int mt9m001_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); +} + +static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(ctrl->handler, + struct mt9m001, hdl); + s32 min, max; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + min = mt9m001->exposure->minimum; + max = mt9m001->exposure->maximum; + mt9m001->exposure->val = + (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min; + break; + } + return 0; +} + +static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(ctrl->handler, + struct mt9m001, hdl); + struct v4l2_subdev *sd = &mt9m001->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl *exp = mt9m001->exposure; + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); + else + data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); + if (data < 0) + return -EIO; + return 0; + + case V4L2_CID_GAIN: + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->val <= ctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = ctrl->default_value - ctrl->minimum; + data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&client->dev, "Setting gain %d\n", data); + data = reg_write(client, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = ctrl->maximum - ctrl->default_value - 1; + unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * + 111 + range / 2) / range + 9; + + if (gain <= 32) + data = gain; + else if (gain <= 64) + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + data = ((gain - 64) * 7 + 28) / 56 + 96; + + dev_dbg(&client->dev, "Setting gain from %d to %d\n", + reg_read(client, MT9M001_GLOBAL_GAIN), data); + data = reg_write(client, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + return 0; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + unsigned long range = exp->maximum - exp->minimum; + unsigned long shutter = ((exp->val - (s32)exp->minimum) * 1048 + + range / 2) / range + 1; + + dev_dbg(&client->dev, + "Setting shutter width from %d to %lu\n", + reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); + if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) + return -EIO; + } else { + const u16 vblank = 25; + + mt9m001->total_h = mt9m001->rect.height + + mt9m001->y_skip_top + vblank; + if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) + return -EIO; + } + return 0; + } + return -EINVAL; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, + struct i2c_client *client) +{ + struct mt9m001 *mt9m001 = to_mt9m001(client); + s32 data; + unsigned long flags; + int ret; + + ret = mt9m001_s_power(&mt9m001->subdev, 1); + if (ret < 0) + return ret; + + /* Enable the chip */ + data = reg_write(client, MT9M001_CHIP_ENABLE, 1); + dev_dbg(&client->dev, "write: %d\n", data); + + /* Read out the chip version register */ + data = reg_read(client, MT9M001_CHIP_VERSION); + + /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ + switch (data) { + case 0x8411: + case 0x8421: + mt9m001->fmts = mt9m001_colour_fmts; + break; + case 0x8431: + mt9m001->fmts = mt9m001_monochrome_fmts; + break; + default: + dev_err(&client->dev, + "No MT9M001 chip detected, register read %x\n", data); + ret = -ENODEV; + goto done; + } + + mt9m001->num_fmts = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + mt9m001->num_fmts++; + else + mt9m001->fmts++; + + if (flags & SOCAM_DATAWIDTH_8) + mt9m001->num_fmts++; + + mt9m001->fmt = &mt9m001->fmts[0]; + + dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, + data == 0x8431 ? "C12STM" : "C12ST"); + + ret = mt9m001_init(client); + if (ret < 0) { + dev_err(&client->dev, "Failed to initialise the camera\n"); + goto done; + } + + /* mt9m001_init() has reset the chip, returning registers to defaults */ + ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); + +done: + mt9m001_s_power(&mt9m001->subdev, 0); + return ret; +} + +static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) +{ + if (ssdd->free_bus) + ssdd->free_bus(ssdd); +} + +static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + *lines = mt9m001->y_skip_top; + + return 0; +} + +static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { + .g_volatile_ctrl = mt9m001_g_volatile_ctrl, + .s_ctrl = mt9m001_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m001_g_register, + .s_register = mt9m001_s_register, +#endif + .s_power = mt9m001_s_power, +}; + +static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + if (code->pad || code->index >= mt9m001->num_fmts) + return -EINVAL; + + code->code = mt9m001->fmts[code->index].code; + return 0; +} + +static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + /* MT9M001 has all capture_format parameters fixed */ + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + const struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; + + if (ssdd->set_bus_param) + return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); + + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + return bps == 10 ? 0 : -EINVAL; +} + +static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { + .s_stream = mt9m001_s_stream, + .g_mbus_config = mt9m001_g_mbus_config, + .s_mbus_config = mt9m001_s_mbus_config, +}; + +static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { + .g_skip_top_lines = mt9m001_g_skip_top_lines, +}; + +static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { + .enum_mbus_code = mt9m001_enum_mbus_code, + .get_selection = mt9m001_get_selection, + .set_selection = mt9m001_set_selection, + .get_fmt = mt9m001_get_fmt, + .set_fmt = mt9m001_set_fmt, +}; + +static const struct v4l2_subdev_ops mt9m001_subdev_ops = { + .core = &mt9m001_subdev_core_ops, + .video = &mt9m001_subdev_video_ops, + .sensor = &mt9m001_subdev_sensor_ops, + .pad = &mt9m001_subdev_pad_ops, +}; + +static int mt9m001_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9m001 *mt9m001; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "MT9M001 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); + if (!mt9m001) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + v4l2_ctrl_handler_init(&mt9m001->hdl, 4); + v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl, + &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9m001->subdev.ctrl_handler = &mt9m001->hdl; + if (mt9m001->hdl.error) + return mt9m001->hdl.error; + + v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + + /* Second stage probe - when a capture adapter is there */ + mt9m001->y_skip_top = 0; + mt9m001->rect.left = MT9M001_COLUMN_SKIP; + mt9m001->rect.top = MT9M001_ROW_SKIP; + mt9m001->rect.width = MT9M001_MAX_WIDTH; + mt9m001->rect.height = MT9M001_MAX_HEIGHT; + + mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(mt9m001->clk)) { + ret = PTR_ERR(mt9m001->clk); + goto eclkget; + } + + ret = mt9m001_video_probe(ssdd, client); + if (ret) { + v4l2_clk_put(mt9m001->clk); +eclkget: + v4l2_ctrl_handler_free(&mt9m001->hdl); + } + + return ret; +} + +static int mt9m001_remove(struct i2c_client *client) +{ + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + v4l2_clk_put(mt9m001->clk); + v4l2_device_unregister_subdev(&mt9m001->subdev); + v4l2_ctrl_handler_free(&mt9m001->hdl); + mt9m001_video_remove(ssdd); + + return 0; +} + +static const struct i2c_device_id mt9m001_id[] = { + { "mt9m001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9m001_id); + +static struct i2c_driver mt9m001_i2c_driver = { + .driver = { + .name = "mt9m001", + }, + .probe = mt9m001_probe, + .remove = mt9m001_remove, + .id_table = mt9m001_id, +}; + +module_i2c_driver(mt9m001_i2c_driver); + +MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL"); From 4915255bfcae85ffed97150df7c445120f67a64f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:39 -0200 Subject: [PATCH 0672/4277] media: i2c: mt9m001: dt: add binding for mt9m001 Add device tree binding documentation for the MT9M001 CMOS image sensor. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/mt9m001.txt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/mt9m001.txt diff --git a/Documentation/devicetree/bindings/media/i2c/mt9m001.txt b/Documentation/devicetree/bindings/media/i2c/mt9m001.txt new file mode 100644 index 000000000000..c920552b03ef --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/mt9m001.txt @@ -0,0 +1,38 @@ +MT9M001: 1/2-Inch Megapixel Digital Image Sensor + +The MT9M001 is an SXGA-format with a 1/2-inch CMOS active-pixel digital +image sensor. It is programmable through I2C interface. + +Required Properties: + +- compatible: shall be "onnn,mt9m001". +- clocks: reference to the master clock into sensor + +Optional Properties: + +- reset-gpios: GPIO handle which is connected to the reset pin of the chip. + Active low. +- standby-gpios: GPIO handle which is connected to the standby pin of the chip. + Active high. + +The device node must contain one 'port' child node with one 'endpoint' child +sub-node for its digital output video port, in accordance with the video +interface bindings defined in: +Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + &i2c1 { + camera-sensor@5d { + compatible = "onnn,mt9m001"; + reg = <0x5d>; + reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + standby-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + clocks = <&camera_clk>; + port { + mt9m001_out: endpoint { + remote-endpoint = <&vcap_in>; + }; + }; + }; + }; From 1f01091fa1c0416b0423b92f927a83227c22d8d3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:40 -0200 Subject: [PATCH 0673/4277] media: mt9m001: convert to SPDX license identifer Replace GPL license statements with SPDX license identifiers (GPL-2.0). This also fixes MODULE_LICENSE() ident to match the actual license text. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index a1a85ff838c5..c0b6b0c9d081 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for MT9M001 CMOS Image Sensor from Micron * * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include @@ -754,4 +751,4 @@ module_i2c_driver(mt9m001_i2c_driver); MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); From 9f4f37b03e3c45ef343044816fa33b99c7200319 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:41 -0200 Subject: [PATCH 0674/4277] media: mt9m001: sort headers alphabetically Sort header block alphabetically for easy maintenance. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index c0b6b0c9d081..dc6cf46ed8d0 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -5,17 +5,17 @@ * Copyright (C) 2008, Guennadi Liakhovetski */ -#include -#include #include #include #include +#include +#include -#include #include +#include #include -#include #include +#include /* * mt9m001 i2c address 0x5d From 1e57e4ce90f4190b989670fe1b161995b5b5f333 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:42 -0200 Subject: [PATCH 0675/4277] media: mt9m001: add of_match_table Add of_match_table for the MT9M001 CMOS image sensor. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index dc6cf46ed8d0..3864d884b8c3 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -738,9 +738,16 @@ static const struct i2c_device_id mt9m001_id[] = { }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); +static const struct of_device_id mt9m001_of_match[] = { + { .compatible = "onnn,mt9m001", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt9m001_of_match); + static struct i2c_driver mt9m001_i2c_driver = { .driver = { .name = "mt9m001", + .of_match_table = mt9m001_of_match, }, .probe = mt9m001_probe, .remove = mt9m001_remove, From a5826484d46ebc2334ad338d570c6865b8d9f70c Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:43 -0200 Subject: [PATCH 0676/4277] media: mt9m001: introduce multi_reg_write() Introduce multi_reg_write() to write multiple registers to the device and use it where possible. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 88 ++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 3864d884b8c3..7ba11a8da037 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -47,6 +47,8 @@ #define MT9M001_MIN_HEIGHT 32 #define MT9M001_COLUMN_SKIP 20 #define MT9M001_ROW_SKIP 12 +#define MT9M001_DEFAULT_HBLANK 9 +#define MT9M001_DEFAULT_VBLANK 25 /* MT9M001 has only one fixed colorspace per pixelcode */ struct mt9m001_datafmt { @@ -137,25 +139,65 @@ static int reg_clear(struct i2c_client *client, const u8 reg, return reg_write(client, reg, ret & ~data); } +struct mt9m001_reg { + u8 reg; + u16 data; +}; + +static int multi_reg_write(struct i2c_client *client, + const struct mt9m001_reg *regs, int num) +{ + int i; + + for (i = 0; i < num; i++) { + int ret = reg_write(client, regs[i].reg, regs[i].data); + + if (ret) + return ret; + } + + return 0; +} + static int mt9m001_init(struct i2c_client *client) { - int ret; + const struct mt9m001_reg init_regs[] = { + /* + * Issue a soft reset. This returns all registers to their + * default values. + */ + { MT9M001_RESET, 1 }, + { MT9M001_RESET, 0 }, + /* Disable chip, synchronous option update */ + { MT9M001_OUTPUT_CONTROL, 0 } + }; dev_dbg(&client->dev, "%s\n", __func__); - /* - * We don't know, whether platform provides reset, issue a soft reset - * too. This returns all registers to their default values. - */ - ret = reg_write(client, MT9M001_RESET, 1); - if (!ret) - ret = reg_write(client, MT9M001_RESET, 0); + return multi_reg_write(client, init_regs, ARRAY_SIZE(init_regs)); +} - /* Disable chip, synchronous option update */ - if (!ret) - ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); +static int mt9m001_apply_selection(struct v4l2_subdev *sd, + struct v4l2_rect *rect) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + const struct mt9m001_reg regs[] = { + /* Blanking and start values - default... */ + { MT9M001_HORIZONTAL_BLANKING, MT9M001_DEFAULT_HBLANK }, + { MT9M001_VERTICAL_BLANKING, MT9M001_DEFAULT_VBLANK }, + /* + * The caller provides a supported format, as verified per + * call to .set_fmt(FORMAT_TRY). + */ + { MT9M001_COLUMN_START, rect->left }, + { MT9M001_ROW_START, rect->top }, + { MT9M001_WINDOW_WIDTH, rect->width - 1 }, + { MT9M001_WINDOW_HEIGHT, + rect->height + mt9m001->y_skip_top - 1 }, + }; - return ret; + return multi_reg_write(client, regs, ARRAY_SIZE(regs)); } static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) @@ -175,7 +217,6 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_rect rect = sel->r; - const u16 hblank = 9, vblank = 25; int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || @@ -199,26 +240,11 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, soc_camera_limit_side(&rect.top, &rect.height, MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); - mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; + mt9m001->total_h = rect.height + mt9m001->y_skip_top + + MT9M001_DEFAULT_VBLANK; - /* Blanking and start values - default... */ - ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); - if (!ret) - ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); - /* - * The caller provides a supported format, as verified per - * call to .set_fmt(FORMAT_TRY). - */ - if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect.top); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect.height + mt9m001->y_skip_top - 1); + ret = mt9m001_apply_selection(sd, &rect); if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); From 8fcfc491c6ca5887bb341b3a622cca3ed8e3c9f0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:44 -0200 Subject: [PATCH 0677/4277] media: mt9m001: switch s_power callback to runtime PM Switch s_power() callback to runtime PM framework. This also removes soc_camera specific power management code and introduces reset and standby gpios instead. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 222 ++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 62 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 7ba11a8da037..1619c8cdbcb6 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -5,15 +5,18 @@ * Copyright (C) 2008, Guennadi Liakhovetski */ +#include +#include +#include #include #include #include +#include #include #include #include #include -#include #include #include @@ -92,8 +95,12 @@ struct mt9m001 { struct v4l2_ctrl *autoexposure; struct v4l2_ctrl *exposure; }; + bool streaming; + struct mutex mutex; struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; + struct clk *clk; + struct gpio_desc *standby_gpio; + struct gpio_desc *reset_gpio; const struct mt9m001_datafmt *fmt; const struct mt9m001_datafmt *fmts; int num_fmts; @@ -177,8 +184,7 @@ static int mt9m001_init(struct i2c_client *client) return multi_reg_write(client, init_regs, ARRAY_SIZE(init_regs)); } -static int mt9m001_apply_selection(struct v4l2_subdev *sd, - struct v4l2_rect *rect) +static int mt9m001_apply_selection(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); @@ -190,11 +196,11 @@ static int mt9m001_apply_selection(struct v4l2_subdev *sd, * The caller provides a supported format, as verified per * call to .set_fmt(FORMAT_TRY). */ - { MT9M001_COLUMN_START, rect->left }, - { MT9M001_ROW_START, rect->top }, - { MT9M001_WINDOW_WIDTH, rect->width - 1 }, + { MT9M001_COLUMN_START, mt9m001->rect.left }, + { MT9M001_ROW_START, mt9m001->rect.top }, + { MT9M001_WINDOW_WIDTH, mt9m001->rect.width - 1 }, { MT9M001_WINDOW_HEIGHT, - rect->height + mt9m001->y_skip_top - 1 }, + mt9m001->rect.height + mt9m001->y_skip_top - 1 }, }; return multi_reg_write(client, regs, ARRAY_SIZE(regs)); @@ -203,11 +209,48 @@ static int mt9m001_apply_selection(struct v4l2_subdev *sd, static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + int ret = 0; + + mutex_lock(&mt9m001->mutex); + + if (mt9m001->streaming == enable) + goto done; + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) + goto put_unlock; + + ret = mt9m001_apply_selection(sd); + if (ret) + goto put_unlock; + + ret = __v4l2_ctrl_handler_setup(&mt9m001->hdl); + if (ret) + goto put_unlock; + + /* Switch to master "normal" mode */ + ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 2); + if (ret < 0) + goto put_unlock; + } else { + /* Switch to master stop sensor readout */ + reg_write(client, MT9M001_OUTPUT_CONTROL, 0); + pm_runtime_put(&client->dev); + } + + mt9m001->streaming = enable; +done: + mutex_unlock(&mt9m001->mutex); - /* Switch to master "normal" mode or stop sensor readout */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) - return -EIO; return 0; + +put_unlock: + pm_runtime_put(&client->dev); + mutex_unlock(&mt9m001->mutex); + + return ret; } static int mt9m001_set_selection(struct v4l2_subdev *sd, @@ -217,7 +260,6 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_rect rect = sel->r; - int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || sel->target != V4L2_SEL_TGT_CROP) @@ -243,15 +285,9 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, mt9m001->total_h = rect.height + mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK; + mt9m001->rect = rect; - ret = mt9m001_apply_selection(sd, &rect); - if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); - - if (!ret) - mt9m001->rect = rect; - - return ret; + return 0; } static int mt9m001_get_selection(struct v4l2_subdev *sd, @@ -395,13 +431,40 @@ static int mt9m001_s_register(struct v4l2_subdev *sd, } #endif -static int mt9m001_s_power(struct v4l2_subdev *sd, int on) +static int mt9m001_power_on(struct device *dev) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct i2c_client *client = to_i2c_client(dev); + struct mt9m001 *mt9m001 = to_mt9m001(client); + int ret; + + ret = clk_prepare_enable(mt9m001->clk); + if (ret) + return ret; + + if (mt9m001->standby_gpio) { + gpiod_set_value_cansleep(mt9m001->standby_gpio, 0); + usleep_range(1000, 2000); + } + + if (mt9m001->reset_gpio) { + gpiod_set_value_cansleep(mt9m001->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(mt9m001->reset_gpio, 0); + usleep_range(1000, 2000); + } + + return 0; +} + +static int mt9m001_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); struct mt9m001 *mt9m001 = to_mt9m001(client); - return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); + gpiod_set_value_cansleep(mt9m001->standby_gpio, 1); + clk_disable_unprepare(mt9m001->clk); + + return 0; } static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -429,16 +492,18 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_ctrl *exp = mt9m001->exposure; int data; + int ret; + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->val) - data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); + ret = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); else - data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); - if (data < 0) - return -EIO; - return 0; + ret = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); + break; case V4L2_CID_GAIN: /* See Datasheet Table 7, Gain settings. */ @@ -448,9 +513,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; + ret = reg_write(client, MT9M001_GLOBAL_GAIN, data); } else { /* Pack it into 1.125..15 variable step, register values 9..67 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ @@ -467,11 +530,9 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&client->dev, "Setting gain from %d to %d\n", reg_read(client, MT9M001_GLOBAL_GAIN), data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; + ret = reg_write(client, MT9M001_GLOBAL_GAIN, data); } - return 0; + break; case V4L2_CID_EXPOSURE_AUTO: if (ctrl->val == V4L2_EXPOSURE_MANUAL) { @@ -482,19 +543,22 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&client->dev, "Setting shutter width from %d to %lu\n", reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); - if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) - return -EIO; + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, shutter); } else { - const u16 vblank = 25; - mt9m001->total_h = mt9m001->rect.height + - mt9m001->y_skip_top + vblank; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) - return -EIO; + mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK; + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, + mt9m001->total_h); } - return 0; + break; + default: + ret = -EINVAL; + break; } - return -EINVAL; + + pm_runtime_put(&client->dev); + + return ret; } /* @@ -509,10 +573,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, unsigned long flags; int ret; - ret = mt9m001_s_power(&mt9m001->subdev, 1); - if (ret < 0) - return ret; - /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); dev_dbg(&client->dev, "write: %d\n", data); @@ -571,7 +631,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); done: - mt9m001_s_power(&mt9m001->subdev, 0); return ret; } @@ -601,7 +660,6 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { .g_register = mt9m001_g_register, .s_register = mt9m001_s_register, #endif - .s_power = mt9m001_s_power, }; static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, @@ -700,6 +758,20 @@ static int mt9m001_probe(struct i2c_client *client, if (!mt9m001) return -ENOMEM; + mt9m001->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9m001->clk)) + return PTR_ERR(mt9m001->clk); + + mt9m001->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", + GPIOD_OUT_LOW); + if (IS_ERR(mt9m001->standby_gpio)) + return PTR_ERR(mt9m001->standby_gpio); + + mt9m001->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(mt9m001->reset_gpio)) + return PTR_ERR(mt9m001->reset_gpio); + v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); v4l2_ctrl_handler_init(&mt9m001->hdl, 4); v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, @@ -722,6 +794,9 @@ static int mt9m001_probe(struct i2c_client *client, v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, V4L2_EXPOSURE_MANUAL, true); + mutex_init(&mt9m001->mutex); + mt9m001->hdl.lock = &mt9m001->mutex; + /* Second stage probe - when a capture adapter is there */ mt9m001->y_skip_top = 0; mt9m001->rect.left = MT9M001_COLUMN_SKIP; @@ -729,18 +804,29 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9m001->clk)) { - ret = PTR_ERR(mt9m001->clk); - goto eclkget; - } + ret = mt9m001_power_on(&client->dev); + if (ret) + goto error_hdl_free; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); ret = mt9m001_video_probe(ssdd, client); - if (ret) { - v4l2_clk_put(mt9m001->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9m001->hdl); - } + if (ret) + goto error_power_off; + + pm_runtime_idle(&client->dev); + + return 0; + +error_power_off: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + mt9m001_power_off(&client->dev); + +error_hdl_free: + v4l2_ctrl_handler_free(&mt9m001->hdl); + mutex_destroy(&mt9m001->mutex); return ret; } @@ -750,10 +836,17 @@ static int mt9m001_remove(struct i2c_client *client) struct mt9m001 *mt9m001 = to_mt9m001(client); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - v4l2_clk_put(mt9m001->clk); v4l2_device_unregister_subdev(&mt9m001->subdev); + pm_runtime_get_sync(&client->dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + mt9m001_power_off(&client->dev); + v4l2_ctrl_handler_free(&mt9m001->hdl); mt9m001_video_remove(ssdd); + mutex_destroy(&mt9m001->mutex); return 0; } @@ -764,6 +857,10 @@ static const struct i2c_device_id mt9m001_id[] = { }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); +static const struct dev_pm_ops mt9m001_pm_ops = { + SET_RUNTIME_PM_OPS(mt9m001_power_off, mt9m001_power_on, NULL) +}; + static const struct of_device_id mt9m001_of_match[] = { { .compatible = "onnn,mt9m001", }, { /* sentinel */ }, @@ -773,6 +870,7 @@ MODULE_DEVICE_TABLE(of, mt9m001_of_match); static struct i2c_driver mt9m001_i2c_driver = { .driver = { .name = "mt9m001", + .pm = &mt9m001_pm_ops, .of_match_table = mt9m001_of_match, }, .probe = mt9m001_probe, From 74021329a76b049d8f411828867d80fc288d2e5a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 Jan 2019 13:07:46 -0200 Subject: [PATCH 0678/4277] media: mt9m001: remove remaining soc_camera specific code Remove remaining soc_camera specific code and drop soc_camera dependency from this driver. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/mt9m001.c | 84 +++++++------------------------------ 2 files changed, 15 insertions(+), 71 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7629e03ebcc9..ac35284f2c7a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -869,7 +869,7 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" - depends on SOC_CAMERA && I2C + depends on I2C && VIDEO_V4L2 help This driver supports MT9M001 cameras from Micron, monochrome and colour models. diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 1619c8cdbcb6..5a3b61238fca 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -15,15 +15,12 @@ #include #include -#include -#include #include +#include #include /* * mt9m001 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc */ /* mt9m001 selected register addresses */ @@ -276,11 +273,15 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, rect.width = ALIGN(rect.width, 2); rect.left = ALIGN(rect.left, 2); - soc_camera_limit_side(&rect.left, &rect.width, - MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + rect.width = clamp_t(u32, rect.width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH); + rect.left = clamp_t(u32, rect.left, MT9M001_COLUMN_SKIP, + MT9M001_COLUMN_SKIP + MT9M001_MAX_WIDTH - rect.width); - soc_camera_limit_side(&rect.top, &rect.height, - MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + rect.height = clamp_t(u32, rect.height, MT9M001_MIN_HEIGHT, + MT9M001_MAX_HEIGHT); + rect.top = clamp_t(u32, rect.top, MT9M001_ROW_SKIP, + MT9M001_ROW_SKIP + MT9M001_MAX_HEIGHT - rect.height); mt9m001->total_h = rect.height + mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK; @@ -565,12 +566,10 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, - struct i2c_client *client) +static int mt9m001_video_probe(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); s32 data; - unsigned long flags; int ret; /* Enable the chip */ @@ -585,9 +584,11 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, case 0x8411: case 0x8421: mt9m001->fmts = mt9m001_colour_fmts; + mt9m001->num_fmts = ARRAY_SIZE(mt9m001_colour_fmts); break; case 0x8431: mt9m001->fmts = mt9m001_monochrome_fmts; + mt9m001->num_fmts = ARRAY_SIZE(mt9m001_monochrome_fmts); break; default: dev_err(&client->dev, @@ -596,26 +597,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, goto done; } - mt9m001->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9m001->num_fmts++; - else - mt9m001->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9m001->num_fmts++; - mt9m001->fmt = &mt9m001->fmts[0]; dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, @@ -634,12 +615,6 @@ done: return ret; } -static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) -{ - if (ssdd->free_bus) - ssdd->free_bus(ssdd); -} - static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -679,41 +654,18 @@ static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } -static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9m001 *mt9m001 = to_mt9m001(client); - unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - - if (ssdd->set_bus_param) - return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return bps == 10 ? 0 : -EINVAL; -} - static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, .g_mbus_config = mt9m001_g_mbus_config, - .s_mbus_config = mt9m001_s_mbus_config, }; static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { @@ -740,21 +692,15 @@ static int mt9m001_probe(struct i2c_client *client, { struct mt9m001 *mt9m001; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!ssdd) { - dev_err(&client->dev, "MT9M001 driver needs platform data\n"); - return -EINVAL; - } - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); return -EIO; } - mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); + mt9m001 = devm_kzalloc(&client->dev, sizeof(*mt9m001), GFP_KERNEL); if (!mt9m001) return -ENOMEM; @@ -811,7 +757,7 @@ static int mt9m001_probe(struct i2c_client *client, pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); - ret = mt9m001_video_probe(ssdd, client); + ret = mt9m001_video_probe(client); if (ret) goto error_power_off; @@ -834,7 +780,6 @@ error_hdl_free: static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9m001->subdev); pm_runtime_get_sync(&client->dev); @@ -845,7 +790,6 @@ static int mt9m001_remove(struct i2c_client *client) mt9m001_power_off(&client->dev); v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(ssdd); mutex_destroy(&mt9m001->mutex); return 0; From 12d85c3e275be6b72fb815eb03d15cab31ee10c1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:46 -0200 Subject: [PATCH 0679/4277] media: mt9m001: add media controller support Create a source pad and set the media controller type to the sensor. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 ++ drivers/media/i2c/mt9m001.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ac35284f2c7a..2a9e87a7ba25 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -870,6 +870,8 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + depends on MEDIA_CONTROLLER help This driver supports MT9M001 cameras from Micron, monochrome and colour models. diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 5a3b61238fca..0a648e2df00d 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -103,6 +103,7 @@ struct mt9m001 { int num_fmts; unsigned int total_h; unsigned short y_skip_top; /* Lines to skip at the top */ + struct media_pad pad; }; static struct mt9m001 *to_mt9m001(const struct i2c_client *client) @@ -761,6 +762,12 @@ static int mt9m001_probe(struct i2c_client *client, if (ret) goto error_power_off; + mt9m001->pad.flags = MEDIA_PAD_FL_SOURCE; + mt9m001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&mt9m001->subdev.entity, 1, &mt9m001->pad); + if (ret) + goto error_power_off; + pm_runtime_idle(&client->dev); return 0; @@ -784,6 +791,8 @@ static int mt9m001_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m001->subdev); pm_runtime_get_sync(&client->dev); + media_entity_cleanup(&mt9m001->subdev.entity); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); From 73b338609845a2c429953f7dd5d027f477b189ea Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:47 -0200 Subject: [PATCH 0680/4277] media: mt9m001: register to V4L2 asynchronous subdevice framework Register a sub-device to the asynchronous subdevice framework, and also create subdevice device node. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 3 +-- drivers/media/i2c/mt9m001.c | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2a9e87a7ba25..19c112cda078 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -869,9 +869,8 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT - depends on MEDIA_CONTROLLER help This driver supports MT9M001 cameras from Micron, monochrome and colour models. diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 0a648e2df00d..94c9919963b4 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -720,6 +720,7 @@ static int mt9m001_probe(struct i2c_client *client, return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); @@ -768,10 +769,16 @@ static int mt9m001_probe(struct i2c_client *client, if (ret) goto error_power_off; + ret = v4l2_async_register_subdev(&mt9m001->subdev); + if (ret) + goto error_entity_cleanup; + pm_runtime_idle(&client->dev); return 0; +error_entity_cleanup: + media_entity_cleanup(&mt9m001->subdev.entity); error_power_off: pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); @@ -788,9 +795,9 @@ static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - v4l2_device_unregister_subdev(&mt9m001->subdev); pm_runtime_get_sync(&client->dev); + v4l2_async_unregister_subdev(&mt9m001->subdev); media_entity_cleanup(&mt9m001->subdev.entity); pm_runtime_disable(&client->dev); From dd15f0702f12cf80ecebe173c6b04ec3dfa10703 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:48 -0200 Subject: [PATCH 0681/4277] media: mt9m001: support log_status ioctl and event interface This adds log_status ioctl and event interface for mt9m001's v4l2 controls. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 94c9919963b4..479e42dffd2e 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -17,6 +17,7 @@ #include #include +#include #include /* @@ -632,6 +633,9 @@ static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { }; static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9m001_g_register, .s_register = mt9m001_s_register, @@ -720,7 +724,8 @@ static int mt9m001_probe(struct i2c_client *client, return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); - mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); From 71f247daf0c7a82a2e9713daa0895f462a6fd8df Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:49 -0200 Subject: [PATCH 0682/4277] media: mt9m001: make SUBDEV_G_FMT ioctl work with SUBDEV_FORMAT_TRY The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE is specified. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 479e42dffd2e..eac10ffce795 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -329,6 +329,12 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *mf; + return 0; + } + mf->width = mt9m001->rect.width; mf->height = mt9m001->rect.height; mf->code = mt9m001->fmt->code; @@ -642,6 +648,26 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { #endif }; +static int mt9m001_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, cfg, 0); + + try_fmt->width = MT9M001_MAX_WIDTH; + try_fmt->height = MT9M001_MAX_HEIGHT; + try_fmt->code = mt9m001->fmts[0].code; + try_fmt->colorspace = mt9m001->fmts[0].colorspace; + try_fmt->field = V4L2_FIELD_NONE; + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -678,6 +704,7 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { }; static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { + .init_cfg = mt9m001_init_cfg, .enum_mbus_code = mt9m001_enum_mbus_code, .get_selection = mt9m001_get_selection, .set_selection = mt9m001_set_selection, From 772f63c00c31d651cf07c1910683093bdd3ad4be Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:50 -0200 Subject: [PATCH 0683/4277] media: mt9m001: set all mbus format field when G_FMT and S_FMT ioctls This driver doesn't set all members of mbus format field when the VIDIOC_SUBDEV_{S,G}_FMT ioctls are called. This is detected by v4l2-compliance. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index eac10ffce795..4b23fde937b3 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -340,6 +340,9 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, mf->code = mt9m001->fmt->code; mf->colorspace = mt9m001->fmt->colorspace; mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; return 0; } @@ -400,6 +403,10 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, } mf->colorspace = fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); From 49410d3abf4492d8be1e6ce6e8ece0e16407f22d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 15 Jan 2019 12:05:39 -0200 Subject: [PATCH 0684/4277] media: mt9m111: make SUBDEV_G_FMT ioctl work with SUBDEV_FORMAT_TRY The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE is specified. Cc: Enrico Scholz Cc: Michael Grzeschik Cc: Marco Felsch Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index d639b9bcf64a..63a5253a3376 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -528,6 +528,16 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mf = v4l2_subdev_get_try_format(sd, cfg, format->pad); + format->format = *mf; + return 0; +#else + return -ENOTTY; +#endif + } + mf->width = mt9m111->width; mf->height = mt9m111->height; mf->code = mt9m111->fmt->code; @@ -1089,6 +1099,25 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) return 0; } +static int mt9m111_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(sd, cfg, 0); + + format->width = MT9M111_MAX_WIDTH; + format->height = MT9M111_MAX_HEIGHT; + format->code = mt9m111_colour_fmts[0].code; + format->colorspace = mt9m111_colour_fmts[0].colorspace; + format->field = V4L2_FIELD_NONE; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_DEFAULT; + format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +#endif + return 0; +} + static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { @@ -1114,6 +1143,7 @@ static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { + .init_cfg = mt9m111_init_cfg, .enum_mbus_code = mt9m111_enum_mbus_code, .get_selection = mt9m111_get_selection, .set_selection = mt9m111_set_selection, From 2e1566abe54b3ad3e5094c7b80883bc70eda6539 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 15 Jan 2019 12:05:40 -0200 Subject: [PATCH 0685/4277] media: mt9m111: set all mbus format field when G_FMT and S_FMT ioctls This driver doesn't set all members of mbus format field when the VIDIOC_SUBDEV_{S,G}_FMT ioctls are called. This is detected by v4l2-compliance. Cc: Enrico Scholz Cc: Michael Grzeschik Cc: Marco Felsch Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 63a5253a3376..9c92eca8020c 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -543,6 +543,9 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, mf->code = mt9m111->fmt->code; mf->colorspace = mt9m111->fmt->colorspace; mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; return 0; } @@ -670,6 +673,10 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, mf->code = fmt->code; mf->colorspace = fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { cfg->try_fmt = *mf; From 29856308137de1c21eda89411695f4fc6e9780ff Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 15 Jan 2019 12:05:41 -0200 Subject: [PATCH 0686/4277] media: mt9m111: set initial frame size other than 0x0 This driver sets initial frame width and height to 0x0, which is invalid. So set it to selection rectangle bounds instead. This is detected by v4l2-compliance detected. Cc: Enrico Scholz Cc: Michael Grzeschik Cc: Marco Felsch Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 9c92eca8020c..5168bb5880c4 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1310,6 +1310,8 @@ static int mt9m111_probe(struct i2c_client *client, mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; mt9m111->rect.width = MT9M111_MAX_WIDTH; mt9m111->rect.height = MT9M111_MAX_HEIGHT; + mt9m111->width = mt9m111->rect.width; + mt9m111->height = mt9m111->rect.height; mt9m111->fmt = &mt9m111_colour_fmts[0]; mt9m111->lastpage = -1; mutex_init(&mt9m111->power_lock); From 61c1baa63430b391f09dce6b6cd2390293c4bcd6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Jan 2019 17:54:54 -0200 Subject: [PATCH 0687/4277] media: s5k4ecgx: delete a bogus error message This function prints an error message on success. I don't have the hardware, I just noticed this while reading the code. Fixes: 8b99312b7214 ("[media] Add v4l2 subdev driver for S5K4ECGX sensor") Signed-off-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k4ecgx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 79aa2740edc4..79c1894c2c83 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -263,8 +263,6 @@ static int s5k4ecgx_read(struct i2c_client *client, u32 addr, u16 *val) ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRL, low); if (!ret) ret = s5k4ecgx_i2c_read(client, REG_CMDBUF0_ADDR, val); - if (!ret) - dev_err(&client->dev, "Failed to execute read command\n"); return ret; } From adbd2969afbb9868f9a480cee4234455577a93c8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 22 Jan 2019 11:42:59 -0200 Subject: [PATCH 0688/4277] media: ov2640: fix initial try format Set initial try format with default configuration instead of current one. Fixes: 8d3b307a150a ("media: ov2640: make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY") Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 37e7c986af86..83031cfc7914 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1011,14 +1011,14 @@ static int ov2640_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov2640_priv *priv = to_ov2640(client); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + const struct ov2640_win_size *win = + ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); - try_fmt->width = priv->win->width; - try_fmt->height = priv->win->height; - try_fmt->code = priv->cfmt_code; + try_fmt->width = win->width; + try_fmt->height = win->height; + try_fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; try_fmt->colorspace = V4L2_COLORSPACE_SRGB; try_fmt->field = V4L2_FIELD_NONE; try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; From 9359a8cbcc77d4afb07444f341e39015d232bd55 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2019 16:30:35 +0200 Subject: [PATCH 0689/4277] RAS: Use consistent types for UUIDs The commit 297b64c74385 ("ras: acpi / apei: generate trace event for unrecognized CPER section") brought inconsistency in UUID types which are used across the RAS subsystem. Fix this by using guid_t everywhere. Signed-off-by: Andy Shevchenko Signed-off-by: Borislav Petkov Reviewed-by: Christoph Hellwig Cc: "Rafael J. Wysocki" Cc: "Steven Rostedt (VMware)" Cc: Bjorn Helgaas Cc: Thomas Tai Cc: Tony Luck Cc: Tyler Baicar Link: https://lkml.kernel.org/r/20190125143035.81589-1-andriy.shevchenko@linux.intel.com --- drivers/ras/ras.c | 2 +- include/ras/ras_event.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c index 3f38907320dc..95540ea8dd9d 100644 --- a/drivers/ras/ras.c +++ b/drivers/ras/ras.c @@ -14,7 +14,7 @@ #define TRACE_INCLUDE_PATH ../../include/ras #include -void log_non_standard_event(const uuid_le *sec_type, const uuid_le *fru_id, +void log_non_standard_event(const guid_t *sec_type, const guid_t *fru_id, const char *fru_text, const u8 sev, const u8 *err, const u32 len) { diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h index a0794632fd01..36c5c5e38c1d 100644 --- a/include/ras/ras_event.h +++ b/include/ras/ras_event.h @@ -27,7 +27,7 @@ TRACE_EVENT(extlog_mem_event, TP_PROTO(struct cper_sec_mem_err *mem, u32 err_seq, - const uuid_le *fru_id, + const guid_t *fru_id, const char *fru_text, u8 sev), @@ -39,7 +39,7 @@ TRACE_EVENT(extlog_mem_event, __field(u8, sev) __field(u64, pa) __field(u8, pa_mask_lsb) - __field_struct(uuid_le, fru_id) + __field_struct(guid_t, fru_id) __string(fru_text, fru_text) __field_struct(struct cper_mem_err_compact, data) ), @@ -218,8 +218,8 @@ TRACE_EVENT(arm_event, */ TRACE_EVENT(non_standard_event, - TP_PROTO(const uuid_le *sec_type, - const uuid_le *fru_id, + TP_PROTO(const guid_t *sec_type, + const guid_t *fru_id, const char *fru_text, const u8 sev, const u8 *err, From 5792ae7c3dd41883cdfa91284b802104b75c59d4 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 6 Dec 2018 11:28:43 -0200 Subject: [PATCH 0690/4277] media: venus: firmware: check fw size against DT memory region size By historical reasons we defined firmware memory size to be 6MB even that the firmware size for all supported Venus versions is 5MBs. Correct that by compare the required firmware size returned from mdt loader and the one provided by DT reserved memory region. We proceed further if the required firmware size is smaller than provided by DT memory region. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/firmware.c | 53 +++++++++++--------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 6382cea29185..79c7e816c706 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -134,6 +134,7 @@ struct venus_core { struct video_firmware { struct device *dev; struct iommu_domain *iommu_domain; + size_t mapped_mem_size; } fw; struct mutex lock; struct list_head instances; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index c29acfd70c1b..6cfa8021721e 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -35,14 +35,15 @@ static void venus_reset_cpu(struct venus_core *core) { + u32 fw_size = core->fw.mapped_mem_size; void __iomem *base = core->base; writel(0, base + WRAPPER_FW_START_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_FW_END_ADDR); + writel(fw_size, base + WRAPPER_FW_END_ADDR); writel(0, base + WRAPPER_CPA_START_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_CPA_END_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_NONPIX_START_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_NONPIX_END_ADDR); + writel(fw_size, base + WRAPPER_CPA_END_ADDR); + writel(fw_size, base + WRAPPER_NONPIX_START_ADDR); + writel(fw_size, base + WRAPPER_NONPIX_END_ADDR); writel(0x0, base + WRAPPER_CPU_CGC_DIS); writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG); @@ -74,6 +75,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, void *mem_va; int ret; + *mem_phys = 0; + *mem_size = 0; + dev = core->dev; node = of_parse_phandle(dev->of_node, "memory-region", 0); if (!node) { @@ -85,28 +89,30 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, if (ret) return ret; + ret = request_firmware(&mdt, fwname, dev); + if (ret < 0) + return ret; + + fw_size = qcom_mdt_get_size(mdt); + if (fw_size < 0) { + ret = fw_size; + goto err_release_fw; + } + *mem_phys = r.start; *mem_size = resource_size(&r); - if (*mem_size < VENUS_FW_MEM_SIZE) - return -EINVAL; + if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) { + ret = -EINVAL; + goto err_release_fw; + } mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); if (!mem_va) { dev_err(dev, "unable to map memory region: %pa+%zx\n", &r.start, *mem_size); - return -ENOMEM; - } - - ret = request_firmware(&mdt, fwname, dev); - if (ret < 0) - goto err_unmap; - - fw_size = qcom_mdt_get_size(mdt); - if (fw_size < 0) { - ret = fw_size; - release_firmware(mdt); - goto err_unmap; + ret = -ENOMEM; + goto err_release_fw; } if (core->use_tz) @@ -116,10 +122,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID, mem_va, *mem_phys, *mem_size, NULL); - release_firmware(mdt); - -err_unmap: memunmap(mem_va); +err_release_fw: + release_firmware(mdt); return ret; } @@ -135,6 +140,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys, return -EPROBE_DEFER; iommu = core->fw.iommu_domain; + core->fw.mapped_mem_size = mem_size; ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size, IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV); @@ -150,6 +156,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys, static int venus_shutdown_no_tz(struct venus_core *core) { + const size_t mapped = core->fw.mapped_mem_size; struct iommu_domain *iommu; size_t unmapped; u32 reg; @@ -166,8 +173,8 @@ static int venus_shutdown_no_tz(struct venus_core *core) iommu = core->fw.iommu_domain; - unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, VENUS_FW_MEM_SIZE); - if (unmapped != VENUS_FW_MEM_SIZE) + unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped); + if (unmapped != mapped) dev_err(dev, "failed to unmap firmware\n"); return 0; From de5a0bafcfc479b3b9513d6171b608f73a72362b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 6 Dec 2018 11:52:45 -0200 Subject: [PATCH 0691/4277] media: venus: core: correct maximum hardware load for sdm845 This correct maximum hardware load constant in per SoC resources for sdm845 aka Venus v4. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index cb411eb85ee4..d95185ea32c3 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -455,7 +455,7 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, - .max_load = 2563200, + .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, From d24f800247b5030794a0e9540c2a95ab49afff7f Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 6 Dec 2018 14:00:15 -0200 Subject: [PATCH 0692/4277] media: venus: core: correct frequency table for sdm845 This corrects clock frequency table rates to be in sync with video clock controller frequency table. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index d95185ea32c3..739366744e0f 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -465,10 +465,12 @@ static const struct venus_resources msm8996_res = { }; static const struct freq_tbl sdm845_freq_table[] = { - { 1944000, 380000000 }, /* 4k UHD @ 60 */ - { 972000, 320000000 }, /* 4k UHD @ 30 */ - { 489600, 200000000 }, /* 1080p @ 60 */ - { 244800, 100000000 }, /* 1080p @ 30 */ + { 3110400, 533000000 }, /* 4096x2160@90 */ + { 2073600, 444000000 }, /* 4096x2160@60 */ + { 1944000, 404000000 }, /* 3840x2160@60 */ + { 972000, 330000000 }, /* 3840x2160@30 */ + { 489600, 200000000 }, /* 1920x1080@60 */ + { 244800, 100000000 }, /* 1920x1080@30 */ }; static const struct venus_resources sdm845_res = { From 947e3b3cf190929604965ab06588ff025fbec2b3 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 7 Dec 2018 11:04:13 -0200 Subject: [PATCH 0693/4277] media: venus: helpers: drop setting of timestamp invalid flag The zero timestamp is really valid so fix that mistake by dropping the code which checks for zero timestamp. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index e436385bc5ab..5cad601d4c57 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -439,9 +439,6 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) fdata.flags = 0; fdata.clnt_data = vbuf->vb2_buf.index; - if (!fdata.timestamp) - fdata.flags |= HFI_BUFFERFLAG_TIMESTAMPINVALID; - if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { fdata.buffer_type = HFI_BUFFER_INPUT; fdata.filled_len = vb2_get_plane_payload(vb, 0); From 60b6527952edf59f9c86c04fd8f91019ce77a0a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 09:14:32 -0200 Subject: [PATCH 0694/4277] media: v4l2-pci-skeleton.c: fix outdated irq code Fix the outdated irq example code. It is under an #ifdef TODO so it is never actually compiled and this code was never compiled. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- samples/v4l/v4l2-pci-skeleton.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index 27ec30952cfa..758ced8c3d06 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -139,16 +139,16 @@ static irqreturn_t skeleton_irq(int irq, void *dev_id) spin_lock(&skel->qlock); list_del(&new_buf->list); spin_unlock(&skel->qlock); - v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); - new_buf->vb.v4l2_buf.sequence = skel->sequence++; - new_buf->vb.v4l2_buf.field = skel->field; + new_buf->vb.vb2_buf.timestamp = ktime_get_ns(); + new_buf->vb.sequence = skel->sequence++; + new_buf->vb.field = skel->field; if (skel->format.field == V4L2_FIELD_ALTERNATE) { if (skel->field == V4L2_FIELD_BOTTOM) skel->field = V4L2_FIELD_TOP; else if (skel->field == V4L2_FIELD_TOP) skel->field = V4L2_FIELD_BOTTOM; } - vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } #endif return IRQ_HANDLED; From b4ba92d56b4f8ea692a5aaa4b0054824aa6fe47c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Jan 2019 09:27:25 -0200 Subject: [PATCH 0695/4277] media: dev-effect.rst: remove unused Effect Interface chapter We never had an effect interface, and if you want to do such things you use the mem2mem interface instead. Just drop this from the spec. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/dev-effect.rst | 28 --------------------- Documentation/media/uapi/v4l/devices.rst | 1 - 2 files changed, 29 deletions(-) delete mode 100644 Documentation/media/uapi/v4l/dev-effect.rst diff --git a/Documentation/media/uapi/v4l/dev-effect.rst b/Documentation/media/uapi/v4l/dev-effect.rst deleted file mode 100644 index b165e2c20910..000000000000 --- a/Documentation/media/uapi/v4l/dev-effect.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. Permission is granted to copy, distribute and/or modify this -.. document under the terms of the GNU Free Documentation License, -.. Version 1.1 or any later version published by the Free Software -.. Foundation, with no Invariant Sections, no Front-Cover Texts -.. and no Back-Cover Texts. A copy of the license is included at -.. Documentation/media/uapi/fdl-appendix.rst. -.. -.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections - -.. _effect: - -************************ -Effect Devices Interface -************************ - -.. note:: - This interface has been be suspended from the V4L2 API. - The implementation for such effects should be done - via mem2mem devices. - -A V4L2 video effect device can do image effects, filtering, or combine -two or more images or image streams. For example video transitions or -wipes. Applications send data to be processed and receive the result -data either with :ref:`read() ` and -:ref:`write() ` functions, or through the streaming I/O -mechanism. - -[to do] diff --git a/Documentation/media/uapi/v4l/devices.rst b/Documentation/media/uapi/v4l/devices.rst index 5dbe9d13b6e6..c959c0443c2f 100644 --- a/Documentation/media/uapi/v4l/devices.rst +++ b/Documentation/media/uapi/v4l/devices.rst @@ -22,7 +22,6 @@ Interfaces dev-output dev-osd dev-codec - dev-effect dev-raw-vbi dev-sliced-vbi dev-teletext From 0b7a4b41ab0e4209c0744442231976448ea3b2d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Jan 2019 09:27:26 -0200 Subject: [PATCH 0696/4277] media: dev-teletext.rst: remove obsolete teletext interface The teletext interface has been dead for years now, just remove it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/dev-teletext.rst | 41 ------------------- Documentation/media/uapi/v4l/devices.rst | 1 - 2 files changed, 42 deletions(-) delete mode 100644 Documentation/media/uapi/v4l/dev-teletext.rst diff --git a/Documentation/media/uapi/v4l/dev-teletext.rst b/Documentation/media/uapi/v4l/dev-teletext.rst deleted file mode 100644 index 35e8c4b35458..000000000000 --- a/Documentation/media/uapi/v4l/dev-teletext.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. Permission is granted to copy, distribute and/or modify this -.. document under the terms of the GNU Free Documentation License, -.. Version 1.1 or any later version published by the Free Software -.. Foundation, with no Invariant Sections, no Front-Cover Texts -.. and no Back-Cover Texts. A copy of the license is included at -.. Documentation/media/uapi/fdl-appendix.rst. -.. -.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections - -.. _ttx: - -****************** -Teletext Interface -****************** - -This interface was aimed at devices receiving and demodulating Teletext -data [:ref:`ets300706`, :ref:`itu653`], evaluating the Teletext -packages and storing formatted pages in cache memory. Such devices are -usually implemented as microcontrollers with serial interface -(I\ :sup:`2`\ C) and could be found on old TV cards, dedicated Teletext -decoding cards and home-brew devices connected to the PC parallel port. - -The Teletext API was designed by Martin Buck. It was defined in the -kernel header file ``linux/videotext.h``, the specification is available -from -`ftp://ftp.gwdg.de/pub/linux/misc/videotext/ `__. -(Videotext is the name of the German public television Teletext -service.) - -Eventually the Teletext API was integrated into the V4L API with -character device file names ``/dev/vtx0`` to ``/dev/vtx31``, device -major number 81, minor numbers 192 to 223. - -However, teletext decoders were quickly replaced by more generic VBI -demodulators and those dedicated teletext decoders no longer exist. For -many years the vtx devices were still around, even though nobody used -them. So the decision was made to finally remove support for the -Teletext API in kernel 2.6.37. - -Modern devices all use the :ref:`raw ` or -:ref:`sliced` VBI API. diff --git a/Documentation/media/uapi/v4l/devices.rst b/Documentation/media/uapi/v4l/devices.rst index c959c0443c2f..d6fcf3db5909 100644 --- a/Documentation/media/uapi/v4l/devices.rst +++ b/Documentation/media/uapi/v4l/devices.rst @@ -24,7 +24,6 @@ Interfaces dev-codec dev-raw-vbi dev-sliced-vbi - dev-teletext dev-radio dev-rds dev-sdr From 49179ff480ee51c096253c8e4092f7cdeb81ca91 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 23 Jan 2019 06:07:20 -0200 Subject: [PATCH 0697/4277] media: Documentation/media: rename "Codec Interface" The "Codec Interface" chapter is poorly named since codecs are just one use-case of the Memory-to-Memory Interface. Rename it and clean up the text a bit. Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Signed-off-by: Mauro Carvalho Chehab --- .../media/uapi/mediactl/request-api.rst | 4 +- .../v4l/{dev-codec.rst => dev-mem2mem.rst} | 41 +++++++++---------- Documentation/media/uapi/v4l/devices.rst | 2 +- .../media/uapi/v4l/pixfmt-compressed.rst | 2 +- Documentation/media/uapi/v4l/vidioc-qbuf.rst | 2 +- 5 files changed, 25 insertions(+), 26 deletions(-) rename Documentation/media/uapi/v4l/{dev-codec.rst => dev-mem2mem.rst} (50%) diff --git a/Documentation/media/uapi/mediactl/request-api.rst b/Documentation/media/uapi/mediactl/request-api.rst index 4b25ad03f45a..1ad631e549fe 100644 --- a/Documentation/media/uapi/mediactl/request-api.rst +++ b/Documentation/media/uapi/mediactl/request-api.rst @@ -91,7 +91,7 @@ A request must contain at least one buffer, otherwise ``ENOENT`` is returned. A queued request cannot be modified anymore. .. caution:: - For :ref:`memory-to-memory devices ` you can use requests only for + For :ref:`memory-to-memory devices ` you can use requests only for output buffers, not for capture buffers. Attempting to add a capture buffer to a request will result in an ``EACCES`` error. @@ -152,7 +152,7 @@ if it had just been allocated. Example for a Codec Device -------------------------- -For use-cases such as :ref:`codecs `, the request API can be used +For use-cases such as :ref:`codecs `, the request API can be used to associate specific controls to be applied by the driver for the OUTPUT buffer, allowing user-space to queue many such buffers in advance. It can also take advantage of requests' diff --git a/Documentation/media/uapi/v4l/dev-codec.rst b/Documentation/media/uapi/v4l/dev-mem2mem.rst similarity index 50% rename from Documentation/media/uapi/v4l/dev-codec.rst rename to Documentation/media/uapi/v4l/dev-mem2mem.rst index b5e017c17834..67a980818dc8 100644 --- a/Documentation/media/uapi/v4l/dev-codec.rst +++ b/Documentation/media/uapi/v4l/dev-mem2mem.rst @@ -7,37 +7,36 @@ .. .. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections -.. _codec: +.. _mem2mem: -*************** -Codec Interface -*************** +******************************** +Video Memory-To-Memory Interface +******************************** -A V4L2 codec can compress, decompress, transform, or otherwise convert -video data from one format into another format, in memory. Typically -such devices are memory-to-memory devices (i.e. devices with the -``V4L2_CAP_VIDEO_M2M`` or ``V4L2_CAP_VIDEO_M2M_MPLANE`` capability set). +A V4L2 memory-to-memory device can compress, decompress, transform, or +otherwise convert video data from one format into another format, in memory. +Such memory-to-memory devices set the ``V4L2_CAP_VIDEO_M2M`` or +``V4L2_CAP_VIDEO_M2M_MPLANE`` capability. Examples of memory-to-memory +devices are codecs, scalers, deinterlacers or format converters (i.e. +converting from YUV to RGB). A memory-to-memory video node acts just like a normal video node, but it -supports both output (sending frames from memory to the codec hardware) -and capture (receiving the processed frames from the codec hardware into +supports both output (sending frames from memory to the hardware) +and capture (receiving the processed frames from the hardware into memory) stream I/O. An application will have to setup the stream I/O for both sides and finally call :ref:`VIDIOC_STREAMON ` -for both capture and output to start the codec. - -Video compression codecs use the MPEG controls to setup their codec -parameters - -.. note:: - - The MPEG controls actually support many more codecs than - just MPEG. See :ref:`mpeg-controls`. +for both capture and output to start the hardware. Memory-to-memory devices function as a shared resource: you can open the video node multiple times, each application setting up their -own codec properties that are local to the file handle, and each can use +own properties that are local to the file handle, and each can use it independently from the others. The driver will arbitrate access to -the codec and reprogram it whenever another file handler gets access. +the hardware and reprogram it whenever another file handler gets access. This is different from the usual video node behavior where the video properties are global to the device (i.e. changing something through one file handle is visible through another file handle). + +One of the most common memory-to-memory device is the codec. Codecs +are more complicated than most and require additional setup for +their codec parameters. This is done through codec controls. +See :ref:`mpeg-controls`. diff --git a/Documentation/media/uapi/v4l/devices.rst b/Documentation/media/uapi/v4l/devices.rst index d6fcf3db5909..07f8d047662b 100644 --- a/Documentation/media/uapi/v4l/devices.rst +++ b/Documentation/media/uapi/v4l/devices.rst @@ -21,7 +21,7 @@ Interfaces dev-overlay dev-output dev-osd - dev-codec + dev-mem2mem dev-raw-vbi dev-sliced-vbi dev-radio diff --git a/Documentation/media/uapi/v4l/pixfmt-compressed.rst b/Documentation/media/uapi/v4l/pixfmt-compressed.rst index e4c5e456df59..2675bef3eefe 100644 --- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst +++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst @@ -73,7 +73,7 @@ Compressed Formats - 'MG2S' - MPEG-2 parsed slice data, as extracted from the MPEG-2 bitstream. This format is adapted for stateless video decoders that implement a - MPEG-2 pipeline (using the :ref:`codec` and :ref:`media-request-api`). + MPEG-2 pipeline (using the :ref:`mem2mem` and :ref:`media-request-api`). Metadata associated with the frame to decode is required to be passed through the ``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS`` control and quantization matrices can optionally be specified through the diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst index 3259168a7358..c138d149faea 100644 --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst @@ -123,7 +123,7 @@ then ``EINVAL`` will be returned. :ref:`VIDIOC_STREAMOFF ` or calling :ref:`VIDIOC_REQBUFS` the check for this will be reset. - For :ref:`memory-to-memory devices ` you can specify the + For :ref:`memory-to-memory devices ` you can specify the ``request_fd`` only for output buffers, not for capture buffers. Attempting to specify this for a capture buffer will result in an ``EACCES`` error. From 8c1d02f140182fcd7cbecc77db68c2b5181c266f Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sun, 20 Jan 2019 11:29:02 -0200 Subject: [PATCH 0698/4277] media: vicodec: Add num_planes field to v4l2_fwht_pixfmt_info Add the field 'num_planes' to 'v4l2_fwht_pixfmt_info' struct. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/vicodec/codec-v4l2-fwht.c | 48 +++++++++---------- .../media/platform/vicodec/codec-v4l2-fwht.h | 1 + drivers/media/platform/vicodec/vicodec-core.c | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index 8cb0212df67f..5e9040f6c902 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -11,30 +11,30 @@ #include "codec-v4l2-fwht.h" static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = { - { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3}, - { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3}, - { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3}, - { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3}, - { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3}, - { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3}, - { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3}, - { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3}, - { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3}, - { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3}, - { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3}, - { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3}, - { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4}, - { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4}, - { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1}, + { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, + { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, + { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3}, + { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2}, + { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2}, + { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2}, + { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2}, + { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2}, + { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2}, + { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, + { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, + { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, + { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, + { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, + { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1}, }; const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index ed53e28d4f9c..685b665590c1 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -19,6 +19,7 @@ struct v4l2_fwht_pixfmt_info { unsigned int width_div; unsigned int height_div; unsigned int components_num; + unsigned int planes_num; }; struct v4l2_fwht_state { diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 2378582d9790..6a7643bceb92 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -61,7 +61,7 @@ struct pixfmt_info { }; static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = { - V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0 + V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1 }; static void vicodec_dev_release(struct device *dev) From 9e812549883730f559cdd95d0c05419f5bfa3713 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:14 -0200 Subject: [PATCH 0699/4277] media: vicodec: add support for CROP and COMPOSE selection Add support for the selection api for the crop and compose targets. The driver rounds up the coded width and height such that all planes dimensions are multiple of 8. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.c | 83 +++-- drivers/media/platform/vicodec/codec-fwht.h | 17 +- .../media/platform/vicodec/codec-v4l2-fwht.c | 293 ++++++++++++------ .../media/platform/vicodec/codec-v4l2-fwht.h | 7 +- drivers/media/platform/vicodec/vicodec-core.c | 166 ++++++++-- 5 files changed, 404 insertions(+), 162 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c index a6fd0477633b..e5e0a80c2f73 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/platform/vicodec/codec-fwht.c @@ -10,6 +10,7 @@ */ #include +#include #include "codec-fwht.h" /* @@ -237,8 +238,6 @@ static void fwht(const u8 *block, s16 *output_block, unsigned int stride, unsigned int i; /* stage 1 */ - stride *= input_step; - for (i = 0; i < 8; i++, tmp += stride, out += 8) { switch (input_step) { case 1: @@ -562,7 +561,7 @@ static void fill_encoder_block(const u8 *input, s16 *dst, for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++, input += input_step) *dst++ = *input; - input += (stride - 8) * input_step; + input += stride - 8 * input_step; } } @@ -660,7 +659,7 @@ static void add_deltas(s16 *deltas, const u8 *ref, int stride) static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, struct fwht_cframe *cf, u32 height, u32 width, - unsigned int input_step, + u32 stride, unsigned int input_step, bool is_intra, bool next_is_intra) { u8 *input_start = input; @@ -671,7 +670,11 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, unsigned int last_size = 0; unsigned int i, j; + width = round_up(width, 8); + height = round_up(height, 8); + for (j = 0; j < height / 8; j++) { + input = input_start + j * 8 * stride; for (i = 0; i < width / 8; i++) { /* intra code, first frame is always intra coded. */ int blocktype = IBLOCK; @@ -679,9 +682,9 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, if (!is_intra) blocktype = decide_blocktype(input, refp, - deltablock, width, input_step); + deltablock, stride, input_step); if (blocktype == IBLOCK) { - fwht(input, cf->coeffs, width, input_step, 1); + fwht(input, cf->coeffs, stride, input_step, 1); quantize_intra(cf->coeffs, cf->de_coeffs, cf->i_frame_qp); } else { @@ -722,12 +725,12 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, } last_size = size; } - input += width * 7 * input_step; } exit_loop: if (encoding & FWHT_FRAME_UNENCODED) { u8 *out = (u8 *)rlco_start; + u8 *p; input = input_start; /* @@ -736,8 +739,11 @@ exit_loop: * by 0xfe. Since YUV is limited range such values * shouldn't appear anyway. */ - for (i = 0; i < height * width; i++, input += input_step) - *out++ = (*input == 0xff) ? 0xfe : *input; + for (j = 0; j < height; j++) { + for (i = 0, p = input; i < width; i++, p += input_step) + *out++ = (*p == 0xff) ? 0xfe : *p; + input += stride; + } *rlco = (__be16 *)out; encoding &= ~FWHT_FRAME_PCODED; } @@ -747,30 +753,32 @@ exit_loop: u32 fwht_encode_frame(struct fwht_raw_frame *frm, struct fwht_raw_frame *ref_frm, struct fwht_cframe *cf, - bool is_intra, bool next_is_intra) + bool is_intra, bool next_is_intra, + unsigned int width, unsigned int height, + unsigned int stride, unsigned int chroma_stride) { - unsigned int size = frm->height * frm->width; + unsigned int size = height * width; __be16 *rlco = cf->rlc_data; __be16 *rlco_max; u32 encoding; rlco_max = rlco + size / 2 - 256; encoding = encode_plane(frm->luma, ref_frm->luma, &rlco, rlco_max, cf, - frm->height, frm->width, + height, width, stride, frm->luma_alpha_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_LUMA_UNENCODED; encoding &= ~FWHT_FRAME_UNENCODED; if (frm->components_num >= 3) { - u32 chroma_h = frm->height / frm->height_div; - u32 chroma_w = frm->width / frm->width_div; + u32 chroma_h = height / frm->height_div; + u32 chroma_w = width / frm->width_div; unsigned int chroma_size = chroma_h * chroma_w; rlco_max = rlco + chroma_size / 2 - 256; encoding |= encode_plane(frm->cb, ref_frm->cb, &rlco, rlco_max, cf, chroma_h, chroma_w, - frm->chroma_step, + chroma_stride, frm->chroma_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_CB_UNENCODED; @@ -778,7 +786,7 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, rlco_max = rlco + chroma_size / 2 - 256; encoding |= encode_plane(frm->cr, ref_frm->cr, &rlco, rlco_max, cf, chroma_h, chroma_w, - frm->chroma_step, + chroma_stride, frm->chroma_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_CR_UNENCODED; @@ -788,8 +796,8 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, if (frm->components_num == 4) { rlco_max = rlco + size / 2 - 256; encoding |= encode_plane(frm->alpha, ref_frm->alpha, &rlco, - rlco_max, cf, frm->height, frm->width, - frm->luma_alpha_step, + rlco_max, cf, height, width, + stride, frm->luma_alpha_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_ALPHA_UNENCODED; @@ -801,13 +809,17 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, } static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, - u32 height, u32 width, bool uncompressed) + u32 height, u32 width, u32 coded_width, + bool uncompressed) { unsigned int copies = 0; s16 copy[8 * 8]; s16 stat; unsigned int i, j; + width = round_up(width, 8); + height = round_up(height, 8); + if (uncompressed) { memcpy(ref, *rlco, width * height); *rlco += width * height / 2; @@ -822,13 +834,15 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, */ for (j = 0; j < height / 8; j++) { for (i = 0; i < width / 8; i++) { - u8 *refp = ref + j * 8 * width + i * 8; + u8 *refp = ref + j * 8 * coded_width + i * 8; if (copies) { memcpy(cf->de_fwht, copy, sizeof(copy)); if (stat & PFRAME_BIT) - add_deltas(cf->de_fwht, refp, width); - fill_decoder_block(refp, cf->de_fwht, width); + add_deltas(cf->de_fwht, refp, + coded_width); + fill_decoder_block(refp, cf->de_fwht, + coded_width); copies--; continue; } @@ -847,35 +861,40 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, if (copies) memcpy(copy, cf->de_fwht, sizeof(copy)); if (stat & PFRAME_BIT) - add_deltas(cf->de_fwht, refp, width); - fill_decoder_block(refp, cf->de_fwht, width); + add_deltas(cf->de_fwht, refp, coded_width); + fill_decoder_block(refp, cf->de_fwht, coded_width); } } } void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, - u32 hdr_flags, unsigned int components_num) + u32 hdr_flags, unsigned int components_num, + unsigned int width, unsigned int height, + unsigned int coded_width) { const __be16 *rlco = cf->rlc_data; - decode_plane(cf, &rlco, ref->luma, cf->height, cf->width, + decode_plane(cf, &rlco, ref->luma, height, width, coded_width, hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED); if (components_num >= 3) { - u32 h = cf->height; - u32 w = cf->width; + u32 h = height; + u32 w = width; + u32 c = coded_width; if (!(hdr_flags & FWHT_FL_CHROMA_FULL_HEIGHT)) h /= 2; - if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH)) + if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH)) { w /= 2; - decode_plane(cf, &rlco, ref->cb, h, w, + c /= 2; + } + decode_plane(cf, &rlco, ref->cb, h, w, c, hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED); - decode_plane(cf, &rlco, ref->cr, h, w, + decode_plane(cf, &rlco, ref->cr, h, w, c, hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED); } if (components_num == 4) - decode_plane(cf, &rlco, ref->alpha, cf->height, cf->width, + decode_plane(cf, &rlco, ref->alpha, height, width, coded_width, hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED); } diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 90ff8962fca7..6d230f5e9d60 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -81,6 +81,13 @@ #define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(17, 16) #define FWHT_FL_COMPONENTS_NUM_OFFSET 16 +/* + * A macro to calculate the needed padding in order to make sure + * both luma and chroma components resolutions are rounded up to + * a multiple of 8 + */ +#define vic_round_dim(dim, div) (round_up((dim) / (div), 8) * (div)) + struct fwht_cframe_hdr { u32 magic1; u32 magic2; @@ -95,7 +102,6 @@ struct fwht_cframe_hdr { }; struct fwht_cframe { - unsigned int width, height; u16 i_frame_qp; u16 p_frame_qp; __be16 *rlc_data; @@ -106,7 +112,6 @@ struct fwht_cframe { }; struct fwht_raw_frame { - unsigned int width, height; unsigned int width_div; unsigned int height_div; unsigned int luma_alpha_step; @@ -125,8 +130,12 @@ struct fwht_raw_frame { u32 fwht_encode_frame(struct fwht_raw_frame *frm, struct fwht_raw_frame *ref_frm, struct fwht_cframe *cf, - bool is_intra, bool next_is_intra); + bool is_intra, bool next_is_intra, + unsigned int width, unsigned int height, + unsigned int stride, unsigned int chroma_stride); void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, - u32 hdr_flags, unsigned int components_num); + u32 hdr_flags, unsigned int components_num, + unsigned int width, unsigned int height, + unsigned int coded_width); #endif diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index 5e9040f6c902..e9c522d209ab 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -56,7 +56,8 @@ const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx) int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) { - unsigned int size = state->width * state->height; + unsigned int size = state->stride * state->coded_height; + unsigned int chroma_stride = state->stride; const struct v4l2_fwht_pixfmt_info *info = state->info; struct fwht_cframe_hdr *p_hdr; struct fwht_cframe cf; @@ -66,8 +67,7 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) if (!info) return -EINVAL; - rf.width = state->width; - rf.height = state->height; + rf.luma = p_in; rf.width_div = info->width_div; rf.height_div = info->height_div; @@ -84,14 +84,17 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) case V4L2_PIX_FMT_YUV420: rf.cb = rf.luma + size; rf.cr = rf.cb + size / 4; + chroma_stride /= 2; break; case V4L2_PIX_FMT_YVU420: rf.cr = rf.luma + size; rf.cb = rf.cr + size / 4; + chroma_stride /= 2; break; case V4L2_PIX_FMT_YUV422P: rf.cb = rf.luma + size; rf.cr = rf.cb + size / 2; + chroma_stride /= 2; break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: @@ -163,15 +166,16 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) return -EINVAL; } - cf.width = state->width; - cf.height = state->height; cf.i_frame_qp = state->i_frame_qp; cf.p_frame_qp = state->p_frame_qp; cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr)); encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf, !state->gop_cnt, - state->gop_cnt == state->gop_size - 1); + state->gop_cnt == state->gop_size - 1, + state->visible_width, + state->visible_height, + state->stride, chroma_stride); if (!(encoding & FWHT_FRAME_PCODED)) state->gop_cnt = 0; if (++state->gop_cnt >= state->gop_size) @@ -181,8 +185,8 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr->magic1 = FWHT_MAGIC1; p_hdr->magic2 = FWHT_MAGIC2; p_hdr->version = htonl(FWHT_VERSION); - p_hdr->width = htonl(cf.width); - p_hdr->height = htonl(cf.height); + p_hdr->width = htonl(state->visible_width); + p_hdr->height = htonl(state->visible_height); flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET; if (encoding & FWHT_LUMA_UNENCODED) flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED; @@ -202,29 +206,26 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr->ycbcr_enc = htonl(state->ycbcr_enc); p_hdr->quantization = htonl(state->quantization); p_hdr->size = htonl(cf.size); - state->ref_frame.width = cf.width; - state->ref_frame.height = cf.height; return cf.size + sizeof(*p_hdr); } int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) { - unsigned int size = state->width * state->height; - unsigned int chroma_size = size; - unsigned int i; + unsigned int i, j, k; u32 flags; struct fwht_cframe_hdr *p_hdr; struct fwht_cframe cf; - u8 *p; + u8 *p, *ref_p; unsigned int components_num = 3; unsigned int version; + const struct v4l2_fwht_pixfmt_info *info; + unsigned int hdr_width_div, hdr_height_div; if (!state->info) return -EINVAL; + info = state->info; p_hdr = (struct fwht_cframe_hdr *)p_in; - cf.width = ntohl(p_hdr->width); - cf.height = ntohl(p_hdr->height); version = ntohl(p_hdr->version); if (!version || version > FWHT_VERSION) { @@ -234,12 +235,12 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) } if (p_hdr->magic1 != FWHT_MAGIC1 || - p_hdr->magic2 != FWHT_MAGIC2 || - (cf.width & 7) || (cf.height & 7)) + p_hdr->magic2 != FWHT_MAGIC2) return -EINVAL; /* TODO: support resolution changes */ - if (cf.width != state->width || cf.height != state->height) + if (ntohl(p_hdr->width) != state->visible_width || + ntohl(p_hdr->height) != state->visible_height) return -EINVAL; flags = ntohl(p_hdr->flags); @@ -255,12 +256,15 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) state->quantization = ntohl(p_hdr->quantization); cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr)); - if (!(flags & FWHT_FL_CHROMA_FULL_WIDTH)) - chroma_size /= 2; - if (!(flags & FWHT_FL_CHROMA_FULL_HEIGHT)) - chroma_size /= 2; + hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + if (hdr_width_div != info->width_div || + hdr_height_div != info->height_div) + return -EINVAL; - fwht_decode_frame(&cf, &state->ref_frame, flags, components_num); + fwht_decode_frame(&cf, &state->ref_frame, flags, components_num, + state->visible_width, state->visible_height, + state->coded_width); /* * TODO - handle the case where the compressed stream encodes a @@ -268,123 +272,226 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) */ switch (state->info->id) { case V4L2_PIX_FMT_GREY: - memcpy(p_out, state->ref_frame.luma, size); + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV422P: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - memcpy(p_out, state->ref_frame.cb, chroma_size); - p_out += chroma_size; - memcpy(p_out, state->ref_frame.cr, chroma_size); + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + ref_p = state->ref_frame.cb; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } + ref_p = state->ref_frame.cr; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } break; case V4L2_PIX_FMT_YVU420: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - memcpy(p_out, state->ref_frame.cr, chroma_size); - p_out += chroma_size; - memcpy(p_out, state->ref_frame.cb, chroma_size); + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + ref_p = state->ref_frame.cr; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } + ref_p = state->ref_frame.cb; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV24: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - for (i = 0, p = p_out; i < chroma_size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.cr[i]; + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + k = 0; + for (i = 0; i < state->coded_height / 2; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.cr[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV42: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - for (i = 0, p = p_out; i < chroma_size; i++) { - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.cb[i]; + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + k = 0; + for (i = 0; i < state->coded_height / 2; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_YUYV: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; - *p++ = state->ref_frame.cr[i / 2]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + *p++ = state->ref_frame.cr[k / 2]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_YVYU: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; - *p++ = state->ref_frame.cb[i / 2]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + *p++ = state->ref_frame.cb[k / 2]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_UYVY: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.cb[i / 2]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cb[k / 2]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_VYUY: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.cr[i / 2]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cr[k / 2]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_HSV24: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_BGR24: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_XRGB32: case V4L2_PIX_FMT_HSV32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = 0; - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = 0; + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_XBGR32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i]; - *p++ = 0; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k]; + *p++ = 0; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_ARGB32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.alpha[i]; - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.alpha[k]; + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_ABGR32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.alpha[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.alpha[k]; + k++; + } + p_out += state->stride; } break; default: diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index 685b665590c1..203c45d98905 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -24,8 +24,11 @@ struct v4l2_fwht_pixfmt_info { struct v4l2_fwht_state { const struct v4l2_fwht_pixfmt_info *info; - unsigned int width; - unsigned int height; + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + unsigned int stride; unsigned int gop_size; unsigned int gop_cnt; u16 i_frame_qp; diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 6a7643bceb92..840e78c2cbe8 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -75,8 +75,10 @@ static struct platform_device vicodec_pdev = { /* Per-queue, driver-specific private data */ struct vicodec_q_data { - unsigned int width; - unsigned int height; + unsigned int coded_width; + unsigned int coded_height; + unsigned int visible_width; + unsigned int visible_height; unsigned int sizeimage; unsigned int sequence; const struct v4l2_fwht_pixfmt_info *info; @@ -454,11 +456,12 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (multiplanar) return -EINVAL; pix = &f->fmt.pix; - pix->width = q_data->width; - pix->height = q_data->height; + pix->width = q_data->coded_width; + pix->height = q_data->coded_height; pix->field = V4L2_FIELD_NONE; pix->pixelformat = info->id; - pix->bytesperline = q_data->width * info->bytesperline_mult; + pix->bytesperline = q_data->coded_width * + info->bytesperline_mult; pix->sizeimage = q_data->sizeimage; pix->colorspace = ctx->state.colorspace; pix->xfer_func = ctx->state.xfer_func; @@ -471,13 +474,13 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (!multiplanar) return -EINVAL; pix_mp = &f->fmt.pix_mp; - pix_mp->width = q_data->width; - pix_mp->height = q_data->height; + pix_mp->width = q_data->coded_width; + pix_mp->height = q_data->coded_height; pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = info->id; pix_mp->num_planes = 1; pix_mp->plane_fmt[0].bytesperline = - q_data->width * info->bytesperline_mult; + q_data->coded_width * info->bytesperline_mult; pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage; pix_mp->colorspace = ctx->state.colorspace; pix_mp->xfer_func = ctx->state.xfer_func; @@ -518,8 +521,13 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix = &f->fmt.pix; if (pix->pixelformat != V4L2_PIX_FMT_FWHT) info = find_fmt(pix->pixelformat); - pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH) & ~7; - pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); + pix->width = vic_round_dim(pix->width, info->width_div); + + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); + pix->height = vic_round_dim(pix->height, info->height_div); + pix->field = V4L2_FIELD_NONE; pix->bytesperline = pix->width * info->bytesperline_mult; @@ -535,9 +543,14 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (pix_mp->pixelformat != V4L2_PIX_FMT_FWHT) info = find_fmt(pix_mp->pixelformat); pix_mp->num_planes = 1; - pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH) & ~7; - pix_mp->height = - clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + + pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH); + pix_mp->width = vic_round_dim(pix_mp->width, info->width_div); + + pix_mp->height = clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT); + pix_mp->height = vic_round_dim(pix_mp->height, + info->height_div); + pix_mp->field = V4L2_FIELD_NONE; plane->bytesperline = pix_mp->width * info->bytesperline_mult; @@ -648,8 +661,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = q_data->info->id != pix->pixelformat || - q_data->width != pix->width || - q_data->height != pix->height; + q_data->coded_width != pix->width || + q_data->coded_height != pix->height; if (vb2_is_busy(vq) && fmt_changed) return -EBUSY; @@ -658,8 +671,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data->info = &pixfmt_fwht; else q_data->info = find_fmt(pix->pixelformat); - q_data->width = pix->width; - q_data->height = pix->height; + q_data->coded_width = pix->width; + q_data->coded_height = pix->height; q_data->sizeimage = pix->sizeimage; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -668,8 +681,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = q_data->info->id != pix_mp->pixelformat || - q_data->width != pix_mp->width || - q_data->height != pix_mp->height; + q_data->coded_width != pix_mp->width || + q_data->coded_height != pix_mp->height; if (vb2_is_busy(vq) && fmt_changed) return -EBUSY; @@ -678,17 +691,24 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data->info = &pixfmt_fwht; else q_data->info = find_fmt(pix_mp->pixelformat); - q_data->width = pix_mp->width; - q_data->height = pix_mp->height; + q_data->coded_width = pix_mp->width; + q_data->coded_height = pix_mp->height; q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage; break; default: return -EINVAL; } + if (q_data->visible_width > q_data->coded_width) + q_data->visible_width = q_data->coded_width; + if (q_data->visible_height > q_data->coded_height) + q_data->visible_height = q_data->coded_height; + dprintk(ctx->dev, - "Setting format for type %d, wxh: %dx%d, fourcc: %08x\n", - f->type, q_data->width, q_data->height, q_data->info->id); + "Setting format for type %d, coded wxh: %dx%d, visible wxh: %dx%d, fourcc: %08x\n", + f->type, q_data->coded_width, q_data->coded_height, + q_data->visible_width, q_data->visible_height, + q_data->info->id); return 0; } @@ -743,6 +763,79 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return ret; } +static int vidioc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct vicodec_q_data *q_data; + enum v4l2_buf_type valid_cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + enum v4l2_buf_type valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (multiplanar) { + valid_cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + } + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + /* + * encoder supports only cropping on the OUTPUT buffer + * decoder supports only composing on the CAPTURE buffer + */ + if ((ctx->is_enc && s->type == valid_out_type) || + (!ctx->is_enc && s->type == valid_cap_type)) { + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_CROP: + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + return 0; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->coded_width; + s->r.height = q_data->coded_height; + return 0; + } + } + return -EINVAL; +} + +static int vidioc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct vicodec_q_data *q_data; + enum v4l2_buf_type out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (multiplanar) + out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + if (!ctx->is_enc || s->type != out_type || + s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + s->r.left = 0; + s->r.top = 0; + q_data->visible_width = clamp(s->r.width, MIN_WIDTH, + q_data->coded_width); + s->r.width = q_data->visible_width; + q_data->visible_height = clamp(s->r.height, MIN_HEIGHT, + q_data->coded_height); + s->r.height = q_data->visible_height; + return 0; +} + static void vicodec_mark_last_buf(struct vicodec_ctx *ctx) { static const struct v4l2_event eos_event = { @@ -885,6 +978,9 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, + .vidioc_try_encoder_cmd = vicodec_try_encoder_cmd, .vidioc_encoder_cmd = vicodec_encoder_cmd, .vidioc_try_decoder_cmd = vicodec_try_decoder_cmd, @@ -978,8 +1074,8 @@ static int vicodec_start_streaming(struct vb2_queue *q, struct vicodec_ctx *ctx = vb2_get_drv_priv(q); struct vicodec_q_data *q_data = get_q_data(ctx, q->type); struct v4l2_fwht_state *state = &ctx->state; - unsigned int size = q_data->width * q_data->height; const struct v4l2_fwht_pixfmt_info *info = q_data->info; + unsigned int size = q_data->coded_width * q_data->coded_height; unsigned int chroma_div = info->width_div * info->height_div; unsigned int total_planes_size; @@ -998,17 +1094,23 @@ static int vicodec_start_streaming(struct vb2_queue *q, if (!V4L2_TYPE_IS_OUTPUT(q->type)) { if (!ctx->is_enc) { - state->width = q_data->width; - state->height = q_data->height; + state->visible_width = q_data->visible_width; + state->visible_height = q_data->visible_height; + state->coded_width = q_data->coded_width; + state->coded_height = q_data->coded_height; + state->stride = q_data->coded_width * + info->bytesperline_mult; } return 0; } if (ctx->is_enc) { - state->width = q_data->width; - state->height = q_data->height; + state->visible_width = q_data->visible_width; + state->visible_height = q_data->visible_height; + state->coded_width = q_data->coded_width; + state->coded_height = q_data->coded_height; + state->stride = q_data->coded_width * info->bytesperline_mult; } - state->ref_frame.width = state->ref_frame.height = 0; state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); ctx->comp_max_size = total_planes_size + sizeof(struct fwht_cframe_hdr); state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); @@ -1194,8 +1296,10 @@ static int vicodec_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].info = ctx->is_enc ? v4l2_fwht_get_pixfmt(0) : &pixfmt_fwht; - ctx->q_data[V4L2_M2M_SRC].width = 1280; - ctx->q_data[V4L2_M2M_SRC].height = 720; + ctx->q_data[V4L2_M2M_SRC].coded_width = 1280; + ctx->q_data[V4L2_M2M_SRC].coded_height = 720; + ctx->q_data[V4L2_M2M_SRC].visible_width = 1280; + ctx->q_data[V4L2_M2M_SRC].visible_height = 720; size = 1280 * 720 * ctx->q_data[V4L2_M2M_SRC].info->sizeimage_mult / ctx->q_data[V4L2_M2M_SRC].info->sizeimage_div; if (ctx->is_enc) From 7248d1ff0e2199128618a2eb427e798a46deaaf1 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:15 -0200 Subject: [PATCH 0700/4277] media: vicodec: use 3 bits for the number of components Use 3 bits for the number of components mask in the fwht header flags Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 6d230f5e9d60..2984dc772515 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -78,7 +78,7 @@ #define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9) /* A 4-values flag - the number of components - 1 */ -#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(17, 16) +#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) #define FWHT_FL_COMPONENTS_NUM_OFFSET 16 /* From 5fbd0729cfc6601a703ec79a301928bd00bfcb6f Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:16 -0200 Subject: [PATCH 0701/4277] media: vicodec: Add pixel encoding flags to fwht header Add flags indicating the pixel encoding - yuv/rgb/hsv to fwht header and to the pixel info. Use it to enumerate the supported pixel formats. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.h | 6 ++ .../media/platform/vicodec/codec-v4l2-fwht.c | 77 +++++++++++++------ .../media/platform/vicodec/codec-v4l2-fwht.h | 6 ++ drivers/media/platform/vicodec/vicodec-core.c | 19 +++-- 4 files changed, 78 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 2984dc772515..ad8cfc60a152 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -81,6 +81,12 @@ #define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) #define FWHT_FL_COMPONENTS_NUM_OFFSET 16 +#define FWHT_FL_PIXENC_MSK GENMASK(20, 19) +#define FWHT_FL_PIXENC_OFFSET 19 +#define FWHT_FL_PIXENC_YUV (1 << FWHT_FL_PIXENC_OFFSET) +#define FWHT_FL_PIXENC_RGB (2 << FWHT_FL_PIXENC_OFFSET) +#define FWHT_FL_PIXENC_HSV (3 << FWHT_FL_PIXENC_OFFSET) + /* * A macro to calculate the needed padding in order to make sure * both luma and chroma components resolutions are rounded up to diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index e9c522d209ab..71ae19062b15 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -11,32 +11,53 @@ #include "codec-v4l2-fwht.h" static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = { - { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, - { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, - { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3}, - { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2}, - { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2}, - { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2}, - { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2}, - { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2}, - { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2}, - { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, - { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, - { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, - { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, - { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, - { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1}, + { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV}, + { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV}, + { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB}, }; +const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div, + u32 height_div, + u32 components_num, + u32 pixenc, + unsigned int start_idx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) { + if (v4l2_fwht_pixfmts[i].width_div == width_div && + v4l2_fwht_pixfmts[i].height_div == height_div && + (!pixenc || v4l2_fwht_pixfmts[i].pixenc == pixenc) && + v4l2_fwht_pixfmts[i].components_num == components_num) { + if (start_idx == 0) + return v4l2_fwht_pixfmts + i; + start_idx--; + } + } + return NULL; +} + const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat) { unsigned int i; @@ -188,6 +209,7 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr->width = htonl(state->visible_width); p_hdr->height = htonl(state->visible_height); flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET; + flags |= info->pixenc; if (encoding & FWHT_LUMA_UNENCODED) flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED; if (encoding & FWHT_CB_UNENCODED) @@ -246,10 +268,15 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags = ntohl(p_hdr->flags); if (version == FWHT_VERSION) { + if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) + return -EINVAL; components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> - FWHT_FL_COMPONENTS_NUM_OFFSET); + FWHT_FL_COMPONENTS_NUM_OFFSET); } + if (components_num != info->components_num) + return -EINVAL; + state->colorspace = ntohl(p_hdr->colorspace); state->xfer_func = ntohl(p_hdr->xfer_func); state->ycbcr_enc = ntohl(p_hdr->ycbcr_enc); diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index 203c45d98905..18ac25978829 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -20,6 +20,7 @@ struct v4l2_fwht_pixfmt_info { unsigned int height_div; unsigned int components_num; unsigned int planes_num; + unsigned int pixenc; }; struct v4l2_fwht_state { @@ -45,6 +46,11 @@ struct v4l2_fwht_state { const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat); const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx); +const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div, + u32 height_div, + u32 components_num, + u32 pixenc, + unsigned int start_idx); int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out); int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out); diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 840e78c2cbe8..413b6bca91e5 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -395,9 +395,10 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out) +static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, + bool is_out) { - bool is_uncomp = (is_enc && is_out) || (!is_enc && !is_out); + bool is_uncomp = (ctx->is_enc && is_out) || (!ctx->is_enc && !is_out); if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar) return -EINVAL; @@ -406,8 +407,16 @@ static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out) if (is_uncomp) { const struct v4l2_fwht_pixfmt_info *info = - v4l2_fwht_get_pixfmt(f->index); + get_q_data(ctx, f->type)->info; + if (ctx->is_enc) + info = v4l2_fwht_get_pixfmt(f->index); + else + info = v4l2_fwht_default_fmt(info->width_div, + info->height_div, + info->components_num, + info->pixenc, + f->index); if (!info) return -EINVAL; f->pixelformat = info->id; @@ -424,7 +433,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, { struct vicodec_ctx *ctx = file2ctx(file); - return enum_fmt(f, ctx->is_enc, false); + return enum_fmt(f, ctx, false); } static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, @@ -432,7 +441,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, { struct vicodec_ctx *ctx = file2ctx(file); - return enum_fmt(f, ctx->is_enc, true); + return enum_fmt(f, ctx, true); } static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) From ddc1b085275144a80260b5062fa341a503b4495c Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:17 -0200 Subject: [PATCH 0702/4277] media: vicodec: Separate fwht header from the frame data Keep the fwht header in separated field from the data. Refactor job_ready to use a new function 'get_next_header' Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/vicodec/codec-v4l2-fwht.c | 24 ++-- .../media/platform/vicodec/codec-v4l2-fwht.h | 1 + drivers/media/platform/vicodec/vicodec-core.c | 110 +++++++++++------- 3 files changed, 77 insertions(+), 58 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index 71ae19062b15..ee6903b8896c 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -235,7 +235,6 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) { unsigned int i, j, k; u32 flags; - struct fwht_cframe_hdr *p_hdr; struct fwht_cframe cf; u8 *p, *ref_p; unsigned int components_num = 3; @@ -247,25 +246,24 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) return -EINVAL; info = state->info; - p_hdr = (struct fwht_cframe_hdr *)p_in; - version = ntohl(p_hdr->version); + version = ntohl(state->header.version); if (!version || version > FWHT_VERSION) { pr_err("version %d is not supported, current version is %d\n", version, FWHT_VERSION); return -EINVAL; } - if (p_hdr->magic1 != FWHT_MAGIC1 || - p_hdr->magic2 != FWHT_MAGIC2) + if (state->header.magic1 != FWHT_MAGIC1 || + state->header.magic2 != FWHT_MAGIC2) return -EINVAL; /* TODO: support resolution changes */ - if (ntohl(p_hdr->width) != state->visible_width || - ntohl(p_hdr->height) != state->visible_height) + if (ntohl(state->header.width) != state->visible_width || + ntohl(state->header.height) != state->visible_height) return -EINVAL; - flags = ntohl(p_hdr->flags); + flags = ntohl(state->header.flags); if (version == FWHT_VERSION) { if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) @@ -277,11 +275,11 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) if (components_num != info->components_num) return -EINVAL; - state->colorspace = ntohl(p_hdr->colorspace); - state->xfer_func = ntohl(p_hdr->xfer_func); - state->ycbcr_enc = ntohl(p_hdr->ycbcr_enc); - state->quantization = ntohl(p_hdr->quantization); - cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr)); + state->colorspace = ntohl(state->header.colorspace); + state->xfer_func = ntohl(state->header.xfer_func); + state->ycbcr_enc = ntohl(state->header.ycbcr_enc); + state->quantization = ntohl(state->header.quantization); + cf.rlc_data = (__be16 *)p_in; hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index 18ac25978829..aa6fa90a48be 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -41,6 +41,7 @@ struct v4l2_fwht_state { enum v4l2_quantization quantization; struct fwht_raw_frame ref_frame; + struct fwht_cframe_hdr header; u8 *compressed_frame; }; diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 413b6bca91e5..7c68959aae34 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -124,6 +124,7 @@ struct vicodec_ctx { u32 cur_buf_offset; u32 comp_max_size; u32 comp_size; + u32 header_size; u32 comp_magic_cnt; u32 comp_frame_size; bool comp_has_frame; @@ -201,6 +202,62 @@ static int device_process(struct vicodec_ctx *ctx, /* * mem2mem callbacks */ +enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, u8 **pp, u32 sz) +{ + static const u8 magic[] = { + 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff + }; + u8 *p = *pp; + u32 state; + u8 *header = (u8 *)&ctx->state.header; + + state = VB2_BUF_STATE_DONE; + + if (!ctx->header_size) { + state = VB2_BUF_STATE_ERROR; + for (; p < *pp + sz; p++) { + u32 copy; + + p = memchr(p, magic[ctx->comp_magic_cnt], + *pp + sz - p); + if (!p) { + ctx->comp_magic_cnt = 0; + p = *pp + sz; + break; + } + copy = sizeof(magic) - ctx->comp_magic_cnt; + if (*pp + sz - p < copy) + copy = *pp + sz - p; + + memcpy(header + ctx->comp_magic_cnt, p, copy); + ctx->comp_magic_cnt += copy; + if (!memcmp(header, magic, ctx->comp_magic_cnt)) { + p += copy; + state = VB2_BUF_STATE_DONE; + break; + } + ctx->comp_magic_cnt = 0; + } + if (ctx->comp_magic_cnt < sizeof(magic)) { + *pp = p; + return state; + } + ctx->header_size = sizeof(magic); + } + + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->header_size; + + if (*pp + sz - p < copy) + copy = *pp + sz - p; + + memcpy(header + ctx->header_size, p, copy); + p += copy; + ctx->header_size += copy; + } + *pp = p; + return state; +} /* device_run() - prepares and starts the device */ static void device_run(void *priv) @@ -241,6 +298,7 @@ static void device_run(void *priv) } v4l2_m2m_buf_done(dst_buf, state); ctx->comp_size = 0; + ctx->header_size = 0; ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; spin_unlock(ctx->lock); @@ -291,54 +349,15 @@ restart: state = VB2_BUF_STATE_DONE; - if (!ctx->comp_size) { - state = VB2_BUF_STATE_ERROR; - for (; p < p_src + sz; p++) { - u32 copy; - - p = memchr(p, magic[ctx->comp_magic_cnt], - p_src + sz - p); - if (!p) { - ctx->comp_magic_cnt = 0; - break; - } - copy = sizeof(magic) - ctx->comp_magic_cnt; - if (p_src + sz - p < copy) - copy = p_src + sz - p; - - memcpy(ctx->state.compressed_frame + ctx->comp_magic_cnt, - p, copy); - ctx->comp_magic_cnt += copy; - if (!memcmp(ctx->state.compressed_frame, magic, - ctx->comp_magic_cnt)) { - p += copy; - state = VB2_BUF_STATE_DONE; - break; - } - ctx->comp_magic_cnt = 0; - } - if (ctx->comp_magic_cnt < sizeof(magic)) { + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + state = get_next_header(ctx, &p, p_src + sz - p); + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { job_remove_src_buf(ctx, state); goto restart; } - ctx->comp_size = sizeof(magic); - } - if (ctx->comp_size < sizeof(struct fwht_cframe_hdr)) { - struct fwht_cframe_hdr *p_hdr = - (struct fwht_cframe_hdr *)ctx->state.compressed_frame; - u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->comp_size; - if (copy > p_src + sz - p) - copy = p_src + sz - p; - memcpy(ctx->state.compressed_frame + ctx->comp_size, - p, copy); - p += copy; - ctx->comp_size += copy; - if (ctx->comp_size < sizeof(struct fwht_cframe_hdr)) { - job_remove_src_buf(ctx, state); - goto restart; - } - ctx->comp_frame_size = ntohl(p_hdr->size) + sizeof(*p_hdr); + ctx->comp_frame_size = ntohl(ctx->state.header.size); + if (ctx->comp_frame_size > ctx->comp_max_size) ctx->comp_frame_size = ctx->comp_max_size; } @@ -1121,7 +1140,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->stride = q_data->coded_width * info->bytesperline_mult; } state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); - ctx->comp_max_size = total_planes_size + sizeof(struct fwht_cframe_hdr); + ctx->comp_max_size = total_planes_size; state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); if (!state->ref_frame.luma || !state->compressed_frame) { kvfree(state->ref_frame.luma); @@ -1148,6 +1167,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->gop_cnt = 0; ctx->cur_buf_offset = 0; ctx->comp_size = 0; + ctx->header_size = 0; ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; From ed4cdf4a213197d8f01bf25427b3fa21eb24fd66 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 25 Jan 2019 13:39:30 +0900 Subject: [PATCH 0703/4277] infiniband: remove unneeded header search paths The included headers are located in include/target/. I was able to build these drivers without the extra header search paths. Signed-off-by: Masahiro Yamada Reviewed-by: Bart Van Assche Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/isert/Makefile | 1 - drivers/infiniband/ulp/srpt/Makefile | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/infiniband/ulp/isert/Makefile b/drivers/infiniband/ulp/isert/Makefile index c8bf2421f5bc..a4a4766e3e18 100644 --- a/drivers/infiniband/ulp/isert/Makefile +++ b/drivers/infiniband/ulp/isert/Makefile @@ -1,2 +1 @@ -ccflags-y := -Idrivers/target -Idrivers/target/iscsi obj-$(CONFIG_INFINIBAND_ISERT) += ib_isert.o diff --git a/drivers/infiniband/ulp/srpt/Makefile b/drivers/infiniband/ulp/srpt/Makefile index e3ee4bdfffa5..43fbde42c58b 100644 --- a/drivers/infiniband/ulp/srpt/Makefile +++ b/drivers/infiniband/ulp/srpt/Makefile @@ -1,2 +1 @@ -ccflags-y := -Idrivers/target obj-$(CONFIG_INFINIBAND_SRPT) += ib_srpt.o From b360ce3b2be9fb93d7ba7ecdcb4eb16d7e469998 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 25 Jan 2019 13:39:31 +0900 Subject: [PATCH 0704/4277] infiniband: prefix header search paths with $(srctree)/ Currently, the Kbuild core manipulates header search paths in a crazy way [1]. To fix this mess, I want all Makefiles to add explicit $(srctree)/ to the search paths in the srctree. Some Makefiles are already written in that way, but not all. The goal of this work is to make the notation consistent, and finally get rid of the gross hacks. Having whitespaces after -I does not matter since commit 48f6e3cf5bc6 ("kbuild: do not drop -I without parameter"). [1]: https://patchwork.kernel.org/patch/9632347/ Signed-off-by: Masahiro Yamada Acked-by: Parvi Kaustubhi Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/Makefile | 2 +- drivers/infiniband/hw/cxgb3/Makefile | 2 +- drivers/infiniband/hw/cxgb4/Makefile | 4 ++-- drivers/infiniband/hw/hns/Makefile | 2 +- drivers/infiniband/hw/i40iw/Makefile | 2 +- drivers/infiniband/hw/ocrdma/Makefile | 2 +- drivers/infiniband/hw/usnic/Makefile | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/Makefile b/drivers/infiniband/hw/bnxt_re/Makefile index 6e3bc25cc140..ee9bb1be61ea 100644 --- a/drivers/infiniband/hw/bnxt_re/Makefile +++ b/drivers/infiniband/hw/bnxt_re/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y := -Idrivers/net/ethernet/broadcom/bnxt +ccflags-y := -I $(srctree)/drivers/net/ethernet/broadcom/bnxt obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o bnxt_re-y := main.o ib_verbs.o \ qplib_res.o qplib_rcfw.o \ diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile index 66fe0917aba0..34bb86a6ae3a 100644 --- a/drivers/infiniband/hw/cxgb3/Makefile +++ b/drivers/infiniband/hw/cxgb3/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb3 +ccflags-y := -I $(srctree)/drivers/net/ethernet/chelsio/cxgb3 obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o diff --git a/drivers/infiniband/hw/cxgb4/Makefile b/drivers/infiniband/hw/cxgb4/Makefile index 9edd92023e18..31a87d90a40b 100644 --- a/drivers/infiniband/hw/cxgb4/Makefile +++ b/drivers/infiniband/hw/cxgb4/Makefile @@ -1,5 +1,5 @@ -ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4 -ccflags-y += -Idrivers/net/ethernet/chelsio/libcxgb +ccflags-y := -I $(srctree)/drivers/net/ethernet/chelsio/cxgb4 +ccflags-y += -I $(srctree)/drivers/net/ethernet/chelsio/libcxgb obj-$(CONFIG_INFINIBAND_CXGB4) += iw_cxgb4.o diff --git a/drivers/infiniband/hw/hns/Makefile b/drivers/infiniband/hw/hns/Makefile index 004c88b32e13..e2a7f1488f76 100644 --- a/drivers/infiniband/hw/hns/Makefile +++ b/drivers/infiniband/hw/hns/Makefile @@ -2,7 +2,7 @@ # Makefile for the Hisilicon RoCE drivers. # -ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3 +ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3 obj-$(CONFIG_INFINIBAND_HNS) += hns-roce.o hns-roce-objs := hns_roce_main.o hns_roce_cmd.o hns_roce_pd.o \ diff --git a/drivers/infiniband/hw/i40iw/Makefile b/drivers/infiniband/hw/i40iw/Makefile index 5a8a7a3f28ae..8942f8229945 100644 --- a/drivers/infiniband/hw/i40iw/Makefile +++ b/drivers/infiniband/hw/i40iw/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y := -Idrivers/net/ethernet/intel/i40e +ccflags-y := -I $(srctree)/drivers/net/ethernet/intel/i40e obj-$(CONFIG_INFINIBAND_I40IW) += i40iw.o diff --git a/drivers/infiniband/hw/ocrdma/Makefile b/drivers/infiniband/hw/ocrdma/Makefile index d1bfd4f4cdde..e3f20ca15462 100644 --- a/drivers/infiniband/hw/ocrdma/Makefile +++ b/drivers/infiniband/hw/ocrdma/Makefile @@ -1,4 +1,4 @@ -ccflags-y := -Idrivers/net/ethernet/emulex/benet +ccflags-y := -I $(srctree)/drivers/net/ethernet/emulex/benet obj-$(CONFIG_INFINIBAND_OCRDMA) += ocrdma.o diff --git a/drivers/infiniband/hw/usnic/Makefile b/drivers/infiniband/hw/usnic/Makefile index 94ae7a1a6950..f12a4938ffd2 100644 --- a/drivers/infiniband/hw/usnic/Makefile +++ b/drivers/infiniband/hw/usnic/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y := -Idrivers/net/ethernet/cisco/enic +ccflags-y := -I $(srctree)/drivers/net/ethernet/cisco/enic obj-$(CONFIG_INFINIBAND_USNIC)+= usnic_verbs.o From 6e1849ec75792c46a18a2b14b8bd915435ff2a2a Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 25 Jan 2019 16:45:57 +0100 Subject: [PATCH 0705/4277] RAS: Add a MAINTAINERS entry This was long overdue and it is needed at least so that Tony and I get CCed on patches. Signed-off-by: Borislav Petkov Acked-by: Tony Luck Acked-by: Ingo Molnar Link: https://lkml.kernel.org/r/20190125174227.10652-1-bp@alien8.de --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4d04cebb4a71..a63f2b52a8dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12767,6 +12767,16 @@ M: Alexandre Bounine S: Maintained F: drivers/rapidio/ +RAS INFRASTRUCTURE +M: Tony Luck +M: Borislav Petkov +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/ras/ +F: include/linux/ras.h +F: include/ras/ras_event.h +F: Documentation/admin-guide/ras.rst + RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER L: linux-wireless@vger.kernel.org S: Orphan From 3b15f68e19c28a76d175f61943a8c23224afce93 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:18 -0200 Subject: [PATCH 0706/4277] media: vicodec: Add support for resolution change event. If the the queues are not streaming then the first resolution change is handled in the buf_queue callback. The following resolution change events are handled in job_ready. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: wrap info_from_header prototype] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 362 ++++++++++++++---- 1 file changed, 294 insertions(+), 68 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 7c68959aae34..454476a9f659 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -126,9 +126,10 @@ struct vicodec_ctx { u32 comp_size; u32 header_size; u32 comp_magic_cnt; - u32 comp_frame_size; bool comp_has_frame; bool comp_has_next_frame; + bool first_source_change_sent; + bool source_changed; }; static inline struct vicodec_ctx *file2ctx(struct file *file) @@ -323,6 +324,96 @@ static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state) spin_unlock(ctx->lock); } +static const struct v4l2_fwht_pixfmt_info * +info_from_header(const struct fwht_cframe_hdr *p_hdr) +{ + unsigned int flags = ntohl(p_hdr->flags); + unsigned int width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + unsigned int components_num = 3; + unsigned int pixenc = 0; + unsigned int version = ntohl(p_hdr->version); + + if (version == FWHT_VERSION) { + components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> + FWHT_FL_COMPONENTS_NUM_OFFSET); + pixenc = (flags & FWHT_FL_PIXENC_MSK); + } + return v4l2_fwht_default_fmt(width_div, height_div, + components_num, pixenc, 0); +} + +static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr) +{ + const struct v4l2_fwht_pixfmt_info *info; + unsigned int w = ntohl(p_hdr->width); + unsigned int h = ntohl(p_hdr->height); + unsigned int version = ntohl(p_hdr->version); + unsigned int flags = ntohl(p_hdr->flags); + + if (!version || version > FWHT_VERSION) + return false; + + if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) + return false; + + if (version == FWHT_VERSION) { + unsigned int components_num = 1 + + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> + FWHT_FL_COMPONENTS_NUM_OFFSET); + unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK; + + if (components_num == 0 || components_num > 4 || !pixenc) + return false; + } + + info = info_from_header(p_hdr); + if (!info) + return false; + return true; +} + +static void update_capture_data_from_header(struct vicodec_ctx *ctx) +{ + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + const struct fwht_cframe_hdr *p_hdr = &ctx->state.header; + const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr); + unsigned int flags = ntohl(p_hdr->flags); + unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + + q_dst->info = info; + q_dst->visible_width = ntohl(p_hdr->width); + q_dst->visible_height = ntohl(p_hdr->height); + q_dst->coded_width = vic_round_dim(q_dst->visible_width, hdr_width_div); + q_dst->coded_height = vic_round_dim(q_dst->visible_height, + hdr_height_div); + + q_dst->sizeimage = q_dst->coded_width * q_dst->coded_height * + q_dst->info->sizeimage_mult / q_dst->info->sizeimage_div; + ctx->state.colorspace = ntohl(p_hdr->colorspace); + + ctx->state.xfer_func = ntohl(p_hdr->xfer_func); + ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc); + ctx->state.quantization = ntohl(p_hdr->quantization); +} + +static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf, + const struct vb2_v4l2_buffer *src_buf, + struct vicodec_ctx *ctx) +{ + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + dst_buf->sequence = q_dst->sequence++; + + v4l2_m2m_buf_copy_data(src_buf, dst_buf, !ctx->is_enc); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); +} + static int job_ready(void *priv) { static const u8 magic[] = { @@ -334,7 +425,16 @@ static int job_ready(void *priv) u8 *p; u32 sz; u32 state; + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + unsigned int flags; + unsigned int hdr_width_div; + unsigned int hdr_height_div; + unsigned int max_to_copy; + unsigned int comp_frame_size; + if (ctx->source_changed) + return 0; if (ctx->is_enc || ctx->comp_has_frame) return 1; @@ -355,14 +455,21 @@ restart: job_remove_src_buf(ctx, state); goto restart; } - - ctx->comp_frame_size = ntohl(ctx->state.header.size); - - if (ctx->comp_frame_size > ctx->comp_max_size) - ctx->comp_frame_size = ctx->comp_max_size; } - if (ctx->comp_size < ctx->comp_frame_size) { - u32 copy = ctx->comp_frame_size - ctx->comp_size; + + comp_frame_size = ntohl(ctx->state.header.size); + + /* + * The current scanned frame might be the first frame of a new + * resolution so its size might be larger than ctx->comp_max_size. + * In that case it is copied up to the current buffer capacity and + * the copy will continue after allocating new large enough buffer + * when restreaming + */ + max_to_copy = min(comp_frame_size, ctx->comp_max_size); + + if (ctx->comp_size < max_to_copy) { + u32 copy = max_to_copy - ctx->comp_size; if (copy > p_src + sz - p) copy = p_src + sz - p; @@ -371,15 +478,17 @@ restart: p, copy); p += copy; ctx->comp_size += copy; - if (ctx->comp_size < ctx->comp_frame_size) { + if (ctx->comp_size < max_to_copy) { job_remove_src_buf(ctx, state); goto restart; } } ctx->cur_buf_offset = p - p_src; - ctx->comp_has_frame = true; + if (ctx->comp_size == comp_frame_size) + ctx->comp_has_frame = true; ctx->comp_has_next_frame = false; - if (sz - ctx->cur_buf_offset >= sizeof(struct fwht_cframe_hdr)) { + if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >= + sizeof(struct fwht_cframe_hdr)) { struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p; u32 frame_size = ntohl(p_hdr->size); u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); @@ -387,6 +496,36 @@ restart: if (!memcmp(p, magic, sizeof(magic))) ctx->comp_has_next_frame = remaining >= frame_size; } + /* + * if the header is invalid the device_run will just drop the frame + * with an error + */ + if (!is_header_valid(&ctx->state.header) && ctx->comp_has_frame) + return 1; + flags = ntohl(ctx->state.header.flags); + hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + + if (ntohl(ctx->state.header.width) != q_dst->visible_width || + ntohl(ctx->state.header.height) != q_dst->visible_height || + !q_dst->info || + hdr_width_div != q_dst->info->width_div || + hdr_height_div != q_dst->info->height_div) { + static const struct v4l2_event rs_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + struct vb2_v4l2_buffer *dst_buf = + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + update_capture_data_from_header(ctx); + ctx->first_source_change_sent = true; + v4l2_event_queue_fh(&ctx->fh, &rs_event); + set_last_buffer(dst_buf, src_buf, ctx); + ctx->source_changed = true; + return 0; + } return 1; } @@ -428,7 +567,7 @@ static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, const struct v4l2_fwht_pixfmt_info *info = get_q_data(ctx, f->type)->info; - if (ctx->is_enc) + if (!info || ctx->is_enc) info = v4l2_fwht_get_pixfmt(f->index); else info = v4l2_fwht_default_fmt(info->width_div, @@ -478,6 +617,9 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data = get_q_data(ctx, f->type); info = q_data->info; + if (!info) + info = v4l2_fwht_get_pixfmt(0); + switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -688,6 +830,7 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix = &f->fmt.pix; if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = + !q_data->info || q_data->info->id != pix->pixelformat || q_data->coded_width != pix->width || q_data->coded_height != pix->height; @@ -708,6 +851,7 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix_mp = &f->fmt.pix_mp; if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = + !q_data->info || q_data->info->id != pix_mp->pixelformat || q_data->coded_width != pix_mp->width || q_data->coded_height != pix_mp->height; @@ -966,6 +1110,7 @@ static int vicodec_subscribe_event(struct v4l2_fh *fh, { switch (sub->type) { case V4L2_EVENT_EOS: + case V4L2_EVENT_SOURCE_CHANGE: return v4l2_event_subscribe(fh, sub, 0, NULL); default: return v4l2_ctrl_subscribe_event(fh, sub); @@ -1074,7 +1219,71 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + unsigned int sz = vb2_get_plane_payload(&vbuf->vb2_buf, 0); + u8 *p_src = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + u8 *p = p_src; + struct vb2_queue *vq_out = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT); + struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + bool header_valid = false; + static const struct v4l2_event rs_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + /* buf_queue handles only the first source change event */ + if (ctx->first_source_change_sent) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + return; + } + + /* + * if both queues are streaming, the source change event is + * handled in job_ready + */ + if (vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out)) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + return; + } + + /* + * source change event is relevant only for the decoder + * in the compressed stream + */ + if (ctx->is_enc || !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + return; + } + + do { + enum vb2_buffer_state state = + get_next_header(ctx, &p, p_src + sz - p); + + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + v4l2_m2m_buf_done(vbuf, state); + return; + } + header_valid = is_header_valid(&ctx->state.header); + /* + * p points right after the end of the header in the + * buffer. If the header is invalid we set p to point + * to the next byte after the start of the header + */ + if (!header_valid) { + p = p - sizeof(struct fwht_cframe_hdr) + 1; + if (p < p_src) + p = p_src; + ctx->header_size = 0; + ctx->comp_magic_cnt = 0; + } + + } while (!header_valid); + + ctx->cur_buf_offset = p - p_src; + update_capture_data_from_header(ctx); + ctx->first_source_change_sent = true; + v4l2_event_queue_fh(&ctx->fh, &rs_event); v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } @@ -1104,51 +1313,68 @@ static int vicodec_start_streaming(struct vb2_queue *q, struct v4l2_fwht_state *state = &ctx->state; const struct v4l2_fwht_pixfmt_info *info = q_data->info; unsigned int size = q_data->coded_width * q_data->coded_height; - unsigned int chroma_div = info->width_div * info->height_div; + unsigned int chroma_div; unsigned int total_planes_size; + u8 *new_comp_frame; - /* - * we don't know ahead how many components are in the encoding type - * V4L2_PIX_FMT_FWHT, so we will allocate space for 4 planes. - */ - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4) + if (!info) + return -EINVAL; + + chroma_div = info->width_div * info->height_div; + q_data->sequence = 0; + + ctx->last_src_buf = NULL; + ctx->last_dst_buf = NULL; + state->gop_cnt = 0; + + if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || + (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) + return 0; + + if (info->id == V4L2_PIX_FMT_FWHT) { + vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); + return -EINVAL; + } + if (info->components_num == 4) total_planes_size = 2 * size + 2 * (size / chroma_div); else if (info->components_num == 3) total_planes_size = size + 2 * (size / chroma_div); else total_planes_size = size; - q_data->sequence = 0; + state->visible_width = q_data->visible_width; + state->visible_height = q_data->visible_height; + state->coded_width = q_data->coded_width; + state->coded_height = q_data->coded_height; + state->stride = q_data->coded_width * + info->bytesperline_mult; - if (!V4L2_TYPE_IS_OUTPUT(q->type)) { - if (!ctx->is_enc) { - state->visible_width = q_data->visible_width; - state->visible_height = q_data->visible_height; - state->coded_width = q_data->coded_width; - state->coded_height = q_data->coded_height; - state->stride = q_data->coded_width * - info->bytesperline_mult; - } - return 0; - } - - if (ctx->is_enc) { - state->visible_width = q_data->visible_width; - state->visible_height = q_data->visible_height; - state->coded_width = q_data->coded_width; - state->coded_height = q_data->coded_height; - state->stride = q_data->coded_width * info->bytesperline_mult; - } state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); ctx->comp_max_size = total_planes_size; - state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); - if (!state->ref_frame.luma || !state->compressed_frame) { + new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); + + if (!state->ref_frame.luma || !new_comp_frame) { kvfree(state->ref_frame.luma); - kvfree(state->compressed_frame); + kvfree(new_comp_frame); vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); return -ENOMEM; } - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num >= 3) { + /* + * if state->compressed_frame was already allocated then + * it contain data of the first frame of the new resolution + */ + if (state->compressed_frame) { + if (ctx->comp_size > ctx->comp_max_size) + ctx->comp_size = ctx->comp_max_size; + + memcpy(new_comp_frame, + state->compressed_frame, ctx->comp_size); + } + + kvfree(state->compressed_frame); + state->compressed_frame = new_comp_frame; + + if (info->components_num >= 3) { state->ref_frame.cb = state->ref_frame.luma + size; state->ref_frame.cr = state->ref_frame.cb + size / chroma_div; } else { @@ -1156,21 +1382,11 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->ref_frame.cr = NULL; } - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4) + if (info->components_num == 4) state->ref_frame.alpha = state->ref_frame.cr + size / chroma_div; else state->ref_frame.alpha = NULL; - - ctx->last_src_buf = NULL; - ctx->last_dst_buf = NULL; - state->gop_cnt = 0; - ctx->cur_buf_offset = 0; - ctx->comp_size = 0; - ctx->header_size = 0; - ctx->comp_magic_cnt = 0; - ctx->comp_has_frame = false; - return 0; } @@ -1180,11 +1396,20 @@ static void vicodec_stop_streaming(struct vb2_queue *q) vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); - if (!V4L2_TYPE_IS_OUTPUT(q->type)) - return; - - kvfree(ctx->state.ref_frame.luma); - kvfree(ctx->state.compressed_frame); + if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || + (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { + kvfree(ctx->state.ref_frame.luma); + ctx->comp_max_size = 0; + ctx->source_changed = false; + } + if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) { + ctx->cur_buf_offset = 0; + ctx->comp_size = 0; + ctx->header_size = 0; + ctx->comp_magic_cnt = 0; + ctx->comp_has_frame = 0; + ctx->comp_has_next_frame = 0; + } } static const struct vb2_ops vicodec_qops = { @@ -1336,16 +1561,17 @@ static int vicodec_open(struct file *file) else ctx->q_data[V4L2_M2M_SRC].sizeimage = size + sizeof(struct fwht_cframe_hdr); - ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; - ctx->q_data[V4L2_M2M_DST].info = - ctx->is_enc ? &pixfmt_fwht : v4l2_fwht_get_pixfmt(0); - size = 1280 * 720 * ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / - ctx->q_data[V4L2_M2M_DST].info->sizeimage_div; - if (ctx->is_enc) - ctx->q_data[V4L2_M2M_DST].sizeimage = - size + sizeof(struct fwht_cframe_hdr); - else - ctx->q_data[V4L2_M2M_DST].sizeimage = size; + if (ctx->is_enc) { + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; + ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; + ctx->q_data[V4L2_M2M_DST].sizeimage = 1280 * 720 * + ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / + ctx->q_data[V4L2_M2M_DST].info->sizeimage_div + + sizeof(struct fwht_cframe_hdr); + } else { + ctx->q_data[V4L2_M2M_DST].info = NULL; + } + ctx->state.colorspace = V4L2_COLORSPACE_REC709; if (ctx->is_enc) { From f863f222b49a9c7a6cfcc72f1ac74ab14c7a7258 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 24 Jan 2019 07:51:08 -0200 Subject: [PATCH 0707/4277] media: vicodec: ensure comp frame pointer kept in range Make sure that the pointer to the compressed frame does not get out of the buffer. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.c | 71 +++++++++++++------ drivers/media/platform/vicodec/codec-fwht.h | 2 +- .../media/platform/vicodec/codec-v4l2-fwht.c | 8 ++- drivers/media/platform/vicodec/vicodec-core.c | 4 ++ 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c index e5e0a80c2f73..d1d6085da9f1 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/platform/vicodec/codec-fwht.c @@ -13,6 +13,8 @@ #include #include "codec-fwht.h" +#define OVERFLOW_BIT BIT(14) + /* * Note: bit 0 of the header must always be 0. Otherwise it cannot * be guaranteed that the magic 8 byte sequence (see below) can @@ -104,16 +106,21 @@ static int rlc(const s16 *in, __be16 *output, int blocktype) * This function will worst-case increase rlc_in by 65*2 bytes: * one s16 value for the header and 8 * 8 coefficients of type s16. */ -static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) +static u16 derlc(const __be16 **rlc_in, s16 *dwht_out, + const __be16 *end_of_input) { /* header */ const __be16 *input = *rlc_in; - s16 ret = ntohs(*input++); + u16 stat; int dec_count = 0; s16 block[8 * 8 + 16]; s16 *wp = block; int i; + if (input > end_of_input) + return OVERFLOW_BIT; + stat = ntohs(*input++); + /* * Now de-compress, it expands one byte to up to 15 bytes * (or fills the remainder of the 64 bytes with zeroes if it @@ -123,9 +130,15 @@ static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) * allow for overflow if the incoming data was malformed. */ while (dec_count < 8 * 8) { - s16 in = ntohs(*input++); - int length = in & 0xf; - int coeff = in >> 4; + s16 in; + int length; + int coeff; + + if (input > end_of_input) + return OVERFLOW_BIT; + in = ntohs(*input++); + length = in & 0xf; + coeff = in >> 4; /* fill remainder with zeros */ if (length == 15) { @@ -150,7 +163,7 @@ static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) dwht_out[x + y * 8] = *wp++; } *rlc_in = input; - return ret; + return stat; } static const int quant_table[] = { @@ -808,22 +821,24 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, return encoding; } -static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, +static bool decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, u32 height, u32 width, u32 coded_width, - bool uncompressed) + bool uncompressed, const __be16 *end_of_rlco_buf) { unsigned int copies = 0; s16 copy[8 * 8]; - s16 stat; + u16 stat; unsigned int i, j; width = round_up(width, 8); height = round_up(height, 8); if (uncompressed) { + if (end_of_rlco_buf + 1 < *rlco + width * height / 2) + return false; memcpy(ref, *rlco, width * height); *rlco += width * height / 2; - return; + return true; } /* @@ -847,8 +862,9 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, continue; } - stat = derlc(rlco, cf->coeffs); - + stat = derlc(rlco, cf->coeffs, end_of_rlco_buf); + if (stat & OVERFLOW_BIT) + return false; if (stat & PFRAME_BIT) dequantize_inter(cf->coeffs); else @@ -865,17 +881,22 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, fill_decoder_block(refp, cf->de_fwht, coded_width); } } + return true; } -void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, +bool fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, u32 hdr_flags, unsigned int components_num, unsigned int width, unsigned int height, unsigned int coded_width) { const __be16 *rlco = cf->rlc_data; + const __be16 *end_of_rlco_buf = cf->rlc_data + + (cf->size / sizeof(*rlco)) - 1; - decode_plane(cf, &rlco, ref->luma, height, width, coded_width, - hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED); + if (!decode_plane(cf, &rlco, ref->luma, height, width, coded_width, + hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; if (components_num >= 3) { u32 h = height; @@ -888,13 +909,21 @@ void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, w /= 2; c /= 2; } - decode_plane(cf, &rlco, ref->cb, h, w, c, - hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED); - decode_plane(cf, &rlco, ref->cr, h, w, c, - hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED); + if (!decode_plane(cf, &rlco, ref->cb, h, w, c, + hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; + if (!decode_plane(cf, &rlco, ref->cr, h, w, c, + hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; } if (components_num == 4) - decode_plane(cf, &rlco, ref->alpha, height, width, coded_width, - hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED); + if (!decode_plane(cf, &rlco, ref->alpha, height, width, + coded_width, + hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; + return true; } diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index ad8cfc60a152..60d71d9dacb3 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -139,7 +139,7 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, bool is_intra, bool next_is_intra, unsigned int width, unsigned int height, unsigned int stride, unsigned int chroma_stride); -void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, +bool fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, u32 hdr_flags, unsigned int components_num, unsigned int width, unsigned int height, unsigned int coded_width); diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index ee6903b8896c..c15034849133 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -280,6 +280,7 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) state->ycbcr_enc = ntohl(state->header.ycbcr_enc); state->quantization = ntohl(state->header.quantization); cf.rlc_data = (__be16 *)p_in; + cf.size = ntohl(state->header.size); hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; @@ -287,9 +288,10 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) hdr_height_div != info->height_div) return -EINVAL; - fwht_decode_frame(&cf, &state->ref_frame, flags, components_num, - state->visible_width, state->visible_height, - state->coded_width); + if (!fwht_decode_frame(&cf, &state->ref_frame, flags, components_num, + state->visible_width, state->visible_height, + state->coded_width)) + return -EINVAL; /* * TODO - handle the case where the compressed stream encodes a diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 454476a9f659..28c3a3d57783 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -186,6 +186,10 @@ static int device_process(struct vicodec_ctx *ctx, return ret; vb2_set_plane_payload(&dst_vb->vb2_buf, 0, ret); } else { + unsigned int comp_frame_size = ntohl(ctx->state.header.size); + + if (comp_frame_size > ctx->comp_max_size) + return -EINVAL; state->info = q_dst->info; ret = v4l2_fwht_decode(state, p_src, p_dst); if (ret < 0) From c9d06df612977a88c484668ad0a37bc8e4463b22 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jan 2019 09:12:04 -0200 Subject: [PATCH 0708/4277] media: vicodec: get_next_header is static drivers/media/platform/vicodec/vicodec-core.c:drivers/media/platform/vicodec/vicodec-core.c:210:23: warning: symbol 'get_next_header' was not declared. Should it be static? drivers/media/platform/vicodec/vicodec-core.c:210:23: warning: no previous prototype for 'get_next_header' [-Wmissing-prototypes] enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, u8 **pp, u32 sz) ^~~~~~~~~~~~~~~ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 28c3a3d57783..3703b587e25e 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -207,7 +207,8 @@ static int device_process(struct vicodec_ctx *ctx, /* * mem2mem callbacks */ -enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, u8 **pp, u32 sz) +static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, + u8 **pp, u32 sz) { static const u8 magic[] = { 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff From e97a34563d18606ee5db93e495382a967f999cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20de=20Bretagne?= Date: Sun, 6 Jan 2019 18:56:44 +0100 Subject: [PATCH 0709/4277] platform/x86: intel-hid: Missing power button release on some Dell models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power button suspend for some Dell models was added in: commit 821b85366284 ("platform/x86: intel-hid: Power button suspend on Dell Latitude 7275") by checking against the power button press notification (0xCE) to report the power button press event. The corresponding power button release notification (0xCF) was caught and ignored to stop it from being reported as an "unknown event" in the logs. The missing button release event is creating issues on Android-x86, as reported on the project mailing list for a Dell Latitude 5175 model, since the events are expected in down/up pairs. Report the power button release event to fix this issue. Link: https://groups.google.com/forum/#!topic/android-x86/aSwZK9Nf9Ro Tested-by: Tristian Celestin Tested-by: Jérôme de Bretagne Signed-off-by: Jérôme de Bretagne Reviewed-by: Mario Limonciello [dvhart: corrected commit reference format per checkpatch] Signed-off-by: Darren Hart (VMware) --- drivers/platform/x86/intel-hid.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index e28bcf61b126..bc0d55a59015 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -363,7 +363,7 @@ wakeup: * the 5-button array, but still send notifies with power button * event code to this device object on power button actions. * - * Report the power button press; catch and ignore the button release. + * Report the power button press and release. */ if (!priv->array) { if (event == 0xce) { @@ -372,8 +372,11 @@ wakeup: return; } - if (event == 0xcf) + if (event == 0xcf) { + input_report_key(priv->input_dev, KEY_POWER, 0); + input_sync(priv->input_dev); return; + } } /* 0xC0 is for HID events, other values are for 5 button array */ From 522e4ee6e5263ac324279b164762b0bccca3de87 Mon Sep 17 00:00:00 2001 From: "Darren Hart (VMware)" Date: Sat, 12 Jan 2019 20:27:35 -0800 Subject: [PATCH 0710/4277] Documentation/ABI: Correct mlxreg-io KernelVersion for 5.0 The mlxreg-io for the merge window assumed 4.21 as the next kernel version. Replace 4.21 with 5.0. Signed-off-by: Darren Hart (VMware) Reviewed-by: Andy Shevchenko --- Documentation/ABI/stable/sysfs-driver-mlxreg-io | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io index 9b642669cb16..169fe08a649b 100644 --- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io +++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io @@ -24,7 +24,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ cpld3_version Date: November 2018 -KernelVersion: 4.21 +KernelVersion: 5.0 Contact: Vadim Pasternak Description: These files show with which CPLD versions have been burned on LED board. @@ -35,7 +35,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ jtag_enable Date: November 2018 -KernelVersion: 4.21 +KernelVersion: 5.0 Contact: Vadim Pasternak Description: These files enable and disable the access to the JTAG domain. By default access to the JTAG domain is disabled. @@ -105,7 +105,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ reset_voltmon_upgrade_fail Date: November 2018 -KernelVersion: 4.21 +KernelVersion: 5.0 Contact: Vadim Pasternak Description: These files show the system reset cause, as following: ComEx power fail, reset from ComEx, system platform reset, reset From aff475804f608c5375dc1c5df6f0fdeb63459ccb Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 12 Dec 2018 23:59:11 +0000 Subject: [PATCH 0711/4277] platform/x86: mlx-platform: Add support for fan direction register Provide support for the fan direction register. This register shows configuration for system fans direction, which could be forward or reversed. For forward direction - relevant bit is set 0; For reversed direction - relevant bit is set 1. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- drivers/platform/x86/mlx-platform.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index df3fcd36776a..3b795680d81c 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -33,6 +33,7 @@ #define MLXPLAT_CPLD_LPC_REG_LED3_OFFSET 0x22 #define MLXPLAT_CPLD_LPC_REG_LED4_OFFSET 0x23 #define MLXPLAT_CPLD_LPC_REG_LED5_OFFSET 0x24 +#define MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION 0x2a #define MLXPLAT_CPLD_LPC_REG_GP1_OFFSET 0x30 #define MLXPLAT_CPLD_LPC_REG_WP1_OFFSET 0x31 #define MLXPLAT_CPLD_LPC_REG_GP2_OFFSET 0x32 @@ -1184,6 +1185,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .bit = 1, .mode = 0444, }, + { + .label = "fan_dir", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION, + .bit = GENMASK(7, 0), + .mode = 0200, + }, }; static struct mlxreg_core_platform_data mlxplat_default_ng_regs_io_data = { @@ -1307,6 +1314,7 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION: case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_WP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET: @@ -1360,6 +1368,7 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION: case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET: From 2752e34442b5cb48993e4c5789cd5e73ecab61ff Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 12 Dec 2018 23:59:12 +0000 Subject: [PATCH 0712/4277] Documentation/ABI: Add new attribute for mlxreg-io sysfs interfaces Add documentation for the new attribute fan_dir (fan direction for systems of types MQM87xx, MSN34xx, MSN37xx. Signed-off-by: Vadim Pasternak [dvhart: minor gramatical correction] Signed-off-by: Darren Hart (VMware) --- Documentation/ABI/stable/sysfs-driver-mlxreg-io | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io index 169fe08a649b..156319fc5b80 100644 --- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io +++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io @@ -21,7 +21,19 @@ Description: These files show with which CPLD versions have been burned The files are read only. What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ - cpld3_version + fan_dir + +Date: December 2018 +KernelVersion: 5.0 +Contact: Vadim Pasternak +Description: This file shows the system fans direction: + forward direction - relevant bit is set 0; + reversed direction - relevant bit is set 1. + + The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ + jtag_enable Date: November 2018 KernelVersion: 5.0 From 9b28aa1d0eae1be1016c8f4ba504545caff01da3 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 12 Dec 2018 23:59:13 +0000 Subject: [PATCH 0713/4277] platform_data/mlxreg: Document fixes for core platform data Remove "led" from the description, since the structure "mlxreg_core_platform_data" is used not only for led data. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- include/linux/platform_data/mlxreg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index 19f5cb618c55..d823713f94ec 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -107,9 +107,9 @@ struct mlxreg_core_item { /** * struct mlxreg_core_platform_data - platform data: * - * @led_data: led private data; + * @data: instance private data; * @regmap: register map of parent device; - * @counter: number of led instances; + * @counter: number of instances; */ struct mlxreg_core_platform_data { struct mlxreg_core_data *data; From 946e4e02b11889cb161b15ff4712a8ba21a50eb6 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 12 Dec 2018 23:59:14 +0000 Subject: [PATCH 0714/4277] platform_data/mlxreg: Add capability field to core platform data Add capability field to "mlxreg_core_platform_data" structure. The purpose of this register is to provide additional info to platform driver through the atribute related capability register. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- include/linux/platform_data/mlxreg.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index d823713f94ec..1b2f86f96743 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -61,6 +61,7 @@ struct mlxreg_hotplug_device { * @reg: attribute register; * @mask: attribute access mask; * @bit: attribute effective bit; + * @capability: attribute capability register; * @mode: access mode; * @np - pointer to node platform associated with attribute; * @hpdev - hotplug device data; @@ -72,6 +73,7 @@ struct mlxreg_core_data { u32 reg; u32 mask; u32 bit; + u32 capability; umode_t mode; struct device_node *np; struct mlxreg_hotplug_device hpdev; From 83cdb2c11173ee4aa621c8cce6e1c33fb564d2be Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 12 Dec 2018 23:59:15 +0000 Subject: [PATCH 0715/4277] platform/x86: mlx-platform: Add support for fan capability registers Provide support for the fan capability registers for the next generation systems of types MQM87xx, MSN34xx, MSN37xx. These new registers provide configuration for tachometers and fan drawers connectivity. Use these registers for next generation led, fan and hotplug structures in order to distinguish between the systems which have minor configuration differences. This reduces the amount of code used to describe such systems. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- drivers/platform/mellanox/mlxreg-hotplug.c | 23 +++++++- drivers/platform/x86/mlx-platform.c | 69 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index b6d44550d98c..52314a1ffaaf 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -495,7 +495,9 @@ static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) { struct mlxreg_core_hotplug_platform_data *pdata; struct mlxreg_core_item *item; - int i, ret; + struct mlxreg_core_data *data; + u32 regval; + int i, j, ret; pdata = dev_get_platdata(&priv->pdev->dev); item = pdata->items; @@ -507,6 +509,25 @@ static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) if (ret) goto out; + /* + * Verify if hardware configuration requires to disable + * interrupt capability for some of components. + */ + data = item->data; + for (j = 0; j < item->count; j++, data++) { + /* Verify if the attribute has capability register. */ + if (data->capability) { + /* Read capability register. */ + ret = regmap_read(priv->regmap, + data->capability, ®val); + if (ret) + goto out; + + if (!(regval & data->bit)) + item->mask &= ~BIT(j); + } + } + /* Set group initial status as mask and unmask group event. */ if (item->inversed) { item->cache = item->mask; diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 3b795680d81c..73efb12a420a 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -68,6 +68,9 @@ #define MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET 0xee #define MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET 0xef #define MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET 0xf0 +#define MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET 0xf5 +#define MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET 0xf6 +#define MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET 0xf7 #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda @@ -585,36 +588,48 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = { .label = "fan1", .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, .mask = BIT(0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(0), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, { .label = "fan2", .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, .mask = BIT(1), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(1), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, { .label = "fan3", .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, .mask = BIT(2), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(2), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, { .label = "fan4", .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, .mask = BIT(3), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(3), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, { .label = "fan5", .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, .mask = BIT(4), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(4), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, { .label = "fan6", .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, .mask = BIT(5), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(5), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, }; @@ -817,61 +832,85 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = { .label = "fan1:green", .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(0), }, { .label = "fan1:orange", .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(0), }, { .label = "fan2:green", .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(1), }, { .label = "fan2:orange", .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(1), }, { .label = "fan3:green", .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(2), }, { .label = "fan3:orange", .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(2), }, { .label = "fan4:green", .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(3), }, { .label = "fan4:orange", .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(3), }, { .label = "fan5:green", .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(4), }, { .label = "fan5:orange", .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(4), }, { .label = "fan6:green", .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(5), }, { .label = "fan6:orange", .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(5), }, }; @@ -1208,61 +1247,85 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = { .label = "tacho1", .reg = MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(0), }, { .label = "tacho2", .reg = MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(1), }, { .label = "tacho3", .reg = MLXPLAT_CPLD_LPC_REG_TACHO3_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(2), }, { .label = "tacho4", .reg = MLXPLAT_CPLD_LPC_REG_TACHO4_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(3), }, { .label = "tacho5", .reg = MLXPLAT_CPLD_LPC_REG_TACHO5_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(4), }, { .label = "tacho6", .reg = MLXPLAT_CPLD_LPC_REG_TACHO6_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(5), }, { .label = "tacho7", .reg = MLXPLAT_CPLD_LPC_REG_TACHO7_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(6), }, { .label = "tacho8", .reg = MLXPLAT_CPLD_LPC_REG_TACHO8_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET, + .bit = BIT(7), }, { .label = "tacho9", .reg = MLXPLAT_CPLD_LPC_REG_TACHO9_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET, + .bit = BIT(0), }, { .label = "tacho10", .reg = MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET, + .bit = BIT(1), }, { .label = "tacho11", .reg = MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET, + .bit = BIT(2), }, { .label = "tacho12", .reg = MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET, .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET, + .bit = BIT(3), }, }; @@ -1349,6 +1412,9 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET: return true; } return false; @@ -1401,6 +1467,9 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET: return true; } return false; From e7706a4359f0f172b5f2ab6807f421145041c393 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 12 Dec 2018 23:59:16 +0000 Subject: [PATCH 0716/4277] platform/x86: mlx-platform: Add support for new VMOD0007 board name Add support for new Mellanox system type MSN3700C, which is a cost reduced flavor of the MSN37 system class. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- drivers/platform/x86/mlx-platform.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 73efb12a420a..aa3311971be6 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -1691,6 +1691,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "MSN34"), }, }, + { + .callback = mlxplat_dmi_qmb7xx_matched, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), + DMI_MATCH(DMI_PRODUCT_NAME, "MSN38"), + }, + }, { .callback = mlxplat_dmi_default_matched, .matches = { @@ -1721,6 +1728,12 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"), }, }, + { + .callback = mlxplat_dmi_qmb7xx_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0007"), + }, + }, { } }; From 58156ba4468f1d0de166a4330374bc9df9b74efc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 16 Jan 2019 11:56:40 +0900 Subject: [PATCH 0717/4277] kbuild: skip 'addtree' and 'flags' magic for external module build When building an external module, $(obj) is the absolute path to it. The header search paths from ccflags-y etc. should not be tweaked. Signed-off-by: Masahiro Yamada --- scripts/Makefile.host | 6 ++++-- scripts/Makefile.lib | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/Makefile.host b/scripts/Makefile.host index 0393f75db4d4..a115259b57e7 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -67,13 +67,15 @@ _hostc_flags = $(KBUILD_HOSTCFLAGS) $(HOST_EXTRACFLAGS) \ _hostcxx_flags = $(KBUILD_HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \ $(HOSTCXXFLAGS_$(basetarget).o) -ifeq ($(KBUILD_SRC),) __hostc_flags = $(_hostc_flags) __hostcxx_flags = $(_hostcxx_flags) -else + +ifeq ($(KBUILD_EXTMOD),) +ifneq ($(KBUILD_SRC),) __hostc_flags = -I$(obj) $(call flags,_hostc_flags) __hostcxx_flags = -I$(obj) $(call flags,_hostcxx_flags) endif +endif hostc_flags = -Wp,-MD,$(depfile) $(__hostc_flags) hostcxx_flags = -Wp,-MD,$(depfile) $(__hostcxx_flags) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 12b88d09c3a4..ecad15b4cda3 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -137,14 +137,14 @@ _c_flags += $(if $(patsubst n%,, \ $(CFLAGS_KCOV)) endif -# If building the kernel in a separate objtree expand all occurrences -# of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/'). - -ifeq ($(KBUILD_SRC),) __c_flags = $(_c_flags) __a_flags = $(_a_flags) __cpp_flags = $(_cpp_flags) -else + +# If building the kernel in a separate objtree expand all occurrences +# of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/'). +ifeq ($(KBUILD_EXTMOD),) +ifneq ($(KBUILD_SRC),) # -I$(obj) locates generated .h files # $(call addtree,-I$(obj)) locates .h files in srctree, from generated .c files @@ -155,6 +155,7 @@ __c_flags = $(if $(obj),$(call addtree,-I$(src)) -I$(obj)) \ __a_flags = $(call flags,_a_flags) __cpp_flags = $(call flags,_cpp_flags) endif +endif c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \ -include $(srctree)/include/linux/compiler_types.h \ From dee9495303f2c6d63a20c43a26420765909898eb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jan 2019 09:10:03 +0900 Subject: [PATCH 0718/4277] kbuild: remove top-level built-in.a The symbol table in the final archive is unneeded; the linker does not require the symbol table after the --whole-archive option. Every object file in the archive is included in the link anyway. Pass thin archives from subdirectories directly to the linker, and remove the final archiving step. Fix up the document and comments as well. Signed-off-by: Masahiro Yamada Acked-by: Nicholas Piggin --- Documentation/kbuild/makefiles.txt | 9 ++------- scripts/Makefile.build | 8 ++------ scripts/link-vmlinux.sh | 30 ++++++------------------------ 3 files changed, 10 insertions(+), 37 deletions(-) diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index bf28c47bfd72..48eab0bb1183 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -154,13 +154,8 @@ more details, with real examples. Kbuild compiles all the $(obj-y) files. It then calls "$(AR) rcSTP" to merge these files into one built-in.a file. - This is a thin archive without a symbol table, which makes it - unsuitable as a linker input. - - The scripts/link-vmlinux.sh script later makes an aggregate - built-in.a with "${AR} rcsTP", which creates the thin archive - with a symbol table and an index, making it a valid input for - the final vmlinux link passes. + This is a thin archive without a symbol table. It will be later + linked into vmlinux by scripts/link-vmlinux.sh The order of files in $(obj-y) is significant. Duplicates in the lists are allowed: the first instance will be linked into diff --git a/scripts/Makefile.build b/scripts/Makefile.build index fd03d60f6c5a..681ab58a1548 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -394,14 +394,10 @@ $(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler $(sort $(subdir-obj-y)): $(subdir-ym) ; # -# Rule to compile a set of .o files into one .o file +# Rule to compile a set of .o files into one .a file (without symbol table) # ifdef builtin-target -# built-in.a archives are made with no symbol table or index which -# makes them small and fast, but unable to be used by the linker. -# scripts/link-vmlinux.sh builds an aggregate built-in.a with a symbol -# table and index. quiet_cmd_ar_builtin = AR $@ cmd_ar_builtin = rm -f $@; \ $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(filter $(real-obj-y), $^) @@ -426,7 +422,7 @@ $(modorder-target): $(subdir-ym) FORCE $(Q)(cat /dev/null; $(modorder-cmds)) > $@ # -# Rule to compile a set of .o files into one .a file +# Rule to compile a set of .o files into one .a file (with symbol table) # ifdef lib-target quiet_cmd_link_l_target = AR $@ diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index c8cf45362bd6..4788def71358 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -44,24 +44,6 @@ info() fi } -# Thin archive build here makes a final archive with symbol table and indexes -# from vmlinux objects INIT and MAIN, which can be used as input to linker. -# KBUILD_VMLINUX_LIBS archives should already have symbol table and indexes -# added. -# -# Traditional incremental style of link does not require this step -# -# built-in.a output file -# -archive_builtin() -{ - info AR built-in.a - rm -f built-in.a; - ${AR} rcsTP${KBUILD_ARFLAGS} built-in.a \ - ${KBUILD_VMLINUX_INIT} \ - ${KBUILD_VMLINUX_MAIN} -} - # Link of vmlinux.o used for section mismatch analysis # ${1} output file modpost_link() @@ -69,7 +51,8 @@ modpost_link() local objects objects="--whole-archive \ - built-in.a \ + ${KBUILD_VMLINUX_INIT} \ + ${KBUILD_VMLINUX_MAIN} \ --no-whole-archive \ --start-group \ ${KBUILD_VMLINUX_LIBS} \ @@ -88,7 +71,8 @@ vmlinux_link() if [ "${SRCARCH}" != "um" ]; then objects="--whole-archive \ - built-in.a \ + ${KBUILD_VMLINUX_INIT} \ + ${KBUILD_VMLINUX_MAIN} \ --no-whole-archive \ --start-group \ ${KBUILD_VMLINUX_LIBS} \ @@ -99,7 +83,8 @@ vmlinux_link() -T ${lds} ${objects} else objects="-Wl,--whole-archive \ - built-in.a \ + ${KBUILD_VMLINUX_INIT} \ + ${KBUILD_VMLINUX_MAIN} \ -Wl,--no-whole-archive \ -Wl,--start-group \ ${KBUILD_VMLINUX_LIBS} \ @@ -160,7 +145,6 @@ cleanup() rm -f .tmp_System.map rm -f .tmp_kallsyms* rm -f .tmp_vmlinux* - rm -f built-in.a rm -f System.map rm -f vmlinux rm -f vmlinux.o @@ -217,8 +201,6 @@ fi; # final build of init/ ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init -archive_builtin - #link vmlinux.o info LD vmlinux.o modpost_link vmlinux.o From d151e9719f184f00d0bb2af864d0aef9beaee7ab Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jan 2019 09:10:04 +0900 Subject: [PATCH 0719/4277] kbuild: merge KBUILD_VMLINUX_{INIT,MAIN} into KBUILD_VMLINUX_OBJS The top Makefile does not need to export KBUILD_VMLINUX_INIT and KBUILD_VMLINUX_MAIN separately. Put every built-in.a into KBUILD_VMLINUX_OBJS. The order of $(head-y), $(init-y), $(core-y), ... is still retained. Signed-off-by: Masahiro Yamada --- Documentation/kbuild/kbuild.txt | 15 +++++---------- Makefile | 6 +++--- scripts/link-vmlinux.sh | 28 ++++++++++------------------ 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/Documentation/kbuild/kbuild.txt b/Documentation/kbuild/kbuild.txt index c9e3d93e7a89..8a3830b39c7d 100644 --- a/Documentation/kbuild/kbuild.txt +++ b/Documentation/kbuild/kbuild.txt @@ -232,17 +232,12 @@ KBUILD_LDS -------------------------------------------------- The linker script with full path. Assigned by the top-level Makefile. -KBUILD_VMLINUX_INIT +KBUILD_VMLINUX_OBJS -------------------------------------------------- -All object files for the init (first) part of vmlinux. -Files specified with KBUILD_VMLINUX_INIT are linked first. - -KBUILD_VMLINUX_MAIN --------------------------------------------------- -All object files for the main part of vmlinux. +All object files for vmlinux. They are linked to vmlinux in the same +order as listed in KBUILD_VMLINUX_OBJS. KBUILD_VMLINUX_LIBS -------------------------------------------------- -All .a "lib" files for vmlinux. -KBUILD_VMLINUX_INIT, KBUILD_VMLINUX_MAIN, and KBUILD_VMLINUX_LIBS together -specify all the object files used to link vmlinux. +All .a "lib" files for vmlinux. KBUILD_VMLINUX_OBJS and KBUILD_VMLINUX_LIBS +together specify all the object files used to link vmlinux. diff --git a/Makefile b/Makefile index 141653226f3c..2b05cd59ff54 100644 --- a/Makefile +++ b/Makefile @@ -976,15 +976,15 @@ libs-y2 := $(patsubst %/, %/built-in.a, $(filter-out %.a, $(libs-y))) virt-y := $(patsubst %/, %/built-in.a, $(virt-y)) # Externally visible symbols (used by link-vmlinux.sh) -export KBUILD_VMLINUX_INIT := $(head-y) $(init-y) -export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y) +export KBUILD_VMLINUX_OBJS := $(head-y) $(init-y) $(core-y) $(libs-y2) \ + $(drivers-y) $(net-y) $(virt-y) export KBUILD_VMLINUX_LIBS := $(libs-y1) export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds export LDFLAGS_vmlinux # used by scripts/package/Makefile export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation include samples scripts tools) -vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS) +vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS) # Recurse until adjust_autoksyms.sh is satisfied PHONY += autoksyms_recursive diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 4788def71358..bc7f1fc1f55b 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -3,22 +3,17 @@ # # link vmlinux # -# vmlinux is linked from the objects selected by $(KBUILD_VMLINUX_INIT) and -# $(KBUILD_VMLINUX_MAIN) and $(KBUILD_VMLINUX_LIBS). Most are built-in.a files -# from top-level directories in the kernel tree, others are specified in -# arch/$(ARCH)/Makefile. Ordering when linking is important, and -# $(KBUILD_VMLINUX_INIT) must be first. $(KBUILD_VMLINUX_LIBS) are archives -# which are linked conditionally (not within --whole-archive), and do not -# require symbol indexes added. +# vmlinux is linked from the objects selected by $(KBUILD_VMLINUX_OBJS) and +# $(KBUILD_VMLINUX_LIBS). Most are built-in.a files from top-level directories +# in the kernel tree, others are specified in arch/$(ARCH)/Makefile. +# $(KBUILD_VMLINUX_LIBS) are archives which are linked conditionally +# (not within --whole-archive), and do not require symbol indexes added. # # vmlinux # ^ # | -# +-< $(KBUILD_VMLINUX_INIT) -# | +--< init/version.o + more -# | -# +--< $(KBUILD_VMLINUX_MAIN) -# | +--< drivers/built-in.a mm/built-in.a + more +# +--< $(KBUILD_VMLINUX_OBJS) +# | +--< init/built-in.a drivers/built-in.a mm/built-in.a + more # | # +--< $(KBUILD_VMLINUX_LIBS) # | +--< lib/lib.a + more @@ -51,8 +46,7 @@ modpost_link() local objects objects="--whole-archive \ - ${KBUILD_VMLINUX_INIT} \ - ${KBUILD_VMLINUX_MAIN} \ + ${KBUILD_VMLINUX_OBJS} \ --no-whole-archive \ --start-group \ ${KBUILD_VMLINUX_LIBS} \ @@ -71,8 +65,7 @@ vmlinux_link() if [ "${SRCARCH}" != "um" ]; then objects="--whole-archive \ - ${KBUILD_VMLINUX_INIT} \ - ${KBUILD_VMLINUX_MAIN} \ + ${KBUILD_VMLINUX_OBJS} \ --no-whole-archive \ --start-group \ ${KBUILD_VMLINUX_LIBS} \ @@ -83,8 +76,7 @@ vmlinux_link() -T ${lds} ${objects} else objects="-Wl,--whole-archive \ - ${KBUILD_VMLINUX_INIT} \ - ${KBUILD_VMLINUX_MAIN} \ + ${KBUILD_VMLINUX_OBJS} \ -Wl,--no-whole-archive \ -Wl,--start-group \ ${KBUILD_VMLINUX_LIBS} \ From ecbd10d90e9401563f1bcb768f42119c9cbfac91 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jan 2019 19:02:41 +0900 Subject: [PATCH 0720/4277] kbuild: simplify rules of data compression with size appending All the callers of size_append pass $(filter-out FORCE,$^). Move $(filter-out FORCE,$^) to the definition of size_append. This makes the callers cleaner because $(call ...) is unneeded for a macro with no argument. Signed-off-by: Masahiro Yamada --- scripts/Makefile.lib | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index ecad15b4cda3..ebaa348c9981 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -321,7 +321,7 @@ dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) # append the size as a 32-bit littleendian number as gzip does. size_append = printf $(shell \ dec_size=0; \ -for F in $1; do \ +for F in $(filter-out FORCE,$^); do \ fsize=$$($(CONFIG_SHELL) $(srctree)/scripts/file-size.sh $$F); \ dec_size=$$(expr $$dec_size + $$fsize); \ done; \ @@ -335,23 +335,20 @@ printf "%08x\n" $$dec_size | \ ) quiet_cmd_bzip2 = BZIP2 $@ -cmd_bzip2 = (cat $(filter-out FORCE,$^) | \ - bzip2 -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ + cmd_bzip2 = (cat $(filter-out FORCE,$^) | bzip2 -9 && $(size_append)) > $@ # Lzma # --------------------------------------------------------------------------- quiet_cmd_lzma = LZMA $@ -cmd_lzma = (cat $(filter-out FORCE,$^) | \ - lzma -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ + cmd_lzma = (cat $(filter-out FORCE,$^) | lzma -9 && $(size_append)) > $@ quiet_cmd_lzo = LZO $@ -cmd_lzo = (cat $(filter-out FORCE,$^) | \ - lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ + cmd_lzo = (cat $(filter-out FORCE,$^) | lzop -9 && $(size_append)) > $@ quiet_cmd_lz4 = LZ4 $@ -cmd_lz4 = (cat $(filter-out FORCE,$^) | \ - lz4c -l -c1 stdin stdout && $(call size_append, $(filter-out FORCE,$^))) > $@ + cmd_lz4 = (cat $(filter-out FORCE,$^) | lz4c -l -c1 stdin stdout && \ + $(size_append)) > $@ # U-Boot mkimage # --------------------------------------------------------------------------- @@ -394,8 +391,7 @@ quiet_cmd_uimage = UIMAGE $@ # decompression mode. A BCJ filter isn't used either. quiet_cmd_xzkern = XZKERN $@ cmd_xzkern = (cat $(filter-out FORCE,$^) | \ - sh $(srctree)/scripts/xz_wrap.sh && \ - $(call size_append, $(filter-out FORCE,$^))) > $@ + sh $(srctree)/scripts/xz_wrap.sh && $(size_append)) > $@ quiet_cmd_xzmisc = XZMISC $@ cmd_xzmisc = (cat $(filter-out FORCE,$^) | \ From 5d680056cb6de18f0221e321ee51d60ccd700781 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jan 2019 19:02:42 +0900 Subject: [PATCH 0721/4277] s390: make built-in.a not directly depend on *.o.chkbss files When I was refactoring cmd_ar_builtin in scripts/Makefile.build, I noticed the build breakage of s390. Some Makefiles of s390 add extra dependencies to built-in.a; built-in.a depends on timestamp files *.o.chkbss, but $(AR) does not want to include them into built-in.a. Insert a phony target 'chkbss' in between so that $(AR) can take $(filter-out $(PHONY), $^) as input. While I was here, I refactored Makefile.chkbss a little bit. Signed-off-by: Masahiro Yamada Acked-by: Heiko Carstens --- arch/s390/boot/Makefile | 4 ++-- arch/s390/boot/compressed/Makefile | 4 ++-- arch/s390/scripts/Makefile.chkbss | 25 +++++++++++-------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index d5ad724f5c96..b4d8a426ec74 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -67,6 +67,6 @@ install: $(CONFIGURE) $(obj)/bzImage sh -x $(srctree)/$(obj)/install.sh $(KERNELRELEASE) $(obj)/bzImage \ System.map "$(INSTALL_PATH)" -chkbss := $(OBJECTS) -chkbss-target := $(obj)/startup.a +chkbss := $(obj-y) +chkbss-target := startup.a include $(srctree)/arch/s390/scripts/Makefile.chkbss diff --git a/arch/s390/boot/compressed/Makefile b/arch/s390/boot/compressed/Makefile index b1bdd15e3429..fa529c5b4486 100644 --- a/arch/s390/boot/compressed/Makefile +++ b/arch/s390/boot/compressed/Makefile @@ -63,6 +63,6 @@ OBJCOPYFLAGS_piggy.o := -I binary -O elf64-s390 -B s390:64-bit --rename-section $(obj)/piggy.o: $(obj)/vmlinux.bin$(suffix-y) FORCE $(call if_changed,objcopy) -chkbss := $(filter-out $(obj)/piggy.o $(obj)/info.o,$(OBJECTS)) -chkbss-target := $(obj)/vmlinux.bin +chkbss := $(filter-out piggy.o info.o, $(obj-y)) +chkbss-target := vmlinux.bin include $(srctree)/arch/s390/scripts/Makefile.chkbss diff --git a/arch/s390/scripts/Makefile.chkbss b/arch/s390/scripts/Makefile.chkbss index 9bba2c14e0ca..cd7e8f4419f5 100644 --- a/arch/s390/scripts/Makefile.chkbss +++ b/arch/s390/scripts/Makefile.chkbss @@ -1,23 +1,20 @@ # SPDX-License-Identifier: GPL-2.0 +chkbss-target ?= built-in.a +$(obj)/$(chkbss-target): chkbss + +chkbss-files := $(addsuffix .chkbss, $(chkbss)) +clean-files += $(chkbss-files) + +PHONY += chkbss +chkbss: $(addprefix $(obj)/, $(chkbss-files)) + quiet_cmd_chkbss = CHKBSS $< -define cmd_chkbss - rm -f $@; \ + cmd_chkbss = \ if ! $(OBJDUMP) -j .bss -w -h $< | awk 'END { if ($$3) exit 1 }'; then \ echo "error: $< .bss section is not empty" >&2; exit 1; \ fi; \ touch $@; -endef -chkbss-target ?= $(obj)/built-in.a -ifneq (,$(findstring /,$(chkbss))) -chkbss-files := $(patsubst %, %.chkbss, $(chkbss)) -else -chkbss-files := $(patsubst %, $(obj)/%.chkbss, $(chkbss)) -endif - -$(chkbss-target): $(chkbss-files) -targets += $(notdir $(chkbss-files)) - -%.o.chkbss: %.o +$(obj)/%.o.chkbss: $(obj)/%.o $(call cmd,chkbss) From afa974b771281fd89e8fdcb71152152f17fb8303 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jan 2019 19:02:43 +0900 Subject: [PATCH 0722/4277] kbuild: add real-prereqs shorthand for $(filter-out FORCE,$^) In Kbuild, if_changed and friends must have FORCE as a prerequisite. Hence, $(filter-out FORCE,$^) or $(filter-out $(PHONY),$^) is a common idiom to get the names of all the prerequisites except phony targets. Add real-prereqs as a shorthand. Note: We cannot replace $(filter %.o,$^) in cmd_link_multi-m because $^ may include auto-generated dependencies from the .*.cmd file when a single object module is changed into a multi object module. Refer to commit 69ea912fda74 ("kbuild: remove unneeded link_multi_deps"). I added some comment to avoid accidental breakage. Signed-off-by: Masahiro Yamada Acked-by: Rob Herring --- Documentation/devicetree/bindings/Makefile | 2 +- arch/mips/boot/Makefile | 2 +- arch/powerpc/boot/Makefile | 2 +- arch/s390/boot/Makefile | 2 +- arch/x86/realmode/rm/Makefile | 3 +-- scripts/Kbuild.include | 4 ++++ scripts/Makefile.build | 9 ++++++--- scripts/Makefile.lib | 18 +++++++++--------- scripts/Makefile.modpost | 2 +- 9 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/Makefile b/Documentation/devicetree/bindings/Makefile index 6e5cef0ed6fb..e4eb5d1ea3b5 100644 --- a/Documentation/devicetree/bindings/Makefile +++ b/Documentation/devicetree/bindings/Makefile @@ -15,7 +15,7 @@ DT_TMP_SCHEMA := processed-schema.yaml extra-y += $(DT_TMP_SCHEMA) quiet_cmd_mk_schema = SCHEMA $@ - cmd_mk_schema = $(DT_MK_SCHEMA) $(DT_MK_SCHEMA_FLAGS) -o $@ $(filter-out FORCE, $^) + cmd_mk_schema = $(DT_MK_SCHEMA) $(DT_MK_SCHEMA_FLAGS) -o $@ $(real-prereqs) DT_DOCS = $(shell cd $(srctree)/$(src) && find * -name '*.yaml') DT_SCHEMA_FILES ?= $(addprefix $(src)/,$(DT_DOCS)) diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile index 35704c28a28b..3ce4dd578370 100644 --- a/arch/mips/boot/Makefile +++ b/arch/mips/boot/Makefile @@ -115,7 +115,7 @@ endif targets += vmlinux.its.S quiet_cmd_its_cat = CAT $@ - cmd_its_cat = cat $(filter-out $(PHONY), $^) >$@ + cmd_its_cat = cat $(real-prereqs) >$@ $(obj)/vmlinux.its.S: $(addprefix $(srctree)/arch/mips/$(PLATFORM)/,$(ITS_INPUTS)) FORCE $(call if_changed,its_cat) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 0e8dadd011bc..73d1f3562978 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -218,7 +218,7 @@ quiet_cmd_bootas = BOOTAS $@ cmd_bootas = $(BOOTCC) -Wp,-MD,$(depfile) $(BOOTAFLAGS) -c -o $@ $< quiet_cmd_bootar = BOOTAR $@ - cmd_bootar = $(BOOTAR) $(BOOTARFLAGS) $@.$$$$ $(filter-out FORCE,$^); mv $@.$$$$ $@ + cmd_bootar = $(BOOTAR) $(BOOTARFLAGS) $@.$$$$ $(real-prereqs); mv $@.$$$$ $@ $(obj-libfdt): $(obj)/%.o: $(srctree)/scripts/dtc/libfdt/%.c FORCE $(call if_changed_dep,bootcc) diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index b4d8a426ec74..11ca8795b74a 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -58,7 +58,7 @@ $(obj)/compressed/vmlinux: $(obj)/startup.a FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed $@ quiet_cmd_ar = AR $@ - cmd_ar = rm -f $@; $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(filter $(OBJECTS), $^) + cmd_ar = rm -f $@; $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(real-prereqs) $(obj)/startup.a: $(OBJECTS) FORCE $(call if_changed,ar) diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile index 4463fa72db94..394377c9f7f8 100644 --- a/arch/x86/realmode/rm/Makefile +++ b/arch/x86/realmode/rm/Makefile @@ -37,8 +37,7 @@ REALMODE_OBJS = $(addprefix $(obj)/,$(realmode-y)) sed-pasyms := -n -r -e 's/^([0-9a-fA-F]+) [ABCDGRSTVW] (.+)$$/pa_\2 = \2;/p' quiet_cmd_pasyms = PASYMS $@ - cmd_pasyms = $(NM) $(filter-out FORCE,$^) | \ - sed $(sed-pasyms) | sort | uniq > $@ + cmd_pasyms = $(NM) $(real-prereqs) | sed $(sed-pasyms) | sort | uniq > $@ targets += pasyms.h $(obj)/pasyms.h: $(REALMODE_OBJS) FORCE diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 30816037036e..d93250b33bf6 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -23,6 +23,10 @@ depfile = $(subst $(comma),_,$(dot-target).d) # filename of target with directory and extension stripped basetarget = $(basename $(notdir $@)) +### +# real prerequisites without phony targets +real-prereqs = $(filter-out $(PHONY), $^) + ### # Escape single quote for use in echo statements escsq = $(subst $(squote),'\$(squote)',$1) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 681ab58a1548..9800178fdcd2 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -399,8 +399,7 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ; ifdef builtin-target quiet_cmd_ar_builtin = AR $@ - cmd_ar_builtin = rm -f $@; \ - $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(filter $(real-obj-y), $^) + cmd_ar_builtin = rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(real-prereqs) $(builtin-target): $(real-obj-y) FORCE $(call if_changed,ar_builtin) @@ -428,7 +427,7 @@ ifdef lib-target quiet_cmd_link_l_target = AR $@ # lib target archives do get a symbol table and index -cmd_link_l_target = rm -f $@; $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(lib-y) +cmd_link_l_target = rm -f $@; $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(real-prereqs) $(lib-target): $(lib-y) FORCE $(call if_changed,link_l_target) @@ -453,6 +452,10 @@ targets += $(obj)/lib-ksyms.o endif +# NOTE: +# Do not replace $(filter %.o,^) with $(real-prereqs). When a single object +# module is turned into a multi object module, $^ will contain header file +# dependencies recorded in the .*.cmd file. quiet_cmd_link_multi-m = LD [M] $@ cmd_link_multi-m = $(LD) $(ld_flags) -r -o $@ $(filter %.o,$^) $(cmd_secanalysis) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index ebaa348c9981..c6fc295e0c48 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -231,7 +231,7 @@ $(obj)/%: $(src)/%_shipped # --------------------------------------------------------------------------- quiet_cmd_ld = LD $@ -cmd_ld = $(LD) $(ld_flags) $(filter-out FORCE,$^) -o $@ + cmd_ld = $(LD) $(ld_flags) $(real-prereqs) -o $@ # Objcopy # --------------------------------------------------------------------------- @@ -243,7 +243,7 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@ # --------------------------------------------------------------------------- quiet_cmd_gzip = GZIP $@ - cmd_gzip = cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@ + cmd_gzip = cat $(real-prereqs) | gzip -n -f -9 > $@ # DTC # --------------------------------------------------------------------------- @@ -321,7 +321,7 @@ dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) # append the size as a 32-bit littleendian number as gzip does. size_append = printf $(shell \ dec_size=0; \ -for F in $(filter-out FORCE,$^); do \ +for F in $(real-prereqs); do \ fsize=$$($(CONFIG_SHELL) $(srctree)/scripts/file-size.sh $$F); \ dec_size=$$(expr $$dec_size + $$fsize); \ done; \ @@ -335,19 +335,19 @@ printf "%08x\n" $$dec_size | \ ) quiet_cmd_bzip2 = BZIP2 $@ - cmd_bzip2 = (cat $(filter-out FORCE,$^) | bzip2 -9 && $(size_append)) > $@ + cmd_bzip2 = (cat $(real-prereqs) | bzip2 -9 && $(size_append)) > $@ # Lzma # --------------------------------------------------------------------------- quiet_cmd_lzma = LZMA $@ - cmd_lzma = (cat $(filter-out FORCE,$^) | lzma -9 && $(size_append)) > $@ + cmd_lzma = (cat $(real-prereqs) | lzma -9 && $(size_append)) > $@ quiet_cmd_lzo = LZO $@ - cmd_lzo = (cat $(filter-out FORCE,$^) | lzop -9 && $(size_append)) > $@ + cmd_lzo = (cat $(real-prereqs) | lzop -9 && $(size_append)) > $@ quiet_cmd_lz4 = LZ4 $@ - cmd_lz4 = (cat $(filter-out FORCE,$^) | lz4c -l -c1 stdin stdout && \ + cmd_lz4 = (cat $(real-prereqs) | lz4c -l -c1 stdin stdout && \ $(size_append)) > $@ # U-Boot mkimage @@ -390,11 +390,11 @@ quiet_cmd_uimage = UIMAGE $@ # big dictionary would increase the memory usage too much in the multi-call # decompression mode. A BCJ filter isn't used either. quiet_cmd_xzkern = XZKERN $@ -cmd_xzkern = (cat $(filter-out FORCE,$^) | \ + cmd_xzkern = (cat $(real-prereqs) | \ sh $(srctree)/scripts/xz_wrap.sh && $(size_append)) > $@ quiet_cmd_xzmisc = XZMISC $@ -cmd_xzmisc = (cat $(filter-out FORCE,$^) | \ + cmd_xzmisc = (cat $(real-prereqs) | \ xz --check=crc32 --lzma2=dict=1MiB) > $@ # ASM offsets diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 7d4af0d0accb..c0b7f526f95e 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -122,7 +122,7 @@ quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = \ $(LD) -r $(KBUILD_LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ - -o $@ $(filter-out FORCE,$^) ; \ + -o $@ $(real-prereqs) ; \ $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) $(modules): %.ko :%.o %.mod.o FORCE From b79c6aa6a1f111eab2fc08b92541c9e88824be72 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jan 2019 19:02:44 +0900 Subject: [PATCH 0723/4277] kbuild: remove unnecessary in-subshell execution The commands surrounded by ( ) are executed in a subshell, but in most cases, we do not need to spawn an extra subshell. Signed-off-by: Masahiro Yamada --- scripts/Makefile.build | 4 ++-- scripts/Makefile.lib | 21 ++++++++++----------- scripts/mkcompile_h | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 9800178fdcd2..f8e2794107ed 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -312,11 +312,11 @@ $(real-obj-m:.o=.s): modkern_aflags := $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE) # # These mirror gensymtypes_c and co above, keep them in synch. cmd_gensymtypes_S = \ - (echo "\#include " ; \ + { echo "\#include " ; \ echo "\#include " ; \ $(CPP) $(a_flags) $< | \ grep "\<___EXPORT_SYMBOL\>" | \ - sed 's/.*___EXPORT_SYMBOL[[:space:]]*\([a-zA-Z0-9_]*\)[[:space:]]*,.*/EXPORT_SYMBOL(\1);/' ) | \ + sed 's/.*___EXPORT_SYMBOL[[:space:]]*\([a-zA-Z0-9_]*\)[[:space:]]*,.*/EXPORT_SYMBOL(\1);/' ; } | \ $(CPP) -D__GENKSYMS__ $(c_flags) -xc - | \ $(GENKSYMS) $(if $(1), -T $(2)) \ $(patsubst y,-R,$(CONFIG_MODULE_REL_CRCS)) \ diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index c6fc295e0c48..c0abd9a779c3 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -271,7 +271,7 @@ DTC_FLAGS += $(DTC_FLAGS_$(basetarget)) # Generate an assembly file to wrap the output of the device tree compiler quiet_cmd_dt_S_dtb= DTB $@ cmd_dt_S_dtb= \ -( \ +{ \ echo '\#include '; \ echo '.section .dtb.init.rodata,"a"'; \ echo '.balign STRUCT_ALIGNMENT'; \ @@ -281,7 +281,7 @@ cmd_dt_S_dtb= \ echo '__dtb_$(subst -,_,$(*F))_end:'; \ echo '.global __dtb_$(subst -,_,$(*F))_end'; \ echo '.balign STRUCT_ALIGNMENT'; \ -) > $@ +} > $@ $(obj)/%.dtb.S: $(obj)/%.dtb FORCE $(call if_changed,dt_S_dtb) @@ -335,20 +335,20 @@ printf "%08x\n" $$dec_size | \ ) quiet_cmd_bzip2 = BZIP2 $@ - cmd_bzip2 = (cat $(real-prereqs) | bzip2 -9 && $(size_append)) > $@ + cmd_bzip2 = { cat $(real-prereqs) | bzip2 -9 && $(size_append); } > $@ # Lzma # --------------------------------------------------------------------------- quiet_cmd_lzma = LZMA $@ - cmd_lzma = (cat $(real-prereqs) | lzma -9 && $(size_append)) > $@ + cmd_lzma = { cat $(real-prereqs) | lzma -9 && $(size_append); } > $@ quiet_cmd_lzo = LZO $@ - cmd_lzo = (cat $(real-prereqs) | lzop -9 && $(size_append)) > $@ + cmd_lzo = { cat $(real-prereqs) | lzop -9 && $(size_append); } > $@ quiet_cmd_lz4 = LZ4 $@ - cmd_lz4 = (cat $(real-prereqs) | lz4c -l -c1 stdin stdout && \ - $(size_append)) > $@ + cmd_lz4 = { cat $(real-prereqs) | lz4c -l -c1 stdin stdout && \ + $(size_append); } > $@ # U-Boot mkimage # --------------------------------------------------------------------------- @@ -390,12 +390,11 @@ quiet_cmd_uimage = UIMAGE $@ # big dictionary would increase the memory usage too much in the multi-call # decompression mode. A BCJ filter isn't used either. quiet_cmd_xzkern = XZKERN $@ - cmd_xzkern = (cat $(real-prereqs) | \ - sh $(srctree)/scripts/xz_wrap.sh && $(size_append)) > $@ + cmd_xzkern = { cat $(real-prereqs) | sh $(srctree)/scripts/xz_wrap.sh && \ + $(size_append); } > $@ quiet_cmd_xzmisc = XZMISC $@ - cmd_xzmisc = (cat $(real-prereqs) | \ - xz --check=crc32 --lzma2=dict=1MiB) > $@ + cmd_xzmisc = cat $(real-prereqs) | xz --check=crc32 --lzma2=dict=1MiB > $@ # ASM offsets # --------------------------------------------------------------------------- diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h index 87f1fc9801d7..2339f86126cb 100755 --- a/scripts/mkcompile_h +++ b/scripts/mkcompile_h @@ -62,7 +62,7 @@ UTS_TRUNCATE="cut -b -$UTS_LEN" # Generate a temporary compile.h -( echo /\* This file is auto generated, version $VERSION \*/ +{ echo /\* This file is auto generated, version $VERSION \*/ if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi echo \#define UTS_MACHINE \"$ARCH\" @@ -73,7 +73,7 @@ UTS_TRUNCATE="cut -b -$UTS_LEN" echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\" echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | grep ' version ' | sed 's/[[:space:]]*$//'`\" -) > .tmpcompile +} > .tmpcompile # Only replace the real compile.h if the new one is different, # in order to preserve the timestamp and avoid unnecessary From 4f1c1008e786cefd9da0ac9ec84192c666719b8c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 18 Jan 2019 12:58:30 +0900 Subject: [PATCH 0724/4277] kbuild: remove meaningless prepare2 target There is no build order among the following: prepare3 outputmakefile asm-generic $(version_h) $(autoksyms_h) include/generated/utsrelease.h It is meaningless to insert the prepare2 target between the first three and the last three. The comment says, "prepare2 creates a makefile if using a separate output directory." Let me explain it more precisely. The prepare targets cannot be executed without the .config file. Because the configuration targets depend on the outputmakefile target, the generated makefile is already there before the parepare2 is run. Signed-off-by: Masahiro Yamada --- Makefile | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 2b05cd59ff54..97ee0be24d52 100644 --- a/Makefile +++ b/Makefile @@ -1062,7 +1062,7 @@ scripts: scripts_basic scripts_dtc # archprepare is used in arch Makefiles and when processed asm symlink, # version.h and scripts_basic is processed / created. -PHONY += prepare archprepare prepare1 prepare2 prepare3 +PHONY += prepare archprepare prepare1 prepare3 # prepare3 is used to check if we are building in a separate output directory, # and if so do: @@ -1077,12 +1077,8 @@ ifneq ($(KBUILD_SRC),) fi; endif -# prepare2 creates a makefile if using a separate output directory. -# From this point forward, .config has been reprocessed, so any rules -# that need to depend on updated CONFIG_* values can be checked here. -prepare2: prepare3 outputmakefile asm-generic - -prepare1: prepare2 $(version_h) $(autoksyms_h) include/generated/utsrelease.h +prepare1: prepare3 outputmakefile asm-generic $(version_h) $(autoksyms_h) \ + include/generated/utsrelease.h $(cmd_crmodverdir) archprepare: archheaders archscripts prepare1 scripts From 127428356eded298a2ed2af26010236c0a8e321c Mon Sep 17 00:00:00 2001 From: Ramon Fried Date: Mon, 21 Jan 2019 16:38:22 +0200 Subject: [PATCH 0725/4277] pinctrl.txt: Remove outdated information Returning -EAGAIN is no longer supported by pin_config_group_set() since ad42fc6c8479 ("pinctrl: rip out the direct pinconf API") Remove the relevant section from the documentation. Signed-off-by: Ramon Fried Signed-off-by: Linus Walleij --- Documentation/driver-api/pinctl.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Documentation/driver-api/pinctl.rst b/Documentation/driver-api/pinctl.rst index 6cb68d67fa75..2bb1bc484278 100644 --- a/Documentation/driver-api/pinctl.rst +++ b/Documentation/driver-api/pinctl.rst @@ -274,15 +274,6 @@ configuration in the pin controller ops like this:: .confops = &foo_pconf_ops, }; -Since some controllers have special logic for handling entire groups of pins -they can exploit the special whole-group pin control function. The -pin_config_group_set() callback is allowed to return the error code -EAGAIN, -for groups it does not want to handle, or if it just wants to do some -group-level handling and then fall through to iterate over all pins, in which -case each individual pin will be treated by separate pin_config_set() calls as -well. - - Interaction with the GPIO subsystem =================================== From c1c9d41319c35daa099b2e6cd1325e3ae55cfda8 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Tue, 22 Jan 2019 10:17:13 +0000 Subject: [PATCH 0726/4277] dt-bindings: imx: Add pinctrl binding doc for imx8mm Add binding doc imx8mm pinctrl driver. Signed-off-by: Bai Ping Acked-by: Aisheng Dong Signed-off-by: Linus Walleij --- .../bindings/pinctrl/fsl,imx8mm-pinctrl.txt | 36 + .../arm64/boot/dts/freescale/imx8mm-pinfunc.h | 629 ++++++++++++++++++ 2 files changed, 665 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/fsl,imx8mm-pinctrl.txt create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx8mm-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx8mm-pinctrl.txt new file mode 100644 index 000000000000..524a16fca666 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx8mm-pinctrl.txt @@ -0,0 +1,36 @@ +* Freescale IMX8MM IOMUX Controller + +Please refer to fsl,imx-pinctrl.txt and pinctrl-bindings.txt in this directory +for common binding part and usage. + +Required properties: +- compatible: "fsl,imx8mm-iomuxc" +- reg: should contain the base physical address and size of the iomuxc + registers. + +Required properties in sub-nodes: +- fsl,pins: each entry consists of 6 integers and represents the mux and config + setting for one pin. The first 5 integers are specified using a PIN_FUNC_ID macro, which can be found in + . The last integer CONFIG is + the pad setting value like pull-up on this pin. Please refer to i.MX8M Mini + Reference Manual for detailed CONFIG settings. + +Examples: + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; +}; + +iomuxc: pinctrl@30330000 { + compatible = "fsl,imx8mm-iomuxc"; + reg = <0x0 0x30330000 0x0 0x10000>; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX 0x140 + MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x140 + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h new file mode 100644 index 000000000000..e25f7fcd7997 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h @@ -0,0 +1,629 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2017-2018 NXP + */ + +#ifndef __DTS_IMX8MM_PINFUNC_H +#define __DTS_IMX8MM_PINFUNC_H + +/* + * The pin function ID is a tuple of + * + */ + +#define MX8MM_IOMUXC_GPIO1_IO00_GPIO1_IO0 0x028 0x290 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO00_CCMSRCGPCMIX_ENET_PHY_REF_CLK_ROOT 0x028 0x290 0x4C0 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO00_ANAMIX_REF_CLK_32K 0x028 0x290 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO00_CCMSRCGPCMIX_EXT_CLK1 0x028 0x290 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO00_SJC_FAIL 0x028 0x290 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO01_GPIO1_IO1 0x02C 0x294 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO01_PWM1_OUT 0x02C 0x294 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO01_ANAMIX_REF_CLK_24M 0x02C 0x294 0x4BC 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO01_CCMSRCGPCMIX_EXT_CLK2 0x02C 0x294 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO01_SJC_ACTIVE 0x02C 0x294 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO02_GPIO1_IO2 0x030 0x298 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0x030 0x298 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_ANY 0x030 0x298 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO02_SJC_DE_B 0x030 0x298 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x034 0x29C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO03_USDHC1_VSELECT 0x034 0x29C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO03_SDMA1_EXT_EVENT0 0x034 0x29C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO03_ANAMIX_XTAL_OK 0x034 0x29C 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO03_SJC_DONE 0x034 0x29C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO04_GPIO1_IO4 0x038 0x2A0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x038 0x2A0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO04_SDMA1_EXT_EVENT1 0x038 0x2A0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO04_ANAMIX_XTAL_OK_LV 0x038 0x2A0 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO04_USDHC1_TEST_TRIG 0x038 0x2A0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x03C 0x2A4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO05_M4_NMI 0x03C 0x2A4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO05_CCMSRCGPCMIX_PMIC_READY 0x03C 0x2A4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO05_CCMSRCGPCMIX_INT_BOOT 0x03C 0x2A4 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO05_USDHC2_TEST_TRIG 0x03C 0x2A4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO06_GPIO1_IO6 0x040 0x2A8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO06_ENET1_MDC 0x040 0x2A8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO06_USDHC1_CD_B 0x040 0x2A8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO06_CCMSRCGPCMIX_EXT_CLK3 0x040 0x2A8 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO06_ECSPI1_TEST_TRIG 0x040 0x2A8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO07_GPIO1_IO7 0x044 0x2AC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO07_ENET1_MDIO 0x044 0x2AC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO07_USDHC1_WP 0x044 0x2AC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO07_CCMSRCGPCMIX_EXT_CLK4 0x044 0x2AC 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO07_ECSPI2_TEST_TRIG 0x044 0x2AC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO08_GPIO1_IO8 0x048 0x2B0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO08_ENET1_1588_EVENT0_IN 0x048 0x2B0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO08_USDHC2_RESET_B 0x048 0x2B0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO08_CCMSRCGPCMIX_WAIT 0x048 0x2B0 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO08_QSPI_TEST_TRIG 0x048 0x2B0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9 0x04C 0x2B4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO09_ENET1_1588_EVENT0_OUT 0x04C 0x2B4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO09_SDMA2_EXT_EVENT0 0x04C 0x2B4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO09_CCMSRCGPCMIX_STOP 0x04C 0x2B4 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO09_RAWNAND_TEST_TRIG 0x04C 0x2B4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO10_GPIO1_IO10 0x050 0x2B8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO10_USB1_OTG_ID 0x050 0x2B8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO10_OCOTP_CTRL_WRAPPER_FUSE_LATCHED 0x050 0x2B8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11 0x054 0x2BC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO11_USB2_OTG_ID 0x054 0x2BC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO11_CCMSRCGPCMIX_PMIC_READY 0x054 0x2BC 0x4BC 0x5 0x1 +#define MX8MM_IOMUXC_GPIO1_IO11_CCMSRCGPCMIX_OUT0 0x054 0x2BC 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO11_CAAM_WRAPPER_RNG_OSC_OBS 0x054 0x2BC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO12_GPIO1_IO12 0x058 0x2C0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO12_USB1_OTG_PWR 0x058 0x2C0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO12_SDMA2_EXT_EVENT1 0x058 0x2C0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO12_CCMSRCGPCMIX_OUT1 0x058 0x2C0 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO12_CSU_CSU_ALARM_AUT0 0x058 0x2C0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO13_GPIO1_IO13 0x05C 0x2C4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO13_USB1_OTG_OC 0x05C 0x2C4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO13_PWM2_OUT 0x05C 0x2C4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO13_CCMSRCGPCMIX_OUT2 0x05C 0x2C4 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO13_CSU_CSU_ALARM_AUT1 0x05C 0x2C4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO14_GPIO1_IO14 0x060 0x2C8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO14_USB2_OTG_PWR 0x060 0x2C8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO14_PWM3_OUT 0x060 0x2C8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO14_CCMSRCGPCMIX_CLKO1 0x060 0x2C8 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO14_CSU_CSU_ALARM_AUT2 0x060 0x2C8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_GPIO1_IO15_GPIO1_IO15 0x064 0x2CC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_GPIO1_IO15_USB2_OTG_OC 0x064 0x2CC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_GPIO1_IO15_PWM4_OUT 0x064 0x2CC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_GPIO1_IO15_CCMSRCGPCMIX_CLKO2 0x064 0x2CC 0x000 0x6 0x0 +#define MX8MM_IOMUXC_GPIO1_IO15_CSU_CSU_INT_DEB 0x064 0x2CC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ENET_MDC_ENET1_MDC 0x068 0x2D0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_MDC_GPIO1_IO16 0x068 0x2D0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_MDIO_ENET1_MDIO 0x06C 0x2D4 0x4C0 0x0 0x1 +#define MX8MM_IOMUXC_ENET_MDIO_GPIO1_IO17 0x06C 0x2D4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_TD3_ENET1_RGMII_TD3 0x070 0x2D8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_TD3_GPIO1_IO18 0x070 0x2D8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_TD2_ENET1_RGMII_TD2 0x074 0x2DC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_TD2_ENET1_TX_CLK 0x074 0x2DC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ENET_TD2_GPIO1_IO19 0x074 0x2DC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_TD1_ENET1_RGMII_TD1 0x078 0x2E0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_TD1_GPIO1_IO20 0x078 0x2E0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_TD0_ENET1_RGMII_TD0 0x07C 0x2E4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_TD0_GPIO1_IO21 0x07C 0x2E4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL 0x080 0x2E8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_TX_CTL_GPIO1_IO22 0x080 0x2E8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_TXC_ENET1_RGMII_TXC 0x084 0x2EC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_TXC_ENET1_TX_ER 0x084 0x2EC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ENET_TXC_GPIO1_IO23 0x084 0x2EC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL 0x088 0x2F0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_RX_CTL_GPIO1_IO24 0x088 0x2F0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_RXC_ENET1_RGMII_RXC 0x08C 0x2F4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_RXC_ENET1_RX_ER 0x08C 0x2F4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ENET_RXC_GPIO1_IO25 0x08C 0x2F4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_RD0_ENET1_RGMII_RD0 0x090 0x2F8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_RD0_GPIO1_IO26 0x090 0x2F8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_RD1_ENET1_RGMII_RD1 0x094 0x2FC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_RD1_GPIO1_IO27 0x094 0x2FC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_RD2_ENET1_RGMII_RD2 0x098 0x300 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_RD2_GPIO1_IO28 0x098 0x300 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ENET_RD3_ENET1_RGMII_RD3 0x09C 0x304 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ENET_RD3_GPIO1_IO29 0x09C 0x304 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x0A0 0x308 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_CLK_GPIO2_IO0 0x0A0 0x308 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x0A4 0x30C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_CMD_GPIO2_IO1 0x0A4 0x30C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x0A8 0x310 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA0_GPIO2_IO2 0x0A8 0x31 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x0AC 0x314 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA1_GPIO2_IO3 0x0AC 0x314 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x0B0 0x318 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA2_GPIO2_IO4 0x0B0 0x318 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x0B4 0x31C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA3_GPIO2_IO5 0x0B4 0x31C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA4_USDHC1_DATA4 0x0B8 0x320 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA4_GPIO2_IO6 0x0B8 0x320 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA5_USDHC1_DATA5 0x0BC 0x324 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA5_GPIO2_IO7 0x0BC 0x324 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA6_USDHC1_DATA6 0x0C0 0x328 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA6_GPIO2_IO8 0x0C0 0x328 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_DATA7_USDHC1_DATA7 0x0C4 0x32C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_DATA7_GPIO2_IO9 0x0C4 0x32C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_RESET_B_USDHC1_RESET_B 0x0C8 0x330 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_RESET_B_GPIO2_IO10 0x0C8 0x330 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD1_STROBE_USDHC1_STROBE 0x0CC 0x334 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD1_STROBE_GPIO2_IO11 0x0CC 0x334 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0D0 0x338 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x0D0 0x338 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x0D4 0x33C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_CLK_GPIO2_IO13 0x0D4 0x33C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_CLK_CCMSRCGPCMIX_OBSERVE0 0x0D4 0x33C 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_CLK_OBSERVE_MUX_OUT0 0x0D4 0x33C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x0D8 0x340 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_CMD_GPIO2_IO14 0x0D8 0x340 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_CMD_CCMSRCGPCMIX_OBSERVE1 0x0D8 0x340 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_CMD_OBSERVE_MUX_OUT1 0x0D8 0x340 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x0DC 0x344 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_DATA0_GPIO2_IO15 0x0DC 0x344 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_DATA0_CCMSRCGPCMIX_OBSERVE2 0x0DC 0x344 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_DATA0_OBSERVE_MUX_OUT2 0x0DC 0x344 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x0E0 0x348 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_DATA1_GPIO2_IO16 0x0E0 0x348 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_DATA1_CCMSRCGPCMIX_WAIT 0x0E0 0x348 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_DATA1_OBSERVE_MUX_OUT3 0x0E0 0x348 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x0E4 0x34C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_DATA2_GPIO2_IO17 0x0E4 0x34C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_DATA2_CCMSRCGPCMIX_STOP 0x0E4 0x34C 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_DATA2_OBSERVE_MUX_OUT4 0x0E4 0x34C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x0E8 0x350 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_DATA3_GPIO2_IO18 0x0E8 0x350 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_DATA3_CCMSRCGPCMIX_EARLY_RESET 0x0E8 0x350 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_RESET_B_USDHC2_RESET_B 0x0EC 0x354 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_RESET_B_GPIO2_IO19 0x0EC 0x354 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_RESET_B_CCMSRCGPCMIX_SYSTEM_RESET 0x0EC 0x354 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x0F0 0x358 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SD2_WP_GPIO2_IO20 0x0F0 0x358 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SD2_WP_SIM_M_HMASTLOCK 0x0F0 0x358 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_ALE_RAWNAND_ALE 0x0F4 0x35C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_ALE_QSPI_A_SCLK 0x0F4 0x35C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_ALE_GPIO3_IO0 0x0F4 0x35C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_ALE_SIM_M_HPROT0 0x0F4 0x35C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_CE0_B_RAWNAND_CE0_B 0x0F8 0x360 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_CE0_B_QSPI_A_SS0_B 0x0F8 0x360 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_CE0_B_GPIO3_IO1 0x0F8 0x360 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_CE0_B_SIM_M_HPROT1 0x0F8 0x360 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_CE1_B_RAWNAND_CE1_B 0x0FC 0x364 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_CE1_B_QSPI_A_SS1_B 0x0FC 0x364 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x0FC 0x364 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_CE1_B_GPIO3_IO2 0x0FC 0x364 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_CE1_B_SIM_M_HPROT2 0x0FC 0x364 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_CE2_B_RAWNAND_CE2_B 0x100 0x368 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_CE2_B_QSPI_B_SS0_B 0x100 0x368 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x100 0x368 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_CE2_B_GPIO3_IO3 0x100 0x368 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_CE2_B_SIM_M_HPROT3 0x100 0x368 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_CE3_B_RAWNAND_CE3_B 0x104 0x36C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_CE3_B_QSPI_B_SS1_B 0x104 0x36C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x104 0x36C 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_CE3_B_GPIO3_IO4 0x104 0x36C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_CE3_B_SIM_M_HADDR0 0x104 0x36C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_CLE_RAWNAND_CLE 0x108 0x370 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_CLE_QSPI_B_SCLK 0x108 0x370 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x108 0x370 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_CLE_GPIO3_IO5 0x108 0x370 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_CLE_SIM_M_HADDR1 0x108 0x370 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA00_RAWNAND_DATA00 0x10C 0x374 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA00_QSPI_A_DATA0 0x10C 0x374 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA00_GPIO3_IO6 0x10C 0x374 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA00_SIM_M_HADDR2 0x10C 0x374 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA01_RAWNAND_DATA01 0x110 0x378 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA01_QSPI_A_DATA1 0x110 0x378 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA01_GPIO3_IO7 0x110 0x378 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA01_SIM_M_HADDR3 0x110 0x378 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA02_RAWNAND_DATA02 0x114 0x37C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA02_QSPI_A_DATA2 0x114 0x37C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA02_GPIO3_IO8 0x114 0x37C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA02_SIM_M_HADDR4 0x114 0x37C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA03_RAWNAND_DATA03 0x118 0x380 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA03_QSPI_A_DATA3 0x118 0x380 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA03_GPIO3_IO9 0x118 0x380 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA03_SIM_M_HADDR5 0x118 0x380 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA04_RAWNAND_DATA04 0x11C 0x384 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA04_QSPI_B_DATA0 0x11C 0x384 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x11C 0x384 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_DATA04_GPIO3_IO10 0x11C 0x384 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA04_SIM_M_HADDR6 0x11C 0x384 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA05_RAWNAND_DATA05 0x120 0x388 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA05_QSPI_B_DATA1 0x120 0x388 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x120 0x388 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_DATA05_GPIO3_IO11 0x120 0x388 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA05_SIM_M_HADDR7 0x120 0x388 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA06_RAWNAND_DATA06 0x124 0x38C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA06_QSPI_B_DATA2 0x124 0x38C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x124 0x38C 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_DATA06_GPIO3_IO12 0x124 0x38C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA06_SIM_M_HADDR8 0x124 0x38C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DATA07_RAWNAND_DATA07 0x128 0x390 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DATA07_QSPI_B_DATA3 0x128 0x390 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x128 0x390 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_DATA07_GPIO3_IO13 0x128 0x390 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DATA07_SIM_M_HADDR9 0x128 0x390 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_DQS_RAWNAND_DQS 0x12C 0x394 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_DQS_QSPI_A_DQS 0x12C 0x394 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_DQS_GPIO3_IO14 0x12C 0x394 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_DQS_SIM_M_HADDR10 0x12C 0x394 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_RE_B_RAWNAND_RE_B 0x130 0x398 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_RE_B_QSPI_B_DQS 0x130 0x398 0x000 0x1 0x0 +#define MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x130 0x398 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_RE_B_GPIO3_IO15 0x130 0x398 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_RE_B_SIM_M_HADDR11 0x130 0x398 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_READY_B_RAWNAND_READY_B 0x134 0x39C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_READY_B_GPIO3_IO16 0x134 0x39C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_READY_B_SIM_M_HADDR12 0x134 0x39C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_WE_B_RAWNAND_WE_B 0x138 0x3A0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x138 0x3A0 0x000 0x12 0x0 +#define MX8MM_IOMUXC_NAND_WE_B_GPIO3_IO17 0x138 0x3A0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_WE_B_SIM_M_HADDR13 0x138 0x3A0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_NAND_WP_B_RAWNAND_WP_B 0x13C 0x3A4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x13C 0x3A4 0x000 0x2 0x0 +#define MX8MM_IOMUXC_NAND_WP_B_GPIO3_IO18 0x13C 0x3A4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_NAND_WP_B_SIM_M_HADDR14 0x13C 0x3A4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI5_RXFS_SAI5_RX_SYNC 0x140 0x3A8 0x4E4 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_RXFS_SAI1_TX_DATA0 0x140 0x3A8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_RXFS_GPIO3_IO19 0x140 0x3A8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_RXC_SAI5_RX_BCLK 0x144 0x3AC 0x4D0 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_RXC_SAI1_TX_DATA1 0x144 0x3AC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_RXC_PDM_CLK 0x144 0x3AC 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI5_RXC_GPIO3_IO20 0x144 0x3AC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_RXD0_SAI5_RX_DATA0 0x148 0x3B0 0x4D4 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_RXD0_SAI1_TX_DATA2 0x148 0x3B0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_RXD0_PDM_DATA0 0x148 0x3B0 0x534 0x4 0x0 +#define MX8MM_IOMUXC_SAI5_RXD0_GPIO3_IO21 0x148 0x3B0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_RXD1_SAI5_RX_DATA1 0x14C 0x3B4 0x4D8 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_RXD1_SAI1_TX_DATA3 0x14C 0x3B4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_RXD1_SAI1_TX_SYNC 0x14C 0x3B4 0x4CC 0x2 0x0 +#define MX8MM_IOMUXC_SAI5_RXD1_SAI5_TX_SYNC 0x14C 0x3B4 0x4EC 0x3 0x0 +#define MX8MM_IOMUXC_SAI5_RXD1_PDM_DATA1 0x14C 0x3B4 0x538 0x4 0x0 +#define MX8MM_IOMUXC_SAI5_RXD1_GPIO3_IO22 0x14C 0x3B4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_RXD2_SAI5_RX_DATA2 0x150 0x3B8 0x4DC 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_RXD2_SAI1_TX_DATA4 0x150 0x3B8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_RXD2_SAI1_TX_SYNC 0x150 0x3B8 0x4CC 0x2 0x1 +#define MX8MM_IOMUXC_SAI5_RXD2_SAI5_TX_BCLK 0x150 0x3B8 0x4E8 0x3 0x0 +#define MX8MM_IOMUXC_SAI5_RXD2_PDM_DATA2 0x150 0x3B8 0x53c 0x4 0x0 +#define MX8MM_IOMUXC_SAI5_RXD2_GPIO3_IO23 0x150 0x3B8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_RXD3_SAI5_RX_DATA3 0x154 0x3BC 0x4E0 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_RXD3_SAI1_TX_DATA5 0x154 0x3BC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_RXD3_SAI1_TX_SYNC 0x154 0x3BC 0x4CC 0x2 0x2 +#define MX8MM_IOMUXC_SAI5_RXD3_SAI5_TX_DATA0 0x154 0x3BC 0x000 0x3 0x0 +#define MX8MM_IOMUXC_SAI5_RXD3_PDM_DATA3 0x154 0x3BC 0x540 0x4 0x0 +#define MX8MM_IOMUXC_SAI5_RXD3_GPIO3_IO24 0x154 0x3BC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_MCLK_SAI5_MCLK 0x158 0x3C0 0x52C 0x0 0x0 +#define MX8MM_IOMUXC_SAI5_MCLK_SAI1_TX_BCLK 0x158 0x3C0 0x4C8 0x1 0x0 +#define MX8MM_IOMUXC_SAI5_MCLK_SAI4_MCLK 0x158 0x3C0 0x000 0x2 0x0 +#define MX8MM_IOMUXC_SAI5_MCLK_GPIO3_IO25 0x158 0x3C0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI5_MCLK_CCMSRCGPCMIX_TESTER_ACK 0x158 0x3C0 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXFS_SAI1_RX_SYNC 0x15C 0x3C4 0x4C4 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXFS_SAI5_RX_SYNC 0x15C 0x3C4 0x4E4 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_RXFS_CORESIGHT_TRACE_CLK 0x15C 0x3C4 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXFS_GPIO4_IO0 0x15C 0x3C4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXFS_SIM_M_HADDR15 0x15C 0x3C4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXC_SAI1_RX_BCLK 0x160 0x3C8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXC_SAI5_RX_BCLK 0x160 0x3C8 0x4D0 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_RXC_CORESIGHT_TRACE_CTL 0x160 0x3C8 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXC_GPIO4_IO1 0x160 0x3C8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXC_SIM_M_HADDR16 0x160 0x3C8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD0_SAI1_RX_DATA0 0x164 0x3CC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD0_SAI5_RX_DATA0 0x164 0x3CC 0x4D4 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_RXD0_PDM_DATA0 0x164 0x3CC 0x534 0x3 0x1 +#define MX8MM_IOMUXC_SAI1_RXD0_CORESIGHT_TRACE0 0x164 0x3CC 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD0_GPIO4_IO2 0x164 0x3CC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD0_CCMSRCGPCMIX_BOOT_CFG0 0x164 0x3CC 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD0_SIM_M_HADDR17 0x164 0x3CC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD1_SAI1_RX_DATA1 0x168 0x3D0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD1_SAI5_RX_DATA1 0x168 0x3D0 0x4D8 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_RXD1_PDM_DATA1 0x168 0x3D0 0x538 0x3 0x1 +#define MX8MM_IOMUXC_SAI1_RXD1_CORESIGHT_TRACE1 0x168 0x3D0 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD1_GPIO4_IO3 0x168 0x3D0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD1_CCMSRCGPCMIX_BOOT_CFG1 0x168 0x3D0 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD1_SIM_M_HADDR18 0x168 0x3D0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD2_SAI1_RX_DATA2 0x16C 0x3D4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD2_SAI5_RX_DATA2 0x16C 0x3D4 0x4DC 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_RXD2_PDM_DATA2 0x16C 0x3D4 0x53C 0x3 0x1 +#define MX8MM_IOMUXC_SAI1_RXD2_CORESIGHT_TRACE2 0x16C 0x3D4 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD2_GPIO4_IO4 0x16C 0x3D4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD2_CCMSRCGPCMIX_BOOT_CFG2 0x16C 0x3D4 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD2_SIM_M_HADDR19 0x16C 0x3D4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD3_SAI1_RX_DATA3 0x170 0x3D8 0x4E0 0x0 0x1 +#define MX8MM_IOMUXC_SAI1_RXD3_SAI5_RX_DATA3 0x170 0x3D8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_RXD3_PDM_DATA3 0x170 0x3D8 0x540 0x3 0x1 +#define MX8MM_IOMUXC_SAI1_RXD3_CORESIGHT_TRACE3 0x170 0x3D8 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD3_GPIO4_IO5 0x170 0x3D8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD3_CCMSRCGPCMIX_BOOT_CFG3 0x170 0x3D8 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD3_SIM_M_HADDR20 0x170 0x3D8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_SAI1_RX_DATA4 0x174 0x3DC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_SAI6_TX_BCLK 0x174 0x3DC 0x51C 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_SAI6_RX_BCLK 0x174 0x3DC 0x510 0x2 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_CORESIGHT_TRACE4 0x174 0x3DC 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_GPIO4_IO6 0x174 0x3DC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_CCMSRCGPCMIX_BOOT_CFG4 0x174 0x3DC 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD4_SIM_M_HADDR21 0x174 0x3DC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_SAI1_RX_DATA5 0x178 0x3E0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_SAI6_TX_DATA0 0x178 0x3E0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_SAI6_RX_DATA0 0x178 0x3E0 0x514 0x2 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_SAI1_RX_SYNC 0x178 0x3E0 0x4C4 0x3 0x1 +#define MX8MM_IOMUXC_SAI1_RXD5_CORESIGHT_TRACE5 0x178 0x3E0 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_GPIO4_IO7 0x178 0x3E0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_CCMSRCGPCMIX_BOOT_CFG5 0x178 0x3E0 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD5_SIM_M_HADDR22 0x178 0x3E0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_SAI1_RX_DATA6 0x17C 0x3E4 0x520 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_SAI6_TX_SYNC 0x17C 0x3E4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_SAI6_RX_SYNC 0x17C 0x3E4 0x518 0x2 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_CORESIGHT_TRACE6 0x17C 0x3E4 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_GPIO4_IO8 0x17C 0x3E4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_CCMSRCGPCMIX_BOOT_CFG6 0x17C 0x3E4 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD6_SIM_M_HADDR23 0x17C 0x3E4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_SAI1_RX_DATA7 0x180 0x3E8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_SAI6_MCLK 0x180 0x3E8 0x530 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_SAI1_TX_SYNC 0x180 0x3E8 0x4CC 0x2 0x4 +#define MX8MM_IOMUXC_SAI1_RXD7_SAI1_TX_DATA4 0x180 0x3E8 0x000 0x3 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_CORESIGHT_TRACE7 0x180 0x3E8 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_GPIO4_IO9 0x180 0x3E8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_CCMSRCGPCMIX_BOOT_CFG7 0x180 0x3E8 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_RXD7_SIM_M_HADDR24 0x180 0x3E8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXFS_SAI1_TX_SYNC 0x184 0x3EC 0x4CC 0x0 0x3 +#define MX8MM_IOMUXC_SAI1_TXFS_SAI5_TX_SYNC 0x184 0x3EC 0x4EC 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_TXFS_CORESIGHT_EVENTO 0x184 0x3EC 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXFS_GPIO4_IO10 0x184 0x3EC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXFS_SIM_M_HADDR25 0x184 0x3EC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXC_SAI1_TX_BCLK 0x188 0x3F0 0x4C8 0x0 0x1 +#define MX8MM_IOMUXC_SAI1_TXC_SAI5_TX_BCLK 0x188 0x3F0 0x4E8 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_TXC_CORESIGHT_EVENTI 0x188 0x3F0 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXC_GPIO4_IO11 0x188 0x3F0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXC_SIM_M_HADDR26 0x188 0x3F0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD0_SAI1_TX_DATA0 0x18C 0x3F4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD0_SAI5_TX_DATA0 0x18C 0x3F4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_TXD0_CORESIGHT_TRACE8 0x18C 0x3F4 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD0_GPIO4_IO12 0x18C 0x3F4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD0_CCMSRCGPCMIX_BOOT_CFG8 0x18C 0x3F4 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD0_SIM_M_HADDR27 0x18C 0x3F4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD1_SAI1_TX_DATA1 0x190 0x3F8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD1_SAI5_TX_DATA1 0x190 0x3F8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_TXD1_CORESIGHT_TRACE9 0x190 0x3F8 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD1_GPIO4_IO13 0x190 0x3F8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD1_CCMSRCGPCMIX_BOOT_CFG9 0x190 0x3F8 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD1_SIM_M_HADDR28 0x190 0x3F8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD2_SAI1_TX_DATA2 0x194 0x3FC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD2_SAI5_TX_DATA2 0x194 0x3FC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_TXD2_CORESIGHT_TRACE10 0x194 0x3FC 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD2_GPIO4_IO14 0x194 0x3FC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD2_CCMSRCGPCMIX_BOOT_CFG10 0x194 0x3FC 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD2_SIM_M_HADDR29 0x194 0x3FC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD3_SAI1_TX_DATA3 0x198 0x400 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD3_SAI5_TX_DATA3 0x198 0x400 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI1_TXD3_CORESIGHT_TRACE11 0x198 0x400 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD3_GPIO4_IO15 0x198 0x400 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD3_CCMSRCGPCMIX_BOOT_CFG11 0x198 0x400 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD3_SIM_M_HADDR30 0x198 0x400 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD4_SAI1_TX_DATA4 0x19C 0x404 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD4_SAI6_RX_BCLK 0x19C 0x404 0x510 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_TXD4_SAI6_TX_BCLK 0x19C 0x404 0x51C 0x2 0x1 +#define MX8MM_IOMUXC_SAI1_TXD4_CORESIGHT_TRACE12 0x19C 0x404 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD4_GPIO4_IO16 0x19C 0x404 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD4_CCMSRCGPCMIX_BOOT_CFG12 0x19C 0x404 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD4_SIM_M_HADDR31 0x19C 0x404 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD5_SAI1_TX_DATA5 0x1A0 0x408 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD5_SAI6_RX_DATA0 0x1A0 0x408 0x514 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_TXD5_SAI6_TX_DATA0 0x1A0 0x408 0x000 0x2 0x0 +#define MX8MM_IOMUXC_SAI1_TXD5_CORESIGHT_TRACE13 0x1A0 0x408 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD5_GPIO4_IO17 0x1A0 0x408 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD5_CCMSRCGPCMIX_BOOT_CFG13 0x1A0 0x408 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD5_SIM_M_HBURST0 0x1A0 0x408 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD6_SAI1_TX_DATA6 0x1A4 0x40C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD6_SAI6_RX_SYNC 0x1A4 0x40C 0x518 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_TXD6_SAI6_TX_SYNC 0x1A4 0x40C 0x520 0x2 0x1 +#define MX8MM_IOMUXC_SAI1_TXD6_CORESIGHT_TRACE14 0x1A4 0x40C 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD6_GPIO4_IO18 0x1A4 0x40C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD6_CCMSRCGPCMIX_BOOT_CFG14 0x1A4 0x40C 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD6_SIM_M_HBURST1 0x1A4 0x40C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_TXD7_SAI1_TX_DATA7 0x1A8 0x410 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_TXD7_SAI6_MCLK 0x1A8 0x410 0x530 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_TXD7_PDM_CLK 0x1A8 0x410 0x000 0x3 0x0 +#define MX8MM_IOMUXC_SAI1_TXD7_CORESIGHT_TRACE15 0x1A8 0x410 0x000 0x4 0x0 +#define MX8MM_IOMUXC_SAI1_TXD7_GPIO4_IO19 0x1A8 0x410 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_TXD7_CCMSRCGPCMIX_BOOT_CFG15 0x1A8 0x410 0x000 0x6 0x0 +#define MX8MM_IOMUXC_SAI1_TXD7_SIM_M_HBURST2 0x1A8 0x410 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI1_MCLK_SAI1_MCLK 0x1AC 0x414 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI1_MCLK_SAI5_MCLK 0x1AC 0x414 0x52C 0x1 0x1 +#define MX8MM_IOMUXC_SAI1_MCLK_SAI1_TX_BCLK 0x1AC 0x414 0x4C8 0x2 0x2 +#define MX8MM_IOMUXC_SAI1_MCLK_PDM_CLK 0x1AC 0x414 0x000 0x3 0x0 +#define MX8MM_IOMUXC_SAI1_MCLK_GPIO4_IO20 0x1AC 0x414 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI1_MCLK_SIM_M_HRESP 0x1AC 0x414 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_RXFS_SAI2_RX_SYNC 0x1B0 0x418 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_RXFS_SAI5_TX_SYNC 0x1B0 0x418 0x4EC 0x1 0x2 +#define MX8MM_IOMUXC_SAI2_RXFS_GPIO4_IO21 0x1B0 0x418 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_RXFS_SIM_M_HSIZE0 0x1B0 0x418 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_RXC_SAI2_RX_BCLK 0x1B4 0x41C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_RXC_SAI5_TX_BCLK 0x1B4 0x41C 0x4E8 0x1 0x2 +#define MX8MM_IOMUXC_SAI2_RXC_GPIO4_IO22 0x1B4 0x41C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_RXC_SIM_M_HSIZE1 0x1B4 0x41C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0 0x1B8 0x420 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_RXD0_SAI5_TX_DATA0 0x1B8 0x420 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI2_RXD0_GPIO4_IO23 0x1B8 0x420 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_RXD0_SIM_M_HSIZE2 0x1B8 0x420 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC 0x1BC 0x424 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_TXFS_SAI5_TX_DATA1 0x1BC 0x424 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI2_TXFS_GPIO4_IO24 0x1BC 0x424 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_TXFS_SIM_M_HWRITE 0x1BC 0x424 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_TXC_SAI2_TX_BCLK 0x1C0 0x428 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_TXC_SAI5_TX_DATA2 0x1C0 0x428 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI2_TXC_GPIO4_IO25 0x1C0 0x428 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_TXC_SIM_M_HREADYOUT 0x1C0 0x428 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0 0x1C4 0x42C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_TXD0_SAI5_TX_DATA3 0x1C4 0x42C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI2_TXD0_GPIO4_IO26 0x1C4 0x42C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_TXD0_TPSMP_CLK 0x1C4 0x42C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI2_MCLK_SAI2_MCLK 0x1C8 0x430 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI2_MCLK_SAI5_MCLK 0x1C8 0x430 0x52C 0x1 0x2 +#define MX8MM_IOMUXC_SAI2_MCLK_GPIO4_IO27 0x1C8 0x430 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI2_MCLK_TPSMP_HDATA_DIR 0x1C8 0x430 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_RXFS_SAI3_RX_SYNC 0x1CC 0x434 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_RXFS_GPT1_CAPTURE1 0x1CC 0x434 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_RXFS_SAI5_RX_SYNC 0x1CC 0x434 0x4E4 0x2 0x2 +#define MX8MM_IOMUXC_SAI3_RXFS_GPIO4_IO28 0x1CC 0x434 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_RXFS_TPSMP_HTRANS0 0x1CC 0x434 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_RXC_SAI3_RX_BCLK 0x1D0 0x438 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_RXC_GPT1_CAPTURE2 0x1D0 0x438 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_RXC_SAI5_RX_BCLK 0x1D0 0x438 0x4D0 0x2 0x2 +#define MX8MM_IOMUXC_SAI3_RXC_GPIO4_IO29 0x1D0 0x438 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_RXC_TPSMP_HTRANS1 0x1D0 0x438 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_RXD_SAI3_RX_DATA0 0x1D4 0x43C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_RXD_GPT1_COMPARE1 0x1D4 0x43C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_RXD_SAI5_RX_DATA0 0x1D4 0x43C 0x4D4 0x2 0x2 +#define MX8MM_IOMUXC_SAI3_RXD_GPIO4_IO30 0x1D4 0x43C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_RXD_TPSMP_HDATA0 0x1D4 0x43C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_TXFS_SAI3_TX_SYNC 0x1D8 0x440 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_TXFS_GPT1_CLK 0x1D8 0x440 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_TXFS_SAI5_RX_DATA1 0x1D8 0x440 0x4D8 0x2 0x2 +#define MX8MM_IOMUXC_SAI3_TXFS_GPIO4_IO31 0x1D8 0x440 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_TXFS_TPSMP_HDATA1 0x1D8 0x440 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_TXC_SAI3_TX_BCLK 0x1DC 0x444 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_TXC_GPT1_COMPARE2 0x1DC 0x444 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_TXC_SAI5_RX_DATA2 0x1DC 0x444 0x4DC 0x2 0x2 +#define MX8MM_IOMUXC_SAI3_TXC_GPIO5_IO0 0x1DC 0x444 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_TXC_TPSMP_HDATA2 0x1DC 0x444 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_TXD_SAI3_TX_DATA0 0x1E0 0x448 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_TXD_GPT1_COMPARE3 0x1E0 0x448 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_TXD_SAI5_RX_DATA3 0x1E0 0x448 0x4E0 0x2 0x2 +#define MX8MM_IOMUXC_SAI3_TXD_GPIO5_IO1 0x1E0 0x448 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_TXD_TPSMP_HDATA3 0x1E0 0x448 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SAI3_MCLK_SAI3_MCLK 0x1E4 0x44C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SAI3_MCLK_PWM4_OUT 0x1E4 0x44C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SAI3_MCLK_SAI5_MCLK 0x1E4 0x44C 0x52C 0x2 0x3 +#define MX8MM_IOMUXC_SAI3_MCLK_GPIO5_IO2 0x1E4 0x44C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SAI3_MCLK_TPSMP_HDATA4 0x1E4 0x44C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SPDIF_TX_SPDIF1_OUT 0x1E8 0x450 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SPDIF_TX_PWM3_OUT 0x1E8 0x450 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SPDIF_TX_GPIO5_IO3 0x1E8 0x450 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SPDIF_TX_TPSMP_HDATA5 0x1E8 0x450 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SPDIF_RX_SPDIF1_IN 0x1EC 0x454 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SPDIF_RX_PWM2_OUT 0x1EC 0x454 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SPDIF_RX_GPIO5_IO4 0x1EC 0x454 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SPDIF_RX_TPSMP_HDATA6 0x1EC 0x454 0x000 0x7 0x0 +#define MX8MM_IOMUXC_SPDIF_EXT_CLK_SPDIF1_EXT_CLK 0x1F0 0x458 0x000 0x0 0x0 +#define MX8MM_IOMUXC_SPDIF_EXT_CLK_PWM1_OUT 0x1F0 0x458 0x000 0x1 0x0 +#define MX8MM_IOMUXC_SPDIF_EXT_CLK_GPIO5_IO5 0x1F0 0x458 0x000 0x5 0x0 +#define MX8MM_IOMUXC_SPDIF_EXT_CLK_TPSMP_HDATA7 0x1F0 0x458 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK 0x1F4 0x45C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI1_SCLK_UART3_DCE_RX 0x1F4 0x45C 0x504 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI1_SCLK_UART3_DTE_TX 0x1F4 0x45C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI1_SCLK_GPIO5_IO6 0x1F4 0x45C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI1_SCLK_TPSMP_HDATA8 0x1F4 0x45C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI1_MOSI_ECSPI1_MOSI 0x1F8 0x460 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI1_MOSI_UART3_DCE_TX 0x1F8 0x460 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI1_MOSI_UART3_DTE_RX 0x1F8 0x460 0x504 0x1 0x1 +#define MX8MM_IOMUXC_ECSPI1_MOSI_GPIO5_IO7 0x1F8 0x460 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI1_MOSI_TPSMP_HDATA9 0x1F8 0x460 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI1_MISO_ECSPI1_MISO 0x1FC 0x464 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI1_MISO_UART3_DCE_CTS_B 0x1FC 0x464 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI1_MISO_UART3_DTE_RTS_B 0x1FC 0x464 0x500 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI1_MISO_GPIO5_IO8 0x1FC 0x464 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI1_MISO_TPSMP_HDATA10 0x1FC 0x464 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI1_SS0_ECSPI1_SS0 0x200 0x468 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI1_SS0_UART3_DCE_RTS_B 0x200 0x468 0x500 0x1 0x1 +#define MX8MM_IOMUXC_ECSPI1_SS0_UART3_DTE_CTS_B 0x200 0x468 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI1_SS0_GPIO5_IO9 0x200 0x468 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI1_SS0_TPSMP_HDATA11 0x200 0x468 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK 0x204 0x46C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI2_SCLK_UART4_DCE_RX 0x204 0x46C 0x50C 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI2_SCLK_UART4_DTE_TX 0x204 0x46C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI2_SCLK_GPIO5_IO10 0x204 0x46C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI2_SCLK_TPSMP_HDATA12 0x204 0x46C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI 0x208 0x470 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI2_MOSI_UART4_DCE_TX 0x208 0x470 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI2_MOSI_UART4_DTE_RX 0x208 0x470 0x50C 0x1 0x1 +#define MX8MM_IOMUXC_ECSPI2_MOSI_GPIO5_IO11 0x208 0x470 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI2_MOSI_TPSMP_HDATA13 0x208 0x470 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI2_MISO_ECSPI2_MISO 0x20C 0x474 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI2_MISO_UART4_DCE_CTS_B 0x20C 0x474 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI2_MISO_UART4_DTE_RTS_B 0x20C 0x474 0x508 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI2_MISO_GPIO5_IO12 0x20C 0x474 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI2_MISO_TPSMP_HDATA14 0x20C 0x474 0x000 0x7 0x0 +#define MX8MM_IOMUXC_ECSPI2_SS0_ECSPI2_SS0 0x210 0x478 0x000 0x0 0x0 +#define MX8MM_IOMUXC_ECSPI2_SS0_UART4_DCE_RTS_B 0x210 0x478 0x508 0x1 0x1 +#define MX8MM_IOMUXC_ECSPI2_SS0_UART4_DTE_CTS_B 0x210 0x478 0x000 0x1 0x0 +#define MX8MM_IOMUXC_ECSPI2_SS0_GPIO5_IO13 0x210 0x478 0x000 0x5 0x0 +#define MX8MM_IOMUXC_ECSPI2_SS0_TPSMP_HDATA15 0x210 0x478 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL 0x214 0x47C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C1_SCL_ENET1_MDC 0x214 0x47C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C1_SCL_GPIO5_IO14 0x214 0x47C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C1_SCL_TPSMP_HDATA16 0x214 0x47C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA 0x218 0x480 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C1_SDA_ENET1_MDIO 0x218 0x480 0x4C0 0x1 0x2 +#define MX8MM_IOMUXC_I2C1_SDA_GPIO5_IO15 0x218 0x480 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C1_SDA_TPSMP_HDATA17 0x218 0x480 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C2_SCL_I2C2_SCL 0x21C 0x484 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C2_SCL_ENET1_1588_EVENT1_IN 0x21C 0x484 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C2_SCL_GPIO5_IO16 0x21C 0x484 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C2_SCL_TPSMP_HDATA18 0x21C 0x484 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C2_SDA_I2C2_SDA 0x220 0x488 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C2_SDA_ENET1_1588_EVENT1_OUT 0x220 0x488 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C2_SDA_GPIO5_IO17 0x220 0x488 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C2_SDA_TPSMP_HDATA19 0x220 0x488 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL 0x224 0x48C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C3_SCL_PWM4_OUT 0x224 0x48C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C3_SCL_GPT2_CLK 0x224 0x48C 0x000 0x2 0x0 +#define MX8MM_IOMUXC_I2C3_SCL_GPIO5_IO18 0x224 0x48C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C3_SCL_TPSMP_HDATA20 0x224 0x48C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA 0x228 0x490 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C3_SDA_PWM3_OUT 0x228 0x490 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C3_SDA_GPT3_CLK 0x228 0x490 0x000 0x2 0x0 +#define MX8MM_IOMUXC_I2C3_SDA_GPIO5_IO19 0x228 0x490 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C3_SDA_TPSMP_HDATA21 0x228 0x490 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C4_SCL_I2C4_SCL 0x22C 0x494 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C4_SCL_PWM2_OUT 0x22C 0x494 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C4_SCL_PCIE1_CLKREQ_B 0x22C 0x494 0x524 0x12 0x0 +#define MX8MM_IOMUXC_I2C4_SCL_GPIO5_IO20 0x22C 0x494 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C4_SCL_TPSMP_HDATA22 0x22C 0x494 0x000 0x7 0x0 +#define MX8MM_IOMUXC_I2C4_SDA_I2C4_SDA 0x230 0x498 0x000 0x0 0x0 +#define MX8MM_IOMUXC_I2C4_SDA_PWM1_OUT 0x230 0x498 0x000 0x1 0x0 +#define MX8MM_IOMUXC_I2C4_SDA_PCIE2_CLKREQ_B 0x230 0x498 0x528 0x2 0x0 +#define MX8MM_IOMUXC_I2C4_SDA_GPIO5_IO21 0x230 0x498 0x000 0x5 0x0 +#define MX8MM_IOMUXC_I2C4_SDA_TPSMP_HDATA23 0x230 0x498 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX 0x234 0x49C 0x4F4 0x0 0x0 +#define MX8MM_IOMUXC_UART1_RXD_UART1_DTE_TX 0x234 0x49C 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART1_RXD_ECSPI3_SCLK 0x234 0x49C 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART1_RXD_GPIO5_IO22 0x234 0x49C 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART1_RXD_TPSMP_HDATA24 0x234 0x49C 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x238 0x4A0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART1_TXD_UART1_DTE_RX 0x238 0x4A0 0x4F4 0x0 0x0 +#define MX8MM_IOMUXC_UART1_TXD_ECSPI3_MOSI 0x238 0x4A0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART1_TXD_GPIO5_IO23 0x238 0x4A0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART1_TXD_TPSMP_HDATA25 0x238 0x4A0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART2_RXD_UART2_DCE_RX 0x23C 0x4A4 0x4FC 0x0 0x0 +#define MX8MM_IOMUXC_UART2_RXD_UART2_DTE_TX 0x23C 0x4A4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART2_RXD_ECSPI3_MISO 0x23C 0x4A4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART2_RXD_GPIO5_IO24 0x23C 0x4A4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART2_RXD_TPSMP_HDATA26 0x23C 0x4A4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART2_TXD_UART2_DCE_TX 0x240 0x4A8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART2_TXD_UART2_DTE_RX 0x240 0x4A8 0x4FC 0x0 0x1 +#define MX8MM_IOMUXC_UART2_TXD_ECSPI3_SS0 0x240 0x4A8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART2_TXD_GPIO5_IO25 0x240 0x4A8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART2_TXD_TPSMP_HDATA27 0x240 0x4A8 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART3_RXD_UART3_DCE_RX 0x244 0x4AC 0x504 0x0 0x2 +#define MX8MM_IOMUXC_UART3_RXD_UART3_DTE_TX 0x244 0x4AC 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART3_RXD_UART1_DCE_CTS_B 0x244 0x4AC 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART3_RXD_UART1_DTE_RTS_B 0x244 0x4AC 0x4F0 0x1 0x0 +#define MX8MM_IOMUXC_UART3_RXD_GPIO5_IO26 0x244 0x4AC 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART3_RXD_TPSMP_HDATA28 0x244 0x4AC 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX 0x248 0x4B0 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART3_TXD_UART3_DTE_RX 0x248 0x4B0 0x504 0x0 0x3 +#define MX8MM_IOMUXC_UART3_TXD_UART1_DCE_RTS_B 0x248 0x4B0 0x4F0 0x1 0x1 +#define MX8MM_IOMUXC_UART3_TXD_UART1_DTE_CTS_B 0x248 0x4B0 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART3_TXD_GPIO5_IO27 0x248 0x4B0 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART3_TXD_TPSMP_HDATA29 0x248 0x4B0 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x24C 0x4B4 0x50C 0x0 0x2 +#define MX8MM_IOMUXC_UART4_RXD_UART4_DTE_TX 0x24C 0x4B4 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART4_RXD_UART2_DCE_CTS_B 0x24C 0x4B4 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART4_RXD_UART2_DTE_RTS_B 0x24C 0x4B4 0x4F8 0x1 0x0 +#define MX8MM_IOMUXC_UART4_RXD_PCIE1_CLKREQ_B 0x24C 0x4B4 0x524 0x2 0x1 +#define MX8MM_IOMUXC_UART4_RXD_GPIO5_IO28 0x24C 0x4B4 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART4_RXD_TPSMP_HDATA30 0x24C 0x4B4 0x000 0x7 0x0 +#define MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x250 0x4B8 0x000 0x0 0x0 +#define MX8MM_IOMUXC_UART4_TXD_UART4_DTE_RX 0x250 0x4B8 0x50C 0x0 0x3 +#define MX8MM_IOMUXC_UART4_TXD_UART2_DCE_RTS_B 0x250 0x4B8 0x4F8 0x1 0x1 +#define MX8MM_IOMUXC_UART4_TXD_UART2_DTE_CTS_B 0x250 0x4B8 0x000 0x1 0x0 +#define MX8MM_IOMUXC_UART4_TXD_PCIE2_CLKREQ_B 0x250 0x4B8 0x528 0x2 0x1 +#define MX8MM_IOMUXC_UART4_TXD_GPIO5_IO29 0x250 0x4B8 0x000 0x5 0x0 +#define MX8MM_IOMUXC_UART4_TXD_TPSMP_HDATA31 0x250 0x4B8 0x000 0x7 0x0 + +#endif /* __DTS_IMX8MM_PINFUNC_H */ From 87eff9af7efb154cc4a940ed12efc803a0bf3fba Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 22 Jan 2019 23:18:21 +0200 Subject: [PATCH 0727/4277] pinctrl: remove pinctrl/machine.h inclusion from pinctrl/pinconf.h The change adds explicit inclusion of linux/pinctrl/machine.h header to the only needed pinctrl-madera-core.c file, and therefore inclusion of pinctrl/machine.h header from pinctrl/pinconf.h can be removed. The change is preparatory to a follow-up reversal of commit f07512e615dd ("pinctrl/pinconfig: add debug interface"). Signed-off-by: Vladimir Zapolskiy Cc: Charles Keepax Reviewed-by Richard Fitzgerald Signed-off-by: Linus Walleij --- drivers/pinctrl/cirrus/pinctrl-madera-core.c | 1 + include/linux/pinctrl/pinconf.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-madera-core.c b/drivers/pinctrl/cirrus/pinctrl-madera-core.c index a5dda832024a..7c9694593f79 100644 --- a/drivers/pinctrl/cirrus/pinctrl-madera-core.c +++ b/drivers/pinctrl/cirrus/pinctrl-madera-core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index 8dd85d302b90..109468d9d849 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -14,8 +14,6 @@ #ifdef CONFIG_PINCONF -#include - struct pinctrl_dev; struct seq_file; From e73339037f6b6d65e84f5fd42e56dd3cdf0d9e9c Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 22 Jan 2019 23:18:22 +0200 Subject: [PATCH 0728/4277] pinctrl: remove unused 'pinconf-config' debugfs interface The main goal of the change is to remove .pin_config_dbg_parse_modify callback before a driver with its support appears. So far the in-kernel interface did not attract any users since its introduction 5 years ago. Originally .pin_config_dbg_parse_modify callback and the associated 'pinconf-config' debugfs file were introduced in commit f07512e615dd ("pinctrl/pinconfig: add debug interface"), a short description of 'pinconf-config' usage for debugging can be expressed this way: Write to 'pinconf-config' (see pinconf_dbg_config_write() function): % echo -n modify $map_type $device_name $state_name $pin_name $config > \ /sys/kernel/debug/pinctrl/$pinctrl/pinconf-config It supposes to update a global (therefore single!) 'pinconf_dbg_conf' variable with an alternative setting, the arguments should match an existing pinconf device and some registered pinctrl mapping 'map': * $map_type is either 'config_pin' or 'config_group', it should match 'map->type' value of PIN_MAP_TYPE_CONFIGS_PIN or PIN_MAP_TYPE_CONFIGS_GROUP accordingly, * $device_name should match 'map->dev_name' string value, * $state_name should match 'map->name' string value, * $pin_name should match 'map->data.configs.group_or_pin' string value, If all above has matched, then $config is a new value to be set by calling pinconfops->pin_config_dbg_parse_modify(pctldev, config, matched_config). After a successful write into 'pinconf-config' a user can read the file to get information about that single modified pin configuration. The fact is .pin_config_dbg_parse_modify callback has never been defined in 'struct pinconf_ops' of any pinconf driver, thus an actual modification of a pin or group state on any present pinconf controller does not happen, and it declares that all related code is no more than dead code. I discovered the issue while attempting to add .pin_config_dbg_parse_modify support in some drivers and found that too short 'MAX_NAME_LEN' set by drivers/pinctrl/pinconf.c:372:#define MAX_NAME_LEN 15 is practically insufficient to store a regular pinctrl device name, which are like 'e6060000.pin-controller-sh-pfc' or pin names like 'MX6QDL_PAD_ENET_REF_CLK', thus it is another indicator that the code is barely usable, insufficiently tested and unprepossessing. Of course it might be possible to increase MAX_NAME_LEN, and then add .pin_config_dbg_parse_modify callbacks to the drivers, but the whole idea of such a limited debug option looks inviable. A more flexible way to functionally substitute the original approach is to implicitly or explicitly use pinctrl_select_state() function whenever needed. Signed-off-by: Vladimir Zapolskiy Cc: Laurent Meunier Cc: Masahiro Yamada Cc: Russell King Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf.c | 222 -------------------------------- include/linux/pinctrl/pinconf.h | 4 - 2 files changed, 226 deletions(-) diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 2c7229380f08..2678603df14b 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -369,225 +368,6 @@ static int pinconf_groups_show(struct seq_file *s, void *what) DEFINE_SHOW_ATTRIBUTE(pinconf_pins); DEFINE_SHOW_ATTRIBUTE(pinconf_groups); -#define MAX_NAME_LEN 15 - -struct dbg_cfg { - enum pinctrl_map_type map_type; - char dev_name[MAX_NAME_LEN + 1]; - char state_name[MAX_NAME_LEN + 1]; - char pin_name[MAX_NAME_LEN + 1]; -}; - -/* - * Goal is to keep this structure as global in order to simply read the - * pinconf-config file after a write to check config is as expected - */ -static struct dbg_cfg pinconf_dbg_conf; - -/** - * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl - * map, of the dev/pin/state that was last written to pinconf-config file. - * @s: string filled in with config description - * @d: not used - */ -static int pinconf_dbg_config_print(struct seq_file *s, void *d) -{ - struct pinctrl_maps *maps_node; - const struct pinctrl_map *map; - const struct pinctrl_map *found = NULL; - struct pinctrl_dev *pctldev; - struct dbg_cfg *dbg = &pinconf_dbg_conf; - int i; - - mutex_lock(&pinctrl_maps_mutex); - - /* Parse the pinctrl map and look for the elected pin/state */ - for_each_maps(maps_node, i, map) { - if (map->type != dbg->map_type) - continue; - if (strcmp(map->dev_name, dbg->dev_name)) - continue; - if (strcmp(map->name, dbg->state_name)) - continue; - - if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { - /* We found the right pin */ - found = map; - break; - } - } - - if (!found) { - seq_printf(s, "No config found for dev/state/pin, expected:\n"); - seq_printf(s, "Searched dev:%s\n", dbg->dev_name); - seq_printf(s, "Searched state:%s\n", dbg->state_name); - seq_printf(s, "Searched pin:%s\n", dbg->pin_name); - seq_printf(s, "Use: modify config_pin "\ - " \n"); - goto exit; - } - - pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name); - seq_printf(s, "Dev %s has config of %s in state %s:\n", - dbg->dev_name, dbg->pin_name, dbg->state_name); - pinconf_show_config(s, pctldev, found->data.configs.configs, - found->data.configs.num_configs); - -exit: - mutex_unlock(&pinctrl_maps_mutex); - - return 0; -} - -/** - * pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl - * map, of a dev/pin/state entry based on user entries to pinconf-config - * @user_buf: contains the modification request with expected format: - * modify - * modify is literal string, alternatives like add/delete not supported yet - * is the configuration to be changed. Supported configs are - * "config_pin" or "config_group", alternatives like config_mux are not - * supported yet. - * are values that should match the pinctrl-maps - * reflects the new config and is driver dependent - */ -static ssize_t pinconf_dbg_config_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct pinctrl_maps *maps_node; - const struct pinctrl_map *map; - const struct pinctrl_map *found = NULL; - struct pinctrl_dev *pctldev; - const struct pinconf_ops *confops = NULL; - struct dbg_cfg *dbg = &pinconf_dbg_conf; - const struct pinctrl_map_configs *configs; - char config[MAX_NAME_LEN + 1]; - char buf[128]; - char *b = &buf[0]; - int buf_size; - char *token; - int i; - - /* Get userspace string and assure termination */ - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - /* - * need to parse entry and extract parameters: - * modify configs_pin devicename state pinname newvalue - */ - - /* Get arg: 'modify' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strcmp(token, "modify")) - return -EINVAL; - - /* - * Get arg type: "config_pin" and "config_group" - * types are supported so far - */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (!strcmp(token, "config_pin")) - dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN; - else if (!strcmp(token, "config_group")) - dbg->map_type = PIN_MAP_TYPE_CONFIGS_GROUP; - else - return -EINVAL; - - /* get arg 'device_name' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(dbg->dev_name, token, MAX_NAME_LEN); - - /* get arg 'state_name' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(dbg->state_name, token, MAX_NAME_LEN); - - /* get arg 'pin_name' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(dbg->pin_name, token, MAX_NAME_LEN); - - /* get new_value of config' */ - token = strsep(&b, " "); - if (!token) - return -EINVAL; - if (strlen(token) >= MAX_NAME_LEN) - return -EINVAL; - strncpy(config, token, MAX_NAME_LEN); - - mutex_lock(&pinctrl_maps_mutex); - - /* Parse the pinctrl map and look for the selected dev/state/pin */ - for_each_maps(maps_node, i, map) { - if (strcmp(map->dev_name, dbg->dev_name)) - continue; - if (map->type != dbg->map_type) - continue; - if (strcmp(map->name, dbg->state_name)) - continue; - - /* we found the right pin / state, so overwrite config */ - if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { - found = map; - break; - } - } - - if (!found) { - count = -EINVAL; - goto exit; - } - - pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name); - if (pctldev) - confops = pctldev->desc->confops; - - if (confops && confops->pin_config_dbg_parse_modify) { - configs = &found->data.configs; - for (i = 0; i < configs->num_configs; i++) { - confops->pin_config_dbg_parse_modify(pctldev, - config, - &configs->configs[i]); - } - } - -exit: - mutex_unlock(&pinctrl_maps_mutex); - - return count; -} - -static int pinconf_dbg_config_open(struct inode *inode, struct file *file) -{ - return single_open(file, pinconf_dbg_config_print, inode->i_private); -} - -static const struct file_operations pinconf_dbg_pinconfig_fops = { - .open = pinconf_dbg_config_open, - .write = pinconf_dbg_config_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev) { @@ -595,8 +375,6 @@ void pinconf_init_device_debugfs(struct dentry *devroot, devroot, pctldev, &pinconf_pins_fops); debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, devroot, pctldev, &pinconf_groups_fops); - debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP), - devroot, pctldev, &pinconf_dbg_pinconfig_fops); } #endif diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index 109468d9d849..93c9dd133e9d 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -29,7 +29,6 @@ struct seq_file; * @pin_config_group_get: get configurations for an entire pin group; should * return -ENOTSUPP and -EINVAL using the same rules as pin_config_get. * @pin_config_group_set: configure all pins in a group - * @pin_config_dbg_parse_modify: optional debugfs to modify a pin configuration * @pin_config_dbg_show: optional debugfs display hook that will provide * per-device info for a certain pin in debugfs * @pin_config_group_dbg_show: optional debugfs display hook that will provide @@ -55,9 +54,6 @@ struct pinconf_ops { unsigned selector, unsigned long *configs, unsigned num_configs); - int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, - const char *arg, - unsigned long *config); void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset); From ff54d82b7716b9bbc8694afd63ebcdb465598cdc Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 23 Jan 2019 01:36:51 +0000 Subject: [PATCH 0729/4277] pinctrl: ti-iodelay: Fix platform_no_drv_owner.cocci warnings Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: YueHaibing Signed-off-by: Linus Walleij --- drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c index 76f9083d9c63..e5e7f1f22813 100644 --- a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -923,7 +923,6 @@ static struct platform_driver ti_iodelay_driver = { .probe = ti_iodelay_probe, .remove = ti_iodelay_remove, .driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, .of_match_table = ti_iodelay_of_match, }, From 4f41e66cf555f74b5aa490684e72b4a6aa34988d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 23 Jan 2019 16:41:40 +0800 Subject: [PATCH 0730/4277] pinctrl: sirf: drop pointless static qualifier in sirfsoc_gpio_probe There is no need to have the 'sgpio' variable static since new value always be assigned before use it. Signed-off-by: YueHaibing Signed-off-by: Linus Walleij --- drivers/pinctrl/sirf/pinctrl-sirf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index 2e42d738b589..2b3bd1a41f21 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -782,7 +782,7 @@ static void sirfsoc_gpio_set_pulldown(struct sirfsoc_gpio_chip *sgpio, static int sirfsoc_gpio_probe(struct device_node *np) { int i, err = 0; - static struct sirfsoc_gpio_chip *sgpio; + struct sirfsoc_gpio_chip *sgpio; struct sirfsoc_gpio_bank *bank; void __iomem *regs; struct platform_device *pdev; From 9ce01efe5bdca3a8291208dc0394ea28453f420c Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Thu, 24 Jan 2019 17:24:53 +0800 Subject: [PATCH 0731/4277] gpio: altera: Fixup block comment style Add missing '*' char to the start of the comment lines. Signed-off-by: Phil Reid Signed-off-by: Linus Walleij --- drivers/gpio/gpio-altera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 8c3ff6e2366f..748fdd4e9a53 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -32,9 +32,9 @@ * struct altera_gpio_chip * @mmchip : memory mapped chip structure. * @gpio_lock : synchronization lock so that new irq/set/get requests - will be blocked until the current one completes. +* will be blocked until the current one completes. * @interrupt_trigger : specifies the hardware configured IRQ trigger type - (rising, falling, both, high) +* (rising, falling, both, high) * @mapped_irq : kernel mapped irq number. */ struct altera_gpio_chip { From b868db94a6a704755a33a362cfcf4625abdda115 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Fri, 25 Jan 2019 02:09:07 +0100 Subject: [PATCH 0732/4277] gpio: tqmx86: Add GPIO from for this IO controller Some TQ-Systems ComExpress modules contain an IO controller with 8 GPIO lines. Signed-off-by: Andrew Lunn Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-tqmx86.c | 333 +++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 drivers/gpio/gpio-tqmx86.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f77180dd87bd..7a934b40bdae 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1191,6 +1191,13 @@ config GPIO_TPS68470 of the TPS68470 must be available before dependent drivers are loaded. +config GPIO_TQMX86 + tristate "TQ-Systems QTMX86 GPIO" + depends on MFD_TQMX86 || COMPILE_TEST + select GPIOLIB_IRQCHIP + help + This driver supports GPIO on the TQMX86 IO controller. + config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 37628f8dbf70..89cfad015687 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -135,6 +135,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o +obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c new file mode 100644 index 000000000000..6f7250c6f597 --- /dev/null +++ b/drivers/gpio/gpio-tqmx86.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TQ-Systems TQMx86 PLD GPIO driver + * + * Based on vendor driver by: + * Vadim V.Vlasov + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TQMX86_NGPIO 8 +#define TQMX86_NGPO 4 /* 0-3 - output */ +#define TQMX86_NGPI 4 /* 4-7 - input */ +#define TQMX86_DIR_INPUT_MASK 0xf0 /* 0-3 - output, 4-7 - input */ + +#define TQMX86_GPIODD 0 /* GPIO Data Direction Register */ +#define TQMX86_GPIOD 1 /* GPIO Data Register */ +#define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ +#define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ + +#define TQMX86_GPII_FALLING BIT(0) +#define TQMX86_GPII_RISING BIT(1) +#define TQMX86_GPII_MASK (BIT(0) | BIT(1)) +#define TQMX86_GPII_BITS 2 + +struct tqmx86_gpio_data { + struct gpio_chip chip; + struct irq_chip irq_chip; + void __iomem *io_base; + int irq; + raw_spinlock_t spinlock; + u8 irq_type[TQMX86_NGPI]; +}; + +static u8 tqmx86_gpio_read(struct tqmx86_gpio_data *gd, unsigned int reg) +{ + return ioread8(gd->io_base + reg); +} + +static void tqmx86_gpio_write(struct tqmx86_gpio_data *gd, u8 val, + unsigned int reg) +{ + iowrite8(val, gd->io_base + reg); +} + +static int tqmx86_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + + return !!(tqmx86_gpio_read(gpio, TQMX86_GPIOD) & BIT(offset)); +} + +static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + unsigned long flags; + u8 val; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); + if (value) + val |= BIT(offset); + else + val &= ~BIT(offset); + tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static int tqmx86_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + /* Direction cannot be changed. Validate is an input. */ + if (BIT(offset) & TQMX86_DIR_INPUT_MASK) + return 0; + else + return -EINVAL; +} + +static int tqmx86_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, + int value) +{ + /* Direction cannot be changed, validate is an output */ + if (BIT(offset) & TQMX86_DIR_INPUT_MASK) + return -EINVAL; + else + return 0; +} + +static int tqmx86_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return !!(TQMX86_DIR_INPUT_MASK & BIT(offset)); +} + +static void tqmx86_gpio_irq_mask(struct irq_data *data) +{ + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; + u8 gpiic, mask; + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static void tqmx86_gpio_irq_unmask(struct irq_data *data) +{ + unsigned int offset = (data->hwirq - TQMX86_NGPO); + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned long flags; + u8 gpiic, mask; + + mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~mask; + gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); +} + +static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct tqmx86_gpio_data *gpio = gpiochip_get_data( + irq_data_get_irq_chip_data(data)); + unsigned int offset = (data->hwirq - TQMX86_NGPO); + unsigned int edge_type = type & IRQF_TRIGGER_MASK; + unsigned long flags; + u8 new_type, gpiic; + + switch (edge_type) { + case IRQ_TYPE_EDGE_RISING: + new_type = TQMX86_GPII_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + new_type = TQMX86_GPII_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; + break; + default: + return -EINVAL; /* not supported */ + } + + gpio->irq_type[offset] = new_type; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); + gpiic |= new_type << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + + return 0; +} + +static void tqmx86_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + unsigned long irq_bits; + int i = 0, child_irq; + u8 irq_status; + + chained_irq_enter(irq_chip, desc); + + irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + irq_bits = irq_status; + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + child_irq = irq_find_mapping(gpio->chip.irq.domain, + i + TQMX86_NGPO); + generic_handle_irq(child_irq); + } + + chained_irq_exit(irq_chip, desc); +} + +/* Minimal runtime PM is needed by the IRQ subsystem */ +static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { + SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, + tqmx86_gpio_runtime_resume, NULL) +}; + +static int tqmx86_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tqmx86_gpio_data *gpio; + struct gpio_chip *chip; + void __iomem *io_base; + struct resource *res; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get I/O\n"); + return -ENODEV; + } + + io_base = devm_ioport_map(&pdev->dev, res->start, resource_size(res)); + if (!io_base) + return -ENOMEM; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + raw_spin_lock_init(&gpio->spinlock); + gpio->io_base = io_base; + + tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = "gpio-tqmx86"; + chip->owner = THIS_MODULE; + chip->can_sleep = false; + chip->base = -1; + chip->direction_input = tqmx86_gpio_direction_input; + chip->direction_output = tqmx86_gpio_direction_output; + chip->get_direction = tqmx86_gpio_get_direction; + chip->get = tqmx86_gpio_get; + chip->set = tqmx86_gpio_set; + chip->ngpio = TQMX86_NGPIO; + chip->irq.need_valid_mask = true; + chip->parent = pdev->dev.parent; + + pm_runtime_enable(&pdev->dev); + + ret = devm_gpiochip_add_data(dev, chip, gpio); + if (ret) { + dev_err(dev, "Could not register GPIO chip\n"); + goto out_pm_dis; + } + + if (irq) { + struct irq_chip *irq_chip = &gpio->irq_chip; + u8 irq_status; + + irq_chip->name = chip->label; + irq_chip->parent_device = &pdev->dev; + irq_chip->irq_mask = tqmx86_gpio_irq_mask; + irq_chip->irq_unmask = tqmx86_gpio_irq_unmask; + irq_chip->irq_set_type = tqmx86_gpio_irq_set_type; + + /* Mask all interrupts */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIIC); + + /* Clear all pending interrupts */ + irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); + tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); + + ret = gpiochip_irqchip_add(chip, irq_chip, + 0, handle_simple_irq, + IRQ_TYPE_EDGE_BOTH); + if (ret) { + dev_err(dev, "Could not add irq chip\n"); + goto out_remove; + } + + gpiochip_set_chained_irqchip(chip, irq_chip, + irq, tqmx86_gpio_irq_handler); + } + + /* Only GPIOs 4-7 are valid for interrupts. Clear the others */ + clear_bit(0, chip->irq.valid_mask); + clear_bit(1, chip->irq.valid_mask); + clear_bit(2, chip->irq.valid_mask); + clear_bit(3, chip->irq.valid_mask); + + dev_info(dev, "GPIO functionality initialized with %d pins\n", + chip->ngpio); + + return 0; + +out_remove: + gpiochip_remove(&gpio->chip); +out_pm_dis: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static struct platform_driver tqmx86_gpio_driver = { + .driver = { + .name = "tqmx86-gpio", + .pm = &tqmx86_gpio_dev_pm_ops, + }, + .probe = tqmx86_gpio_probe, +}; + +module_platform_driver(tqmx86_gpio_driver); + +MODULE_DESCRIPTION("TQMx86 PLD GPIO Driver"); +MODULE_AUTHOR("Andrew Lunn "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tqmx86-gpio"); From c246761b442fb6f42ef16aef8f89061233accb00 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 25 Jan 2019 07:45:47 +0000 Subject: [PATCH 0733/4277] pinctrl: berlin: as370: use generic "pwm" as pwm function name So that we could use the generic "pwm" for two or more pins, e.g pwm0_pmux: pwm0-pmux { groups = "PWM0", "PWM1"; function = "pwm"; }; Signed-off-by: Jisheng Zhang Signed-off-by: Linus Walleij --- drivers/pinctrl/berlin/pinctrl-as370.c | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/pinctrl/berlin/pinctrl-as370.c b/drivers/pinctrl/berlin/pinctrl-as370.c index d2bb811fc5fa..44f8ccdbeeff 100644 --- a/drivers/pinctrl/berlin/pinctrl-as370.c +++ b/drivers/pinctrl/berlin/pinctrl-as370.c @@ -36,13 +36,13 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("I2S1_DO2", 0x0, 0x3, 0x0c, BERLIN_PINCTRL_FUNCTION(0x0, "por"), /* CORE RSTB */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO2 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm4"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM4 */ BERLIN_PINCTRL_FUNCTION(0x3, "gpio"), /* GPIO4 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG4 */ BERLIN_PINCTRL_GROUP("I2S1_DO3", 0x0, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO5 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s1"), /* DO3 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm5"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM5 */ BERLIN_PINCTRL_FUNCTION(0x3, "spififib"), /* SPDIFIB */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG5 */ @@ -61,24 +61,24 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("I2S2_DI0", 0x0, 0x3, 0x1b, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO9 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI0 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm2"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM2 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG9 */ BERLIN_PINCTRL_GROUP("I2S2_DI1", 0x4, 0x3, 0x00, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO10 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI1 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm3"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM3 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG10 */ BERLIN_PINCTRL_GROUP("I2S2_DI2", 0x4, 0x3, 0x03, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO11 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI2 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdific"), /* SPDIFIC */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG11 */ BERLIN_PINCTRL_GROUP("I2S2_DI3", 0x4, 0x3, 0x06, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO12 */ BERLIN_PINCTRL_FUNCTION(0x1, "i2s2"), /* DI3 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdifia"), /* SPDIFIA */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG12 */ @@ -98,14 +98,14 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("PDM_DI2", 0x4, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO16 */ BERLIN_PINCTRL_FUNCTION(0x1, "pdm"), /* DI2 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm4"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM4 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdifid"), /* SPDIFID */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG16 */ BERLIN_PINCTRL_GROUP("PDM_DI3", 0x4, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO17 */ BERLIN_PINCTRL_FUNCTION(0x1, "pdm"), /* DI3 */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm5"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM5 */ BERLIN_PINCTRL_FUNCTION(0x3, "spdifi"), /* SPDIFI */ BERLIN_PINCTRL_FUNCTION(0x4, "spdifo"), /* SPDIFO */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG17 */ @@ -139,11 +139,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_FUNCTION(0x1, "emmc")), /* DATA7 */ BERLIN_PINCTRL_GROUP("NAND_ALE", 0x8, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* ALE */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */ BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO18 */ BERLIN_PINCTRL_GROUP("NAND_CLE", 0x8, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* CLE */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */ BERLIN_PINCTRL_FUNCTION(0x3, "gpio")), /* GPIO19 */ BERLIN_PINCTRL_GROUP("NAND_WEn", 0x8, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "nand"), /* WEn */ @@ -169,12 +169,12 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SPI1_SS1n", 0xc, 0x3, 0x0c, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS1n */ BERLIN_PINCTRL_FUNCTION(0x2, "gpio"), /* GPIO26 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm2")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM2 */ BERLIN_PINCTRL_GROUP("SPI1_SS2n", 0xc, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* RXD */ BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS2n */ BERLIN_PINCTRL_FUNCTION(0x2, "gpio"), /* GPIO27 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm3")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM3 */ BERLIN_PINCTRL_GROUP("SPI1_SS3n", 0xc, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "uart0"), /* TXD */ BERLIN_PINCTRL_FUNCTION(0x1, "spi1"), /* SS3n */ @@ -182,11 +182,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SPI1_SCLK", 0xc, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SCLK */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO29 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm4")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM4 */ BERLIN_PINCTRL_GROUP("SPI1_SDO", 0xc, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDO */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO30 */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm5")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM5 */ BERLIN_PINCTRL_GROUP("SPI1_SDI", 0xc, 0x3, 0x1b, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SDI */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio")), /* GPIO31 */ @@ -209,51 +209,51 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("TMS", 0x10, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TMS */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO37 */ - BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")), + BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM0 */ BERLIN_PINCTRL_GROUP("TDI", 0x10, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDI */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO38 */ - BERLIN_PINCTRL_FUNCTION(0x4, "pwm1")), + BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM1 */ BERLIN_PINCTRL_GROUP("TDO", 0x10, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "jtag"), /* TDO */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO39 */ - BERLIN_PINCTRL_FUNCTION(0x4, "pwm0")), + BERLIN_PINCTRL_FUNCTION(0x4, "pwm")), /* PWM0 */ BERLIN_PINCTRL_GROUP("PWM6", 0x10, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO40 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm6")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM6 */ BERLIN_PINCTRL_GROUP("PWM7", 0x10, 0x3, 0x1b, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO41 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm7")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM7 */ BERLIN_PINCTRL_GROUP("PWM0", 0x14, 0x3, 0x00, BERLIN_PINCTRL_FUNCTION(0x0, "por"), /* VDDCPUSOC RSTB */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm0"), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm"), /* PWM0 */ BERLIN_PINCTRL_FUNCTION(0x2, "gpio")), /* GPIO42 */ BERLIN_PINCTRL_GROUP("PWM1", 0x14, 0x3, 0x03, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO43 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm1")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM1 */ BERLIN_PINCTRL_GROUP("PWM2", 0x14, 0x3, 0x06, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO44 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm2")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM2 */ BERLIN_PINCTRL_GROUP("PWM3", 0x14, 0x3, 0x09, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO45 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm3")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM3 */ BERLIN_PINCTRL_GROUP("PWM4", 0x14, 0x3, 0x0c, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO46 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm4")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM4 */ BERLIN_PINCTRL_GROUP("PWM5", 0x14, 0x3, 0x0f, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO47 */ - BERLIN_PINCTRL_FUNCTION(0x1, "pwm5")), + BERLIN_PINCTRL_FUNCTION(0x1, "pwm")), /* PWM5 */ BERLIN_PINCTRL_GROUP("URT1_RTSn", 0x14, 0x3, 0x12, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO48 */ BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* RTSn */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm6"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM6 */ BERLIN_PINCTRL_FUNCTION(0x3, "tw1a"), /* SCL */ BERLIN_PINCTRL_FUNCTION(0x4, "aio"), /* DBG0 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG18 */ BERLIN_PINCTRL_GROUP("URT1_CTSn", 0x14, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO49 */ BERLIN_PINCTRL_FUNCTION(0x1, "uart1"), /* CTSn */ - BERLIN_PINCTRL_FUNCTION(0x2, "pwm7"), + BERLIN_PINCTRL_FUNCTION(0x2, "pwm"), /* PWM7 */ BERLIN_PINCTRL_FUNCTION(0x3, "tw1a"), /* SDA */ BERLIN_PINCTRL_FUNCTION(0x4, "aio"), /* DBG1 */ BERLIN_PINCTRL_FUNCTION(0x5, "phy")), /* DBG19 */ @@ -308,11 +308,11 @@ static const struct berlin_desc_group as370_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SD0_CDn", 0x1c, 0x3, 0x00, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO62 */ BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* CDn */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm2")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM2 */ BERLIN_PINCTRL_GROUP("SD0_WP", 0x1c, 0x3, 0x03, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO63 */ BERLIN_PINCTRL_FUNCTION(0x1, "sd0"), /* WP */ - BERLIN_PINCTRL_FUNCTION(0x3, "pwm3")), + BERLIN_PINCTRL_FUNCTION(0x3, "pwm")), /* PWM3 */ }; static const struct berlin_pinctrl_desc as370_soc_pinctrl_data = { From 876811f7c34eff986462ed2def81f4ecfbcef2ec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Jan 2019 19:03:02 +0200 Subject: [PATCH 0734/4277] gpiolib: acpi: Correct kernel doc of struct acpi_gpio_event The checker complains during build gpiolib-acpi.c:45: warning: Function parameter or member 'irq_requested' not described in 'acpi_gpio_event' because the typo in the field description. Fix the name to have documentation up-to-date. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index f0763e0e02f1..3401823427f6 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -29,7 +29,7 @@ * @irq: Linux IRQ number for the event, for request_ / free_irq * @irqflags: flags to pass to request_irq when requesting the IRQ * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source - * @is_requested: True if request_irq has been done + * @irq_requested:True if request_irq has been done * @desc: gpio_desc for the GPIO pin for this event */ struct acpi_gpio_event { From 351e0592bfeae58c76a11bdcec59eb78b2937593 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Mon, 28 Jan 2019 10:38:10 -0800 Subject: [PATCH 0735/4277] Input: st1232 - add support for st1633 Add support for the Sitronix ST1633 touchscreen controller to the st1232 driver. A protocol spec can be found here: www.ampdisplay.com/documents/pdf/AM-320480B6TZQW-TC0H.pdf Signed-off-by: Martin Kepplinger Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/sitronix-st1232.txt | 6 +- drivers/input/touchscreen/Kconfig | 6 +- drivers/input/touchscreen/st1232.c | 120 +++++++++++++----- 3 files changed, 93 insertions(+), 39 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt index 64ad48b824a2..e73e826e0f2a 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt @@ -1,7 +1,9 @@ -* Sitronix st1232 touchscreen controller +* Sitronix st1232 or st1633 touchscreen controller Required properties: -- compatible: must be "sitronix,st1232" +- compatible: must contain one of + * "sitronix,st1232" + * "sitronix,st1633" - reg: I2C address of the chip - interrupts: interrupt to which the chip is connected diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index af6027cc7bbf..6c16aaeb4191 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1168,11 +1168,11 @@ config TOUCHSCREEN_SIS_I2C module will be called sis_i2c. config TOUCHSCREEN_ST1232 - tristate "Sitronix ST1232 touchscreen controllers" + tristate "Sitronix ST1232 or ST1633 touchscreen controllers" depends on I2C help - Say Y here if you want to support Sitronix ST1232 - touchscreen controller. + Say Y here if you want to support the Sitronix ST1232 + or ST1633 touchscreen controller. If unsure, say N. diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 11ff32c68025..777df903605d 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -23,13 +23,7 @@ #include #define ST1232_TS_NAME "st1232-ts" - -#define MIN_X 0x00 -#define MIN_Y 0x00 -#define MAX_X 0x31f /* (800 - 1) */ -#define MAX_Y 0x1df /* (480 - 1) */ -#define MAX_AREA 0xff -#define MAX_FINGERS 2 +#define ST1633_TS_NAME "st1633-ts" struct st1232_ts_finger { u16 x; @@ -38,12 +32,24 @@ struct st1232_ts_finger { bool is_valid; }; +struct st_chip_info { + bool have_z; + u16 max_x; + u16 max_y; + u16 max_area; + u16 max_fingers; + u8 start_reg; +}; + struct st1232_ts_data { struct i2c_client *client; struct input_dev *input_dev; - struct st1232_ts_finger finger[MAX_FINGERS]; struct dev_pm_qos_request low_latency_req; int reset_gpio; + const struct st_chip_info *chip_info; + int read_buf_len; + u8 *read_buf; + struct st1232_ts_finger *finger; }; static int st1232_ts_read_data(struct st1232_ts_data *ts) @@ -52,40 +58,35 @@ static int st1232_ts_read_data(struct st1232_ts_data *ts) struct i2c_client *client = ts->client; struct i2c_msg msg[2]; int error; - u8 start_reg; - u8 buf[10]; + int i, y; + u8 start_reg = ts->chip_info->start_reg; + u8 *buf = ts->read_buf; - /* read touchscreen data from ST1232 */ + /* read touchscreen data */ msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = &start_reg; - start_reg = 0x10; msg[1].addr = ts->client->addr; msg[1].flags = I2C_M_RD; - msg[1].len = sizeof(buf); + msg[1].len = ts->read_buf_len; msg[1].buf = buf; error = i2c_transfer(client->adapter, msg, 2); if (error < 0) return error; - /* get "valid" bits */ - finger[0].is_valid = buf[2] >> 7; - finger[1].is_valid = buf[5] >> 7; + for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) { + finger[i].is_valid = buf[i + y] >> 7; + if (finger[i].is_valid) { + finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1]; + finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2]; - /* get xy coordinate */ - if (finger[0].is_valid) { - finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; - finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; - finger[0].t = buf[8]; - } - - if (finger[1].is_valid) { - finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; - finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; - finger[1].t = buf[9]; + /* st1232 includes a z-axis / touch strength */ + if (ts->chip_info->have_z) + finger[i].t = buf[i + 6]; + } } return 0; @@ -104,11 +105,14 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) goto end; /* multi touch protocol */ - for (i = 0; i < MAX_FINGERS; i++) { + for (i = 0; i < ts->chip_info->max_fingers; i++) { if (!finger[i].is_valid) continue; - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); + if (ts->chip_info->have_z) + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[i].t); + input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); input_mt_sync(input_dev); @@ -142,13 +146,41 @@ static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) gpio_direction_output(ts->reset_gpio, poweron); } +static const struct st_chip_info st1232_chip_info = { + .have_z = true, + .max_x = 0x31f, /* 800 - 1 */ + .max_y = 0x1df, /* 480 -1 */ + .max_area = 0xff, + .max_fingers = 2, + .start_reg = 0x12, +}; + +static const struct st_chip_info st1633_chip_info = { + .have_z = false, + .max_x = 0x13f, /* 320 - 1 */ + .max_y = 0x1df, /* 480 -1 */ + .max_area = 0x00, + .max_fingers = 5, + .start_reg = 0x12, +}; + static int st1232_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct st_chip_info *match; struct st1232_ts_data *ts; + struct st1232_ts_finger *finger; struct input_dev *input_dev; int error; + match = device_get_match_data(&client->dev); + if (!match && id) + match = (const void *)id->driver_data; + if (!match) { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "need I2C_FUNC_I2C\n"); return -EIO; @@ -163,6 +195,19 @@ static int st1232_ts_probe(struct i2c_client *client, if (!ts) return -ENOMEM; + ts->chip_info = match; + ts->finger = devm_kcalloc(&client->dev, + ts->chip_info->max_fingers, sizeof(*finger), + GFP_KERNEL); + if (!ts->finger) + return -ENOMEM; + + /* allocate a buffer according to the number of registers to read */ + ts->read_buf_len = ts->chip_info->max_fingers * 4; + ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len, GFP_KERNEL); + if (!ts->read_buf) + return -ENOMEM; + input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) return -ENOMEM; @@ -192,9 +237,14 @@ static int st1232_ts_probe(struct i2c_client *client, __set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_ABS, input_dev->evbit); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); + if (ts->chip_info->have_z) + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, + ts->chip_info->max_area, 0, 0); + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, ts->chip_info->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, ts->chip_info->max_y, 0, 0); error = devm_request_threaded_irq(&client->dev, client->irq, NULL, st1232_ts_irq_handler, @@ -261,13 +311,15 @@ static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops, st1232_ts_suspend, st1232_ts_resume); static const struct i2c_device_id st1232_ts_id[] = { - { ST1232_TS_NAME, 0 }, + { ST1232_TS_NAME, (unsigned long)&st1232_chip_info }, + { ST1633_TS_NAME, (unsigned long)&st1633_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, st1232_ts_id); static const struct of_device_id st1232_ts_dt_ids[] = { - { .compatible = "sitronix,st1232", }, + { .compatible = "sitronix,st1232", .data = &st1232_chip_info }, + { .compatible = "sitronix,st1633", .data = &st1633_chip_info }, { } }; MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids); From 4dbb71195b34232d9ecc12c4cfbee6871688a0e3 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Mon, 28 Jan 2019 11:04:08 -0800 Subject: [PATCH 0736/4277] Input: st1232 - add Martin as module author This adds myself as an author of the st1232 driver module as Tony's email address doesn't seem to work anymore. Signed-off-by: Martin Kepplinger Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 777df903605d..634d6c243845 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -338,5 +338,6 @@ static struct i2c_driver st1232_ts_driver = { module_i2c_driver(st1232_ts_driver); MODULE_AUTHOR("Tony SIM "); +MODULE_AUTHOR("Martin Kepplinger "); MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); MODULE_LICENSE("GPL v2"); From c3a39380a39df3750149a2f4699c1c241a0e6ea2 Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Mon, 28 Jan 2019 14:49:37 -0800 Subject: [PATCH 0737/4277] Input: sx8654 - add reset-gpio support The sx8654 features a NRST input which may be connected to a GPIO. Therefore add support for hard-resetting the sx8654 via this NRST. If the reset-gpio property is provided the sx8654 is resetted via NRST instead of the soft-reset via I2C. Signed-off-by: Richard Leitner Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/sx8654.txt | 4 ++ drivers/input/touchscreen/sx8654.c | 44 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt index 4886c4aa2906..ca521d8f7d65 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt @@ -5,6 +5,9 @@ Required properties: - reg: i2c slave address - interrupts: touch controller interrupt +Optional properties: + - reset-gpios: GPIO specification for the NRST input + Example: sx8654@48 { @@ -12,4 +15,5 @@ Example: reg = <0x48>; interrupt-parent = <&gpio6>; interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>; }; diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index ed29db3ec731..ab5cbf7e0879 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -27,12 +27,14 @@ * published by the Free Software Foundation. */ -#include -#include -#include +#include +#include #include +#include #include #include +#include +#include /* register addresses */ #define I2C_REG_TOUCH0 0x00 @@ -74,6 +76,7 @@ struct sx8654 { struct input_dev *input; struct i2c_client *client; + struct gpio_desc *gpio_reset; }; static irqreturn_t sx8654_irq(int irq, void *handle) @@ -124,6 +127,25 @@ out: return IRQ_HANDLED; } +static int sx8654_reset(struct sx8654 *ts) +{ + int err; + + if (ts->gpio_reset) { + gpiod_set_value_cansleep(ts->gpio_reset, 1); + udelay(2); /* Tpulse > 1µs */ + gpiod_set_value_cansleep(ts->gpio_reset, 0); + } else { + dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n"); + err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET, + SOFTRESET_VALUE); + if (err) + return err; + } + + return 0; +} + static int sx8654_open(struct input_dev *dev) { struct sx8654 *sx8654 = input_get_drvdata(dev); @@ -186,6 +208,17 @@ static int sx8654_probe(struct i2c_client *client, if (!sx8654) return -ENOMEM; + sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sx8654->gpio_reset)) { + error = PTR_ERR(sx8654->gpio_reset); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, "unable to get reset-gpio: %d\n", + error); + return error; + } + dev_dbg(&client->dev, "got GPIO reset pin\n"); + input = devm_input_allocate_device(&client->dev); if (!input) return -ENOMEM; @@ -206,10 +239,9 @@ static int sx8654_probe(struct i2c_client *client, input_set_drvdata(sx8654->input, sx8654); - error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, - SOFTRESET_VALUE); + error = sx8654_reset(sx8654); if (error) { - dev_err(&client->dev, "writing softreset value failed"); + dev_err(&client->dev, "reset failed"); return error; } From 5896756a70b2f1d476d5cf2e174c1675ff0d9e8b Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Mon, 28 Jan 2019 14:54:04 -0800 Subject: [PATCH 0738/4277] Input: sx8654 - add sx8655 and sx8656 to compatibles As the sx865[456] share the same datasheet and differ only in the presence of a "capacitive proximity detection circuit" and a "haptics motor driver for LRA/ERM" add them to the compatbiles. As the driver doesn't implement these features it should be no problem. Signed-off-by: Richard Leitner Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/sx8654.txt | 5 ++++- drivers/input/touchscreen/sx8654.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt index ca521d8f7d65..a538678424dd 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt @@ -1,7 +1,10 @@ * Semtech SX8654 I2C Touchscreen Controller Required properties: -- compatible: must be "semtech,sx8654" +- compatible: must be one of the following, depending on the model: + "semtech,sx8654" + "semtech,sx8655" + "semtech,sx8656" - reg: i2c slave address - interrupts: touch controller interrupt diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index ab5cbf7e0879..9e1777ed93a7 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -291,6 +291,8 @@ static int sx8654_probe(struct i2c_client *client, #ifdef CONFIG_OF static const struct of_device_id sx8654_of_match[] = { { .compatible = "semtech,sx8654", }, + { .compatible = "semtech,sx8655", }, + { .compatible = "semtech,sx8656", }, { }, }; MODULE_DEVICE_TABLE(of, sx8654_of_match); @@ -298,6 +300,8 @@ MODULE_DEVICE_TABLE(of, sx8654_of_match); static const struct i2c_device_id sx8654_id_table[] = { { "semtech_sx8654", 0 }, + { "semtech_sx8655", 0 }, + { "semtech_sx8656", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, sx8654_id_table); From 43df039c6d92266d6e023f7eb23aeb6511934f20 Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Mon, 28 Jan 2019 14:58:27 -0800 Subject: [PATCH 0739/4277] Input: sx8654 - add sx8650 support The sx8654 and sx8650 are quite similar, therefore add support for the sx8650 within the sx8654 driver. Signed-off-by: Richard Leitner Reviewed-by: Rob Herring [dtor: use __be16 in sx8650_irq, add missing del_timer_sync] Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/sx8654.txt | 1 + drivers/input/touchscreen/sx8654.c | 196 ++++++++++++++++-- 2 files changed, 177 insertions(+), 20 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt index a538678424dd..0ebe6dd043c7 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt @@ -2,6 +2,7 @@ Required properties: - compatible: must be one of the following, depending on the model: + "semtech,sx8650" "semtech,sx8654" "semtech,sx8655" "semtech,sx8656" diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 9e1777ed93a7..5f5af8eaecea 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -44,9 +44,11 @@ #define I2C_REG_IRQSRC 0x23 #define I2C_REG_SOFTRESET 0x3f +#define I2C_REG_SX8650_STAT 0x05 +#define SX8650_STAT_CONVIRQ 0x80 + /* commands */ #define CMD_READ_REGISTER 0x40 -#define CMD_MANUAL 0xc0 #define CMD_PENTRG 0xe0 /* value for I2C_REG_SOFTRESET */ @@ -58,6 +60,7 @@ /* bits for RegTouch1 */ #define CONDIRQ 0x20 +#define RPDNT_100K 0x00 #define FILT_7SA 0x03 /* bits for I2C_REG_CHANMASK */ @@ -71,14 +74,121 @@ /* power delay: lower nibble of CTRL0 register */ #define POWDLY_1_1MS 0x0b +/* for sx8650, as we have no pen release IRQ there: timeout in ns following the + * last PENIRQ after which we assume the pen is lifted. + */ +#define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10) + #define MAX_12BIT ((1 << 12) - 1) +#define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */ + +/* channel definition */ +#define CH_X 0x00 +#define CH_Y 0x01 + +struct sx865x_data { + u8 cmd_manual; + u8 chan_mask; + bool has_irq_penrelease; + bool has_reg_irqmask; + irq_handler_t irqh; +}; struct sx8654 { struct input_dev *input; struct i2c_client *client; struct gpio_desc *gpio_reset; + + spinlock_t lock; /* for input reporting from irq/timer */ + struct timer_list timer; + + const struct sx865x_data *data; }; +static inline void sx865x_penrelease(struct sx8654 *ts) +{ + struct input_dev *input_dev = ts->input; + + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); +} + +static void sx865x_penrelease_timer_handler(struct timer_list *t) +{ + struct sx8654 *ts = from_timer(ts, t, timer); + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + sx865x_penrelease(ts); + spin_unlock_irqrestore(&ts->lock, flags); + dev_dbg(&ts->client->dev, "penrelease by timer\n"); +} + +static irqreturn_t sx8650_irq(int irq, void *handle) +{ + struct sx8654 *ts = handle; + struct device *dev = &ts->client->dev; + int len, i; + unsigned long flags; + u8 stat; + u16 x, y; + u16 ch; + u16 chdata; + __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)]; + u8 nchan = hweight32(ts->data->chan_mask); + u8 readlen = nchan * sizeof(*data); + + stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER + | I2C_REG_SX8650_STAT); + + if (!(stat & SX8650_STAT_CONVIRQ)) { + dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat); + return IRQ_HANDLED; + } + + len = i2c_master_recv(ts->client, (u8 *)data, readlen); + if (len != readlen) { + dev_dbg(dev, "ignore short recv (%d)\n", len); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&ts->lock, flags); + + x = 0; + y = 0; + for (i = 0; i < nchan; i++) { + chdata = be16_to_cpu(data[i]); + + if (unlikely(chdata == 0xFFFF)) { + dev_dbg(dev, "invalid qualified data @ %d\n", i); + continue; + } else if (unlikely(chdata & 0x8000)) { + dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata); + continue; + } + + ch = chdata >> 12; + if (ch == CH_X) + x = chdata & MAX_12BIT; + else if (ch == CH_Y) + y = chdata & MAX_12BIT; + else + dev_warn(dev, "unknown channel %d [0x%04x]\n", ch, + chdata); + } + + input_report_abs(ts->input, ABS_X, x); + input_report_abs(ts->input, ABS_Y, y); + input_report_key(ts->input, BTN_TOUCH, 1); + input_sync(ts->input); + dev_dbg(dev, "point(%4d,%4d)\n", x, y); + + mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT); + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + static irqreturn_t sx8654_irq(int irq, void *handle) { struct sx8654 *sx8654 = handle; @@ -179,14 +289,17 @@ static void sx8654_close(struct input_dev *dev) disable_irq(client->irq); + if (!sx8654->data->has_irq_penrelease) + del_timer_sync(&sx8654->timer); + /* enable manual mode mode */ - error = i2c_smbus_write_byte(client, CMD_MANUAL); + error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual); if (error) { dev_err(&client->dev, "writing command CMD_MANUAL failed"); return; } - error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL); if (error) { dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); return; @@ -219,6 +332,20 @@ static int sx8654_probe(struct i2c_client *client, } dev_dbg(&client->dev, "got GPIO reset pin\n"); + sx8654->data = device_get_match_data(&client->dev); + if (!sx8654->data) + sx8654->data = (const struct sx865x_data *)id->driver_data; + if (!sx8654->data) { + dev_err(&client->dev, "invalid or missing device data\n"); + return -EINVAL; + } + + if (!sx8654->data->has_irq_penrelease) { + dev_dbg(&client->dev, "use timer for penrelease\n"); + timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0); + spin_lock_init(&sx8654->lock); + } + input = devm_input_allocate_device(&client->dev); if (!input) return -ENOMEM; @@ -246,29 +373,31 @@ static int sx8654_probe(struct i2c_client *client, } error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, - CONV_X | CONV_Y); + sx8654->data->chan_mask); if (error) { dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); return error; } - error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, - IRQ_PENTOUCH_TOUCHCONVDONE | - IRQ_PENRELEASE); - if (error) { - dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); - return error; + if (sx8654->data->has_reg_irqmask) { + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, + IRQ_PENTOUCH_TOUCHCONVDONE | + IRQ_PENRELEASE); + if (error) { + dev_err(&client->dev, "writing I2C_REG_IRQMASK failed"); + return error; + } } error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, - CONDIRQ | FILT_7SA); + CONDIRQ | RPDNT_100K | FILT_7SA); if (error) { dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); return error; } error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, sx8654_irq, + NULL, sx8654->data->irqh, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, sx8654); if (error) { @@ -288,21 +417,48 @@ static int sx8654_probe(struct i2c_client *client, return 0; } +static const struct sx865x_data sx8650_data = { + .cmd_manual = 0xb0, + .has_irq_penrelease = false, + .has_reg_irqmask = false, + .chan_mask = (CONV_X | CONV_Y), + .irqh = sx8650_irq, +}; + +static const struct sx865x_data sx8654_data = { + .cmd_manual = 0xc0, + .has_irq_penrelease = true, + .has_reg_irqmask = true, + .chan_mask = (CONV_X | CONV_Y), + .irqh = sx8654_irq, +}; + #ifdef CONFIG_OF static const struct of_device_id sx8654_of_match[] = { - { .compatible = "semtech,sx8654", }, - { .compatible = "semtech,sx8655", }, - { .compatible = "semtech,sx8656", }, - { }, + { + .compatible = "semtech,sx8650", + .data = &sx8650_data, + }, { + .compatible = "semtech,sx8654", + .data = &sx8654_data, + }, { + .compatible = "semtech,sx8655", + .data = &sx8654_data, + }, { + .compatible = "semtech,sx8656", + .data = &sx8654_data, + }, + { } }; MODULE_DEVICE_TABLE(of, sx8654_of_match); #endif static const struct i2c_device_id sx8654_id_table[] = { - { "semtech_sx8654", 0 }, - { "semtech_sx8655", 0 }, - { "semtech_sx8656", 0 }, - { }, + { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data }, + { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data }, + { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data }, + { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data }, + { } }; MODULE_DEVICE_TABLE(i2c, sx8654_id_table); From 4ec90ac5047e33f5d64e21c31046be2ff8aaaf4b Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Mon, 28 Jan 2019 16:14:16 -0800 Subject: [PATCH 0740/4277] Input: sx8654 - use common of_touchscreen functions of_touchscreen.c provides a common interface for a axis inversion and swapping of touchscreens. Therefore use it in the sx8654 driver. Signed-off-by: Richard Leitner Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 5f5af8eaecea..de83ed1da36e 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,8 @@ struct sx8654 { spinlock_t lock; /* for input reporting from irq/timer */ struct timer_list timer; + struct touchscreen_properties props; + const struct sx865x_data *data; }; @@ -177,8 +180,7 @@ static irqreturn_t sx8650_irq(int irq, void *handle) chdata); } - input_report_abs(ts->input, ABS_X, x); - input_report_abs(ts->input, ABS_Y, y); + touchscreen_report_pos(ts->input, &ts->props, x, y, false); input_report_key(ts->input, BTN_TOUCH, 1); input_sync(ts->input); dev_dbg(dev, "point(%4d,%4d)\n", x, y); @@ -225,8 +227,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle) x = ((data[0] & 0xf) << 8) | (data[1]); y = ((data[2] & 0xf) << 8) | (data[3]); - input_report_abs(sx8654->input, ABS_X, x); - input_report_abs(sx8654->input, ABS_Y, y); + touchscreen_report_pos(sx8654->input, &sx8654->props, x, y, + false); input_report_key(sx8654->input, BTN_TOUCH, 1); input_sync(sx8654->input); @@ -361,6 +363,8 @@ static int sx8654_probe(struct i2c_client *client, input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); + touchscreen_parse_properties(input, false, &sx8654->props); + sx8654->client = client; sx8654->input = input; From 573d8884f89b1e1d8cce3e17763b2e61b1cdb4fb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 15:27:35 +0100 Subject: [PATCH 0741/4277] scsi: fcoe: convert to use BUS_ATTR_WO We are trying to get rid of BUS_ATTR() and the usage of that in the fcoe driver can be trivially converted to use BUS_ATTR_WO(), so use that instead. Cc: Johannes Thumshirn Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Signed-off-by: Greg Kroah-Hartman Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/fcoe/fcoe_sysfs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index 5c8310bade61..c3dcbdc3aa64 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -671,8 +671,19 @@ static const struct device_type fcoe_fcf_device_type = { .release = fcoe_fcf_device_release, }; -static BUS_ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store); -static BUS_ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store); +static ssize_t ctlr_create_store(struct bus_type *bus, const char *buf, + size_t count) +{ + return fcoe_ctlr_create_store(bus, buf, count); +} +static BUS_ATTR_WO(ctlr_create); + +static ssize_t ctlr_destroy_store(struct bus_type *bus, const char *buf, + size_t count) +{ + return fcoe_ctlr_destroy_store(bus, buf, count); +} +static BUS_ATTR_WO(ctlr_destroy); static struct attribute *fcoe_bus_attrs[] = { &bus_attr_ctlr_create.attr, From 6156dc1c9a3074dea0f24dd9c80711392147b58f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 15:28:18 +0100 Subject: [PATCH 0742/4277] scsi: fcoe: remove unneeded fcoe_ctlr_destroy_store export There's no need to export fcoe_ctlr_destroy_store as a symbol, so remove the EXPORT_SYMBOL() line for it. Cc: Johannes Thumshirn Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Signed-off-by: Greg Kroah-Hartman Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/fcoe/fcoe_transport.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index f4909cd206d3..6d3949b687ef 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -855,7 +855,6 @@ out_nodev: mutex_unlock(&ft_mutex); return rc; } -EXPORT_SYMBOL(fcoe_ctlr_destroy_store); /** * fcoe_transport_create() - Create a fcoe interface From b8432724b6b1383df74a822f2b36c08f2fb49436 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 28 Jan 2019 12:06:34 +0100 Subject: [PATCH 0743/4277] scsi: MAINTAINERS: Move FCoE to Hannes Reinecke I'll be moving on to different things in the storage stack and Hannes agreed to take over FCoE. Cc: Hannes Reinecke Signed-off-by: Johannes Thumshirn Acked-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..70c93b141393 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5852,7 +5852,7 @@ S: Maintained F: drivers/media/tuners/fc2580* FCOE SUBSYSTEM (libfc, libfcoe, fcoe) -M: Johannes Thumshirn +M: Hannes Reinecke L: linux-scsi@vger.kernel.org W: www.Open-FCoE.org S: Supported From 8389f1281cd5cc7cc95218516800f42dddfd9111 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:00 +0100 Subject: [PATCH 0744/4277] scsi: bfa: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. [mkp: removed unused label] Cc: Anil Gurumurthy Cc: Sudarsana Kalluru Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/bfa/bfad_debugfs.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c index 349cfe7d055e..bfcd87c0dc74 100644 --- a/drivers/scsi/bfa/bfad_debugfs.c +++ b/drivers/scsi/bfa/bfad_debugfs.c @@ -460,11 +460,6 @@ bfad_debugfs_init(struct bfad_port_s *port) if (!bfa_debugfs_root) { bfa_debugfs_root = debugfs_create_dir("bfa", NULL); atomic_set(&bfa_debugfs_port_count, 0); - if (!bfa_debugfs_root) { - printk(KERN_WARNING - "BFA debugfs root dir creation failed\n"); - goto err; - } } /* Setup the pci_dev debugfs directory for the port */ @@ -472,12 +467,6 @@ bfad_debugfs_init(struct bfad_port_s *port) if (!port->port_debugfs_root) { port->port_debugfs_root = debugfs_create_dir(name, bfa_debugfs_root); - if (!port->port_debugfs_root) { - printk(KERN_WARNING - "bfa %s: debugfs root creation failed\n", - bfad->pci_name); - goto err; - } atomic_inc(&bfa_debugfs_port_count); @@ -489,16 +478,9 @@ bfad_debugfs_init(struct bfad_port_s *port) port->port_debugfs_root, port, file->fops); - if (!bfad->bfad_dentry_files[i]) { - printk(KERN_WARNING - "bfa %s: debugfs %s creation failed\n", - bfad->pci_name, file->name); - goto err; - } } } -err: return; } From b6533640ed3e25e9a12497dbc679a10896848c91 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:01 +0100 Subject: [PATCH 0745/4277] scsi: csiostor: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: Varun Prakash Cc: Bjorn Helgaas Cc: Johannes Thumshirn Cc: Oza Pawandeep Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/csiostor/csio_init.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c index cf629380a981..460e4ee1c8fe 100644 --- a/drivers/scsi/csiostor/csio_init.c +++ b/drivers/scsi/csiostor/csio_init.c @@ -167,14 +167,10 @@ csio_dfs_destroy(struct csio_hw *hw) * csio_dfs_init - Debug filesystem initialization for the module. * */ -static int +static void csio_dfs_init(void) { csio_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (!csio_debugfs_root) - pr_warn("Could not create debugfs entry, continuing\n"); - - return 0; } /* From 1dbaa379a419343dab5f99f253d8225b5ab4f939 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:02 +0100 Subject: [PATCH 0746/4277] scsi: fnic: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Satish Kharat Cc: Sesidhar Baddela Cc: Karan Tilak Kumar Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Acked-by: Satish Kharat Signed-off-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/fnic_debugfs.c | 88 ++------------------------------ drivers/scsi/fnic/fnic_main.c | 7 +-- drivers/scsi/fnic/fnic_stats.h | 2 +- drivers/scsi/fnic/fnic_trace.c | 17 ++---- drivers/scsi/fnic/fnic_trace.h | 4 +- 5 files changed, 10 insertions(+), 108 deletions(-) diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c index 139fffa3658a..21991c99db7c 100644 --- a/drivers/scsi/fnic/fnic_debugfs.c +++ b/drivers/scsi/fnic/fnic_debugfs.c @@ -54,23 +54,9 @@ int fnic_debugfs_init(void) { int rc = -1; fnic_trace_debugfs_root = debugfs_create_dir("fnic", NULL); - if (!fnic_trace_debugfs_root) { - printk(KERN_DEBUG "Cannot create debugfs root\n"); - return rc; - } - - if (!fnic_trace_debugfs_root) { - printk(KERN_DEBUG - "fnic root directory doesn't exist in debugfs\n"); - return rc; - } fnic_stats_debugfs_root = debugfs_create_dir("statistics", fnic_trace_debugfs_root); - if (!fnic_stats_debugfs_root) { - printk(KERN_DEBUG "Cannot create Statistics directory\n"); - return rc; - } /* Allocate memory to structure */ fc_trc_flag = (struct fc_trace_flag_type *) @@ -356,39 +342,19 @@ static const struct file_operations fnic_trace_debugfs_fops = { * it will also create file trace_enable to control enable/disable of * trace logging into trace buffer. */ -int fnic_trace_debugfs_init(void) +void fnic_trace_debugfs_init(void) { - int rc = -1; - if (!fnic_trace_debugfs_root) { - printk(KERN_DEBUG - "FNIC Debugfs root directory doesn't exist\n"); - return rc; - } fnic_trace_enable = debugfs_create_file("tracing_enable", S_IFREG|S_IRUGO|S_IWUSR, fnic_trace_debugfs_root, &(fc_trc_flag->fnic_trace), &fnic_trace_ctrl_fops); - if (!fnic_trace_enable) { - printk(KERN_DEBUG - "Cannot create trace_enable file under debugfs\n"); - return rc; - } - fnic_trace_debugfs_file = debugfs_create_file("trace", S_IFREG|S_IRUGO|S_IWUSR, fnic_trace_debugfs_root, &(fc_trc_flag->fnic_trace), &fnic_trace_debugfs_fops); - - if (!fnic_trace_debugfs_file) { - printk(KERN_DEBUG - "Cannot create trace file under debugfs\n"); - return rc; - } - rc = 0; - return rc; } /* @@ -419,37 +385,20 @@ void fnic_trace_debugfs_terminate(void) * trace logging into trace buffer. */ -int fnic_fc_trace_debugfs_init(void) +void fnic_fc_trace_debugfs_init(void) { - int rc = -1; - - if (!fnic_trace_debugfs_root) { - pr_err("fnic:Debugfs root directory doesn't exist\n"); - return rc; - } - fnic_fc_trace_enable = debugfs_create_file("fc_trace_enable", S_IFREG|S_IRUGO|S_IWUSR, fnic_trace_debugfs_root, &(fc_trc_flag->fc_trace), &fnic_trace_ctrl_fops); - if (!fnic_fc_trace_enable) { - pr_err("fnic: Failed create fc_trace_enable file\n"); - return rc; - } - fnic_fc_trace_clear = debugfs_create_file("fc_trace_clear", S_IFREG|S_IRUGO|S_IWUSR, fnic_trace_debugfs_root, &(fc_trc_flag->fc_clear), &fnic_trace_ctrl_fops); - if (!fnic_fc_trace_clear) { - pr_err("fnic: Failed to create fc_trace_enable file\n"); - return rc; - } - fnic_fc_rdata_trace_debugfs_file = debugfs_create_file("fc_trace_rdata", S_IFREG|S_IRUGO|S_IWUSR, @@ -457,24 +406,12 @@ int fnic_fc_trace_debugfs_init(void) &(fc_trc_flag->fc_normal_file), &fnic_trace_debugfs_fops); - if (!fnic_fc_rdata_trace_debugfs_file) { - pr_err("fnic: Failed create fc_rdata_trace file\n"); - return rc; - } - fnic_fc_trace_debugfs_file = debugfs_create_file("fc_trace", S_IFREG|S_IRUGO|S_IWUSR, fnic_trace_debugfs_root, &(fc_trc_flag->fc_row_file), &fnic_trace_debugfs_fops); - - if (!fnic_fc_trace_debugfs_file) { - pr_err("fnic: Failed to create fc_trace file\n"); - return rc; - } - rc = 0; - return rc; } /* @@ -757,45 +694,26 @@ static const struct file_operations fnic_reset_debugfs_fops = { * It will create file stats and reset_stats under statistics/host# directory * to log per fnic stats. */ -int fnic_stats_debugfs_init(struct fnic *fnic) +void fnic_stats_debugfs_init(struct fnic *fnic) { - int rc = -1; char name[16]; snprintf(name, sizeof(name), "host%d", fnic->lport->host->host_no); - if (!fnic_stats_debugfs_root) { - printk(KERN_DEBUG "fnic_stats root doesn't exist\n"); - return rc; - } fnic->fnic_stats_debugfs_host = debugfs_create_dir(name, fnic_stats_debugfs_root); - if (!fnic->fnic_stats_debugfs_host) { - printk(KERN_DEBUG "Cannot create host directory\n"); - return rc; - } fnic->fnic_stats_debugfs_file = debugfs_create_file("stats", S_IFREG|S_IRUGO|S_IWUSR, fnic->fnic_stats_debugfs_host, fnic, &fnic_stats_debugfs_fops); - if (!fnic->fnic_stats_debugfs_file) { - printk(KERN_DEBUG "Cannot create host stats file\n"); - return rc; - } fnic->fnic_reset_debugfs_file = debugfs_create_file("reset_stats", S_IFREG|S_IRUGO|S_IWUSR, fnic->fnic_stats_debugfs_host, fnic, &fnic_reset_debugfs_fops); - if (!fnic->fnic_reset_debugfs_file) { - printk(KERN_DEBUG "Cannot create host stats file\n"); - return rc; - } - rc = 0; - return rc; } /* diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 0f42114be2d1..18584ab27c32 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -586,12 +586,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) host->transportt = fnic_fc_transport; - err = fnic_stats_debugfs_init(fnic); - if (err) { - shost_printk(KERN_ERR, fnic->lport->host, - "Failed to initialize debugfs for stats\n"); - fnic_stats_debugfs_remove(fnic); - } + fnic_stats_debugfs_init(fnic); /* Setup PCI resources */ pci_set_drvdata(pdev, fnic); diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h index 1b1006a882e9..086f729f3c46 100644 --- a/drivers/scsi/fnic/fnic_stats.h +++ b/drivers/scsi/fnic/fnic_stats.h @@ -138,6 +138,6 @@ struct stats_debug_info { }; int fnic_get_stats_data(struct stats_debug_info *, struct fnic_stats *); -int fnic_stats_debugfs_init(struct fnic *); +void fnic_stats_debugfs_init(struct fnic *); void fnic_stats_debugfs_remove(struct fnic *); #endif /* _FNIC_STATS_H_ */ diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c index ce32380a182e..9621831e17ba 100644 --- a/drivers/scsi/fnic/fnic_trace.c +++ b/drivers/scsi/fnic/fnic_trace.c @@ -514,15 +514,10 @@ int fnic_trace_buf_init(void) fnic_trace_entries.page_offset[i] = fnic_buf_head; fnic_buf_head += FNIC_ENTRY_SIZE_BYTES; } - err = fnic_trace_debugfs_init(); - if (err < 0) { - pr_err("fnic: Failed to initialize debugfs for tracing\n"); - goto err_fnic_trace_debugfs_init; - } + fnic_trace_debugfs_init(); pr_info("fnic: Successfully Initialized Trace Buffer\n"); return err; -err_fnic_trace_debugfs_init: - fnic_trace_free(); + err_fnic_trace_buf_init: return err; } @@ -607,16 +602,10 @@ int fnic_fc_trace_init(void) fc_trace_entries.page_offset[i] = fc_trace_buf_head; fc_trace_buf_head += FC_TRC_SIZE_BYTES; } - err = fnic_fc_trace_debugfs_init(); - if (err < 0) { - pr_err("fnic: Failed to initialize FC_CTLR tracing.\n"); - goto err_fnic_fc_ctlr_trace_debugfs_init; - } + fnic_fc_trace_debugfs_init(); pr_info("fnic: Successfully Initialized FC_CTLR Trace Buffer\n"); return err; -err_fnic_fc_ctlr_trace_debugfs_init: - fnic_fc_trace_free(); err_fnic_fc_ctlr_trace_buf_init: return err; } diff --git a/drivers/scsi/fnic/fnic_trace.h b/drivers/scsi/fnic/fnic_trace.h index e375d0c2eaaf..8aa55c1e2025 100644 --- a/drivers/scsi/fnic/fnic_trace.h +++ b/drivers/scsi/fnic/fnic_trace.h @@ -111,7 +111,7 @@ int fnic_trace_buf_init(void); void fnic_trace_free(void); int fnic_debugfs_init(void); void fnic_debugfs_terminate(void); -int fnic_trace_debugfs_init(void); +void fnic_trace_debugfs_init(void); void fnic_trace_debugfs_terminate(void); /* Fnic FC CTLR Trace releated function */ @@ -123,7 +123,7 @@ int fnic_fc_trace_get_data(fnic_dbgfs_t *fnic_dbgfs_prt, u8 rdata_flag); void copy_and_format_trace_data(struct fc_trace_hdr *tdata, fnic_dbgfs_t *fnic_dbgfs_prt, int *len, u8 rdata_flag); -int fnic_fc_trace_debugfs_init(void); +void fnic_fc_trace_debugfs_init(void); void fnic_fc_trace_debugfs_terminate(void); #endif From fd84ec206d7a0d5a2aa0c3c78868deec6bfcd050 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:03 +0100 Subject: [PATCH 0747/4277] scsi: snic: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Karan Tilak Kumar Cc: Sesidhar Baddela Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/snic/snic_debugfs.c | 131 ++++++------------------------- drivers/scsi/snic/snic_main.c | 14 +--- drivers/scsi/snic/snic_stats.h | 2 +- drivers/scsi/snic/snic_trc.c | 12 +-- drivers/scsi/snic/snic_trc.h | 4 +- 5 files changed, 31 insertions(+), 132 deletions(-) diff --git a/drivers/scsi/snic/snic_debugfs.c b/drivers/scsi/snic/snic_debugfs.c index 0abe17c1a73b..2b349365592f 100644 --- a/drivers/scsi/snic/snic_debugfs.c +++ b/drivers/scsi/snic/snic_debugfs.c @@ -30,33 +30,13 @@ * fnic directory and statistics directory for trace buffer and * stats logging */ - -int -snic_debugfs_init(void) +void snic_debugfs_init(void) { - int rc = -1; - struct dentry *de = NULL; + snic_glob->trc_root = debugfs_create_dir("snic", NULL); - de = debugfs_create_dir("snic", NULL); - if (!de) { - SNIC_DBG("Cannot create debugfs root\n"); - - return rc; - } - snic_glob->trc_root = de; - - de = debugfs_create_dir("statistics", snic_glob->trc_root); - if (!de) { - SNIC_DBG("Cannot create Statistics directory\n"); - - return rc; - } - snic_glob->stats_root = de; - - rc = 0; - - return rc; -} /* end of snic_debugfs_init */ + snic_glob->stats_root = debugfs_create_dir("statistics", + snic_glob->trc_root); +} /* * snic_debugfs_term - Tear down debugfs intrastructure @@ -391,56 +371,23 @@ static const struct file_operations snic_reset_stats_fops = { * It will create file stats and reset_stats under statistics/host# directory * to log per snic stats */ -int -snic_stats_debugfs_init(struct snic *snic) +void snic_stats_debugfs_init(struct snic *snic) { - int rc = -1; char name[16]; - struct dentry *de = NULL; snprintf(name, sizeof(name), "host%d", snic->shost->host_no); - if (!snic_glob->stats_root) { - SNIC_DBG("snic_stats root doesn't exist\n"); - return rc; - } + snic->stats_host = debugfs_create_dir(name, snic_glob->stats_root); - de = debugfs_create_dir(name, snic_glob->stats_root); - if (!de) { - SNIC_DBG("Cannot create host directory\n"); + snic->stats_file = debugfs_create_file("stats", S_IFREG|S_IRUGO, + snic->stats_host, snic, + &snic_stats_fops); - return rc; - } - snic->stats_host = de; - - de = debugfs_create_file("stats", - S_IFREG|S_IRUGO, - snic->stats_host, - snic, - &snic_stats_fops); - if (!de) { - SNIC_DBG("Cannot create host's stats file\n"); - - return rc; - } - snic->stats_file = de; - - de = debugfs_create_file("reset_stats", - S_IFREG|S_IRUGO|S_IWUSR, - snic->stats_host, - snic, - &snic_reset_stats_fops); - - if (!de) { - SNIC_DBG("Cannot create host's reset_stats file\n"); - - return rc; - } - snic->reset_stats_file = de; - rc = 0; - - return rc; -} /* end of snic_stats_debugfs_init */ + snic->reset_stats_file = debugfs_create_file("reset_stats", + S_IFREG|S_IRUGO|S_IWUSR, + snic->stats_host, snic, + &snic_reset_stats_fops); +} /* * snic_stats_debugfs_remove - Tear down debugfs infrastructure of stats @@ -517,46 +464,18 @@ static const struct file_operations snic_trc_fops = { * snic_trc_debugfs_init : creates trace/tracing_enable files for trace * under debugfs */ -int -snic_trc_debugfs_init(void) +void snic_trc_debugfs_init(void) { - struct dentry *de = NULL; - int ret = -1; + snic_glob->trc.trc_enable = debugfs_create_bool("tracing_enable", + S_IFREG | S_IRUGO | S_IWUSR, + snic_glob->trc_root, + &snic_glob->trc.enable); - if (!snic_glob->trc_root) { - SNIC_ERR("Debugfs root directory for snic doesn't exist.\n"); - - return ret; - } - - de = debugfs_create_bool("tracing_enable", - S_IFREG | S_IRUGO | S_IWUSR, - snic_glob->trc_root, - &snic_glob->trc.enable); - - if (!de) { - SNIC_ERR("Can't create trace_enable file.\n"); - - return ret; - } - snic_glob->trc.trc_enable = de; - - de = debugfs_create_file("trace", - S_IFREG | S_IRUGO | S_IWUSR, - snic_glob->trc_root, - NULL, - &snic_trc_fops); - - if (!de) { - SNIC_ERR("Cannot create trace file.\n"); - - return ret; - } - snic_glob->trc.trc_file = de; - ret = 0; - - return ret; -} /* end of snic_trc_debugfs_init */ + snic_glob->trc.trc_file = debugfs_create_file("trace", + S_IFREG | S_IRUGO | S_IWUSR, + snic_glob->trc_root, NULL, + &snic_trc_fops); +} /* * snic_trc_debugfs_term : cleans up the files created for trace under debugfs diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c index 5e824fd6047a..14f4ce665e58 100644 --- a/drivers/scsi/snic/snic_main.c +++ b/drivers/scsi/snic/snic_main.c @@ -397,12 +397,7 @@ snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); #ifdef CONFIG_SCSI_SNIC_DEBUG_FS /* Per snic debugfs init */ - ret = snic_stats_debugfs_init(snic); - if (ret) { - SNIC_HOST_ERR(snic->shost, - "Failed to initialize debugfs stats\n"); - snic_stats_debugfs_remove(snic); - } + snic_stats_debugfs_init(snic); #endif /* Setup PCI Resources */ @@ -850,12 +845,7 @@ snic_global_data_init(void) #ifdef CONFIG_SCSI_SNIC_DEBUG_FS /* Debugfs related Initialization */ /* Create debugfs entries for snic */ - ret = snic_debugfs_init(); - if (ret < 0) { - SNIC_ERR("Failed to create sysfs dir for tracing and stats.\n"); - snic_debugfs_term(); - /* continue even if it fails */ - } + snic_debugfs_init(); /* Trace related Initialization */ /* Allocate memory for trace buffer */ diff --git a/drivers/scsi/snic/snic_stats.h b/drivers/scsi/snic/snic_stats.h index fd1066b1cad5..faf0cb601954 100644 --- a/drivers/scsi/snic/snic_stats.h +++ b/drivers/scsi/snic/snic_stats.h @@ -99,7 +99,7 @@ struct snic_stats { atomic64_t io_cmpl_skip; }; -int snic_stats_debugfs_init(struct snic *); +void snic_stats_debugfs_init(struct snic *); void snic_stats_debugfs_remove(struct snic *); /* Auxillary function to update active IO counter */ diff --git a/drivers/scsi/snic/snic_trc.c b/drivers/scsi/snic/snic_trc.c index 458eaba24c78..f23fe2f88438 100644 --- a/drivers/scsi/snic/snic_trc.c +++ b/drivers/scsi/snic/snic_trc.c @@ -138,12 +138,7 @@ snic_trc_init(void) trc->buf = (struct snic_trc_data *) tbuf; spin_lock_init(&trc->lock); - ret = snic_trc_debugfs_init(); - if (ret) { - SNIC_ERR("Failed to create Debugfs Files.\n"); - - goto error; - } + snic_trc_debugfs_init(); trc->max_idx = (tbuf_sz / SNIC_TRC_ENTRY_SZ); trc->rd_idx = trc->wr_idx = 0; @@ -152,11 +147,6 @@ snic_trc_init(void) tbuf_sz / PAGE_SIZE); ret = 0; - return ret; - -error: - snic_trc_free(); - return ret; } /* end of snic_trc_init */ diff --git a/drivers/scsi/snic/snic_trc.h b/drivers/scsi/snic/snic_trc.h index b37f8867bfde..87dcc7457d15 100644 --- a/drivers/scsi/snic/snic_trc.h +++ b/drivers/scsi/snic/snic_trc.h @@ -53,12 +53,12 @@ struct snic_trc { int snic_trc_init(void); void snic_trc_free(void); -int snic_trc_debugfs_init(void); +void snic_trc_debugfs_init(void); void snic_trc_debugfs_term(void); struct snic_trc_data *snic_get_trc_buf(void); int snic_get_trc_data(char *buf, int buf_sz); -int snic_debugfs_init(void); +void snic_debugfs_init(void); void snic_debugfs_term(void); static inline void From 50e931679ace9cf1c2915f664f29f52a203d8687 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:04 +0100 Subject: [PATCH 0748/4277] scsi: lpfc: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: James Smart Cc: Dick Kennedy Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_debugfs.c | 170 ------------------------------- 1 file changed, 170 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index a58f0b3f03a9..a851dee09587 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -5280,11 +5280,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) if (!lpfc_debugfs_root) { lpfc_debugfs_root = debugfs_create_dir("lpfc", NULL); atomic_set(&lpfc_debugfs_hba_count, 0); - if (!lpfc_debugfs_root) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0408 Cannot create debugfs root\n"); - goto debug_failed; - } } if (!lpfc_debugfs_start_time) lpfc_debugfs_start_time = jiffies; @@ -5295,11 +5290,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) pport_setup = true; phba->hba_debugfs_root = debugfs_create_dir(name, lpfc_debugfs_root); - if (!phba->hba_debugfs_root) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0412 Cannot create debugfs hba\n"); - goto debug_failed; - } atomic_inc(&lpfc_debugfs_hba_count); atomic_set(&phba->debugfs_vport_count, 0); @@ -5309,11 +5299,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_hbqinfo); - if (!phba->debug_hbqinfo) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0411 Cannot create debugfs hbqinfo\n"); - goto debug_failed; - } /* Setup dumpHBASlim */ if (phba->sli_rev < LPFC_SLI_REV4) { @@ -5323,12 +5308,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dumpHBASlim); - if (!phba->debug_dumpHBASlim) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0413 Cannot create debugfs " - "dumpHBASlim\n"); - goto debug_failed; - } } else phba->debug_dumpHBASlim = NULL; @@ -5340,12 +5319,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dumpHostSlim); - if (!phba->debug_dumpHostSlim) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0414 Cannot create debugfs " - "dumpHostSlim\n"); - goto debug_failed; - } } else phba->debug_dumpHostSlim = NULL; @@ -5355,11 +5328,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dumpData); - if (!phba->debug_dumpData) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0800 Cannot create debugfs dumpData\n"); - goto debug_failed; - } /* Setup dumpDif */ snprintf(name, sizeof(name), "dumpDif"); @@ -5367,11 +5335,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dumpDif); - if (!phba->debug_dumpDif) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0801 Cannot create debugfs dumpDif\n"); - goto debug_failed; - } /* Setup DIF Error Injections */ snprintf(name, sizeof(name), "InjErrLBA"); @@ -5379,11 +5342,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_InjErrLBA) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0807 Cannot create debugfs InjErrLBA\n"); - goto debug_failed; - } phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; snprintf(name, sizeof(name), "InjErrNPortID"); @@ -5391,88 +5349,48 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_InjErrNPortID) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0809 Cannot create debugfs InjErrNPortID\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "InjErrWWPN"); phba->debug_InjErrWWPN = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_InjErrWWPN) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0810 Cannot create debugfs InjErrWWPN\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "writeGuardInjErr"); phba->debug_writeGuard = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_writeGuard) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0802 Cannot create debugfs writeGuard\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "writeAppInjErr"); phba->debug_writeApp = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_writeApp) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0803 Cannot create debugfs writeApp\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "writeRefInjErr"); phba->debug_writeRef = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_writeRef) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0804 Cannot create debugfs writeRef\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "readGuardInjErr"); phba->debug_readGuard = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_readGuard) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0808 Cannot create debugfs readGuard\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "readAppInjErr"); phba->debug_readApp = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_readApp) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0805 Cannot create debugfs readApp\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "readRefInjErr"); phba->debug_readRef = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_dif_err); - if (!phba->debug_readRef) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0806 Cannot create debugfs readApp\n"); - goto debug_failed; - } /* Setup slow ring trace */ if (lpfc_debugfs_max_slow_ring_trc) { @@ -5496,12 +5414,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_slow_ring_trc); - if (!phba->debug_slow_ring_trc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0415 Cannot create debugfs " - "slow_ring_trace\n"); - goto debug_failed; - } if (!phba->slow_ring_trc) { phba->slow_ring_trc = kmalloc( (sizeof(struct lpfc_debugfs_trc) * @@ -5524,11 +5436,6 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, 0644, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_nvmeio_trc); - if (!phba->debug_nvmeio_trc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0574 No create debugfs nvmeio_trc\n"); - goto debug_failed; - } atomic_set(&phba->nvmeio_trc_cnt, 0); if (lpfc_debugfs_max_nvmeio_trc) { @@ -5576,11 +5483,6 @@ nvmeio_off: if (!vport->vport_debugfs_root) { vport->vport_debugfs_root = debugfs_create_dir(name, phba->hba_debugfs_root); - if (!vport->vport_debugfs_root) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0417 Can't create debugfs\n"); - goto debug_failed; - } atomic_inc(&phba->debugfs_vport_count); } @@ -5617,55 +5519,29 @@ nvmeio_off: debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_disc_trc); - if (!vport->debug_disc_trc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0419 Cannot create debugfs " - "discovery_trace\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "nodelist"); vport->debug_nodelist = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_nodelist); - if (!vport->debug_nodelist) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2985 Can't create debugfs nodelist\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "nvmestat"); vport->debug_nvmestat = debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_nvmestat); - if (!vport->debug_nvmestat) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0811 Cannot create debugfs nvmestat\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "nvmektime"); vport->debug_nvmektime = debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_nvmektime); - if (!vport->debug_nvmektime) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0815 Cannot create debugfs nvmektime\n"); - goto debug_failed; - } snprintf(name, sizeof(name), "cpucheck"); vport->debug_cpucheck = debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_cpucheck); - if (!vport->debug_cpucheck) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0819 Cannot create debugfs cpucheck\n"); - goto debug_failed; - } /* * The following section is for additional directories/files for the @@ -5685,11 +5561,6 @@ nvmeio_off: if (!phba->idiag_root) { phba->idiag_root = debugfs_create_dir(name, phba->hba_debugfs_root); - if (!phba->idiag_root) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2922 Can't create idiag debugfs\n"); - goto debug_failed; - } /* Initialize iDiag data structure */ memset(&idiag, 0, sizeof(idiag)); } @@ -5700,11 +5571,6 @@ nvmeio_off: phba->idiag_pci_cfg = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_pciCfg); - if (!phba->idiag_pci_cfg) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2923 Can't create idiag debugfs\n"); - goto debug_failed; - } idiag.offset.last_rd = 0; } @@ -5714,11 +5580,6 @@ nvmeio_off: phba->idiag_bar_acc = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_barAcc); - if (!phba->idiag_bar_acc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "3056 Can't create idiag debugfs\n"); - goto debug_failed; - } idiag.offset.last_rd = 0; } @@ -5728,11 +5589,6 @@ nvmeio_off: phba->idiag_que_info = debugfs_create_file(name, S_IFREG|S_IRUGO, phba->idiag_root, phba, &lpfc_idiag_op_queInfo); - if (!phba->idiag_que_info) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2924 Can't create idiag debugfs\n"); - goto debug_failed; - } } /* iDiag access PCI function queue */ @@ -5741,11 +5597,6 @@ nvmeio_off: phba->idiag_que_acc = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_queAcc); - if (!phba->idiag_que_acc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2926 Can't create idiag debugfs\n"); - goto debug_failed; - } } /* iDiag access PCI function doorbell registers */ @@ -5754,11 +5605,6 @@ nvmeio_off: phba->idiag_drb_acc = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_drbAcc); - if (!phba->idiag_drb_acc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2927 Can't create idiag debugfs\n"); - goto debug_failed; - } } /* iDiag access PCI function control registers */ @@ -5767,11 +5613,6 @@ nvmeio_off: phba->idiag_ctl_acc = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_ctlAcc); - if (!phba->idiag_ctl_acc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2981 Can't create idiag debugfs\n"); - goto debug_failed; - } } /* iDiag access mbox commands */ @@ -5780,11 +5621,6 @@ nvmeio_off: phba->idiag_mbx_acc = debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_mbxAcc); - if (!phba->idiag_mbx_acc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2980 Can't create idiag debugfs\n"); - goto debug_failed; - } } /* iDiag extents access commands */ @@ -5796,12 +5632,6 @@ nvmeio_off: S_IFREG|S_IRUGO|S_IWUSR, phba->idiag_root, phba, &lpfc_idiag_op_extAcc); - if (!phba->idiag_ext_acc) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "2986 Cant create " - "idiag debugfs\n"); - goto debug_failed; - } } } From 26febfb38c7d679e1f02dabcedc38f1817c0710b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:05 +0100 Subject: [PATCH 0749/4277] scsi: qedf: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: QLogic-Storage-Upstream@cavium.com Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Acked-by: Manish Rangankar Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_debugfs.c | 18 ++---------------- drivers/scsi/qedi/qedi_debugfs.c | 17 ++--------------- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c index c29c162a494f..a32d8ee4666e 100644 --- a/drivers/scsi/qedf/qedf_debugfs.c +++ b/drivers/scsi/qedf/qedf_debugfs.c @@ -27,30 +27,19 @@ qedf_dbg_host_init(struct qedf_dbg_ctx *qedf, const struct file_operations *fops) { char host_dirname[32]; - struct dentry *file_dentry = NULL; QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Creating debugfs host node\n"); /* create pf dir */ sprintf(host_dirname, "host%u", qedf->host_no); qedf->bdf_dentry = debugfs_create_dir(host_dirname, qedf_dbg_root); - if (!qedf->bdf_dentry) - return; /* create debugfs files */ while (dops) { if (!(dops->name)) break; - file_dentry = debugfs_create_file(dops->name, 0600, - qedf->bdf_dentry, qedf, - fops); - if (!file_dentry) { - QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, - "Debugfs entry %s creation failed\n", - dops->name); - debugfs_remove_recursive(qedf->bdf_dentry); - return; - } + debugfs_create_file(dops->name, 0600, qedf->bdf_dentry, qedf, + fops); dops++; fops++; } @@ -80,9 +69,6 @@ qedf_dbg_init(char *drv_name) /* create qed dir in root of debugfs. NULL means debugfs root */ qedf_dbg_root = debugfs_create_dir(drv_name, NULL); - if (!qedf_dbg_root) - QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Init of debugfs " - "failed\n"); } /** diff --git a/drivers/scsi/qedi/qedi_debugfs.c b/drivers/scsi/qedi/qedi_debugfs.c index fd914ca4149a..5667e4752e2e 100644 --- a/drivers/scsi/qedi/qedi_debugfs.c +++ b/drivers/scsi/qedi/qedi_debugfs.c @@ -23,27 +23,16 @@ qedi_dbg_host_init(struct qedi_dbg_ctx *qedi, const struct file_operations *fops) { char host_dirname[32]; - struct dentry *file_dentry = NULL; sprintf(host_dirname, "host%u", qedi->host_no); qedi->bdf_dentry = debugfs_create_dir(host_dirname, qedi_dbg_root); - if (!qedi->bdf_dentry) - return; while (dops) { if (!(dops->name)) break; - file_dentry = debugfs_create_file(dops->name, 0600, - qedi->bdf_dentry, qedi, - fops); - if (!file_dentry) { - QEDI_INFO(qedi, QEDI_LOG_DEBUGFS, - "Debugfs entry %s creation failed\n", - dops->name); - debugfs_remove_recursive(qedi->bdf_dentry); - return; - } + debugfs_create_file(dops->name, 0600, qedi->bdf_dentry, qedi, + fops); dops++; fops++; } @@ -60,8 +49,6 @@ void qedi_dbg_init(char *drv_name) { qedi_dbg_root = debugfs_create_dir(drv_name, NULL); - if (!qedi_dbg_root) - QEDI_INFO(NULL, QEDI_LOG_DEBUGFS, "Init of debugfs failed\n"); } void From b45a3a428f559082418b182d35fc20a49b647c75 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:09:06 +0100 Subject: [PATCH 0750/4277] scsi: qla2xxx: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: qla2xxx-upstream@qlogic.com Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_dfs.c | 43 +--------------------------------- 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 0b190082aa8d..ead17288e2a7 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -446,11 +446,6 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha) atomic_set(&qla2x00_dfs_root_count, 0); qla2x00_dfs_root = debugfs_create_dir(QLA2XXX_DRIVER_NAME, NULL); - if (!qla2x00_dfs_root) { - ql_log(ql_log_warn, vha, 0x00f7, - "Unable to create debugfs root directory.\n"); - goto out; - } create_dir: if (ha->dfs_dir) @@ -458,64 +453,28 @@ create_dir: mutex_init(&ha->fce_mutex); ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root); - if (!ha->dfs_dir) { - ql_log(ql_log_warn, vha, 0x00f8, - "Unable to create debugfs ha directory.\n"); - goto out; - } atomic_inc(&qla2x00_dfs_root_count); create_nodes: ha->dfs_fw_resource_cnt = debugfs_create_file("fw_resource_count", S_IRUSR, ha->dfs_dir, vha, &dfs_fw_resource_cnt_ops); - if (!ha->dfs_fw_resource_cnt) { - ql_log(ql_log_warn, vha, 0x00fd, - "Unable to create debugFS fw_resource_count node.\n"); - goto out; - } ha->dfs_tgt_counters = debugfs_create_file("tgt_counters", S_IRUSR, ha->dfs_dir, vha, &dfs_tgt_counters_ops); - if (!ha->dfs_tgt_counters) { - ql_log(ql_log_warn, vha, 0xd301, - "Unable to create debugFS tgt_counters node.\n"); - goto out; - } ha->tgt.dfs_tgt_port_database = debugfs_create_file("tgt_port_database", S_IRUSR, ha->dfs_dir, vha, &dfs_tgt_port_database_ops); - if (!ha->tgt.dfs_tgt_port_database) { - ql_log(ql_log_warn, vha, 0xd03f, - "Unable to create debugFS tgt_port_database node.\n"); - goto out; - } ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha, &dfs_fce_ops); - if (!ha->dfs_fce) { - ql_log(ql_log_warn, vha, 0x00f9, - "Unable to create debugfs fce node.\n"); - goto out; - } ha->tgt.dfs_tgt_sess = debugfs_create_file("tgt_sess", S_IRUSR, ha->dfs_dir, vha, &dfs_tgt_sess_ops); - if (!ha->tgt.dfs_tgt_sess) { - ql_log(ql_log_warn, vha, 0xd040, - "Unable to create debugFS tgt_sess node.\n"); - goto out; - } - if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) ha->tgt.dfs_naqp = debugfs_create_file("naqp", 0400, ha->dfs_dir, vha, &dfs_naqp_ops); - if (!ha->tgt.dfs_naqp) { - ql_log(ql_log_warn, vha, 0xd011, - "Unable to create debugFS naqp node.\n"); - goto out; - } - } out: return 0; } From b64ae4abbdc4a5d56de8c97095a43182c10aa726 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 22 Jan 2019 15:18:30 +0000 Subject: [PATCH 0751/4277] scsi: hpsa: clean up two indentation issues There are two statements that are indented incorrectly. Fix these. Signed-off-by: Colin Ian King Acked-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index ff67ef5d5347..5284444fdd10 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -1327,7 +1327,7 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, dev_warn(&h->pdev->dev, "physical device with no LUN=0," " suspect firmware bug or unsupported hardware " "configuration.\n"); - return -1; + return -1; } lun_assigned: @@ -4110,7 +4110,7 @@ static int hpsa_gather_lun_info(struct ctlr_info *h, "maximum logical LUNs (%d) exceeded. " "%d LUNs ignored.\n", HPSA_MAX_LUN, *nlogicals - HPSA_MAX_LUN); - *nlogicals = HPSA_MAX_LUN; + *nlogicals = HPSA_MAX_LUN; } if (*nlogicals + *nphysicals > HPSA_MAX_PHYS_LUN) { dev_warn(&h->pdev->dev, From 59abc8cc05856cb31d6e07a8e25f0d749d559233 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 23 Jan 2019 11:08:05 -0800 Subject: [PATCH 0752/4277] scsi: libsas: Remove scsi_to_u32() Since the function scsi_to_u32() is identical to get_unaligned_be32(), change all scsi_to_u32() calls into get_unaligned_be32() calls. Cc: Jian Luo Cc: John Garry Signed-off-by: Bart Van Assche Reviewed-by: John Garry Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_expander.c | 9 +++++---- include/scsi/scsi.h | 6 ------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 83e3715d30e4..b13b245791b0 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "sas_internal.h" @@ -696,10 +697,10 @@ int sas_smp_get_phy_events(struct sas_phy *phy) if (res) goto out; - phy->invalid_dword_count = scsi_to_u32(&resp[12]); - phy->running_disparity_error_count = scsi_to_u32(&resp[16]); - phy->loss_of_dword_sync_count = scsi_to_u32(&resp[20]); - phy->phy_reset_problem_count = scsi_to_u32(&resp[24]); + phy->invalid_dword_count = get_unaligned_be32(&resp[12]); + phy->running_disparity_error_count = get_unaligned_be32(&resp[16]); + phy->loss_of_dword_sync_count = get_unaligned_be32(&resp[20]); + phy->phy_reset_problem_count = get_unaligned_be32(&resp[24]); out: kfree(req); diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index eb7853c1a23b..5339baadc082 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -274,10 +274,4 @@ static inline int scsi_is_wlun(u64 lun) /* Used to obtain the PCI location of a device */ #define SCSI_IOCTL_GET_PCI 0x5387 -/* Pull a u32 out of a SCSI message (using BE SCSI conventions) */ -static inline __u32 scsi_to_u32(__u8 *ptr) -{ - return (ptr[0]<<24) + (ptr[1]<<16) + (ptr[2]<<8) + ptr[3]; -} - #endif /* _SCSI_SCSI_H */ From db5db4b91cabcf57f3efd98d92d24ab875cde8ae Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 23 Jan 2019 11:12:37 -0800 Subject: [PATCH 0753/4277] scsi: sd: Protect against READ(6) or WRITE(6) with zero block transfer length Since the READ(6) and WRITE(6) commands interpret a zero in the transfer length field in the CDB as 256 logical blocks, avoid submitting such commands. Cc: Douglas Gilbert Cc: Hannes Reinecke Cc: Christoph Hellwig Reported-by: Douglas Gilbert Signed-off-by: Bart Van Assche Reviewed-by: Douglas Gilbert Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 7a1cf6c80f6a..4fbb8310e268 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1128,6 +1128,10 @@ static blk_status_t sd_setup_rw6_cmnd(struct scsi_cmnd *cmd, bool write, sector_t lba, unsigned int nr_blocks, unsigned char flags) { + /* Avoid that 0 blocks gets translated into 256 blocks. */ + if (WARN_ON_ONCE(nr_blocks == 0)) + return BLK_STS_IOERR; + if (unlikely(flags & 0x8)) { /* * This happens only if this drive failed 10byte rw From f2aecc1d38ff26f972d8c825ccc1f5152b79e203 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 25 Jan 2019 10:00:14 +0800 Subject: [PATCH 0754/4277] scsi: fnic: Remove set but not used variable 'vdev' Fixes gcc '-Wunused-but-set-variable' warning: drivers/scsi/fnic/vnic_wq.c: In function 'vnic_wq_alloc_bufs': drivers/scsi/fnic/vnic_wq.c:50:19: warning: variable 'vdev' set but not used [-Wunused-but-set-variable] drivers/scsi/fnic/vnic_rq.c: In function 'vnic_rq_alloc_bufs': drivers/scsi/fnic/vnic_rq.c:30:19: warning: variable 'vdev' set but not used [-Wunused-but-set-variable] Never used since introduction. Signed-off-by: YueHaibing Acked-by: Satish Kharat Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_rq.c | 3 --- drivers/scsi/fnic/vnic_wq.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c index 74d2f2cd6370..6a35b1be0032 100644 --- a/drivers/scsi/fnic/vnic_rq.c +++ b/drivers/scsi/fnic/vnic_rq.c @@ -27,12 +27,9 @@ static int vnic_rq_alloc_bufs(struct vnic_rq *rq) { struct vnic_rq_buf *buf; - struct vnic_dev *vdev; unsigned int i, j, count = rq->ring.desc_count; unsigned int blks = VNIC_RQ_BUF_BLKS_NEEDED(count); - vdev = rq->vdev; - for (i = 0; i < blks; i++) { rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ, GFP_ATOMIC); if (!rq->bufs[i]) { diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c index 74cfc567f657..015af2cdabaf 100644 --- a/drivers/scsi/fnic/vnic_wq.c +++ b/drivers/scsi/fnic/vnic_wq.c @@ -47,12 +47,9 @@ int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq, static int vnic_wq_alloc_bufs(struct vnic_wq *wq) { struct vnic_wq_buf *buf; - struct vnic_dev *vdev; unsigned int i, j, count = wq->ring.desc_count; unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count); - vdev = wq->vdev; - for (i = 0; i < blks; i++) { wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC); if (!wq->bufs[i]) { From 34022f0072e76c6d64c22f83dbe3652c00ba03d2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 25 Jan 2019 16:09:03 +0900 Subject: [PATCH 0755/4277] scsi: remove unneeded header search paths I was able to build without these extra header search paths. Especially, the header search path -I. in kernel Makefiles is always suspicious; it allows the compiler to search for headers in the top of $(srctree), where obviously no header file exists. Signed-off-by: Masahiro Yamada Signed-off-by: Martin K. Petersen --- drivers/scsi/aacraid/Makefile | 2 -- drivers/scsi/aic7xxx/Makefile | 1 - drivers/scsi/smartpqi/Makefile | 1 - 3 files changed, 4 deletions(-) diff --git a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile index 1bd9fd18f7f3..3893b95b140b 100644 --- a/drivers/scsi/aacraid/Makefile +++ b/drivers/scsi/aacraid/Makefile @@ -4,5 +4,3 @@ obj-$(CONFIG_SCSI_AACRAID) := aacraid.o aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \ dpcsup.o rx.o sa.o rkt.o nark.o src.o - -ccflags-y := -Idrivers/scsi diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile index c15be2590d1c..e0188ecd85b2 100644 --- a/drivers/scsi/aic7xxx/Makefile +++ b/drivers/scsi/aic7xxx/Makefile @@ -34,7 +34,6 @@ aic79xx-y += aic79xx_osm.o \ aic79xx_proc.o \ aic79xx_osm_pci.o -ccflags-y += -Idrivers/scsi ifdef WARNINGS_BECOME_ERRORS ccflags-y += -Werror endif diff --git a/drivers/scsi/smartpqi/Makefile b/drivers/scsi/smartpqi/Makefile index e6b779930230..a03a6edb0060 100644 --- a/drivers/scsi/smartpqi/Makefile +++ b/drivers/scsi/smartpqi/Makefile @@ -1,3 +1,2 @@ -ccflags-y += -I. obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi.o smartpqi-objs := smartpqi_init.o smartpqi_sis.o smartpqi_sas_transport.o From bc03abd28708d134e063d9a33c7a66400d6970f0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 25 Jan 2019 16:09:04 +0900 Subject: [PATCH 0756/4277] scsi: prefix header search paths with $(srctree)/ Currently, the Kbuild core manipulates header search paths in a crazy way [1]. To fix this mess, I want all Makefiles to add explicit $(srctree)/ to the search paths in the srctree. Some Makefiles are already written in that way, but not all. The goal of this work is to make the notation consistent, and finally get rid of the gross hacks. Having whitespaces after -I does not matter since commit 48f6e3cf5bc6 ("kbuild: do not drop -I without parameter"). [1]: https://patchwork.kernel.org/patch/9632347/ Signed-off-by: Masahiro Yamada Signed-off-by: Martin K. Petersen --- drivers/scsi/cxgbi/Makefile | 2 +- drivers/scsi/pcmcia/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxgbi/Makefile b/drivers/scsi/cxgbi/Makefile index a73781ac1800..f78c9cc460a2 100644 --- a/drivers/scsi/cxgbi/Makefile +++ b/drivers/scsi/cxgbi/Makefile @@ -1,4 +1,4 @@ -ccflags-y += -Idrivers/net/ethernet/chelsio/libcxgb +ccflags-y += -I $(srctree)/drivers/net/ethernet/chelsio/libcxgb obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libcxgbi.o cxgb3i/ obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libcxgbi.o cxgb4i/ diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile index faa87a4b2d2b..a5a24dd44e7e 100644 --- a/drivers/scsi/pcmcia/Makefile +++ b/drivers/scsi/pcmcia/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y := -Idrivers/scsi +ccflags-y := -I $(srctree)/drivers/scsi # 16-bit client drivers obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o From c9c1df3e8c9d323a4045ef3db72e388d40609bba Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 10 Dec 2018 16:51:56 -0700 Subject: [PATCH 0757/4277] scsi: nsp32: Remove unnecessary self assignment in nsp32_set_sync_entry Clang warns: drivers/scsi/nsp32.c:2444:14: warning: explicitly assigning value of variable of type 'unsigned char' to itself [-Wself-assign] offset = offset; ~~~~~~ ^ Signed-off-by: Nathan Chancellor Acked-by: GOTO Masanori Signed-off-by: Martin K. Petersen --- drivers/scsi/nsp32.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c index 00e3cbee55b8..da4d6e1106c4 100644 --- a/drivers/scsi/nsp32.c +++ b/drivers/scsi/nsp32.c @@ -2441,7 +2441,6 @@ static void nsp32_set_sync_entry(nsp32_hw_data *data, period = data->synct[entry].period_num; ackwidth = data->synct[entry].ackwidth; - offset = offset; sample_rate = data->synct[entry].sample_rate; target->syncreg = TO_SYNCREG(period, offset); From 7e1c99e5c1dd919cd974c4531164c13998e44cba Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 10 Dec 2018 16:55:40 -0700 Subject: [PATCH 0758/4277] scsi: pcmcia: nsp_cs: Remove unnecessary parentheses Clang warns: drivers/scsi/pcmcia/nsp_cs.c:1137:27: warning: equality comparison with extraneous parentheses [-Wparentheses-equality] if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~ drivers/scsi/pcmcia/nsp_cs.c:1137:27: note: remove extraneous parentheses around the comparison to silence this warning if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { ~ ^ ~ drivers/scsi/pcmcia/nsp_cs.c:1137:27: note: use '=' to turn this equality comparison into an assignment if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { ^~ = 1 warning generated. Signed-off-by: Nathan Chancellor Signed-off-by: Martin K. Petersen --- drivers/scsi/pcmcia/nsp_cs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 1bd6825a4f14..a81748e6e8fb 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -1134,7 +1134,8 @@ static irqreturn_t nspintr(int irq, void *dev_id) //*sync_neg = SYNC_NOT_YET; - if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { /* all command complete and return status */ + /* all command complete and return status */ + if (tmpSC->SCp.Message == MSG_COMMAND_COMPLETE) { tmpSC->result = (DID_OK << 16) | ((tmpSC->SCp.Message & 0xff) << 8) | ((tmpSC->SCp.Status & 0xff) << 0); From c2c7e740577154e755ec373712e32e5864e88315 Mon Sep 17 00:00:00 2001 From: John Garry Date: Fri, 25 Jan 2019 22:22:27 +0800 Subject: [PATCH 0759/4277] scsi: hisi_sas: No need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 53 ++++++++------------------- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 6a1a5adc0878..b8b1ded77d60 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -368,6 +368,7 @@ struct hisi_hba { struct dentry *debugfs_dir; struct dentry *debugfs_dump_dentry; + bool debugfs_snapshot; }; /* Generic HW DMA host memory structures */ diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 3d24366ceeef..07959fae37f0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1429,8 +1429,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) struct Scsi_Host *shost = hisi_hba->shost; int rc; - if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct && - !hisi_hba->debugfs_dump_dentry) + if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct) queue_work(hisi_hba->wq, &hisi_hba->debugfs_work); if (!hisi_hba->hw->soft_reset) @@ -2780,63 +2779,42 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) /* Create dump dir inside device dir */ dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir); - if (!dump_dentry) - goto fail; - hisi_hba->debugfs_dump_dentry = dump_dentry; - if (!debugfs_create_file("global", 0400, dump_dentry, hisi_hba, - &hisi_sas_debugfs_global_fops)) - goto fail; + debugfs_create_file("global", 0400, dump_dentry, hisi_hba, + &hisi_sas_debugfs_global_fops); /* Create port dir and files */ dentry = debugfs_create_dir("port", dump_dentry); - if (!dentry) - goto fail; - for (p = 0; p < hisi_hba->n_phy; p++) { snprintf(name, 256, "%d", p); - if (!debugfs_create_file(name, 0400, dentry, - &hisi_hba->phy[p], - &hisi_sas_debugfs_port_fops)) - goto fail; + + debugfs_create_file(name, 0400, dentry, &hisi_hba->phy[p], + &hisi_sas_debugfs_port_fops); } /* Create CQ dir and files */ dentry = debugfs_create_dir("cq", dump_dentry); - if (!dentry) - goto fail; - for (c = 0; c < hisi_hba->queue_count; c++) { snprintf(name, 256, "%d", c); - if (!debugfs_create_file(name, 0400, dentry, - &hisi_hba->cq[c], - &hisi_sas_debugfs_cq_fops)) - goto fail; + debugfs_create_file(name, 0400, dentry, &hisi_hba->cq[c], + &hisi_sas_debugfs_cq_fops); } /* Create DQ dir and files */ dentry = debugfs_create_dir("dq", dump_dentry); - if (!dentry) - goto fail; - for (d = 0; d < hisi_hba->queue_count; d++) { snprintf(name, 256, "%d", d); - if (!debugfs_create_file(name, 0400, dentry, - &hisi_hba->dq[d], - &hisi_sas_debugfs_dq_fops)) - goto fail; + debugfs_create_file(name, 0400, dentry, &hisi_hba->dq[d], + &hisi_sas_debugfs_dq_fops); } - if (!debugfs_create_file("iost", 0400, dump_dentry, hisi_hba, - &hisi_sas_debugfs_iost_fops)) - goto fail; + debugfs_create_file("iost", 0400, dump_dentry, hisi_hba, + &hisi_sas_debugfs_iost_fops); return; -fail: - debugfs_remove_recursive(hisi_hba->debugfs_dir); } static void hisi_sas_debugfs_snapshot_regs(struct hisi_hba *hisi_hba) @@ -2860,6 +2838,10 @@ void hisi_sas_debugfs_work_handler(struct work_struct *work) struct hisi_hba *hisi_hba = container_of(work, struct hisi_hba, debugfs_work); + if (hisi_hba->debugfs_snapshot) + return; + hisi_hba->debugfs_snapshot = true; + hisi_sas_debugfs_snapshot_regs(hisi_hba); } EXPORT_SYMBOL_GPL(hisi_sas_debugfs_work_handler); @@ -2874,9 +2856,6 @@ void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba) hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev), hisi_sas_debugfs_dir); - if (!hisi_hba->debugfs_dir) - return; - /* Alloc buffer for global */ sz = hisi_hba->hw->debugfs_reg_global->count * 4; hisi_hba->debugfs_global_reg = From 5b0eeac4bed4b1a261acf3cd893f929e68814a0d Mon Sep 17 00:00:00 2001 From: John Garry Date: Fri, 25 Jan 2019 22:22:28 +0800 Subject: [PATCH 0760/4277] scsi: hisi_sas: Fix type casting and missing static qualifier in debugfs code Sparse can detect some type casting issues in the debugfs code, so fix it up. Also a missing static qualifier is added to hisi_sas_debugfs_to_reg_name(). Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_main.c | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index b8b1ded77d60..5bb6759f41ea 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -359,8 +359,8 @@ struct hisi_hba { u32 intr_coal_count; /* Interrupt count to coalesce */ /* debugfs memories */ - void *debugfs_global_reg; - void *debugfs_port_reg[HISI_SAS_MAX_PHYS]; + u32 *debugfs_global_reg; + u32 *debugfs_port_reg[HISI_SAS_MAX_PHYS]; void *debugfs_complete_hdr[HISI_SAS_MAX_QUEUES]; struct hisi_sas_cmd_hdr *debugfs_cmd_hdr[HISI_SAS_MAX_QUEUES]; struct hisi_sas_iost *debugfs_iost; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 07959fae37f0..54e4e77159f5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2543,7 +2543,7 @@ static void hisi_sas_debugfs_snapshot_iost_reg(struct hisi_hba *hisi_hba) } } -const char * +static const char * hisi_sas_debugfs_to_reg_name(int off, int base_off, const struct hisi_sas_debugfs_reg_lu *lu) { @@ -2570,10 +2570,10 @@ static void hisi_sas_debugfs_print_reg(u32 *regs_val, const void *ptr, if (name) seq_printf(s, "0x%08x 0x%08x %s\n", off, - le32_to_cpu(regs_val[i]), name); + regs_val[i], name); else seq_printf(s, "0x%08x 0x%08x\n", off, - le32_to_cpu(regs_val[i])); + regs_val[i]); } } @@ -2583,7 +2583,7 @@ static int hisi_sas_debugfs_global_show(struct seq_file *s, void *p) const struct hisi_sas_hw *hw = hisi_hba->hw; const struct hisi_sas_debugfs_reg *reg_global = hw->debugfs_reg_global; - hisi_sas_debugfs_print_reg((u32 *)hisi_hba->debugfs_global_reg, + hisi_sas_debugfs_print_reg(hisi_hba->debugfs_global_reg, reg_global, s); return 0; @@ -2630,7 +2630,7 @@ static const struct file_operations hisi_sas_debugfs_port_fops = { }; static int hisi_sas_show_row_64(struct seq_file *s, int index, - int sz, u64 *ptr) + int sz, __le64 *ptr) { int i; @@ -2648,7 +2648,7 @@ static int hisi_sas_show_row_64(struct seq_file *s, int index, } static int hisi_sas_show_row_32(struct seq_file *s, int index, - int sz, u32 *ptr) + int sz, __le32 *ptr) { int i; @@ -2668,7 +2668,7 @@ static int hisi_sas_cq_show_slot(struct seq_file *s, int slot, void *cq_ptr) struct hisi_sas_cq *cq = cq_ptr; struct hisi_hba *hisi_hba = cq->hisi_hba; void *complete_queue = hisi_hba->debugfs_complete_hdr[cq->id]; - void *complete_hdr = complete_queue + + __le32 *complete_hdr = complete_queue + (hisi_hba->hw->complete_hdr_size * slot); return hisi_sas_show_row_32(s, slot, @@ -2707,8 +2707,8 @@ static int hisi_sas_dq_show_slot(struct seq_file *s, int slot, void *dq_ptr) struct hisi_sas_dq *dq = dq_ptr; struct hisi_hba *hisi_hba = dq->hisi_hba; void *cmd_queue = hisi_hba->debugfs_cmd_hdr[dq->id]; - void *cmd_hdr = cmd_queue + - hisi_hba->hw->complete_hdr_size * slot; + __le32 *cmd_hdr = cmd_queue + + sizeof(struct hisi_sas_cmd_hdr) * slot; return hisi_sas_show_row_32(s, slot, sizeof(struct hisi_sas_cmd_hdr), cmd_hdr); @@ -2744,10 +2744,11 @@ static int hisi_sas_debugfs_iost_show(struct seq_file *s, void *p) struct hisi_hba *hisi_hba = s->private; struct hisi_sas_iost *debugfs_iost = hisi_hba->debugfs_iost; int i, ret, max_command_entries = hisi_hba->hw->max_command_entries; + __le64 *iost = &debugfs_iost->qw0; for (i = 0; i < max_command_entries; i++, debugfs_iost++) { ret = hisi_sas_show_row_64(s, i, sizeof(*debugfs_iost), - (u64 *)debugfs_iost); + iost); if (ret) return ret; } From 5979f33b982dca807ea802b512480bc62f81396a Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Fri, 25 Jan 2019 22:22:29 +0800 Subject: [PATCH 0761/4277] scsi: hisi_sas: Add debugfs ITCT file and add file operations This patch creates debugfs file for ITCT and adds file operations. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 54e4e77159f5..a8fda1c9af9e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2769,6 +2769,36 @@ static const struct file_operations hisi_sas_debugfs_iost_fops = { .owner = THIS_MODULE, }; +static int hisi_sas_debugfs_itct_show(struct seq_file *s, void *p) +{ + int i, ret; + struct hisi_hba *hisi_hba = s->private; + struct hisi_sas_itct *debugfs_itct = hisi_hba->debugfs_itct; + __le64 *itct = &debugfs_itct->qw0; + + for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, debugfs_itct++) { + ret = hisi_sas_show_row_64(s, i, sizeof(*debugfs_itct), + itct); + if (ret) + return ret; + } + + return 0; +} + +static int hisi_sas_debugfs_itct_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hisi_sas_debugfs_itct_show, inode->i_private); +} + +static const struct file_operations hisi_sas_debugfs_itct_fops = { + .open = hisi_sas_debugfs_itct_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) { struct dentry *dump_dentry; @@ -2815,6 +2845,9 @@ static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba) debugfs_create_file("iost", 0400, dump_dentry, hisi_hba, &hisi_sas_debugfs_iost_fops); + debugfs_create_file("itct", 0400, dump_dentry, hisi_hba, + &hisi_sas_debugfs_itct_fops); + return; } From 569eddcf3a0f4efff4ef96a7012010e0f7daa8b4 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Fri, 25 Jan 2019 22:22:30 +0800 Subject: [PATCH 0762/4277] scsi: hisi_sas: send primitive NOTIFY to SSP situation only Send primitive NOTIFY to SSP situation only, or it causes underflow issue when sending IO. Also rename hisi_sas_hw.sl_notify() to hisi_sas_hw. sl_notify_ssp(). Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 +- drivers/scsi/hisi_sas/hisi_sas_main.c | 3 ++- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 4 ++-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 5bb6759f41ea..97f0fda931a1 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -246,7 +246,7 @@ struct hisi_sas_hw { int (*slot_index_alloc)(struct hisi_hba *hisi_hba, struct domain_device *device); struct hisi_sas_device *(*alloc_dev)(struct domain_device *device); - void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no); + void (*sl_notify_ssp)(struct hisi_hba *hisi_hba, int phy_no); int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq); void (*start_delivery)(struct hisi_sas_dq *dq); void (*prep_ssp)(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index a8fda1c9af9e..c6e5bce6c955 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -778,7 +778,8 @@ static void hisi_sas_phyup_work(struct work_struct *work) struct asd_sas_phy *sas_phy = &phy->sas_phy; int phy_no = sas_phy->id; - hisi_hba->hw->sl_notify(hisi_hba, phy_no); /* This requires a sleep */ + if (phy->identify.target_port_protocols == SAS_PROTOCOL_SSP) + hisi_hba->hw->sl_notify_ssp(hisi_hba, phy_no); hisi_sas_bytes_dmaed(hisi_hba, phy_no); } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 28ab52a021cf..c8f808faed95 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -835,7 +835,7 @@ static void phys_init_v1_hw(struct hisi_hba *hisi_hba) mod_timer(timer, jiffies + HZ); } -static void sl_notify_v1_hw(struct hisi_hba *hisi_hba, int phy_no) +static void sl_notify_ssp_v1_hw(struct hisi_hba *hisi_hba, int phy_no) { u32 sl_control; @@ -1826,7 +1826,7 @@ static struct scsi_host_template sht_v1_hw = { static const struct hisi_sas_hw hisi_sas_v1_hw = { .hw_init = hisi_sas_v1_init, .setup_itct = setup_itct_v1_hw, - .sl_notify = sl_notify_v1_hw, + .sl_notify_ssp = sl_notify_ssp_v1_hw, .clear_itct = clear_itct_v1_hw, .prep_smp = prep_smp_v1_hw, .prep_ssp = prep_ssp_v1_hw, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 5c4b8dff4c10..3792f8abec1a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1589,7 +1589,7 @@ static void phys_init_v2_hw(struct hisi_hba *hisi_hba) } } -static void sl_notify_v2_hw(struct hisi_hba *hisi_hba, int phy_no) +static void sl_notify_ssp_v2_hw(struct hisi_hba *hisi_hba, int phy_no) { u32 sl_control; @@ -3595,7 +3595,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .setup_itct = setup_itct_v2_hw, .slot_index_alloc = slot_index_alloc_quirk_v2_hw, .alloc_dev = alloc_dev_quirk_v2_hw, - .sl_notify = sl_notify_v2_hw, + .sl_notify_ssp = sl_notify_ssp_v2_hw, .get_wideport_bitmap = get_wideport_bitmap_v2_hw, .clear_itct = clear_itct_v2_hw, .free_device = free_device_v2_hw, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index b780b5269f0f..726ac4bd7dce 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -870,7 +870,7 @@ static void phys_init_v3_hw(struct hisi_hba *hisi_hba) } } -static void sl_notify_v3_hw(struct hisi_hba *hisi_hba, int phy_no) +static void sl_notify_ssp_v3_hw(struct hisi_hba *hisi_hba, int phy_no) { u32 sl_control; @@ -2520,7 +2520,7 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .get_wideport_bitmap = get_wideport_bitmap_v3_hw, .complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr), .clear_itct = clear_itct_v3_hw, - .sl_notify = sl_notify_v3_hw, + .sl_notify_ssp = sl_notify_ssp_v3_hw, .prep_ssp = prep_ssp_v3_hw, .prep_smp = prep_smp_v3_hw, .prep_stp = prep_ata_v3_hw, From 5c31b0c677531c2b8b4e29b3cfb923df663f39b7 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Fri, 25 Jan 2019 22:22:31 +0800 Subject: [PATCH 0763/4277] scsi: hisi_sas: shutdown axi bus to avoid exception CQ returned When injecting 2 bit ECC error, it will cause fatal AXI interrupts. Before the recovery of SAS controller reset, the internal of SAS controller is in error. If CQ interrupts return at the time, actually it is exception CQ interrupt, and it may cause resource release in disorder. To avoid the exception situation, shutdown AXI bus after fatal AXI interrupt. In SAS controller reset, it will restart AXI bus. For later version of v3 hw, hardware will shutdown AXI bus for this situation, so just fix current ver of v3 hw. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 726ac4bd7dce..d5eabfc12509 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1646,6 +1646,7 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p) u32 irq_value, irq_msk; struct hisi_hba *hisi_hba = p; struct device *dev = hisi_hba->dev; + struct pci_dev *pdev = hisi_hba->pci_dev; int i; irq_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3); @@ -1677,6 +1678,17 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p) error->msg, irq_value); queue_work(hisi_hba->wq, &hisi_hba->rst_work); } + + if (pdev->revision < 0x21) { + u32 reg_val; + + reg_val = hisi_sas_read32(hisi_hba, + AXI_MASTER_CFG_BASE + + AM_CTRL_GLOBAL); + reg_val |= AM_CTRL_SHUTDOWN_REQ_MSK; + hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + + AM_CTRL_GLOBAL, reg_val); + } } if (irq_value & BIT(ENT_INT_SRC3_ITC_INT_OFF)) { From ffb1c820b8b6a0466a51c467dcd8c0add77d00a7 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Fri, 25 Jan 2019 22:22:32 +0800 Subject: [PATCH 0764/4277] scsi: hisi_sas: remove the check of sas_dev status in hisi_sas_I_T_nexus_reset() When issing a hardreset to a SATA device when running IO, it is possible that abnormal CQs of the device are returned. Then enter error handler, it doesn't enter function hisi_sas_abort_task() as there is no timeout IO, and it doesn't set device as HISI_SAS_DEV_EH. So when hardreset by libata later, it actually doesn't issue hardreset as there is a check to judge whether device is in error. For this situation, actually need to hardreset the device to recover. So remove the check of sas_dev status in hisi_sas_I_T_nexus_reset(). Before we add the check to avoid the endless loop of reset for directly-attached SATA device at probe time, actually we flutter it for it, so it is not necessary to add the check now. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 6 ------ drivers/scsi/hisi_sas/hisi_sas_main.c | 9 --------- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 1 - 3 files changed, 16 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 97f0fda931a1..aac8b3864002 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -83,11 +83,6 @@ enum { PORT_TYPE_SATA = (1U << 0), }; -enum dev_status { - HISI_SAS_DEV_NORMAL, - HISI_SAS_DEV_EH, -}; - enum { HISI_SAS_INT_ABT_CMD = 0, HISI_SAS_INT_ABT_DEV = 1, @@ -188,7 +183,6 @@ struct hisi_sas_device { enum sas_device_type dev_type; int device_id; int sata_idx; - u8 dev_status; }; struct hisi_sas_tmf_task { diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index c6e5bce6c955..b7a370af4c32 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -626,7 +626,6 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) hisi_hba->devices[i].device_id = i; sas_dev = &hisi_hba->devices[i]; - sas_dev->dev_status = HISI_SAS_DEV_NORMAL; sas_dev->dev_type = device->dev_type; sas_dev->hisi_hba = hisi_hba; sas_dev->sas_device = device; @@ -1495,7 +1494,6 @@ static int hisi_sas_abort_task(struct sas_task *task) task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); - sas_dev->dev_status = HISI_SAS_DEV_EH; if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { struct scsi_cmnd *cmnd = task->uldd_task; struct hisi_sas_slot *slot = task->lldd_task; @@ -1639,15 +1637,10 @@ static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device) static int hisi_sas_I_T_nexus_reset(struct domain_device *device) { - struct hisi_sas_device *sas_dev = device->lldd_dev; struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; - if (sas_dev->dev_status != HISI_SAS_DEV_EH) - return TMF_RESP_FUNC_FAILED; - sas_dev->dev_status = HISI_SAS_DEV_NORMAL; - rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); if (rc < 0) { @@ -1671,7 +1664,6 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; - sas_dev->dev_status = HISI_SAS_DEV_EH; if (dev_is_sata(device)) { struct sas_phy *phy; @@ -2100,7 +2092,6 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED; hisi_hba->devices[i].device_id = i; - hisi_hba->devices[i].dev_status = HISI_SAS_DEV_NORMAL; } for (i = 0; i < hisi_hba->queue_count; i++) { diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 3792f8abec1a..8a2a24bde8a5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -868,7 +868,6 @@ hisi_sas_device *alloc_dev_quirk_v2_hw(struct domain_device *device) hisi_hba->devices[i].device_id = i; sas_dev = &hisi_hba->devices[i]; - sas_dev->dev_status = HISI_SAS_DEV_NORMAL; sas_dev->dev_type = device->dev_type; sas_dev->hisi_hba = hisi_hba; sas_dev->sas_device = device; From ae68b566e0215b0e2ca6f4abf0dbf91e662e44e4 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Fri, 25 Jan 2019 22:22:33 +0800 Subject: [PATCH 0765/4277] scsi: hisi_sas: Remove unused parameter of function hisi_sas_alloc() In function hisi_sas_alloc(), parameter shost is not used, so remove it. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 +- drivers/scsi/hisi_sas/hisi_sas_main.c | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index aac8b3864002..235b81998c9a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -497,7 +497,7 @@ extern bool hisi_sas_debugfs_enable; extern struct dentry *hisi_sas_debugfs_dir; extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba); -extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost); +extern int hisi_sas_alloc(struct hisi_hba *hisi_hba); extern void hisi_sas_free(struct hisi_hba *hisi_hba); extern u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index b7a370af4c32..86d358aa2732 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2074,7 +2074,7 @@ void hisi_sas_init_mem(struct hisi_hba *hisi_hba) } EXPORT_SYMBOL_GPL(hisi_sas_init_mem); -int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) +int hisi_sas_alloc(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; int i, j, s, max_command_entries = hisi_hba->hw->max_command_entries; @@ -2357,7 +2357,7 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, goto err_out; } - if (hisi_sas_alloc(hisi_hba, shost)) { + if (hisi_sas_alloc(hisi_hba)) { hisi_sas_free(hisi_hba); goto err_out; } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index d5eabfc12509..4b628c20447d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2591,7 +2591,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev) if (hisi_sas_get_fw_info(hisi_hba) < 0) goto err_out; - if (hisi_sas_alloc(hisi_hba, shost)) { + if (hisi_sas_alloc(hisi_hba)) { hisi_sas_free(hisi_hba); goto err_out; } From eb44e4d7b5a3090f0114927f42ae575c29664a09 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Fri, 25 Jan 2019 22:22:34 +0800 Subject: [PATCH 0766/4277] scsi: hisi_sas: Reject setting programmed minimum linkrate > 1.5G The SAS controller cannot support a programmed minimum linkrate of > 1.5G (it will always negotiate to 1.5G at least), so just reject it. This solves a strange situation where the PHY negotiated linkrate may be less than the programmed minimum linkrate. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 86d358aa2732..e44d2cd69de7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -946,7 +946,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) return hisi_sas_task_exec(task, gfp_flags, 0, NULL); } -static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, +static int hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { struct sas_phy_linkrates _r; @@ -955,6 +955,9 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, struct asd_sas_phy *sas_phy = &phy->sas_phy; enum sas_linkrate min, max; + if (r->minimum_linkrate > SAS_LINK_RATE_1_5_GBPS) + return -EINVAL; + if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { max = sas_phy->phy->maximum_linkrate; min = r->minimum_linkrate; @@ -962,7 +965,7 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, max = r->maximum_linkrate; min = sas_phy->phy->minimum_linkrate; } else - return; + return -EINVAL; _r.maximum_linkrate = max; _r.minimum_linkrate = min; @@ -974,6 +977,8 @@ static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, msleep(100); hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, &_r); hisi_hba->hw->phy_start(hisi_hba, phy_no); + + return 0; } static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, @@ -999,8 +1004,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, break; case PHY_FUNC_SET_LINK_RATE: - hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata); - break; + return hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata); case PHY_FUNC_GET_EVENTS: if (hisi_hba->hw->get_events) { hisi_hba->hw->get_events(hisi_hba, phy_no); From b6c9b15e44090aee2a7fba646b06ff166f595b16 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Fri, 25 Jan 2019 22:22:35 +0800 Subject: [PATCH 0767/4277] scsi: hisi_sas: Fix losing directly attached disk when hot-plug Hot-plugging SAS wire of direct hard disk backplane may cause disk lost. We have done this test with several types of SATA disk from different venders, and only two models from Seagate has this problem, ST4000NM0035-1V4107 and ST3000VM002-1ET166. The root cause is that the disk doesn't send D2H frame after OOB finished. SAS controller will issue phyup interrupt only when D2H frame is received, otherwise, will be waiting there all the time. When this issue happen, we can find the disk again with link reset. To fix this issue, we setup an timer after OOB finished. If the PHY is not up in 20s, do link reset. Notes: the 20s is an experience value. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 4 ++++ drivers/scsi/hisi_sas/hisi_sas_main.c | 26 +++++++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 8 ++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 27 +++++++++++++++++--------- 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 235b81998c9a..9b35f84b5029 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -76,6 +76,8 @@ #define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK) +#define HISI_SAS_WAIT_PHYUP_TIMEOUT 20 + struct hisi_hba; enum { @@ -141,6 +143,7 @@ struct hisi_sas_phy { struct asd_sas_phy sas_phy; struct sas_identify identify; struct completion *reset_completion; + struct timer_list timer; spinlock_t lock; u64 port_id; /* from hw */ u64 frame_rcvd_size; @@ -522,6 +525,7 @@ extern void hisi_sas_init_mem(struct hisi_hba *hisi_hba); extern void hisi_sas_rst_work_handler(struct work_struct *work); extern void hisi_sas_sync_rst_work_handler(struct work_struct *work); extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba); +extern void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no); extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, enum hisi_sas_phy_event event); extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index e44d2cd69de7..98ea029e5a35 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -808,6 +808,30 @@ bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, } EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event); +static void hisi_sas_wait_phyup_timedout(struct timer_list *t) +{ + struct hisi_sas_phy *phy = from_timer(phy, t, timer); + struct hisi_hba *hisi_hba = phy->hisi_hba; + struct device *dev = hisi_hba->dev; + int phy_no = phy->sas_phy.id; + + dev_warn(dev, "phy%d wait phyup timeout, issuing link reset\n", phy_no); + hisi_sas_notify_phy_event(phy, HISI_PHYE_LINK_RESET); +} + +void hisi_sas_phy_oob_ready(struct hisi_hba *hisi_hba, int phy_no) +{ + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; + struct device *dev = hisi_hba->dev; + + if (!timer_pending(&phy->timer)) { + dev_dbg(dev, "phy%d OOB ready\n", phy_no); + phy->timer.expires = jiffies + HISI_SAS_WAIT_PHYUP_TIMEOUT * HZ; + add_timer(&phy->timer); + } +} +EXPORT_SYMBOL_GPL(hisi_sas_phy_oob_ready); + static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) { struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; @@ -836,6 +860,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]); spin_lock_init(&phy->lock); + + timer_setup(&phy->timer, hisi_sas_wait_phyup_timedout, 0); } static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 8a2a24bde8a5..aa90f17a1cac 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2676,6 +2676,8 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) if (is_sata_phy_v2_hw(hisi_hba, phy_no)) goto end; + del_timer(&phy->timer); + if (phy_no == 8) { u32 port_state = hisi_sas_read32(hisi_hba, PORT_STATE); @@ -2755,6 +2757,7 @@ static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba) struct hisi_sas_port *port = phy->port; struct device *dev = hisi_hba->dev; + del_timer(&phy->timer); hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1); phy_state = hisi_sas_read32(hisi_hba, PHY_STATE); @@ -2943,6 +2946,9 @@ static irqreturn_t int_chnl_int_v2_hw(int irq_no, void *p) if (irq_value0 & CHL_INT0_SL_RX_BCST_ACK_MSK) phy_bcast_v2_hw(phy_no, hisi_hba); + if (irq_value0 & CHL_INT0_PHY_RDY_MSK) + hisi_sas_phy_oob_ready(hisi_hba, phy_no); + hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, irq_value0 & (~CHL_INT0_HOTPLUG_TOUT_MSK) @@ -3226,6 +3232,8 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p) unsigned long flags; int phy_no, offset; + del_timer(&phy->timer); + phy_no = sas_phy->id; initial_fis = &hisi_hba->initial_fis[phy_no]; fis = &initial_fis->fis; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 4b628c20447d..f1009e27d9bc 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1290,6 +1290,7 @@ static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) struct device *dev = hisi_hba->dev; unsigned long flags; + del_timer(&phy->timer); hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1); port_id = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA); @@ -1383,9 +1384,11 @@ end: static irqreturn_t phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba) { + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; u32 phy_state, sl_ctrl, txid_auto; struct device *dev = hisi_hba->dev; + del_timer(&phy->timer); hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1); phy_state = hisi_sas_read32(hisi_hba, PHY_STATE); @@ -1554,6 +1557,19 @@ static void handle_chl_int2_v3_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, irq_value); } +static void handle_chl_int0_v3_hw(struct hisi_hba *hisi_hba, int phy_no) +{ + u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0); + + if (irq_value0 & CHL_INT0_PHY_RDY_MSK) + hisi_sas_phy_oob_ready(hisi_hba, phy_no); + + hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, + irq_value0 & (~CHL_INT0_SL_RX_BCST_ACK_MSK) + & (~CHL_INT0_SL_PHY_ENABLE_MSK) + & (~CHL_INT0_NOT_RDY_MSK)); +} + static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) { struct hisi_hba *hisi_hba = p; @@ -1564,8 +1580,8 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) & 0xeeeeeeee; while (irq_msk) { - u32 irq_value0 = hisi_sas_phy_read32(hisi_hba, phy_no, - CHL_INT0); + if (irq_msk & (2 << (phy_no * 4))) + handle_chl_int0_v3_hw(hisi_hba, phy_no); if (irq_msk & (4 << (phy_no * 4))) handle_chl_int1_v3_hw(hisi_hba, phy_no); @@ -1573,13 +1589,6 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) if (irq_msk & (8 << (phy_no * 4))) handle_chl_int2_v3_hw(hisi_hba, phy_no); - if (irq_msk & (2 << (phy_no * 4)) && irq_value0) { - hisi_sas_phy_write32(hisi_hba, phy_no, - CHL_INT0, irq_value0 - & (~CHL_INT0_SL_RX_BCST_ACK_MSK) - & (~CHL_INT0_SL_PHY_ENABLE_MSK) - & (~CHL_INT0_NOT_RDY_MSK)); - } irq_msk &= ~(0xe << (phy_no * 4)); phy_no++; } From d1548e9c323f51e6d7da4392ec4368cfc5425a78 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Fri, 25 Jan 2019 22:22:36 +0800 Subject: [PATCH 0768/4277] scsi: hisi_sas: Correct memory allocation size for DQ debugfs Some sizes we allocate for debugfs structure are incorrect, so fix them. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 98ea029e5a35..6841a594fd5d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2498,7 +2498,7 @@ static void hisi_sas_debugfs_snapshot_cq_reg(struct hisi_hba *hisi_hba) static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba) { - int queue_entry_size = hisi_hba->hw->complete_hdr_size; + int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr); int i; for (i = 0; i < hisi_hba->queue_count; i++) @@ -2941,7 +2941,7 @@ void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba) } /* Alloc buffer for dq */ - sz = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; + sz = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; for (d = 0; d < hisi_hba->queue_count; d++) { hisi_hba->debugfs_cmd_hdr[d] = devm_kmalloc(dev, sz, GFP_KERNEL); From 26889e5ec87a0e4ef31ba2dc4f60933be0dbdffe Mon Sep 17 00:00:00 2001 From: John Garry Date: Fri, 25 Jan 2019 22:22:37 +0800 Subject: [PATCH 0769/4277] scsi: hisi_sas: Some misc tidy-up Sparse detected some problems in the driver, so tidy them up. Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 33 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 6841a594fd5d..445f8eee93d2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2075,14 +2075,18 @@ static struct sas_domain_function_template hisi_sas_transport_ops = { void hisi_sas_init_mem(struct hisi_hba *hisi_hba) { - int i, s, max_command_entries = hisi_hba->hw->max_command_entries; + int i, s, j, max_command_entries = hisi_hba->hw->max_command_entries; + struct hisi_sas_breakpoint *sata_breakpoint = hisi_hba->sata_breakpoint; for (i = 0; i < hisi_hba->queue_count; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; struct hisi_sas_dq *dq = &hisi_hba->dq[i]; + struct hisi_sas_cmd_hdr *cmd_hdr = hisi_hba->cmd_hdr[i]; + + s = sizeof(struct hisi_sas_cmd_hdr); + for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++) + memset(&cmd_hdr[j], 0, s); - s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; - memset(hisi_hba->cmd_hdr[i], 0, s); dq->wr_point = 0; s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; @@ -2099,8 +2103,9 @@ void hisi_sas_init_mem(struct hisi_hba *hisi_hba) s = max_command_entries * sizeof(struct hisi_sas_breakpoint); memset(hisi_hba->breakpoint, 0, s); - s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_sata_breakpoint); - memset(hisi_hba->sata_breakpoint, 0, s); + s = sizeof(struct hisi_sas_sata_breakpoint); + for (j = 0; j < HISI_SAS_MAX_ITCT_ENTRIES; j++) + memset(&sata_breakpoint[j], 0, s); } EXPORT_SYMBOL_GPL(hisi_sas_init_mem); @@ -2157,10 +2162,9 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba) s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); hisi_hba->itct = dmam_alloc_coherent(dev, s, &hisi_hba->itct_dma, - GFP_KERNEL); + GFP_KERNEL | __GFP_ZERO); if (!hisi_hba->itct) goto err_out; - memset(hisi_hba->itct, 0, s); hisi_hba->slot_info = devm_kcalloc(dev, max_command_entries, sizeof(struct hisi_sas_slot), @@ -2501,10 +2505,17 @@ static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba) int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr); int i; - for (i = 0; i < hisi_hba->queue_count; i++) - memcpy(hisi_hba->debugfs_cmd_hdr[i], - hisi_hba->cmd_hdr[i], - HISI_SAS_QUEUE_SLOTS * queue_entry_size); + for (i = 0; i < hisi_hba->queue_count; i++) { + struct hisi_sas_cmd_hdr *debugfs_cmd_hdr, *cmd_hdr; + int j; + + debugfs_cmd_hdr = hisi_hba->debugfs_cmd_hdr[i]; + cmd_hdr = hisi_hba->cmd_hdr[i]; + + for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++) + memcpy(&debugfs_cmd_hdr[j], &cmd_hdr[j], + queue_entry_size); + } } static void hisi_sas_debugfs_snapshot_port_reg(struct hisi_hba *hisi_hba) From e1ba0b0b44512c5a209526c09ea3eb7d256b6951 Mon Sep 17 00:00:00 2001 From: John Garry Date: Fri, 25 Jan 2019 22:22:38 +0800 Subject: [PATCH 0770/4277] scsi: hisi_sas: Fix to only call scsi_get_prot_op() for non-NULL scsi_cmnd A NULL-pointer dereference was introduced for TMF SSP commands from the upstreaming reworking. Fix this by relocating the scsi_get_prot_op() callsite. Fixes: d6a9000b81be ("scsi: hisi_sas: Add support for DIF feature for v2 hw") Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index f1009e27d9bc..108683b0a049 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1035,8 +1035,8 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; struct hisi_sas_tmf_task *tmf = slot->tmf; - unsigned char prot_op = scsi_get_prot_op(scsi_cmnd); int has_data = 0, priority = !!tmf; + unsigned char prot_op; u8 *buf_cmd; u32 dw1 = 0, dw2 = 0, len = 0; @@ -1051,6 +1051,7 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF; dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF; } else { + prot_op = scsi_get_prot_op(scsi_cmnd); dw1 |= 1 << CMD_HDR_FRAME_TYPE_OFF; switch (scsi_cmnd->sc_data_direction) { case DMA_TO_DEVICE: From ede2afb9c8ed784cdf1b6de9cd02f044668b842a Mon Sep 17 00:00:00 2001 From: John Garry Date: Fri, 25 Jan 2019 22:22:39 +0800 Subject: [PATCH 0771/4277] scsi: hisi_sas: Add missing seq_printf() call in hisi_sas_show_row_32() This call must have been missed when I reworked the debugfs feature for upstreaming, so add it back. Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 445f8eee93d2..2d6b5fe90a9e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2688,6 +2688,7 @@ static int hisi_sas_show_row_32(struct seq_file *s, int index, /* completion header size not fixed per HW version */ seq_printf(s, "index %04d:\n\t", index); for (i = 1; i <= sz / 4; i++, ptr++) { + seq_printf(s, " 0x%08x", le32_to_cpu(*ptr)); if (!(i % 4)) seq_puts(s, "\n\t"); } From 6bf8f22aea0ddd93af822aed8afeeee4acdf7694 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 22 Jan 2019 08:29:56 +0200 Subject: [PATCH 0772/4277] IB/mlx5: Introduce MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD Introduce MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD and its initial implementation. This object is from type class FD and will be used to read DEVX async commands completion. The core layer should allow the driver to set object from type FD in a safe mode, this option was added with a matching comment in place. Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/rdma_core.c | 1 + drivers/infiniband/core/uverbs_uapi.c | 15 +++-- drivers/infiniband/hw/mlx5/devx.c | 83 ++++++++++++++++++++++++ include/rdma/uverbs_types.h | 1 + include/uapi/rdma/mlx5_user_ioctl_cmds.h | 9 +++ 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index 6c4747e61d2b..a260d2f8e0b7 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -801,6 +801,7 @@ void uverbs_close_fd(struct file *f) /* Pairs with filp->private_data in alloc_begin_fd_uobject */ uverbs_uobject_put(uobj); } +EXPORT_SYMBOL(uverbs_close_fd); /* * Drop the ucontext off the ufile and completely disconnect it from the diff --git a/drivers/infiniband/core/uverbs_uapi.c b/drivers/infiniband/core/uverbs_uapi.c index 9ae08e4b78a3..7a987acf0c0b 100644 --- a/drivers/infiniband/core/uverbs_uapi.c +++ b/drivers/infiniband/core/uverbs_uapi.c @@ -188,13 +188,18 @@ static int uapi_merge_obj_tree(struct uverbs_api *uapi, obj_elm->type_attrs = obj->type_attrs; obj_elm->type_class = obj->type_attrs->type_class; /* - * Today drivers are only permitted to use idr_class - * types. They cannot use FD types because we currently have - * no way to revoke the fops pointer after device - * disassociation. + * Today drivers are only permitted to use idr_class and + * fd_class types. We can revoke the IDR types during + * disassociation, and the FD types require the driver to use + * struct file_operations.owner to prevent the driver module + * code from unloading while the file is open. This provides + * enough safety that uverbs_close_fd() will continue to work. + * Drivers using FD are responsible to handle disassociation of + * the device on their own. */ if (WARN_ON(is_driver && - obj->type_attrs->type_class != &uverbs_idr_class)) + obj->type_attrs->type_class != &uverbs_idr_class && + obj->type_attrs->type_class != &uverbs_fd_class)) return -EINVAL; } diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 5a588f3cfb1b..9933bcf83a6b 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1168,6 +1168,38 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)( cmd_out, cmd_out_len); } +struct devx_async_event_queue { + spinlock_t lock; + wait_queue_head_t poll_wait; + struct list_head event_list; +}; + +struct devx_async_cmd_event_file { + struct ib_uobject uobj; + struct devx_async_event_queue ev_queue; +}; + +static void devx_init_event_queue(struct devx_async_event_queue *ev_queue) +{ + spin_lock_init(&ev_queue->lock); + INIT_LIST_HEAD(&ev_queue->event_list); + init_waitqueue_head(&ev_queue->poll_wait); +} + +static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC)( + struct uverbs_attr_bundle *attrs) +{ + struct devx_async_cmd_event_file *ev_file; + + struct ib_uobject *uobj = uverbs_attr_get_uobject( + attrs, MLX5_IB_ATTR_DEVX_ASYNC_CMD_FD_ALLOC_HANDLE); + + ev_file = container_of(uobj, struct devx_async_cmd_event_file, + uobj); + devx_init_event_queue(&ev_file->ev_queue); + return 0; +} + static int devx_umem_get(struct mlx5_ib_dev *dev, struct ib_ucontext *ucontext, struct uverbs_attr_bundle *attrs, struct devx_umem *obj) @@ -1313,6 +1345,38 @@ static int devx_umem_cleanup(struct ib_uobject *uobject, return 0; } +static ssize_t devx_async_cmd_event_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int devx_async_cmd_event_close(struct inode *inode, struct file *filp) +{ + uverbs_close_fd(filp); + return 0; +} + +static __poll_t devx_async_cmd_event_poll(struct file *filp, + struct poll_table_struct *wait) +{ + return 0; +} + +const struct file_operations devx_async_cmd_event_fops = { + .owner = THIS_MODULE, + .read = devx_async_cmd_event_read, + .poll = devx_async_cmd_event_poll, + .release = devx_async_cmd_event_close, + .llseek = no_llseek, +}; + +static int devx_hot_unplug_async_cmd_event_file(struct ib_uobject *uobj, + enum rdma_remove_reason why) +{ + return 0; +}; + DECLARE_UVERBS_NAMED_METHOD( MLX5_IB_METHOD_DEVX_UMEM_REG, UVERBS_ATTR_IDR(MLX5_IB_ATTR_DEVX_UMEM_REG_HANDLE, @@ -1440,6 +1504,22 @@ DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_DEVX_UMEM, &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_UMEM_REG), &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_UMEM_DEREG)); + +DECLARE_UVERBS_NAMED_METHOD( + MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC, + UVERBS_ATTR_FD(MLX5_IB_ATTR_DEVX_ASYNC_CMD_FD_ALLOC_HANDLE, + MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD, + UVERBS_ACCESS_NEW, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT( + MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD, + UVERBS_TYPE_ALLOC_FD(sizeof(struct devx_async_cmd_event_file), + devx_hot_unplug_async_cmd_event_file, + &devx_async_cmd_event_fops, "[devx_async_cmd]", + O_RDONLY), + &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC)); + static bool devx_is_supported(struct ib_device *device) { struct mlx5_ib_dev *dev = to_mdev(device); @@ -1457,5 +1537,8 @@ const struct uapi_definition mlx5_ib_devx_defs[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED( MLX5_IB_OBJECT_DEVX_UMEM, UAPI_DEF_IS_OBJ_SUPPORTED(devx_is_supported)), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED( + MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD, + UAPI_DEF_IS_OBJ_SUPPORTED(devx_is_supported)), {}, }; diff --git a/include/rdma/uverbs_types.h b/include/rdma/uverbs_types.h index acb1bfa3cc99..175d761695e1 100644 --- a/include/rdma/uverbs_types.h +++ b/include/rdma/uverbs_types.h @@ -157,6 +157,7 @@ struct uverbs_obj_fd_type { extern const struct uverbs_obj_type_class uverbs_idr_class; extern const struct uverbs_obj_type_class uverbs_fd_class; +void uverbs_close_fd(struct file *f); #define UVERBS_BUILD_BUG_ON(cond) (sizeof(char[1 - 2 * !!(cond)]) - \ sizeof(char)) diff --git a/include/uapi/rdma/mlx5_user_ioctl_cmds.h b/include/uapi/rdma/mlx5_user_ioctl_cmds.h index b8d121d457f1..6ceae29d77cd 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_cmds.h +++ b/include/uapi/rdma/mlx5_user_ioctl_cmds.h @@ -113,11 +113,20 @@ enum mlx5_ib_devx_umem_methods { MLX5_IB_METHOD_DEVX_UMEM_DEREG, }; +enum mlx5_ib_devx_async_cmd_fd_alloc_attrs { + MLX5_IB_ATTR_DEVX_ASYNC_CMD_FD_ALLOC_HANDLE = (1U << UVERBS_ID_NS_SHIFT), +}; + +enum mlx5_ib_devx_async_cmd_fd_methods { + MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC = (1U << UVERBS_ID_NS_SHIFT), +}; + enum mlx5_ib_objects { MLX5_IB_OBJECT_DEVX = (1U << UVERBS_ID_NS_SHIFT), MLX5_IB_OBJECT_DEVX_OBJ, MLX5_IB_OBJECT_DEVX_UMEM, MLX5_IB_OBJECT_FLOW_MATCHER, + MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD, }; enum mlx5_ib_flow_matcher_create_attrs { From a124edba26270697540f1058bfcd490c1c65b116 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 22 Jan 2019 08:29:57 +0200 Subject: [PATCH 0773/4277] IB/mlx5: Introduce async DEVX obj query API Introduce async DEVX obj query API to get the command response back to user space once it's ready without blocking when calling the firmware. The event's data includes a header with some meta data then the firmware output command data. The header includes: - The input 'wr_id' to let application recognizing the response. The input FD attribute is used to have the event data ready on. Downstream patches from this series will implement the file ops to let application read it. Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 164 +++++++++++++++++++++- include/uapi/rdma/mlx5_user_ioctl_cmds.h | 9 ++ include/uapi/rdma/mlx5_user_ioctl_verbs.h | 5 + 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 9933bcf83a6b..9ca116155f9c 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,16 @@ #define UVERBS_MODULE_NAME mlx5_ib #include +struct devx_async_data { + struct mlx5_ib_dev *mdev; + struct list_head list; + struct ib_uobject *fd_uobj; + struct mlx5_async_work cb_work; + u16 cmd_out_len; + /* must be last field in this structure */ + struct mlx5_ib_uapi_devx_async_cmd_hdr hdr; +}; + #define MLX5_MAX_DESTROY_INBOX_SIZE_DW MLX5_ST_SZ_DW(delete_fte_in) struct devx_obj { struct mlx5_core_dev *mdev; @@ -1172,11 +1183,13 @@ struct devx_async_event_queue { spinlock_t lock; wait_queue_head_t poll_wait; struct list_head event_list; + atomic_t bytes_in_use; }; struct devx_async_cmd_event_file { struct ib_uobject uobj; struct devx_async_event_queue ev_queue; + struct mlx5_async_ctx async_ctx; }; static void devx_init_event_queue(struct devx_async_event_queue *ev_queue) @@ -1184,6 +1197,7 @@ static void devx_init_event_queue(struct devx_async_event_queue *ev_queue) spin_lock_init(&ev_queue->lock); INIT_LIST_HEAD(&ev_queue->event_list); init_waitqueue_head(&ev_queue->poll_wait); + atomic_set(&ev_queue->bytes_in_use, 0); } static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC)( @@ -1193,13 +1207,123 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC)( struct ib_uobject *uobj = uverbs_attr_get_uobject( attrs, MLX5_IB_ATTR_DEVX_ASYNC_CMD_FD_ALLOC_HANDLE); + struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device); ev_file = container_of(uobj, struct devx_async_cmd_event_file, uobj); devx_init_event_queue(&ev_file->ev_queue); + mlx5_cmd_init_async_ctx(mdev->mdev, &ev_file->async_ctx); return 0; } +static void devx_query_callback(int status, struct mlx5_async_work *context) +{ + struct devx_async_data *async_data = + container_of(context, struct devx_async_data, cb_work); + struct ib_uobject *fd_uobj = async_data->fd_uobj; + struct devx_async_cmd_event_file *ev_file; + struct devx_async_event_queue *ev_queue; + unsigned long flags; + + ev_file = container_of(fd_uobj, struct devx_async_cmd_event_file, + uobj); + ev_queue = &ev_file->ev_queue; + + spin_lock_irqsave(&ev_queue->lock, flags); + list_add_tail(&async_data->list, &ev_queue->event_list); + spin_unlock_irqrestore(&ev_queue->lock, flags); + + wake_up_interruptible(&ev_queue->poll_wait); + fput(fd_uobj->object); +} + +#define MAX_ASYNC_BYTES_IN_USE (1024 * 1024) /* 1MB */ + +static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)( + struct uverbs_attr_bundle *attrs) +{ + void *cmd_in = uverbs_attr_get_alloced_ptr(attrs, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_CMD_IN); + struct ib_uobject *uobj = uverbs_attr_get_uobject( + attrs, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_HANDLE); + u16 cmd_out_len; + struct mlx5_ib_ucontext *c = to_mucontext(uobj->context); + struct ib_uobject *fd_uobj; + int err; + int uid; + struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device); + struct devx_async_cmd_event_file *ev_file; + struct devx_async_data *async_data; + + uid = devx_get_uid(c, cmd_in); + if (uid < 0) + return uid; + + if (!devx_is_obj_query_cmd(cmd_in)) + return -EINVAL; + + err = uverbs_get_const(&cmd_out_len, attrs, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_OUT_LEN); + if (err) + return err; + + if (!devx_is_valid_obj_id(uobj, cmd_in)) + return -EINVAL; + + fd_uobj = uverbs_attr_get_uobject(attrs, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_FD); + if (IS_ERR(fd_uobj)) + return PTR_ERR(fd_uobj); + + ev_file = container_of(fd_uobj, struct devx_async_cmd_event_file, + uobj); + + if (atomic_add_return(cmd_out_len, &ev_file->ev_queue.bytes_in_use) > + MAX_ASYNC_BYTES_IN_USE) { + atomic_sub(cmd_out_len, &ev_file->ev_queue.bytes_in_use); + return -EAGAIN; + } + + async_data = kvzalloc(struct_size(async_data, hdr.out_data, + cmd_out_len), GFP_KERNEL); + if (!async_data) { + err = -ENOMEM; + goto sub_bytes; + } + + err = uverbs_copy_from(&async_data->hdr.wr_id, attrs, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_WR_ID); + if (err) + goto free_async; + + async_data->cmd_out_len = cmd_out_len; + async_data->mdev = mdev; + async_data->fd_uobj = fd_uobj; + + get_file(fd_uobj->object); + MLX5_SET(general_obj_in_cmd_hdr, cmd_in, uid, uid); + err = mlx5_cmd_exec_cb(&ev_file->async_ctx, cmd_in, + uverbs_attr_get_len(attrs, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_CMD_IN), + async_data->hdr.out_data, + async_data->cmd_out_len, + devx_query_callback, &async_data->cb_work); + + if (err) + goto cb_err; + + return 0; + +cb_err: + fput(fd_uobj->object); +free_async: + kvfree(async_data); +sub_bytes: + atomic_sub(cmd_out_len, &ev_file->ev_queue.bytes_in_use); + return err; +} + static int devx_umem_get(struct mlx5_ib_dev *dev, struct ib_ucontext *ucontext, struct uverbs_attr_bundle *attrs, struct devx_umem *obj) @@ -1353,6 +1477,17 @@ static ssize_t devx_async_cmd_event_read(struct file *filp, char __user *buf, static int devx_async_cmd_event_close(struct inode *inode, struct file *filp) { + struct ib_uobject *uobj = filp->private_data; + struct devx_async_cmd_event_file *comp_ev_file = container_of( + uobj, struct devx_async_cmd_event_file, uobj); + struct devx_async_data *entry, *tmp; + + spin_lock_irq(&comp_ev_file->ev_queue.lock); + list_for_each_entry_safe(entry, tmp, + &comp_ev_file->ev_queue.event_list, list) + kvfree(entry); + spin_unlock_irq(&comp_ev_file->ev_queue.lock); + uverbs_close_fd(filp); return 0; } @@ -1374,6 +1509,11 @@ const struct file_operations devx_async_cmd_event_fops = { static int devx_hot_unplug_async_cmd_event_file(struct ib_uobject *uobj, enum rdma_remove_reason why) { + struct devx_async_cmd_event_file *comp_ev_file = + container_of(uobj, struct devx_async_cmd_event_file, + uobj); + + mlx5_cmd_cleanup_async_ctx(&comp_ev_file->async_ctx); return 0; }; @@ -1487,6 +1627,27 @@ DECLARE_UVERBS_NAMED_METHOD( UVERBS_ATTR_MIN_SIZE(MLX5_ST_SZ_BYTES(general_obj_out_cmd_hdr)), UA_MANDATORY)); +DECLARE_UVERBS_NAMED_METHOD( + MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY, + UVERBS_ATTR_IDR(MLX5_IB_ATTR_DEVX_OBJ_QUERY_HANDLE, + UVERBS_IDR_ANY_OBJECT, + UVERBS_ACCESS_READ, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN( + MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_IN, + UVERBS_ATTR_MIN_SIZE(MLX5_ST_SZ_BYTES(general_obj_in_cmd_hdr)), + UA_MANDATORY, + UA_ALLOC_AND_COPY), + UVERBS_ATTR_CONST_IN(MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_OUT_LEN, + u16, UA_MANDATORY), + UVERBS_ATTR_FD(MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_FD, + MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD, + UVERBS_ACCESS_READ, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_WR_ID, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY)); + DECLARE_UVERBS_GLOBAL_METHODS(MLX5_IB_OBJECT_DEVX, &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OTHER), &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_QUERY_UAR), @@ -1497,7 +1658,8 @@ DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_DEVX_OBJ, &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_CREATE), &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_DESTROY), &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_MODIFY), - &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_QUERY)); + &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_QUERY), + &UVERBS_METHOD(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)); DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_DEVX_UMEM, UVERBS_TYPE_ALLOC_IDR(devx_umem_cleanup), diff --git a/include/uapi/rdma/mlx5_user_ioctl_cmds.h b/include/uapi/rdma/mlx5_user_ioctl_cmds.h index 6ceae29d77cd..8149d224030b 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_cmds.h +++ b/include/uapi/rdma/mlx5_user_ioctl_cmds.h @@ -84,6 +84,14 @@ enum mlx5_ib_devx_obj_query_attrs { MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_OUT, }; +enum mlx5_ib_devx_obj_query_async_attrs { + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_HANDLE = (1U << UVERBS_ID_NS_SHIFT), + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_CMD_IN, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_FD, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_WR_ID, + MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_OUT_LEN, +}; + enum mlx5_ib_devx_query_eqn_attrs { MLX5_IB_ATTR_DEVX_QUERY_EQN_USER_VEC = (1U << UVERBS_ID_NS_SHIFT), MLX5_IB_ATTR_DEVX_QUERY_EQN_DEV_EQN, @@ -94,6 +102,7 @@ enum mlx5_ib_devx_obj_methods { MLX5_IB_METHOD_DEVX_OBJ_DESTROY, MLX5_IB_METHOD_DEVX_OBJ_MODIFY, MLX5_IB_METHOD_DEVX_OBJ_QUERY, + MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY, }; enum mlx5_ib_devx_umem_reg_attrs { diff --git a/include/uapi/rdma/mlx5_user_ioctl_verbs.h b/include/uapi/rdma/mlx5_user_ioctl_verbs.h index 4ef62c0e8452..4a701033b93f 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_verbs.h +++ b/include/uapi/rdma/mlx5_user_ioctl_verbs.h @@ -51,5 +51,10 @@ enum mlx5_ib_uapi_flow_action_packet_reformat_type { MLX5_IB_UAPI_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L3_TUNNEL = 0x3, }; +struct mlx5_ib_uapi_devx_async_cmd_hdr { + __aligned_u64 wr_id; + __u8 out_data[]; +}; + #endif From 4accbb3fd2638fa11db4670b2d5e8f36a756c51d Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 22 Jan 2019 08:29:58 +0200 Subject: [PATCH 0774/4277] IB/mlx5: Implement the file ops of DEVX async command FD Implement the file ops of the DEVX async command FD, this enables using the FD for reading the events and manage other options on the FD. Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 57 +++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 9ca116155f9c..234e413c7ab8 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1472,7 +1472,49 @@ static int devx_umem_cleanup(struct ib_uobject *uobject, static ssize_t devx_async_cmd_event_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) { - return -EINVAL; + struct devx_async_cmd_event_file *comp_ev_file = filp->private_data; + struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue; + struct devx_async_data *event; + int ret = 0; + size_t eventsz; + + spin_lock_irq(&ev_queue->lock); + + while (list_empty(&ev_queue->event_list)) { + spin_unlock_irq(&ev_queue->lock); + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible( + ev_queue->poll_wait, + !list_empty(&ev_queue->event_list))) { + return -ERESTARTSYS; + } + spin_lock_irq(&ev_queue->lock); + } + + event = list_entry(ev_queue->event_list.next, + struct devx_async_data, list); + eventsz = event->cmd_out_len + + sizeof(struct mlx5_ib_uapi_devx_async_cmd_hdr); + + if (eventsz > count) { + spin_unlock_irq(&ev_queue->lock); + return -ENOSPC; + } + + list_del(ev_queue->event_list.next); + spin_unlock_irq(&ev_queue->lock); + + if (copy_to_user(buf, &event->hdr, eventsz)) + ret = -EFAULT; + else + ret = eventsz; + + atomic_sub(event->cmd_out_len, &ev_queue->bytes_in_use); + kvfree(event); + return ret; } static int devx_async_cmd_event_close(struct inode *inode, struct file *filp) @@ -1495,7 +1537,18 @@ static int devx_async_cmd_event_close(struct inode *inode, struct file *filp) static __poll_t devx_async_cmd_event_poll(struct file *filp, struct poll_table_struct *wait) { - return 0; + struct devx_async_cmd_event_file *comp_ev_file = filp->private_data; + struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue; + __poll_t pollflags = 0; + + poll_wait(filp, &ev_queue->poll_wait, wait); + + spin_lock_irq(&ev_queue->lock); + if (!list_empty(&ev_queue->event_list)) + pollflags = EPOLLIN | EPOLLRDNORM; + spin_unlock_irq(&ev_queue->lock); + + return pollflags; } const struct file_operations devx_async_cmd_event_fops = { From eaebaf77e7cb22fc371b7843370c903f1dd0fc3d Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Tue, 22 Jan 2019 08:29:59 +0200 Subject: [PATCH 0775/4277] IB/mlx5: Implement DEVX hot unplug for async command FD Implement DEVX hot unplug for the async command FD. This is done by managing a list of the inflight commands and wait until all launched work is completed as part of devx_hot_unplug_async_cmd_event_file. Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 234e413c7ab8..84ef296857ef 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1184,6 +1184,7 @@ struct devx_async_event_queue { wait_queue_head_t poll_wait; struct list_head event_list; atomic_t bytes_in_use; + u8 is_destroyed:1; }; struct devx_async_cmd_event_file { @@ -1198,6 +1199,7 @@ static void devx_init_event_queue(struct devx_async_event_queue *ev_queue) INIT_LIST_HEAD(&ev_queue->event_list); init_waitqueue_head(&ev_queue->poll_wait); atomic_set(&ev_queue->bytes_in_use, 0); + ev_queue->is_destroyed = 0; } static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_ASYNC_CMD_FD_ALLOC)( @@ -1488,9 +1490,15 @@ static ssize_t devx_async_cmd_event_read(struct file *filp, char __user *buf, if (wait_event_interruptible( ev_queue->poll_wait, - !list_empty(&ev_queue->event_list))) { + (!list_empty(&ev_queue->event_list) || + ev_queue->is_destroyed))) { return -ERESTARTSYS; } + + if (list_empty(&ev_queue->event_list) && + ev_queue->is_destroyed) + return -EIO; + spin_lock_irq(&ev_queue->lock); } @@ -1544,7 +1552,9 @@ static __poll_t devx_async_cmd_event_poll(struct file *filp, poll_wait(filp, &ev_queue->poll_wait, wait); spin_lock_irq(&ev_queue->lock); - if (!list_empty(&ev_queue->event_list)) + if (ev_queue->is_destroyed) + pollflags = EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; + else if (!list_empty(&ev_queue->event_list)) pollflags = EPOLLIN | EPOLLRDNORM; spin_unlock_irq(&ev_queue->lock); @@ -1565,6 +1575,14 @@ static int devx_hot_unplug_async_cmd_event_file(struct ib_uobject *uobj, struct devx_async_cmd_event_file *comp_ev_file = container_of(uobj, struct devx_async_cmd_event_file, uobj); + struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue; + + spin_lock_irq(&ev_queue->lock); + ev_queue->is_destroyed = 1; + spin_unlock_irq(&ev_queue->lock); + + if (why == RDMA_REMOVE_DRIVER_REMOVE) + wake_up_interruptible(&ev_queue->poll_wait); mlx5_cmd_cleanup_async_ctx(&comp_ev_file->async_ctx); return 0; From 807ffb1e1eabbcdcd46494ee415317aa80ed415c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 28 Jan 2019 13:57:28 +0200 Subject: [PATCH 0776/4277] PCI/AER: Use match_string() helper to simplify the code match_string() returns the array index of a matching string. Use it instead of the open-coded implementation. Signed-off-by: Andy Shevchenko Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index fed29de783e0..f8fc2114ad39 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -117,7 +117,7 @@ bool pci_aer_available(void) static int ecrc_policy = ECRC_POLICY_DEFAULT; -static const char *ecrc_policy_str[] = { +static const char * const ecrc_policy_str[] = { [ECRC_POLICY_DEFAULT] = "bios", [ECRC_POLICY_OFF] = "off", [ECRC_POLICY_ON] = "on" @@ -203,11 +203,8 @@ void pcie_ecrc_get_policy(char *str) { int i; - for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) - if (!strncmp(str, ecrc_policy_str[i], - strlen(ecrc_policy_str[i]))) - break; - if (i >= ARRAY_SIZE(ecrc_policy_str)) + i = match_string(ecrc_policy_str, ARRAY_SIZE(ecrc_policy_str), str); + if (i < 0) return; ecrc_policy = i; From 2a727f6091372231281efaa8a5f56ee79509fd83 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 22:49:10 +0100 Subject: [PATCH 0777/4277] PCI: ibmphp: Turn semaphores into completions or mutexes The sem_exit variable is conceptually a completion, so it should be called that. Similarly, the semOperations semaphore is a simple mutex, and can be changed into that, respectively. With both converted, the ibmphp_hpc_initvars() function is no longer used and can be removed. Signed-off-by: Arnd Bergmann Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/ibmphp.h | 1 - drivers/pci/hotplug/ibmphp_core.c | 2 -- drivers/pci/hotplug/ibmphp_hpc.c | 47 +++++++++---------------------- 3 files changed, 14 insertions(+), 36 deletions(-) diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h index b89f850c3a4e..e90a4ebf6550 100644 --- a/drivers/pci/hotplug/ibmphp.h +++ b/drivers/pci/hotplug/ibmphp.h @@ -378,7 +378,6 @@ int ibmphp_add_pfmem_from_mem(struct resource_node *); struct bus_node *ibmphp_find_res_bus(u8); void ibmphp_print_test(void); /* for debugging purposes */ -void ibmphp_hpc_initvars(void); int ibmphp_hpc_readslot(struct slot *, u8, u8 *); int ibmphp_hpc_writeslot(struct slot *, u8); void ibmphp_lock_operations(void); diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 08a58e911fc2..17124254d897 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -1277,8 +1277,6 @@ static int __init ibmphp_init(void) ibmphp_debug = debug; - ibmphp_hpc_initvars(); - for (i = 0; i < 16; i++) irqs[i] = 0; diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c index 752c384cbd4c..508a62a6b5f9 100644 --- a/drivers/pci/hotplug/ibmphp_hpc.c +++ b/drivers/pci/hotplug/ibmphp_hpc.c @@ -15,13 +15,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include #include "ibmphp.h" @@ -88,10 +88,10 @@ static int to_debug = 0; //---------------------------------------------------------------------------- // global variables //---------------------------------------------------------------------------- -static struct mutex sem_hpcaccess; // lock access to HPC -static struct semaphore semOperations; // lock all operations and +static DEFINE_MUTEX(sem_hpcaccess); // lock access to HPC +static DEFINE_MUTEX(operations_mutex); // lock all operations and // access to data structures -static struct semaphore sem_exit; // make sure polling thread goes away +static DECLARE_COMPLETION(exit_complete); // make sure polling thread goes away static struct task_struct *ibmphp_poll_thread; //---------------------------------------------------------------------------- // local function prototypes @@ -109,23 +109,6 @@ static int hpc_wait_ctlr_notworking(int, struct controller *, void __iomem *, u8 //---------------------------------------------------------------------------- -/*---------------------------------------------------------------------- -* Name: ibmphp_hpc_initvars -* -* Action: initialize semaphores and variables -*---------------------------------------------------------------------*/ -void __init ibmphp_hpc_initvars(void) -{ - debug("%s - Entry\n", __func__); - - mutex_init(&sem_hpcaccess); - sema_init(&semOperations, 1); - sema_init(&sem_exit, 0); - to_debug = 0; - - debug("%s - Exit\n", __func__); -} - /*---------------------------------------------------------------------- * Name: i2c_ctrl_read * @@ -780,7 +763,7 @@ void free_hpc_access(void) *---------------------------------------------------------------------*/ void ibmphp_lock_operations(void) { - down(&semOperations); + mutex_lock(&operations_mutex); to_debug = 1; } @@ -790,7 +773,7 @@ void ibmphp_lock_operations(void) void ibmphp_unlock_operations(void) { debug("%s - Entry\n", __func__); - up(&semOperations); + mutex_unlock(&operations_mutex); to_debug = 0; debug("%s - Exit\n", __func__); } @@ -816,7 +799,7 @@ static int poll_hpc(void *data) while (!kthread_should_stop()) { /* try to get the lock to do some kind of hardware access */ - down(&semOperations); + mutex_lock(&operations_mutex); switch (poll_state) { case POLL_LATCH_REGISTER: @@ -871,13 +854,13 @@ static int poll_hpc(void *data) break; case POLL_SLEEP: /* don't sleep with a lock on the hardware */ - up(&semOperations); + mutex_unlock(&operations_mutex); msleep(POLL_INTERVAL_SEC * 1000); if (kthread_should_stop()) goto out_sleep; - down(&semOperations); + mutex_lock(&operations_mutex); if (poll_count >= POLL_LATCH_CNT) { poll_count = 0; @@ -887,12 +870,12 @@ static int poll_hpc(void *data) break; } /* give up the hardware semaphore */ - up(&semOperations); + mutex_unlock(&operations_mutex); /* sleep for a short time just for good measure */ out_sleep: msleep(100); } - up(&sem_exit); + complete(&exit_complete); debug("%s - Exit\n", __func__); return 0; } @@ -1060,9 +1043,9 @@ void __exit ibmphp_hpc_stop_poll_thread(void) debug("after locking operations\n"); // wait for poll thread to exit - debug("before sem_exit down\n"); - down(&sem_exit); - debug("after sem_exit down\n"); + debug("before exit_complete down\n"); + wait_for_completion(&exit_complete); + debug("after exit_completion down\n"); // cleanup debug("before free_hpc_access\n"); @@ -1070,8 +1053,6 @@ void __exit ibmphp_hpc_stop_poll_thread(void) debug("after free_hpc_access\n"); ibmphp_unlock_operations(); debug("after unlock operations\n"); - up(&sem_exit); - debug("after sem exit up\n"); debug("%s - Exit\n", __func__); } From 8aa04ad3b39396e315b23448c56d5465200fa777 Mon Sep 17 00:00:00 2001 From: Adit Ranadive Date: Sat, 26 Jan 2019 05:09:36 +0000 Subject: [PATCH 0778/4277] RDMA/vmw_pvrdma: Support upto 64-bit PFNs Update the driver to use the new device capability to report 64-bit UAR PFNs. Reviewed-by: Jorgen Hansen Signed-off-by: Adit Ranadive Reviewed-by: Vishnu Dasa Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h | 15 ++++++++++----- drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c | 6 +++++- drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c | 7 ++++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h index 6fd5a8f4e2f6..8f9749d54688 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h @@ -57,7 +57,8 @@ #define PVRDMA_ROCEV1_VERSION 17 #define PVRDMA_ROCEV2_VERSION 18 -#define PVRDMA_VERSION PVRDMA_ROCEV2_VERSION +#define PVRDMA_PPN64_VERSION 19 +#define PVRDMA_VERSION PVRDMA_PPN64_VERSION #define PVRDMA_BOARD_ID 1 #define PVRDMA_REV_ID 1 @@ -279,8 +280,10 @@ struct pvrdma_device_shared_region { /* W: Async ring page info. */ struct pvrdma_ring_page_info cq_ring_pages; /* W: CQ ring page info. */ - u32 uar_pfn; /* W: UAR pageframe. */ - u32 pad2; /* Pad to 8-byte align. */ + union { + u32 uar_pfn; /* W: UAR pageframe. */ + u64 uar_pfn64; /* W: 64-bit UAR page frame. */ + }; struct pvrdma_device_caps caps; /* R: Device capabilities. */ }; @@ -411,8 +414,10 @@ struct pvrdma_cmd_query_pkey_resp { struct pvrdma_cmd_create_uc { struct pvrdma_cmd_hdr hdr; - u32 pfn; /* UAR page frame number */ - u8 reserved[4]; + union { + u32 pfn; /* UAR page frame number */ + u64 pfn64; /* 64-bit UAR page frame number */ + }; }; struct pvrdma_cmd_create_uc_resp { diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index b5864750fb6e..a5f02276d903 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -905,7 +905,11 @@ static int pvrdma_pci_probe(struct pci_dev *pdev, PVRDMA_GOS_BITS_64; dev->dsr->gos_info.gos_type = PVRDMA_GOS_TYPE_LINUX; dev->dsr->gos_info.gos_ver = 1; - dev->dsr->uar_pfn = dev->driver_uar.pfn; + + if (dev->dsr_version < PVRDMA_PPN64_VERSION) + dev->dsr->uar_pfn = dev->driver_uar.pfn; + else + dev->dsr->uar_pfn64 = dev->driver_uar.pfn; /* Command slot. */ dev->cmd_slot = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index e10149248ce2..fafb2add3b44 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -340,7 +340,12 @@ struct ib_ucontext *pvrdma_alloc_ucontext(struct ib_device *ibdev, /* get ctx_handle from host */ memset(cmd, 0, sizeof(*cmd)); - cmd->pfn = context->uar.pfn; + + if (vdev->dsr_version < PVRDMA_PPN64_VERSION) + cmd->pfn = context->uar.pfn; + else + cmd->pfn64 = context->uar.pfn; + cmd->hdr.cmd = PVRDMA_CMD_CREATE_UC; ret = pvrdma_cmd_post(vdev, &req, &rsp, PVRDMA_CMD_CREATE_UC_RESP); if (ret < 0) { From ed0bc2658e1b7c083fc7f49cc6b56ea66f5ed350 Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Tue, 29 Jan 2019 13:55:48 +0200 Subject: [PATCH 0779/4277] IB/ipoib: Make ipoib_intercept_dev_id_attr() static The function ipoib_intercept_dev_id_attr() is only used in ipoib_main.c Fixes: f6350da41dc7 ("IB/ipoib: Log sysfs 'dev_id' accesses from userspace") Signed-off-by: Kamal Heib Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 45ef3b0f03c5..3d3407d1feb4 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -2411,7 +2411,7 @@ static ssize_t dev_id_show(struct device *dev, } static DEVICE_ATTR_RO(dev_id); -int ipoib_intercept_dev_id_attr(struct net_device *dev) +static int ipoib_intercept_dev_id_attr(struct net_device *dev) { device_remove_file(&dev->dev, &dev_attr_dev_id); return device_create_file(&dev->dev, &dev_attr_dev_id); From 4107102d60791d04b2ecf6e6d78bfc53ea6e8db3 Mon Sep 17 00:00:00 2001 From: Ramon Fried Date: Thu, 24 May 2018 22:21:41 +0300 Subject: [PATCH 0780/4277] remoteproc: qcom_q6v5: don't auto boot remote processor Sometimes that rmtfs userspace module is not brought up fast enough and the modem crashes. disabling automated boot in the driver and triggering the boot from user-space sovles the problem. Acked-by: Sibi Sankar Signed-off-by: Ramon Fried Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 07d1cc52a647..71cfac580967 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1269,6 +1269,8 @@ static int q6v5_probe(struct platform_device *pdev) return -ENOMEM; } + rproc->auto_boot = false; + qproc = (struct q6v5 *)rproc->priv; qproc->dev = &pdev->dev; qproc->rproc = rproc; From a5a4e02d083d88d7c8b9716c30eb42d6d5b0b1cc Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Tue, 15 Jan 2019 01:20:01 +0530 Subject: [PATCH 0781/4277] remoteproc: qcom: Add support for parsing fw dt bindings Add support for parsing "firmware-name" dt bindings which specifies the relative paths of mba/modem/pas image as strings. Fallback to the default paths for mba/modem/pas image on -EINVAL. Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 47 +++++++++++++++++++++++------- drivers/remoteproc/qcom_q6v5_pas.c | 9 +++++- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 71cfac580967..7c344771c335 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -188,6 +188,7 @@ struct q6v5 { bool has_alt_reset; int mpss_perm; int mba_perm; + const char *hexagon_mdt_image; int version; }; @@ -860,17 +861,26 @@ static int q6v5_mpss_load(struct q6v5 *qproc) phys_addr_t min_addr = PHYS_ADDR_MAX; phys_addr_t max_addr = 0; bool relocate = false; - char seg_name[10]; + char *fw_name; + size_t fw_name_len; ssize_t offset; size_t size = 0; void *ptr; int ret; int i; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); + fw_name_len = strlen(qproc->hexagon_mdt_image); + if (fw_name_len <= 4) + return -EINVAL; + + fw_name = kstrdup(qproc->hexagon_mdt_image, GFP_KERNEL); + if (!fw_name) + return -ENOMEM; + + ret = request_firmware(&fw, fw_name, qproc->dev); if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); - return ret; + dev_err(qproc->dev, "unable to load %s\n", fw_name); + goto out; } /* Initialize the RMB validator */ @@ -918,10 +928,11 @@ static int q6v5_mpss_load(struct q6v5 *qproc) ptr = qproc->mpss_region + offset; if (phdr->p_filesz) { - snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i); - ret = request_firmware(&seg_fw, seg_name, qproc->dev); + /* Replace "xxx.xxx" with "xxx.bxx" */ + sprintf(fw_name + fw_name_len - 3, "b%02d", i); + ret = request_firmware(&seg_fw, fw_name, qproc->dev); if (ret) { - dev_err(qproc->dev, "failed to load %s\n", seg_name); + dev_err(qproc->dev, "failed to load %s\n", fw_name); goto release_firmware; } @@ -960,6 +971,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc) release_firmware: release_firmware(fw); +out: + kfree(fw_name); return ret < 0 ? ret : 0; } @@ -1075,9 +1088,10 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, unsigned long i; int ret; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); + ret = request_firmware(&fw, qproc->hexagon_mdt_image, qproc->dev); if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); + dev_err(qproc->dev, "unable to load %s\n", + qproc->hexagon_mdt_image); return ret; } @@ -1253,6 +1267,7 @@ static int q6v5_probe(struct platform_device *pdev) const struct rproc_hexagon_res *desc; struct q6v5 *qproc; struct rproc *rproc; + const char *mba_image; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -1262,8 +1277,14 @@ static int q6v5_probe(struct platform_device *pdev) if (desc->need_mem_protection && !qcom_scm_is_available()) return -EPROBE_DEFER; + mba_image = desc->hexagon_mba_image; + ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", + 0, &mba_image); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, - desc->hexagon_mba_image, sizeof(*qproc)); + mba_image, sizeof(*qproc)); if (!rproc) { dev_err(&pdev->dev, "failed to allocate rproc\n"); return -ENOMEM; @@ -1274,6 +1295,12 @@ static int q6v5_probe(struct platform_device *pdev) qproc = (struct q6v5 *)rproc->priv; qproc->dev = &pdev->dev; qproc->rproc = rproc; + qproc->hexagon_mdt_image = "modem.mdt"; + ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", + 1, &qproc->hexagon_mdt_image); + if (ret < 0 && ret != -EINVAL) + return ret; + platform_set_drvdata(pdev, qproc); ret = q6v5_init_mem(qproc, pdev); diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index c529a04c14ed..f280f196d007 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -258,6 +258,7 @@ static int adsp_probe(struct platform_device *pdev) const struct adsp_data *desc; struct qcom_adsp *adsp; struct rproc *rproc; + const char *fw_name; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -267,8 +268,14 @@ static int adsp_probe(struct platform_device *pdev) if (!qcom_scm_is_available()) return -EPROBE_DEFER; + fw_name = desc->firmware_name; + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &fw_name); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, - desc->firmware_name, sizeof(*adsp)); + fw_name, sizeof(*adsp)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; From f596c8844fe1d0022007ae6c7a377361fb653eff Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 29 Jan 2019 22:06:41 -0800 Subject: [PATCH 0782/4277] nfit: Fix nfit_intel_shutdown_status() command submission The implementation is broken in all the ways the unit test did not touch: 1/ The local definition of in_buf and in_obj violated C99 initializer expectations for zeroing. By only initializing 2 out of the three struct members the compiler was free to zero-initialize the remaining entry even though the aliased location in the union was initialized. 2/ The implementation made assumptions about the state of the 'smart' payload after command execution that are satisfied by acpi_nfit_ctl(), but not acpi_evaluate_dsm(). 3/ populate_shutdown_status() is skipped on Intel NVDIMMs due to the early return for skipping the common _LS{I,R,W} enabling. 4/ The input length should be zero. This breakage was missed due to the unit test implementation only testing the case where nfit_intel_shutdown_status() returns a valid payload. Much of this complexity would be saved if acpi_nfit_ctl() could be used, but that currently requires a 'struct nvdimm *' argument and one is not created until later in the init process. The health result is needed before the device is created because the payload gates whether the nmemX/nfit/dirty_shutdown property is visible in sysfs. Cc: Fixes: 0ead11181fe0 ("acpi, nfit: Collect shutdown status") Reported-by: Dexuan Cui Reviewed-by: Dexuan Cui Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 41 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 011d3db19c80..95db6a2d0d6b 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1738,14 +1738,14 @@ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) { + struct device *dev = &nfit_mem->adev->dev; struct nd_intel_smart smart = { 0 }; union acpi_object in_buf = { - .type = ACPI_TYPE_BUFFER, - .buffer.pointer = (char *) &smart, - .buffer.length = sizeof(smart), + .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = 0, }; union acpi_object in_obj = { - .type = ACPI_TYPE_PACKAGE, + .package.type = ACPI_TYPE_PACKAGE, .package.count = 1, .package.elements = &in_buf, }; @@ -1760,8 +1760,15 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) return; out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj); - if (!out_obj) + if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER + || out_obj->buffer.length < sizeof(smart)) { + dev_dbg(dev->parent, "%s: failed to retrieve initial health\n", + dev_name(dev)); + ACPI_FREE(out_obj); return; + } + memcpy(&smart, out_obj->buffer.pointer, sizeof(smart)); + ACPI_FREE(out_obj); if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) { if (smart.shutdown_state) @@ -1772,7 +1779,6 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags); nfit_mem->dirty_shutdown = smart.shutdown_count; } - ACPI_FREE(out_obj); } static void populate_shutdown_status(struct nfit_mem *nfit_mem) @@ -1887,18 +1893,19 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, | 1 << ND_CMD_SET_CONFIG_DATA; if (family == NVDIMM_FAMILY_INTEL && (dsm_mask & label_mask) == label_mask) - return 0; + /* skip _LS{I,R,W} enabling */; + else { + if (acpi_nvdimm_has_method(adev_dimm, "_LSI") + && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { + dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); + set_bit(NFIT_MEM_LSR, &nfit_mem->flags); + } - if (acpi_nvdimm_has_method(adev_dimm, "_LSI") - && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { - dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); - set_bit(NFIT_MEM_LSR, &nfit_mem->flags); - } - - if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags) - && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { - dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); - set_bit(NFIT_MEM_LSW, &nfit_mem->flags); + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags) + && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { + dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); + set_bit(NFIT_MEM_LSW, &nfit_mem->flags); + } } populate_shutdown_status(nfit_mem); From 43f89877f26671c6309cd87d7364b1a3e66e71cf Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 30 Jan 2019 01:23:01 +0000 Subject: [PATCH 0783/4277] nfit: acpi_nfit_ctl(): Check out_obj->type in the right place In the case of ND_CMD_CALL, we should also check out_obj->type. The patch uses out_obj->type, which is a short alias to out_obj->package.type. Fixes: 31eca76ba2fc ("nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism") Cc: Signed-off-by: Dexuan Cui Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 95db6a2d0d6b..1598e3a121a6 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -535,6 +535,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, return -EINVAL; } + if (out_obj->type != ACPI_TYPE_BUFFER) { + dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n", + dimm_name, cmd_name, out_obj->type); + rc = -EINVAL; + goto out; + } + if (call_pkg) { call_pkg->nd_fw_size = out_obj->buffer.length; memcpy(call_pkg->nd_payload + call_pkg->nd_size_in, @@ -553,13 +560,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, return 0; } - if (out_obj->package.type != ACPI_TYPE_BUFFER) { - dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n", - dimm_name, cmd_name, out_obj->type); - rc = -EINVAL; - goto out; - } - dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name, cmd_name, out_obj->buffer.length); print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4, From 1194c4133195dfcb6c5fc0935d54bbed872a5285 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Tue, 29 Jan 2019 00:56:17 +0000 Subject: [PATCH 0784/4277] nfit: Add Hyper-V NVDIMM DSM command set to white list Add the Hyper-V _DSM command set to the white list of NVDIMM command sets. This command set is documented at http://www.uefi.org/RFIC_LIST (see "Virtual NVDIMM 0x1901"). Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 17 ++++++++++++++--- drivers/acpi/nfit/nfit.h | 6 +++++- include/uapi/linux/ndctl.h | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 1598e3a121a6..4a7e8b1fa43b 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1846,9 +1846,17 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, dev_set_drvdata(&adev_dimm->dev, nfit_mem); /* - * Until standardization materializes we need to consider 4 - * different command sets. Note, that checking for function0 (bit0) - * tells us if any commands are reachable through this GUID. + * There are 4 "legacy" NVDIMM command sets + * (NVDIMM_FAMILY_{INTEL,MSFT,HPE1,HPE2}) that were created before + * an EFI working group was established to constrain this + * proliferation. The nfit driver probes for the supported command + * set by GUID. Note, if you're a platform developer looking to add + * a new command set to this probe, consider using an existing set, + * or otherwise seek approval to publish the command set at + * http://www.uefi.org/RFIC_LIST. + * + * Note, that checking for function0 (bit0) tells us if any commands + * are reachable through this GUID. */ for (i = 0; i <= NVDIMM_FAMILY_MAX; i++) if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) @@ -1871,6 +1879,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, dsm_mask &= ~(1 << 8); } else if (nfit_mem->family == NVDIMM_FAMILY_MSFT) { dsm_mask = 0xffffffff; + } else if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) { + dsm_mask = 0x1f; } else { dev_dbg(dev, "unknown dimm command family\n"); nfit_mem->family = -1; @@ -3714,6 +3724,7 @@ static __init int nfit_init(void) guid_parse(UUID_NFIT_DIMM_N_HPE1, &nfit_uuid[NFIT_DEV_DIMM_N_HPE1]); guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]); guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]); + guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]); nfit_wq = create_singlethread_workqueue("nfit"); if (!nfit_wq) diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 33691aecfcee..4de167b4f76f 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -34,11 +34,14 @@ /* https://msdn.microsoft.com/library/windows/hardware/mt604741 */ #define UUID_NFIT_DIMM_N_MSFT "1ee68b36-d4bd-4a1a-9a16-4f8e53d46e05" +/* http://www.uefi.org/RFIC_LIST (see "Virtual NVDIMM 0x1901") */ +#define UUID_NFIT_DIMM_N_HYPERV "5746c5f2-a9a2-4264-ad0e-e4ddc9e09e80" + #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ | ACPI_NFIT_MEM_NOT_ARMED | ACPI_NFIT_MEM_MAP_FAILED) -#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_MSFT +#define NVDIMM_FAMILY_MAX NVDIMM_FAMILY_HYPERV #define NVDIMM_STANDARD_CMDMASK \ (1 << ND_CMD_SMART | 1 << ND_CMD_SMART_THRESHOLD | 1 << ND_CMD_DIMM_FLAGS \ @@ -94,6 +97,7 @@ enum nfit_uuids { NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1, NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2, NFIT_DEV_DIMM_N_MSFT = NVDIMM_FAMILY_MSFT, + NFIT_DEV_DIMM_N_HYPERV = NVDIMM_FAMILY_HYPERV, NFIT_SPA_VOLATILE, NFIT_SPA_PM, NFIT_SPA_DCR, diff --git a/include/uapi/linux/ndctl.h b/include/uapi/linux/ndctl.h index f57c9e434d2d..de5d90212409 100644 --- a/include/uapi/linux/ndctl.h +++ b/include/uapi/linux/ndctl.h @@ -243,6 +243,7 @@ struct nd_cmd_pkg { #define NVDIMM_FAMILY_HPE1 1 #define NVDIMM_FAMILY_HPE2 2 #define NVDIMM_FAMILY_MSFT 3 +#define NVDIMM_FAMILY_HYPERV 4 #define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\ struct nd_cmd_pkg) From ff656e47a91698c923d59f5e7f7df1a625f6095c Mon Sep 17 00:00:00 2001 From: Zhou Yanjie Date: Mon, 28 Jan 2019 23:19:57 +0800 Subject: [PATCH 0785/4277] Pinctrl: Ingenic: Fix bugs caused by differences between JZ4770 and JZ4780. Delete uart4 and i2c3/4 from JZ4770: According to the datasheet, only JZ4780 have uart4 and i2c3/4. So we remove it from the JZ4770 code and add a section corresponding the JZ4780. Fix bugs in i2c0/1: The pin number was wrong in the original code. Fix bugs in uart2: JZ4770 and JZ4780 have different uart2 pins. So the original section JZ4770 has been modified and the corresponding section of JZ4780 has been added. Fix bugs in mmc0: JZ4770 and JZ4780 assigned different pins to mmc0's 4~7 data lines. So the original section JZ4770 has been modified and the corresponding section of JZ4780 has been added. Fix bugs in mmc1: JZ4770's mmc1 has 8bit mode, while JZ4780 doesn't. So the original section JZ4770 has been modified and the corresponding section of JZ4780 has been added. Fix bugs in nemc: JZ4770's nemc has 16bit mode, while JZ4780 doesn't. So the original section JZ4770 has been modified and the corresponding section of JZ4780 has been added. And add missing cs2~5 groups for JZ4770 and JZ4780. Fix bugs in cim: JZ4770's cim has 12bit mode, while JZ4780 doesn't. So the original section JZ4770 has been modified and the corresponding section of JZ4780 has been added. Fix bugs in lcd: Both JZ4770 and JZ4780 lcd should be 24bit instead of 32bit. Signed-off-by: Zhou Yanjie Reviewed-by: Paul Cercueil Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-ingenic.c | 249 +++++++++++++++++++++++------- 1 file changed, 191 insertions(+), 58 deletions(-) diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index db6b48ea5f47..710062be5241 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -321,23 +321,26 @@ static int jz4770_uart0_data_pins[] = { 0xa0, 0xa3, }; static int jz4770_uart0_hwflow_pins[] = { 0xa1, 0xa2, }; static int jz4770_uart1_data_pins[] = { 0x7a, 0x7c, }; static int jz4770_uart1_hwflow_pins[] = { 0x7b, 0x7d, }; -static int jz4770_uart2_data_pins[] = { 0x66, 0x67, }; -static int jz4770_uart2_hwflow_pins[] = { 0x65, 0x64, }; +static int jz4770_uart2_data_pins[] = { 0x5c, 0x5e, }; +static int jz4770_uart2_hwflow_pins[] = { 0x5d, 0x5f, }; static int jz4770_uart3_data_pins[] = { 0x6c, 0x85, }; static int jz4770_uart3_hwflow_pins[] = { 0x88, 0x89, }; -static int jz4770_uart4_data_pins[] = { 0x54, 0x4a, }; -static int jz4770_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, }; -static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, }; static int jz4770_mmc0_1bit_a_pins[] = { 0x12, 0x13, 0x14, }; -static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, }; static int jz4770_mmc0_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; -static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, }; +static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc0_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; static int jz4770_mmc1_1bit_d_pins[] = { 0x78, 0x79, 0x74, }; -static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, }; static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; -static int jz4770_nemc_data_pins[] = { +static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc1_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; +static int jz4770_nemc_8bit_data_pins[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, }; +static int jz4770_nemc_16bit_data_pins[] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +}; static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, }; static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, }; static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, }; @@ -348,20 +351,21 @@ static int jz4770_nemc_cs3_pins[] = { 0x17, }; static int jz4770_nemc_cs4_pins[] = { 0x18, }; static int jz4770_nemc_cs5_pins[] = { 0x19, }; static int jz4770_nemc_cs6_pins[] = { 0x1a, }; -static int jz4770_i2c0_pins[] = { 0x6e, 0x6f, }; -static int jz4770_i2c1_pins[] = { 0x8e, 0x8f, }; +static int jz4770_i2c0_pins[] = { 0x7e, 0x7f, }; +static int jz4770_i2c1_pins[] = { 0x9e, 0x9f, }; static int jz4770_i2c2_pins[] = { 0xb0, 0xb1, }; -static int jz4770_i2c3_pins[] = { 0x6a, 0x6b, }; -static int jz4770_i2c4_e_pins[] = { 0x8c, 0x8d, }; -static int jz4770_i2c4_f_pins[] = { 0xb9, 0xb8, }; -static int jz4770_cim_pins[] = { - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, +static int jz4770_cim_8bit_pins[] = { + 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, }; -static int jz4770_lcd_32bit_pins[] = { +static int jz4770_cim_12bit_pins[] = { + 0x32, 0x33, 0xb0, 0xb1, +}; +static int jz4770_lcd_24bit_pins[] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x51, + 0x58, 0x59, 0x5a, 0x5b, }; static int jz4770_pwm_pwm0_pins[] = { 0x80, }; static int jz4770_pwm_pwm1_pins[] = { 0x81, }; @@ -376,21 +380,22 @@ static int jz4770_uart0_data_funcs[] = { 0, 0, }; static int jz4770_uart0_hwflow_funcs[] = { 0, 0, }; static int jz4770_uart1_data_funcs[] = { 0, 0, }; static int jz4770_uart1_hwflow_funcs[] = { 0, 0, }; -static int jz4770_uart2_data_funcs[] = { 1, 1, }; -static int jz4770_uart2_hwflow_funcs[] = { 1, 1, }; +static int jz4770_uart2_data_funcs[] = { 0, 0, }; +static int jz4770_uart2_hwflow_funcs[] = { 0, 0, }; static int jz4770_uart3_data_funcs[] = { 0, 1, }; static int jz4770_uart3_hwflow_funcs[] = { 0, 0, }; -static int jz4770_uart4_data_funcs[] = { 2, 2, }; -static int jz4770_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, }; -static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, }; static int jz4770_mmc0_1bit_a_funcs[] = { 1, 1, 0, }; -static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, }; static int jz4770_mmc0_1bit_e_funcs[] = { 0, 0, 0, }; -static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc0_8bit_e_funcs[] = { 0, 0, 0, 0, }; static int jz4770_mmc1_1bit_d_funcs[] = { 0, 0, 0, }; -static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, }; +static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, }; static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, }; -static int jz4770_nemc_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, }; +static int jz4770_mmc1_8bit_e_funcs[] = { 1, 1, 1, 1, }; +static int jz4770_nemc_8bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_nemc_16bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, }; static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, }; static int jz4770_nemc_rd_we_funcs[] = { 0, 0, }; @@ -404,14 +409,13 @@ static int jz4770_nemc_cs6_funcs[] = { 0, }; static int jz4770_i2c0_funcs[] = { 0, 0, }; static int jz4770_i2c1_funcs[] = { 0, 0, }; static int jz4770_i2c2_funcs[] = { 2, 2, }; -static int jz4770_i2c3_funcs[] = { 1, 1, }; -static int jz4770_i2c4_e_funcs[] = { 1, 1, }; -static int jz4770_i2c4_f_funcs[] = { 1, 1, }; -static int jz4770_cim_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -static int jz4770_lcd_32bit_funcs[] = { +static int jz4770_cim_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_cim_12bit_funcs[] = { 0, 0, 0, 0, }; +static int jz4770_lcd_24bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, }; static int jz4770_pwm_pwm0_funcs[] = { 0, }; static int jz4770_pwm_pwm1_funcs[] = { 0, }; @@ -431,17 +435,18 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("uart2-hwflow", jz4770_uart2_hwflow), INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data), INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow), - INGENIC_PIN_GROUP("uart4-data", jz4770_uart4_data), - INGENIC_PIN_GROUP("mmc0-8bit-a", jz4770_mmc0_8bit_a), - INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a), INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a), - INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e), + INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a), INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e), - INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), + INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e), + INGENIC_PIN_GROUP("mmc0-8bit-e", jz4770_mmc0_8bit_e), INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d), - INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e), - INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_data), + INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("mmc1-8bit-e", jz4770_mmc1_8bit_e), + INGENIC_PIN_GROUP("nemc-8bit-data", jz4770_nemc_8bit_data), + INGENIC_PIN_GROUP("nemc-16bit-data", jz4770_nemc_16bit_data), INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale), INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr), INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we), @@ -455,11 +460,9 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0), INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1), INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2), - INGENIC_PIN_GROUP("i2c3-data", jz4770_i2c3), - INGENIC_PIN_GROUP("i2c4-data-e", jz4770_i2c4_e), - INGENIC_PIN_GROUP("i2c4-data-f", jz4770_i2c4_f), - INGENIC_PIN_GROUP("cim-data", jz4770_cim), - INGENIC_PIN_GROUP("lcd-32bit", jz4770_lcd_32bit), + INGENIC_PIN_GROUP("cim-data-8bit", jz4770_cim_8bit), + INGENIC_PIN_GROUP("cim-data-12bit", jz4770_cim_12bit), + INGENIC_PIN_GROUP("lcd-24bit", jz4770_lcd_24bit), { "lcd-no-pins", }, INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0), INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1), @@ -475,26 +478,29 @@ static const char *jz4770_uart0_groups[] = { "uart0-data", "uart0-hwflow", }; static const char *jz4770_uart1_groups[] = { "uart1-data", "uart1-hwflow", }; static const char *jz4770_uart2_groups[] = { "uart2-data", "uart2-hwflow", }; static const char *jz4770_uart3_groups[] = { "uart3-data", "uart3-hwflow", }; -static const char *jz4770_uart4_groups[] = { "uart4-data", }; static const char *jz4770_mmc0_groups[] = { - "mmc0-8bit-a", "mmc0-4bit-a", "mmc0-1bit-a", - "mmc0-1bit-e", "mmc0-4bit-e", + "mmc0-1bit-a", "mmc0-4bit-a", + "mmc0-1bit-e", "mmc0-4bit-e", "mmc0-8bit-e", }; static const char *jz4770_mmc1_groups[] = { - "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e", + "mmc1-1bit-d", "mmc1-4bit-d", + "mmc1-1bit-e", "mmc1-4bit-e", "mmc1-8bit-e", }; static const char *jz4770_nemc_groups[] = { - "nemc-data", "nemc-cle-ale", "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", + "nemc-8bit-data", "nemc-16bit-data", "nemc-cle-ale", + "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", }; static const char *jz4770_cs1_groups[] = { "nemc-cs1", }; +static const char *jz4770_cs2_groups[] = { "nemc-cs2", }; +static const char *jz4770_cs3_groups[] = { "nemc-cs3", }; +static const char *jz4770_cs4_groups[] = { "nemc-cs4", }; +static const char *jz4770_cs5_groups[] = { "nemc-cs5", }; static const char *jz4770_cs6_groups[] = { "nemc-cs6", }; static const char *jz4770_i2c0_groups[] = { "i2c0-data", }; static const char *jz4770_i2c1_groups[] = { "i2c1-data", }; static const char *jz4770_i2c2_groups[] = { "i2c2-data", }; -static const char *jz4770_i2c3_groups[] = { "i2c3-data", }; -static const char *jz4770_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", }; -static const char *jz4770_cim_groups[] = { "cim-data", }; -static const char *jz4770_lcd_groups[] = { "lcd-32bit", "lcd-no-pins", }; +static const char *jz4770_cim_groups[] = { "cim-data-8bit", "cim-data-12bit", }; +static const char *jz4770_lcd_groups[] = { "lcd-24bit", "lcd-no-pins", }; static const char *jz4770_pwm0_groups[] = { "pwm0", }; static const char *jz4770_pwm1_groups[] = { "pwm1", }; static const char *jz4770_pwm2_groups[] = { "pwm2", }; @@ -509,17 +515,18 @@ static const struct function_desc jz4770_functions[] = { { "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), }, { "uart2", jz4770_uart2_groups, ARRAY_SIZE(jz4770_uart2_groups), }, { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), }, - { "uart4", jz4770_uart4_groups, ARRAY_SIZE(jz4770_uart4_groups), }, { "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), }, { "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), }, { "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), }, { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), }, + { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), }, + { "nemc-cs3", jz4770_cs3_groups, ARRAY_SIZE(jz4770_cs3_groups), }, + { "nemc-cs4", jz4770_cs4_groups, ARRAY_SIZE(jz4770_cs4_groups), }, + { "nemc-cs5", jz4770_cs5_groups, ARRAY_SIZE(jz4770_cs5_groups), }, { "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), }, { "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), }, { "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), }, { "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), }, - { "i2c3", jz4770_i2c3_groups, ARRAY_SIZE(jz4770_i2c3_groups), }, - { "i2c4", jz4770_i2c4_groups, ARRAY_SIZE(jz4770_i2c4_groups), }, { "cim", jz4770_cim_groups, ARRAY_SIZE(jz4770_cim_groups), }, { "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), }, { "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), }, @@ -542,6 +549,130 @@ static const struct ingenic_chip_info jz4770_chip_info = { .pull_downs = jz4770_pull_downs, }; +static int jz4780_uart2_data_pins[] = { 0x66, 0x67, }; +static int jz4780_uart2_hwflow_pins[] = { 0x65, 0x64, }; +static int jz4780_uart4_data_pins[] = { 0x54, 0x4a, }; +static int jz4780_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, }; +static int jz4780_i2c3_pins[] = { 0x6a, 0x6b, }; +static int jz4780_i2c4_e_pins[] = { 0x8c, 0x8d, }; +static int jz4780_i2c4_f_pins[] = { 0xb9, 0xb8, }; + +static int jz4780_uart2_data_funcs[] = { 1, 1, }; +static int jz4780_uart2_hwflow_funcs[] = { 1, 1, }; +static int jz4780_uart4_data_funcs[] = { 2, 2, }; +static int jz4780_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, }; +static int jz4780_i2c3_funcs[] = { 1, 1, }; +static int jz4780_i2c4_e_funcs[] = { 1, 1, }; +static int jz4780_i2c4_f_funcs[] = { 1, 1, }; + +static const struct group_desc jz4780_groups[] = { + INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data), + INGENIC_PIN_GROUP("uart0-hwflow", jz4770_uart0_hwflow), + INGENIC_PIN_GROUP("uart1-data", jz4770_uart1_data), + INGENIC_PIN_GROUP("uart1-hwflow", jz4770_uart1_hwflow), + INGENIC_PIN_GROUP("uart2-data", jz4780_uart2_data), + INGENIC_PIN_GROUP("uart2-hwflow", jz4780_uart2_hwflow), + INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data), + INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow), + INGENIC_PIN_GROUP("uart4-data", jz4780_uart4_data), + INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a), + INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a), + INGENIC_PIN_GROUP("mmc0-8bit-a", jz4780_mmc0_8bit_a), + INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e), + INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e), + INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d), + INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), + INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e), + INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_8bit_data), + INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale), + INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr), + INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we), + INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe), + INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1), + INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2), + INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3), + INGENIC_PIN_GROUP("nemc-cs4", jz4770_nemc_cs4), + INGENIC_PIN_GROUP("nemc-cs5", jz4770_nemc_cs5), + INGENIC_PIN_GROUP("nemc-cs6", jz4770_nemc_cs6), + INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0), + INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1), + INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2), + INGENIC_PIN_GROUP("i2c3-data", jz4780_i2c3), + INGENIC_PIN_GROUP("i2c4-data-e", jz4780_i2c4_e), + INGENIC_PIN_GROUP("i2c4-data-f", jz4780_i2c4_f), + INGENIC_PIN_GROUP("cim-data", jz4770_cim_8bit), + INGENIC_PIN_GROUP("lcd-24bit", jz4770_lcd_24bit), + { "lcd-no-pins", }, + INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0), + INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1), + INGENIC_PIN_GROUP("pwm2", jz4770_pwm_pwm2), + INGENIC_PIN_GROUP("pwm3", jz4770_pwm_pwm3), + INGENIC_PIN_GROUP("pwm4", jz4770_pwm_pwm4), + INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5), + INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6), + INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7), +}; + +static const char *jz4780_uart2_groups[] = { "uart2-data", "uart2-hwflow", }; +static const char *jz4780_uart4_groups[] = { "uart4-data", }; +static const char *jz4780_mmc0_groups[] = { + "mmc0-1bit-a", "mmc0-4bit-a", "mmc0-8bit-a", + "mmc0-1bit-e", "mmc0-4bit-e", +}; +static const char *jz4780_mmc1_groups[] = { + "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e", +}; +static const char *jz4780_nemc_groups[] = { + "nemc-data", "nemc-cle-ale", "nemc-addr", + "nemc-rd-we", "nemc-frd-fwe", +}; +static const char *jz4780_i2c3_groups[] = { "i2c3-data", }; +static const char *jz4780_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", }; +static const char *jz4780_cim_groups[] = { "cim-data", }; + +static const struct function_desc jz4780_functions[] = { + { "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), }, + { "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), }, + { "uart2", jz4780_uart2_groups, ARRAY_SIZE(jz4780_uart2_groups), }, + { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), }, + { "uart4", jz4780_uart4_groups, ARRAY_SIZE(jz4780_uart4_groups), }, + { "mmc0", jz4780_mmc0_groups, ARRAY_SIZE(jz4780_mmc0_groups), }, + { "mmc1", jz4780_mmc1_groups, ARRAY_SIZE(jz4780_mmc1_groups), }, + { "nemc", jz4780_nemc_groups, ARRAY_SIZE(jz4780_nemc_groups), }, + { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), }, + { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), }, + { "nemc-cs3", jz4770_cs3_groups, ARRAY_SIZE(jz4770_cs3_groups), }, + { "nemc-cs4", jz4770_cs4_groups, ARRAY_SIZE(jz4770_cs4_groups), }, + { "nemc-cs5", jz4770_cs5_groups, ARRAY_SIZE(jz4770_cs5_groups), }, + { "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), }, + { "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), }, + { "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), }, + { "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), }, + { "i2c3", jz4780_i2c3_groups, ARRAY_SIZE(jz4780_i2c3_groups), }, + { "i2c4", jz4780_i2c4_groups, ARRAY_SIZE(jz4780_i2c4_groups), }, + { "cim", jz4780_cim_groups, ARRAY_SIZE(jz4780_cim_groups), }, + { "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), }, + { "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), }, + { "pwm1", jz4770_pwm1_groups, ARRAY_SIZE(jz4770_pwm1_groups), }, + { "pwm2", jz4770_pwm2_groups, ARRAY_SIZE(jz4770_pwm2_groups), }, + { "pwm3", jz4770_pwm3_groups, ARRAY_SIZE(jz4770_pwm3_groups), }, + { "pwm4", jz4770_pwm4_groups, ARRAY_SIZE(jz4770_pwm4_groups), }, + { "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), }, + { "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), }, + { "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), }, +}; + +static const struct ingenic_chip_info jz4780_chip_info = { + .num_chips = 6, + .groups = jz4780_groups, + .num_groups = ARRAY_SIZE(jz4780_groups), + .functions = jz4780_functions, + .num_functions = ARRAY_SIZE(jz4780_functions), + .pull_ups = jz4770_pull_ups, + .pull_downs = jz4770_pull_downs, +}; + static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) { unsigned int val; @@ -1185,7 +1316,9 @@ static int __init ingenic_pinctrl_probe(struct platform_device *pdev) else jzpc->version = (enum jz_version)id->driver_data; - if (jzpc->version >= ID_JZ4770) + if (jzpc->version >= ID_JZ4780) + chip_info = &jz4780_chip_info; + else if (jzpc->version >= ID_JZ4770) chip_info = &jz4770_chip_info; else if (jzpc->version >= ID_JZ4725B) chip_info = &jz4725b_chip_info; From 5de1a73e78ed6c7735d53dbb2988e9bd8dd14769 Mon Sep 17 00:00:00 2001 From: Zhou Yanjie Date: Mon, 28 Jan 2019 23:19:58 +0800 Subject: [PATCH 0786/4277] Pinctrl: Ingenic: Add missing parts for JZ4770 and JZ4780. Add mmc2 for JZ4770 and JZ4780: According to the datasheet, both JZ4770 and JZ4780 have mmc2. But this part of the original code is missing. It is worth noting that JZ4770's mmc2 supports 8bit mode while JZ4780's does not, so we added the corresponding code for both models. Add nemc-wait for JZ4770 and JZ4780: Both JZ4770 and JZ4780 have a nemc-wait pin. But this part of the original code is missing. Add mac for JZ4770: JZ4770 have a mac. But this part of the original code is missing. Signed-off-by: Zhou Yanjie Reviewed-by: Paul Cercueil Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-ingenic.c | 46 +++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index 710062be5241..6501f352c619 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -335,6 +335,11 @@ static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, }; static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; static int jz4770_mmc1_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; +static int jz4770_mmc2_1bit_b_pins[] = { 0x3c, 0x3d, 0x34, }; +static int jz4770_mmc2_4bit_b_pins[] = { 0x35, 0x3e, 0x3f, }; +static int jz4770_mmc2_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, }; +static int jz4770_mmc2_4bit_e_pins[] = { 0x95, 0x96, 0x97, }; +static int jz4770_mmc2_8bit_e_pins[] = { 0x98, 0x99, 0x9a, 0x9b, }; static int jz4770_nemc_8bit_data_pins[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, }; @@ -345,6 +350,7 @@ static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, }; static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, }; static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, }; static int jz4770_nemc_frd_fwe_pins[] = { 0x12, 0x13, }; +static int jz4770_nemc_wait_pins[] = { 0x1b, }; static int jz4770_nemc_cs1_pins[] = { 0x15, }; static int jz4770_nemc_cs2_pins[] = { 0x16, }; static int jz4770_nemc_cs3_pins[] = { 0x17, }; @@ -375,6 +381,10 @@ static int jz4770_pwm_pwm4_pins[] = { 0x84, }; static int jz4770_pwm_pwm5_pins[] = { 0x85, }; static int jz4770_pwm_pwm6_pins[] = { 0x6a, }; static int jz4770_pwm_pwm7_pins[] = { 0x6b, }; +static int jz4770_mac_rmii_pins[] = { + 0xa9, 0xab, 0xaa, 0xac, 0xa5, 0xa4, 0xad, 0xae, 0xa6, 0xa8, +}; +static int jz4770_mac_mii_pins[] = { 0xa7, 0xaf, }; static int jz4770_uart0_data_funcs[] = { 0, 0, }; static int jz4770_uart0_hwflow_funcs[] = { 0, 0, }; @@ -394,12 +404,18 @@ static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, }; static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, }; static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, }; static int jz4770_mmc1_8bit_e_funcs[] = { 1, 1, 1, 1, }; +static int jz4770_mmc2_1bit_b_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc2_4bit_b_funcs[] = { 0, 0, 0, }; +static int jz4770_mmc2_1bit_e_funcs[] = { 2, 2, 2, }; +static int jz4770_mmc2_4bit_e_funcs[] = { 2, 2, 2, }; +static int jz4770_mmc2_8bit_e_funcs[] = { 2, 2, 2, 2, }; static int jz4770_nemc_8bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; static int jz4770_nemc_16bit_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, }; static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, }; static int jz4770_nemc_rd_we_funcs[] = { 0, 0, }; static int jz4770_nemc_frd_fwe_funcs[] = { 0, 0, }; +static int jz4770_nemc_wait_funcs[] = { 0, }; static int jz4770_nemc_cs1_funcs[] = { 0, }; static int jz4770_nemc_cs2_funcs[] = { 0, }; static int jz4770_nemc_cs3_funcs[] = { 0, }; @@ -425,6 +441,8 @@ static int jz4770_pwm_pwm4_funcs[] = { 0, }; static int jz4770_pwm_pwm5_funcs[] = { 0, }; static int jz4770_pwm_pwm6_funcs[] = { 0, }; static int jz4770_pwm_pwm7_funcs[] = { 0, }; +static int jz4770_mac_rmii_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4770_mac_mii_funcs[] = { 0, 0, }; static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data), @@ -445,12 +463,18 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e), INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), INGENIC_PIN_GROUP("mmc1-8bit-e", jz4770_mmc1_8bit_e), + INGENIC_PIN_GROUP("mmc2-1bit-b", jz4770_mmc2_1bit_b), + INGENIC_PIN_GROUP("mmc2-4bit-b", jz4770_mmc2_4bit_b), + INGENIC_PIN_GROUP("mmc2-1bit-e", jz4770_mmc2_1bit_e), + INGENIC_PIN_GROUP("mmc2-4bit-e", jz4770_mmc2_4bit_e), + INGENIC_PIN_GROUP("mmc2-8bit-e", jz4770_mmc2_8bit_e), INGENIC_PIN_GROUP("nemc-8bit-data", jz4770_nemc_8bit_data), INGENIC_PIN_GROUP("nemc-16bit-data", jz4770_nemc_16bit_data), INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale), INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr), INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we), INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe), + INGENIC_PIN_GROUP("nemc-wait", jz4770_nemc_wait), INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1), INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2), INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3), @@ -472,6 +496,8 @@ static const struct group_desc jz4770_groups[] = { INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5), INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6), INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7), + INGENIC_PIN_GROUP("mac-rmii", jz4770_mac_rmii), + INGENIC_PIN_GROUP("mac-mii", jz4770_mac_mii), }; static const char *jz4770_uart0_groups[] = { "uart0-data", "uart0-hwflow", }; @@ -486,9 +512,13 @@ static const char *jz4770_mmc1_groups[] = { "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e", "mmc1-8bit-e", }; +static const char *jz4770_mmc2_groups[] = { + "mmc2-1bit-b", "mmc2-4bit-b", + "mmc2-1bit-e", "mmc2-4bit-e", "mmc2-8bit-e", +}; static const char *jz4770_nemc_groups[] = { "nemc-8bit-data", "nemc-16bit-data", "nemc-cle-ale", - "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", + "nemc-addr", "nemc-rd-we", "nemc-frd-fwe", "nemc-wait", }; static const char *jz4770_cs1_groups[] = { "nemc-cs1", }; static const char *jz4770_cs2_groups[] = { "nemc-cs2", }; @@ -509,6 +539,7 @@ static const char *jz4770_pwm4_groups[] = { "pwm4", }; static const char *jz4770_pwm5_groups[] = { "pwm5", }; static const char *jz4770_pwm6_groups[] = { "pwm6", }; static const char *jz4770_pwm7_groups[] = { "pwm7", }; +static const char *jz4770_mac_groups[] = { "mac-rmii", "mac-mii", }; static const struct function_desc jz4770_functions[] = { { "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), }, @@ -517,6 +548,7 @@ static const struct function_desc jz4770_functions[] = { { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), }, { "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), }, { "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), }, + { "mmc2", jz4770_mmc2_groups, ARRAY_SIZE(jz4770_mmc2_groups), }, { "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), }, { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), }, { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), }, @@ -537,6 +569,7 @@ static const struct function_desc jz4770_functions[] = { { "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), }, { "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), }, { "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), }, + { "mac", jz4770_mac_groups, ARRAY_SIZE(jz4770_mac_groups), }, }; static const struct ingenic_chip_info jz4770_chip_info = { @@ -584,11 +617,16 @@ static const struct group_desc jz4780_groups[] = { INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d), INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e), INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e), + INGENIC_PIN_GROUP("mmc2-1bit-b", jz4770_mmc2_1bit_b), + INGENIC_PIN_GROUP("mmc2-4bit-b", jz4770_mmc2_4bit_b), + INGENIC_PIN_GROUP("mmc2-1bit-e", jz4770_mmc2_1bit_e), + INGENIC_PIN_GROUP("mmc2-4bit-e", jz4770_mmc2_4bit_e), INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_8bit_data), INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale), INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr), INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we), INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe), + INGENIC_PIN_GROUP("nemc-wait", jz4770_nemc_wait), INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1), INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2), INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3), @@ -623,9 +661,12 @@ static const char *jz4780_mmc0_groups[] = { static const char *jz4780_mmc1_groups[] = { "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e", }; +static const char *jz4780_mmc2_groups[] = { + "mmc2-1bit-b", "mmc2-4bit-b", "mmc2-1bit-e", "mmc2-4bit-e", +}; static const char *jz4780_nemc_groups[] = { "nemc-data", "nemc-cle-ale", "nemc-addr", - "nemc-rd-we", "nemc-frd-fwe", + "nemc-rd-we", "nemc-frd-fwe", "nemc-wait", }; static const char *jz4780_i2c3_groups[] = { "i2c3-data", }; static const char *jz4780_i2c4_groups[] = { "i2c4-data-e", "i2c4-data-f", }; @@ -639,6 +680,7 @@ static const struct function_desc jz4780_functions[] = { { "uart4", jz4780_uart4_groups, ARRAY_SIZE(jz4780_uart4_groups), }, { "mmc0", jz4780_mmc0_groups, ARRAY_SIZE(jz4780_mmc0_groups), }, { "mmc1", jz4780_mmc1_groups, ARRAY_SIZE(jz4780_mmc1_groups), }, + { "mmc2", jz4780_mmc2_groups, ARRAY_SIZE(jz4780_mmc2_groups), }, { "nemc", jz4780_nemc_groups, ARRAY_SIZE(jz4780_nemc_groups), }, { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), }, { "nemc-cs2", jz4770_cs2_groups, ARRAY_SIZE(jz4770_cs2_groups), }, From b71c1844124d670b677bc4724d29ab0d8b71edbb Mon Sep 17 00:00:00 2001 From: Zhou Yanjie Date: Mon, 28 Jan 2019 23:19:59 +0800 Subject: [PATCH 0787/4277] Pinctrl: Ingenic: Unify the function name prefix to "ingenic_gpio_". In the original code, some function names begin with "ingenic_gpio_", and some with "gpio_ingenic_". For the sake of uniform style, all of them are changed to the beginning of "ingenic_gpio_". Signed-off-by: Zhou Yanjie Reviewed-by: Paul Cercueil Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-ingenic.c | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index 6501f352c619..2b3f7e4708b0 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -715,7 +715,7 @@ static const struct ingenic_chip_info jz4780_chip_info = { .pull_downs = jz4770_pull_downs, }; -static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) +static u32 ingenic_gpio_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) { unsigned int val; @@ -724,7 +724,7 @@ static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) return (u32) val; } -static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, +static void ingenic_gpio_set_bit(struct ingenic_gpio_chip *jzgc, u8 reg, u8 offset, bool set) { if (set) @@ -738,7 +738,7 @@ static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, static inline bool ingenic_gpio_get_value(struct ingenic_gpio_chip *jzgc, u8 offset) { - unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN); + unsigned int val = ingenic_gpio_read_reg(jzgc, GPIO_PIN); return !!(val & BIT(offset)); } @@ -747,9 +747,9 @@ static void ingenic_gpio_set_value(struct ingenic_gpio_chip *jzgc, u8 offset, int value) { if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); } static void irq_set_type(struct ingenic_gpio_chip *jzgc, @@ -767,21 +767,21 @@ static void irq_set_type(struct ingenic_gpio_chip *jzgc, switch (type) { case IRQ_TYPE_EDGE_RISING: - gpio_ingenic_set_bit(jzgc, reg2, offset, true); - gpio_ingenic_set_bit(jzgc, reg1, offset, true); + ingenic_gpio_set_bit(jzgc, reg2, offset, true); + ingenic_gpio_set_bit(jzgc, reg1, offset, true); break; case IRQ_TYPE_EDGE_FALLING: - gpio_ingenic_set_bit(jzgc, reg2, offset, false); - gpio_ingenic_set_bit(jzgc, reg1, offset, true); + ingenic_gpio_set_bit(jzgc, reg2, offset, false); + ingenic_gpio_set_bit(jzgc, reg1, offset, true); break; case IRQ_TYPE_LEVEL_HIGH: - gpio_ingenic_set_bit(jzgc, reg2, offset, true); - gpio_ingenic_set_bit(jzgc, reg1, offset, false); + ingenic_gpio_set_bit(jzgc, reg2, offset, true); + ingenic_gpio_set_bit(jzgc, reg1, offset, false); break; case IRQ_TYPE_LEVEL_LOW: default: - gpio_ingenic_set_bit(jzgc, reg2, offset, false); - gpio_ingenic_set_bit(jzgc, reg1, offset, false); + ingenic_gpio_set_bit(jzgc, reg2, offset, false); + ingenic_gpio_set_bit(jzgc, reg1, offset, false); break; } } @@ -791,7 +791,7 @@ static void ingenic_gpio_irq_mask(struct irq_data *irqd) struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); + ingenic_gpio_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); } static void ingenic_gpio_irq_unmask(struct irq_data *irqd) @@ -799,7 +799,7 @@ static void ingenic_gpio_irq_unmask(struct irq_data *irqd) struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); + ingenic_gpio_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); } static void ingenic_gpio_irq_enable(struct irq_data *irqd) @@ -809,9 +809,9 @@ static void ingenic_gpio_irq_enable(struct irq_data *irqd) int irq = irqd->hwirq; if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); ingenic_gpio_irq_unmask(irqd); } @@ -825,9 +825,9 @@ static void ingenic_gpio_irq_disable(struct irq_data *irqd) ingenic_gpio_irq_mask(irqd); if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); } static void ingenic_gpio_irq_ack(struct irq_data *irqd) @@ -850,9 +850,9 @@ static void ingenic_gpio_irq_ack(struct irq_data *irqd) } if (jzgc->jzpc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); + ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); + ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); } static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) @@ -907,9 +907,9 @@ static void ingenic_gpio_irq_handler(struct irq_desc *desc) chained_irq_enter(irq_chip, desc); if (jzgc->jzpc->version >= ID_JZ4770) - flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG); + flag = ingenic_gpio_read_reg(jzgc, JZ4770_GPIO_FLAG); else - flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG); + flag = ingenic_gpio_read_reg(jzgc, JZ4740_GPIO_FLAG); for_each_set_bit(i, &flag, 32) generic_handle_irq(irq_linear_revmap(gc->irq.domain, i)); From 85e4e6881dbaae42bbac935c346753bea412ab76 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Tue, 29 Jan 2019 02:32:53 +0000 Subject: [PATCH 0788/4277] pinctrl: freescale: Add imx8mm pinctrl driver support Add the pinctrl driver support for i.MX8MM. Signed-off-by: Bai Ping Acked-by: Aisheng Dong Signed-off-by: Linus Walleij --- drivers/pinctrl/freescale/Kconfig | 7 + drivers/pinctrl/freescale/Makefile | 1 + drivers/pinctrl/freescale/pinctrl-imx8mm.c | 348 +++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 drivers/pinctrl/freescale/pinctrl-imx8mm.c diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig index b318c24b61be..e6dff3167a67 100644 --- a/drivers/pinctrl/freescale/Kconfig +++ b/drivers/pinctrl/freescale/Kconfig @@ -122,6 +122,13 @@ config PINCTRL_IMX7ULP help Say Y here to enable the imx7ulp pinctrl driver +config PINCTRL_IMX8MM + bool "IMX8MM pinctrl driver" + depends on ARCH_MXC && ARM64 + select PINCTRL_IMX + help + Say Y here to enable the imx8mm pinctrl driver + config PINCTRL_IMX8MQ bool "IMX8MQ pinctrl driver" depends on ARCH_MXC && ARM64 diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile index a6783f2560ae..02020a76bd9c 100644 --- a/drivers/pinctrl/freescale/Makefile +++ b/drivers/pinctrl/freescale/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_PINCTRL_IMX6SX) += pinctrl-imx6sx.o obj-$(CONFIG_PINCTRL_IMX6UL) += pinctrl-imx6ul.o obj-$(CONFIG_PINCTRL_IMX7D) += pinctrl-imx7d.o obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o +obj-$(CONFIG_PINCTRL_IMX8MM) += pinctrl-imx8mm.o obj-$(CONFIG_PINCTRL_IMX8MQ) += pinctrl-imx8mq.o obj-$(CONFIG_PINCTRL_IMX8QM) += pinctrl-imx8qm.o obj-$(CONFIG_PINCTRL_IMX8QXP) += pinctrl-imx8qxp.o diff --git a/drivers/pinctrl/freescale/pinctrl-imx8mm.c b/drivers/pinctrl/freescale/pinctrl-imx8mm.c new file mode 100644 index 000000000000..6d1038af59f4 --- /dev/null +++ b/drivers/pinctrl/freescale/pinctrl-imx8mm.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-imx.h" + +enum imx8mm_pads { + MX8MM_PAD_RESERVE0 = 0, + MX8MM_PAD_RESERVE1 = 1, + MX8MM_PAD_RESERVE2 = 2, + MX8MM_PAD_RESERVE3 = 3, + MX8MM_PAD_RESERVE4 = 4, + MX8MM_PAD_RESERVE5 = 5, + MX8MM_PAD_RESERVE6 = 6, + MX8MM_PAD_RESERVE7 = 7, + MX8MM_PAD_RESERVE8 = 8, + MX8MM_PAD_RESERVE9 = 9, + MX8MM_IOMUXC_GPIO1_IO00 = 10, + MX8MM_IOMUXC_GPIO1_IO01 = 11, + MX8MM_IOMUXC_GPIO1_IO02 = 12, + MX8MM_IOMUXC_GPIO1_IO03 = 13, + MX8MM_IOMUXC_GPIO1_IO04 = 14, + MX8MM_IOMUXC_GPIO1_IO05 = 15, + MX8MM_IOMUXC_GPIO1_IO06 = 16, + MX8MM_IOMUXC_GPIO1_IO07 = 17, + MX8MM_IOMUXC_GPIO1_IO08 = 18, + MX8MM_IOMUXC_GPIO1_IO09 = 19, + MX8MM_IOMUXC_GPIO1_IO10 = 20, + MX8MM_IOMUXC_GPIO1_IO11 = 21, + MX8MM_IOMUXC_GPIO1_IO12 = 22, + MX8MM_IOMUXC_GPIO1_IO13 = 23, + MX8MM_IOMUXC_GPIO1_IO14 = 24, + MX8MM_IOMUXC_GPIO1_IO15 = 25, + MX8MM_IOMUXC_ENET_MDC = 26, + MX8MM_IOMUXC_ENET_MDIO = 27, + MX8MM_IOMUXC_ENET_TD3 = 28, + MX8MM_IOMUXC_ENET_TD2 = 29, + MX8MM_IOMUXC_ENET_TD1 = 30, + MX8MM_IOMUXC_ENET_TD0 = 31, + MX8MM_IOMUXC_ENET_TX_CTL = 32, + MX8MM_IOMUXC_ENET_TXC = 33, + MX8MM_IOMUXC_ENET_RX_CTL = 34, + MX8MM_IOMUXC_ENET_RXC = 35, + MX8MM_IOMUXC_ENET_RD0 = 36, + MX8MM_IOMUXC_ENET_RD1 = 37, + MX8MM_IOMUXC_ENET_RD2 = 38, + MX8MM_IOMUXC_ENET_RD3 = 39, + MX8MM_IOMUXC_SD1_CLK = 40, + MX8MM_IOMUXC_SD1_CMD = 41, + MX8MM_IOMUXC_SD1_DATA0 = 42, + MX8MM_IOMUXC_SD1_DATA1 = 43, + MX8MM_IOMUXC_SD1_DATA2 = 44, + MX8MM_IOMUXC_SD1_DATA3 = 45, + MX8MM_IOMUXC_SD1_DATA4 = 46, + MX8MM_IOMUXC_SD1_DATA5 = 47, + MX8MM_IOMUXC_SD1_DATA6 = 48, + MX8MM_IOMUXC_SD1_DATA7 = 49, + MX8MM_IOMUXC_SD1_RESET_B = 50, + MX8MM_IOMUXC_SD1_STROBE = 51, + MX8MM_IOMUXC_SD2_CD_B = 52, + MX8MM_IOMUXC_SD2_CLK = 53, + MX8MM_IOMUXC_SD2_CMD = 54, + MX8MM_IOMUXC_SD2_DATA0 = 55, + MX8MM_IOMUXC_SD2_DATA1 = 56, + MX8MM_IOMUXC_SD2_DATA2 = 57, + MX8MM_IOMUXC_SD2_DATA3 = 58, + MX8MM_IOMUXC_SD2_RESET_B = 59, + MX8MM_IOMUXC_SD2_WP = 60, + MX8MM_IOMUXC_NAND_ALE = 61, + MX8MM_IOMUXC_NAND_CE0 = 62, + MX8MM_IOMUXC_NAND_CE1 = 63, + MX8MM_IOMUXC_NAND_CE2 = 64, + MX8MM_IOMUXC_NAND_CE3 = 65, + MX8MM_IOMUXC_NAND_CLE = 66, + MX8MM_IOMUXC_NAND_DATA00 = 67, + MX8MM_IOMUXC_NAND_DATA01 = 68, + MX8MM_IOMUXC_NAND_DATA02 = 69, + MX8MM_IOMUXC_NAND_DATA03 = 70, + MX8MM_IOMUXC_NAND_DATA04 = 71, + MX8MM_IOMUXC_NAND_DATA05 = 72, + MX8MM_IOMUXC_NAND_DATA06 = 73, + MX8MM_IOMUXC_NAND_DATA07 = 74, + MX8MM_IOMUXC_NAND_DQS = 75, + MX8MM_IOMUXC_NAND_RE_B = 76, + MX8MM_IOMUXC_NAND_READY_B = 77, + MX8MM_IOMUXC_NAND_WE_B = 78, + MX8MM_IOMUXC_NAND_WP_B = 79, + MX8MM_IOMUXC_SAI5_RXFS = 80, + MX8MM_IOMUXC_SAI5_RXC = 81, + MX8MM_IOMUXC_SAI5_RXD0 = 82, + MX8MM_IOMUXC_SAI5_RXD1 = 83, + MX8MM_IOMUXC_SAI5_RXD2 = 84, + MX8MM_IOMUXC_SAI5_RXD3 = 85, + MX8MM_IOMUXC_SAI5_MCLK = 86, + MX8MM_IOMUXC_SAI1_RXFS = 87, + MX8MM_IOMUXC_SAI1_RXC = 88, + MX8MM_IOMUXC_SAI1_RXD0 = 89, + MX8MM_IOMUXC_SAI1_RXD1 = 90, + MX8MM_IOMUXC_SAI1_RXD2 = 91, + MX8MM_IOMUXC_SAI1_RXD3 = 92, + MX8MM_IOMUXC_SAI1_RXD4 = 93, + MX8MM_IOMUXC_SAI1_RXD5 = 94, + MX8MM_IOMUXC_SAI1_RXD6 = 95, + MX8MM_IOMUXC_SAI1_RXD7 = 96, + MX8MM_IOMUXC_SAI1_TXFS = 97, + MX8MM_IOMUXC_SAI1_TXC = 98, + MX8MM_IOMUXC_SAI1_TXD0 = 99, + MX8MM_IOMUXC_SAI1_TXD1 = 100, + MX8MM_IOMUXC_SAI1_TXD2 = 101, + MX8MM_IOMUXC_SAI1_TXD3 = 102, + MX8MM_IOMUXC_SAI1_TXD4 = 103, + MX8MM_IOMUXC_SAI1_TXD5 = 104, + MX8MM_IOMUXC_SAI1_TXD6 = 105, + MX8MM_IOMUXC_SAI1_TXD7 = 106, + MX8MM_IOMUXC_SAI1_MCLK = 107, + MX8MM_IOMUXC_SAI2_RXFS = 108, + MX8MM_IOMUXC_SAI2_RXC = 109, + MX8MM_IOMUXC_SAI2_RXD0 = 110, + MX8MM_IOMUXC_SAI2_TXFS = 111, + MX8MM_IOMUXC_SAI2_TXC = 112, + MX8MM_IOMUXC_SAI2_TXD0 = 113, + MX8MM_IOMUXC_SAI2_MCLK = 114, + MX8MM_IOMUXC_SAI3_RXFS = 115, + MX8MM_IOMUXC_SAI3_RXC = 116, + MX8MM_IOMUXC_SAI3_RXD = 117, + MX8MM_IOMUXC_SAI3_TXFS = 118, + MX8MM_IOMUXC_SAI3_TXC = 119, + MX8MM_IOMUXC_SAI3_TXD = 120, + MX8MM_IOMUXC_SAI3_MCLK = 121, + MX8MM_IOMUXC_SPDIF_TX = 122, + MX8MM_IOMUXC_SPDIF_RX = 123, + MX8MM_IOMUXC_SPDIF_EXT_CLK = 124, + MX8MM_IOMUXC_ECSPI1_SCLK = 125, + MX8MM_IOMUXC_ECSPI1_MOSI = 126, + MX8MM_IOMUXC_ECSPI1_MISO = 127, + MX8MM_IOMUXC_ECSPI1_SS0 = 128, + MX8MM_IOMUXC_ECSPI2_SCLK = 129, + MX8MM_IOMUXC_ECSPI2_MOSI = 130, + MX8MM_IOMUXC_ECSPI2_MISO = 131, + MX8MM_IOMUXC_ECSPI2_SS0 = 132, + MX8MM_IOMUXC_I2C1_SCL = 133, + MX8MM_IOMUXC_I2C1_SDA = 134, + MX8MM_IOMUXC_I2C2_SCL = 135, + MX8MM_IOMUXC_I2C2_SDA = 136, + MX8MM_IOMUXC_I2C3_SCL = 137, + MX8MM_IOMUXC_I2C3_SDA = 138, + MX8MM_IOMUXC_I2C4_SCL = 139, + MX8MM_IOMUXC_I2C4_SDA = 140, + MX8MM_IOMUXC_UART1_RXD = 141, + MX8MM_IOMUXC_UART1_TXD = 142, + MX8MM_IOMUXC_UART2_RXD = 143, + MX8MM_IOMUXC_UART2_TXD = 144, + MX8MM_IOMUXC_UART3_RXD = 145, + MX8MM_IOMUXC_UART3_TXD = 146, + MX8MM_IOMUXC_UART4_RXD = 147, + MX8MM_IOMUXC_UART4_TXD = 148, +}; + +/* Pad names for the pinmux subsystem */ +static const struct pinctrl_pin_desc imx8mm_pinctrl_pads[] = { + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE0), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE1), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE2), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE3), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE4), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE5), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE6), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE7), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE8), + IMX_PINCTRL_PIN(MX8MM_PAD_RESERVE9), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO00), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO01), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO02), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO03), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO04), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO05), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO06), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO07), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO08), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO09), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO10), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO11), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO12), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO13), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO14), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_GPIO1_IO15), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_MDC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_MDIO), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TX_CTL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RX_CTL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ENET_RD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_CLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_CMD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA4), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA5), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA6), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_DATA7), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_RESET_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD1_STROBE), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CD_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_CMD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_DATA3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_RESET_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SD2_WP), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_ALE), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CE3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_CLE), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA00), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA01), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA02), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA03), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA04), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA05), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA06), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DATA07), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_DQS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_RE_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_READY_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_WE_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_NAND_WP_B), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_RXD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI5_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD4), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD5), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD6), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_RXD7), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD1), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD2), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD3), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD4), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD5), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD6), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_TXD7), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI1_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_RXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_TXD0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI2_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXFS), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXC), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SAI3_MCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_TX), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_RX), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_SPDIF_EXT_CLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_SCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_MOSI), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_MISO), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI1_SS0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_SCLK), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_MOSI), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_MISO), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_ECSPI2_SS0), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C1_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C1_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C2_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C2_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C3_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C3_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C4_SCL), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_I2C4_SDA), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART1_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART1_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART2_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART2_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART3_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART3_TXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART4_RXD), + IMX_PINCTRL_PIN(MX8MM_IOMUXC_UART4_TXD), +}; + +static const struct imx_pinctrl_soc_info imx8mm_pinctrl_info = { + .pins = imx8mm_pinctrl_pads, + .npins = ARRAY_SIZE(imx8mm_pinctrl_pads), + .gpr_compatible = "fsl,imx8mm-iomuxc-gpr", +}; + +static const struct of_device_id imx8mm_pinctrl_of_match[] = { + { .compatible = "fsl,imx8mm-iomuxc", .data = &imx8mm_pinctrl_info, }, + { /* sentinel */ } +}; + +static int imx8mm_pinctrl_probe(struct platform_device *pdev) +{ + return imx_pinctrl_probe(pdev, &imx8mm_pinctrl_info); +} + +static struct platform_driver imx8mm_pinctrl_driver = { + .driver = { + .name = "imx8mm-pinctrl", + .of_match_table = of_match_ptr(imx8mm_pinctrl_of_match), + .suppress_bind_attrs = true, + }, + .probe = imx8mm_pinctrl_probe, +}; + +static int __init imx8mm_pinctrl_init(void) +{ + return platform_driver_register(&imx8mm_pinctrl_driver); +} +arch_initcall(imx8mm_pinctrl_init); From 0294951030eb8e7d808d7f151a2cf0a2ef1afb64 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Thu, 13 Dec 2018 09:23:42 -0700 Subject: [PATCH 0789/4277] PCI/VMD: Configure MPS settings before adding devices In order to provide the most performance and/or compatible settings, ensure VMD root buses observe the pcie bus tuning settings by configuring those settings prior to adding the devices to the pcie tree. This patch open-codes pci_rescan_bus() and configures the buses prior to adding devices and attaching drivers. Signed-off-by: Jon Derrick Signed-off-by: Lorenzo Pieralisi Reviewed-by: Keith Busch --- drivers/pci/controller/vmd.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 3890812cdf87..cf6816b55b5e 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -571,6 +571,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) LIST_HEAD(resources); resource_size_t offset[2] = {0}; resource_size_t membar2_offset = 0x2000, busn_start = 0; + struct pci_bus *child; /* * Shadow registers may exist in certain VMD device ids which allow @@ -698,7 +699,19 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) vmd_attach_resources(vmd); vmd_setup_dma_ops(vmd); dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); - pci_rescan_bus(vmd->bus); + + pci_scan_child_bus(vmd->bus); + pci_assign_unassigned_bus_resources(vmd->bus); + + /* + * VMD root buses are virtual and don't return true on pci_is_pcie() + * and will fail pcie_bus_configure_settings() early. It can instead be + * run on each of the real root ports. + */ + list_for_each_entry(child, &vmd->bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(vmd->bus); WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj, "domain"), "Can't create symlink to domain\n"); From cec2b18832e26bc866bef2be22eff4e25bbc4034 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 27 Jan 2019 22:50:54 +0800 Subject: [PATCH 0790/4277] backlight: pwm_bl: Use gpiod_get_value_cansleep() to get initial state gpiod_get_value() gives out a warning if access to the underlying gpiochip requires sleeping, which is common for I2C based chips: WARNING: CPU: 0 PID: 77 at drivers/gpio/gpiolib.c:2500 gpiod_get_value+0xd0/0x100 Modules linked in: CPU: 0 PID: 77 Comm: kworker/0:2 Not tainted 4.14.0-rc3-00589-gf32897915d48-dirty #90 Hardware name: Allwinner sun4i/sun5i Families Workqueue: events deferred_probe_work_func [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0x88/0x9c) [] (dump_stack) from [] (__warn+0xe8/0x100) [] (__warn) from [] (warn_slowpath_null+0x20/0x28) [] (warn_slowpath_null) from [] (gpiod_get_value+0xd0/0x100) [] (gpiod_get_value) from [] (pwm_backlight_probe+0x238/0x508) [] (pwm_backlight_probe) from [] (platform_drv_probe+0x50/0xac) [] (platform_drv_probe) from [] (driver_probe_device+0x238/0x2e8) [] (driver_probe_device) from [] (bus_for_each_drv+0x44/0x94) [] (bus_for_each_drv) from [] (__device_attach+0xb0/0x114) [] (__device_attach) from [] (bus_probe_device+0x84/0x8c) [] (bus_probe_device) from [] (deferred_probe_work_func+0x50/0x14c) [] (deferred_probe_work_func) from [] (process_one_work+0x1ec/0x414) [] (process_one_work) from [] (worker_thread+0x2b0/0x5a0) [] (worker_thread) from [] (kthread+0x14c/0x154) [] (kthread) from [] (ret_from_fork+0x14/0x24) This was missed in commit 0c9501f823a4 ("backlight: pwm_bl: Handle gpio that can sleep"). The code was then moved to a separate function in commit 7613c922315e ("backlight: pwm_bl: Move the checks for initial power state to a separate function"). The only usage of gpiod_get_value() is during the probe stage, which is safe to sleep in. Switch to gpiod_get_value_cansleep(). Fixes: 0c9501f823a4 ("backlight: pwm_bl: Handle gpio that can sleep") Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Acked-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index feb90764a811..53b8ceea9bde 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -435,7 +435,7 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) */ /* if the enable GPIO is disabled, do not enable the backlight */ - if (pb->enable_gpio && gpiod_get_value(pb->enable_gpio) == 0) + if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0) return FB_BLANK_POWERDOWN; /* The regulator is disabled, do not enable the backlight */ From a26ec8857da387303c6121a8aff7b7544a981690 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Jan 2019 12:39:50 +0100 Subject: [PATCH 0791/4277] dt-bindings: rtc: Add RV1805 to abracon,abx80x bindings Add compatible string for Microcrystal RV1805 RTC, which is compatible with AB1805. The RV1805 uses smaller 10 pin package, while the AB1805 uses larger 16pin package, however the die inside the chip is the same. Signed-off-by: Marek Vasut Cc: Alexandre Belloni Cc: Rob Herring To: devicetree@vger.kernel.org Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/abracon,abx80x.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt b/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt index 18b892d010d8..2405e35a1bc0 100644 --- a/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt +++ b/Documentation/devicetree/bindings/rtc/abracon,abx80x.txt @@ -16,6 +16,7 @@ Required properties: "abracon,ab1803" "abracon,ab1804" "abracon,ab1805" + "microcrystal,rv1805" Using "abracon,abx80x" will enable chip autodetection. - "reg": I2C bus address of the device From 75455e258ea2b3ea08f08edc41735a447c3e7711 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Jan 2019 12:40:28 +0100 Subject: [PATCH 0792/4277] rtc: abx80x: Configure reserved bits in RV1805 The RV1805 uses smaller package than the AB1805, discern those two chips based on the compatible value and configure reserved bits in the RV1805 to prevent current leakage and accidental test mode entry. Signed-off-by: Marek Vasut Cc: Alexandre Belloni To: linux-rtc@vger.kernel.org Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-abx80x.c | 67 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 4d24f7288ad7..b76f65983658 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -46,6 +46,9 @@ #define ABX8XX_CTRL_ARST BIT(2) #define ABX8XX_CTRL_12_24 BIT(6) +#define ABX8XX_REG_CTRL2 0x11 +#define ABX8XX_CTRL2_RSVD BIT(5) + #define ABX8XX_REG_IRQ 0x12 #define ABX8XX_IRQ_AIE BIT(2) #define ABX8XX_IRQ_IM_1_4 (0x3 << 5) @@ -78,6 +81,9 @@ #define ABX8XX_REG_ID0 0x28 +#define ABX8XX_REG_OUT_CTRL 0x30 +#define ABX8XX_OUT_CTRL_EXDS BIT(4) + #define ABX8XX_REG_TRICKLE 0x20 #define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0 #define ABX8XX_TRICKLE_STANDARD_DIODE 0x8 @@ -86,7 +92,7 @@ static u8 trickle_resistors[] = {0, 3, 6, 11}; enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, - AB1801, AB1803, AB1804, AB1805, ABX80X}; + AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X}; struct abx80x_cap { u16 pn; @@ -103,6 +109,7 @@ static struct abx80x_cap abx80x_caps[] = { [AB1803] = {.pn = 0x1803}, [AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true}, [AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, + [RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, [ABX80X] = {.pn = 0} }; @@ -723,6 +730,62 @@ static int abx80x_probe(struct i2c_client *client, return -EIO; } + /* Configure RV1805 specifics */ + if (part == RV1805) { + /* + * Avoid accidentally entering test mode. This can happen + * on the RV1805 in case the reserved bit 5 in control2 + * register is set. RV-1805-C3 datasheet indicates that + * the bit should be cleared in section 11h - Control2. + */ + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2); + if (data < 0) { + dev_err(&client->dev, + "Unable to read control2 register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2, + data & ~ABX8XX_CTRL2_RSVD); + if (err < 0) { + dev_err(&client->dev, + "Unable to write control2 register\n"); + return -EIO; + } + + /* + * Avoid extra power leakage. The RV1805 uses smaller + * 10pin package and the EXTI input is not present. + * Disable it to avoid leakage. + */ + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL); + if (data < 0) { + dev_err(&client->dev, + "Unable to read output control register\n"); + return -EIO; + } + + /* + * Write the configuration key register to enable access to + * the config2 register + */ + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, + ABX8XX_CFG_KEY_MISC); + if (err < 0) { + dev_err(&client->dev, + "Unable to write configuration key\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OUT_CTRL, + data | ABX8XX_OUT_CTRL_EXDS); + if (err < 0) { + dev_err(&client->dev, + "Unable to write output control register\n"); + return -EIO; + } + } + /* part autodetection */ if (part == ABX80X) { for (i = 0; abx80x_caps[i].pn; i++) @@ -826,7 +889,7 @@ static const struct i2c_device_id abx80x_id[] = { { "ab1803", AB1803 }, { "ab1804", AB1804 }, { "ab1805", AB1805 }, - { "rv1805", AB1805 }, + { "rv1805", RV1805 }, { } }; MODULE_DEVICE_TABLE(i2c, abx80x_id); From 21d5d27c042d41d0ec1d138651909405d967f8af Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 22 Jan 2019 14:30:45 -0700 Subject: [PATCH 0793/4277] iommu/vt-d: Implement dma_[un]map_resource() Currently the Intel IOMMU uses the default dma_[un]map_resource() implementations does nothing and simply returns the physical address unmodified. However, this doesn't create the IOVA entries necessary for addresses mapped this way to work when the IOMMU is enabled. Thus, when the IOMMU is enabled, drivers relying on dma_map_resource() will trigger DMAR errors. We see this when running ntb_transport with the IOMMU enabled, DMA, and switchtec hardware. The implementation for intel_map_resource() is nearly identical to intel_map_page(), we just have to re-create __intel_map_single(). dma_unmap_resource() uses intel_unmap_page() directly as the functions are identical. Signed-off-by: Logan Gunthorpe Cc: David Woodhouse Cc: Joerg Roedel Reviewed-by: Christoph Hellwig Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 2bd9ac285c0d..64dab37c0b96 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3649,11 +3649,9 @@ static int iommu_no_mapping(struct device *dev) return 0; } -static dma_addr_t __intel_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, int dir, - u64 dma_mask) +static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, + size_t size, int dir, u64 dma_mask) { - phys_addr_t paddr = page_to_phys(page) + offset; struct dmar_domain *domain; phys_addr_t start_paddr; unsigned long iova_pfn; @@ -3715,7 +3713,15 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page, enum dma_data_direction dir, unsigned long attrs) { - return __intel_map_page(dev, page, offset, size, dir, *dev->dma_mask); + return __intel_map_single(dev, page_to_phys(page) + offset, size, + dir, *dev->dma_mask); +} + +static dma_addr_t intel_map_resource(struct device *dev, phys_addr_t phys_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + return __intel_map_single(dev, phys_addr, size, dir, *dev->dma_mask); } static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) @@ -3806,8 +3812,9 @@ static void *intel_alloc_coherent(struct device *dev, size_t size, return NULL; memset(page_address(page), 0, size); - *dma_handle = __intel_map_page(dev, page, 0, size, DMA_BIDIRECTIONAL, - dev->coherent_dma_mask); + *dma_handle = __intel_map_single(dev, page_to_phys(page), size, + DMA_BIDIRECTIONAL, + dev->coherent_dma_mask); if (*dma_handle != DMA_MAPPING_ERROR) return page_address(page); if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT)) @@ -3924,6 +3931,8 @@ static const struct dma_map_ops intel_dma_ops = { .unmap_sg = intel_unmap_sg, .map_page = intel_map_page, .unmap_page = intel_unmap_page, + .map_resource = intel_map_resource, + .unmap_resource = intel_unmap_page, .dma_supported = dma_direct_supported, }; From 2e6c6a8657e8709ce363a62238e792918dd158b3 Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Mon, 28 Jan 2019 17:59:37 -0700 Subject: [PATCH 0794/4277] iommu/amd: Print reason for iommu_map_page failure in map_sg Since there are multiple possible failures in iommu_map_page it would be useful to know which case is being hit when the error message is printed in map_sg. While here, fix up checkpatch complaint about using function name in a string instead of __func__. Cc: Joerg Roedel Signed-off-by: Jerry Snitselaar Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 87ba23a75b38..675f7027aa04 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2562,6 +2562,7 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, struct scatterlist *s; unsigned long address; u64 dma_mask; + int ret; domain = get_domain(dev); if (IS_ERR(domain)) @@ -2584,7 +2585,6 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, for (j = 0; j < pages; ++j) { unsigned long bus_addr, phys_addr; - int ret; bus_addr = address + s->dma_address + (j << PAGE_SHIFT); phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT); @@ -2605,8 +2605,8 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, return nelems; out_unmap: - pr_err("%s: IOMMU mapping error in map_sg (io-pages: %d)\n", - dev_name(dev), npages); + pr_err("%s: IOMMU mapping error in %s (io-pages: %d) reason: %d\n", + dev_name(dev), __func__, npages, ret); for_each_sg(sglist, s, nelems, i) { int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE); From 5a63f0adebe7d941de29304c526bfe184c43bdec Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 30 Jan 2019 13:57:58 +0800 Subject: [PATCH 0795/4277] iommu/amd: Remove clear_flush_young notifier AMD IOMMU driver is using the clear_flush_young() to do cache flushing but that's actually already covered by invalidate_range(). Remove the extra notifier and the chunks. Signed-off-by: Peter Xu Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_v2.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 23dae9348ace..5d7ef750e4a0 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -370,29 +370,6 @@ static struct pasid_state *mn_to_state(struct mmu_notifier *mn) return container_of(mn, struct pasid_state, mn); } -static void __mn_flush_page(struct mmu_notifier *mn, - unsigned long address) -{ - struct pasid_state *pasid_state; - struct device_state *dev_state; - - pasid_state = mn_to_state(mn); - dev_state = pasid_state->device_state; - - amd_iommu_flush_page(dev_state->domain, pasid_state->pasid, address); -} - -static int mn_clear_flush_young(struct mmu_notifier *mn, - struct mm_struct *mm, - unsigned long start, - unsigned long end) -{ - for (; start < end; start += PAGE_SIZE) - __mn_flush_page(mn, start); - - return 0; -} - static void mn_invalidate_range(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long start, unsigned long end) @@ -430,7 +407,6 @@ static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm) static const struct mmu_notifier_ops iommu_mn = { .release = mn_release, - .clear_flush_young = mn_clear_flush_young, .invalidate_range = mn_invalidate_range, }; From 1a9eb9b98f0a6c8836cc4d6dcf4d5d005fb5f389 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 30 Jan 2019 13:57:57 +0800 Subject: [PATCH 0796/4277] iommu/vt-d: Remove change_pte notifier The change_pte() interface is tailored for PFN updates, while the other notifier invalidate_range() should be enough for Intel IOMMU cache flushing. Actually we've done similar thing for AMD IOMMU already in 8301da53fbc1 ("iommu/amd: Remove change_pte mmu_notifier call-back", 2014-07-30) but the Intel IOMMU driver still have it. Signed-off-by: Peter Xu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-svm.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 79add5716552..c79540deaf00 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -180,14 +180,6 @@ static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address, rcu_read_unlock(); } -static void intel_change_pte(struct mmu_notifier *mn, struct mm_struct *mm, - unsigned long address, pte_t pte) -{ - struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); - - intel_flush_svm_range(svm, address, 1, 1, 0); -} - /* Pages have been freed at this point */ static void intel_invalidate_range(struct mmu_notifier *mn, struct mm_struct *mm, @@ -227,7 +219,6 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) static const struct mmu_notifier_ops intel_mmuops = { .release = intel_mm_release, - .change_pte = intel_change_pte, .invalidate_range = intel_invalidate_range, }; From 01b37f851ca150554496fd6e79c6d9a67992a2c0 Mon Sep 17 00:00:00 2001 From: Du Changbin Date: Sat, 13 Oct 2018 08:49:19 +0800 Subject: [PATCH 0797/4277] PCI: Make pci_size() return real BAR size Currently, the pci_size() function actually returns 'size-1'. Make it return real size to avoid confusion. Signed-off-by: Du Changbin Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2ef8b954c65a..8e2e4154cdd9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -121,13 +121,13 @@ static u64 pci_size(u64 base, u64 maxbase, u64 mask) * Get the lowest of them to find the decode size, and from that * the extent. */ - size = (size & ~(size-1)) - 1; + size = size & ~(size-1); /* * base == maxbase can be valid only if the BAR has already been * programmed with all 1s. */ - if (base == maxbase && ((base | size) & mask) != mask) + if (base == maxbase && ((base | (size - 1)) & mask) != mask) return 0; return size; @@ -278,7 +278,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Above 32-bit boundary; try to reallocate */ res->flags |= IORESOURCE_UNSET; res->start = 0; - res->end = sz64; + res->end = sz64 - 1; pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n", pos, (unsigned long long)l64); goto out; @@ -286,7 +286,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, } region.start = l64; - region.end = l64 + sz64; + region.end = l64 + sz64 - 1; pcibios_bus_to_resource(dev->bus, res, ®ion); pcibios_resource_to_bus(dev->bus, &inverted_region, res); From b2fb5cc574695a32361a6c1878816b3d6563aa0f Mon Sep 17 00:00:00 2001 From: Honghui Zhang Date: Tue, 16 Oct 2018 18:44:43 +0800 Subject: [PATCH 0798/4277] PCI: Rely on config space header type, not class code The PCI configuration space header type tells us whether the device is a bridge, a CardBus bridge, or a normal device, and defines the layout of the rest of the header (PCI r3.0 sec 6.1, PCIe r4.0 sec 7.5.1.1.9). When we rely on the header format, e.g., when we're dealing with bridge windows, we should check the header type, not the class code. The class code is loosely related to the header type, but is often incorrect and the spec doesn't actually require it to be related to the header format. Suggested-by: Bjorn Helgaas Signed-off-by: Honghui Zhang [bhelgaas: changelog, keep the PCI_CLASS_BRIDGE_HOST check] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- drivers/pci/probe.c | 3 --- drivers/pci/setup-bus.c | 18 +++++++++--------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..e9d938e14ba8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6000,8 +6000,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) * to enable the kernel to reassign new resource * window later on. */ - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { r = &dev->resource[i]; if (!(r->flags & IORESOURCE_MEM)) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8e2e4154cdd9..128459a0ffba 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1779,9 +1779,6 @@ int pci_setup_device(struct pci_dev *dev) break; case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ - if (class != PCI_CLASS_BRIDGE_PCI) - goto bad; - /* * The PCI-to-PCI bridge spec requires that subtractive * decoding (i.e. transparent) bridge must have programming diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 1941bb0a6c13..ec44a0f3a7ac 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1186,12 +1186,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) if (!b) continue; - switch (dev->class >> 8) { - case PCI_CLASS_BRIDGE_CARDBUS: + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: pci_bus_size_cardbus(b, realloc_head); break; - case PCI_CLASS_BRIDGE_PCI: + case PCI_HEADER_TYPE_BRIDGE: default: __pci_bus_size_bridges(b, realloc_head); break; @@ -1202,12 +1202,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) if (pci_is_root_bus(bus)) return; - switch (bus->self->class >> 8) { - case PCI_CLASS_BRIDGE_CARDBUS: + switch (bus->self->hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: /* don't size cardbuses yet. */ break; - case PCI_CLASS_BRIDGE_PCI: + case PCI_HEADER_TYPE_BRIDGE: pci_bridge_check_ranges(bus); if (bus->self->is_hotplug_bridge) { additional_io_size = pci_hotplug_io_size; @@ -1356,13 +1356,13 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, __pci_bus_assign_resources(b, realloc_head, fail_head); - switch (dev->class >> 8) { - case PCI_CLASS_BRIDGE_PCI: + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_BRIDGE: if (!pci_is_enabled(dev)) pci_setup_bridge(b); break; - case PCI_CLASS_BRIDGE_CARDBUS: + case PCI_HEADER_TYPE_CARDBUS: pci_setup_cardbus(b); break; From 87fc34b575fdb90994c6063541d79cd18b31b14d Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Wed, 23 Jan 2019 19:08:19 -0800 Subject: [PATCH 0799/4277] IB/{hfi1,qib}: Cleanup open coded sge sizing Sge sizing is done in several places using an open coded method. This can cause maintenance issues. The open coded method is encapsulated in a helper routine. The helper was introduced with commit: 1198fcea8a78 ("IB/hfi1, rdmavt: Move SGE state helper routines into rdmavt") Update all call sites that have the open coded path with the helper routine. Reviewed-by: Mike Marciniszyn Signed-off-by: Michael J. Ruhl Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/verbs.c | 12 ++---------- drivers/infiniband/hw/qib/qib_ud.c | 6 +----- drivers/infiniband/hw/qib/qib_verbs.c | 18 +++--------------- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 8957adf58af7..c980345cf1e1 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -553,11 +553,7 @@ static noinline int build_verbs_ulp_payload( int ret = 0; while (length) { - len = ss->sge.length; - if (len > length) - len = length; - if (len > ss->sge.sge_length) - len = ss->sge.sge_length; + len = rvt_get_sge_length(&ss->sge, length); WARN_ON_ONCE(len == 0); ret = sdma_txadd_kvaddr( sde->dd, @@ -914,12 +910,8 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps, if (ss) { while (len) { void *addr = ss->sge.vaddr; - u32 slen = ss->sge.length; + u32 slen = rvt_get_sge_length(&ss->sge, len); - if (slen > len) - slen = len; - if (slen > ss->sge.sge_length) - slen = ss->sge.sge_length; rvt_update_sge(ss, slen, false); seg_pio_copy_mid(pbuf, addr, slen); len -= slen; diff --git a/drivers/infiniband/hw/qib/qib_ud.c b/drivers/infiniband/hw/qib/qib_ud.c index 868da0ece7ba..6668bbf4e96d 100644 --- a/drivers/infiniband/hw/qib/qib_ud.c +++ b/drivers/infiniband/hw/qib/qib_ud.c @@ -172,12 +172,8 @@ static void qib_ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) ssge.num_sge = swqe->wr.num_sge; sge = &ssge.sge; while (length) { - u32 len = sge->length; + u32 len = rvt_get_sge_length(sge, length); - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; rvt_copy_sge(qp, &qp->r_sge, sge->vaddr, len, true, false); sge->vaddr += len; sge->length -= len; diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c index ff8dab8e2344..5ff32d32c61c 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.c +++ b/drivers/infiniband/hw/qib/qib_verbs.c @@ -144,12 +144,8 @@ static u32 qib_count_sge(struct rvt_sge_state *ss, u32 length) u32 ndesc = 1; /* count the header */ while (length) { - u32 len = sge.length; + u32 len = rvt_get_sge_length(&sge, length); - if (len > length) - len = length; - if (len > sge.sge_length) - len = sge.sge_length; if (((long) sge.vaddr & (sizeof(u32) - 1)) || (len != length && (len & (sizeof(u32) - 1)))) { ndesc = 0; @@ -186,12 +182,8 @@ static void qib_copy_from_sge(void *data, struct rvt_sge_state *ss, u32 length) struct rvt_sge *sge = &ss->sge; while (length) { - u32 len = sge->length; + u32 len = rvt_get_sge_length(sge, length); - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; memcpy(data, sge->vaddr, len); sge->vaddr += len; sge->length -= len; @@ -440,13 +432,9 @@ static void copy_io(u32 __iomem *piobuf, struct rvt_sge_state *ss, u32 last; while (1) { - u32 len = ss->sge.length; + u32 len = rvt_get_sge_length(&ss->sge, length); u32 off; - if (len > length) - len = length; - if (len > ss->sge.sge_length) - len = ss->sge.sge_length; /* If the source address is not aligned, try to align it. */ off = (unsigned long)ss->sge.vaddr & (sizeof(u32) - 1); if (off) { From db421a54996c602503204345171c662e65f20527 Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Wed, 23 Jan 2019 19:08:29 -0800 Subject: [PATCH 0800/4277] IB/{hfi1, qib, rvt} Cleanup open coded sge usage Several locations for manipulating sges use an open coded sequence that is covered by helper functions. Use the appropriate helper functions. Signed-off-by: Michael J. Ruhl Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/ud.c | 24 ++---------------------- drivers/infiniband/hw/qib/qib_sdma.c | 26 ++------------------------ drivers/infiniband/sw/rdmavt/qp.c | 26 ++------------------------ 3 files changed, 6 insertions(+), 70 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/ud.c b/drivers/infiniband/hw/hfi1/ud.c index 88242fe95eaa..c98d94cda15e 100644 --- a/drivers/infiniband/hw/hfi1/ud.c +++ b/drivers/infiniband/hw/hfi1/ud.c @@ -222,31 +222,11 @@ static void ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) ssge.num_sge = swqe->wr.num_sge; sge = &ssge.sge; while (length) { - u32 len = sge->length; + u32 len = rvt_get_sge_length(sge, length); - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; WARN_ON_ONCE(len == 0); rvt_copy_sge(qp, &qp->r_sge, sge->vaddr, len, true, false); - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (--ssge.num_sge) - *sge = *ssge.sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } + rvt_update_sge(&ssge, len, false); length -= len; } rvt_put_ss(&qp->r_sge); diff --git a/drivers/infiniband/hw/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c index 3d64081c4819..99e11c347130 100644 --- a/drivers/infiniband/hw/qib/qib_sdma.c +++ b/drivers/infiniband/hw/qib/qib_sdma.c @@ -565,13 +565,8 @@ retry: sge = &ss->sge; while (dwords) { u32 dw; - u32 len; + u32 len = rvt_get_sge_length(sge, dwords << 2); - len = dwords << 2; - if (len > sge->length) - len = sge->length; - if (len > sge->sge_length) - len = sge->sge_length; dw = (len + 3) >> 2; addr = dma_map_single(&ppd->dd->pcidev->dev, sge->vaddr, dw << 2, DMA_TO_DEVICE); @@ -594,24 +589,7 @@ retry: descqp = &ppd->sdma_descq[0].qw[0]; ++ppd->sdma_generation; } - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } - + rvt_update_sge(ss, len, false); dwoffset += dw; dwords -= dw; } diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index a1bd8cfc2c25..16247d2a671d 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -2986,34 +2986,12 @@ do_write: sge = &sqp->s_sge.sge; while (sqp->s_len) { - u32 len = sqp->s_len; + u32 len = rvt_get_sge_length(sge, sqp->s_len); - if (len > sge->length) - len = sge->length; - if (len > sge->sge_length) - len = sge->sge_length; WARN_ON_ONCE(len == 0); rvt_copy_sge(qp, &qp->r_sge, sge->vaddr, len, release, copy_last); - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (!release) - rvt_put_mr(sge->mr); - if (--sqp->s_sge.num_sge) - *sge = *sqp->s_sge.sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } + rvt_update_sge(&sqp->s_sge, len, !release); sqp->s_len -= len; } if (release) From 0b5cb3300ae59ed7e93b465dfa2384a6a4df8eb4 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 22 Jan 2019 10:25:20 -0800 Subject: [PATCH 0801/4277] RDMA/srp: Increase max_segment_size The default behavior of the SCSI core is to set the block layer request queue parameter max_segment_size to 64 KB. That means that elements of scatterlists are limited to 64 KB. Since RDMA adapters support larger sizes, increase max_segment_size for the SRP initiator. Notes: - The SCSI max_segment_size parameter was introduced in kernel v5.0. See also commit 50c2e9107f17 ("scsi: introduce a max_segment_size host_template parameters"). - Some other block drivers already set max_segment_size to UINT_MAX, e.g. nbd and rbd. Signed-off-by: Bart Van Assche Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/srp/ib_srp.c | 1 + include/rdma/ib_verbs.h | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 0f855294ff3f..29fe46dbdbf1 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -3823,6 +3823,7 @@ static ssize_t srp_create_target(struct device *dev, target_host->max_id = 1; target_host->max_lun = -1LL; target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb; + target_host->max_segment_size = ib_dma_max_seg_size(ibdev); target = host_to_target(target_host); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 94b6e1dd4dab..71ea144ec823 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -3715,6 +3715,19 @@ static inline unsigned int ib_sg_dma_len(struct ib_device *dev, return sg_dma_len(sg); } +/** + * ib_dma_max_seg_size - Return the size limit of a single DMA transfer + * @dev: The device to query + * + * The returned value represents a size in bytes. + */ +static inline unsigned int ib_dma_max_seg_size(struct ib_device *dev) +{ + struct device_dma_parameters *p = dev->dma_device->dma_parms; + + return p ? p->max_segment_size : UINT_MAX; +} + /** * ib_dma_sync_single_for_cpu - Prepare DMA region to be accessed by CPU * @dev: The device for which the DMA address was created From 3bd045cc9c4be2049602b47505256b43908b4e2f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 Jan 2019 13:15:45 -0500 Subject: [PATCH 0802/4277] separate copying and locking mount tree on cross-userns copies Rather than having propagate_mnt() check doing unprivileged copies, lock them before commit_tree(). Signed-off-by: Al Viro --- fs/namespace.c | 59 +++++++++++++++++++++++++++++++------------------- fs/pnode.c | 5 ----- fs/pnode.h | 3 +-- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index a677b59efd74..9ed2f2930dfd 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1013,27 +1013,6 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, mnt->mnt.mnt_flags = old->mnt.mnt_flags; mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD|MNT_MARKED|MNT_INTERNAL); - /* Don't allow unprivileged users to change mount flags */ - if (flag & CL_UNPRIVILEGED) { - mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; - - if (mnt->mnt.mnt_flags & MNT_READONLY) - mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; - - if (mnt->mnt.mnt_flags & MNT_NODEV) - mnt->mnt.mnt_flags |= MNT_LOCK_NODEV; - - if (mnt->mnt.mnt_flags & MNT_NOSUID) - mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID; - - if (mnt->mnt.mnt_flags & MNT_NOEXEC) - mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC; - } - - /* Don't allow unprivileged users to reveal what is under a mount */ - if ((flag & CL_UNPRIVILEGED) && - (!(flag & CL_EXPIRE) || list_empty(&old->mnt_expire))) - mnt->mnt.mnt_flags |= MNT_LOCKED; atomic_inc(&sb->s_active); mnt->mnt.mnt_sb = sb; @@ -1837,6 +1816,33 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, return 0; } +static void lock_mnt_tree(struct mount *mnt) +{ + struct mount *p; + + for (p = mnt; p; p = next_mnt(p, mnt)) { + int flags = p->mnt.mnt_flags; + /* Don't allow unprivileged users to change mount flags */ + flags |= MNT_LOCK_ATIME; + + if (flags & MNT_READONLY) + flags |= MNT_LOCK_READONLY; + + if (flags & MNT_NODEV) + flags |= MNT_LOCK_NODEV; + + if (flags & MNT_NOSUID) + flags |= MNT_LOCK_NOSUID; + + if (flags & MNT_NOEXEC) + flags |= MNT_LOCK_NOEXEC; + /* Don't allow unprivileged users to reveal what is under a mount */ + if (list_empty(&p->mnt_expire)) + flags |= MNT_LOCKED; + p->mnt.mnt_flags = flags; + } +} + static void cleanup_group_ids(struct mount *mnt, struct mount *end) { struct mount *p; @@ -1954,6 +1960,7 @@ static int attach_recursive_mnt(struct mount *source_mnt, struct mountpoint *dest_mp, struct path *parent_path) { + struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; HLIST_HEAD(tree_list); struct mnt_namespace *ns = dest_mnt->mnt_ns; struct mountpoint *smp; @@ -2004,6 +2011,9 @@ static int attach_recursive_mnt(struct mount *source_mnt, child->mnt_mountpoint); if (q) mnt_change_mountpoint(child, smp, q); + /* Notice when we are propagating across user namespaces */ + if (child->mnt_parent->mnt_ns->user_ns != user_ns) + lock_mnt_tree(child); commit_tree(child); } put_mountpoint(smp); @@ -2941,13 +2951,18 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, /* First pass: copy the tree topology */ copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; if (user_ns != ns->user_ns) - copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; + copy_flags |= CL_SHARED_TO_SLAVE; new = copy_tree(old, old->mnt.mnt_root, copy_flags); if (IS_ERR(new)) { namespace_unlock(); free_mnt_ns(new_ns); return ERR_CAST(new); } + if (user_ns != ns->user_ns) { + lock_mount_hash(); + lock_mnt_tree(new); + unlock_mount_hash(); + } new_ns->root = new; list_add_tail(&new_ns->list, &new->mnt_list); diff --git a/fs/pnode.c b/fs/pnode.c index 1100e810d855..7ea6cfb65077 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -214,7 +214,6 @@ static struct mount *next_group(struct mount *m, struct mount *origin) } /* all accesses are serialized by namespace_sem */ -static struct user_namespace *user_ns; static struct mount *last_dest, *first_source, *last_source, *dest_master; static struct mountpoint *mp; static struct hlist_head *list; @@ -260,9 +259,6 @@ static int propagate_one(struct mount *m) type |= CL_MAKE_SHARED; } - /* Notice when we are propagating across user namespaces */ - if (m->mnt_ns->user_ns != user_ns) - type |= CL_UNPRIVILEGED; child = copy_tree(last_source, last_source->mnt.mnt_root, type); if (IS_ERR(child)) return PTR_ERR(child); @@ -303,7 +299,6 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp, * propagate_one(); everything is serialized by namespace_sem, * so globals will do just fine. */ - user_ns = current->nsproxy->mnt_ns->user_ns; last_dest = dest_mnt; first_source = source_mnt; last_source = source_mnt; diff --git a/fs/pnode.h b/fs/pnode.h index dc87e65becd2..3960a83666cf 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -27,8 +27,7 @@ #define CL_MAKE_SHARED 0x08 #define CL_PRIVATE 0x10 #define CL_SHARED_TO_SLAVE 0x20 -#define CL_UNPRIVILEGED 0x40 -#define CL_COPY_MNT_NS_FILE 0x80 +#define CL_COPY_MNT_NS_FILE 0x40 #define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) From f3ffed0ce49a41cdbac5a85f42baeebe334ca9ab Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Wed, 30 Jan 2019 16:13:42 +0200 Subject: [PATCH 0803/4277] IB/mlx5: Make mlx5_ib_stage_odp_cleanup() static The function mlx5_ib_stage_odp_cleanup() is only used in main.c Fixes: d5d284b829a6 ("{net,IB}/mlx5: Move Page fault EQ and ODP logic to RDMA") Signed-off-by: Kamal Heib Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 2aaa196c2653..96bf1b2f9dd7 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -6176,7 +6176,7 @@ static int mlx5_ib_stage_odp_init(struct mlx5_ib_dev *dev) return mlx5_ib_odp_init_one(dev); } -void mlx5_ib_stage_odp_cleanup(struct mlx5_ib_dev *dev) +static void mlx5_ib_stage_odp_cleanup(struct mlx5_ib_dev *dev) { mlx5_ib_odp_cleanup_one(dev); } From e5c1bb47cca679e9b9e286981d64fd08792e1e80 Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Wed, 30 Jan 2019 16:13:43 +0200 Subject: [PATCH 0804/4277] IB/mlx5: Remove set but not used variable Remove 'del_mkey' variable that is set but not used. Fixes: 534fd7aac56a ("IB/mlx5: Manage indirection mkey upon DEVX flow for ODP") Signed-off-by: Kamal Heib Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 12de63c63fa0..cd43e39ced87 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1107,12 +1107,10 @@ static void devx_free_indirect_mkey(struct rcu_head *rcu) static void devx_cleanup_mkey(struct devx_obj *obj) { struct mlx5_mkey_table *table = &obj->mdev->priv.mkey_table; - struct mlx5_core_mkey *del_mkey; unsigned long flags; write_lock_irqsave(&table->lock, flags); - del_mkey = radix_tree_delete(&table->tree, - mlx5_base_mkey(obj->devx_mr.mmkey.key)); + radix_tree_delete(&table->tree, mlx5_base_mkey(obj->devx_mr.mmkey.key)); write_unlock_irqrestore(&table->lock, flags); } From 74e831221cfd79460ec11c1b641093863f0ef3ce Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 Jan 2019 13:30:21 -0500 Subject: [PATCH 0805/4277] saner handling of temporary namespaces mount_subtree() creates (and soon destroys) a temporary namespace, so that automounts could function normally. These beasts should never become anyone's current namespaces; they don't, but it would be better to make prevention of that more straightforward. And since they don't become anyone's current namespace, we don't need to bother with reserving procfs inums for those. Teach alloc_mnt_ns() to skip inum allocation if told so, adjust put_mnt_ns() accordingly, make mount_subtree() use temporary (anon) namespace. is_anon_ns() checks if a namespace is such. Signed-off-by: Al Viro --- fs/mount.h | 5 ++++ fs/namespace.c | 74 ++++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/fs/mount.h b/fs/mount.h index f39bc9da4d73..6250de544760 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -146,3 +146,8 @@ static inline bool is_local_mountpoint(struct dentry *dentry) return __is_local_mountpoint(dentry); } + +static inline bool is_anon_ns(struct mnt_namespace *ns) +{ + return ns->seq == 0; +} diff --git a/fs/namespace.c b/fs/namespace.c index 9ed2f2930dfd..f0b8a8ca08df 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2873,7 +2873,8 @@ static void dec_mnt_namespaces(struct ucounts *ucounts) static void free_mnt_ns(struct mnt_namespace *ns) { - ns_free_inum(&ns->ns); + if (!is_anon_ns(ns)) + ns_free_inum(&ns->ns); dec_mnt_namespaces(ns->ucounts); put_user_ns(ns->user_ns); kfree(ns); @@ -2888,7 +2889,7 @@ static void free_mnt_ns(struct mnt_namespace *ns) */ static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1); -static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns) +static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool anon) { struct mnt_namespace *new_ns; struct ucounts *ucounts; @@ -2898,28 +2899,27 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns) if (!ucounts) return ERR_PTR(-ENOSPC); - new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); + new_ns = kzalloc(sizeof(struct mnt_namespace), GFP_KERNEL); if (!new_ns) { dec_mnt_namespaces(ucounts); return ERR_PTR(-ENOMEM); } - ret = ns_alloc_inum(&new_ns->ns); - if (ret) { - kfree(new_ns); - dec_mnt_namespaces(ucounts); - return ERR_PTR(ret); + if (!anon) { + ret = ns_alloc_inum(&new_ns->ns); + if (ret) { + kfree(new_ns); + dec_mnt_namespaces(ucounts); + return ERR_PTR(ret); + } } new_ns->ns.ops = &mntns_operations; - new_ns->seq = atomic64_add_return(1, &mnt_ns_seq); + if (!anon) + new_ns->seq = atomic64_add_return(1, &mnt_ns_seq); atomic_set(&new_ns->count, 1); - new_ns->root = NULL; INIT_LIST_HEAD(&new_ns->list); init_waitqueue_head(&new_ns->poll); - new_ns->event = 0; new_ns->user_ns = get_user_ns(user_ns); new_ns->ucounts = ucounts; - new_ns->mounts = 0; - new_ns->pending_mounts = 0; return new_ns; } @@ -2943,7 +2943,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, old = ns->root; - new_ns = alloc_mnt_ns(user_ns); + new_ns = alloc_mnt_ns(user_ns, false); if (IS_ERR(new_ns)) return new_ns; @@ -3003,37 +3003,25 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, return new_ns; } -/** - * create_mnt_ns - creates a private namespace and adds a root filesystem - * @mnt: pointer to the new root filesystem mountpoint - */ -static struct mnt_namespace *create_mnt_ns(struct vfsmount *m) -{ - struct mnt_namespace *new_ns = alloc_mnt_ns(&init_user_ns); - if (!IS_ERR(new_ns)) { - struct mount *mnt = real_mount(m); - mnt->mnt_ns = new_ns; - new_ns->root = mnt; - new_ns->mounts++; - list_add(&mnt->mnt_list, &new_ns->list); - } else { - mntput(m); - } - return new_ns; -} - -struct dentry *mount_subtree(struct vfsmount *mnt, const char *name) +struct dentry *mount_subtree(struct vfsmount *m, const char *name) { + struct mount *mnt = real_mount(m); struct mnt_namespace *ns; struct super_block *s; struct path path; int err; - ns = create_mnt_ns(mnt); - if (IS_ERR(ns)) + ns = alloc_mnt_ns(&init_user_ns, true); + if (IS_ERR(ns)) { + mntput(m); return ERR_CAST(ns); + } + mnt->mnt_ns = ns; + ns->root = mnt; + ns->mounts++; + list_add(&mnt->mnt_list, &ns->list); - err = vfs_path_lookup(mnt->mnt_root, mnt, + err = vfs_path_lookup(m->mnt_root, m, name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); put_mnt_ns(ns); @@ -3243,6 +3231,7 @@ out0: static void __init init_mount_tree(void) { struct vfsmount *mnt; + struct mount *m; struct mnt_namespace *ns; struct path root; struct file_system_type *type; @@ -3255,10 +3244,14 @@ static void __init init_mount_tree(void) if (IS_ERR(mnt)) panic("Can't create rootfs"); - ns = create_mnt_ns(mnt); + ns = alloc_mnt_ns(&init_user_ns, false); if (IS_ERR(ns)) panic("Can't allocate initial namespace"); - + m = real_mount(mnt); + m->mnt_ns = ns; + ns->root = m; + ns->mounts = 1; + list_add(&m->mnt_list, &ns->list); init_task.nsproxy->mnt_ns = ns; get_mnt_ns(ns); @@ -3499,6 +3492,9 @@ static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns) !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; + if (is_anon_ns(mnt_ns)) + return -EINVAL; + if (fs->users != 1) return -EINVAL; From 9bc61ab18b1d41f26dc06b9e6d3c203e65f83fe6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 4 Nov 2018 03:19:03 -0500 Subject: [PATCH 0806/4277] vfs: Introduce fs_context, switch vfs_kern_mount() to it. Introduce a filesystem context concept to be used during superblock creation for mount and superblock reconfiguration for remount. This is allocated at the beginning of the mount procedure and into it is placed: (1) Filesystem type. (2) Namespaces. (3) Source/Device names (there may be multiple). (4) Superblock flags (SB_*). (5) Security details. (6) Filesystem-specific data, as set by the mount options. Accessor functions are then provided to set up a context, parameterise it from monolithic mount data (the data page passed to mount(2)) and tear it down again. A legacy wrapper is provided that implements what will be the basic operations, wrapping access to filesystems that aren't yet aware of the fs_context. Finally, vfs_kern_mount() is changed to make use of the fs_context and mount_fs() is replaced by vfs_get_tree(), called from vfs_kern_mount(). [AV -- add missing kstrdup()] [AV -- put_cred() can be unconditional - fc->cred can't be NULL] [AV -- take legacy_validate() contents into legacy_parse_monolithic()] [AV -- merge KERNEL_MOUNT and USER_MOUNT] [AV -- don't unlock superblock on success return from vfs_get_tree()] [AV -- kill 'reference' argument of init_fs_context()] Signed-off-by: David Howells Co-developed-by: Al Viro Signed-off-by: Al Viro --- fs/Makefile | 3 +- fs/fs_context.c | 182 +++++++++++++++++++++++++++++++++++++ fs/internal.h | 9 +- fs/namespace.c | 46 +++++++--- fs/super.c | 50 +++++----- include/linux/fs_context.h | 64 +++++++++++++ 6 files changed, 310 insertions(+), 44 deletions(-) create mode 100644 fs/fs_context.c create mode 100644 include/linux/fs_context.h diff --git a/fs/Makefile b/fs/Makefile index 293733f61594..5563cf34f7c2 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -12,7 +12,8 @@ obj-y := open.o read_write.o file_table.o super.o \ attr.o bad_inode.o file.o filesystems.o namespace.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o splice.o sync.o utimes.o d_path.o \ - stack.o fs_struct.o statfs.o fs_pin.o nsfs.o + stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ + fs_context.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o diff --git a/fs/fs_context.c b/fs/fs_context.c new file mode 100644 index 000000000000..4294091b689d --- /dev/null +++ b/fs/fs_context.c @@ -0,0 +1,182 @@ +/* Provide a way to create a superblock configuration context within the kernel + * that allows a superblock to be set up prior to mounting. + * + * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mount.h" +#include "internal.h" + +struct legacy_fs_context { + char *legacy_data; /* Data page for legacy filesystems */ + size_t data_size; +}; + +static int legacy_init_fs_context(struct fs_context *fc); + +/** + * alloc_fs_context - Create a filesystem context. + * @fs_type: The filesystem type. + * @reference: The dentry from which this one derives (or NULL) + * @sb_flags: Filesystem/superblock flags (SB_*) + * @sb_flags_mask: Applicable members of @sb_flags + * @purpose: The purpose that this configuration shall be used for. + * + * Open a filesystem and create a mount context. The mount context is + * initialised with the supplied flags and, if a submount/automount from + * another superblock (referred to by @reference) is supplied, may have + * parameters such as namespaces copied across from that superblock. + */ +static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, + struct dentry *reference, + unsigned int sb_flags, + unsigned int sb_flags_mask, + enum fs_context_purpose purpose) +{ + struct fs_context *fc; + int ret = -ENOMEM; + + fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL); + if (!fc) + return ERR_PTR(-ENOMEM); + + fc->purpose = purpose; + fc->sb_flags = sb_flags; + fc->sb_flags_mask = sb_flags_mask; + fc->fs_type = get_filesystem(fs_type); + fc->cred = get_current_cred(); + fc->net_ns = get_net(current->nsproxy->net_ns); + + switch (purpose) { + case FS_CONTEXT_FOR_MOUNT: + fc->user_ns = get_user_ns(fc->cred->user_ns); + break; + } + + ret = legacy_init_fs_context(fc); + if (ret < 0) + goto err_fc; + fc->need_free = true; + return fc; + +err_fc: + put_fs_context(fc); + return ERR_PTR(ret); +} + +struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, + unsigned int sb_flags) +{ + return alloc_fs_context(fs_type, NULL, sb_flags, 0, + FS_CONTEXT_FOR_MOUNT); +} +EXPORT_SYMBOL(fs_context_for_mount); + +static void legacy_fs_context_free(struct fs_context *fc); +/** + * put_fs_context - Dispose of a superblock configuration context. + * @fc: The context to dispose of. + */ +void put_fs_context(struct fs_context *fc) +{ + struct super_block *sb; + + if (fc->root) { + sb = fc->root->d_sb; + dput(fc->root); + fc->root = NULL; + deactivate_super(sb); + } + + if (fc->need_free) + legacy_fs_context_free(fc); + + security_free_mnt_opts(&fc->security); + if (fc->net_ns) + put_net(fc->net_ns); + put_user_ns(fc->user_ns); + put_cred(fc->cred); + kfree(fc->subtype); + put_filesystem(fc->fs_type); + kfree(fc->source); + kfree(fc); +} +EXPORT_SYMBOL(put_fs_context); + +/* + * Free the config for a filesystem that doesn't support fs_context. + */ +static void legacy_fs_context_free(struct fs_context *fc) +{ + kfree(fc->fs_private); +} + +/* + * Add monolithic mount data. + */ +static int legacy_parse_monolithic(struct fs_context *fc, void *data) +{ + struct legacy_fs_context *ctx = fc->fs_private; + ctx->legacy_data = data; + if (!ctx->legacy_data) + return 0; + if (fc->fs_type->fs_flags & FS_BINARY_MOUNTDATA) + return 0; + return security_sb_eat_lsm_opts(ctx->legacy_data, &fc->security); +} + +/* + * Get a mountable root with the legacy mount command. + */ +int legacy_get_tree(struct fs_context *fc) +{ + struct legacy_fs_context *ctx = fc->fs_private; + struct super_block *sb; + struct dentry *root; + + root = fc->fs_type->mount(fc->fs_type, fc->sb_flags, + fc->source, ctx->legacy_data); + if (IS_ERR(root)) + return PTR_ERR(root); + + sb = root->d_sb; + BUG_ON(!sb); + + fc->root = root; + return 0; +} + +/* + * Initialise a legacy context for a filesystem that doesn't support + * fs_context. + */ +static int legacy_init_fs_context(struct fs_context *fc) +{ + fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL); + if (!fc->fs_private) + return -ENOMEM; + return 0; +} + +int parse_monolithic_mount_data(struct fs_context *fc, void *data) +{ + return legacy_parse_monolithic(fc, data); +} diff --git a/fs/internal.h b/fs/internal.h index d410186bc369..f85c3212d25d 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -17,6 +17,7 @@ struct linux_binprm; struct path; struct mount; struct shrink_control; +struct fs_context; /* * block_dev.c @@ -51,6 +52,12 @@ int __generic_write_end(struct inode *inode, loff_t pos, unsigned copied, */ extern void __init chrdev_init(void); +/* + * fs_context.c + */ +extern int legacy_get_tree(struct fs_context *fc); +extern int parse_monolithic_mount_data(struct fs_context *, void *); + /* * namei.c */ @@ -101,8 +108,6 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *); */ extern int do_remount_sb(struct super_block *, int, void *, int); extern bool trylock_super(struct super_block *sb); -extern struct dentry *mount_fs(struct file_system_type *, - int, const char *, void *); extern struct super_block *user_get_super(dev_t); /* diff --git a/fs/namespace.c b/fs/namespace.c index f0b8a8ca08df..3f2fd7a34733 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "pnode.h" #include "internal.h" @@ -940,36 +941,53 @@ static struct mount *skip_mnt_tree(struct mount *p) return p; } -struct vfsmount * -vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) +struct vfsmount *vfs_kern_mount(struct file_system_type *type, + int flags, const char *name, + void *data) { + struct fs_context *fc; struct mount *mnt; - struct dentry *root; + int ret = 0; if (!type) return ERR_PTR(-ENODEV); + fc = fs_context_for_mount(type, flags); + if (IS_ERR(fc)) + return ERR_CAST(fc); + + if (name) { + fc->source = kstrdup(name, GFP_KERNEL); + if (!fc->source) + ret = -ENOMEM; + } + if (!ret) + ret = parse_monolithic_mount_data(fc, data); + if (!ret) + ret = vfs_get_tree(fc); + if (ret) { + put_fs_context(fc); + return ERR_PTR(ret); + } + up_write(&fc->root->d_sb->s_umount); mnt = alloc_vfsmnt(name); - if (!mnt) + if (!mnt) { + put_fs_context(fc); return ERR_PTR(-ENOMEM); + } if (flags & SB_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; - root = mount_fs(type, flags, name, data); - if (IS_ERR(root)) { - mnt_free_id(mnt); - free_vfsmnt(mnt); - return ERR_CAST(root); - } - - mnt->mnt.mnt_root = root; - mnt->mnt.mnt_sb = root->d_sb; + atomic_inc(&fc->root->d_sb->s_active); + mnt->mnt.mnt_root = dget(fc->root); + mnt->mnt.mnt_sb = fc->root->d_sb; mnt->mnt_mountpoint = mnt->mnt.mnt_root; mnt->mnt_parent = mnt; lock_mount_hash(); - list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts); + list_add_tail(&mnt->mnt_instance, &fc->root->d_sb->s_mounts); unlock_mount_hash(); + put_fs_context(fc); return &mnt->mnt; } EXPORT_SYMBOL_GPL(vfs_kern_mount); diff --git a/fs/super.c b/fs/super.c index 48e25eba8465..fc3887277ad1 100644 --- a/fs/super.c +++ b/fs/super.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "internal.h" @@ -1241,27 +1242,24 @@ struct dentry *mount_single(struct file_system_type *fs_type, } EXPORT_SYMBOL(mount_single); -struct dentry * -mount_fs(struct file_system_type *type, int flags, const char *name, void *data) +/** + * vfs_get_tree - Get the mountable root + * @fc: The superblock configuration context. + * + * The filesystem is invoked to get or create a superblock which can then later + * be used for mounting. The filesystem places a pointer to the root to be + * used for mounting in @fc->root. + */ +int vfs_get_tree(struct fs_context *fc) { - struct dentry *root; struct super_block *sb; - int error = -ENOMEM; - void *sec_opts = NULL; + int error; - if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) { - error = security_sb_eat_lsm_opts(data, &sec_opts); - if (error) - return ERR_PTR(error); - } + error = legacy_get_tree(fc); + if (error < 0) + return error; - root = type->mount(type, flags, name, data); - if (IS_ERR(root)) { - error = PTR_ERR(root); - goto out_free_secdata; - } - sb = root->d_sb; - BUG_ON(!sb); + sb = fc->root->d_sb; WARN_ON(!sb->s_bdi); /* @@ -1273,11 +1271,11 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data) smp_wmb(); sb->s_flags |= SB_BORN; - error = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL); + error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL); if (error) goto out_sb; - if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT))) { + if (!(fc->sb_flags & (MS_KERNMOUNT|MS_SUBMOUNT))) { error = security_sb_kern_mount(sb); if (error) goto out_sb; @@ -1290,18 +1288,16 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data) * violate this rule. */ WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to " - "negative value (%lld)\n", type->name, sb->s_maxbytes); + "negative value (%lld)\n", fc->fs_type->name, sb->s_maxbytes); - up_write(&sb->s_umount); - security_free_mnt_opts(&sec_opts); - return root; + return 0; out_sb: - dput(root); + dput(fc->root); + fc->root = NULL; deactivate_locked_super(sb); -out_free_secdata: - security_free_mnt_opts(&sec_opts); - return ERR_PTR(error); + return error; } +EXPORT_SYMBOL(vfs_get_tree); /* * Setup private BDI for given superblock. It gets automatically cleaned up diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h new file mode 100644 index 000000000000..9805514444c9 --- /dev/null +++ b/include/linux/fs_context.h @@ -0,0 +1,64 @@ +/* Filesystem superblock creation and reconfiguration context. + * + * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_FS_CONTEXT_H +#define _LINUX_FS_CONTEXT_H + +#include +#include +#include + +struct cred; +struct dentry; +struct file_operations; +struct file_system_type; +struct net; +struct user_namespace; + +enum fs_context_purpose { + FS_CONTEXT_FOR_MOUNT, /* New superblock for explicit mount */ +}; + +/* + * Filesystem context for holding the parameters used in the creation or + * reconfiguration of a superblock. + * + * Superblock creation fills in ->root whereas reconfiguration begins with this + * already set. + * + * See Documentation/filesystems/mounting.txt + */ +struct fs_context { + struct file_system_type *fs_type; + void *fs_private; /* The filesystem's context */ + struct dentry *root; /* The root and superblock */ + struct user_namespace *user_ns; /* The user namespace for this mount */ + struct net *net_ns; /* The network namespace for this mount */ + const struct cred *cred; /* The mounter's credentials */ + const char *source; /* The source name (eg. dev path) */ + const char *subtype; /* The subtype to set on the superblock */ + void *security; /* Linux S&M options */ + unsigned int sb_flags; /* Proposed superblock flags (SB_*) */ + unsigned int sb_flags_mask; /* Superblock flags that were changed */ + enum fs_context_purpose purpose:8; + bool need_free:1; /* Need to call ops->free() */ +}; + +/* + * fs_context manipulation functions. + */ +extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, + unsigned int sb_flags); + +extern int vfs_get_tree(struct fs_context *fc); +extern void put_fs_context(struct fs_context *fc); + +#endif /* _LINUX_FS_CONTEXT_H */ From 8f2918898eb5fe25845dde7f4a77bda0e2966e05 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 4 Nov 2018 06:48:34 -0500 Subject: [PATCH 0807/4277] new helpers: vfs_create_mount(), fc_mount() Create a new helper, vfs_create_mount(), that creates a detached vfsmount object from an fs_context that has a superblock attached to it. Almost all uses will be paired with immediately preceding vfs_get_tree(); add a helper for such combination. Switch vfs_kern_mount() to use this. NOTE: mild behaviour change; passing NULL as 'device name' to something like procfs will change /proc/*/mountstats - "device none" instead on "no device". That is consistent with /proc/mounts et.al. [do'h - EXPORT_SYMBOL_GPL slipped in by mistake; removed] [AV -- remove confused comment from vfs_create_mount()] [AV -- removed the second argument] Reviewed-by: David Howells Signed-off-by: Al Viro --- fs/namespace.c | 76 +++++++++++++++++++++++++++++-------------- include/linux/mount.h | 3 ++ 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 3f2fd7a34733..156771f5745a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -941,12 +941,59 @@ static struct mount *skip_mnt_tree(struct mount *p) return p; } +/** + * vfs_create_mount - Create a mount for a configured superblock + * @fc: The configuration context with the superblock attached + * + * Create a mount to an already configured superblock. If necessary, the + * caller should invoke vfs_get_tree() before calling this. + * + * Note that this does not attach the mount to anything. + */ +struct vfsmount *vfs_create_mount(struct fs_context *fc) +{ + struct mount *mnt; + + if (!fc->root) + return ERR_PTR(-EINVAL); + + mnt = alloc_vfsmnt(fc->source ?: "none"); + if (!mnt) + return ERR_PTR(-ENOMEM); + + if (fc->sb_flags & SB_KERNMOUNT) + mnt->mnt.mnt_flags = MNT_INTERNAL; + + atomic_inc(&fc->root->d_sb->s_active); + mnt->mnt.mnt_sb = fc->root->d_sb; + mnt->mnt.mnt_root = dget(fc->root); + mnt->mnt_mountpoint = mnt->mnt.mnt_root; + mnt->mnt_parent = mnt; + + lock_mount_hash(); + list_add_tail(&mnt->mnt_instance, &mnt->mnt.mnt_sb->s_mounts); + unlock_mount_hash(); + return &mnt->mnt; +} +EXPORT_SYMBOL(vfs_create_mount); + +struct vfsmount *fc_mount(struct fs_context *fc) +{ + int err = vfs_get_tree(fc); + if (!err) { + up_write(&fc->root->d_sb->s_umount); + return vfs_create_mount(fc); + } + return ERR_PTR(err); +} +EXPORT_SYMBOL(fc_mount); + struct vfsmount *vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) { struct fs_context *fc; - struct mount *mnt; + struct vfsmount *mnt; int ret = 0; if (!type) @@ -964,31 +1011,12 @@ struct vfsmount *vfs_kern_mount(struct file_system_type *type, if (!ret) ret = parse_monolithic_mount_data(fc, data); if (!ret) - ret = vfs_get_tree(fc); - if (ret) { - put_fs_context(fc); - return ERR_PTR(ret); - } - up_write(&fc->root->d_sb->s_umount); - mnt = alloc_vfsmnt(name); - if (!mnt) { - put_fs_context(fc); - return ERR_PTR(-ENOMEM); - } + mnt = fc_mount(fc); + else + mnt = ERR_PTR(ret); - if (flags & SB_KERNMOUNT) - mnt->mnt.mnt_flags = MNT_INTERNAL; - - atomic_inc(&fc->root->d_sb->s_active); - mnt->mnt.mnt_root = dget(fc->root); - mnt->mnt.mnt_sb = fc->root->d_sb; - mnt->mnt_mountpoint = mnt->mnt.mnt_root; - mnt->mnt_parent = mnt; - lock_mount_hash(); - list_add_tail(&mnt->mnt_instance, &fc->root->d_sb->s_mounts); - unlock_mount_hash(); put_fs_context(fc); - return &mnt->mnt; + return mnt; } EXPORT_SYMBOL_GPL(vfs_kern_mount); diff --git a/include/linux/mount.h b/include/linux/mount.h index 037eed52164b..9197ddbf35fb 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -21,6 +21,7 @@ struct super_block; struct vfsmount; struct dentry; struct mnt_namespace; +struct fs_context; #define MNT_NOSUID 0x01 #define MNT_NODEV 0x02 @@ -88,6 +89,8 @@ struct path; extern struct vfsmount *clone_private_mount(const struct path *path); struct file_system_type; +extern struct vfsmount *fc_mount(struct fs_context *fc); +extern struct vfsmount *vfs_create_mount(struct fs_context *fc); extern struct vfsmount *vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data); From a0c9a8b8fd9fd572b0d60276beb2142c8f59f9b8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 4 Nov 2018 07:18:51 -0500 Subject: [PATCH 0808/4277] teach vfs_get_tree() to handle subtype, switch do_new_mount() to it Roll the handling of subtypes into do_new_mount() and vfs_get_tree(). The former determines any subtype string and hangs it off the fs_context; the latter applies it. Make do_new_mount() create, parameterise and commit an fs_context and create a mount for itself rather than calling vfs_kern_mount(). [AV -- missing kstrdup()] [AV -- ... and no kstrdup() if we get to setting ->s_submount - we simply transfer it from fc, leaving NULL behind] [AV -- constify ->s_submount, while we are at it] Reviewed-by: David Howells Signed-off-by: Al Viro --- fs/namespace.c | 77 +++++++++++++++++++++++++++------------------- fs/super.c | 5 +++ include/linux/fs.h | 2 +- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 156771f5745a..0354cb6ac2d3 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2479,29 +2479,6 @@ out: return err; } -static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) -{ - int err; - const char *subtype = strchr(fstype, '.'); - if (subtype) { - subtype++; - err = -EINVAL; - if (!subtype[0]) - goto err; - } else - subtype = ""; - - mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL); - err = -ENOMEM; - if (!mnt->mnt_sb->s_subtype) - goto err; - return mnt; - - err: - mntput(mnt); - return ERR_PTR(err); -} - /* * add a mount into a namespace's mount tree */ @@ -2557,7 +2534,9 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, { struct file_system_type *type; struct vfsmount *mnt; - int err; + struct fs_context *fc; + const char *subtype = NULL; + int err = 0; if (!fstype) return -EINVAL; @@ -2566,23 +2545,59 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, if (!type) return -ENODEV; - mnt = vfs_kern_mount(type, sb_flags, name, data); - if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && - !mnt->mnt_sb->s_subtype) - mnt = fs_set_subtype(mnt, fstype); + if (type->fs_flags & FS_HAS_SUBTYPE) { + subtype = strchr(fstype, '.'); + if (subtype) { + subtype++; + if (!*subtype) { + put_filesystem(type); + return -EINVAL; + } + } else { + subtype = ""; + } + } + fc = fs_context_for_mount(type, sb_flags); put_filesystem(type); - if (IS_ERR(mnt)) - return PTR_ERR(mnt); + if (IS_ERR(fc)) + return PTR_ERR(fc); + + if (subtype) { + fc->subtype = kstrdup(subtype, GFP_KERNEL); + if (!fc->subtype) + err = -ENOMEM; + } + if (!err && name) { + fc->source = kstrdup(name, GFP_KERNEL); + if (!fc->source) + err = -ENOMEM; + } + if (!err) + err = parse_monolithic_mount_data(fc, data); + if (!err) + err = vfs_get_tree(fc); + if (err) + goto out; + + up_write(&fc->root->d_sb->s_umount); + mnt = vfs_create_mount(fc); + if (IS_ERR(mnt)) { + err = PTR_ERR(mnt); + goto out; + } if (mount_too_revealing(mnt, &mnt_flags)) { mntput(mnt); - return -EPERM; + err = -EPERM; + goto out; } err = do_add_mount(real_mount(mnt), path, mnt_flags); if (err) mntput(mnt); +out: + put_fs_context(fc); return err; } diff --git a/fs/super.c b/fs/super.c index fc3887277ad1..b91b6df05b67 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1262,6 +1262,11 @@ int vfs_get_tree(struct fs_context *fc) sb = fc->root->d_sb; WARN_ON(!sb->s_bdi); + if (fc->subtype && !sb->s_subtype) { + sb->s_subtype = fc->subtype; + fc->subtype = NULL; + } + /* * Write barrier is for super_cache_count(). We place it before setting * SB_BORN as the data dependency between the two functions is the diff --git a/include/linux/fs.h b/include/linux/fs.h index 811c77743dad..36fff12ab890 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1447,7 +1447,7 @@ struct super_block { * Filesystem subtype. If non-empty the filesystem type field * in /proc/mounts will be "type.subtype" */ - char *s_subtype; + const char *s_subtype; const struct dentry_operations *s_d_op; /* default d_op for dentries */ From 132e460848f4261b8a6b9c28fae52bf9e02b52fd Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 4 Nov 2018 07:43:08 -0500 Subject: [PATCH 0809/4277] new helper: do_new_mount_fc() Create an fs_context-aware version of do_new_mount(). This takes an fs_context with a superblock already attached to it. Make do_new_mount() use do_new_mount_fc() rather than do_new_mount(); this allows the consolidation of the mount creation, check and add steps. To make this work, mount_too_revealing() is changed to take a superblock rather than a mount (which the fs_context doesn't have available), allowing this check to be done before the mount object is created. Signed-off-by: David Howells Co-developed-by: Al Viro Signed-off-by: Al Viro --- fs/namespace.c | 65 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 0354cb6ac2d3..f629e1c7f3cc 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2523,7 +2523,37 @@ unlock: return err; } -static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags); +static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags); + +/* + * Create a new mount using a superblock configuration and request it + * be added to the namespace tree. + */ +static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint, + unsigned int mnt_flags) +{ + struct vfsmount *mnt; + struct super_block *sb = fc->root->d_sb; + int error; + + if (mount_too_revealing(sb, &mnt_flags)) { + dput(fc->root); + fc->root = NULL; + deactivate_locked_super(sb); + return -EPERM; + } + + up_write(&sb->s_umount); + + mnt = vfs_create_mount(fc); + if (IS_ERR(mnt)) + return PTR_ERR(mnt); + + error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags); + if (error < 0) + mntput(mnt); + return error; +} /* * create a new mount for userspace and request it to be added into the @@ -2533,7 +2563,6 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, int mnt_flags, const char *name, void *data) { struct file_system_type *type; - struct vfsmount *mnt; struct fs_context *fc; const char *subtype = NULL; int err = 0; @@ -2577,26 +2606,9 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, err = parse_monolithic_mount_data(fc, data); if (!err) err = vfs_get_tree(fc); - if (err) - goto out; + if (!err) + err = do_new_mount_fc(fc, path, mnt_flags); - up_write(&fc->root->d_sb->s_umount); - mnt = vfs_create_mount(fc); - if (IS_ERR(mnt)) { - err = PTR_ERR(mnt); - goto out; - } - - if (mount_too_revealing(mnt, &mnt_flags)) { - mntput(mnt); - err = -EPERM; - goto out; - } - - err = do_add_mount(real_mount(mnt), path, mnt_flags); - if (err) - mntput(mnt); -out: put_fs_context(fc); return err; } @@ -3421,7 +3433,8 @@ bool current_chrooted(void) return chrooted; } -static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new, +static bool mnt_already_visible(struct mnt_namespace *ns, + const struct super_block *sb, int *new_mnt_flags) { int new_flags = *new_mnt_flags; @@ -3433,7 +3446,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new, struct mount *child; int mnt_flags; - if (mnt->mnt.mnt_sb->s_type != new->mnt_sb->s_type) + if (mnt->mnt.mnt_sb->s_type != sb->s_type) continue; /* This mount is not fully visible if it's root directory @@ -3484,7 +3497,7 @@ found: return visible; } -static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) +static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags) { const unsigned long required_iflags = SB_I_NOEXEC | SB_I_NODEV; struct mnt_namespace *ns = current->nsproxy->mnt_ns; @@ -3494,7 +3507,7 @@ static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) return false; /* Can this filesystem be too revealing? */ - s_iflags = mnt->mnt_sb->s_iflags; + s_iflags = sb->s_iflags; if (!(s_iflags & SB_I_USERNS_VISIBLE)) return false; @@ -3504,7 +3517,7 @@ static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) return true; } - return !mnt_already_visible(ns, mnt, new_mnt_flags); + return !mnt_already_visible(ns, sb, new_mnt_flags); } bool mnt_may_suid(struct vfsmount *mnt) From c9ce29ed795fae86e594844857fad1b0d3be85f4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 20 Dec 2018 15:04:50 -0500 Subject: [PATCH 0810/4277] vfs_get_tree(): evict the call of security_sb_kern_mount() Right now vfs_get_tree() calls security_sb_kern_mount() (i.e. mount MAC) unless it gets MS_KERNMOUNT or MS_SUBMOUNT in flags. Doing it that way is both clumsy and imprecise. Consider the callers' tree of vfs_get_tree(): vfs_get_tree() <- do_new_mount() <- vfs_kern_mount() <- simple_pin_fs() <- vfs_submount() <- kern_mount_data() <- init_mount_tree() <- btrfs_mount() <- vfs_get_tree() <- nfs_do_root_mount() <- nfs4_try_mount() <- nfs_fs_mount() <- vfs_get_tree() <- nfs4_referral_mount() do_new_mount() always does need MAC (we are guaranteed that neither MS_KERNMOUNT nor MS_SUBMOUNT will be passed there). simple_pin_fs(), vfs_submount() and kern_mount_data() pass explicit flags inhibiting that check. So does nfs4_referral_mount() (the flags there are ulimately coming from vfs_submount()). init_mount_tree() is called too early for anything LSM-related; it doesn't matter whether we attempt those checks, they'll do nothing. Finally, in case of btrfs_mount() and nfs_fs_mount(), doing MAC is pointless - either the caller will do it, or the flags are such that we wouldn't have done it either. In other words, the one and only case when we want that check done is when we are called from do_new_mount(), and there we want it unconditionally. So let's simply move it there. The superblock is still locked, so nobody is going to get access to it (via ustat(2), etc.) until we get a chance to apply the checks - we are free to move them to any point up to where we drop ->s_umount (in do_new_mount_fc()). Signed-off-by: Al Viro --- fs/fs_context.c | 8 ++++++++ fs/internal.h | 1 + fs/namespace.c | 12 +++++++----- fs/super.c | 15 +++------------ 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/fs/fs_context.c b/fs/fs_context.c index 4294091b689d..857cd46a687b 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -90,6 +90,14 @@ struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, } EXPORT_SYMBOL(fs_context_for_mount); +void fc_drop_locked(struct fs_context *fc) +{ + struct super_block *sb = fc->root->d_sb; + dput(fc->root); + fc->root = NULL; + deactivate_locked_super(sb); +} + static void legacy_fs_context_free(struct fs_context *fc); /** * put_fs_context - Dispose of a superblock configuration context. diff --git a/fs/internal.h b/fs/internal.h index f85c3212d25d..6af26d897034 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -57,6 +57,7 @@ extern void __init chrdev_init(void); */ extern int legacy_get_tree(struct fs_context *fc); extern int parse_monolithic_mount_data(struct fs_context *, void *); +extern void fc_drop_locked(struct fs_context *); /* * namei.c diff --git a/fs/namespace.c b/fs/namespace.c index f629e1c7f3cc..750500c6c33d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2536,11 +2536,13 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint, struct super_block *sb = fc->root->d_sb; int error; - if (mount_too_revealing(sb, &mnt_flags)) { - dput(fc->root); - fc->root = NULL; - deactivate_locked_super(sb); - return -EPERM; + error = security_sb_kern_mount(sb); + if (!error && mount_too_revealing(sb, &mnt_flags)) + error = -EPERM; + + if (unlikely(error)) { + fc_drop_locked(fc); + return error; } up_write(&sb->s_umount); diff --git a/fs/super.c b/fs/super.c index b91b6df05b67..11e2a6cb3baf 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1277,13 +1277,9 @@ int vfs_get_tree(struct fs_context *fc) sb->s_flags |= SB_BORN; error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL); - if (error) - goto out_sb; - - if (!(fc->sb_flags & (MS_KERNMOUNT|MS_SUBMOUNT))) { - error = security_sb_kern_mount(sb); - if (error) - goto out_sb; + if (unlikely(error)) { + fc_drop_locked(fc); + return error; } /* @@ -1296,11 +1292,6 @@ int vfs_get_tree(struct fs_context *fc) "negative value (%lld)\n", fc->fs_type->name, sb->s_maxbytes); return 0; -out_sb: - dput(fc->root); - fc->root = NULL; - deactivate_locked_super(sb); - return error; } EXPORT_SYMBOL(vfs_get_tree); From 8d0347f6c3a9d4953ddd636a31c6584da082e084 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 4 Nov 2018 09:28:36 -0500 Subject: [PATCH 0811/4277] convert do_remount_sb() to fs_context Replace do_remount_sb() with a function, reconfigure_super(), that's fs_context aware. The fs_context is expected to be parameterised already and have ->root pointing to the superblock to be reconfigured. A legacy wrapper is provided that is intended to be called from the fs_context ops when those appear, but for now is called directly from reconfigure_super(). This wrapper invokes the ->remount_fs() superblock op for the moment. It is intended that the remount_fs() op will be phased out. The fs_context->purpose is set to FS_CONTEXT_FOR_RECONFIGURE to indicate that the context is being used for reconfiguration. do_umount_root() is provided to consolidate remount-to-R/O for umount and emergency remount by creating a context and invoking reconfiguration. do_remount(), do_umount() and do_emergency_remount_callback() are switched to use the new process. [AV -- fold UMOUNT and EMERGENCY_REMOUNT in; fixes the umount / bug, gets rid of pointless complexity] [AV -- set ->net_ns in all cases; nfs remount will need that] [AV -- shift security_sb_remount() call into reconfigure_super(); the callers that didn't do security_sb_remount() have NULL fc->security anyway, so it's a no-op for them] Signed-off-by: David Howells Co-developed-by: Al Viro Signed-off-by: Al Viro --- fs/fs_context.c | 35 +++++++++++- fs/internal.h | 3 +- fs/namespace.c | 61 +++++++++++++-------- fs/super.c | 107 +++++++++++++++++++++++++------------ include/linux/fs.h | 1 + include/linux/fs_context.h | 4 ++ 6 files changed, 152 insertions(+), 59 deletions(-) diff --git a/fs/fs_context.c b/fs/fs_context.c index 857cd46a687b..5e2c3aba1dd8 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -69,6 +69,13 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, case FS_CONTEXT_FOR_MOUNT: fc->user_ns = get_user_ns(fc->cred->user_ns); break; + case FS_CONTEXT_FOR_RECONFIGURE: + /* We don't pin any namespaces as the superblock's + * subscriptions cannot be changed at this point. + */ + atomic_inc(&reference->d_sb->s_active); + fc->root = dget(reference); + break; } ret = legacy_init_fs_context(fc); @@ -90,6 +97,15 @@ struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, } EXPORT_SYMBOL(fs_context_for_mount); +struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, + unsigned int sb_flags, + unsigned int sb_flags_mask) +{ + return alloc_fs_context(dentry->d_sb->s_type, dentry, sb_flags, + sb_flags_mask, FS_CONTEXT_FOR_RECONFIGURE); +} +EXPORT_SYMBOL(fs_context_for_reconfigure); + void fc_drop_locked(struct fs_context *fc) { struct super_block *sb = fc->root->d_sb; @@ -99,6 +115,7 @@ void fc_drop_locked(struct fs_context *fc) } static void legacy_fs_context_free(struct fs_context *fc); + /** * put_fs_context - Dispose of a superblock configuration context. * @fc: The context to dispose of. @@ -118,8 +135,7 @@ void put_fs_context(struct fs_context *fc) legacy_fs_context_free(fc); security_free_mnt_opts(&fc->security); - if (fc->net_ns) - put_net(fc->net_ns); + put_net(fc->net_ns); put_user_ns(fc->user_ns); put_cred(fc->cred); kfree(fc->subtype); @@ -172,6 +188,21 @@ int legacy_get_tree(struct fs_context *fc) return 0; } +/* + * Handle remount. + */ +int legacy_reconfigure(struct fs_context *fc) +{ + struct legacy_fs_context *ctx = fc->fs_private; + struct super_block *sb = fc->root->d_sb; + + if (!sb->s_op->remount_fs) + return 0; + + return sb->s_op->remount_fs(sb, &fc->sb_flags, + ctx ? ctx->legacy_data : NULL); +} + /* * Initialise a legacy context for a filesystem that doesn't support * fs_context. diff --git a/fs/internal.h b/fs/internal.h index 6af26d897034..016a5b8dd305 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -56,6 +56,7 @@ extern void __init chrdev_init(void); * fs_context.c */ extern int legacy_get_tree(struct fs_context *fc); +extern int legacy_reconfigure(struct fs_context *fc); extern int parse_monolithic_mount_data(struct fs_context *, void *); extern void fc_drop_locked(struct fs_context *); @@ -107,7 +108,7 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *); /* * super.c */ -extern int do_remount_sb(struct super_block *, int, void *, int); +extern int reconfigure_super(struct fs_context *); extern bool trylock_super(struct super_block *sb); extern struct super_block *user_get_super(dev_t); diff --git a/fs/namespace.c b/fs/namespace.c index 750500c6c33d..931228d8518a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1489,6 +1489,29 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) static void shrink_submounts(struct mount *mnt); +static int do_umount_root(struct super_block *sb) +{ + int ret = 0; + + down_write(&sb->s_umount); + if (!sb_rdonly(sb)) { + struct fs_context *fc; + + fc = fs_context_for_reconfigure(sb->s_root, SB_RDONLY, + SB_RDONLY); + if (IS_ERR(fc)) { + ret = PTR_ERR(fc); + } else { + ret = parse_monolithic_mount_data(fc, NULL); + if (!ret) + ret = reconfigure_super(fc); + put_fs_context(fc); + } + } + up_write(&sb->s_umount); + return ret; +} + static int do_umount(struct mount *mnt, int flags) { struct super_block *sb = mnt->mnt.mnt_sb; @@ -1554,11 +1577,7 @@ static int do_umount(struct mount *mnt, int flags) */ if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) return -EPERM; - down_write(&sb->s_umount); - if (!sb_rdonly(sb)) - retval = do_remount_sb(sb, SB_RDONLY, NULL, 0); - up_write(&sb->s_umount); - return retval; + return do_umount_root(sb); } namespace_lock(); @@ -2367,7 +2386,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, int err; struct super_block *sb = path->mnt->mnt_sb; struct mount *mnt = real_mount(path->mnt); - void *sec_opts = NULL; + struct fs_context *fc; if (!check_mnt(mnt)) return -EINVAL; @@ -2378,24 +2397,22 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, if (!can_change_locked_flags(mnt, mnt_flags)) return -EPERM; - if (data && !(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)) { - err = security_sb_eat_lsm_opts(data, &sec_opts); - if (err) - return err; - } - err = security_sb_remount(sb, sec_opts); - security_free_mnt_opts(&sec_opts); - if (err) - return err; + fc = fs_context_for_reconfigure(path->dentry, sb_flags, MS_RMT_MASK); + if (IS_ERR(fc)) + return PTR_ERR(fc); - down_write(&sb->s_umount); - err = -EPERM; - if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) { - err = do_remount_sb(sb, sb_flags, data, 0); - if (!err) - set_mount_attributes(mnt, mnt_flags); + err = parse_monolithic_mount_data(fc, data); + if (!err) { + down_write(&sb->s_umount); + err = -EPERM; + if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) { + err = reconfigure_super(fc); + if (!err) + set_mount_attributes(mnt, mnt_flags); + } + up_write(&sb->s_umount); } - up_write(&sb->s_umount); + put_fs_context(fc); return err; } diff --git a/fs/super.c b/fs/super.c index 11e2a6cb3baf..50553233dd15 100644 --- a/fs/super.c +++ b/fs/super.c @@ -836,28 +836,35 @@ rescan: } /** - * do_remount_sb - asks filesystem to change mount options. - * @sb: superblock in question - * @sb_flags: revised superblock flags - * @data: the rest of options - * @force: whether or not to force the change + * reconfigure_super - asks filesystem to change superblock parameters + * @fc: The superblock and configuration * - * Alters the mount options of a mounted file system. + * Alters the configuration parameters of a live superblock. */ -int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) +int reconfigure_super(struct fs_context *fc) { + struct super_block *sb = fc->root->d_sb; int retval; - int remount_ro; + bool remount_ro = false; + bool force = fc->sb_flags & SB_FORCE; + if (fc->sb_flags_mask & ~MS_RMT_MASK) + return -EINVAL; if (sb->s_writers.frozen != SB_UNFROZEN) return -EBUSY; + retval = security_sb_remount(sb, fc->security); + if (retval) + return retval; + + if (fc->sb_flags_mask & SB_RDONLY) { #ifdef CONFIG_BLOCK - if (!(sb_flags & SB_RDONLY) && bdev_read_only(sb->s_bdev)) - return -EACCES; + if (!(fc->sb_flags & SB_RDONLY) && bdev_read_only(sb->s_bdev)) + return -EACCES; #endif - remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb); + remount_ro = (fc->sb_flags & SB_RDONLY) && !sb_rdonly(sb); + } if (remount_ro) { if (!hlist_empty(&sb->s_pins)) { @@ -868,13 +875,14 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) return 0; if (sb->s_writers.frozen != SB_UNFROZEN) return -EBUSY; - remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb); + remount_ro = !sb_rdonly(sb); } } shrink_dcache_sb(sb); - /* If we are remounting RDONLY and current sb is read/write, - make sure there are no rw files opened */ + /* If we are reconfiguring to RDONLY and current sb is read/write, + * make sure there are no files open for writing. + */ if (remount_ro) { if (force) { sb->s_readonly_remount = 1; @@ -886,17 +894,17 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) } } - if (sb->s_op->remount_fs) { - retval = sb->s_op->remount_fs(sb, &sb_flags, data); - if (retval) { - if (!force) - goto cancel_readonly; - /* If forced remount, go ahead despite any errors */ - WARN(1, "forced remount of a %s fs returned %i\n", - sb->s_type->name, retval); - } + retval = legacy_reconfigure(fc); + if (retval) { + if (!force) + goto cancel_readonly; + /* If forced remount, go ahead despite any errors */ + WARN(1, "forced remount of a %s fs returned %i\n", + sb->s_type->name, retval); } - sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (sb_flags & MS_RMT_MASK); + + WRITE_ONCE(sb->s_flags, ((sb->s_flags & ~fc->sb_flags_mask) | + (fc->sb_flags & fc->sb_flags_mask))); /* Needs to be ordered wrt mnt_is_readonly() */ smp_wmb(); sb->s_readonly_remount = 0; @@ -923,10 +931,15 @@ static void do_emergency_remount_callback(struct super_block *sb) down_write(&sb->s_umount); if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) && !sb_rdonly(sb)) { - /* - * What lock protects sb->s_flags?? - */ - do_remount_sb(sb, SB_RDONLY, NULL, 1); + struct fs_context *fc; + + fc = fs_context_for_reconfigure(sb->s_root, + SB_RDONLY | SB_FORCE, SB_RDONLY); + if (!IS_ERR(fc)) { + if (parse_monolithic_mount_data(fc, NULL) == 0) + (void)reconfigure_super(fc); + put_fs_context(fc); + } } up_write(&sb->s_umount); } @@ -1213,6 +1226,31 @@ struct dentry *mount_nodev(struct file_system_type *fs_type, } EXPORT_SYMBOL(mount_nodev); +static int reconfigure_single(struct super_block *s, + int flags, void *data) +{ + struct fs_context *fc; + int ret; + + /* The caller really need to be passing fc down into mount_single(), + * then a chunk of this can be removed. [Bollocks -- AV] + * Better yet, reconfiguration shouldn't happen, but rather the second + * mount should be rejected if the parameters are not compatible. + */ + fc = fs_context_for_reconfigure(s->s_root, flags, MS_RMT_MASK); + if (IS_ERR(fc)) + return PTR_ERR(fc); + + ret = parse_monolithic_mount_data(fc, data); + if (ret < 0) + goto out; + + ret = reconfigure_super(fc); +out: + put_fs_context(fc); + return ret; +} + static int compare_single(struct super_block *s, void *p) { return 1; @@ -1230,13 +1268,14 @@ struct dentry *mount_single(struct file_system_type *fs_type, return ERR_CAST(s); if (!s->s_root) { error = fill_super(s, data, flags & SB_SILENT ? 1 : 0); - if (error) { - deactivate_locked_super(s); - return ERR_PTR(error); - } - s->s_flags |= SB_ACTIVE; + if (!error) + s->s_flags |= SB_ACTIVE; } else { - do_remount_sb(s, flags, data, 0); + error = reconfigure_single(s, flags, data); + } + if (unlikely(error)) { + deactivate_locked_super(s); + return ERR_PTR(error); } return dget(s->s_root); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 36fff12ab890..c65d02c5c512 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1337,6 +1337,7 @@ extern int send_sigurg(struct fown_struct *fown); /* These sb flags are internal to the kernel */ #define SB_SUBMOUNT (1<<26) +#define SB_FORCE (1<<27) #define SB_NOSEC (1<<28) #define SB_BORN (1<<29) #define SB_ACTIVE (1<<30) diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index 9805514444c9..98772f882a3e 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -25,6 +25,7 @@ struct user_namespace; enum fs_context_purpose { FS_CONTEXT_FOR_MOUNT, /* New superblock for explicit mount */ + FS_CONTEXT_FOR_RECONFIGURE, /* Superblock reconfiguration (remount) */ }; /* @@ -57,6 +58,9 @@ struct fs_context { */ extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, unsigned int sb_flags); +extern struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, + unsigned int sb_flags, + unsigned int sb_flags_mask); extern int vfs_get_tree(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc); From e1a91586d5da6f879b6dd385a2e7227bf1653570 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Dec 2018 16:25:31 -0500 Subject: [PATCH 0812/4277] fs_context flavour for submounts This is an eventual replacement for vfs_submount() uses. Unlike the "mount" and "remount" cases, the users of that thing are not in VFS - they are buried in various ->d_automount() instances and rather than converting them all at once we introduce the (thankfully small and simple) infrastructure here and deal with the prospective users in afs, nfs, etc. parts of the series. Here we just introduce a new constructor (fs_context_for_submount()) along with the corresponding enum constant to be put into fc->purpose for those. Signed-off-by: Al Viro --- fs/fs_context.c | 10 ++++++++++ include/linux/fs_context.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/fs/fs_context.c b/fs/fs_context.c index 5e2c3aba1dd8..2bd652b6e848 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -69,6 +69,9 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, case FS_CONTEXT_FOR_MOUNT: fc->user_ns = get_user_ns(fc->cred->user_ns); break; + case FS_CONTEXT_FOR_SUBMOUNT: + fc->user_ns = get_user_ns(reference->d_sb->s_user_ns); + break; case FS_CONTEXT_FOR_RECONFIGURE: /* We don't pin any namespaces as the superblock's * subscriptions cannot be changed at this point. @@ -106,6 +109,13 @@ struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, } EXPORT_SYMBOL(fs_context_for_reconfigure); +struct fs_context *fs_context_for_submount(struct file_system_type *type, + struct dentry *reference) +{ + return alloc_fs_context(type, reference, 0, 0, FS_CONTEXT_FOR_SUBMOUNT); +} +EXPORT_SYMBOL(fs_context_for_submount); + void fc_drop_locked(struct fs_context *fc) { struct super_block *sb = fc->root->d_sb; diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index 98772f882a3e..7feb018c7a9e 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -25,6 +25,7 @@ struct user_namespace; enum fs_context_purpose { FS_CONTEXT_FOR_MOUNT, /* New superblock for explicit mount */ + FS_CONTEXT_FOR_SUBMOUNT, /* New superblock for automatic submount */ FS_CONTEXT_FOR_RECONFIGURE, /* Superblock reconfiguration (remount) */ }; @@ -61,6 +62,8 @@ extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, extern struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, unsigned int sb_flags, unsigned int sb_flags_mask); +extern struct fs_context *fs_context_for_submount(struct file_system_type *fs_type, + struct dentry *reference); extern int vfs_get_tree(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc); From f3a09c92018a91ad0981146a4ac59414f814d801 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Dec 2018 18:55:56 -0500 Subject: [PATCH 0813/4277] introduce fs_context methods Signed-off-by: Al Viro --- fs/fs_context.c | 28 ++++++++++++++++++++++------ fs/internal.h | 2 -- fs/super.c | 36 ++++++++++++++++++++++++++++-------- include/linux/fs.h | 2 ++ include/linux/fs_context.h | 13 +++++++++++++ 5 files changed, 65 insertions(+), 16 deletions(-) diff --git a/fs/fs_context.c b/fs/fs_context.c index 2bd652b6e848..825d1b2c8807 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -51,6 +51,7 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, unsigned int sb_flags_mask, enum fs_context_purpose purpose) { + int (*init_fs_context)(struct fs_context *); struct fs_context *fc; int ret = -ENOMEM; @@ -81,7 +82,12 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type, break; } - ret = legacy_init_fs_context(fc); + /* TODO: Make all filesystems support this unconditionally */ + init_fs_context = fc->fs_type->init_fs_context; + if (!init_fs_context) + init_fs_context = legacy_init_fs_context; + + ret = init_fs_context(fc); if (ret < 0) goto err_fc; fc->need_free = true; @@ -141,8 +147,8 @@ void put_fs_context(struct fs_context *fc) deactivate_super(sb); } - if (fc->need_free) - legacy_fs_context_free(fc); + if (fc->need_free && fc->ops && fc->ops->free) + fc->ops->free(fc); security_free_mnt_opts(&fc->security); put_net(fc->net_ns); @@ -180,7 +186,7 @@ static int legacy_parse_monolithic(struct fs_context *fc, void *data) /* * Get a mountable root with the legacy mount command. */ -int legacy_get_tree(struct fs_context *fc) +static int legacy_get_tree(struct fs_context *fc) { struct legacy_fs_context *ctx = fc->fs_private; struct super_block *sb; @@ -201,7 +207,7 @@ int legacy_get_tree(struct fs_context *fc) /* * Handle remount. */ -int legacy_reconfigure(struct fs_context *fc) +static int legacy_reconfigure(struct fs_context *fc) { struct legacy_fs_context *ctx = fc->fs_private; struct super_block *sb = fc->root->d_sb; @@ -213,6 +219,13 @@ int legacy_reconfigure(struct fs_context *fc) ctx ? ctx->legacy_data : NULL); } +const struct fs_context_operations legacy_fs_context_ops = { + .free = legacy_fs_context_free, + .parse_monolithic = legacy_parse_monolithic, + .get_tree = legacy_get_tree, + .reconfigure = legacy_reconfigure, +}; + /* * Initialise a legacy context for a filesystem that doesn't support * fs_context. @@ -222,10 +235,13 @@ static int legacy_init_fs_context(struct fs_context *fc) fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL); if (!fc->fs_private) return -ENOMEM; + fc->ops = &legacy_fs_context_ops; return 0; } int parse_monolithic_mount_data(struct fs_context *fc, void *data) { - return legacy_parse_monolithic(fc, data); + int (*monolithic_mount_data)(struct fs_context *, void *); + monolithic_mount_data = fc->ops->parse_monolithic; + return monolithic_mount_data(fc, data); } diff --git a/fs/internal.h b/fs/internal.h index 016a5b8dd305..8f8d07cc433f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -55,8 +55,6 @@ extern void __init chrdev_init(void); /* * fs_context.c */ -extern int legacy_get_tree(struct fs_context *fc); -extern int legacy_reconfigure(struct fs_context *fc); extern int parse_monolithic_mount_data(struct fs_context *, void *); extern void fc_drop_locked(struct fs_context *); diff --git a/fs/super.c b/fs/super.c index 50553233dd15..76b3181c782d 100644 --- a/fs/super.c +++ b/fs/super.c @@ -894,13 +894,15 @@ int reconfigure_super(struct fs_context *fc) } } - retval = legacy_reconfigure(fc); - if (retval) { - if (!force) - goto cancel_readonly; - /* If forced remount, go ahead despite any errors */ - WARN(1, "forced remount of a %s fs returned %i\n", - sb->s_type->name, retval); + if (fc->ops->reconfigure) { + retval = fc->ops->reconfigure(fc); + if (retval) { + if (!force) + goto cancel_readonly; + /* If forced remount, go ahead despite any errors */ + WARN(1, "forced remount of a %s fs returned %i\n", + sb->s_type->name, retval); + } } WRITE_ONCE(sb->s_flags, ((sb->s_flags & ~fc->sb_flags_mask) | @@ -1294,10 +1296,28 @@ int vfs_get_tree(struct fs_context *fc) struct super_block *sb; int error; - error = legacy_get_tree(fc); + if (fc->fs_type->fs_flags & FS_REQUIRES_DEV && !fc->source) + return -ENOENT; + + if (fc->root) + return -EBUSY; + + /* Get the mountable root in fc->root, with a ref on the root and a ref + * on the superblock. + */ + error = fc->ops->get_tree(fc); if (error < 0) return error; + if (!fc->root) { + pr_err("Filesystem %s get_tree() didn't set fc->root\n", + fc->fs_type->name); + /* We don't know what the locking state of the superblock is - + * if there is a superblock. + */ + BUG(); + } + sb = fc->root->d_sb; WARN_ON(!sb->s_bdi); diff --git a/include/linux/fs.h b/include/linux/fs.h index c65d02c5c512..8d578a9e1e8c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -61,6 +61,7 @@ struct workqueue_struct; struct iov_iter; struct fscrypt_info; struct fscrypt_operations; +struct fs_context; extern void __init inode_init(void); extern void __init inode_init_early(void); @@ -2173,6 +2174,7 @@ struct file_system_type { #define FS_HAS_SUBTYPE 4 #define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */ #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ + int (*init_fs_context)(struct fs_context *); struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); void (*kill_sb) (struct super_block *); diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index 7feb018c7a9e..087c12954360 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -20,8 +20,13 @@ struct cred; struct dentry; struct file_operations; struct file_system_type; +struct mnt_namespace; struct net; +struct pid_namespace; +struct super_block; struct user_namespace; +struct vfsmount; +struct path; enum fs_context_purpose { FS_CONTEXT_FOR_MOUNT, /* New superblock for explicit mount */ @@ -39,6 +44,7 @@ enum fs_context_purpose { * See Documentation/filesystems/mounting.txt */ struct fs_context { + const struct fs_context_operations *ops; struct file_system_type *fs_type; void *fs_private; /* The filesystem's context */ struct dentry *root; /* The root and superblock */ @@ -54,6 +60,13 @@ struct fs_context { bool need_free:1; /* Need to call ops->free() */ }; +struct fs_context_operations { + void (*free)(struct fs_context *fc); + int (*parse_monolithic)(struct fs_context *fc, void *data); + int (*get_tree)(struct fs_context *fc); + int (*reconfigure)(struct fs_context *fc); +}; + /* * fs_context manipulation functions. */ From c6b82263f9c6e745eb4c5dfc2578d147c4cd7604 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 1 Nov 2018 23:07:23 +0000 Subject: [PATCH 0814/4277] vfs: Introduce logging functions Introduce a set of logging functions through which informational messages, warnings and error messages incurred by the mount procedure can be logged and, in a future patch, passed to userspace instead by way of the filesystem configuration context file descriptor. There are four functions: (1) infof(const char *fmt, ...); Logs an informational message. (2) warnf(const char *fmt, ...); Logs a warning message. (3) errorf(const char *fmt, ...); Logs an error message. (4) invalf(const char *fmt, ...); As errof(), but returns -EINVAL so can be used on a return statement. Signed-off-by: David Howells Signed-off-by: Al Viro --- include/linux/fs_context.h | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index 087c12954360..d208cc40b868 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -81,4 +81,46 @@ extern struct fs_context *fs_context_for_submount(struct file_system_type *fs_ty extern int vfs_get_tree(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc); +#define logfc(FC, FMT, ...) pr_notice(FMT, ## __VA_ARGS__) + +/** + * infof - Store supplementary informational message + * @fc: The context in which to log the informational message + * @fmt: The format string + * + * Store the supplementary informational message for the process if the process + * has enabled the facility. + */ +#define infof(fc, fmt, ...) ({ logfc(fc, fmt, ## __VA_ARGS__); }) + +/** + * warnf - Store supplementary warning message + * @fc: The context in which to log the error message + * @fmt: The format string + * + * Store the supplementary warning message for the process if the process has + * enabled the facility. + */ +#define warnf(fc, fmt, ...) ({ logfc(fc, fmt, ## __VA_ARGS__); }) + +/** + * errorf - Store supplementary error message + * @fc: The context in which to log the error message + * @fmt: The format string + * + * Store the supplementary error message for the process if the process has + * enabled the facility. + */ +#define errorf(fc, fmt, ...) ({ logfc(fc, fmt, ## __VA_ARGS__); }) + +/** + * invalf - Store supplementary invalid argument error message + * @fc: The context in which to log the error message + * @fmt: The format string + * + * Store the supplementary error message for the process if the process has + * enabled the facility and return -EINVAL. + */ +#define invalf(fc, fmt, ...) ({ errorf(fc, fmt, ## __VA_ARGS__); -EINVAL; }) + #endif /* _LINUX_FS_CONTEXT_H */ From 459cc69fa4c17caf21de596693d8a07170820a58 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:49:11 +0200 Subject: [PATCH 0815/4277] RDMA: Provide safe ib_alloc_device() function All callers to ib_alloc_device() provide a larger size than struct ib_device and rely on the fact that struct ib_device is embedded in their driver specific structure as the first member. Provide a safer variant of ib_alloc_device() that checks and enforces this approach to make sure the drivers are using it right. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 6 +++--- drivers/infiniband/hw/bnxt_re/main.c | 2 +- drivers/infiniband/hw/cxgb3/iwch.c | 2 +- drivers/infiniband/hw/cxgb4/device.c | 2 +- drivers/infiniband/hw/hns/hns_roce_hw_v1.c | 2 +- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- drivers/infiniband/hw/i40iw/i40iw_verbs.c | 2 +- drivers/infiniband/hw/mlx4/main.c | 2 +- drivers/infiniband/hw/mlx5/ib_rep.c | 2 +- drivers/infiniband/hw/mlx5/main.c | 2 +- drivers/infiniband/hw/mthca/mthca_main.c | 2 +- drivers/infiniband/hw/nes/nes_verbs.c | 2 +- drivers/infiniband/hw/ocrdma/ocrdma_main.c | 2 +- drivers/infiniband/hw/qedr/main.c | 2 +- drivers/infiniband/hw/usnic/usnic_ib_main.c | 2 +- drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c | 2 +- drivers/infiniband/sw/rdmavt/vt.c | 2 +- drivers/infiniband/sw/rxe/rxe_net.c | 2 +- include/rdma/ib_verbs.h | 8 +++++++- 19 files changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 200431c540f2..b511cfa00bdb 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -268,7 +268,7 @@ static struct class ib_class = { }; /** - * ib_alloc_device - allocate an IB device struct + * _ib_alloc_device - allocate an IB device struct * @size:size of structure to allocate * * Low-level drivers should use ib_alloc_device() to allocate &struct @@ -277,7 +277,7 @@ static struct class ib_class = { * ib_dealloc_device() must be used to free structures allocated with * ib_alloc_device(). */ -struct ib_device *ib_alloc_device(size_t size) +struct ib_device *_ib_alloc_device(size_t size) { struct ib_device *device; @@ -303,7 +303,7 @@ struct ib_device *ib_alloc_device(size_t size) return device; } -EXPORT_SYMBOL(ib_alloc_device); +EXPORT_SYMBOL(_ib_alloc_device); /** * ib_dealloc_device - free an IB device struct diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 16eecfa5882c..08777506d0b1 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -688,7 +688,7 @@ static struct bnxt_re_dev *bnxt_re_dev_add(struct net_device *netdev, struct bnxt_re_dev *rdev; /* Allocate bnxt_re_dev instance here */ - rdev = (struct bnxt_re_dev *)ib_alloc_device(sizeof(*rdev)); + rdev = ib_alloc_device(bnxt_re_dev, ibdev); if (!rdev) { dev_err(NULL, "%s: bnxt_re_dev allocation failure!", ROCE_DRV_MODULE_NAME); diff --git a/drivers/infiniband/hw/cxgb3/iwch.c b/drivers/infiniband/hw/cxgb3/iwch.c index 591de319c178..fb03bc492ef7 100644 --- a/drivers/infiniband/hw/cxgb3/iwch.c +++ b/drivers/infiniband/hw/cxgb3/iwch.c @@ -146,7 +146,7 @@ static void open_rnic_dev(struct t3cdev *tdev) pr_debug("%s t3cdev %p\n", __func__, tdev); pr_info_once("Chelsio T3 RDMA Driver - version %s\n", DRV_VERSION); - rnicp = (struct iwch_dev *)ib_alloc_device(sizeof(*rnicp)); + rnicp = ib_alloc_device(iwch_dev, ibdev); if (!rnicp) { pr_err("Cannot allocate ib device\n"); return; diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 9c10fff6dcfb..4b4e2464b705 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -966,7 +966,7 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop) pr_info("%s: On-Chip Queues not supported on this device\n", pci_name(infop->pdev)); - devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp)); + devp = ib_alloc_device(c4iw_dev, ibdev); if (!devp) { pr_err("Cannot allocate ib device\n"); return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c index b74c742b000c..fa08c22aad66 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c @@ -5002,7 +5002,7 @@ static int hns_roce_probe(struct platform_device *pdev) struct hns_roce_dev *hr_dev; struct device *dev = &pdev->dev; - hr_dev = (struct hns_roce_dev *)ib_alloc_device(sizeof(*hr_dev)); + hr_dev = ib_alloc_device(hns_roce_dev, ib_dev); if (!hr_dev) return -ENOMEM; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 5c483b437bdd..48a5d6548cd4 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -6059,7 +6059,7 @@ static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) struct hns_roce_dev *hr_dev; int ret; - hr_dev = (struct hns_roce_dev *)ib_alloc_device(sizeof(*hr_dev)); + hr_dev = ib_alloc_device(hns_roce_dev, ib_dev); if (!hr_dev) return -ENOMEM; diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 12b31a8440be..d4ab46dd9e6c 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -2762,7 +2762,7 @@ static struct i40iw_ib_device *i40iw_init_rdma_device(struct i40iw_device *iwdev struct net_device *netdev = iwdev->netdev; struct pci_dev *pcidev = (struct pci_dev *)iwdev->hw.dev_context; - iwibdev = (struct i40iw_ib_device *)ib_alloc_device(sizeof(*iwibdev)); + iwibdev = ib_alloc_device(i40iw_ib_device, ibdev); if (!iwibdev) { i40iw_pr_err("iwdev == NULL\n"); return NULL; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index dc2ffd293a11..d66002a31000 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2635,7 +2635,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) if (num_ports == 0) return NULL; - ibdev = (struct mlx4_ib_dev *) ib_alloc_device(sizeof *ibdev); + ibdev = ib_alloc_device(mlx4_ib_dev, ib_dev); if (!ibdev) { dev_err(&dev->persist->pdev->dev, "Device struct alloc failed\n"); diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c index 46a9ddc8ca56..6d7b8bad4b61 100644 --- a/drivers/infiniband/hw/mlx5/ib_rep.c +++ b/drivers/infiniband/hw/mlx5/ib_rep.c @@ -70,7 +70,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) { struct mlx5_ib_dev *ibdev; - ibdev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*ibdev)); + ibdev = ib_alloc_device(mlx5_ib_dev, ib_dev); if (!ibdev) return -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 96bf1b2f9dd7..8161acda64e6 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -6508,7 +6508,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) if (mlx5_core_is_mp_slave(mdev) && ll == IB_LINK_LAYER_ETHERNET) return mlx5_ib_add_slave_port(mdev); - dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev)); + dev = ib_alloc_device(mlx5_ib_dev, ib_dev); if (!dev) return NULL; diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index 92c49bff22bc..fe9654a7af71 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -961,7 +961,7 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type) /* We can handle large RDMA requests, so allow larger segments. */ dma_set_max_seg_size(&pdev->dev, 1024 * 1024 * 1024); - mdev = (struct mthca_dev *) ib_alloc_device(sizeof *mdev); + mdev = ib_alloc_device(mthca_dev, ib_dev); if (!mdev) { dev_err(&pdev->dev, "Device struct alloc failed, " "aborting.\n"); diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 034156f7e9ed..6eb991d40035 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -3669,7 +3669,7 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev) struct nes_vnic *nesvnic = netdev_priv(netdev); struct nes_device *nesdev = nesvnic->nesdev; - nesibdev = (struct nes_ib_device *)ib_alloc_device(sizeof(struct nes_ib_device)); + nesibdev = ib_alloc_device(nes_ib_device, ibdev); if (nesibdev == NULL) { return NULL; } diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index b0491b9ecfe4..88970a6bb555 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -297,7 +297,7 @@ static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info) u8 lstate = 0; struct ocrdma_dev *dev; - dev = (struct ocrdma_dev *)ib_alloc_device(sizeof(struct ocrdma_dev)); + dev = ib_alloc_device(ocrdma_dev, ibdev); if (!dev) { pr_err("Unable to allocate ib device\n"); return NULL; diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index f85e72b65a10..878e9e23652b 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -853,7 +853,7 @@ static struct qedr_dev *qedr_add(struct qed_dev *cdev, struct pci_dev *pdev, struct qedr_dev *dev; int rc = 0; - dev = (struct qedr_dev *)ib_alloc_device(sizeof(*dev)); + dev = ib_alloc_device(qedr_dev, ibdev); if (!dev) { pr_err("Unable to allocate ib device\n"); return NULL; diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index 3201dd1899c7..0c0a288cb585 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -372,7 +372,7 @@ static void *usnic_ib_device_add(struct pci_dev *dev) usnic_dbg("\n"); netdev = pci_get_drvdata(dev); - us_ibdev = (struct usnic_ib_dev *)ib_alloc_device(sizeof(*us_ibdev)); + us_ibdev = ib_alloc_device(usnic_ib_dev, ib_dev); if (!us_ibdev) { usnic_err("Device %s context alloc failed\n", netdev_name(pci_get_drvdata(dev))); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index a5f02276d903..e582beaf9430 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -795,7 +795,7 @@ static int pvrdma_pci_probe(struct pci_dev *pdev, dev_dbg(&pdev->dev, "initializing driver %s\n", pci_name(pdev)); /* Allocate zero-out device */ - dev = (struct pvrdma_dev *)ib_alloc_device(sizeof(*dev)); + dev = ib_alloc_device(pvrdma_dev, ib_dev); if (!dev) { dev_err(&pdev->dev, "failed to allocate IB device\n"); return -ENOMEM; diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index 7de7389d0235..b3f0c5578925 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -91,7 +91,7 @@ struct rvt_dev_info *rvt_alloc_device(size_t size, int nports) { struct rvt_dev_info *rdi; - rdi = (struct rvt_dev_info *)ib_alloc_device(size); + rdi = container_of(_ib_alloc_device(size), struct rvt_dev_info, ibdev); if (!rdi) return rdi; diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 8fd03ae20efc..19f3c69916b1 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -555,7 +555,7 @@ struct rxe_dev *rxe_net_add(struct net_device *ndev) int err; struct rxe_dev *rxe = NULL; - rxe = (struct rxe_dev *)ib_alloc_device(sizeof(*rxe)); + rxe = ib_alloc_device(rxe_dev, ib_dev); if (!rxe) return NULL; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 71ea144ec823..a1a1e710642c 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2621,7 +2621,13 @@ struct ib_client { struct list_head list; }; -struct ib_device *ib_alloc_device(size_t size); +struct ib_device *_ib_alloc_device(size_t size); +#define ib_alloc_device(drv_struct, member) \ + container_of(_ib_alloc_device(sizeof(struct drv_struct) + \ + BUILD_BUG_ON_ZERO(offsetof( \ + struct drv_struct, member))), \ + struct drv_struct, member) + void ib_dealloc_device(struct ib_device *device); void ib_get_device_fw_str(struct ib_device *device, char *str); From 4760a896be88e836bedc902932fb16f60499a25e Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 30 Jan 2019 16:39:30 -0800 Subject: [PATCH 0816/4277] remoteproc: q6v5-mss: Vote for rpmh power domains With rpmh ARC resources being modelled as power domains with performance state, we need to proxy vote on these for SDM845. Add support to vote on multiple of them, now that genpd supports associating mutliple power domains to a device. Tested-by: Sibi Sankar Reviewed-by: Sibi Sankar Signed-off-by: Rajendra Nayak [bjorn: Drop device link, improve error handling, name things "proxy"] Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 119 +++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 5 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 7c344771c335..1a428dad5f11 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -131,6 +133,7 @@ struct rproc_hexagon_res { char **proxy_clk_names; char **reset_clk_names; char **active_clk_names; + char **proxy_pd_names; int version; bool need_mem_protection; bool has_alt_reset; @@ -156,9 +159,11 @@ struct q6v5 { struct clk *active_clks[8]; struct clk *reset_clks[4]; struct clk *proxy_clks[4]; + struct device *proxy_pds[3]; int active_clk_count; int reset_clk_count; int proxy_clk_count; + int proxy_pd_count; struct reg_info active_regs[1]; struct reg_info proxy_regs[3]; @@ -322,6 +327,41 @@ static void q6v5_clk_disable(struct device *dev, clk_disable_unprepare(clks[i]); } +static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int ret; + int i; + + for (i = 0; i < pd_count; i++) { + dev_pm_genpd_set_performance_state(pds[i], INT_MAX); + ret = pm_runtime_get_sync(pds[i]); + if (ret < 0) + goto unroll_pd_votes; + } + + return 0; + +unroll_pd_votes: + for (i--; i >= 0; i--) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } + + return ret; +}; + +static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int i; + + for (i = 0; i < pd_count; i++) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } +} + static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, bool remote_owner, phys_addr_t addr, size_t size) @@ -691,11 +731,17 @@ static int q6v5_mba_load(struct q6v5 *qproc) qcom_q6v5_prepare(&qproc->q6v5); + ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable proxy power domains\n"); + goto disable_irqs; + } + ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); if (ret) { dev_err(qproc->dev, "failed to enable proxy supplies\n"); - goto disable_irqs; + goto disable_proxy_pds; } ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, @@ -792,6 +838,8 @@ disable_proxy_clk: disable_proxy_reg: q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); +disable_proxy_pds: + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); disable_irqs: qcom_q6v5_unprepare(&qproc->q6v5); @@ -842,6 +890,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) ret = qcom_q6v5_unprepare(&qproc->q6v5); if (ret) { + q6v5_pds_disable(qproc, qproc->proxy_pds, + qproc->proxy_pd_count); q6v5_clk_disable(qproc->dev, qproc->proxy_clks, qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, @@ -1135,6 +1185,7 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5) qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); } static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) @@ -1195,6 +1246,45 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks, return i; } +static int q6v5_pds_attach(struct device *dev, struct device **devs, + char **pd_names) +{ + size_t num_pds = 0; + int ret; + int i; + + if (!pd_names) + return 0; + + while (pd_names[num_pds]) + num_pds++; + + for (i = 0; i < num_pds; i++) { + devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]); + if (IS_ERR(devs[i])) { + ret = PTR_ERR(devs[i]); + goto unroll_attach; + } + } + + return num_pds; + +unroll_attach: + for (i--; i >= 0; i--) + dev_pm_domain_detach(devs[i], false); + + return ret; +}; + +static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int i; + + for (i = 0; i < pd_count; i++) + dev_pm_domain_detach(pds[i], false); +} + static int q6v5_init_reset(struct q6v5 *qproc) { qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev, @@ -1351,10 +1441,18 @@ static int q6v5_probe(struct platform_device *pdev) } qproc->active_reg_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, + desc->proxy_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init power domains\n"); + goto free_rproc; + } + qproc->proxy_pd_count = ret; + qproc->has_alt_reset = desc->has_alt_reset; ret = q6v5_init_reset(qproc); if (ret) - goto free_rproc; + goto detach_proxy_pds; qproc->version = desc->version; qproc->need_mem_protection = desc->need_mem_protection; @@ -1362,7 +1460,7 @@ static int q6v5_probe(struct platform_device *pdev) ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, qcom_msa_handover); if (ret) - goto free_rproc; + goto detach_proxy_pds; qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); @@ -1372,15 +1470,17 @@ static int q6v5_probe(struct platform_device *pdev) qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); if (IS_ERR(qproc->sysmon)) { ret = PTR_ERR(qproc->sysmon); - goto free_rproc; + goto detach_proxy_pds; } ret = rproc_add(rproc); if (ret) - goto free_rproc; + goto detach_proxy_pds; return 0; +detach_proxy_pds: + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); free_rproc: rproc_free(rproc); @@ -1397,6 +1497,9 @@ static int q6v5_remove(struct platform_device *pdev) qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); + + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + rproc_free(qproc->rproc); return 0; @@ -1421,6 +1524,12 @@ static const struct rproc_hexagon_res sdm845_mss = { "mnoc_axi", NULL }, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + "mss", + NULL + }, .need_mem_protection = true, .has_alt_reset = true, .version = MSS_SDM845, From deb9bb83e7ae742fcd37d3ab90e8b6ab5fff822a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 30 Jan 2019 16:39:31 -0800 Subject: [PATCH 0817/4277] remoteproc: q6v5-mss: Active powerdomain for SDM845 The SDM845 MSS needs the load_state powerdomain voted for during the duration of the MSS being powered on, to let the AOSS know that it may not perform certain power save measures. So vote for this. Tested-by: Sibi Sankar Reviewed-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 31 ++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 1a428dad5f11..eacdf10fcfaf 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -133,6 +133,7 @@ struct rproc_hexagon_res { char **proxy_clk_names; char **reset_clk_names; char **active_clk_names; + char **active_pd_names; char **proxy_pd_names; int version; bool need_mem_protection; @@ -159,10 +160,12 @@ struct q6v5 { struct clk *active_clks[8]; struct clk *reset_clks[4]; struct clk *proxy_clks[4]; + struct device *active_pds[1]; struct device *proxy_pds[3]; int active_clk_count; int reset_clk_count; int proxy_clk_count; + int active_pd_count; int proxy_pd_count; struct reg_info active_regs[1]; @@ -731,10 +734,16 @@ static int q6v5_mba_load(struct q6v5 *qproc) qcom_q6v5_prepare(&qproc->q6v5); + ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable active power domains\n"); + goto disable_irqs; + } + ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); if (ret < 0) { dev_err(qproc->dev, "failed to enable proxy power domains\n"); - goto disable_irqs; + goto disable_active_pds; } ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, @@ -840,6 +849,8 @@ disable_proxy_reg: qproc->proxy_reg_count); disable_proxy_pds: q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +disable_active_pds: + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); disable_irqs: qcom_q6v5_unprepare(&qproc->q6v5); @@ -879,6 +890,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) qproc->active_clk_count); q6v5_regulator_disable(qproc, qproc->active_regs, qproc->active_reg_count); + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); /* In case of failure or coredump scenario where reclaiming MBA memory * could not happen reclaim it here. @@ -1441,11 +1453,19 @@ static int q6v5_probe(struct platform_device *pdev) } qproc->active_reg_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds, + desc->active_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to attach active power domains\n"); + goto free_rproc; + } + qproc->active_pd_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, desc->proxy_pd_names); if (ret < 0) { dev_err(&pdev->dev, "Failed to init power domains\n"); - goto free_rproc; + goto detach_active_pds; } qproc->proxy_pd_count = ret; @@ -1481,6 +1501,8 @@ static int q6v5_probe(struct platform_device *pdev) detach_proxy_pds: q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +detach_active_pds: + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); free_rproc: rproc_free(rproc); @@ -1498,6 +1520,7 @@ static int q6v5_remove(struct platform_device *pdev) qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); rproc_free(qproc->rproc); @@ -1524,6 +1547,10 @@ static const struct rproc_hexagon_res sdm845_mss = { "mnoc_axi", NULL }, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "cx", "mx", From 6780c4fa9d6e091b2f206ac429a40e2e8d2e45f3 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Tue, 22 Jan 2019 10:08:22 +0200 Subject: [PATCH 0818/4277] RDMA: Add indication for in kernel API support to IB device Drivers that do not provide kernel verbs support should not be used by ib kernel clients at all. In case a device does not implement all mandatory verbs for kverbs usage mark it as a non kverbs provider and prevent its usage for all clients except for uverbs. The device is marked as a non kverbs provider using the 'kverbs_provider' flag which should only be set by the core code. The clients can choose whether kverbs are requested for its usage using the 'no_kverbs_req' flag which is currently set for uverbs only. This patch allows drivers to remove mandatory verbs stubs and simply set the callbacks to NULL. The IB device will be registered as a non-kverbs provider. Note that verbs that are required for the device registration process must be implemented. Signed-off-by: Gal Pressman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 10 ++++++---- drivers/infiniband/core/uverbs_main.c | 1 + include/rdma/ib_verbs.h | 5 +++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index b511cfa00bdb..9d2e108235e9 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -121,13 +121,12 @@ static int ib_device_check_mandatory(struct ib_device *device) }; int i; + device->kverbs_provider = true; for (i = 0; i < ARRAY_SIZE(mandatory_table); ++i) { if (!*(void **) ((void *) &device->ops + mandatory_table[i].offset)) { - dev_warn(&device->dev, - "Device is missing mandatory function %s\n", - mandatory_table[i].name); - return -EINVAL; + device->kverbs_provider = false; + break; } } @@ -325,6 +324,9 @@ static int add_client_context(struct ib_device *device, struct ib_client *client { struct ib_client_data *context; + if (!device->kverbs_provider && !client->no_kverbs_req) + return -EOPNOTSUPP; + context = kmalloc(sizeof(*context), GFP_KERNEL); if (!context) return -ENOMEM; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 996f167d1436..d628747e058c 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -1151,6 +1151,7 @@ static const struct file_operations uverbs_mmap_fops = { static struct ib_client uverbs_client = { .name = "uverbs", + .no_kverbs_req = true, .add = ib_uverbs_add_one, .remove = ib_uverbs_remove_one }; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index a1a1e710642c..4183a03b46b5 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2565,6 +2565,8 @@ struct ib_device { __be64 node_guid; u32 local_dma_lkey; u16 is_switch:1; + /* Indicates kernel verbs support, should not be used in drivers */ + u16 kverbs_provider:1; u8 node_type; u8 phys_port_cnt; struct ib_device_attr attrs; @@ -2619,6 +2621,9 @@ struct ib_client { const struct sockaddr *addr, void *client_data); struct list_head list; + + /* kverbs are not required by the client */ + u8 no_kverbs_req:1; }; struct ib_device *_ib_alloc_device(size_t size); From cfc30ad3d098b63ac08d2d2d61ef9aeff659c57a Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Tue, 22 Jan 2019 10:08:23 +0200 Subject: [PATCH 0819/4277] IB/usnic: Remove stub functions Lack of mandatory verbs no longer fail device registration, the device will be marked as a non-kverbs provider. Signed-off-by: Gal Pressman Tested-by: Parvi Kaustubhi Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/usnic/usnic_ib_main.c | 7 --- drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 53 -------------------- drivers/infiniband/hw/usnic/usnic_ib_verbs.h | 15 ------ 3 files changed, 75 deletions(-) diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index 0c0a288cb585..c4a4cfe4b567 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -333,32 +333,25 @@ static void usnic_get_dev_fw_str(struct ib_device *device, char *str) static const struct ib_device_ops usnic_dev_ops = { .alloc_pd = usnic_ib_alloc_pd, .alloc_ucontext = usnic_ib_alloc_ucontext, - .create_ah = usnic_ib_create_ah, .create_cq = usnic_ib_create_cq, .create_qp = usnic_ib_create_qp, .dealloc_pd = usnic_ib_dealloc_pd, .dealloc_ucontext = usnic_ib_dealloc_ucontext, .dereg_mr = usnic_ib_dereg_mr, - .destroy_ah = usnic_ib_destroy_ah, .destroy_cq = usnic_ib_destroy_cq, .destroy_qp = usnic_ib_destroy_qp, .get_dev_fw_str = usnic_get_dev_fw_str, - .get_dma_mr = usnic_ib_get_dma_mr, .get_link_layer = usnic_ib_port_link_layer, .get_netdev = usnic_get_netdev, .get_port_immutable = usnic_port_immutable, .mmap = usnic_ib_mmap, .modify_qp = usnic_ib_modify_qp, - .poll_cq = usnic_ib_poll_cq, - .post_recv = usnic_ib_post_recv, - .post_send = usnic_ib_post_send, .query_device = usnic_ib_query_device, .query_gid = usnic_ib_query_gid, .query_pkey = usnic_ib_query_pkey, .query_port = usnic_ib_query_port, .query_qp = usnic_ib_query_qp, .reg_user_mr = usnic_ib_reg_mr, - .req_notify_cq = usnic_ib_req_notify_cq, }; /* Start of PF discovery section */ diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 432e6f6599fa..9dea18106247 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -760,57 +760,4 @@ int usnic_ib_mmap(struct ib_ucontext *context, return -EINVAL; } -/* In ib callbacks section - Start of stub funcs */ -struct ib_ah *usnic_ib_create_ah(struct ib_pd *pd, - struct rdma_ah_attr *ah_attr, - u32 flags, - struct ib_udata *udata) - -{ - usnic_dbg("\n"); - return ERR_PTR(-EPERM); -} - -int usnic_ib_destroy_ah(struct ib_ah *ah, u32 flags) -{ - usnic_dbg("\n"); - return -EINVAL; -} - -int usnic_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, - const struct ib_send_wr **bad_wr) -{ - usnic_dbg("\n"); - return -EINVAL; -} - -int usnic_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr, - const struct ib_recv_wr **bad_wr) -{ - usnic_dbg("\n"); - return -EINVAL; -} - -int usnic_ib_poll_cq(struct ib_cq *ibcq, int num_entries, - struct ib_wc *wc) -{ - usnic_dbg("\n"); - return -EINVAL; -} - -int usnic_ib_req_notify_cq(struct ib_cq *cq, - enum ib_cq_notify_flags flags) -{ - usnic_dbg("\n"); - return -EINVAL; -} - -struct ib_mr *usnic_ib_get_dma_mr(struct ib_pd *pd, int acc) -{ - usnic_dbg("\n"); - return ERR_PTR(-ENOMEM); -} - - -/* In ib callbacks section - End of stub funcs */ /* End of ib callbacks section */ diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h index e33144261b9a..99a6d81c2bcd 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h @@ -75,19 +75,4 @@ struct ib_ucontext *usnic_ib_alloc_ucontext(struct ib_device *ibdev, int usnic_ib_dealloc_ucontext(struct ib_ucontext *ibcontext); int usnic_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma); -struct ib_ah *usnic_ib_create_ah(struct ib_pd *pd, - struct rdma_ah_attr *ah_attr, - u32 flags, - struct ib_udata *udata); - -int usnic_ib_destroy_ah(struct ib_ah *ah, u32 flags); -int usnic_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, - const struct ib_send_wr **bad_wr); -int usnic_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr, - const struct ib_recv_wr **bad_wr); -int usnic_ib_poll_cq(struct ib_cq *ibcq, int num_entries, - struct ib_wc *wc); -int usnic_ib_req_notify_cq(struct ib_cq *cq, - enum ib_cq_notify_flags flags); -struct ib_mr *usnic_ib_get_dma_mr(struct ib_pd *pd, int acc); #endif /* !USNIC_IB_VERBS_H */ From cf34e1fe52540842aeae3c5cdaaedae97ee788dd Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Sun, 27 Jan 2019 20:35:50 +0200 Subject: [PATCH 0820/4277] IB/mlx5: Consider vlan of lower netdev for macvlan GID entries When a given netdev of the GID entry is macvlan netdevice, and if the lower netdevice is vlan device, GID entry for macvlan based IP address needs to inherit the vlan of the lower netdevice. Therefore, attempt to find out if the lower device exist and if so, if it is vlan device and setup the vlan tag correctly. Signed-off-by: Parav Pandit Reviewed-by: Mark Bloch Reviewed-by: Yuval Avnery Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 40 ++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 8161acda64e6..76d6c2557d0c 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -476,24 +476,51 @@ out: return err; } +struct mlx5_ib_vlan_info { + u16 vlan_id; + bool vlan; +}; + +static int get_lower_dev_vlan(struct net_device *lower_dev, void *data) +{ + struct mlx5_ib_vlan_info *vlan_info = data; + + if (is_vlan_dev(lower_dev)) { + vlan_info->vlan = true; + vlan_info->vlan_id = vlan_dev_vlan_id(lower_dev); + } + /* We are interested only in first level vlan device, so + * always return 1 to stop iterating over next level devices. + */ + return 1; +} + static int set_roce_addr(struct mlx5_ib_dev *dev, u8 port_num, unsigned int index, const union ib_gid *gid, const struct ib_gid_attr *attr) { enum ib_gid_type gid_type = IB_GID_TYPE_IB; + struct mlx5_ib_vlan_info vlan_info = { }; u8 roce_version = 0; u8 roce_l3_type = 0; - bool vlan = false; u8 mac[ETH_ALEN]; - u16 vlan_id = 0; if (gid) { gid_type = attr->gid_type; ether_addr_copy(mac, attr->ndev->dev_addr); if (is_vlan_dev(attr->ndev)) { - vlan = true; - vlan_id = vlan_dev_vlan_id(attr->ndev); + vlan_info.vlan = true; + vlan_info.vlan_id = vlan_dev_vlan_id(attr->ndev); + } else { + /* If the netdev is upper device and if it's lower + * lower device is vlan device, consider vlan id of + * the lower vlan device for this gid entry. + */ + rcu_read_lock(); + netdev_walk_all_lower_dev_rcu(attr->ndev, + get_lower_dev_vlan, &vlan_info); + rcu_read_unlock(); } } @@ -514,8 +541,9 @@ static int set_roce_addr(struct mlx5_ib_dev *dev, u8 port_num, } return mlx5_core_roce_gid_set(dev->mdev, index, roce_version, - roce_l3_type, gid->raw, mac, vlan, - vlan_id, port_num); + roce_l3_type, gid->raw, mac, + vlan_info.vlan, vlan_info.vlan_id, + port_num); } static int mlx5_ib_add_gid(const struct ib_gid_attr *attr, From f732e7135b8b256184bcf5ec89bec1aaa8f71943 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:48:53 +0200 Subject: [PATCH 0821/4277] RDMA/nldev: Dynamically generate restrack dumpit callbacks There is no need to manually write same callbacks, automatically generate them using C-macro language. This macro is going to be extended to generate doit callbacks too, so use general name for this macro. Signed-off-by: Leon Romanovsky Reviewed-by: Yuval Shaia Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/nldev.c | 39 ++++++++++----------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index e600fc23ae62..68b2d7a871cb 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -1077,35 +1077,18 @@ err_index: return ret; } -static int nldev_res_get_qp_dumpit(struct sk_buff *skb, - struct netlink_callback *cb) -{ - return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_QP); -} +#define RES_GET_FUNCS(name, type) \ + static int nldev_res_get_##name##_dumpit(struct sk_buff *skb, \ + struct netlink_callback *cb) \ + { \ + return res_get_common_dumpit(skb, cb, type); \ + } -static int nldev_res_get_cm_id_dumpit(struct sk_buff *skb, - struct netlink_callback *cb) -{ - return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_CM_ID); -} - -static int nldev_res_get_cq_dumpit(struct sk_buff *skb, - struct netlink_callback *cb) -{ - return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_CQ); -} - -static int nldev_res_get_mr_dumpit(struct sk_buff *skb, - struct netlink_callback *cb) -{ - return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_MR); -} - -static int nldev_res_get_pd_dumpit(struct sk_buff *skb, - struct netlink_callback *cb) -{ - return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_PD); -} +RES_GET_FUNCS(qp, RDMA_RESTRACK_QP); +RES_GET_FUNCS(cm_id, RDMA_RESTRACK_CM_ID); +RES_GET_FUNCS(cq, RDMA_RESTRACK_CQ); +RES_GET_FUNCS(pd, RDMA_RESTRACK_PD); +RES_GET_FUNCS(mr, RDMA_RESTRACK_MR); static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { [RDMA_NLDEV_CMD_GET] = { From 8be565e65fa9f4b1dee96e19bce3ca34e68cc8cb Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:48:54 +0200 Subject: [PATCH 0822/4277] RDMA/nldev: Factor out the PID namespace check The PID namespace is going to be used in the .doit callback, so generalize its implementation. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/nldev.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 68b2d7a871cb..e54a79d3f458 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -943,6 +943,17 @@ static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = { }, }; +static bool is_visible_in_pid_ns(struct rdma_restrack_entry *res) +{ + /* + * 1. Kern resources should be visible in init name space only + * 2. Present only resources visible in the current namespace + */ + if (rdma_is_kernel_res(res)) + return task_active_pid_ns(current) == &init_pid_ns; + return task_active_pid_ns(current) == task_active_pid_ns(res->task); +} + static int res_get_common_dumpit(struct sk_buff *skb, struct netlink_callback *cb, enum rdma_restrack_type res_type) @@ -1007,16 +1018,7 @@ static int res_get_common_dumpit(struct sk_buff *skb, if (idx < start) goto next; - if ((rdma_is_kernel_res(res) && - task_active_pid_ns(current) != &init_pid_ns) || - (!rdma_is_kernel_res(res) && task_active_pid_ns(current) != - task_active_pid_ns(res->task))) - /* - * 1. Kern resources should be visible in init - * namspace only - * 2. Present only resources visible in the current - * namespace - */ + if (!is_visible_in_pid_ns(res)) goto next; if (!rdma_restrack_get(res)) From 659067b0b5c36efa02ebae5e30d09d9303039029 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:48:55 +0200 Subject: [PATCH 0823/4277] RDMA/nldev: Prepare CAP_NET_ADMIN checks for .doit callbacks The .doit callbacks don't have a netlink_callback to check capabilities so in order to use the same fill_res_func for both .dump and .doit, we need to do the capability check outside of those functions. For .doit callbacks, it is possible to check CAP_NET_ADMIN directly on the received sk_buff. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/nldev.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index e54a79d3f458..1742ff4fbf79 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -361,7 +361,7 @@ static int fill_res_name_pid(struct sk_buff *msg, return 0; } -static int fill_res_qp_entry(struct sk_buff *msg, struct netlink_callback *cb, +static int fill_res_qp_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_qp *qp = container_of(res, struct ib_qp, res); @@ -427,8 +427,7 @@ out: return -EMSGSIZE; } -static int fill_res_cm_id_entry(struct sk_buff *msg, - struct netlink_callback *cb, +static int fill_res_cm_id_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct rdma_id_private *id_priv = @@ -487,7 +486,7 @@ out: return -EMSGSIZE; } -static int fill_res_cq_entry(struct sk_buff *msg, struct netlink_callback *cb, +static int fill_res_cq_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_cq *cq = container_of(res, struct ib_cq, res); @@ -524,7 +523,7 @@ out: return -EMSGSIZE; } -static int fill_res_mr_entry(struct sk_buff *msg, struct netlink_callback *cb, +static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_mr *mr = container_of(res, struct ib_mr, res); @@ -535,7 +534,7 @@ static int fill_res_mr_entry(struct sk_buff *msg, struct netlink_callback *cb, if (!entry_attr) goto out; - if (netlink_capable(cb->skb, CAP_NET_ADMIN)) { + if (has_cap_net_admin) { if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey)) goto err; if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LKEY, mr->lkey)) @@ -561,7 +560,7 @@ out: return -EMSGSIZE; } -static int fill_res_pd_entry(struct sk_buff *msg, struct netlink_callback *cb, +static int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_pd *pd = container_of(res, struct ib_pd, res); @@ -572,7 +571,7 @@ static int fill_res_pd_entry(struct sk_buff *msg, struct netlink_callback *cb, if (!entry_attr) goto out; - if (netlink_capable(cb->skb, CAP_NET_ADMIN)) { + if (has_cap_net_admin) { if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY, pd->local_dma_lkey)) goto err; @@ -909,7 +908,7 @@ static int nldev_res_get_dumpit(struct sk_buff *skb, } struct nldev_fill_res_entry { - int (*fill_res_func)(struct sk_buff *msg, struct netlink_callback *cb, + int (*fill_res_func)(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, u32 port); enum rdma_nldev_attr nldev_attr; enum rdma_nldev_command nldev_cmd; @@ -965,6 +964,7 @@ static int res_get_common_dumpit(struct sk_buff *skb, struct nlattr *table_attr; struct ib_device *device; int start = cb->args[0]; + bool has_cap_net_admin; struct nlmsghdr *nlh; u32 index, port = 0; bool filled = false; @@ -1013,6 +1013,8 @@ static int res_get_common_dumpit(struct sk_buff *skb, goto err; } + has_cap_net_admin = netlink_capable(cb->skb, CAP_NET_ADMIN); + down_read(&device->res.rwsem); hash_for_each_possible(device->res.hash, res, node, res_type) { if (idx < start) @@ -1032,7 +1034,7 @@ static int res_get_common_dumpit(struct sk_buff *skb, filled = true; up_read(&device->res.rwsem); - ret = fe->fill_res_func(skb, cb, res, port); + ret = fe->fill_res_func(skb, has_cap_net_admin, res, port); down_read(&device->res.rwsem); /* * Return resource back, but it won't be released till From 0ad699c0edc97a864177679dd67f2ccd73b07cb7 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:48:58 +0200 Subject: [PATCH 0824/4277] RDMA/core: Simplify restrack interface In the current implementation, we have one restrack root per-device and all users are simply providing it directly. Let's simplify the interface and have callers provide the ib_device and internally access the restrack_root. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 4 ++-- drivers/infiniband/core/nldev.c | 4 ++-- drivers/infiniband/core/restrack.c | 26 +++++++++++++++++++++----- include/rdma/restrack.h | 23 ++++------------------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 9d2e108235e9..919e94ff4b25 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -287,7 +287,7 @@ struct ib_device *_ib_alloc_device(size_t size) if (!device) return NULL; - rdma_restrack_init(&device->res); + rdma_restrack_init(device); device->dev.class = &ib_class; device_initialize(&device->dev); @@ -315,7 +315,7 @@ void ib_dealloc_device(struct ib_device *device) WARN_ON(!list_empty(&device->client_data_list)); WARN_ON(device->reg_state != IB_DEV_UNREGISTERED && device->reg_state != IB_DEV_UNINITIALIZED); - rdma_restrack_clean(&device->res); + rdma_restrack_clean(device); put_device(&device->dev); } EXPORT_SYMBOL(ib_dealloc_device); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 1742ff4fbf79..ee98fc9058b1 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -314,7 +314,6 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device) [RDMA_RESTRACK_CTX] = "ctx", }; - struct rdma_restrack_root *res = &device->res; struct nlattr *table_attr; int ret, i, curr; @@ -328,7 +327,8 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device) for (i = 0; i < RDMA_RESTRACK_MAX; i++) { if (!names[i]) continue; - curr = rdma_restrack_count(res, i, task_active_pid_ns(current)); + curr = rdma_restrack_count(device, i, + task_active_pid_ns(current)); ret = fill_res_info_entry(msg, names[i], curr); if (ret) goto err; diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index 46a5c553c624..0ade3da0a5c7 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -18,8 +18,14 @@ static int fill_res_noop(struct sk_buff *msg, return 0; } -void rdma_restrack_init(struct rdma_restrack_root *res) +/** + * rdma_restrack_init() - initialize resource tracking + * @dev: IB device + */ +void rdma_restrack_init(struct ib_device *dev) { + struct rdma_restrack_root *res = &dev->res; + init_rwsem(&res->rwsem); res->fill_res_entry = fill_res_noop; } @@ -38,11 +44,15 @@ static const char *type2str(enum rdma_restrack_type type) return names[type]; }; -void rdma_restrack_clean(struct rdma_restrack_root *res) +/** + * rdma_restrack_clean() - clean resource tracking + * @dev: IB device + */ +void rdma_restrack_clean(struct ib_device *dev) { + struct rdma_restrack_root *res = &dev->res; struct rdma_restrack_entry *e; char buf[TASK_COMM_LEN]; - struct ib_device *dev; const char *owner; int bkt; @@ -72,10 +82,16 @@ void rdma_restrack_clean(struct rdma_restrack_root *res) pr_err("restrack: %s", CUT_HERE); } -int rdma_restrack_count(struct rdma_restrack_root *res, - enum rdma_restrack_type type, +/** + * rdma_restrack_count() - the current usage of specific object + * @dev: IB device + * @type: actual type of object to operate + * @ns: PID namespace + */ +int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, struct pid_namespace *ns) { + struct rdma_restrack_root *res = &dev->res; struct rdma_restrack_entry *e; u32 cnt = 0; diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h index 8f179be9d9a9..f756fc48eee5 100644 --- a/include/rdma/restrack.h +++ b/include/rdma/restrack.h @@ -49,6 +49,7 @@ enum rdma_restrack_type { }; #define RDMA_RESTRACK_HASH_BITS 8 +struct ib_device; struct rdma_restrack_entry; /** @@ -122,25 +123,9 @@ struct rdma_restrack_entry { bool user; }; -/** - * rdma_restrack_init() - initialize resource tracking - * @res: resource tracking root - */ -void rdma_restrack_init(struct rdma_restrack_root *res); - -/** - * rdma_restrack_clean() - clean resource tracking - * @res: resource tracking root - */ -void rdma_restrack_clean(struct rdma_restrack_root *res); - -/** - * rdma_restrack_count() - the current usage of specific object - * @res: resource entry - * @type: actual type of object to operate - * @ns: PID namespace - */ -int rdma_restrack_count(struct rdma_restrack_root *res, +void rdma_restrack_init(struct ib_device *dev); +void rdma_restrack_clean(struct ib_device *dev); +int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, struct pid_namespace *ns); From 5e458d3f899ef6158d59b6909b8df9348868ccc4 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:49:05 +0200 Subject: [PATCH 0825/4277] RDMA/restrack: Refactor user/kernel restrack additions Since we already know if we are user/kernel before calling restrack_add, move type dependent code into the callers to make the flow more readable. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/restrack.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index 0ade3da0a5c7..bd7770ed4174 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -177,17 +177,6 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res) if (!dev) return; - if (res->type != RDMA_RESTRACK_CM_ID || rdma_is_kernel_res(res)) - res->task = NULL; - - if (!rdma_is_kernel_res(res)) { - if (!res->task) - rdma_restrack_set_task(res, NULL); - res->kern_name = NULL; - } else { - set_kern_name(res); - } - kref_init(&res->kref); init_completion(&res->comp); res->valid = true; @@ -203,6 +192,8 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res) */ void rdma_restrack_kadd(struct rdma_restrack_entry *res) { + res->task = NULL; + set_kern_name(res); res->user = false; rdma_restrack_add(res); } @@ -214,6 +205,13 @@ EXPORT_SYMBOL(rdma_restrack_kadd); */ void rdma_restrack_uadd(struct rdma_restrack_entry *res) { + if (res->type != RDMA_RESTRACK_CM_ID) + res->task = NULL; + + if (!res->task) + rdma_restrack_set_task(res, NULL); + res->kern_name = NULL; + res->user = true; rdma_restrack_add(res); } From 02da37509705d3ba6a58fe4799a0caf6b4baecb0 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 30 Jan 2019 12:49:02 +0200 Subject: [PATCH 0826/4277] RDMA/core: Use the ops infrastructure to keep all callbacks in one place As preparation to hide rdma_restrack_root, refactor the code to use the ops structure instead of a special callback which is hidden in rdma_restrack_root. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 1 + drivers/infiniband/core/nldev.c | 28 +++++++++++++++++--------- drivers/infiniband/core/restrack.c | 7 ------- drivers/infiniband/hw/cxgb4/provider.c | 2 +- include/rdma/ib_verbs.h | 5 +++++ include/rdma/restrack.h | 7 ------- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 919e94ff4b25..b9f725df4195 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1263,6 +1263,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, disassociate_ucontext); SET_DEVICE_OP(dev_ops, drain_rq); SET_DEVICE_OP(dev_ops, drain_sq); + SET_DEVICE_OP(dev_ops, fill_res_entry); SET_DEVICE_OP(dev_ops, get_dev_fw_str); SET_DEVICE_OP(dev_ops, get_dma_mr); SET_DEVICE_OP(dev_ops, get_hw_stats); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index ee98fc9058b1..25a248847575 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -361,11 +361,19 @@ static int fill_res_name_pid(struct sk_buff *msg, return 0; } +static bool fill_res_entry(struct ib_device *dev, struct sk_buff *msg, + struct rdma_restrack_entry *res) +{ + if (!dev->ops.fill_res_entry) + return false; + return dev->ops.fill_res_entry(msg, res); +} + static int fill_res_qp_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_qp *qp = container_of(res, struct ib_qp, res); - struct rdma_restrack_root *resroot = &qp->device->res; + struct ib_device *dev = qp->device; struct ib_qp_init_attr qp_init_attr; struct nlattr *entry_attr; struct ib_qp_attr qp_attr; @@ -415,7 +423,7 @@ static int fill_res_qp_entry(struct sk_buff *msg, bool has_cap_net_admin, if (fill_res_name_pid(msg, res)) goto err; - if (resroot->fill_res_entry(msg, res)) + if (fill_res_entry(dev, msg, res)) goto err; nla_nest_end(msg, entry_attr); @@ -432,7 +440,7 @@ static int fill_res_cm_id_entry(struct sk_buff *msg, bool has_cap_net_admin, { struct rdma_id_private *id_priv = container_of(res, struct rdma_id_private, res); - struct rdma_restrack_root *resroot = &id_priv->id.device->res; + struct ib_device *dev = id_priv->id.device; struct rdma_cm_id *cm_id = &id_priv->id; struct nlattr *entry_attr; @@ -474,7 +482,7 @@ static int fill_res_cm_id_entry(struct sk_buff *msg, bool has_cap_net_admin, if (fill_res_name_pid(msg, res)) goto err; - if (resroot->fill_res_entry(msg, res)) + if (fill_res_entry(dev, msg, res)) goto err; nla_nest_end(msg, entry_attr); @@ -490,7 +498,7 @@ static int fill_res_cq_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_cq *cq = container_of(res, struct ib_cq, res); - struct rdma_restrack_root *resroot = &cq->device->res; + struct ib_device *dev = cq->device; struct nlattr *entry_attr; entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_CQ_ENTRY); @@ -511,7 +519,7 @@ static int fill_res_cq_entry(struct sk_buff *msg, bool has_cap_net_admin, if (fill_res_name_pid(msg, res)) goto err; - if (resroot->fill_res_entry(msg, res)) + if (fill_res_entry(dev, msg, res)) goto err; nla_nest_end(msg, entry_attr); @@ -527,7 +535,7 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_mr *mr = container_of(res, struct ib_mr, res); - struct rdma_restrack_root *resroot = &mr->pd->device->res; + struct ib_device *dev = mr->pd->device; struct nlattr *entry_attr; entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_MR_ENTRY); @@ -548,7 +556,7 @@ static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, if (fill_res_name_pid(msg, res)) goto err; - if (resroot->fill_res_entry(msg, res)) + if (fill_res_entry(dev, msg, res)) goto err; nla_nest_end(msg, entry_attr); @@ -564,7 +572,7 @@ static int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin, struct rdma_restrack_entry *res, uint32_t port) { struct ib_pd *pd = container_of(res, struct ib_pd, res); - struct rdma_restrack_root *resroot = &pd->device->res; + struct ib_device *dev = pd->device; struct nlattr *entry_attr; entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_PD_ENTRY); @@ -591,7 +599,7 @@ static int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin, if (fill_res_name_pid(msg, res)) goto err; - if (resroot->fill_res_entry(msg, res)) + if (fill_res_entry(dev, msg, res)) goto err; nla_nest_end(msg, entry_attr); diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index bd7770ed4174..f80b37d437ac 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -12,12 +12,6 @@ #include "cma_priv.h" -static int fill_res_noop(struct sk_buff *msg, - struct rdma_restrack_entry *entry) -{ - return 0; -} - /** * rdma_restrack_init() - initialize resource tracking * @dev: IB device @@ -27,7 +21,6 @@ void rdma_restrack_init(struct ib_device *dev) struct rdma_restrack_root *res = &dev->res; init_rwsem(&res->rwsem); - res->fill_res_entry = fill_res_noop; } static const char *type2str(enum rdma_restrack_type type) diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index f977f8e7e162..cb5b713bbf39 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -549,6 +549,7 @@ static const struct ib_device_ops c4iw_dev_ops = { .destroy_cq = c4iw_destroy_cq, .destroy_qp = c4iw_destroy_qp, .destroy_srq = c4iw_destroy_srq, + .fill_res_entry = fill_res_entry, .get_dev_fw_str = get_dev_fw_str, .get_dma_mr = c4iw_get_dma_mr, .get_hw_stats = c4iw_get_mib, @@ -629,7 +630,6 @@ void c4iw_register_device(struct work_struct *work) dev->ibdev.iwcm->add_ref = c4iw_qp_add_ref; dev->ibdev.iwcm->rem_ref = c4iw_qp_rem_ref; dev->ibdev.iwcm->get_qp = c4iw_get_qp; - dev->ibdev.res.fill_res_entry = fill_res_entry; memcpy(dev->ibdev.iwcm->ifname, dev->rdev.lldi.ports[0]->name, sizeof(dev->ibdev.iwcm->ifname)); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 4183a03b46b5..5fc3be884444 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2510,6 +2510,11 @@ struct ib_device_ops { */ int (*init_port)(struct ib_device *device, u8 port_num, struct kobject *port_sysfs); + /** + * Allows rdma drivers to add their own restrack attributes. + */ + int (*fill_res_entry)(struct sk_buff *msg, + struct rdma_restrack_entry *entry); }; struct ib_device { diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h index f756fc48eee5..cc66cc7a11d3 100644 --- a/include/rdma/restrack.h +++ b/include/rdma/restrack.h @@ -65,13 +65,6 @@ struct rdma_restrack_root { * @hash: global database for all resources per-device */ DECLARE_HASHTABLE(hash, RDMA_RESTRACK_HASH_BITS); - /** - * @fill_res_entry: driver-specific fill function - * - * Allows rdma drivers to add their own restrack attributes. - */ - int (*fill_res_entry)(struct sk_buff *msg, - struct rdma_restrack_entry *entry); }; /** From cf2d65ec1d21f986bc128697aff5f52fef1ff12a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 23 Jan 2019 01:56:31 +0000 Subject: [PATCH 0827/4277] perf: xgene: Remove set but not used variable 'config' Fixes gcc '-Wunused-but-set-variable' warning: drivers/perf/xgene_pmu.c: In function 'xgene_perf_stop': drivers/perf/xgene_pmu.c:1055:6: warning: variable 'config' set but not used [-Wunused-but-set-variable] It never used since introduction. Signed-off-by: Yue Haibing Signed-off-by: Will Deacon --- drivers/perf/xgene_pmu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c index 0dc9ff0f8894..26b2c269e285 100644 --- a/drivers/perf/xgene_pmu.c +++ b/drivers/perf/xgene_pmu.c @@ -1057,7 +1057,6 @@ static void xgene_perf_start(struct perf_event *event, int flags) static void xgene_perf_stop(struct perf_event *event, int flags) { struct hw_perf_event *hw = &event->hw; - u64 config; if (hw->state & PERF_HES_UPTODATE) return; @@ -1069,7 +1068,6 @@ static void xgene_perf_stop(struct perf_event *event, int flags) if (hw->state & PERF_HES_UPTODATE) return; - config = hw->config; xgene_perf_read(event); hw->state |= PERF_HES_UPTODATE; } From 2e0fe66e0a136252f4d89dbbccdcb26deb867eb8 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 21 Jan 2019 21:35:50 -0200 Subject: [PATCH 0828/4277] media: imx: csi: Disable CSI immediately after last EOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). Do this by moving the wait for EOF completion into a new function csi_idmac_wait_last_eof(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: stable@vger.kernel.org # for 4.13 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7abfe0aa1418..920e38885292 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -662,7 +662,7 @@ out_put_ipu: return ret; } -static void csi_idmac_stop(struct csi_priv *priv) +static void csi_idmac_wait_last_eof(struct csi_priv *priv) { unsigned long flags; int ret; @@ -679,7 +679,10 @@ static void csi_idmac_stop(struct csi_priv *priv) &priv->last_eof_comp, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); +} +static void csi_idmac_stop(struct csi_priv *priv) +{ devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -786,6 +789,16 @@ idmac_stop: static void csi_stop(struct csi_priv *priv) { + if (priv->dest == IPU_CSI_DEST_IDMAC) + csi_idmac_wait_last_eof(priv); + + /* + * Disable the CSI asap, after syncing with the last EOF. + * Doing so after the IDMA channel is disabled has shown to + * create hard system-wide hangs. + */ + ipu_csi_disable(priv->csi); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -793,8 +806,6 @@ static void csi_stop(struct csi_priv *priv) if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); } - - ipu_csi_disable(priv->csi); } static const struct csi_skip_desc csi_skip[12] = { From 4bc1ab41eee9d02ad2483bf8f51a7b72e3504eba Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 21 Jan 2019 21:35:51 -0200 Subject: [PATCH 0829/4277] media: imx: csi: Stop upstream before disabling IDMA channel Move upstream stream off to just after receiving the last EOF completion and disabling the CSI (and thus before disabling the IDMA channel) in csi_stop(). For symmetry also move upstream stream on to beginning of csi_start(). Doing this makes csi_s_stream() more symmetric with prp_s_stream() which will require the same change to fix a hard lockup. Signed-off-by: Steve Longerbeam Cc: stable@vger.kernel.org # for 4.13 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 920e38885292..d851ca2497b4 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -753,10 +753,16 @@ static int csi_start(struct csi_priv *priv) output_fi = &priv->frame_interval[priv->active_output_pad]; + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) + return ret; + if (priv->dest == IPU_CSI_DEST_IDMAC) { ret = csi_idmac_start(priv); if (ret) - return ret; + goto stop_upstream; } ret = csi_setup(priv); @@ -784,6 +790,8 @@ fim_off: idmac_stop: if (priv->dest == IPU_CSI_DEST_IDMAC) csi_idmac_stop(priv); +stop_upstream: + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); return ret; } @@ -799,6 +807,9 @@ static void csi_stop(struct csi_priv *priv) */ ipu_csi_disable(priv->csi); + /* stop upstream */ + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -966,23 +977,13 @@ static int csi_s_stream(struct v4l2_subdev *sd, int enable) goto update_count; if (enable) { - /* upstream must be started first, before starting CSI */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) - goto out; - dev_dbg(priv->dev, "stream ON\n"); ret = csi_start(priv); - if (ret) { - v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret) goto out; - } } else { dev_dbg(priv->dev, "stream OFF\n"); - /* CSI must be stopped first, then stop upstream */ csi_stop(priv); - v4l2_subdev_call(priv->src_sd, video, s_stream, 0); } update_count: From a19c22677377b87e4354f7306f46ad99bc982a9f Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 21 Jan 2019 21:35:52 -0200 Subject: [PATCH 0830/4277] media: imx: prpencvf: Stop upstream before disabling IDMA channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream must be stopped immediately after receiving the last EOF and before disabling the IDMA channel. This can be accomplished by moving upstream stream off to just after receiving the last EOF completion in prp_stop(). For symmetry also move upstream stream on to end of prp_start(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d1 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Stopping the video data stream entering the IDMA channel before disabling the channel itself appears to be a reliable fix for the hard lockup. Fixes: f0d9c8924e2c3 ("[media] media: imx: Add IC subdev drivers") Reported-by: Gaël PORTAY Tested-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: stable@vger.kernel.org # for 4.13 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 26 ++++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 053a911d477a..3637693c2bc8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -706,12 +706,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, + "upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -743,6 +754,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -1173,15 +1190,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) goto out; - /* start/stop upstream */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) { - if (enable) - prp_stop(priv); - goto out; - } - update_count: priv->stream_count += enable ? 1 : -1; if (priv->stream_count < 0) From d8501cc85069f161466016a140e71ea88ee83376 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 17 Jan 2019 03:39:18 -0200 Subject: [PATCH 0831/4277] media: dt-bindings: media: add 'assigned-clocks' to vcodec examples Fix MTK binding document for MT8173 dtsi changed in order to use standard CCF interface. MT8173 SoC from Mediatek. Signed-off-by: Yunfei Dong Signed-off-by: Qianqian Yan Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/mediatek-vcodec.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt index 2a615d84a682..b6b5dde6abd8 100644 --- a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt +++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt @@ -66,6 +66,15 @@ vcodec_dec: vcodec@16000000 { "vencpll", "venc_lt_sel", "vdec_bus_clk_src"; + assigned-clocks = <&topckgen CLK_TOP_VENC_LT_SEL>, + <&topckgen CLK_TOP_CCI400_SEL>, + <&topckgen CLK_TOP_VDEC_SEL>, + <&apmixedsys CLK_APMIXED_VCODECPLL>, + <&apmixedsys CLK_APMIXED_VENCPLL>; + assigned-clock-parents = <&topckgen CLK_TOP_VCODECPLL_370P5>, + <&topckgen CLK_TOP_UNIVPLL_D2>, + <&topckgen CLK_TOP_VCODECPLL>; + assigned-clock-rates = <0>, <0>, <0>, <1482000000>, <800000000>; }; vcodec_enc: vcodec@18002000 { @@ -105,4 +114,8 @@ vcodec_dec: vcodec@16000000 { "venc_sel", "venc_lt_sel_src", "venc_lt_sel"; + assigned-clocks = <&topckgen CLK_TOP_VENC_SEL>, + <&topckgen CLK_TOP_VENC_LT_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_VENCPLL_D2>, + <&topckgen CLK_TOP_UNIVPLL1_D2>; }; From 04bde67410bfff52958298e831fc5c7db04abe2d Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 17 Jan 2019 03:39:20 -0200 Subject: [PATCH 0832/4277] media: mtk-vcodec: Using common interface to manage vdec/venc clock Vdec: Using standard CCF interface to set parent clock and clock rate in dtsi and using common interface to open/close video decoder clock. Venc: Using standard CCF interface to set parent clock in dtsi and using common interface to open/close video encoder clock. Signed-off-by: Yunfei Dong Signed-off-by: Qianqian Yan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../platform/mtk-vcodec/mtk_vcodec_dec_pm.c | 161 ++++++------------ .../platform/mtk-vcodec/mtk_vcodec_drv.h | 31 ++-- .../platform/mtk-vcodec/mtk_vcodec_enc_pm.c | 102 ++++++----- 3 files changed, 130 insertions(+), 164 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c index 79ca03ac449c..7884465afcd2 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -27,11 +27,14 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) struct device_node *node; struct platform_device *pdev; struct mtk_vcodec_pm *pm; - int ret = 0; + struct mtk_vcodec_clk *dec_clk; + struct mtk_vcodec_clk_info *clk_info; + int i = 0, ret = 0; pdev = mtkdev->plat_dev; pm = &mtkdev->pm; pm->mtkdev = mtkdev; + dec_clk = &pm->vdec_clk; node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0); if (!node) { mtk_v4l2_err("of_parse_phandle mediatek,larb fail!"); @@ -47,52 +50,34 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) pdev = mtkdev->plat_dev; pm->dev = &pdev->dev; - pm->vcodecpll = devm_clk_get(&pdev->dev, "vcodecpll"); - if (IS_ERR(pm->vcodecpll)) { - mtk_v4l2_err("devm_clk_get vcodecpll fail"); - ret = PTR_ERR(pm->vcodecpll); + dec_clk->clk_num = + of_property_count_strings(pdev->dev.of_node, "clock-names"); + if (dec_clk->clk_num > 0) { + dec_clk->clk_info = devm_kcalloc(&pdev->dev, + dec_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!dec_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get vdec clock count"); + return -EINVAL; } - pm->univpll_d2 = devm_clk_get(&pdev->dev, "univpll_d2"); - if (IS_ERR(pm->univpll_d2)) { - mtk_v4l2_err("devm_clk_get univpll_d2 fail"); - ret = PTR_ERR(pm->univpll_d2); - } - - pm->clk_cci400_sel = devm_clk_get(&pdev->dev, "clk_cci400_sel"); - if (IS_ERR(pm->clk_cci400_sel)) { - mtk_v4l2_err("devm_clk_get clk_cci400_sel fail"); - ret = PTR_ERR(pm->clk_cci400_sel); - } - - pm->vdec_sel = devm_clk_get(&pdev->dev, "vdec_sel"); - if (IS_ERR(pm->vdec_sel)) { - mtk_v4l2_err("devm_clk_get vdec_sel fail"); - ret = PTR_ERR(pm->vdec_sel); - } - - pm->vdecpll = devm_clk_get(&pdev->dev, "vdecpll"); - if (IS_ERR(pm->vdecpll)) { - mtk_v4l2_err("devm_clk_get vdecpll fail"); - ret = PTR_ERR(pm->vdecpll); - } - - pm->vencpll = devm_clk_get(&pdev->dev, "vencpll"); - if (IS_ERR(pm->vencpll)) { - mtk_v4l2_err("devm_clk_get vencpll fail"); - ret = PTR_ERR(pm->vencpll); - } - - pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); - if (IS_ERR(pm->venc_lt_sel)) { - mtk_v4l2_err("devm_clk_get venc_lt_sel fail"); - ret = PTR_ERR(pm->venc_lt_sel); - } - - pm->vdec_bus_clk_src = devm_clk_get(&pdev->dev, "vdec_bus_clk_src"); - if (IS_ERR(pm->vdec_bus_clk_src)) { - mtk_v4l2_err("devm_clk_get vdec_bus_clk_src"); - ret = PTR_ERR(pm->vdec_bus_clk_src); + for (i = 0; i < dec_clk->clk_num; i++) { + clk_info = &dec_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("Failed to get clock name id = %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } } pm_runtime_enable(&pdev->dev); @@ -125,78 +110,36 @@ void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm) void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm) { - int ret; + struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk; + int ret, i = 0; - ret = clk_set_rate(pm->vcodecpll, 1482 * 1000000); - if (ret) - mtk_v4l2_err("clk_set_rate vcodecpll fail %d", ret); - - ret = clk_set_rate(pm->vencpll, 800 * 1000000); - if (ret) - mtk_v4l2_err("clk_set_rate vencpll fail %d", ret); - - ret = clk_prepare_enable(pm->vcodecpll); - if (ret) - mtk_v4l2_err("clk_prepare_enable vcodecpll fail %d", ret); - - ret = clk_prepare_enable(pm->vencpll); - if (ret) - mtk_v4l2_err("clk_prepare_enable vencpll fail %d", ret); - - ret = clk_prepare_enable(pm->vdec_bus_clk_src); - if (ret) - mtk_v4l2_err("clk_prepare_enable vdec_bus_clk_src fail %d", - ret); - - ret = clk_prepare_enable(pm->venc_lt_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable venc_lt_sel fail %d", ret); - - ret = clk_set_parent(pm->venc_lt_sel, pm->vdec_bus_clk_src); - if (ret) - mtk_v4l2_err("clk_set_parent venc_lt_sel vdec_bus_clk_src fail %d", - ret); - - ret = clk_prepare_enable(pm->univpll_d2); - if (ret) - mtk_v4l2_err("clk_prepare_enable univpll_d2 fail %d", ret); - - ret = clk_prepare_enable(pm->clk_cci400_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable clk_cci400_sel fail %d", ret); - - ret = clk_set_parent(pm->clk_cci400_sel, pm->univpll_d2); - if (ret) - mtk_v4l2_err("clk_set_parent clk_cci400_sel univpll_d2 fail %d", - ret); - - ret = clk_prepare_enable(pm->vdecpll); - if (ret) - mtk_v4l2_err("clk_prepare_enable vdecpll fail %d", ret); - - ret = clk_prepare_enable(pm->vdec_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable vdec_sel fail %d", ret); - - ret = clk_set_parent(pm->vdec_sel, pm->vdecpll); - if (ret) - mtk_v4l2_err("clk_set_parent vdec_sel vdecpll fail %d", ret); + for (i = 0; i < dec_clk->clk_num; i++) { + ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, + dec_clk->clk_info[i].clk_name, ret); + goto error; + } + } ret = mtk_smi_larb_get(pm->larbvdec); - if (ret) + if (ret) { mtk_v4l2_err("mtk_smi_larb_get larbvdec fail %d", ret); + goto error; + } + return; +error: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); } void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm) { + struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk; + int i = 0; + mtk_smi_larb_put(pm->larbvdec); - clk_disable_unprepare(pm->vdec_sel); - clk_disable_unprepare(pm->vdecpll); - clk_disable_unprepare(pm->univpll_d2); - clk_disable_unprepare(pm->clk_cci400_sel); - clk_disable_unprepare(pm->venc_lt_sel); - clk_disable_unprepare(pm->vdec_bus_clk_src); - clk_disable_unprepare(pm->vencpll); - clk_disable_unprepare(pm->vcodecpll); + for (i = dec_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 3cffb381ac8e..8aba69555b12 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -175,23 +175,30 @@ struct mtk_enc_params { unsigned int force_intra; }; +/** + * struct mtk_vcodec_clk_info - Structure used to store clock name + */ +struct mtk_vcodec_clk_info { + const char *clk_name; + struct clk *vcodec_clk; +}; + +/** + * struct mtk_vcodec_clk - Structure used to store vcodec clock information + */ +struct mtk_vcodec_clk { + struct mtk_vcodec_clk_info *clk_info; + int clk_num; +}; + /** * struct mtk_vcodec_pm - Power management data structure */ struct mtk_vcodec_pm { - struct clk *vdec_bus_clk_src; - struct clk *vencpll; - - struct clk *vcodecpll; - struct clk *univpll_d2; - struct clk *clk_cci400_sel; - struct clk *vdecpll; - struct clk *vdec_sel; - struct clk *vencpll_d2; - struct clk *venc_sel; - struct clk *univpll1_d2; - struct clk *venc_lt_sel; + struct mtk_vcodec_clk vdec_clk; struct device *larbvdec; + + struct mtk_vcodec_clk venc_clk; struct device *larbvenc; struct device *larbvenclt; struct device *dev; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c index 7c025045ea90..39375b8ea27c 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -27,9 +27,11 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) { struct device_node *node; struct platform_device *pdev; - struct device *dev; struct mtk_vcodec_pm *pm; - int ret = 0; + struct mtk_vcodec_clk *enc_clk; + struct mtk_vcodec_clk_info *clk_info; + int ret = 0, i = 0; + struct device *dev; pdev = mtkdev->plat_dev; pm = &mtkdev->pm; @@ -37,6 +39,7 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) pm->mtkdev = mtkdev; pm->dev = &pdev->dev; dev = &pdev->dev; + enc_clk = &pm->venc_clk; node = of_parse_phandle(dev->of_node, "mediatek,larb", 0); if (!node) { @@ -68,28 +71,34 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) pdev = mtkdev->plat_dev; pm->dev = &pdev->dev; - pm->vencpll_d2 = devm_clk_get(&pdev->dev, "venc_sel_src"); - if (IS_ERR(pm->vencpll_d2)) { - mtk_v4l2_err("devm_clk_get vencpll_d2 fail"); - ret = PTR_ERR(pm->vencpll_d2); + enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (enc_clk->clk_num > 0) { + enc_clk->clk_info = devm_kcalloc(&pdev->dev, + enc_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!enc_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get venc clock count"); + return -EINVAL; } - pm->venc_sel = devm_clk_get(&pdev->dev, "venc_sel"); - if (IS_ERR(pm->venc_sel)) { - mtk_v4l2_err("devm_clk_get venc_sel fail"); - ret = PTR_ERR(pm->venc_sel); - } - - pm->univpll1_d2 = devm_clk_get(&pdev->dev, "venc_lt_sel_src"); - if (IS_ERR(pm->univpll1_d2)) { - mtk_v4l2_err("devm_clk_get univpll1_d2 fail"); - ret = PTR_ERR(pm->univpll1_d2); - } - - pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); - if (IS_ERR(pm->venc_lt_sel)) { - mtk_v4l2_err("devm_clk_get venc_lt_sel fail"); - ret = PTR_ERR(pm->venc_lt_sel); + for (i = 0; i < enc_clk->clk_num; i++) { + clk_info = &enc_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("venc failed to get clk name %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } } return ret; @@ -102,38 +111,45 @@ void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev) void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) { - int ret; + struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; + int ret, i = 0; - ret = clk_prepare_enable(pm->venc_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable fail %d", ret); - - ret = clk_set_parent(pm->venc_sel, pm->vencpll_d2); - if (ret) - mtk_v4l2_err("clk_set_parent fail %d", ret); - - ret = clk_prepare_enable(pm->venc_lt_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable fail %d", ret); - - ret = clk_set_parent(pm->venc_lt_sel, pm->univpll1_d2); - if (ret) - mtk_v4l2_err("clk_set_parent fail %d", ret); + for (i = 0; i < enc_clk->clk_num; i++) { + ret = clk_prepare_enable(enc_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("venc clk_prepare_enable %d %s fail %d", i, + enc_clk->clk_info[i].clk_name, ret); + goto clkerr; + } + } ret = mtk_smi_larb_get(pm->larbvenc); - if (ret) + if (ret) { mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret); - + goto larbvencerr; + } ret = mtk_smi_larb_get(pm->larbvenclt); - if (ret) + if (ret) { mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret); + goto larbvenclterr; + } + return; +larbvenclterr: + mtk_smi_larb_put(pm->larbvenc); +larbvencerr: +clkerr: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); } void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) { + struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; + int i = 0; + mtk_smi_larb_put(pm->larbvenc); mtk_smi_larb_put(pm->larbvenclt); - clk_disable_unprepare(pm->venc_lt_sel); - clk_disable_unprepare(pm->venc_sel); + for (i = enc_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); } From 03535e7a3a9937da99ee18304309e0574d2504fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Jan 2019 06:47:49 -0200 Subject: [PATCH 0833/4277] media: vb2: vb2_find_timestamp: drop restriction on buffer state There really is no reason why vb2_find_timestamp can't just find buffers in any state. Drop that part of the test. This also means that vb->timestamp should only be set to 0 when the driver doesn't copy timestamps. This change allows for more efficient pipelining (i.e. you can use a buffer for a reference frame even when it is queued). Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Reviewed-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 11 +++-------- include/media/videobuf2-v4l2.h | 3 +-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 75ea90e795d8..2f3b3ca5bde6 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -567,7 +567,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); unsigned int plane; - if (!vb->vb2_queue->is_output || !vb->vb2_queue->copy_timestamp) + if (!vb->vb2_queue->copy_timestamp) vb->timestamp = 0; for (plane = 0; plane < vb->num_planes; ++plane) { @@ -594,14 +594,9 @@ int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, { unsigned int i; - for (i = start_idx; i < q->num_buffers; i++) { - struct vb2_buffer *vb = q->bufs[i]; - - if ((vb->state == VB2_BUF_STATE_DEQUEUED || - vb->state == VB2_BUF_STATE_DONE) && - vb->timestamp == timestamp) + for (i = start_idx; i < q->num_buffers; i++) + if (q->bufs[i]->timestamp == timestamp) return i; - } return -1; } EXPORT_SYMBOL_GPL(vb2_find_timestamp); diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index a9961bc776dc..8a10889dc2fd 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -59,8 +59,7 @@ struct vb2_v4l2_buffer { * vb2_find_timestamp() - Find buffer with given timestamp in the queue * * @q: pointer to &struct vb2_queue with videobuf2 queue. - * @timestamp: the timestamp to find. Only buffers in state DEQUEUED or DONE - * are considered. + * @timestamp: the timestamp to find. * @start_idx: the start index (usually 0) in the buffer array to start * searching from. Note that there may be multiple buffers * with the same timestamp value, so you can restart the search From 826ef7508833f8bae928ccae4071b6efa6ba6184 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 24 Jan 2019 10:49:45 -0200 Subject: [PATCH 0834/4277] media: Revert "media: cedrus: Allow using the current dst buffer as reference" This reverts commit cf20ae1535eb690a87c29b9cc7af51881384e967. The vb2_find_timestamp helper was modified to allow finding buffers regardless of their current state in the queue. This means that we no longer have to take particular care of references to the current capture buffer. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 13 ------------- drivers/staging/media/sunxi/cedrus/cedrus_dec.h | 2 -- drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 10 ++++------ 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 2c295286766c..443fb037e1cf 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -22,19 +22,6 @@ #include "cedrus_dec.h" #include "cedrus_hw.h" -int cedrus_reference_index_find(struct vb2_queue *queue, - struct vb2_buffer *vb2_buf, u64 timestamp) -{ - /* - * Allow using the current capture buffer as reference, which can occur - * for field-coded pictures. - */ - if (vb2_buf->timestamp == timestamp) - return vb2_buf->index; - else - return vb2_find_timestamp(queue, timestamp, 0); -} - void cedrus_device_run(void *priv) { struct cedrus_ctx *ctx = priv; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index 8d0fc248220f..d1ae7903677b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,8 +16,6 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ -int cedrus_reference_index_find(struct vb2_queue *queue, - struct vb2_buffer *vb2_buf, u64 timestamp); void cedrus_device_run(void *priv); #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 81c66a8aa1ac..cb45fda9aaeb 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -10,7 +10,6 @@ #include #include "cedrus.h" -#include "cedrus_dec.h" #include "cedrus_hw.h" #include "cedrus_regs.h" @@ -160,8 +159,8 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - forward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, - slice_params->forward_ref_ts); + forward_idx = vb2_find_timestamp(cap_q, + slice_params->forward_ref_ts, 0); fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); @@ -169,9 +168,8 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - backward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, - slice_params->backward_ref_ts); - + backward_idx = vb2_find_timestamp(cap_q, + slice_params->backward_ref_ts, 0); bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); From 2cc1802f62e562611e86f04d9dae1337c824991e Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Thu, 24 Jan 2019 07:51:55 -0200 Subject: [PATCH 0835/4277] media: vb2: Keep dma-buf buffers mapped until they are freed When using vb2 for video decoding, dequeued capture buffers may still be accessed by the hardware: this is the case when they are used as reference frames for decoding subsequent frames. When the buffer is imported with dma-buf, it needs to be mapped before access. Until now, it was mapped when queuing and unmapped when dequeuing, which doesn't work for access as a reference frames. One way to solve this would be to map the buffer again when it is needed as a reference, but the mapping/unmapping operations can seriously impact performance. As a result, map the buffer once (when it is first needed when queued) and keep it mapped until it is freed. Reviewed-on: https://chromium-review.googlesource.com/334103 [Paul: Updated for mainline and changed commit message] Signed-off-by: Pawel Osciak Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 70e8c3366f9c..ce9294a635cc 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1196,6 +1196,9 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) * userspace knows sooner rather than later if the dma-buf map fails. */ for (plane = 0; plane < vb->num_planes; ++plane) { + if (vb->planes[plane].dbuf_mapped) + continue; + ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); if (ret) { dprintk(1, "failed to map dmabuf for plane %d\n", @@ -1758,14 +1761,6 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) vb->state = VB2_BUF_STATE_DEQUEUED; - /* unmap DMABUF buffer */ - if (q->memory == VB2_MEMORY_DMABUF) - for (i = 0; i < vb->num_planes; ++i) { - if (!vb->planes[i].dbuf_mapped) - continue; - call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv); - vb->planes[i].dbuf_mapped = 0; - } call_void_bufop(q, init_buffer, vb); } From 065e5a31497d8f285da06eac1c8d8e86c723af47 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 24 Jan 2019 07:51:56 -0200 Subject: [PATCH 0836/4277] media: cedrus: Remove completed item from TODO list (dma-buf references) Access to reference frames that were imported from dma-buf was taken care of and is no longer a pending item on the driver's TODO list. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/TODO | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/TODO b/drivers/staging/media/sunxi/cedrus/TODO index a951b3fd1ea1..ec277ece47af 100644 --- a/drivers/staging/media/sunxi/cedrus/TODO +++ b/drivers/staging/media/sunxi/cedrus/TODO @@ -5,8 +5,3 @@ Before this stateless decoder driver can leave the staging area: * Userspace support for the Request API needs to be reviewed; * Another stateless decoder driver should be submitted; * At least one stateless encoder driver should be submitted. -* When queueing a request containing references to I frames, the - refcount of the memory for those I frames needs to be incremented - and decremented when the request is completed. This will likely - require some help from vb2. The driver should fail the request - if the memory/buffer is gone. From 28d77c21cbeb2c6039d48ef88401b87a56a7a07f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:13 -0200 Subject: [PATCH 0837/4277] media: vb2: add buf_out_validate callback When queueing a buffer to a request the 'field' value is not validated. That field is only validated when the _buf_prepare() is called, which happens when the request is queued. However, this validation should happen at QBUF time, since you want to know about this as soon as possible. Also, the spec requires that the 'field' value is validated at QBUF time. This patch adds a new buf_out_validate callback to validate the output buffer at buf_prepare time or when QBUF queues an unprepared buffer to a request. This callback is mandatory for output queues that support requests. This issue was found by v4l2-compliance since it failed to replace V4L2_FIELD_ANY by a proper field value when testing the vivid video output in combination with requests. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-core.c | 22 ++++++++++++++++--- include/media/videobuf2-core.h | 5 +++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index ce9294a635cc..e07b6bdb6982 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -499,9 +499,9 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) pr_info(" buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n", vb->cnt_buf_init, vb->cnt_buf_cleanup, vb->cnt_buf_prepare, vb->cnt_buf_finish); - pr_info(" buf_queue: %u buf_done: %u buf_request_complete: %u\n", - vb->cnt_buf_queue, vb->cnt_buf_done, - vb->cnt_buf_request_complete); + pr_info(" buf_out_validate: %u buf_queue: %u buf_done: %u buf_request_complete: %u\n", + vb->cnt_buf_out_validate, vb->cnt_buf_queue, + vb->cnt_buf_done, vb->cnt_buf_request_complete); pr_info(" alloc: %u put: %u prepare: %u finish: %u mmap: %u\n", vb->cnt_mem_alloc, vb->cnt_mem_put, vb->cnt_mem_prepare, vb->cnt_mem_finish, @@ -1277,6 +1277,14 @@ static int __buf_prepare(struct vb2_buffer *vb) return 0; WARN_ON(vb->synced); + if (q->is_output) { + ret = call_vb_qop(vb, buf_out_validate, vb); + if (ret) { + dprintk(1, "buffer validation failed\n"); + return ret; + } + } + vb->state = VB2_BUF_STATE_PREPARING; switch (q->memory) { @@ -1523,6 +1531,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, return -EINVAL; } + if (q->is_output && !vb->prepared) { + ret = call_vb_qop(vb, buf_out_validate, vb); + if (ret) { + dprintk(1, "buffer validation failed\n"); + return ret; + } + } + media_request_object_init(&vb->req_obj); /* Make sure the request is in a safe state for updating. */ diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4a737b2c610b..4849b865b908 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -296,6 +296,7 @@ struct vb2_buffer { u32 cnt_mem_num_users; u32 cnt_mem_mmap; + u32 cnt_buf_out_validate; u32 cnt_buf_init; u32 cnt_buf_prepare; u32 cnt_buf_finish; @@ -342,6 +343,9 @@ struct vb2_buffer { * @wait_finish: reacquire all locks released in the previous callback; * required to continue operation after sleeping while * waiting for a new buffer to arrive. + * @buf_out_validate: called when the output buffer is prepared or queued + * to a request; drivers can use this to validate + * userspace-provided information; optional. * @buf_init: called once after allocating a buffer (in MMAP case) * or after acquiring a new USERPTR buffer; drivers may * perform additional buffer-related initialization; @@ -409,6 +413,7 @@ struct vb2_ops { void (*wait_prepare)(struct vb2_queue *q); void (*wait_finish)(struct vb2_queue *q); + int (*buf_out_validate)(struct vb2_buffer *vb); int (*buf_init)(struct vb2_buffer *vb); int (*buf_prepare)(struct vb2_buffer *vb); void (*buf_finish)(struct vb2_buffer *vb); From ab7afaf33275acc2ccfaab3fe5b46870eaee9d55 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:14 -0200 Subject: [PATCH 0838/4277] media: vim2m: add buf_out_validate callback Validate the field for an output buffer. This ensures that the field is validated when the buffer is queued to a request, and not when the request itself is queued, which is too late. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index a7a152fb3075..924120d69da2 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -743,25 +743,29 @@ static int vim2m_queue_setup(struct vb2_queue *vq, return 0; } -static int vim2m_buf_prepare(struct vb2_buffer *vb) +static int vim2m_buf_out_validate(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dprintk(ctx->dev, "%s field isn't supported\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int vim2m_buf_prepare(struct vb2_buffer *vb) +{ + struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vim2m_q_data *q_data; dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); q_data = get_q_data(ctx, vb->vb2_queue->type); - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - dprintk(ctx->dev, "%s field isn't supported\n", - __func__); - return -EINVAL; - } - } - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage); @@ -822,6 +826,7 @@ static void vim2m_buf_request_complete(struct vb2_buffer *vb) static const struct vb2_ops vim2m_qops = { .queue_setup = vim2m_queue_setup, + .buf_out_validate = vim2m_buf_out_validate, .buf_prepare = vim2m_buf_prepare, .buf_queue = vim2m_buf_queue, .start_streaming = vim2m_start_streaming, From 1f2f510753becf607f9974a39a89ab1878f24b9d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:15 -0200 Subject: [PATCH 0839/4277] media: vivid: add buf_out_validate callback Validate the field for an output buffer. This ensures that the field is validated when the buffer is queued to a request, and not when the request itself is queued, which is too late. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 55c0628ee9e1..b42b7fcc6145 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -84,10 +84,24 @@ static int vid_out_queue_setup(struct vb2_queue *vq, return 0; } -static int vid_out_buf_prepare(struct vb2_buffer *vb) +static int vid_out_buf_out_validate(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->field_out != V4L2_FIELD_ALTERNATE) + vbuf->field = dev->field_out; + else if (vbuf->field != V4L2_FIELD_TOP && + vbuf->field != V4L2_FIELD_BOTTOM) + return -EINVAL; + return 0; +} + +static int vid_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); unsigned long size; unsigned planes; unsigned p; @@ -108,12 +122,6 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } - if (dev->field_out != V4L2_FIELD_ALTERNATE) - vbuf->field = dev->field_out; - else if (vbuf->field != V4L2_FIELD_TOP && - vbuf->field != V4L2_FIELD_BOTTOM) - return -EINVAL; - for (p = 0; p < planes; p++) { size = dev->bytesperline_out[p] * dev->fmt_out_rect.height + vb->planes[p].data_offset; @@ -191,6 +199,7 @@ static void vid_out_buf_request_complete(struct vb2_buffer *vb) const struct vb2_ops vivid_vid_out_qops = { .queue_setup = vid_out_queue_setup, + .buf_out_validate = vid_out_buf_out_validate, .buf_prepare = vid_out_buf_prepare, .buf_queue = vid_out_buf_queue, .start_streaming = vid_out_start_streaming, From 6b3e4c4cc162390b833e57de656644786ca88919 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:16 -0200 Subject: [PATCH 0840/4277] media: cedrus: add buf_out_validate callback Validate the field for an output buffer. This ensures that the field is validated when the buffer is queued to a request, and not when the request itself is queued, which is too late. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_video.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 8721b4a7d496..b5cc79389d67 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -416,6 +416,14 @@ static void cedrus_buf_cleanup(struct vb2_buffer *vb) ctx->dst_bufs[vb->index] = NULL; } +static int cedrus_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + static int cedrus_buf_prepare(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -493,6 +501,7 @@ static struct vb2_ops cedrus_qops = { .buf_init = cedrus_buf_init, .buf_cleanup = cedrus_buf_cleanup, .buf_queue = cedrus_buf_queue, + .buf_out_validate = cedrus_buf_out_validate, .buf_request_complete = cedrus_buf_request_complete, .start_streaming = cedrus_start_streaming, .stop_streaming = cedrus_stop_streaming, From 1284ed59a147c27cb882e49213571f7d52976eb5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:17 -0200 Subject: [PATCH 0841/4277] media: vb2: check that buf_out_validate is present The buf_out_validate is required for output queues in combination with requests. Check this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 2f3b3ca5bde6..3aeaea3af42a 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -409,6 +409,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md */ if (WARN_ON(!q->ops->buf_request_complete)) return -EINVAL; + /* + * Make sure this op is implemented by the driver for the output queue. + * It's easy to forget this callback, but is it important to correctly + * validate the 'field' value at QBUF time. + */ + if (WARN_ON((q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT || + q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && + !q->ops->buf_out_validate)) + return -EINVAL; if (vb->state != VB2_BUF_STATE_DEQUEUED) { dprintk(1, "%s: buffer is not in dequeued state\n", opname); From 560c053deb94ff65b22a87f28e8e2fab5940555c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 25 Jan 2019 13:40:04 -0200 Subject: [PATCH 0842/4277] media: vivid: fix vid_out_buf_prepare() The wrong size check was performed for output formats like NV24 which set vfmt->buffers to 1, but vfmt->planes is 2. It was incorrectly checking the payload size for plane 1, which doesn't exist. Note: vfmt->buffers refers to the number of per-plane-buffers that should be allocated. vfmt->planes refers to the number of planes that make up an image. vfmt->planes may be > vfmt->buffers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index b42b7fcc6145..e61b91b414f9 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -102,17 +102,20 @@ static int vid_out_buf_out_validate(struct vb2_buffer *vb) static int vid_out_buf_prepare(struct vb2_buffer *vb) { struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size; - unsigned planes; + const struct vivid_fmt *vfmt = dev->fmt_out; + unsigned int planes = vfmt->buffers; + unsigned int h = dev->fmt_out_rect.height; + unsigned int size = dev->bytesperline_out[0] * h; unsigned p; + for (p = vfmt->buffers; p < vfmt->planes; p++) + size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; + dprintk(dev, 1, "%s\n", __func__); if (WARN_ON(NULL == dev->fmt_out)) return -EINVAL; - planes = dev->fmt_out->planes; - if (dev->buf_prepare_error) { /* * Error injection: test what happens if buf_prepare() returns @@ -123,11 +126,12 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) } for (p = 0; p < planes; p++) { - size = dev->bytesperline_out[p] * dev->fmt_out_rect.height + - vb->planes[p].data_offset; + if (p) + size = dev->bytesperline_out[p] * h; + size += vb->planes[p].data_offset; if (vb2_get_plane_payload(vb, p) < size) { - dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %lu)\n", + dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n", __func__, p, vb2_get_plane_payload(vb, p), size); return -EINVAL; } From 44e43d91ad4731d9e2e70c60eecc5982d6671e8c Mon Sep 17 00:00:00 2001 From: Mitko Haralanov Date: Thu, 24 Jan 2019 06:09:46 -0800 Subject: [PATCH 0843/4277] IB/hfi1: OPFN support discovery OPFN (Omni Path Feature Negotiation) support discovery allows a RC QP to announce that it supports OPFN and also discover if OPFN is supported by the peer QP. OPFN parameter negotiation is skipped unless OPFN support is first discovered. OPFN support is announced by claiming what was the reserved bit in dword 1 of OmniPath modified base transport header in requests and responses. Reviewed-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/hfi.h | 1 + drivers/infiniband/hw/hfi1/opfn.h | 53 ++++++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/rc.c | 16 ++++++--- drivers/infiniband/hw/hfi1/ruc.c | 16 ++++----- drivers/infiniband/hw/hfi1/uc.c | 3 +- drivers/infiniband/hw/hfi1/verbs.h | 3 +- 6 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 drivers/infiniband/hw/hfi1/opfn.h diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 6db2276f5c13..ddfcf2fe40ca 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -73,6 +73,7 @@ #include "chip_registers.h" #include "common.h" +#include "opfn.h" #include "verbs.h" #include "pio.h" #include "chip.h" diff --git a/drivers/infiniband/hw/hfi1/opfn.h b/drivers/infiniband/hw/hfi1/opfn.h new file mode 100644 index 000000000000..1a2b3449df67 --- /dev/null +++ b/drivers/infiniband/hw/hfi1/opfn.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2018 Intel Corporation. + * + */ +#ifndef _HFI1_OPFN_H +#define _HFI1_OPFN_H + +/** + * DOC: Omni Path Feature Negotion (OPFN) + * + * OPFN is a discovery protocol for Intel Omni-Path fabric that + * allows two RC QPs to negotiate a common feature that both QPs + * can support. Currently, the only OPA feature that OPFN + * supports is TID RDMA. + * + * Architecture + * + * OPFN involves the communication between two QPs on the HFI + * level on an Omni-Path fabric, and ULPs have no knowledge of + * OPFN at all. + * + * Implementation + * + * OPFN extends the existing IB RC protocol with the following + * changes: + * -- Uses Bit 24 (reserved) of DWORD 1 of Base Transport + * Header (BTH1) to indicate that the RC QP supports OPFN; + * -- Uses a combination of RC COMPARE_SWAP opcode (0x13) and + * the address U64_MAX (0xFFFFFFFFFFFFFFFF) as an OPFN + * request; The 64-bit data carried with the request/response + * contains the parameters for negotiation and will be + * defined in tid_rdma.c file; + * -- Defines IB_WR_RESERVED3 as IB_WR_OPFN. + * + * The OPFN communication will be triggered when an RC QP + * receives a request with Bit 24 of BTH1 set. The responder QP + * will then post send an OPFN request with its local + * parameters, which will be sent to the requester QP once all + * existing requests on the responder QP side have been sent. + * Once the requester QP receives the OPFN request, it will + * keep a copy of the responder QP's parameters, and return a + * response packet with its own local parameters. The responder + * QP receives the response packet and keeps a copy of the requester + * QP's parameters. After this exchange, each side has the parameters + * for both sides and therefore can select the right parameters + * for future transactions + */ + +/* STL Verbs Extended */ +#define IB_BTHE_E_SHIFT 24 + +#endif /* _HFI1_OPFN_H */ diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index be603f35d7e4..940e9236c328 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -89,8 +89,8 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, struct rvt_ack_entry *e; u32 hwords; u32 len; - u32 bth0; - u32 bth2; + u32 bth0, bth2; + u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT); int middle = 0; u32 pmtu = qp->pmtu; struct hfi1_qp_priv *priv = qp->priv; @@ -229,7 +229,7 @@ normal: ps->s_txreq->sde = priv->s_sde; ps->s_txreq->s_cur_size = len; ps->s_txreq->hdr_dwords = hwords; - hfi1_make_ruc_header(qp, ohdr, bth0, bth2, middle, ps); + hfi1_make_ruc_header(qp, ohdr, bth0, bth1, bth2, middle, ps); return 1; bail: @@ -262,8 +262,8 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) struct rvt_swqe *wqe; u32 hwords; u32 len; - u32 bth0 = 0; - u32 bth2; + u32 bth0 = 0, bth2; + u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT); u32 pmtu = qp->pmtu; char newreq; int middle = 0; @@ -693,6 +693,7 @@ no_flow_control: qp, ohdr, bth0 | (qp->s_state << 24), + bth1, bth2, middle, ps); @@ -796,6 +797,11 @@ static inline void hfi1_make_rc_ack_9B(struct hfi1_packet *packet, if (qp->s_mig_state == IB_MIG_MIGRATED) bth0 |= IB_BTH_MIG_REQ; bth1 = (!!is_fecn) << IB_BECN_SHIFT; + /* + * Inline ACKs go out without the use of the Verbs send engine, so + * we need to set the STL Verbs Extended bit here + */ + bth1 |= HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT; hfi1_make_bth_aeth(qp, ohdr, bth0, bth1); } diff --git a/drivers/infiniband/hw/hfi1/ruc.c b/drivers/infiniband/hw/hfi1/ruc.c index 7fb317c711df..f96c0f544cb0 100644 --- a/drivers/infiniband/hw/hfi1/ruc.c +++ b/drivers/infiniband/hw/hfi1/ruc.c @@ -250,7 +250,6 @@ static inline void hfi1_make_ruc_bth(struct rvt_qp *qp, struct ib_other_headers *ohdr, u32 bth0, u32 bth1, u32 bth2) { - bth1 |= qp->remote_qpn; ohdr->bth[0] = cpu_to_be32(bth0); ohdr->bth[1] = cpu_to_be32(bth1); ohdr->bth[2] = cpu_to_be32(bth2); @@ -272,13 +271,13 @@ static inline void hfi1_make_ruc_bth(struct rvt_qp *qp, */ static inline void hfi1_make_ruc_header_16B(struct rvt_qp *qp, struct ib_other_headers *ohdr, - u32 bth0, u32 bth2, int middle, + u32 bth0, u32 bth1, u32 bth2, + int middle, struct hfi1_pkt_state *ps) { struct hfi1_qp_priv *priv = qp->priv; struct hfi1_ibport *ibp = ps->ibp; struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); - u32 bth1 = 0; u32 slid; u16 pkey = hfi1_get_pkey(ibp, qp->s_pkey_index); u8 l4 = OPA_16B_L4_IB_LOCAL; @@ -360,12 +359,12 @@ static inline void hfi1_make_ruc_header_16B(struct rvt_qp *qp, */ static inline void hfi1_make_ruc_header_9B(struct rvt_qp *qp, struct ib_other_headers *ohdr, - u32 bth0, u32 bth2, int middle, + u32 bth0, u32 bth1, u32 bth2, + int middle, struct hfi1_pkt_state *ps) { struct hfi1_qp_priv *priv = qp->priv; struct hfi1_ibport *ibp = ps->ibp; - u32 bth1 = 0; u16 pkey = hfi1_get_pkey(ibp, qp->s_pkey_index); u16 lrh0 = HFI1_LRH_BTH; u8 extra_bytes = -ps->s_txreq->s_cur_size & 3; @@ -415,7 +414,7 @@ static inline void hfi1_make_ruc_header_9B(struct rvt_qp *qp, typedef void (*hfi1_make_ruc_hdr)(struct rvt_qp *qp, struct ib_other_headers *ohdr, - u32 bth0, u32 bth2, int middle, + u32 bth0, u32 bth1, u32 bth2, int middle, struct hfi1_pkt_state *ps); /* We support only two types - 9B and 16B for now */ @@ -425,7 +424,7 @@ static const hfi1_make_ruc_hdr hfi1_ruc_header_tbl[2] = { }; void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, - u32 bth0, u32 bth2, int middle, + u32 bth0, u32 bth1, u32 bth2, int middle, struct hfi1_pkt_state *ps) { struct hfi1_qp_priv *priv = qp->priv; @@ -446,7 +445,8 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, priv->s_ahg->ahgidx = 0; /* Make the appropriate header */ - hfi1_ruc_header_tbl[priv->hdr_type](qp, ohdr, bth0, bth2, middle, ps); + hfi1_ruc_header_tbl[priv->hdr_type](qp, ohdr, bth0, bth1, bth2, middle, + ps); } /* when sending, force a reschedule every one of these periods */ diff --git a/drivers/infiniband/hw/hfi1/uc.c b/drivers/infiniband/hw/hfi1/uc.c index 6ba47037c424..4ed4fcfabd6c 100644 --- a/drivers/infiniband/hw/hfi1/uc.c +++ b/drivers/infiniband/hw/hfi1/uc.c @@ -271,7 +271,8 @@ int hfi1_make_uc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) ps->s_txreq->ss = &qp->s_sge; ps->s_txreq->s_cur_size = len; hfi1_make_ruc_header(qp, ohdr, bth0 | (qp->s_state << 24), - mask_psn(qp->s_psn++), middle, ps); + qp->remote_qpn, mask_psn(qp->s_psn++), + middle, ps); return 1; done_free_tx: diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 1ad0b14bdb3c..8834119184b3 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -72,6 +72,7 @@ struct hfi1_packet; #include "iowait.h" #include "tid_rdma.h" +#include "opfn.h" #define HFI1_MAX_RDMA_ATOMIC 16 @@ -356,7 +357,7 @@ u32 hfi1_make_grh(struct hfi1_ibport *ibp, struct ib_grh *hdr, const struct ib_global_route *grh, u32 hwords, u32 nwords); void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, - u32 bth0, u32 bth2, int middle, + u32 bth0, u32 bth1, u32 bth2, int middle, struct hfi1_pkt_state *ps); void _hfi1_do_send(struct work_struct *work); From d22a207d74adb0b43742f83d025079207425928b Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:20:42 -0800 Subject: [PATCH 0844/4277] IB/hfi1: Add OPFN helper functions for TID RDMA feature This patch adds the OPFN helper functions to initialize, encode, decode, and reset OPFN parameters for the TID RDMA feature. Reviewed-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/chip.c | 11 ++ drivers/infiniband/hw/hfi1/chip.h | 3 +- drivers/infiniband/hw/hfi1/init.c | 2 + drivers/infiniband/hw/hfi1/opfn.h | 5 + drivers/infiniband/hw/hfi1/tid_rdma.c | 202 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 28 +++- drivers/infiniband/hw/hfi1/verbs.h | 5 + 7 files changed, 254 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index b443642eac02..4d40311f082e 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -5222,6 +5222,17 @@ int is_bx(struct hfi1_devdata *dd) return (chip_rev_minor & 0xF0) == 0x10; } +/* return true is kernel urg disabled for rcd */ +bool is_urg_masked(struct hfi1_ctxtdata *rcd) +{ + u64 mask; + u32 is = IS_RCVURGENT_START + rcd->ctxt; + u8 bit = is % 64; + + mask = read_csr(rcd->dd, CCE_INT_MASK + (8 * (is / 64))); + return !(mask & BIT_ULL(bit)); +} + /* * Append string s to buffer buf. Arguments curp and len are the current * position and remaining length, respectively. diff --git a/drivers/infiniband/hw/hfi1/chip.h b/drivers/infiniband/hw/hfi1/chip.h index 6b9c8f12dff8..ba3d99e6e33b 100644 --- a/drivers/infiniband/hw/hfi1/chip.h +++ b/drivers/infiniband/hw/hfi1/chip.h @@ -1,7 +1,7 @@ #ifndef _CHIP_H #define _CHIP_H /* - * Copyright(c) 2015 - 2017 Intel Corporation. + * Copyright(c) 2015 - 2018 Intel Corporation. * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -804,6 +804,7 @@ void clear_linkup_counters(struct hfi1_devdata *dd); u32 hdrqempty(struct hfi1_ctxtdata *rcd); int is_ax(struct hfi1_devdata *dd); int is_bx(struct hfi1_devdata *dd); +bool is_urg_masked(struct hfi1_ctxtdata *rcd); u32 read_physical_state(struct hfi1_devdata *dd); u32 chip_to_opa_pstate(struct hfi1_devdata *dd, u32 chip_pstate); const char *opa_lstate_name(u32 lstate); diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 7835eb52e7c5..2ba5a2a8b68f 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -927,6 +927,8 @@ int hfi1_init(struct hfi1_devdata *dd, int reinit) lastfail = hfi1_create_rcvhdrq(dd, rcd); if (!lastfail) lastfail = hfi1_setup_eagerbufs(rcd); + if (!lastfail) + lastfail = hfi1_kern_exp_rcv_init(rcd, reinit); if (lastfail) { dd_dev_err(dd, "failed to allocate kernel ctxt's rcvhdrq and/or egr bufs\n"); diff --git a/drivers/infiniband/hw/hfi1/opfn.h b/drivers/infiniband/hw/hfi1/opfn.h index 1a2b3449df67..1927c9862b8f 100644 --- a/drivers/infiniband/hw/hfi1/opfn.h +++ b/drivers/infiniband/hw/hfi1/opfn.h @@ -50,4 +50,9 @@ /* STL Verbs Extended */ #define IB_BTHE_E_SHIFT 24 +struct hfi1_opfn_data { + /* serialize opfn function calls */ + spinlock_t lock; +}; + #endif /* _HFI1_OPFN_H */ diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index da1ecb68a928..a8fd66f31fee 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -8,6 +8,208 @@ #include "verbs.h" #include "tid_rdma.h" +/* + * J_KEY for kernel contexts when TID RDMA is used. + * See generate_jkey() in hfi.h for more information. + */ +#define TID_RDMA_JKEY 32 +#define HFI1_KERNEL_MIN_JKEY HFI1_ADMIN_JKEY_RANGE +#define HFI1_KERNEL_MAX_JKEY (2 * HFI1_ADMIN_JKEY_RANGE - 1) + +#define TID_RDMA_MAX_READ_SEGS_PER_REQ 6 +#define TID_RDMA_MAX_WRITE_SEGS_PER_REQ 4 + +#define TID_OPFN_QP_CTXT_MASK 0xff +#define TID_OPFN_QP_CTXT_SHIFT 56 +#define TID_OPFN_QP_KDETH_MASK 0xff +#define TID_OPFN_QP_KDETH_SHIFT 48 +#define TID_OPFN_MAX_LEN_MASK 0x7ff +#define TID_OPFN_MAX_LEN_SHIFT 37 +#define TID_OPFN_TIMEOUT_MASK 0x1f +#define TID_OPFN_TIMEOUT_SHIFT 32 +#define TID_OPFN_RESERVED_MASK 0x3f +#define TID_OPFN_RESERVED_SHIFT 26 +#define TID_OPFN_URG_MASK 0x1 +#define TID_OPFN_URG_SHIFT 25 +#define TID_OPFN_VER_MASK 0x7 +#define TID_OPFN_VER_SHIFT 22 +#define TID_OPFN_JKEY_MASK 0x3f +#define TID_OPFN_JKEY_SHIFT 16 +#define TID_OPFN_MAX_READ_MASK 0x3f +#define TID_OPFN_MAX_READ_SHIFT 10 +#define TID_OPFN_MAX_WRITE_MASK 0x3f +#define TID_OPFN_MAX_WRITE_SHIFT 4 + +/* + * OPFN TID layout + * + * 63 47 31 15 + * NNNNNNNNKKKKKKKK MMMMMMMMMMMTTTTT DDDDDDUVVVJJJJJJ RRRRRRWWWWWWCCCC + * 3210987654321098 7654321098765432 1098765432109876 5432109876543210 + * N - the context Number + * K - the Kdeth_qp + * M - Max_len + * T - Timeout + * D - reserveD + * V - version + * U - Urg capable + * J - Jkey + * R - max_Read + * W - max_Write + * C - Capcode + */ + +static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) +{ + return + (((u64)p->qp & TID_OPFN_QP_CTXT_MASK) << + TID_OPFN_QP_CTXT_SHIFT) | + ((((u64)p->qp >> 16) & TID_OPFN_QP_KDETH_MASK) << + TID_OPFN_QP_KDETH_SHIFT) | + (((u64)((p->max_len >> PAGE_SHIFT) - 1) & + TID_OPFN_MAX_LEN_MASK) << TID_OPFN_MAX_LEN_SHIFT) | + (((u64)p->timeout & TID_OPFN_TIMEOUT_MASK) << + TID_OPFN_TIMEOUT_SHIFT) | + (((u64)p->urg & TID_OPFN_URG_MASK) << TID_OPFN_URG_SHIFT) | + (((u64)p->jkey & TID_OPFN_JKEY_MASK) << TID_OPFN_JKEY_SHIFT) | + (((u64)p->max_read & TID_OPFN_MAX_READ_MASK) << + TID_OPFN_MAX_READ_SHIFT) | + (((u64)p->max_write & TID_OPFN_MAX_WRITE_MASK) << + TID_OPFN_MAX_WRITE_SHIFT); +} + +static void tid_rdma_opfn_decode(struct tid_rdma_params *p, u64 data) +{ + p->max_len = (((data >> TID_OPFN_MAX_LEN_SHIFT) & + TID_OPFN_MAX_LEN_MASK) + 1) << PAGE_SHIFT; + p->jkey = (data >> TID_OPFN_JKEY_SHIFT) & TID_OPFN_JKEY_MASK; + p->max_write = (data >> TID_OPFN_MAX_WRITE_SHIFT) & + TID_OPFN_MAX_WRITE_MASK; + p->max_read = (data >> TID_OPFN_MAX_READ_SHIFT) & + TID_OPFN_MAX_READ_MASK; + p->qp = + ((((data >> TID_OPFN_QP_KDETH_SHIFT) & TID_OPFN_QP_KDETH_MASK) + << 16) | + ((data >> TID_OPFN_QP_CTXT_SHIFT) & TID_OPFN_QP_CTXT_MASK)); + p->urg = (data >> TID_OPFN_URG_SHIFT) & TID_OPFN_URG_MASK; + p->timeout = (data >> TID_OPFN_TIMEOUT_SHIFT) & TID_OPFN_TIMEOUT_MASK; +} + +void tid_rdma_opfn_init(struct rvt_qp *qp, struct tid_rdma_params *p) +{ + struct hfi1_qp_priv *priv = qp->priv; + + p->qp = (kdeth_qp << 16) | priv->rcd->ctxt; + p->max_len = TID_RDMA_MAX_SEGMENT_SIZE; + p->jkey = priv->rcd->jkey; + p->max_read = TID_RDMA_MAX_READ_SEGS_PER_REQ; + p->max_write = TID_RDMA_MAX_WRITE_SEGS_PER_REQ; + p->timeout = qp->timeout; + p->urg = is_urg_masked(priv->rcd); +} + +bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data) +{ + struct hfi1_qp_priv *priv = qp->priv; + + *data = tid_rdma_opfn_encode(&priv->tid_rdma.local); + return true; +} + +bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct tid_rdma_params *remote, *old; + bool ret = true; + + old = rcu_dereference_protected(priv->tid_rdma.remote, + lockdep_is_held(&priv->opfn.lock)); + data &= ~0xfULL; + /* + * If data passed in is zero, return true so as not to continue the + * negotiation process + */ + if (!data || !HFI1_CAP_IS_KSET(TID_RDMA)) + goto null; + /* + * If kzalloc fails, return false. This will result in: + * * at the requester a new OPFN request being generated to retry + * the negotiation + * * at the responder, 0 being returned to the requester so as to + * disable TID RDMA at both the requester and the responder + */ + remote = kzalloc(sizeof(*remote), GFP_ATOMIC); + if (!remote) { + ret = false; + goto null; + } + + tid_rdma_opfn_decode(remote, data); + priv->tid_timer_timeout_jiffies = + usecs_to_jiffies((((4096UL * (1UL << remote->timeout)) / + 1000UL) << 3) * 7); + rcu_assign_pointer(priv->tid_rdma.remote, remote); + /* + * A TID RDMA READ request's segment size is not equal to + * remote->max_len only when the request's data length is smaller + * than remote->max_len. In that case, there will be only one segment. + * Therefore, when priv->pkts_ps is used to calculate req->cur_seg + * during retry, it will lead to req->cur_seg = 0, which is exactly + * what is expected. + */ + priv->pkts_ps = (u16)rvt_div_mtu(qp, remote->max_len); + priv->timeout_shift = ilog2(priv->pkts_ps - 1) + 1; + goto free; +null: + RCU_INIT_POINTER(priv->tid_rdma.remote, NULL); + priv->timeout_shift = 0; +free: + if (old) + kfree_rcu(old, rcu_head); + return ret; +} + +bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data) +{ + bool ret; + + ret = tid_rdma_conn_reply(qp, *data); + *data = 0; + /* + * If tid_rdma_conn_reply() returns error, set *data as 0 to indicate + * TID RDMA could not be enabled. This will result in TID RDMA being + * disabled at the requester too. + */ + if (ret) + (void)tid_rdma_conn_req(qp, data); + return ret; +} + +void tid_rdma_conn_error(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct tid_rdma_params *old; + + old = rcu_dereference_protected(priv->tid_rdma.remote, + lockdep_is_held(&priv->opfn.lock)); + RCU_INIT_POINTER(priv->tid_rdma.remote, NULL); + if (old) + kfree_rcu(old, rcu_head); +} + +/* This is called at context initialization time */ +int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit) +{ + if (reinit) + return 0; + + BUILD_BUG_ON(TID_RDMA_JKEY < HFI1_KERNEL_MIN_JKEY); + BUILD_BUG_ON(TID_RDMA_JKEY > HFI1_KERNEL_MAX_JKEY); + rcd->jkey = TID_RDMA_JKEY; + hfi1_set_ctxt_jkey(rcd->dd, rcd, rcd->jkey); + return 0; +} + /** * qp_to_rcd - determine the receive context used by a qp * @qp - the qp diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 6fcd3adcdcc3..18c6d4333f1e 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -6,8 +6,34 @@ #ifndef HFI1_TID_RDMA_H #define HFI1_TID_RDMA_H +#define TID_RDMA_MAX_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */ + +struct tid_rdma_params { + struct rcu_head rcu_head; + u32 qp; + u32 max_len; + u16 jkey; + u8 max_read; + u8 max_write; + u8 timeout; + u8 urg; + u8 version; +}; + +struct tid_rdma_qp_params { + struct tid_rdma_params local; + struct tid_rdma_params __rcu *remote; +}; + +bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data); +bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data); +bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data); +void tid_rdma_conn_error(struct rvt_qp *qp); +void tid_rdma_opfn_init(struct rvt_qp *qp, struct tid_rdma_params *p); + +int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit); + int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, struct ib_qp_init_attr *init_attr); #endif /* HFI1_TID_RDMA_H */ - diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 8834119184b3..c8baa1e38ff6 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -161,8 +161,13 @@ struct hfi1_qp_priv { struct hfi1_ctxtdata *rcd; /* QP's receive context */ u8 s_sc; /* SC[0..4] for next packet */ struct iowait s_iowait; + struct hfi1_opfn_data opfn; + struct tid_rdma_qp_params tid_rdma; struct rvt_qp *owner; u8 hdr_type; /* 9B or 16B */ + unsigned long tid_timer_timeout_jiffies; + u16 pkts_ps; /* packets per segment */ + u8 timeout_shift; /* account for number of packets per segment */ }; /* From f01b4d5a43da47a9f61618a81a4ff3258ddc2c01 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:20:52 -0800 Subject: [PATCH 0845/4277] IB/hfi1: OPFN interface OPFN allows a pair of connected RC QPs to exchange a set of parameters in succession. The parameter exchange itself is done using the IB compare and swap request with a special virtual address. The request is triggered using a reserved IB work request opcode. This patch implements the OPFN interface to initialize, start, process, and reset the OPFN request. Reviewed-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/Makefile | 1 + drivers/infiniband/hw/hfi1/hfi.h | 2 + drivers/infiniband/hw/hfi1/init.c | 1 - drivers/infiniband/hw/hfi1/opfn.c | 304 ++++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/opfn.h | 33 ++- 5 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 drivers/infiniband/hw/hfi1/opfn.c diff --git a/drivers/infiniband/hw/hfi1/Makefile b/drivers/infiniband/hw/hfi1/Makefile index 3ce9dc8c3463..4044a8c8dbf4 100644 --- a/drivers/infiniband/hw/hfi1/Makefile +++ b/drivers/infiniband/hw/hfi1/Makefile @@ -24,6 +24,7 @@ hfi1-y := \ mad.o \ mmu_rb.o \ msix.o \ + opfn.o \ pcie.o \ pio.o \ pio_copy.o \ diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index ddfcf2fe40ca..9aa0357e17b7 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -99,6 +99,8 @@ #define NEIGHBOR_TYPE_HFI 0 #define NEIGHBOR_TYPE_SWITCH 1 +#define HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES 5 + extern unsigned long hfi1_cap_mask; #define HFI1_CAP_KGET_MASK(mask, cap) ((mask) & HFI1_CAP_##cap) #define HFI1_CAP_UGET_MASK(mask, cap) \ diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 2ba5a2a8b68f..09c898d0975c 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -72,7 +72,6 @@ #undef pr_fmt #define pr_fmt(fmt) DRIVER_NAME ": " fmt -#define HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES 5 /* * min buffers we want to have per context, after driver */ diff --git a/drivers/infiniband/hw/hfi1/opfn.c b/drivers/infiniband/hw/hfi1/opfn.c new file mode 100644 index 000000000000..2d46c91eb129 --- /dev/null +++ b/drivers/infiniband/hw/hfi1/opfn.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Copyright(c) 2018 Intel Corporation. + * + */ +#include "hfi.h" +#include "trace.h" +#include "qp.h" +#include "opfn.h" + +#define IB_BTHE_E BIT(IB_BTHE_E_SHIFT) + +#define OPFN_CODE(code) BIT((code) - 1) +#define OPFN_MASK(code) OPFN_CODE(STL_VERBS_EXTD_##code) + +struct hfi1_opfn_type { + bool (*request)(struct rvt_qp *qp, u64 *data); + bool (*response)(struct rvt_qp *qp, u64 *data); + bool (*reply)(struct rvt_qp *qp, u64 data); + void (*error)(struct rvt_qp *qp); +}; + +static struct hfi1_opfn_type hfi1_opfn_handlers[STL_VERBS_EXTD_MAX] = { + [STL_VERBS_EXTD_TID_RDMA] = { + .request = tid_rdma_conn_req, + .response = tid_rdma_conn_resp, + .reply = tid_rdma_conn_reply, + .error = tid_rdma_conn_error, + }, +}; + +static struct workqueue_struct *opfn_wq; + +static void opfn_schedule_conn_request(struct rvt_qp *qp); + +static bool hfi1_opfn_extended(u32 bth1) +{ + return !!(bth1 & IB_BTHE_E); +} + +static void opfn_conn_request(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct ib_atomic_wr wr; + u16 mask, capcode; + struct hfi1_opfn_type *extd; + u64 data; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&priv->opfn.lock, flags); + /* + * Exit if the extended bit is not set, or if nothing is requested, or + * if we have completed all requests, or if a previous request is in + * progress + */ + if (!priv->opfn.extended || !priv->opfn.requested || + priv->opfn.requested == priv->opfn.completed || priv->opfn.curr) + goto done; + + mask = priv->opfn.requested & ~priv->opfn.completed; + capcode = ilog2(mask & ~(mask - 1)) + 1; + if (capcode >= STL_VERBS_EXTD_MAX) { + priv->opfn.completed |= OPFN_CODE(capcode); + goto done; + } + + extd = &hfi1_opfn_handlers[capcode]; + if (!extd || !extd->request || !extd->request(qp, &data)) { + /* + * Either there is no handler for this capability or the request + * packet could not be generated. Either way, mark it as done so + * we don't keep attempting to complete it. + */ + priv->opfn.completed |= OPFN_CODE(capcode); + goto done; + } + + data = (data & ~0xf) | capcode; + + memset(&wr, 0, sizeof(wr)); + wr.wr.opcode = IB_WR_OPFN; + wr.remote_addr = HFI1_VERBS_E_ATOMIC_VADDR; + wr.compare_add = data; + + priv->opfn.curr = capcode; /* A new request is now in progress */ + /* Drop opfn.lock before calling ib_post_send() */ + spin_unlock_irqrestore(&priv->opfn.lock, flags); + + ret = ib_post_send(&qp->ibqp, &wr.wr, NULL); + if (ret) + goto err; + return; +err: + spin_lock_irqsave(&priv->opfn.lock, flags); + /* + * In case of an unexpected error return from ib_post_send + * clear opfn.curr and reschedule to try again + */ + priv->opfn.curr = STL_VERBS_EXTD_NONE; + opfn_schedule_conn_request(qp); +done: + spin_unlock_irqrestore(&priv->opfn.lock, flags); +} + +void opfn_send_conn_request(struct work_struct *work) +{ + struct hfi1_opfn_data *od; + struct hfi1_qp_priv *qpriv; + + od = container_of(work, struct hfi1_opfn_data, opfn_work); + qpriv = container_of(od, struct hfi1_qp_priv, opfn); + + opfn_conn_request(qpriv->owner); +} + +/* + * When QP s_lock is held in the caller, the OPFN request must be scheduled + * to a different workqueue to avoid double locking QP s_lock in call to + * ib_post_send in opfn_conn_request + */ +static void opfn_schedule_conn_request(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + + queue_work(opfn_wq, &priv->opfn.opfn_work); +} + +void opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_atomic_eth *ateth) +{ + struct hfi1_qp_priv *priv = qp->priv; + u64 data = be64_to_cpu(ateth->compare_data); + struct hfi1_opfn_type *extd; + u8 capcode; + unsigned long flags; + + capcode = data & 0xf; + if (!capcode || capcode >= STL_VERBS_EXTD_MAX) + return; + + extd = &hfi1_opfn_handlers[capcode]; + + if (!extd || !extd->response) { + e->atomic_data = capcode; + return; + } + + spin_lock_irqsave(&priv->opfn.lock, flags); + if (priv->opfn.completed & OPFN_CODE(capcode)) { + /* + * We are receiving a request for a feature that has already + * been negotiated. This may mean that the other side has reset + */ + priv->opfn.completed &= ~OPFN_CODE(capcode); + if (extd->error) + extd->error(qp); + } + + if (extd->response(qp, &data)) + priv->opfn.completed |= OPFN_CODE(capcode); + e->atomic_data = (data & ~0xf) | capcode; + spin_unlock_irqrestore(&priv->opfn.lock, flags); +} + +void opfn_conn_reply(struct rvt_qp *qp, u64 data) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_opfn_type *extd; + u8 capcode; + unsigned long flags; + + capcode = data & 0xf; + if (!capcode || capcode >= STL_VERBS_EXTD_MAX) + return; + + spin_lock_irqsave(&priv->opfn.lock, flags); + /* + * Either there is no previous request or the reply is not for the + * current request + */ + if (!priv->opfn.curr || capcode != priv->opfn.curr) + goto done; + + extd = &hfi1_opfn_handlers[capcode]; + + if (!extd || !extd->reply) + goto clear; + + if (extd->reply(qp, data)) + priv->opfn.completed |= OPFN_CODE(capcode); +clear: + /* + * Clear opfn.curr to indicate that the previous request is no longer in + * progress + */ + priv->opfn.curr = STL_VERBS_EXTD_NONE; +done: + spin_unlock_irqrestore(&priv->opfn.lock, flags); +} + +void opfn_conn_error(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_opfn_type *extd = NULL; + unsigned long flags; + u16 capcode; + + /* + * The QP has gone into the Error state. We have to invalidate all + * negotiated feature, including the one in progress (if any). The RC + * QP handling will clean the WQE for the connection request. + */ + spin_lock_irqsave(&priv->opfn.lock, flags); + while (priv->opfn.completed) { + capcode = priv->opfn.completed & ~(priv->opfn.completed - 1); + extd = &hfi1_opfn_handlers[ilog2(capcode) + 1]; + if (extd->error) + extd->error(qp); + priv->opfn.completed &= ~OPFN_CODE(capcode); + } + priv->opfn.extended = 0; + priv->opfn.requested = 0; + priv->opfn.curr = STL_VERBS_EXTD_NONE; + spin_unlock_irqrestore(&priv->opfn.lock, flags); +} + +void opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask) +{ + struct ib_qp *ibqp = &qp->ibqp; + struct hfi1_qp_priv *priv = qp->priv; + unsigned long flags; + + spin_lock_irqsave(&priv->opfn.lock, flags); + if (ibqp->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { + struct tid_rdma_params *local = &priv->tid_rdma.local; + + if (qp->pmtu == enum_to_mtu(OPA_MTU_4096) || + qp->pmtu == enum_to_mtu(OPA_MTU_8192)) { + tid_rdma_opfn_init(qp, local); + /* + * We only want to set the OPFN requested bit when the + * QP transitions to RTS. + */ + if (attr_mask & IB_QP_STATE && + attr->qp_state == IB_QPS_RTS) { + priv->opfn.requested |= OPFN_MASK(TID_RDMA); + /* + * If the QP is transitioning to RTS and the + * opfn.completed for TID RDMA has already been + * set, the QP is being moved *back* into RTS. + * We can now renegotiate the TID RDMA + * parameters. + */ + if (priv->opfn.completed & + OPFN_MASK(TID_RDMA)) { + priv->opfn.completed &= + ~OPFN_MASK(TID_RDMA); + /* + * Since the opfn.completed bit was + * already set, it is safe to assume + * that the opfn.extended is also set. + */ + opfn_schedule_conn_request(qp); + } + } + } else { + memset(local, 0, sizeof(*local)); + } + } + spin_unlock_irqrestore(&priv->opfn.lock, flags); +} + +void opfn_trigger_conn_request(struct rvt_qp *qp, u32 bth1) +{ + struct hfi1_qp_priv *priv = qp->priv; + + if (!priv->opfn.extended && hfi1_opfn_extended(bth1) && + HFI1_CAP_IS_KSET(OPFN)) { + priv->opfn.extended = 1; + if (qp->state == IB_QPS_RTS) + opfn_conn_request(qp); + } +} + +int opfn_init(void) +{ + opfn_wq = alloc_workqueue("hfi_opfn", + WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | + WQ_MEM_RECLAIM, + HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES); + if (!opfn_wq) + return -ENOMEM; + + return 0; +} + +void opfn_exit(void) +{ + if (opfn_wq) { + destroy_workqueue(opfn_wq); + opfn_wq = NULL; + } +} diff --git a/drivers/infiniband/hw/hfi1/opfn.h b/drivers/infiniband/hw/hfi1/opfn.h index 1927c9862b8f..5f2011cabc25 100644 --- a/drivers/infiniband/hw/hfi1/opfn.h +++ b/drivers/infiniband/hw/hfi1/opfn.h @@ -49,10 +49,37 @@ /* STL Verbs Extended */ #define IB_BTHE_E_SHIFT 24 +#define HFI1_VERBS_E_ATOMIC_VADDR U64_MAX -struct hfi1_opfn_data { - /* serialize opfn function calls */ - spinlock_t lock; +struct ib_atomic_eth; + +enum hfi1_opfn_codes { + STL_VERBS_EXTD_NONE = 0, + STL_VERBS_EXTD_TID_RDMA, + STL_VERBS_EXTD_MAX }; +struct hfi1_opfn_data { + u8 extended; + u16 requested; + u16 completed; + enum hfi1_opfn_codes curr; + /* serialize opfn function calls */ + spinlock_t lock; + struct work_struct opfn_work; +}; + +/* WR opcode for OPFN */ +#define IB_WR_OPFN IB_WR_RESERVED3 + +void opfn_send_conn_request(struct work_struct *work); +void opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_atomic_eth *ateth); +void opfn_conn_reply(struct rvt_qp *qp, u64 data); +void opfn_conn_error(struct rvt_qp *qp); +void opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask); +void opfn_trigger_conn_request(struct rvt_qp *qp, u32 bth1); +int opfn_init(void); +void opfn_exit(void); + #endif /* _HFI1_OPFN_H */ From ddf922c31fedd19c5b89a269c35e5c8b68c64327 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:21:01 -0800 Subject: [PATCH 0846/4277] IB/hfi1, IB/rdmavt: Allow for extending of QP's s_ack_queue The OPFN protocol uses the COMPARE_SWAP request to exchange data between the requester and the responder and therefore needs to be stored in the QP's s_ack_queue when the request is received on the responder side. However, because the user does not know anything about the OPFN protocol, this extra entry in the queue cannot be advertised to the user. This patch adds an extra entry in a QP's s_ack_queue. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 13 +++++++------ drivers/infiniband/hw/hfi1/verbs.c | 1 + include/rdma/rdma_vt.h | 10 +++++++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 940e9236c328..8970fc7ffd4b 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -122,7 +122,8 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, * response has been sent instead of only being * constructed. */ - if (++qp->s_tail_ack_queue > HFI1_MAX_RDMA_ATOMIC) + if (++qp->s_tail_ack_queue > + rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) qp->s_tail_ack_queue = 0; /* FALLTHROUGH */ case OP(SEND_ONLY): @@ -1818,7 +1819,7 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, if (i) prev = i - 1; else - prev = HFI1_MAX_RDMA_ATOMIC; + prev = rvt_size_atomic(ib_to_rvt(qp->ibqp.device)); if (prev == qp->r_head_ack_queue) { e = NULL; break; @@ -1942,7 +1943,7 @@ static inline void update_ack_queue(struct rvt_qp *qp, unsigned n) unsigned next; next = n + 1; - if (next > HFI1_MAX_RDMA_ATOMIC) + if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) next = 0; qp->s_tail_ack_queue = next; qp->s_ack_state = OP(ACKNOWLEDGE); @@ -2298,8 +2299,8 @@ send_last: if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ))) goto nack_inv; next = qp->r_head_ack_queue + 1; - /* s_ack_queue is size HFI1_MAX_RDMA_ATOMIC+1 so use > not >= */ - if (next > HFI1_MAX_RDMA_ATOMIC) + /* s_ack_queue is size rvt_size_atomic()+1 so use > not >= */ + if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) next = 0; spin_lock_irqsave(&qp->s_lock, flags); if (unlikely(next == qp->s_tail_ack_queue)) { @@ -2373,7 +2374,7 @@ send_last: if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC))) goto nack_inv; next = qp->r_head_ack_queue + 1; - if (next > HFI1_MAX_RDMA_ATOMIC) + if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) next = 0; spin_lock_irqsave(&qp->s_lock, flags); if (unlikely(next == qp->s_tail_ack_queue)) { diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index c980345cf1e1..ec3899c0874c 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -1735,6 +1735,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) dd->verbs_dev.rdi.dparms.sge_copy_mode = sge_copy_mode; dd->verbs_dev.rdi.dparms.wss_threshold = wss_threshold; dd->verbs_dev.rdi.dparms.wss_clean_period = wss_clean_period; + dd->verbs_dev.rdi.dparms.extra_rdma_atomic = 1; /* post send table */ dd->verbs_dev.rdi.post_parms = hfi1_post_parms; diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index acb3bc96dfa7..168e40be183c 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -182,6 +182,7 @@ struct rvt_driver_params { u32 max_mad_size; u8 qos_shift; u8 max_rdma_atomic; + u8 extra_rdma_atomic; u8 reserved_operations; }; @@ -519,7 +520,14 @@ static inline unsigned rvt_get_npkeys(struct rvt_dev_info *rdi) */ static inline unsigned int rvt_max_atomic(struct rvt_dev_info *rdi) { - return rdi->dparms.max_rdma_atomic + 1; + return rdi->dparms.max_rdma_atomic + + rdi->dparms.extra_rdma_atomic + 1; +} + +static inline unsigned int rvt_size_atomic(struct rvt_dev_info *rdi) +{ + return rdi->dparms.max_rdma_atomic + + rdi->dparms.extra_rdma_atomic; } /* From 48a615dc00aed68d58244b835b10eb3244aae31d Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:21:11 -0800 Subject: [PATCH 0847/4277] IB/hfi1: Integrate OPFN into RC transactions OPFN parameter negotiation allows a pair of connected RC QPs to exchange a set of parameters in succession. This negotiation does not commence till the first ULP request. Because OPFN operations are operations private to the driver, they do not generate user completions or put the QP into error when they run out of retries. This patch integrates the OPFN protocol into the transactions of an RC QP. Reviewed-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/init.c | 7 ++++ drivers/infiniband/hw/hfi1/qp.c | 13 ++++++ drivers/infiniband/hw/hfi1/rc.c | 58 ++++++++++++++++++++++----- drivers/infiniband/hw/hfi1/tid_rdma.c | 11 +++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 1 + drivers/infiniband/hw/hfi1/verbs.c | 1 + 6 files changed, 81 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 09c898d0975c..a8dbd0f191f5 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1498,6 +1498,12 @@ static int __init hfi1_mod_init(void) /* sanitize link CRC options */ link_crc_mask &= SUPPORTED_CRCS; + ret = opfn_init(); + if (ret < 0) { + pr_err("Failed to allocate opfn_wq"); + goto bail_dev; + } + /* * These must be called before the driver is registered with * the PCI subsystem. @@ -1528,6 +1534,7 @@ module_init(hfi1_mod_init); static void __exit hfi1_mod_cleanup(void) { pci_unregister_driver(&hfi1_pci_driver); + opfn_exit(); node_affinity_destroy_all(); hfi1_dbg_exit(); diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index 5344e8993b28..f822f92b415f 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -132,6 +132,12 @@ const struct rvt_operation_params hfi1_post_parms[RVT_OPERATION_MAX] = { .qpt_support = BIT(IB_QPT_RC), }, +[IB_WR_OPFN] = { + .length = sizeof(struct ib_atomic_wr), + .qpt_support = BIT(IB_QPT_RC), + .flags = RVT_OPERATION_USE_RESERVE, +}, + }; static void flush_list_head(struct list_head *l) @@ -285,6 +291,8 @@ void hfi1_modify_qp(struct rvt_qp *qp, struct ib_qp_attr *attr, priv->s_sendcontext = qp_to_send_context(qp, priv->s_sc); qp_set_16b(qp); } + + opfn_qp_init(qp, attr, attr_mask); } /** @@ -696,6 +704,7 @@ void qp_priv_free(struct rvt_dev_info *rdi, struct rvt_qp *qp) { struct hfi1_qp_priv *priv = qp->priv; + hfi1_qp_priv_tid_free(rdi, qp); kfree(priv->s_ahg); kfree(priv); } @@ -751,6 +760,10 @@ void notify_qp_reset(struct rvt_qp *qp) { qp->r_adefered = 0; clear_ahg(qp); + + /* Clear any OPFN state */ + if (qp->ibqp.qp_type == IB_QPT_RC) + opfn_conn_error(qp); } /* diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 8970fc7ffd4b..092d5eba980f 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -57,6 +57,10 @@ /* cut down ridiculously long IB macro names */ #define OP(x) RC_OP(x) +static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, + struct rvt_swqe *wqe, + struct hfi1_ibport *ibp); + static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 psn, u32 pmtu) { @@ -517,10 +521,14 @@ no_flow_control: goto bail; } qp->s_num_rd_atomic++; - if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) - qp->s_lsn++; } - if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) { + + /* FALLTHROUGH */ + case IB_WR_OPFN: + if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) + qp->s_lsn++; + if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || + wqe->wr.opcode == IB_WR_OPFN) { qp->s_state = OP(COMPARE_SWAP); put_ib_ateth_swap(wqe->atomic_wr.swap, &ohdr->u.atomic_eth); @@ -1040,6 +1048,7 @@ done: */ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) { + struct hfi1_qp_priv *priv = qp->priv; struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, qp->s_acked); struct hfi1_ibport *ibp; @@ -1050,8 +1059,26 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) hfi1_migrate_qp(qp); qp->s_retry = qp->s_retry_cnt; } else if (qp->s_last == qp->s_acked) { - rvt_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR); - rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); + /* + * We need special handling for the OPFN request WQEs as + * they are not allowed to generate real user errors + */ + if (wqe->wr.opcode == IB_WR_OPFN) { + struct hfi1_ibport *ibp = + to_iport(qp->ibqp.device, qp->port_num); + /* + * Call opfn_conn_reply() with capcode and + * remaining data as 0 to close out the + * current request + */ + opfn_conn_reply(qp, priv->opfn.curr); + wqe = do_rc_completion(qp, wqe, ibp); + qp->s_flags &= ~RVT_S_WAIT_ACK; + } else { + rvt_send_complete(qp, wqe, + IB_WC_RETRY_EXC_ERR); + rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); + } return; } else { /* need to handle delayed completion */ return; @@ -1363,6 +1390,9 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, u64 *vaddr = wqe->sg_list[0].vaddr; *vaddr = val; } + if (wqe->wr.opcode == IB_WR_OPFN) + opfn_conn_reply(qp, val); + if (qp->s_num_rd_atomic && (wqe->wr.opcode == IB_WR_RDMA_READ || wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || @@ -2068,6 +2098,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) return; fecn = process_ecn(qp, packet); + opfn_trigger_conn_request(qp, be32_to_cpu(ohdr->bth[1])); /* * Process responses (ACKs) before anything else. Note that the @@ -2363,15 +2394,18 @@ send_last: case OP(COMPARE_SWAP): case OP(FETCH_ADD): { - struct ib_atomic_eth *ateth; + struct ib_atomic_eth *ateth = &ohdr->u.atomic_eth; + u64 vaddr = get_ib_ateth_vaddr(ateth); + bool opfn = opcode == OP(COMPARE_SWAP) && + vaddr == HFI1_VERBS_E_ATOMIC_VADDR; struct rvt_ack_entry *e; - u64 vaddr; atomic64_t *maddr; u64 sdata; u32 rkey; u8 next; - if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC))) + if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC) && + !opfn)) goto nack_inv; next = qp->r_head_ack_queue + 1; if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) @@ -2387,8 +2421,11 @@ send_last: rvt_put_mr(e->rdma_sge.mr); e->rdma_sge.mr = NULL; } - ateth = &ohdr->u.atomic_eth; - vaddr = get_ib_ateth_vaddr(ateth); + /* Process OPFN special virtual address */ + if (opfn) { + opfn_conn_response(qp, e, ateth); + goto ack; + } if (unlikely(vaddr & (sizeof(u64) - 1))) goto nack_inv_unlck; rkey = be32_to_cpu(ateth->rkey); @@ -2407,6 +2444,7 @@ send_last: sdata); rvt_put_mr(qp->r_sge.sge.mr); qp->r_sge.num_sge = 0; +ack: e->opcode = opcode; e->sent = 0; e->psn = psn; diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index a8fd66f31fee..0c9f313d6229 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -246,5 +246,16 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->rcd = qp_to_rcd(rdi, qp); + spin_lock_init(&qpriv->opfn.lock); + INIT_WORK(&qpriv->opfn.opfn_work, opfn_send_conn_request); + return 0; } + +void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + + if (qp->ibqp.qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) + cancel_work_sync(&priv->opfn.opfn_work); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 18c6d4333f1e..ee8151558e3f 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -35,5 +35,6 @@ int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit); int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, struct ib_qp_init_attr *init_attr); +void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp); #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index ec3899c0874c..571bfd549c2a 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -1735,6 +1735,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) dd->verbs_dev.rdi.dparms.sge_copy_mode = sge_copy_mode; dd->verbs_dev.rdi.dparms.wss_threshold = wss_threshold; dd->verbs_dev.rdi.dparms.wss_clean_period = wss_clean_period; + dd->verbs_dev.rdi.dparms.reserved_operations = 1; dd->verbs_dev.rdi.dparms.extra_rdma_atomic = 1; /* post send table */ From a131d16460971353e7dd6916d9fd34c1c946a782 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Thu, 24 Jan 2019 06:10:09 -0800 Subject: [PATCH 0848/4277] IB/hfi1: Add static trace for OPFN This patch adds the static trace to the OPFN code and moves tid related static trace code into a new header file. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/opfn.c | 14 ++ drivers/infiniband/hw/hfi1/tid_rdma.c | 3 + drivers/infiniband/hw/hfi1/trace.h | 1 + drivers/infiniband/hw/hfi1/trace_rx.h | 107 +------- drivers/infiniband/hw/hfi1/trace_tid.h | 332 +++++++++++++++++++++++++ 5 files changed, 351 insertions(+), 106 deletions(-) create mode 100644 drivers/infiniband/hw/hfi1/trace_tid.h diff --git a/drivers/infiniband/hw/hfi1/opfn.c b/drivers/infiniband/hw/hfi1/opfn.c index 2d46c91eb129..2ca070690b2f 100644 --- a/drivers/infiniband/hw/hfi1/opfn.c +++ b/drivers/infiniband/hw/hfi1/opfn.c @@ -48,6 +48,7 @@ static void opfn_conn_request(struct rvt_qp *qp) unsigned long flags; int ret = 0; + trace_hfi1_opfn_state_conn_request(qp); spin_lock_irqsave(&priv->opfn.lock, flags); /* * Exit if the extended bit is not set, or if nothing is requested, or @@ -76,6 +77,7 @@ static void opfn_conn_request(struct rvt_qp *qp) goto done; } + trace_hfi1_opfn_data_conn_request(qp, capcode, data); data = (data & ~0xf) | capcode; memset(&wr, 0, sizeof(wr)); @@ -90,8 +92,11 @@ static void opfn_conn_request(struct rvt_qp *qp) ret = ib_post_send(&qp->ibqp, &wr.wr, NULL); if (ret) goto err; + trace_hfi1_opfn_state_conn_request(qp); return; err: + trace_hfi1_msg_opfn_conn_request(qp, "ib_ost_send failed: ret = ", + (u64)ret); spin_lock_irqsave(&priv->opfn.lock, flags); /* * In case of an unexpected error return from ib_post_send @@ -123,6 +128,7 @@ static void opfn_schedule_conn_request(struct rvt_qp *qp) { struct hfi1_qp_priv *priv = qp->priv; + trace_hfi1_opfn_state_sched_conn_request(qp); queue_work(opfn_wq, &priv->opfn.opfn_work); } @@ -135,7 +141,9 @@ void opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e, u8 capcode; unsigned long flags; + trace_hfi1_opfn_state_conn_response(qp); capcode = data & 0xf; + trace_hfi1_opfn_data_conn_response(qp, capcode, data); if (!capcode || capcode >= STL_VERBS_EXTD_MAX) return; @@ -160,6 +168,7 @@ void opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e, if (extd->response(qp, &data)) priv->opfn.completed |= OPFN_CODE(capcode); e->atomic_data = (data & ~0xf) | capcode; + trace_hfi1_opfn_state_conn_response(qp); spin_unlock_irqrestore(&priv->opfn.lock, flags); } @@ -170,7 +179,9 @@ void opfn_conn_reply(struct rvt_qp *qp, u64 data) u8 capcode; unsigned long flags; + trace_hfi1_opfn_state_conn_reply(qp); capcode = data & 0xf; + trace_hfi1_opfn_data_conn_reply(qp, capcode, data); if (!capcode || capcode >= STL_VERBS_EXTD_MAX) return; @@ -195,6 +206,7 @@ clear: * progress */ priv->opfn.curr = STL_VERBS_EXTD_NONE; + trace_hfi1_opfn_state_conn_reply(qp); done: spin_unlock_irqrestore(&priv->opfn.lock, flags); } @@ -206,6 +218,8 @@ void opfn_conn_error(struct rvt_qp *qp) unsigned long flags; u16 capcode; + trace_hfi1_opfn_state_conn_error(qp); + trace_hfi1_msg_opfn_conn_error(qp, "error. qp state ", (u64)qp->state); /* * The QP has gone into the Error state. We have to invalidate all * negotiated feature, including the one in progress (if any). The RC diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 0c9f313d6229..e8f57c0cd8bc 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -7,6 +7,7 @@ #include "hfi.h" #include "verbs.h" #include "tid_rdma.h" +#include "trace.h" /* * J_KEY for kernel contexts when TID RDMA is used. @@ -148,6 +149,8 @@ bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data) priv->tid_timer_timeout_jiffies = usecs_to_jiffies((((4096UL * (1UL << remote->timeout)) / 1000UL) << 3) * 7); + trace_hfi1_opfn_param(qp, 0, &priv->tid_rdma.local); + trace_hfi1_opfn_param(qp, 1, remote); rcu_assign_pointer(priv->tid_rdma.remote, remote); /* * A TID RDMA READ request's segment size is not equal to diff --git a/drivers/infiniband/hw/hfi1/trace.h b/drivers/infiniband/hw/hfi1/trace.h index 84458f1325e1..1ce551864118 100644 --- a/drivers/infiniband/hw/hfi1/trace.h +++ b/drivers/infiniband/hw/hfi1/trace.h @@ -63,3 +63,4 @@ __print_symbolic(etype, \ #include "trace_tx.h" #include "trace_mmu.h" #include "trace_iowait.h" +#include "trace_tid.h" diff --git a/drivers/infiniband/hw/hfi1/trace_rx.h b/drivers/infiniband/hw/hfi1/trace_rx.h index 7eceb57e0415..3cec960e9674 100644 --- a/drivers/infiniband/hw/hfi1/trace_rx.h +++ b/drivers/infiniband/hw/hfi1/trace_rx.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2015 - 2017 Intel Corporation. + * Copyright(c) 2015 - 2018 Intel Corporation. * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -128,111 +128,6 @@ TRACE_EVENT(hfi1_receive_interrupt, ) ); -DECLARE_EVENT_CLASS( - hfi1_exp_tid_reg_unreg, - TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, - u32 npages, unsigned long va, unsigned long pa, - dma_addr_t dma), - TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma), - TP_STRUCT__entry( - __field(unsigned int, ctxt) - __field(u16, subctxt) - __field(u32, rarr) - __field(u32, npages) - __field(unsigned long, va) - __field(unsigned long, pa) - __field(dma_addr_t, dma) - ), - TP_fast_assign( - __entry->ctxt = ctxt; - __entry->subctxt = subctxt; - __entry->rarr = rarr; - __entry->npages = npages; - __entry->va = va; - __entry->pa = pa; - __entry->dma = dma; - ), - TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx, va:0x%lx dma:0x%llx", - __entry->ctxt, - __entry->subctxt, - __entry->rarr, - __entry->npages, - __entry->pa, - __entry->va, - __entry->dma - ) - ); - -DEFINE_EVENT( - hfi1_exp_tid_reg_unreg, hfi1_exp_tid_unreg, - TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, - unsigned long va, unsigned long pa, dma_addr_t dma), - TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma)); - -DEFINE_EVENT( - hfi1_exp_tid_reg_unreg, hfi1_exp_tid_reg, - TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, - unsigned long va, unsigned long pa, dma_addr_t dma), - TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma)); - -TRACE_EVENT( - hfi1_put_tid, - TP_PROTO(struct hfi1_devdata *dd, - u32 index, u32 type, unsigned long pa, u16 order), - TP_ARGS(dd, index, type, pa, order), - TP_STRUCT__entry( - DD_DEV_ENTRY(dd) - __field(unsigned long, pa); - __field(u32, index); - __field(u32, type); - __field(u16, order); - ), - TP_fast_assign( - DD_DEV_ASSIGN(dd); - __entry->pa = pa; - __entry->index = index; - __entry->type = type; - __entry->order = order; - ), - TP_printk("[%s] type %s pa %lx index %u order %u", - __get_str(dev), - show_tidtype(__entry->type), - __entry->pa, - __entry->index, - __entry->order - ) -); - -TRACE_EVENT(hfi1_exp_tid_inval, - TP_PROTO(unsigned int ctxt, u16 subctxt, unsigned long va, u32 rarr, - u32 npages, dma_addr_t dma), - TP_ARGS(ctxt, subctxt, va, rarr, npages, dma), - TP_STRUCT__entry( - __field(unsigned int, ctxt) - __field(u16, subctxt) - __field(unsigned long, va) - __field(u32, rarr) - __field(u32, npages) - __field(dma_addr_t, dma) - ), - TP_fast_assign( - __entry->ctxt = ctxt; - __entry->subctxt = subctxt; - __entry->va = va; - __entry->rarr = rarr; - __entry->npages = npages; - __entry->dma = dma; - ), - TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx dma: 0x%llx", - __entry->ctxt, - __entry->subctxt, - __entry->rarr, - __entry->npages, - __entry->va, - __entry->dma - ) - ); - TRACE_EVENT(hfi1_mmu_invalidate, TP_PROTO(unsigned int ctxt, u16 subctxt, const char *type, unsigned long start, unsigned long end), diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h new file mode 100644 index 000000000000..57a973c97cde --- /dev/null +++ b/drivers/infiniband/hw/hfi1/trace_tid.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2018 Intel Corporation. + * + */ +#if !defined(__HFI1_TRACE_TID_H) || defined(TRACE_HEADER_MULTI_READ) +#define __HFI1_TRACE_TID_H + +#include +#include + +#include "hfi.h" + +#define tidtype_name(type) { PT_##type, #type } +#define show_tidtype(type) \ +__print_symbolic(type, \ + tidtype_name(EXPECTED), \ + tidtype_name(EAGER), \ + tidtype_name(INVALID)) \ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM hfi1_tid + +#define OPFN_PARAM_PRN "[%s] qpn 0x%x %s OPFN: qp 0x%x, max read %u, " \ + "max write %u, max length %u, jkey 0x%x timeout %u " \ + "urg %u" + +DECLARE_EVENT_CLASS(/* class */ + hfi1_exp_tid_reg_unreg, + TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, + unsigned long va, unsigned long pa, dma_addr_t dma), + TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma), + TP_STRUCT__entry(/* entry */ + __field(unsigned int, ctxt) + __field(u16, subctxt) + __field(u32, rarr) + __field(u32, npages) + __field(unsigned long, va) + __field(unsigned long, pa) + __field(dma_addr_t, dma) + ), + TP_fast_assign(/* assign */ + __entry->ctxt = ctxt; + __entry->subctxt = subctxt; + __entry->rarr = rarr; + __entry->npages = npages; + __entry->va = va; + __entry->pa = pa; + __entry->dma = dma; + ), + TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx, va:0x%lx dma:0x%llx", + __entry->ctxt, + __entry->subctxt, + __entry->rarr, + __entry->npages, + __entry->pa, + __entry->va, + __entry->dma + ) +); + +DEFINE_EVENT(/* exp_tid_unreg */ + hfi1_exp_tid_reg_unreg, hfi1_exp_tid_unreg, + TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, + unsigned long va, unsigned long pa, dma_addr_t dma), + TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma) +); + +DEFINE_EVENT(/* exp_tid_reg */ + hfi1_exp_tid_reg_unreg, hfi1_exp_tid_reg, + TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, + unsigned long va, unsigned long pa, dma_addr_t dma), + TP_ARGS(ctxt, subctxt, rarr, npages, va, pa, dma) +); + +TRACE_EVENT(/* put_tid */ + hfi1_put_tid, + TP_PROTO(struct hfi1_devdata *dd, + u32 index, u32 type, unsigned long pa, u16 order), + TP_ARGS(dd, index, type, pa, order), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd) + __field(unsigned long, pa); + __field(u32, index); + __field(u32, type); + __field(u16, order); + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd); + __entry->pa = pa; + __entry->index = index; + __entry->type = type; + __entry->order = order; + ), + TP_printk("[%s] type %s pa %lx index %u order %u", + __get_str(dev), + show_tidtype(__entry->type), + __entry->pa, + __entry->index, + __entry->order + ) +); + +TRACE_EVENT(/* exp_tid_inval */ + hfi1_exp_tid_inval, + TP_PROTO(unsigned int ctxt, u16 subctxt, unsigned long va, u32 rarr, + u32 npages, dma_addr_t dma), + TP_ARGS(ctxt, subctxt, va, rarr, npages, dma), + TP_STRUCT__entry(/* entry */ + __field(unsigned int, ctxt) + __field(u16, subctxt) + __field(unsigned long, va) + __field(u32, rarr) + __field(u32, npages) + __field(dma_addr_t, dma) + ), + TP_fast_assign(/* assign */ + __entry->ctxt = ctxt; + __entry->subctxt = subctxt; + __entry->va = va; + __entry->rarr = rarr; + __entry->npages = npages; + __entry->dma = dma; + ), + TP_printk("[%u:%u] entry:%u, %u pages @ 0x%lx dma: 0x%llx", + __entry->ctxt, + __entry->subctxt, + __entry->rarr, + __entry->npages, + __entry->va, + __entry->dma + ) +); + +DECLARE_EVENT_CLASS(/* opfn_state */ + hfi1_opfn_state_template, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u16, requested) + __field(u16, completed) + __field(u8, curr) + ), + TP_fast_assign(/* assign */ + struct hfi1_qp_priv *priv = qp->priv; + + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->requested = priv->opfn.requested; + __entry->completed = priv->opfn.completed; + __entry->curr = priv->opfn.curr; + ), + TP_printk(/* print */ + "[%s] qpn 0x%x requested 0x%x completed 0x%x curr 0x%x", + __get_str(dev), + __entry->qpn, + __entry->requested, + __entry->completed, + __entry->curr + ) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_state_template, hfi1_opfn_state_conn_request, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_state_template, hfi1_opfn_state_sched_conn_request, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_state_template, hfi1_opfn_state_conn_response, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_state_template, hfi1_opfn_state_conn_reply, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_state_template, hfi1_opfn_state_conn_error, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DECLARE_EVENT_CLASS(/* opfn_data */ + hfi1_opfn_data_template, + TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data), + TP_ARGS(qp, capcode, data), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u32, state) + __field(u8, capcode) + __field(u64, data) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->state = qp->state; + __entry->capcode = capcode; + __entry->data = data; + ), + TP_printk(/* printk */ + "[%s] qpn 0x%x (state 0x%x) Capcode %u data 0x%llx", + __get_str(dev), + __entry->qpn, + __entry->state, + __entry->capcode, + __entry->data + ) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_data_template, hfi1_opfn_data_conn_request, + TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data), + TP_ARGS(qp, capcode, data) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_data_template, hfi1_opfn_data_conn_response, + TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data), + TP_ARGS(qp, capcode, data) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_data_template, hfi1_opfn_data_conn_reply, + TP_PROTO(struct rvt_qp *qp, u8 capcode, u64 data), + TP_ARGS(qp, capcode, data) +); + +DECLARE_EVENT_CLASS(/* opfn_param */ + hfi1_opfn_param_template, + TP_PROTO(struct rvt_qp *qp, char remote, + struct tid_rdma_params *param), + TP_ARGS(qp, remote, param), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(char, remote) + __field(u32, param_qp) + __field(u32, max_len) + __field(u16, jkey) + __field(u8, max_read) + __field(u8, max_write) + __field(u8, timeout) + __field(u8, urg) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->remote = remote; + __entry->param_qp = param->qp; + __entry->max_len = param->max_len; + __entry->jkey = param->jkey; + __entry->max_read = param->max_read; + __entry->max_write = param->max_write; + __entry->timeout = param->timeout; + __entry->urg = param->urg; + ), + TP_printk(/* print */ + OPFN_PARAM_PRN, + __get_str(dev), + __entry->qpn, + __entry->remote ? "remote" : "local", + __entry->param_qp, + __entry->max_read, + __entry->max_write, + __entry->max_len, + __entry->jkey, + __entry->timeout, + __entry->urg + ) +); + +DEFINE_EVENT(/* event */ + hfi1_opfn_param_template, hfi1_opfn_param, + TP_PROTO(struct rvt_qp *qp, char remote, + struct tid_rdma_params *param), + TP_ARGS(qp, remote, param) +); + +DECLARE_EVENT_CLASS(/* msg */ + hfi1_msg_template, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more), + TP_STRUCT__entry(/* entry */ + __field(u32, qpn) + __string(msg, msg) + __field(u64, more) + ), + TP_fast_assign(/* assign */ + __entry->qpn = qp ? qp->ibqp.qp_num : 0; + __assign_str(msg, msg); + __entry->more = more; + ), + TP_printk(/* print */ + "qpn 0x%x %s 0x%llx", + __entry->qpn, + __get_str(msg), + __entry->more + ) +); + +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_opfn_conn_request, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_opfn_conn_error, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + +#endif /* __HFI1_TRACE_TID_H */ + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace_tid +#include From 33d5c207a9ed16e72f49b77b9387aea5a8a42219 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 24 Jan 2019 13:59:55 +0530 Subject: [PATCH 0849/4277] dt-bindings: PCI: dra7xx: Add SoC specific compatible strings Add new compatible strings for dra74x SoC (also used by dra76x) and dra72x. This can be used to perform SoC specific configuration (like configuring PCIe in x2 lane mode). Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Sekhar Nori Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring --- Documentation/devicetree/bindings/pci/ti-pci.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt index 452fe48c4fdd..e03d23631f5b 100644 --- a/Documentation/devicetree/bindings/pci/ti-pci.txt +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt @@ -1,8 +1,12 @@ TI PCI Controllers PCIe DesignWare Controller - - compatible: Should be "ti,dra7-pcie" for RC - Should be "ti,dra7-pcie-ep" for EP + - compatible: Should be "ti,dra7-pcie" for RC (deprecated) + Should be "ti,dra7-pcie-ep" for EP (deprecated) + Should be "ti,dra746-pcie-rc" for dra74x/dra76 in RC mode + Should be "ti,dra746-pcie-ep" for dra74x/dra76 in EP mode + Should be "ti,dra726-pcie-rc" for dra72x in RC mode + Should be "ti,dra726-pcie-ep" for dra72x in EP mode - phys : list of PHY specifiers (used by generic PHY framework) - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the number of PHYs as specified in *phys* property. From 1c5d2cc7196c8c45c02114858efa4dca69a86583 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 24 Jan 2019 13:59:56 +0530 Subject: [PATCH 0850/4277] dt-bindings: PCI: dra7xx: Add properties to enable x2 lane in dra7 Add syscon properties required for configuring PCIe in x2 lane mode. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Sekhar Nori Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/pci/ti-pci.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt index e03d23631f5b..d5cbfe6b0d89 100644 --- a/Documentation/devicetree/bindings/pci/ti-pci.txt +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt @@ -13,6 +13,9 @@ PCIe DesignWare Controller - ti,hwmods : Name of the hwmod associated to the pcie, "pcie", where is the instance number of the pcie from the HW spec. - num-lanes as specified in ../designware-pcie.txt + - ti,syscon-lane-sel : phandle/offset pair. Phandle to the system control + module and the register offset to specify lane + selection. HOST MODE ========= From c232c0df9610bf2055ff002ecafb4915f0aff528 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 24 Jan 2019 13:59:57 +0530 Subject: [PATCH 0851/4277] PCI: dwc: dra7xx: Enable x2 mode support for dra74x, dra76x and dra72x dra74x/dra76x and dra72x have separate compatible strings. Add support for these compatible strings in pci-dra7xx driver to perform syscon configurations required to get x2 mode working. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index a32d6dde7a57..5c72acb49c11 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -81,6 +81,10 @@ #define MSI_REQ_GRANT BIT(0) #define MSI_VECTOR_SHIFT 7 +#define PCIE_1LANE_2LANE_SELECTION BIT(13) +#define PCIE_B1C0_MODE_SEL BIT(2) +#define PCIE_B0_B1_TSYNCEN BIT(0) + struct dra7xx_pcie { struct dw_pcie *pci; void __iomem *base; /* DT ti_conf */ @@ -93,6 +97,7 @@ struct dra7xx_pcie { struct dra7xx_pcie_of_data { enum dw_pcie_device_mode mode; + u32 b1co_mode_sel_mask; }; #define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev) @@ -529,6 +534,26 @@ static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = { .mode = DW_PCIE_EP_TYPE, }; +static const struct dra7xx_pcie_of_data dra746_pcie_rc_of_data = { + .b1co_mode_sel_mask = BIT(2), + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra726_pcie_rc_of_data = { + .b1co_mode_sel_mask = GENMASK(3, 2), + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra746_pcie_ep_of_data = { + .b1co_mode_sel_mask = BIT(2), + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra726_pcie_ep_of_data = { + .b1co_mode_sel_mask = GENMASK(3, 2), + .mode = DW_PCIE_EP_TYPE, +}; + static const struct of_device_id of_dra7xx_pcie_match[] = { { .compatible = "ti,dra7-pcie", @@ -538,6 +563,22 @@ static const struct of_device_id of_dra7xx_pcie_match[] = { .compatible = "ti,dra7-pcie-ep", .data = &dra7xx_pcie_ep_of_data, }, + { + .compatible = "ti,dra746-pcie-rc", + .data = &dra746_pcie_rc_of_data, + }, + { + .compatible = "ti,dra726-pcie-rc", + .data = &dra726_pcie_rc_of_data, + }, + { + .compatible = "ti,dra746-pcie-ep", + .data = &dra746_pcie_ep_of_data, + }, + { + .compatible = "ti,dra726-pcie-ep", + .data = &dra726_pcie_ep_of_data, + }, {}, }; @@ -583,6 +624,34 @@ static int dra7xx_pcie_unaligned_memaccess(struct device *dev) return ret; } +static int dra7xx_pcie_configure_two_lane(struct device *dev, + u32 b1co_mode_sel_mask) +{ + struct device_node *np = dev->of_node; + struct regmap *pcie_syscon; + unsigned int pcie_reg; + u32 mask; + u32 val; + + pcie_syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-lane-sel"); + if (IS_ERR(pcie_syscon)) { + dev_err(dev, "unable to get ti,syscon-lane-sel\n"); + return -EINVAL; + } + + if (of_property_read_u32_index(np, "ti,syscon-lane-sel", 1, + &pcie_reg)) { + dev_err(dev, "couldn't get lane selection reg offset\n"); + return -EINVAL; + } + + mask = b1co_mode_sel_mask | PCIE_B0_B1_TSYNCEN; + val = PCIE_B1C0_MODE_SEL | PCIE_B0_B1_TSYNCEN; + regmap_update_bits(pcie_syscon, pcie_reg, mask, val); + + return 0; +} + static int __init dra7xx_pcie_probe(struct platform_device *pdev) { u32 reg; @@ -603,6 +672,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) const struct of_device_id *match; const struct dra7xx_pcie_of_data *data; enum dw_pcie_device_mode mode; + u32 b1co_mode_sel_mask; match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev); if (!match) @@ -610,6 +680,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) data = (struct dra7xx_pcie_of_data *)match->data; mode = (enum dw_pcie_device_mode)data->mode; + b1co_mode_sel_mask = data->b1co_mode_sel_mask; dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); if (!dra7xx) @@ -665,6 +736,12 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) dra7xx->pci = pci; dra7xx->phy_count = phy_count; + if (phy_count == 2) { + ret = dra7xx_pcie_configure_two_lane(dev, b1co_mode_sel_mask); + if (ret < 0) + dra7xx->phy_count = 1; /* Fallback to x1 lane mode */ + } + ret = dra7xx_pcie_enable_phy(dra7xx); if (ret) { dev_err(dev, "failed to enable phy\n"); From 65c839a134e6afca13822eeb6e22abea37f86e76 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 24 Jan 2019 16:15:37 +0530 Subject: [PATCH 0852/4277] PCI: dwc: dra7xx: Invoke phy_set_mode() API to set PHY mode to PHY_MODE_PCIE Certain PHYs used with PCIe controller can also be used with other controllers such as USB or SATA. In order to configure the PHY to work with PCIe controller, invoke phy_set_mode() API with mode set to PHY_MODE_PCIE. Signed-off-by: Kishon Vijay Abraham I [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 5c72acb49c11..5d5844fc143e 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -504,6 +504,10 @@ static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx) int i; for (i = 0; i < phy_count; i++) { + ret = phy_set_mode(dra7xx->phy[i], PHY_MODE_PCIE); + if (ret < 0) + goto err_phy; + ret = phy_init(dra7xx->phy[i]); if (ret < 0) goto err_phy; From e2a2e56e40822ab78e304198387f61314af7d7ce Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 15:41:11 +0100 Subject: [PATCH 0853/4277] arm64: dump: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Will Deacon Cc: Marc Zyngier Cc: Peng Donglin Cc: Signed-off-by: Greg Kroah-Hartman Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/ptdump.h | 9 +++------ arch/arm64/mm/dump.c | 4 ++-- arch/arm64/mm/ptdump_debugfs.c | 7 ++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h index 6afd8476c60c..9e948a93d26c 100644 --- a/arch/arm64/include/asm/ptdump.h +++ b/arch/arm64/include/asm/ptdump.h @@ -34,13 +34,10 @@ struct ptdump_info { void ptdump_walk_pgd(struct seq_file *s, struct ptdump_info *info); #ifdef CONFIG_ARM64_PTDUMP_DEBUGFS -int ptdump_debugfs_register(struct ptdump_info *info, const char *name); +void ptdump_debugfs_register(struct ptdump_info *info, const char *name); #else -static inline int ptdump_debugfs_register(struct ptdump_info *info, - const char *name) -{ - return 0; -} +static inline void ptdump_debugfs_register(struct ptdump_info *info, + const char *name) { } #endif void ptdump_check_wx(void); #endif /* CONFIG_ARM64_PTDUMP_CORE */ diff --git a/arch/arm64/mm/dump.c b/arch/arm64/mm/dump.c index fcb1f2a6d7c6..08c250350b8a 100644 --- a/arch/arm64/mm/dump.c +++ b/arch/arm64/mm/dump.c @@ -407,7 +407,7 @@ void ptdump_check_wx(void) static int ptdump_init(void) { ptdump_initialize(); - return ptdump_debugfs_register(&kernel_ptdump_info, - "kernel_page_tables"); + ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables"); + return 0; } device_initcall(ptdump_init); diff --git a/arch/arm64/mm/ptdump_debugfs.c b/arch/arm64/mm/ptdump_debugfs.c index 24d786fc3a4c..064163f25592 100644 --- a/arch/arm64/mm/ptdump_debugfs.c +++ b/arch/arm64/mm/ptdump_debugfs.c @@ -12,10 +12,7 @@ static int ptdump_show(struct seq_file *m, void *v) } DEFINE_SHOW_ATTRIBUTE(ptdump); -int ptdump_debugfs_register(struct ptdump_info *info, const char *name) +void ptdump_debugfs_register(struct ptdump_info *info, const char *name) { - struct dentry *pe; - pe = debugfs_create_file(name, 0400, NULL, info, &ptdump_fops); - return pe ? 0 : -ENOMEM; - + debugfs_create_file(name, 0400, NULL, info, &ptdump_fops); } From 8aa153f103f10807941bd95dda95071380609e05 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jan 2019 14:00:15 -0200 Subject: [PATCH 0854/4277] media: vim2m: fix driver for it to handle different fourcc formats Despite vim2m is reporting that it supports RGB565BE and YUYV, that's not true. Right now, it just says that it supports both format, but it doesn't actually support them. Also, horizontal flip is not properly implemented. It sounds that it was designed to do a pseudo-horizontal flip using 8 tiles. Yet, as it doesn't do format conversion, the result is a mess. I suspect that it was done this way in order to save CPU time, at the time of OMAP2 days. That's messy and doesn't really help if someone wants to use vim2m to test a pipeline. Worse than that, the unique RGB format it says it supports is RGB565BE, with is not supported by Gstreamer. That prevents practical usage of it, even for tests. So, instead, properly implement fourcc format conversions, adding a few more RGB formats: - RGB and BGR with 24 bits - RGB565LE (known as RGB16 at gstreamer) Also allows using any of the 5 supported formats as either capture or output. Note: The YUYV conversion routines are based on the conversion code written by Hans de Goede inside libv4lconvert (part of v4l-utils), released under LGPGL 2.1 (GPL 2.0 compatible). Tested all possible format combinations except for RGB565BE, as Gstreamer currently doesn't support it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 380 +++++++++++++++++++++------------ 1 file changed, 240 insertions(+), 140 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 924120d69da2..036d695eeebe 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -60,8 +60,6 @@ MODULE_PARM_DESC(debug, "activates debug info"); /* Default transaction time in msec */ #define MEM2MEM_DEF_TRANSTIME 40 -#define MEM2MEM_COLOR_STEP (0xff >> 4) -#define MEM2MEM_NUM_TILES 8 /* Flags that indicate processing mode */ #define MEM2MEM_HFLIP (1 << 0) @@ -82,22 +80,24 @@ static struct platform_device vim2m_pdev = { struct vim2m_fmt { u32 fourcc; int depth; - /* Types the format can be used for */ - u32 types; }; static struct vim2m_fmt formats[] = { { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */ .depth = 16, - /* Both capture and output format */ - .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, - }, - { + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */ + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + }, { .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, - /* Output-only format */ - .types = MEM2MEM_OUTPUT, }, }; @@ -201,23 +201,222 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, return NULL; } +#define CLIP(__color) \ + (u8)(((__color) > 0xff) ? 0xff : (((__color) < 0) ? 0 : (__color))) + +static void copy_two_pixels(struct vim2m_fmt *in, struct vim2m_fmt *out, + u8 **src, u8 **dst, bool reverse) +{ + u8 _r[2], _g[2], _b[2], *r, *g, *b; + int i, step; + + // If format is the same just copy the data, respecting the width + if (in->fourcc == out->fourcc) { + int depth = out->depth >> 3; + + if (reverse) { + if (in->fourcc == V4L2_PIX_FMT_YUYV) { + int u, v, y, y1; + + *src -= 2; + + y1 = (*src)[0]; /* copy as second point */ + u = (*src)[1]; + y = (*src)[2]; /* copy as first point */ + v = (*src)[3]; + + *src -= 2; + + *(*dst)++ = y; + *(*dst)++ = u; + *(*dst)++ = y1; + *(*dst)++ = v; + return; + } + + memcpy(*dst, *src, depth); + memcpy(*dst + depth, *src - depth, depth); + *src -= depth << 1; + } else { + memcpy(*dst, *src, depth << 1); + *src += depth << 1; + } + *dst += depth << 1; + return; + } + + /* Step 1: read two consecutive pixels from src pointer */ + + r = _r; + g = _g; + b = _b; + + if (reverse) + step = -1; + else + step = 1; + + switch (in->fourcc) { + case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ + for (i = 0; i < 2; i++) { + u16 pix = *(u16 *)*src; + + *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07; + *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03; + *b++ = (u8)((pix & 0x1f) << 3) | 0x07; + + *src += step << 1; + } + break; + case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ + for (i = 0; i < 2; i++) { + u16 pix = *(u16 *)*src; + + *r++ = (u8)(((0x00f8 & pix) >> 3) << 3) | 0x07; + *g++ = (u8)(((pix & 0x7) << 2) | + ((pix & 0xe000) >> 5)) | 0x03; + *b++ = (u8)(((pix & 0x1f00) >> 8) << 3) | 0x07; + + *src += step << 1; + } + break; + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 2; i++) { + *r++ = (*src)[0]; + *g++ = (*src)[1]; + *b++ = (*src)[2]; + + *src += step * 3; + } + break; + case V4L2_PIX_FMT_BGR24: + for (i = 0; i < 2; i++) { + *b++ = (*src)[0]; + *g++ = (*src)[1]; + *r++ = (*src)[2]; + + *src += step * 3; + } + break; + default: /* V4L2_PIX_FMT_YUYV */ + { + int u, v, y, y1, u1, v1, tmp; + + if (reverse) { + *src -= 2; + + y1 = (*src)[0]; /* copy as second point */ + u = (*src)[1]; + y = (*src)[2]; /* copy as first point */ + v = (*src)[3]; + + *src -= 2; + } else { + y = *(*src)++; + u = *(*src)++; + y1 = *(*src)++; + v = *(*src)++; + } + + u1 = (((u - 128) << 7) + (u - 128)) >> 6; + tmp = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *r++ = CLIP(y + v1); + *g++ = CLIP(y - tmp); + *b++ = CLIP(y + u1); + + *r = CLIP(y1 + v1); + *g = CLIP(y1 - tmp); + *b = CLIP(y1 + u1); + break; + } + } + + /* Step 2: store two consecutive points, reversing them if needed */ + + r = _r; + g = _g; + b = _b; + + switch (out->fourcc) { + case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ + for (i = 0; i < 2; i++) { + u16 *pix = (u16 *)*dst; + + *pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) | + (*b >> 3); + + *dst += 2; + } + return; + case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ + for (i = 0; i < 2; i++) { + u16 *pix = (u16 *)*dst; + u8 green = *g++ >> 2; + + *pix = ((green << 8) & 0xe000) | (green & 0x07) | + ((*b++ << 5) & 0x1f00) | ((*r++ & 0xf8)); + + *dst += 2; + } + return; + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 2; i++) { + *(*dst)++ = *r++; + *(*dst)++ = *g++; + *(*dst)++ = *b++; + } + return; + case V4L2_PIX_FMT_BGR24: + for (i = 0; i < 2; i++) { + *(*dst)++ = *b++; + *(*dst)++ = *g++; + *(*dst)++ = *r++; + } + return; + default: /* V4L2_PIX_FMT_YUYV */ + { + u8 y, y1, u, v; + + y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) + + 524288) >> 15); + u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b) + + 4210688) >> 15); + v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++) + + 4210688) >> 15); + y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) + + 524288) >> 15); + + *(*dst)++ = y; + *(*dst)++ = u; + + *(*dst)++ = y1; + *(*dst)++ = v; + return; + } + } +} static int device_process(struct vim2m_ctx *ctx, struct vb2_v4l2_buffer *in_vb, struct vb2_v4l2_buffer *out_vb) { struct vim2m_dev *dev = ctx->dev; - struct vim2m_q_data *q_data; - u8 *p_in, *p_out; - int x, y, t, w; - int tile_w, bytes_left; - int width, height, bytesperline; + struct vim2m_q_data *q_data_in, *q_data_out; + u8 *p_in, *p, *p_out; + int width, height, bytesperline, x, y, start, end, step; + struct vim2m_fmt *in, *out; - q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + in = q_data_in->fmt; + width = q_data_in->width; + height = q_data_in->height; + bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3; - width = q_data->width; - height = q_data->height; - bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + out = q_data_out->fmt; p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); @@ -227,100 +426,28 @@ static int device_process(struct vim2m_ctx *ctx, return -EFAULT; } - if (vb2_plane_size(&in_vb->vb2_buf, 0) > - vb2_plane_size(&out_vb->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); - return -EINVAL; - } - - tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) - / MEM2MEM_NUM_TILES; - bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; - w = 0; - - out_vb->sequence = - get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; - in_vb->sequence = q_data->sequence++; + out_vb->sequence = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; + in_vb->sequence = q_data_in->sequence++; v4l2_m2m_buf_copy_data(in_vb, out_vb, true); - switch (ctx->mode) { - case MEM2MEM_HFLIP | MEM2MEM_VFLIP: - p_out += bytesperline * height - bytes_left; - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out -= bytes_left; - } - break; + if (ctx->mode & MEM2MEM_VFLIP) { + start = height - 1; + end = -1; + step = -1; + } else { + start = 0; + end = height; + step = 1; + } + for (y = start; y != end; y += step) { + p = p_in + (y * bytesperline); + if (ctx->mode & MEM2MEM_HFLIP) + p += bytesperline - (q_data_in->fmt->depth >> 3); - case MEM2MEM_HFLIP: - for (y = 0; y < height; ++y) { - p_out += MEM2MEM_NUM_TILES * tile_w; - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x01) { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytesperline; - } - break; - - case MEM2MEM_VFLIP: - p_out += bytesperline * (height - 1); - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytes_left - 2 * bytesperline; - } - break; - - default: - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytes_left; - } + for (x = 0; x < width >> 1; x++) + copy_two_pixels(in, out, &p, &p_out, + ctx->mode & MEM2MEM_HFLIP); } return 0; @@ -433,25 +560,11 @@ static int vidioc_querycap(struct file *file, void *priv, static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) { - int i, num; struct vim2m_fmt *fmt; - num = 0; - - for (i = 0; i < NUM_FORMATS; ++i) { - if (formats[i].types & type) { - /* index-th format of type type found ? */ - if (num == f->index) - break; - /* Correct type but haven't reached our index yet, - * just increment per-type index */ - ++num; - } - } - - if (i < NUM_FORMATS) { + if (f->index < NUM_FORMATS) { /* Format found */ - fmt = &formats[i]; + fmt = &formats[f->index]; f->pixelformat = fmt->fourcc; return 0; } @@ -542,12 +655,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = formats[0].fourcc; fmt = find_format(f); } - if (!(fmt->types & MEM2MEM_CAPTURE)) { - v4l2_err(&ctx->dev->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } f->fmt.pix.colorspace = ctx->colorspace; f->fmt.pix.xfer_func = ctx->xfer_func; f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; @@ -560,19 +667,12 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vim2m_fmt *fmt; - struct vim2m_ctx *ctx = file2ctx(file); fmt = find_format(f); if (!fmt) { f->fmt.pix.pixelformat = formats[0].fourcc; fmt = find_format(f); } - if (!(fmt->types & MEM2MEM_OUTPUT)) { - v4l2_err(&ctx->dev->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; From b3e64e5b077841f99a0ab27e99a1c4f942e1dd87 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jan 2019 14:00:16 -0200 Subject: [PATCH 0855/4277] media: vim2m: use per-file handler work queue It doesn't make sense to have a per-device work queue, as the scheduler should be called per file handler. Having a single one causes failures if multiple streams are filtered by vim2m. So, move it to be inside the context structure. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 036d695eeebe..921d656cf988 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -146,9 +146,6 @@ struct vim2m_dev { atomic_t num_inst; struct mutex dev_mutex; - spinlock_t irqlock; - - struct delayed_work work_run; struct v4l2_m2m_dev *m2m_dev; }; @@ -167,6 +164,10 @@ struct vim2m_ctx { /* Transaction time (i.e. simulated processing time) in milliseconds */ u32 transtime; + struct mutex vb_mutex; + struct delayed_work work_run; + spinlock_t irqlock; + /* Abort requested by m2m */ int aborting; @@ -490,7 +491,6 @@ static void job_abort(void *priv) static void device_run(void *priv) { struct vim2m_ctx *ctx = priv; - struct vim2m_dev *dev = ctx->dev; struct vb2_v4l2_buffer *src_buf, *dst_buf; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); @@ -507,18 +507,18 @@ static void device_run(void *priv) &ctx->hdl); /* Run delayed work, which simulates a hardware irq */ - schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime)); + schedule_delayed_work(&ctx->work_run, msecs_to_jiffies(ctx->transtime)); } static void device_work(struct work_struct *w) { - struct vim2m_dev *vim2m_dev = - container_of(w, struct vim2m_dev, work_run.work); struct vim2m_ctx *curr_ctx; + struct vim2m_dev *vim2m_dev; struct vb2_v4l2_buffer *src_vb, *dst_vb; unsigned long flags; - curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev); + curr_ctx = container_of(w, struct vim2m_ctx, work_run.work); + vim2m_dev = curr_ctx->dev; if (NULL == curr_ctx) { pr_err("Instance released before the end of transaction\n"); @@ -530,10 +530,10 @@ static void device_work(struct work_struct *w) curr_ctx->num_processed++; - spin_lock_irqsave(&vim2m_dev->irqlock, flags); + spin_lock_irqsave(&curr_ctx->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); - spin_unlock_irqrestore(&vim2m_dev->irqlock, flags); + spin_unlock_irqrestore(&curr_ctx->irqlock, flags); if (curr_ctx->num_processed == curr_ctx->translen || curr_ctx->aborting) { @@ -897,11 +897,10 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned count) static void vim2m_stop_streaming(struct vb2_queue *q) { struct vim2m_ctx *ctx = vb2_get_drv_priv(q); - struct vim2m_dev *dev = ctx->dev; struct vb2_v4l2_buffer *vbuf; unsigned long flags; - cancel_delayed_work_sync(&dev->work_run); + cancel_delayed_work_sync(&ctx->work_run); for (;;) { if (V4L2_TYPE_IS_OUTPUT(q->type)) vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); @@ -911,9 +910,9 @@ static void vim2m_stop_streaming(struct vb2_queue *q) return; v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &ctx->hdl); - spin_lock_irqsave(&ctx->dev->irqlock, flags); + spin_lock_irqsave(&ctx->irqlock, flags); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + spin_unlock_irqrestore(&ctx->irqlock, flags); } } @@ -948,7 +947,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds src_vq->ops = &vim2m_qops; src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->dev_mutex; + src_vq->lock = &ctx->vb_mutex; src_vq->supports_requests = true; ret = vb2_queue_init(src_vq); @@ -962,7 +961,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds dst_vq->ops = &vim2m_qops; dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->lock = &ctx->vb_mutex; return vb2_queue_init(dst_vq); } @@ -1037,6 +1036,10 @@ static int vim2m_open(struct file *file) ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + mutex_init(&ctx->vb_mutex); + spin_lock_init(&ctx->irqlock); + INIT_DELAYED_WORK(&ctx->work_run, device_work); + if (IS_ERR(ctx->fh.m2m_ctx)) { rc = PTR_ERR(ctx->fh.m2m_ctx); @@ -1117,8 +1120,6 @@ static int vim2m_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - spin_lock_init(&dev->irqlock); - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) return ret; @@ -1130,7 +1131,6 @@ static int vim2m_probe(struct platform_device *pdev) vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - INIT_DELAYED_WORK(&dev->work_run, device_work); ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { From f0ef022c85a899bcc7a1b3a0955c78a3d7109106 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jan 2019 14:00:17 -0200 Subject: [PATCH 0856/4277] media: vim2m: allow setting the default transaction time via parameter While there's a control to allow setting it at runtime, as the control handler is per file handler, only the application setting the m2m device can change it. As this is a custom control, it is unlikely that existing apps would be able to set it. Due to that, and due to the fact that v4l2-mem2mem serializes all accesses to a m2m device, trying to setup two GStreamer v4l2videoconvert instance at the same time will cause frame drops. So, add an alternate way of setting its default via a modprobe parameter. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 921d656cf988..e31c14c7d37f 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -41,6 +41,11 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); +/* Default transaction time in msec */ +static unsigned int default_transtime = 40; /* Max 25 fps */ +module_param(default_transtime, uint, 0644); +MODULE_PARM_DESC(default_transtime, "default transaction time in ms"); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -58,9 +63,6 @@ MODULE_PARM_DESC(debug, "activates debug info"); /* In bytes, per queue */ #define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024) -/* Default transaction time in msec */ -#define MEM2MEM_DEF_TRANSTIME 40 - /* Flags that indicate processing mode */ #define MEM2MEM_HFLIP (1 << 0) #define MEM2MEM_VFLIP (1 << 1) @@ -764,6 +766,8 @@ static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TRANS_TIME_MSEC: ctx->transtime = ctrl->val; + if (ctx->transtime < 1) + ctx->transtime = 1; break; case V4L2_CID_TRANS_NUM_BUFS: @@ -966,12 +970,11 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds return vb2_queue_init(dst_vq); } -static const struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = { +static struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = { .ops = &vim2m_ctrl_ops, .id = V4L2_CID_TRANS_TIME_MSEC, .name = "Transaction Time (msec)", .type = V4L2_CTRL_TYPE_INTEGER, - .def = MEM2MEM_DEF_TRANSTIME, .min = 1, .max = 10001, .step = 1, @@ -1013,6 +1016,8 @@ static int vim2m_open(struct file *file) v4l2_ctrl_handler_init(hdl, 4); v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + vim2m_ctrl_trans_time_msec.def = default_transtime; v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL); v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL); if (hdl->error) { From 22cb4e681523b35f1a0eba40c25ddc1b16898801 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 14 Jan 2019 21:23:30 +0100 Subject: [PATCH 0857/4277] =?UTF-8?q?mtip32xx:=20=D1=95top=20abusing=20the?= =?UTF-8?q?=20managed=20resource=20APIs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mtip32xx driver uses managed resources for DMA coherent memory and irqs, but then always pairs them with free calls anyway, making the resource tracking rather pointless. Given some DMA allocations are transient anyway, the irq freeing seems to require ordering vs other hardware access the best solution seems to be to stop using the managed resource API entirely. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 37 +++++++++++++------------------ 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 88e8440e75c3..9a6f40cd8df6 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -1415,7 +1415,7 @@ static blk_status_t mtip_send_trim(struct driver_data *dd, unsigned int lba, WARN_ON(sizeof(struct mtip_trim) > ATA_SECT_SIZE); /* Allocate a DMA buffer for the trim structure */ - buf = dmam_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr, + buf = dma_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr, GFP_KERNEL); if (!buf) return BLK_STS_RESOURCE; @@ -1452,7 +1452,7 @@ static blk_status_t mtip_send_trim(struct driver_data *dd, unsigned int lba, MTIP_TRIM_TIMEOUT_MS) < 0) ret = BLK_STS_IOERR; - dmam_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr); + dma_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr); return ret; } @@ -1655,7 +1655,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command, if (!user_buffer) return -EFAULT; - buf = dmam_alloc_coherent(&port->dd->pdev->dev, + buf = dma_alloc_coherent(&port->dd->pdev->dev, ATA_SECT_SIZE * xfer_sz, &dma_addr, GFP_KERNEL); @@ -1733,7 +1733,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command, } exit_drive_command: if (buf) - dmam_free_coherent(&port->dd->pdev->dev, + dma_free_coherent(&port->dd->pdev->dev, ATA_SECT_SIZE * xfer_sz, buf, dma_addr); return rv; } @@ -2837,11 +2837,11 @@ static void mtip_dma_free(struct driver_data *dd) struct mtip_port *port = dd->port; if (port->block1) - dmam_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, + dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, port->block1, port->block1_dma); if (port->command_list) { - dmam_free_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, + dma_free_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, port->command_list, port->command_list_dma); } } @@ -2860,7 +2860,7 @@ static int mtip_dma_alloc(struct driver_data *dd) /* Allocate dma memory for RX Fis, Identify, and Sector Bufffer */ port->block1 = - dmam_alloc_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, + dma_alloc_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, &port->block1_dma, GFP_KERNEL); if (!port->block1) return -ENOMEM; @@ -2868,10 +2868,10 @@ static int mtip_dma_alloc(struct driver_data *dd) /* Allocate dma memory for command list */ port->command_list = - dmam_alloc_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, + dma_alloc_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, &port->command_list_dma, GFP_KERNEL); if (!port->command_list) { - dmam_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, + dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, port->block1, port->block1_dma); port->block1 = NULL; port->block1_dma = 0; @@ -3056,13 +3056,8 @@ static int mtip_hw_init(struct driver_data *dd) mtip_start_port(dd->port); /* Setup the ISR and enable interrupts. */ - rv = devm_request_irq(&dd->pdev->dev, - dd->pdev->irq, - mtip_irq_handler, - IRQF_SHARED, - dev_driver_string(&dd->pdev->dev), - dd); - + rv = request_irq(dd->pdev->irq, mtip_irq_handler, IRQF_SHARED, + dev_driver_string(&dd->pdev->dev), dd); if (rv) { dev_err(&dd->pdev->dev, "Unable to allocate IRQ %d\n", dd->pdev->irq); @@ -3090,7 +3085,7 @@ out3: /* Release the IRQ. */ irq_set_affinity_hint(dd->pdev->irq, NULL); - devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd); + free_irq(dd->pdev->irq, dd); out2: mtip_deinit_port(dd->port); @@ -3145,7 +3140,7 @@ static int mtip_hw_exit(struct driver_data *dd) /* Release the IRQ. */ irq_set_affinity_hint(dd->pdev->irq, NULL); - devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd); + free_irq(dd->pdev->irq, dd); msleep(1000); /* Free dma regions */ @@ -3609,8 +3604,8 @@ static void mtip_free_cmd(struct blk_mq_tag_set *set, struct request *rq, if (!cmd->command) return; - dmam_free_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, - cmd->command, cmd->command_dma); + dma_free_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, cmd->command, + cmd->command_dma); } static int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq, @@ -3619,7 +3614,7 @@ static int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq, struct driver_data *dd = set->driver_data; struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); - cmd->command = dmam_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, + cmd->command = dma_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, &cmd->command_dma, GFP_KERNEL); if (!cmd->command) return -ENOMEM; From 20cd32450bcbec37c6d881b84bdddd8ba047ab01 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:25 +0100 Subject: [PATCH 0858/4277] block, bfq: do not consider interactive queues in srt filtering The speed at which a bfq_queue receives I/O is one of the parameters by which bfq decides whether the queue is soft real-time (i.e., whether the queue contains the I/O of a soft real-time application). In particular, when a bfq_queue remains without outstanding I/O requests, bfq computes the minimum time instant, named soft_rt_next_start, at which the next request of the queue may arrive for the queue to be deemed as soft real time. Unfortunately this filtering may cause problems with a queue in interactive weight raising. In fact, such a queue may be conveying the I/O needed to load a soft real-time application. The latter will actually exhibit a soft real-time I/O pattern after it finally starts doing its job. But, if soft_rt_next_start is updated for an interactive bfq_queue, and the queue has received a lot of service before remaining with no outstanding request (likely to happen on a fast device), then soft_rt_next_start is assigned such a high value that, for a very long time, the queue is prevented from being possibly considered as soft real time. This commit removes the updating of soft_rt_next_start for bfq_queues in interactive weight raising. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index cd307767a134..c7a4a15c7c19 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -3274,16 +3274,32 @@ void bfq_bfqq_expire(struct bfq_data *bfqd, * requests, then the request pattern is isochronous * (see the comments on the function * bfq_bfqq_softrt_next_start()). Thus we can compute - * soft_rt_next_start. If, instead, the queue still - * has outstanding requests, then we have to wait for - * the completion of all the outstanding requests to - * discover whether the request pattern is actually - * isochronous. + * soft_rt_next_start. And we do it, unless bfqq is in + * interactive weight raising. We do not do it in the + * latter subcase, for the following reason. bfqq may + * be conveying the I/O needed to load a soft + * real-time application. Such an application will + * actually exhibit a soft real-time I/O pattern after + * it finally starts doing its job. But, if + * soft_rt_next_start is computed here for an + * interactive bfqq, and bfqq had received a lot of + * service before remaining with no outstanding + * request (likely to happen on a fast device), then + * soft_rt_next_start would be assigned such a high + * value that, for a very long time, bfqq would be + * prevented from being possibly considered as soft + * real time. + * + * If, instead, the queue still has outstanding + * requests, then we have to wait for the completion + * of all the outstanding requests to discover whether + * the request pattern is actually isochronous. */ - if (bfqq->dispatched == 0) + if (bfqq->dispatched == 0 && + bfqq->wr_coeff != bfqd->bfq_wr_coeff) bfqq->soft_rt_next_start = bfq_bfqq_softrt_next_start(bfqd, bfqq); - else { + else if (bfqq->dispatched > 0) { /* * Schedule an update of soft_rt_next_start to when * the task may be discovered to be isochronous. @@ -4834,11 +4850,14 @@ static void bfq_completed_request(struct bfq_queue *bfqq, struct bfq_data *bfqd) * isochronous, and both requisites for this condition to hold * are now satisfied, then compute soft_rt_next_start (see the * comments on the function bfq_bfqq_softrt_next_start()). We - * schedule this delayed check when bfqq expires, if it still - * has in-flight requests. + * do not compute soft_rt_next_start if bfqq is in interactive + * weight raising (see the comments in bfq_bfqq_expire() for + * an explanation). We schedule this delayed update when bfqq + * expires, if it still has in-flight requests. */ if (bfq_bfqq_softrt_update(bfqq) && bfqq->dispatched == 0 && - RB_EMPTY_ROOT(&bfqq->sort_list)) + RB_EMPTY_ROOT(&bfqq->sort_list) && + bfqq->wr_coeff != bfqd->bfq_wr_coeff) bfqq->soft_rt_next_start = bfq_bfqq_softrt_next_start(bfqd, bfqq); From 218cb897be6940b8d18eec9bcb32eaa28f1ae8ee Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:26 +0100 Subject: [PATCH 0859/4277] block, bfq: avoid selecting a queue w/o budget To boost throughput on devices with internal queueing and in scenarios where device idling is not strictly needed, bfq immediately starts serving a new bfq_queue if the in-service bfq_queue remains without pending I/O, even if new I/O may arrive soon for the latter queue. Then, if such I/O actually arrives soon, bfq preempts the new in-service bfq_queue so as to give the previous queue a chance to go on being served (in case the previous queue should actually be the one to be served, according to its timestamps). However, the in-service bfq_queue, say Q, may also be without further budget when it remains also pending I/O. Since bfq changes budgets dynamically to fit the needs of bfq_queues, this happens more often than one may expect. If this happens, then there is no point in trying to go on serving Q when new I/O arrives for it soon: Q would be expired immediately after being selected for service. This would only cause useless overhead. This commit avoids such a useless selection. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index c7a4a15c7c19..9ea2c4f42501 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -1380,7 +1380,15 @@ static bool bfq_bfqq_update_budg_for_activation(struct bfq_data *bfqd, { struct bfq_entity *entity = &bfqq->entity; - if (bfq_bfqq_non_blocking_wait_rq(bfqq) && arrived_in_time) { + /* + * In the next compound condition, we check also whether there + * is some budget left, because otherwise there is no point in + * trying to go on serving bfqq with this same budget: bfqq + * would be expired immediately after being selected for + * service. This would only cause useless overhead. + */ + if (bfq_bfqq_non_blocking_wait_rq(bfqq) && arrived_in_time && + bfq_bfqq_budget_left(bfqq) > 0) { /* * We do not clear the flag non_blocking_wait_rq here, as * the latter is used in bfq_activate_bfqq to signal From f3218ad8c6acc9b874060814d6d3ddf154463ac9 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:27 +0100 Subject: [PATCH 0860/4277] block, bfq: make sure queue budgets are not below service received With some unlucky sequences of events, the function bfq_updated_next_req updates the current budget of a bfq_queue to a lower value than the service received by the queue using such a budget. Unfortunately, if this happens, then the return value of the function bfq_bfqq_budget_left becomes inconsistent. This commit solves this problem by lower-bounding the budget computed in bfq_updated_next_req to the service currently charged to the queue. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 9ea2c4f42501..b0e8006475be 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -907,8 +907,10 @@ static void bfq_updated_next_req(struct bfq_data *bfqd, */ return; - new_budget = max_t(unsigned long, bfqq->max_budget, - bfq_serv_to_charge(next_rq, bfqq)); + new_budget = max_t(unsigned long, + max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)), + entity->service); if (entity->budget != new_budget) { entity->budget = new_budget; bfq_log_bfqq(bfqd, bfqq, "updated next rq: new budget %lu", From 03e565e4204c6cf8687d995de5cafd0341503b4e Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:28 +0100 Subject: [PATCH 0861/4277] block, bfq: remove case of redirected bic from insert_request Before commit 18e5a57d7987 ("block, bfq: postpone rq preparation to insert or merge"), the destination queue for a request was chosen by a different hook than the one that then inserted the request. So, between the execution of the two hooks, the bic of the process generating the request could happen to be redirected to a different bfq_queue. As a consequence, the destination bfq_queue stored in the request could be wrong. Such an event does not need to ba handled any longer. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index b0e8006475be..a9275ed57726 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -4633,8 +4633,6 @@ static bool __bfq_insert_request(struct bfq_data *bfqd, struct request *rq) bool waiting, idle_timer_disabled = false; if (new_bfqq) { - if (bic_to_bfqq(RQ_BIC(rq), 1) != bfqq) - new_bfqq = bic_to_bfqq(RQ_BIC(rq), 1); /* * Release the request's reference to the old bfqq * and make sure one is taken to the shared queue. From 73d58118498b14e4d2f2391105459b997b586ddc Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:29 +0100 Subject: [PATCH 0862/4277] block, bfq: consider also ioprio classes in symmetry detection In asymmetric scenarios, i.e., when some bfq_queue or bfq_group needs to be guaranteed a different bandwidth than other bfq_queues or bfq_groups, these service guaranteed can be provided only by plugging I/O dispatch, completely or partially, when the queue in service remains temporarily empty. A case where asymmetry is particularly strong is when some active bfq_queues belong to a higher-priority class than some other active bfq_queues. Unfortunately, this important case is not considered at all in the code for detecting asymmetric scenarios. This commit adds the missing logic. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 86 ++++++++++++++++++++++++--------------------- block/bfq-iosched.h | 8 +++-- block/bfq-wf2q.c | 12 +++++-- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index a9275ed57726..6bfbfa65610b 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -623,26 +623,6 @@ void bfq_pos_tree_add_move(struct bfq_data *bfqd, struct bfq_queue *bfqq) bfqq->pos_root = NULL; } -/* - * Tell whether there are active queues with different weights or - * active groups. - */ -static bool bfq_varied_queue_weights_or_active_groups(struct bfq_data *bfqd) -{ - /* - * For queue weights to differ, queue_weights_tree must contain - * at least two nodes. - */ - return (!RB_EMPTY_ROOT(&bfqd->queue_weights_tree) && - (bfqd->queue_weights_tree.rb_node->rb_left || - bfqd->queue_weights_tree.rb_node->rb_right) -#ifdef CONFIG_BFQ_GROUP_IOSCHED - ) || - (bfqd->num_groups_with_pending_reqs > 0 -#endif - ); -} - /* * The following function returns true if every queue must receive the * same share of the throughput (this condition is used when deciding @@ -651,25 +631,48 @@ static bool bfq_varied_queue_weights_or_active_groups(struct bfq_data *bfqd) * * Such a scenario occurs when: * 1) all active queues have the same weight, - * 2) all active groups at the same level in the groups tree have the same - * weight, + * 2) all active queues belong to the same I/O-priority class, * 3) all active groups at the same level in the groups tree have the same + * weight, + * 4) all active groups at the same level in the groups tree have the same * number of children. * * Unfortunately, keeping the necessary state for evaluating exactly * the last two symmetry sub-conditions above would be quite complex - * and time consuming. Therefore this function evaluates, instead, - * only the following stronger two sub-conditions, for which it is + * and time consuming. Therefore this function evaluates, instead, + * only the following stronger three sub-conditions, for which it is * much easier to maintain the needed state: * 1) all active queues have the same weight, - * 2) there are no active groups. + * 2) all active queues belong to the same I/O-priority class, + * 3) there are no active groups. * In particular, the last condition is always true if hierarchical * support or the cgroups interface are not enabled, thus no state * needs to be maintained in this case. */ static bool bfq_symmetric_scenario(struct bfq_data *bfqd) { - return !bfq_varied_queue_weights_or_active_groups(bfqd); + /* + * For queue weights to differ, queue_weights_tree must contain + * at least two nodes. + */ + bool varied_queue_weights = !RB_EMPTY_ROOT(&bfqd->queue_weights_tree) && + (bfqd->queue_weights_tree.rb_node->rb_left || + bfqd->queue_weights_tree.rb_node->rb_right); + + bool multiple_classes_busy = + (bfqd->busy_queues[0] && bfqd->busy_queues[1]) || + (bfqd->busy_queues[0] && bfqd->busy_queues[2]) || + (bfqd->busy_queues[1] && bfqd->busy_queues[2]); + + /* + * For queue weights to differ, queue_weights_tree must contain + * at least two nodes. + */ + return !(varied_queue_weights || multiple_classes_busy +#ifdef BFQ_GROUP_IOSCHED_ENABLED + || bfqd->num_groups_with_pending_reqs > 0 +#endif + ); } /* @@ -728,15 +731,14 @@ void bfq_weights_tree_add(struct bfq_data *bfqd, struct bfq_queue *bfqq, /* * In the unlucky event of an allocation failure, we just * exit. This will cause the weight of queue to not be - * considered in bfq_varied_queue_weights_or_active_groups, - * which, in its turn, causes the scenario to be deemed - * wrongly symmetric in case bfqq's weight would have been - * the only weight making the scenario asymmetric. On the - * bright side, no unbalance will however occur when bfqq - * becomes inactive again (the invocation of this function - * is triggered by an activation of queue). In fact, - * bfq_weights_tree_remove does nothing if - * !bfqq->weight_counter. + * considered in bfq_symmetric_scenario, which, in its turn, + * causes the scenario to be deemed wrongly symmetric in case + * bfqq's weight would have been the only weight making the + * scenario asymmetric. On the bright side, no unbalance will + * however occur when bfqq becomes inactive again (the + * invocation of this function is triggered by an activation + * of queue). In fact, bfq_weights_tree_remove does nothing + * if !bfqq->weight_counter. */ if (unlikely(!bfqq->weight_counter)) return; @@ -2227,7 +2229,7 @@ bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, return NULL; /* If there is only one backlogged queue, don't search. */ - if (bfqd->busy_queues == 1) + if (bfq_tot_busy_queues(bfqd) == 1) return NULL; in_service_bfqq = bfqd->in_service_queue; @@ -3681,7 +3683,8 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq) * the requests already queued in the device have been served. */ asymmetric_scenario = (bfqq->wr_coeff > 1 && - bfqd->wr_busy_queues < bfqd->busy_queues) || + bfqd->wr_busy_queues < + bfq_tot_busy_queues(bfqd)) || !bfq_symmetric_scenario(bfqd); /* @@ -3960,7 +3963,7 @@ static struct request *bfq_dispatch_rq_from_bfqq(struct bfq_data *bfqd, * belongs to CLASS_IDLE and other queues are waiting for * service. */ - if (!(bfqd->busy_queues > 1 && bfq_class_idle(bfqq))) + if (!(bfq_tot_busy_queues(bfqd) > 1 && bfq_class_idle(bfqq))) goto return_rq; bfq_bfqq_expire(bfqd, bfqq, false, BFQQE_BUDGET_EXHAUSTED); @@ -3978,7 +3981,7 @@ static bool bfq_has_work(struct blk_mq_hw_ctx *hctx) * most a call to dispatch for nothing */ return !list_empty_careful(&bfqd->dispatch) || - bfqd->busy_queues > 0; + bfq_tot_busy_queues(bfqd) > 0; } static struct request *__bfq_dispatch_request(struct blk_mq_hw_ctx *hctx) @@ -4032,9 +4035,10 @@ static struct request *__bfq_dispatch_request(struct blk_mq_hw_ctx *hctx) goto start_rq; } - bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); + bfq_log(bfqd, "dispatch requests: %d busy queues", + bfq_tot_busy_queues(bfqd)); - if (bfqd->busy_queues == 0) + if (bfq_tot_busy_queues(bfqd) == 0) goto exit; /* diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h index 0b02bf302de0..30be669be465 100644 --- a/block/bfq-iosched.h +++ b/block/bfq-iosched.h @@ -501,10 +501,11 @@ struct bfq_data { unsigned int num_groups_with_pending_reqs; /* - * Number of bfq_queues containing requests (including the - * queue in service, even if it is idling). + * Per-class (RT, BE, IDLE) number of bfq_queues containing + * requests (including the queue in service, even if it is + * idling). */ - int busy_queues; + unsigned int busy_queues[3]; /* number of weight-raised busy @bfq_queues */ int wr_busy_queues; /* number of queued requests */ @@ -974,6 +975,7 @@ extern struct blkcg_policy blkcg_policy_bfq; struct bfq_group *bfq_bfqq_to_bfqg(struct bfq_queue *bfqq); struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity); +unsigned int bfq_tot_busy_queues(struct bfq_data *bfqd); struct bfq_service_tree *bfq_entity_service_tree(struct bfq_entity *entity); struct bfq_entity *bfq_entity_of(struct rb_node *node); unsigned short bfq_ioprio_to_weight(int ioprio); diff --git a/block/bfq-wf2q.c b/block/bfq-wf2q.c index 72adbbe975d5..ce37d709a34f 100644 --- a/block/bfq-wf2q.c +++ b/block/bfq-wf2q.c @@ -44,6 +44,12 @@ static unsigned int bfq_class_idx(struct bfq_entity *entity) BFQ_DEFAULT_GRP_CLASS - 1; } +unsigned int bfq_tot_busy_queues(struct bfq_data *bfqd) +{ + return bfqd->busy_queues[0] + bfqd->busy_queues[1] + + bfqd->busy_queues[2]; +} + static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, bool expiration); @@ -1513,7 +1519,7 @@ struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) struct bfq_sched_data *sd; struct bfq_queue *bfqq; - if (bfqd->busy_queues == 0) + if (bfq_tot_busy_queues(bfqd) == 0) return NULL; /* @@ -1665,7 +1671,7 @@ void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, bfq_clear_bfqq_busy(bfqq); - bfqd->busy_queues--; + bfqd->busy_queues[bfqq->ioprio_class - 1]--; if (!bfqq->dispatched) bfq_weights_tree_remove(bfqd, bfqq); @@ -1688,7 +1694,7 @@ void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq) bfq_activate_bfqq(bfqd, bfqq); bfq_mark_bfqq_busy(bfqq); - bfqd->busy_queues++; + bfqd->busy_queues[bfqq->ioprio_class - 1]++; if (!bfqq->dispatched) if (bfqq->wr_coeff == 1) From 05c2f5c30b3ca2346a5bb7c74b0c9515d8f4fbd2 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:30 +0100 Subject: [PATCH 0863/4277] block, bfq: split function bfq_better_to_idle This is a preparatory commit for commits that need to check only one of the two main reasons for idling. This change should also improve the quality of the code a little bit, by splitting a function that contains very long, non-trivial and little related comments. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 155 +++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 73 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 6bfbfa65610b..2756f4b1432b 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -3404,53 +3404,13 @@ static bool bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) bfq_bfqq_budget_timeout(bfqq); } -/* - * For a queue that becomes empty, device idling is allowed only if - * this function returns true for the queue. As a consequence, since - * device idling plays a critical role in both throughput boosting and - * service guarantees, the return value of this function plays a - * critical role in both these aspects as well. - * - * In a nutshell, this function returns true only if idling is - * beneficial for throughput or, even if detrimental for throughput, - * idling is however necessary to preserve service guarantees (low - * latency, desired throughput distribution, ...). In particular, on - * NCQ-capable devices, this function tries to return false, so as to - * help keep the drives' internal queues full, whenever this helps the - * device boost the throughput without causing any service-guarantee - * issue. - * - * In more detail, the return value of this function is obtained by, - * first, computing a number of boolean variables that take into - * account throughput and service-guarantee issues, and, then, - * combining these variables in a logical expression. Most of the - * issues taken into account are not trivial. We discuss these issues - * individually while introducing the variables. - */ -static bool bfq_better_to_idle(struct bfq_queue *bfqq) +static bool idling_boosts_thr_without_issues(struct bfq_data *bfqd, + struct bfq_queue *bfqq) { - struct bfq_data *bfqd = bfqq->bfqd; bool rot_without_queueing = !blk_queue_nonrot(bfqd->queue) && !bfqd->hw_tag, bfqq_sequential_and_IO_bound, - idling_boosts_thr, idling_boosts_thr_without_issues, - idling_needed_for_service_guarantees, - asymmetric_scenario; - - if (bfqd->strict_guarantees) - return true; - - /* - * Idling is performed only if slice_idle > 0. In addition, we - * do not idle if - * (a) bfqq is async - * (b) bfqq is in the idle io prio class: in this case we do - * not idle because we want to minimize the bandwidth that - * queues in this class can steal to higher-priority queues - */ - if (bfqd->bfq_slice_idle == 0 || !bfq_bfqq_sync(bfqq) || - bfq_class_idle(bfqq)) - return false; + idling_boosts_thr; bfqq_sequential_and_IO_bound = !BFQQ_SEEKY(bfqq) && bfq_bfqq_IO_bound(bfqq) && bfq_bfqq_has_short_ttime(bfqq); @@ -3482,8 +3442,7 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq) bfqq_sequential_and_IO_bound); /* - * The value of the next variable, - * idling_boosts_thr_without_issues, is equal to that of + * The return value of this function is equal to that of * idling_boosts_thr, unless a special case holds. In this * special case, described below, idling may cause problems to * weight-raised queues. @@ -3500,32 +3459,35 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq) * which enqueue several requests in advance, and further * reorder internally-queued requests. * - * For this reason, we force to false the value of - * idling_boosts_thr_without_issues if there are weight-raised - * busy queues. In this case, and if bfqq is not weight-raised, - * this guarantees that the device is not idled for bfqq (if, - * instead, bfqq is weight-raised, then idling will be - * guaranteed by another variable, see below). Combined with - * the timestamping rules of BFQ (see [1] for details), this - * behavior causes bfqq, and hence any sync non-weight-raised - * queue, to get a lower number of requests served, and thus - * to ask for a lower number of requests from the request - * pool, before the busy weight-raised queues get served - * again. This often mitigates starvation problems in the - * presence of heavy write workloads and NCQ, thereby - * guaranteeing a higher application and system responsiveness - * in these hostile scenarios. + * For this reason, we force to false the return value if + * there are weight-raised busy queues. In this case, and if + * bfqq is not weight-raised, this guarantees that the device + * is not idled for bfqq (if, instead, bfqq is weight-raised, + * then idling will be guaranteed by another variable, see + * below). Combined with the timestamping rules of BFQ (see + * [1] for details), this behavior causes bfqq, and hence any + * sync non-weight-raised queue, to get a lower number of + * requests served, and thus to ask for a lower number of + * requests from the request pool, before the busy + * weight-raised queues get served again. This often mitigates + * starvation problems in the presence of heavy write + * workloads and NCQ, thereby guaranteeing a higher + * application and system responsiveness in these hostile + * scenarios. */ - idling_boosts_thr_without_issues = idling_boosts_thr && + return idling_boosts_thr && bfqd->wr_busy_queues == 0; +} +static bool idling_needed_for_service_guarantees(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ /* - * There is then a case where idling must be performed not - * for throughput concerns, but to preserve service - * guarantees. + * There is a case where idling must be performed not for + * throughput concerns, but to preserve service guarantees. * * To introduce this case, we can note that allowing the drive - * to enqueue more than one request at a time, and hence + * to enqueue more than one request at a time, and thereby * delegating de facto final scheduling decisions to the * drive's internal scheduler, entails loss of control on the * actual request service order. In particular, the critical @@ -3682,9 +3644,9 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq) * to let requests be served in the desired order until all * the requests already queued in the device have been served. */ - asymmetric_scenario = (bfqq->wr_coeff > 1 && - bfqd->wr_busy_queues < - bfq_tot_busy_queues(bfqd)) || + bool asymmetric_scenario = (bfqq->wr_coeff > 1 && + bfqd->wr_busy_queues < + bfq_tot_busy_queues(bfqd)) || !bfq_symmetric_scenario(bfqd); /* @@ -3701,17 +3663,64 @@ static bool bfq_better_to_idle(struct bfq_queue *bfqq) * now establish when idling is actually needed to preserve * service guarantees. */ - idling_needed_for_service_guarantees = - asymmetric_scenario && !bfq_bfqq_in_large_burst(bfqq); + return asymmetric_scenario && !bfq_bfqq_in_large_burst(bfqq); +} + +/* + * For a queue that becomes empty, device idling is allowed only if + * this function returns true for that queue. As a consequence, since + * device idling plays a critical role for both throughput boosting + * and service guarantees, the return value of this function plays a + * critical role as well. + * + * In a nutshell, this function returns true only if idling is + * beneficial for throughput or, even if detrimental for throughput, + * idling is however necessary to preserve service guarantees (low + * latency, desired throughput distribution, ...). In particular, on + * NCQ-capable devices, this function tries to return false, so as to + * help keep the drives' internal queues full, whenever this helps the + * device boost the throughput without causing any service-guarantee + * issue. + * + * Most of the issues taken into account to get the return value of + * this function are not trivial. We discuss these issues in the two + * functions providing the main pieces of information needed by this + * function. + */ +static bool bfq_better_to_idle(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + bool idling_boosts_thr_with_no_issue, idling_needed_for_service_guar; + + if (unlikely(bfqd->strict_guarantees)) + return true; /* - * We have now all the components we need to compute the + * Idling is performed only if slice_idle > 0. In addition, we + * do not idle if + * (a) bfqq is async + * (b) bfqq is in the idle io prio class: in this case we do + * not idle because we want to minimize the bandwidth that + * queues in this class can steal to higher-priority queues + */ + if (bfqd->bfq_slice_idle == 0 || !bfq_bfqq_sync(bfqq) || + bfq_class_idle(bfqq)) + return false; + + idling_boosts_thr_with_no_issue = + idling_boosts_thr_without_issues(bfqd, bfqq); + + idling_needed_for_service_guar = + idling_needed_for_service_guarantees(bfqd, bfqq); + + /* + * We have now the two components we need to compute the * return value of the function, which is true only if idling * either boosts the throughput (without issues), or is * necessary to preserve service guarantees. */ - return idling_boosts_thr_without_issues || - idling_needed_for_service_guarantees; + return idling_boosts_thr_with_no_issue || + idling_needed_for_service_guar; } /* From ac8b0cb415f3aa9162009d39624501d37031533b Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:31 +0100 Subject: [PATCH 0864/4277] block, bfq: do not plug I/O of in-service queue when harmful If the in-service bfq_queue is sync and remains temporarily idle, then I/O dispatching (from other queues) may be plugged. It may be dome for two reasons: either to boost throughput, or to preserve the bandwidth share of the in-service queue. In the first case, if the I/O of the in-service queue, when it finally arrives, consists only of one small I/O request, then it makes sense to plug even the I/O of the in-service queue. In fact, serving such a small request immediately is likely to lower throughput instead of boosting it, whereas waiting a little bit is likely to let that request grow, thanks to request merging, and become more profitable in terms of throughput (this is likely to happen exactly because the I/O of the queue has been detected to boost throughput). On the opposite end, if I/O dispatching is being plugged only to preserve the bandwidth of the in-service queue, then it would be better not to plug also the I/O of the in-service queue, because such a plugging is likely to cause only loss of bandwidth for the queue. Unfortunately, no distinction is made between the two cases, and the I/O of the in-service queue is always plugged in case just a small I/O request arrives. This commit draws this missing distinction and does not perform harmful plugging. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 2756f4b1432b..a6fe60114ade 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -4599,28 +4599,31 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, bool budget_timeout = bfq_bfqq_budget_timeout(bfqq); /* - * There is just this request queued: if the request - * is small and the queue is not to be expired, then - * just exit. + * There is just this request queued: if + * - the request is small, and + * - we are idling to boost throughput, and + * - the queue is not to be expired, + * then just exit. * * In this way, if the device is being idled to wait * for a new request from the in-service queue, we * avoid unplugging the device and committing the - * device to serve just a small request. On the - * contrary, we wait for the block layer to decide - * when to unplug the device: hopefully, new requests - * will be merged to this one quickly, then the device - * will be unplugged and larger requests will be - * dispatched. + * device to serve just a small request. In contrast + * we wait for the block layer to decide when to + * unplug the device: hopefully, new requests will be + * merged to this one quickly, then the device will be + * unplugged and larger requests will be dispatched. */ - if (small_req && !budget_timeout) + if (small_req && idling_boosts_thr_without_issues(bfqd, bfqq) && + !budget_timeout) return; /* - * A large enough request arrived, or the queue is to - * be expired: in both cases disk idling is to be - * stopped, so clear wait_request flag and reset - * timer. + * A large enough request arrived, or idling is being + * performed to preserve service guarantees, or + * finally the queue is to be expired: in all these + * cases disk idling is to be stopped, so clear + * wait_request flag and reset timer. */ bfq_clear_bfqq_wait_request(bfqq); hrtimer_try_to_cancel(&bfqd->idle_slice_timer); From 530c4cbb3c62f9e42dbf39279fb346f2d2ab4dbb Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:32 +0100 Subject: [PATCH 0865/4277] block, bfq: unconditionally plug I/O in asymmetric scenarios bfq detects the creation of multiple bfq_queues shortly after each other, namely a burst of queue creations in the terminology used in the code. If the burst is large, then no queue in the burst is granted - either I/O-dispatch plugging when the queue remains temporarily idle while in service; - or weight raising, because it causes even longer plugging. In fact, such a plugging tends to lower throughput, while these bursts are typically due to applications or services that spawn multiple processes, to reach a common goal as soon as possible. Examples are a "git grep" or the booting of a system. Unfortunately, disabling plugging may cause a loss of service guarantees in asymmetric scenarios, i.e., if queue weights are differentiated or if more than one group is active. This commit addresses this issue by no longer disabling I/O-dispatch plugging for queues in large bursts. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 346 +++++++++++++++++++++----------------------- 1 file changed, 165 insertions(+), 181 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index a6fe60114ade..c1bb5e5fcdc4 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -3479,191 +3479,175 @@ static bool idling_boosts_thr_without_issues(struct bfq_data *bfqd, bfqd->wr_busy_queues == 0; } +/* + * There is a case where idling must be performed not for + * throughput concerns, but to preserve service guarantees. + * + * To introduce this case, we can note that allowing the drive + * to enqueue more than one request at a time, and hence + * delegating de facto final scheduling decisions to the + * drive's internal scheduler, entails loss of control on the + * actual request service order. In particular, the critical + * situation is when requests from different processes happen + * to be present, at the same time, in the internal queue(s) + * of the drive. In such a situation, the drive, by deciding + * the service order of the internally-queued requests, does + * determine also the actual throughput distribution among + * these processes. But the drive typically has no notion or + * concern about per-process throughput distribution, and + * makes its decisions only on a per-request basis. Therefore, + * the service distribution enforced by the drive's internal + * scheduler is likely to coincide with the desired + * device-throughput distribution only in a completely + * symmetric scenario where: + * (i) each of these processes must get the same throughput as + * the others; + * (ii) the I/O of each process has the same properties, in + * terms of locality (sequential or random), direction + * (reads or writes), request sizes, greediness + * (from I/O-bound to sporadic), and so on. + * In fact, in such a scenario, the drive tends to treat + * the requests of each of these processes in about the same + * way as the requests of the others, and thus to provide + * each of these processes with about the same throughput + * (which is exactly the desired throughput distribution). In + * contrast, in any asymmetric scenario, device idling is + * certainly needed to guarantee that bfqq receives its + * assigned fraction of the device throughput (see [1] for + * details). + * The problem is that idling may significantly reduce + * throughput with certain combinations of types of I/O and + * devices. An important example is sync random I/O, on flash + * storage with command queueing. So, unless bfqq falls in the + * above cases where idling also boosts throughput, it would + * be important to check conditions (i) and (ii) accurately, + * so as to avoid idling when not strictly needed for service + * guarantees. + * + * Unfortunately, it is extremely difficult to thoroughly + * check condition (ii). And, in case there are active groups, + * it becomes very difficult to check condition (i) too. In + * fact, if there are active groups, then, for condition (i) + * to become false, it is enough that an active group contains + * more active processes or sub-groups than some other active + * group. More precisely, for condition (i) to hold because of + * such a group, it is not even necessary that the group is + * (still) active: it is sufficient that, even if the group + * has become inactive, some of its descendant processes still + * have some request already dispatched but still waiting for + * completion. In fact, requests have still to be guaranteed + * their share of the throughput even after being + * dispatched. In this respect, it is easy to show that, if a + * group frequently becomes inactive while still having + * in-flight requests, and if, when this happens, the group is + * not considered in the calculation of whether the scenario + * is asymmetric, then the group may fail to be guaranteed its + * fair share of the throughput (basically because idling may + * not be performed for the descendant processes of the group, + * but it had to be). We address this issue with the + * following bi-modal behavior, implemented in the function + * bfq_symmetric_scenario(). + * + * If there are groups with requests waiting for completion + * (as commented above, some of these groups may even be + * already inactive), then the scenario is tagged as + * asymmetric, conservatively, without checking any of the + * conditions (i) and (ii). So the device is idled for bfqq. + * This behavior matches also the fact that groups are created + * exactly if controlling I/O is a primary concern (to + * preserve bandwidth and latency guarantees). + * + * On the opposite end, if there are no groups with requests + * waiting for completion, then only condition (i) is actually + * controlled, i.e., provided that condition (i) holds, idling + * is not performed, regardless of whether condition (ii) + * holds. In other words, only if condition (i) does not hold, + * then idling is allowed, and the device tends to be + * prevented from queueing many requests, possibly of several + * processes. Since there are no groups with requests waiting + * for completion, then, to control condition (i) it is enough + * to check just whether all the queues with requests waiting + * for completion also have the same weight. + * + * Not checking condition (ii) evidently exposes bfqq to the + * risk of getting less throughput than its fair share. + * However, for queues with the same weight, a further + * mechanism, preemption, mitigates or even eliminates this + * problem. And it does so without consequences on overall + * throughput. This mechanism and its benefits are explained + * in the next three paragraphs. + * + * Even if a queue, say Q, is expired when it remains idle, Q + * can still preempt the new in-service queue if the next + * request of Q arrives soon (see the comments on + * bfq_bfqq_update_budg_for_activation). If all queues and + * groups have the same weight, this form of preemption, + * combined with the hole-recovery heuristic described in the + * comments on function bfq_bfqq_update_budg_for_activation, + * are enough to preserve a correct bandwidth distribution in + * the mid term, even without idling. In fact, even if not + * idling allows the internal queues of the device to contain + * many requests, and thus to reorder requests, we can rather + * safely assume that the internal scheduler still preserves a + * minimum of mid-term fairness. + * + * More precisely, this preemption-based, idleless approach + * provides fairness in terms of IOPS, and not sectors per + * second. This can be seen with a simple example. Suppose + * that there are two queues with the same weight, but that + * the first queue receives requests of 8 sectors, while the + * second queue receives requests of 1024 sectors. In + * addition, suppose that each of the two queues contains at + * most one request at a time, which implies that each queue + * always remains idle after it is served. Finally, after + * remaining idle, each queue receives very quickly a new + * request. It follows that the two queues are served + * alternatively, preempting each other if needed. This + * implies that, although both queues have the same weight, + * the queue with large requests receives a service that is + * 1024/8 times as high as the service received by the other + * queue. + * + * The motivation for using preemption instead of idling (for + * queues with the same weight) is that, by not idling, + * service guarantees are preserved (completely or at least in + * part) without minimally sacrificing throughput. And, if + * there is no active group, then the primary expectation for + * this device is probably a high throughput. + * + * We are now left only with explaining the additional + * compound condition that is checked below for deciding + * whether the scenario is asymmetric. To explain this + * compound condition, we need to add that the function + * bfq_symmetric_scenario checks the weights of only + * non-weight-raised queues, for efficiency reasons (see + * comments on bfq_weights_tree_add()). Then the fact that + * bfqq is weight-raised is checked explicitly here. More + * precisely, the compound condition below takes into account + * also the fact that, even if bfqq is being weight-raised, + * the scenario is still symmetric if all queues with requests + * waiting for completion happen to be + * weight-raised. Actually, we should be even more precise + * here, and differentiate between interactive weight raising + * and soft real-time weight raising. + * + * As a side note, it is worth considering that the above + * device-idling countermeasures may however fail in the + * following unlucky scenario: if idling is (correctly) + * disabled in a time period during which all symmetry + * sub-conditions hold, and hence the device is allowed to + * enqueue many requests, but at some later point in time some + * sub-condition stops to hold, then it may become impossible + * to let requests be served in the desired order until all + * the requests already queued in the device have been served. + */ static bool idling_needed_for_service_guarantees(struct bfq_data *bfqd, struct bfq_queue *bfqq) { - /* - * There is a case where idling must be performed not for - * throughput concerns, but to preserve service guarantees. - * - * To introduce this case, we can note that allowing the drive - * to enqueue more than one request at a time, and thereby - * delegating de facto final scheduling decisions to the - * drive's internal scheduler, entails loss of control on the - * actual request service order. In particular, the critical - * situation is when requests from different processes happen - * to be present, at the same time, in the internal queue(s) - * of the drive. In such a situation, the drive, by deciding - * the service order of the internally-queued requests, does - * determine also the actual throughput distribution among - * these processes. But the drive typically has no notion or - * concern about per-process throughput distribution, and - * makes its decisions only on a per-request basis. Therefore, - * the service distribution enforced by the drive's internal - * scheduler is likely to coincide with the desired - * device-throughput distribution only in a completely - * symmetric scenario where: - * (i) each of these processes must get the same throughput as - * the others; - * (ii) the I/O of each process has the same properties, in - * terms of locality (sequential or random), direction - * (reads or writes), request sizes, greediness - * (from I/O-bound to sporadic), and so on. - * In fact, in such a scenario, the drive tends to treat - * the requests of each of these processes in about the same - * way as the requests of the others, and thus to provide - * each of these processes with about the same throughput - * (which is exactly the desired throughput distribution). In - * contrast, in any asymmetric scenario, device idling is - * certainly needed to guarantee that bfqq receives its - * assigned fraction of the device throughput (see [1] for - * details). - * The problem is that idling may significantly reduce - * throughput with certain combinations of types of I/O and - * devices. An important example is sync random I/O, on flash - * storage with command queueing. So, unless bfqq falls in the - * above cases where idling also boosts throughput, it would - * be important to check conditions (i) and (ii) accurately, - * so as to avoid idling when not strictly needed for service - * guarantees. - * - * Unfortunately, it is extremely difficult to thoroughly - * check condition (ii). And, in case there are active groups, - * it becomes very difficult to check condition (i) too. In - * fact, if there are active groups, then, for condition (i) - * to become false, it is enough that an active group contains - * more active processes or sub-groups than some other active - * group. More precisely, for condition (i) to hold because of - * such a group, it is not even necessary that the group is - * (still) active: it is sufficient that, even if the group - * has become inactive, some of its descendant processes still - * have some request already dispatched but still waiting for - * completion. In fact, requests have still to be guaranteed - * their share of the throughput even after being - * dispatched. In this respect, it is easy to show that, if a - * group frequently becomes inactive while still having - * in-flight requests, and if, when this happens, the group is - * not considered in the calculation of whether the scenario - * is asymmetric, then the group may fail to be guaranteed its - * fair share of the throughput (basically because idling may - * not be performed for the descendant processes of the group, - * but it had to be). We address this issue with the - * following bi-modal behavior, implemented in the function - * bfq_symmetric_scenario(). - * - * If there are groups with requests waiting for completion - * (as commented above, some of these groups may even be - * already inactive), then the scenario is tagged as - * asymmetric, conservatively, without checking any of the - * conditions (i) and (ii). So the device is idled for bfqq. - * This behavior matches also the fact that groups are created - * exactly if controlling I/O is a primary concern (to - * preserve bandwidth and latency guarantees). - * - * On the opposite end, if there are no groups with requests - * waiting for completion, then only condition (i) is actually - * controlled, i.e., provided that condition (i) holds, idling - * is not performed, regardless of whether condition (ii) - * holds. In other words, only if condition (i) does not hold, - * then idling is allowed, and the device tends to be - * prevented from queueing many requests, possibly of several - * processes. Since there are no groups with requests waiting - * for completion, then, to control condition (i) it is enough - * to check just whether all the queues with requests waiting - * for completion also have the same weight. - * - * Not checking condition (ii) evidently exposes bfqq to the - * risk of getting less throughput than its fair share. - * However, for queues with the same weight, a further - * mechanism, preemption, mitigates or even eliminates this - * problem. And it does so without consequences on overall - * throughput. This mechanism and its benefits are explained - * in the next three paragraphs. - * - * Even if a queue, say Q, is expired when it remains idle, Q - * can still preempt the new in-service queue if the next - * request of Q arrives soon (see the comments on - * bfq_bfqq_update_budg_for_activation). If all queues and - * groups have the same weight, this form of preemption, - * combined with the hole-recovery heuristic described in the - * comments on function bfq_bfqq_update_budg_for_activation, - * are enough to preserve a correct bandwidth distribution in - * the mid term, even without idling. In fact, even if not - * idling allows the internal queues of the device to contain - * many requests, and thus to reorder requests, we can rather - * safely assume that the internal scheduler still preserves a - * minimum of mid-term fairness. - * - * More precisely, this preemption-based, idleless approach - * provides fairness in terms of IOPS, and not sectors per - * second. This can be seen with a simple example. Suppose - * that there are two queues with the same weight, but that - * the first queue receives requests of 8 sectors, while the - * second queue receives requests of 1024 sectors. In - * addition, suppose that each of the two queues contains at - * most one request at a time, which implies that each queue - * always remains idle after it is served. Finally, after - * remaining idle, each queue receives very quickly a new - * request. It follows that the two queues are served - * alternatively, preempting each other if needed. This - * implies that, although both queues have the same weight, - * the queue with large requests receives a service that is - * 1024/8 times as high as the service received by the other - * queue. - * - * The motivation for using preemption instead of idling (for - * queues with the same weight) is that, by not idling, - * service guarantees are preserved (completely or at least in - * part) without minimally sacrificing throughput. And, if - * there is no active group, then the primary expectation for - * this device is probably a high throughput. - * - * We are now left only with explaining the additional - * compound condition that is checked below for deciding - * whether the scenario is asymmetric. To explain this - * compound condition, we need to add that the function - * bfq_symmetric_scenario checks the weights of only - * non-weight-raised queues, for efficiency reasons (see - * comments on bfq_weights_tree_add()). Then the fact that - * bfqq is weight-raised is checked explicitly here. More - * precisely, the compound condition below takes into account - * also the fact that, even if bfqq is being weight-raised, - * the scenario is still symmetric if all queues with requests - * waiting for completion happen to be - * weight-raised. Actually, we should be even more precise - * here, and differentiate between interactive weight raising - * and soft real-time weight raising. - * - * As a side note, it is worth considering that the above - * device-idling countermeasures may however fail in the - * following unlucky scenario: if idling is (correctly) - * disabled in a time period during which all symmetry - * sub-conditions hold, and hence the device is allowed to - * enqueue many requests, but at some later point in time some - * sub-condition stops to hold, then it may become impossible - * to let requests be served in the desired order until all - * the requests already queued in the device have been served. - */ - bool asymmetric_scenario = (bfqq->wr_coeff > 1 && - bfqd->wr_busy_queues < - bfq_tot_busy_queues(bfqd)) || + return (bfqq->wr_coeff > 1 && + bfqd->wr_busy_queues < + bfq_tot_busy_queues(bfqd)) || !bfq_symmetric_scenario(bfqd); - - /* - * Finally, there is a case where maximizing throughput is the - * best choice even if it may cause unfairness toward - * bfqq. Such a case is when bfqq became active in a burst of - * queue activations. Queues that became active during a large - * burst benefit only from throughput, as discussed in the - * comments on bfq_handle_burst. Thus, if bfqq became active - * in a burst and not idling the device maximizes throughput, - * then the device must no be idled, because not idling the - * device provides bfqq and all other queues in the burst with - * maximum benefit. Combining this and the above case, we can - * now establish when idling is actually needed to preserve - * service guarantees. - */ - return asymmetric_scenario && !bfq_bfqq_in_large_burst(bfqq); } /* From d87447d84fe194b0e4f5413b5344dc82cc100711 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:33 +0100 Subject: [PATCH 0866/4277] block, bfq: fix sequential rq detection in rate estimation In bfq_update_peak_rate, to check whether an I/O request rq is sequential, only the seek distance of rq w.r.t. the last request dispatched is controlled. This is not sufficient for non-rotational storage, where the size of rq is at least as relevant. This commit adds the missing control. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index c1bb5e5fcdc4..12228af16198 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -235,6 +235,11 @@ static struct kmem_cache *bfq_pool; #define BFQQ_SEEK_THR (sector_t)(8 * 100) #define BFQQ_SECT_THR_NONROT (sector_t)(2 * 32) +#define BFQ_RQ_SEEKY(bfqd, last_pos, rq) \ + (get_sdist(last_pos, rq) > \ + BFQQ_SEEK_THR && \ + (!blk_queue_nonrot(bfqd->queue) || \ + blk_rq_sectors(rq) < BFQQ_SECT_THR_NONROT)) #define BFQQ_CLOSE_THR (sector_t)(8 * 1024) #define BFQQ_SEEKY(bfqq) (hweight32(bfqq->seek_history) > 19) @@ -2754,7 +2759,7 @@ static void bfq_update_peak_rate(struct bfq_data *bfqd, struct request *rq) if ((bfqd->rq_in_driver > 0 || now_ns - bfqd->last_completion < BFQ_MIN_TT) - && get_sdist(bfqd->last_position, rq) < BFQQ_SEEK_THR) + && !BFQ_RQ_SEEKY(bfqd, bfqd->last_position, rq)) bfqd->sequential_samples++; bfqd->tot_sectors_dispatched += blk_rq_sectors(rq); @@ -4511,10 +4516,7 @@ bfq_update_io_seektime(struct bfq_data *bfqd, struct bfq_queue *bfqq, struct request *rq) { bfqq->seek_history <<= 1; - bfqq->seek_history |= - get_sdist(bfqq->last_request_pos, rq) > BFQQ_SEEK_THR && - (!blk_queue_nonrot(bfqd->queue) || - blk_rq_sectors(rq) < BFQQ_SECT_THR_NONROT); + bfqq->seek_history |= BFQ_RQ_SEEKY(bfqd, bfqq->last_request_pos, rq); } static void bfq_update_has_short_ttime(struct bfq_data *bfqd, From 9dee8b3b057e1da26f85f1842f2aaf3bb200fb94 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:34 +0100 Subject: [PATCH 0867/4277] block, bfq: fix queue removal from weights tree bfq maintains an ordered list, through a red-black tree, of unique weights of active bfq_queues. This list is used to detect whether there are active queues with differentiated weights. The weight of a queue is removed from the list when both the following two conditions become true: (1) the bfq_queue is flagged as inactive (2) the has no in-flight request any longer; Unfortunately, in the rare cases where condition (2) becomes true before condition (1), the removal fails, because the function to remove the weight of the queue (bfq_weights_tree_remove) is rightly invoked in the path that deactivates the bfq_queue, but mistakenly invoked *before* the function that actually performs the deactivation (bfq_deactivate_bfqq). This commits moves the invocation of bfq_weights_tree_remove for condition (1) to after bfq_deactivate_bfqq. As a consequence of this move, it is necessary to add a further reference to the queue when the weight of a queue is added, because the queue might otherwise be freed before bfq_weights_tree_remove is invoked. This commit adds this reference and makes all related modifications. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 17 +++++++++++++---- block/bfq-wf2q.c | 6 +++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 12228af16198..bf585ad29bb5 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -754,6 +754,7 @@ void bfq_weights_tree_add(struct bfq_data *bfqd, struct bfq_queue *bfqq, inc_counter: bfqq->weight_counter->num_active++; + bfqq->ref++; } /* @@ -778,6 +779,7 @@ void __bfq_weights_tree_remove(struct bfq_data *bfqd, reset_entity_pointer: bfqq->weight_counter = NULL; + bfq_put_queue(bfqq); } /* @@ -789,9 +791,6 @@ void bfq_weights_tree_remove(struct bfq_data *bfqd, { struct bfq_entity *entity = bfqq->entity.parent; - __bfq_weights_tree_remove(bfqd, bfqq, - &bfqd->queue_weights_tree); - for_each_entity(entity) { struct bfq_sched_data *sd = entity->my_sched_data; @@ -825,6 +824,15 @@ void bfq_weights_tree_remove(struct bfq_data *bfqd, bfqd->num_groups_with_pending_reqs--; } } + + /* + * Next function is invoked last, because it causes bfqq to be + * freed if the following holds: bfqq is not in service and + * has no dispatched request. DO NOT use bfqq after the next + * function invocation. + */ + __bfq_weights_tree_remove(bfqd, bfqq, + &bfqd->queue_weights_tree); } /* @@ -1020,7 +1028,8 @@ bfq_bfqq_resume_state(struct bfq_queue *bfqq, struct bfq_data *bfqd, static int bfqq_process_refs(struct bfq_queue *bfqq) { - return bfqq->ref - bfqq->allocated - bfqq->entity.on_st; + return bfqq->ref - bfqq->allocated - bfqq->entity.on_st - + (bfqq->weight_counter != NULL); } /* Empty burst list and add just bfqq (see comments on bfq_handle_burst) */ diff --git a/block/bfq-wf2q.c b/block/bfq-wf2q.c index ce37d709a34f..63311d1ff1ed 100644 --- a/block/bfq-wf2q.c +++ b/block/bfq-wf2q.c @@ -1673,15 +1673,15 @@ void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, bfqd->busy_queues[bfqq->ioprio_class - 1]--; - if (!bfqq->dispatched) - bfq_weights_tree_remove(bfqd, bfqq); - if (bfqq->wr_coeff > 1) bfqd->wr_busy_queues--; bfqg_stats_update_dequeue(bfqq_group(bfqq)); bfq_deactivate_bfqq(bfqd, bfqq, true, expiration); + + if (!bfqq->dispatched) + bfq_weights_tree_remove(bfqd, bfqq); } /* From a3c92560324bd616deaecb6842b2a0337a80ad8b Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:35 +0100 Subject: [PATCH 0868/4277] block, bfq: reduce threshold for detecting command queueing bfq simple heuristic from cfq for detecting whether the drive performs command queueing: check whether the average number of in-flight requests is above a given threshold. Unfortunately this heuristic does fail to detect queueing (on drives with queueing) if processes doing I/O are few and issue I/O with a low depth. To reduce false negatives, this commit lowers the threshold. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index bf585ad29bb5..48b579032d14 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -230,7 +230,7 @@ static struct kmem_cache *bfq_pool; #define BFQ_MIN_TT (2 * NSEC_PER_MSEC) /* hw_tag detection: parallel requests threshold and min samples needed. */ -#define BFQ_HW_QUEUE_THRESHOLD 4 +#define BFQ_HW_QUEUE_THRESHOLD 3 #define BFQ_HW_QUEUE_SAMPLES 32 #define BFQQ_SEEK_THR (sector_t)(8 * 100) @@ -4798,7 +4798,7 @@ static void bfq_update_hw_tag(struct bfq_data *bfqd) * sum is not exact, as it's not taking into account deactivated * requests. */ - if (bfqd->rq_in_driver + bfqd->queued < BFQ_HW_QUEUE_THRESHOLD) + if (bfqd->rq_in_driver + bfqd->queued <= BFQ_HW_QUEUE_THRESHOLD) return; if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES) From b3c3498112ffafa5f613bb482f9723996bfd5e4f Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:36 +0100 Subject: [PATCH 0869/4277] block, bfq: port commit "cfq-iosched: improve hw_tag detection" The original commit is commit 1a1238a7dd48 ("cfq-iosched: improve hw_tag detection") and has the following commit message: If active queue hasn't enough requests and idle window opens, cfq will not dispatch sufficient requests to hardware. In such situation, current code will zero hw_tag. But this is because cfq doesn't dispatch enough requests instead of hardware queue doesn't work. Don't zero hw_tag in such case. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 48b579032d14..2ab53d93ba12 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -4786,6 +4786,8 @@ static void bfq_insert_requests(struct blk_mq_hw_ctx *hctx, static void bfq_update_hw_tag(struct bfq_data *bfqd) { + struct bfq_queue *bfqq = bfqd->in_service_queue; + bfqd->max_rq_in_driver = max_t(int, bfqd->max_rq_in_driver, bfqd->rq_in_driver); @@ -4801,6 +4803,17 @@ static void bfq_update_hw_tag(struct bfq_data *bfqd) if (bfqd->rq_in_driver + bfqd->queued <= BFQ_HW_QUEUE_THRESHOLD) return; + /* + * If active queue hasn't enough requests and can idle, bfq might not + * dispatch sufficient requests to hardware. Don't zero hw_tag in this + * case + */ + if (bfqq && bfq_bfqq_has_short_ttime(bfqq) && + bfqq->dispatched + bfqq->queued[0] + bfqq->queued[1] < + BFQ_HW_QUEUE_THRESHOLD && + bfqd->rq_in_driver < BFQ_HW_QUEUE_THRESHOLD) + return; + if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES) return; From 02a6d787f4afc8be2d3d52ab0a1df0c6a2d99e7b Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:37 +0100 Subject: [PATCH 0870/4277] block, bfq: do not overcharge writes in asymmetric scenarios Writes tend to starve reads. bfq counters this problem by overcharging writes with an inflated service w.r.t. the actual service (number of sector written) they receive. Yet his overcharging is useless, and actually causes unfairness in the opposite direction, when bfq happens to be enforcing strong I/O control. bfq does this enforcing when the scenario is asymmetric, i.e., when some bfq_queue or group of bfq_queues is to be granted a different bandwidth than some other bfq_queue or group of bfq_queues. So, in such a scenario, this commit disables write overcharging. Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 2ab53d93ba12..06268449d2ca 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -888,7 +888,8 @@ static struct request *bfq_find_next_rq(struct bfq_data *bfqd, static unsigned long bfq_serv_to_charge(struct request *rq, struct bfq_queue *bfqq) { - if (bfq_bfqq_sync(bfqq) || bfqq->wr_coeff > 1) + if (bfq_bfqq_sync(bfqq) || bfqq->wr_coeff > 1 || + !bfq_symmetric_scenario(bfqq->bfqd)) return blk_rq_sectors(rq); return blk_rq_sectors(rq) * bfq_async_charge_factor; From 058fdecc6de7cdecbf4c59b851e80eb2d6c5295f Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 29 Jan 2019 12:06:38 +0100 Subject: [PATCH 0871/4277] block, bfq: fix in-service-queue check for queue merging When a new I/O request arrives for a bfq_queue, say Q, bfq checks whether that request is close to (a) the head request of some other queue waiting to be served, or (b) the last request dispatched for the in-service queue (in case Q itself is not the in-service queue) If a queue, say Q2, is found for which the above condition holds, then bfq merges Q and Q2, to hopefully get a more sequential I/O in the resulting merged queue, and thus a possibly higher throughput. Case (b) is checked by comparing the new request for Q with the last request dispatched, assuming that the latter necessarily belonged to the in-service queue. Unfortunately, this assumption is no longer always correct, since commit d0edc2473be9 ("block, bfq: inject other-queue I/O into seeky idle queues on NCQ flash"). When the assumption does not hold, queues that must not be merged may be merged, causing unexpected loss of control on per-queue service guarantees. This commit solves this problem by adding an extra field, which stores the actual last request dispatched for the in-service queue, and by using this new field to correctly check case (b). Signed-off-by: Paolo Valente Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 5 ++++- block/bfq-iosched.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 06268449d2ca..4c592496a16a 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -2251,7 +2251,8 @@ bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, if (in_service_bfqq && in_service_bfqq != bfqq && likely(in_service_bfqq != &bfqd->oom_bfqq) && - bfq_rq_close_to_sector(io_struct, request, bfqd->last_position) && + bfq_rq_close_to_sector(io_struct, request, + bfqd->in_serv_last_pos) && bfqq->entity.parent == in_service_bfqq->entity.parent && bfq_may_be_close_cooperator(bfqq, in_service_bfqq)) { new_bfqq = bfq_setup_merge(bfqq, in_service_bfqq); @@ -2791,6 +2792,8 @@ update_rate_and_reset: bfq_update_rate_reset(bfqd, rq); update_last_values: bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq); + if (RQ_BFQQ(rq) == bfqd->in_service_queue) + bfqd->in_serv_last_pos = bfqd->last_position; bfqd->last_dispatch = now_ns; } diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h index 30be669be465..062e1c4787f4 100644 --- a/block/bfq-iosched.h +++ b/block/bfq-iosched.h @@ -538,6 +538,9 @@ struct bfq_data { /* on-disk position of the last served request */ sector_t last_position; + /* position of the last served request for the in-service queue */ + sector_t in_serv_last_pos; + /* time of last request completion (ns) */ u64 last_completion; From da91ddfdc7212e6e716be55a5cf2305ce84a422f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 31 Jan 2019 15:19:21 +0000 Subject: [PATCH 0872/4277] RDMA/hns: Remove set but not used variable 'rst' Fixes gcc '-Wunused-but-set-variable' warning: drivers/infiniband/hw/hns/hns_roce_hw_v2.c: In function 'hns_roce_v2_qp_flow_control_init': drivers/infiniband/hw/hns/hns_roce_hw_v2.c:4384:33: warning: variable 'rst' set but not used [-Wunused-but-set-variable] It never used since introduction. Signed-off-by: YueHaibing Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 48a5d6548cd4..6a22db12fc3d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4381,7 +4381,7 @@ static int hns_roce_v2_destroy_qp(struct ib_qp *ibqp) static int hns_roce_v2_qp_flow_control_init(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) { - struct hns_roce_sccc_clr_done *rst, *resp; + struct hns_roce_sccc_clr_done *resp; struct hns_roce_sccc_clr *clr; struct hns_roce_cmq_desc desc; int ret, i; @@ -4390,7 +4390,6 @@ static int hns_roce_v2_qp_flow_control_init(struct hns_roce_dev *hr_dev, /* set scc ctx clear done flag */ hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_RESET_SCCC, false); - rst = (struct hns_roce_sccc_clr_done *)desc.data; ret = hns_roce_cmq_send(hr_dev, &desc, 1); if (ret) { dev_err(hr_dev->dev, "Reset SCC ctx failed(%d)\n", ret); From 6ee977dec7463eec21a06f5ae7d225e83c25fa05 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 24 Jan 2019 11:17:59 -0800 Subject: [PATCH 0873/4277] MAINTAINERS: Update filesystem-dax and NVDIMM entries Ross has moved on to other areas. Matthew and Jan are trusted reviewers for DAX. Dan is now coordinating filesystem-dax pull requests. Ira and Keith are now involved with the NVDIMM and Device-DAX sub-systems. The linux-nvdimm mailing hosts a patchwork instance for both DAX and NVDIMM patches. Cc: Matthew Wilcox Acked-by: Keith Busch Acked-by: Ross Zwisler Acked-by: Ira Weiny Acked-by: Jan Kara Signed-off-by: Dan Williams --- MAINTAINERS | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 4d04cebb4a71..3b9e50a50c14 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4536,10 +4536,11 @@ S: Maintained F: drivers/i2c/busses/i2c-diolan-u2c.c FILESYSTEM DIRECT ACCESS (DAX) -M: Matthew Wilcox -M: Ross Zwisler -M: Jan Kara +M: Dan Williams +R: Matthew Wilcox +R: Jan Kara L: linux-fsdevel@vger.kernel.org +L: linux-nvdimm@lists.01.org S: Supported F: fs/dax.c F: include/linux/dax.h @@ -4547,9 +4548,9 @@ F: include/trace/events/fs_dax.h DEVICE DIRECT ACCESS (DAX) M: Dan Williams -M: Dave Jiang -M: Ross Zwisler M: Vishal Verma +M: Keith Busch +M: Dave Jiang L: linux-nvdimm@lists.01.org S: Supported F: drivers/dax/ @@ -8642,7 +8643,6 @@ S: Maintained F: tools/lib/lockdep/ LIBNVDIMM BLK: MMIO-APERTURE DRIVER -M: Ross Zwisler M: Dan Williams M: Vishal Verma M: Dave Jiang @@ -8655,7 +8655,6 @@ F: drivers/nvdimm/region_devs.c LIBNVDIMM BTT: BLOCK TRANSLATION TABLE M: Vishal Verma M: Dan Williams -M: Ross Zwisler M: Dave Jiang L: linux-nvdimm@lists.01.org Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ @@ -8663,7 +8662,6 @@ S: Supported F: drivers/nvdimm/btt* LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER -M: Ross Zwisler M: Dan Williams M: Vishal Verma M: Dave Jiang @@ -8682,9 +8680,10 @@ F: Documentation/devicetree/bindings/pmem/pmem-region.txt LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM M: Dan Williams -M: Ross Zwisler M: Vishal Verma M: Dave Jiang +M: Keith Busch +M: Ira Weiny L: linux-nvdimm@lists.01.org Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git From 53cf978457325d8fb2cdecd7981b31a8229e446e Mon Sep 17 00:00:00 2001 From: Xiaoguang Wang Date: Thu, 31 Jan 2019 23:42:11 -0500 Subject: [PATCH 0874/4277] jbd2: fix deadlock while checkpoint thread waits commit thread to finish This issue was found when I tried to put checkpoint work in a separate thread, the deadlock below happened: Thread1 | Thread2 __jbd2_log_wait_for_space | jbd2_log_do_checkpoint (hold j_checkpoint_mutex)| if (jh->b_transaction != NULL) | ... | jbd2_log_start_commit(journal, tid); |jbd2_update_log_tail | will lock j_checkpoint_mutex, | but will be blocked here. | jbd2_log_wait_commit(journal, tid); | wait_event(journal->j_wait_done_commit, | !tid_gt(tid, journal->j_commit_sequence)); | ... |wake_up(j_wait_done_commit) } | then deadlock occurs, Thread1 will never be waken up. To fix this issue, drop j_checkpoint_mutex in jbd2_log_do_checkpoint() when we are going to wait for transaction commit. Reviewed-by: Jan Kara Signed-off-by: Xiaoguang Wang Signed-off-by: Theodore Ts'o --- fs/jbd2/checkpoint.c | 17 +++++++++++++++-- fs/jbd2/journal.c | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 26f8d7e46462..02e0b79753e7 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -113,7 +113,7 @@ void __jbd2_log_wait_for_space(journal_t *journal) nblocks = jbd2_space_needed(journal); while (jbd2_log_space_left(journal) < nblocks) { write_unlock(&journal->j_state_lock); - mutex_lock(&journal->j_checkpoint_mutex); + mutex_lock_io(&journal->j_checkpoint_mutex); /* * Test again, another process may have checkpointed while we @@ -276,9 +276,22 @@ restart: "JBD2: %s: Waiting for Godot: block %llu\n", journal->j_devname, (unsigned long long) bh->b_blocknr); + if (batch_count) + __flush_batch(journal, &batch_count); jbd2_log_start_commit(journal, tid); + /* + * jbd2_journal_commit_transaction() may want + * to take the checkpoint_mutex if JBD2_FLUSHED + * is set, jbd2_update_log_tail() called by + * jbd2_journal_commit_transaction() may also take + * checkpoint_mutex. So we need to temporarily + * drop it. + */ + mutex_unlock(&journal->j_checkpoint_mutex); jbd2_log_wait_commit(journal, tid); - goto retry; + mutex_lock_io(&journal->j_checkpoint_mutex); + spin_lock(&journal->j_list_lock); + goto restart; } if (!buffer_dirty(bh)) { if (unlikely(buffer_write_io_error(bh)) && !result) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 8ef6b6daaa7a..88d8f22d2cba 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -2067,7 +2067,7 @@ int jbd2_journal_wipe(journal_t *journal, int write) err = jbd2_journal_skip_recovery(journal); if (write) { /* Lock to make assertions happy... */ - mutex_lock(&journal->j_checkpoint_mutex); + mutex_lock_io(&journal->j_checkpoint_mutex); jbd2_mark_journal_empty(journal, REQ_SYNC | REQ_FUA); mutex_unlock(&journal->j_checkpoint_mutex); } From 5678b5d6a8ec433936ccb9e0626e6361e241c275 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 21 Jan 2019 16:23:25 +0100 Subject: [PATCH 0875/4277] orangefs: don't reinitialize result_mask in ->getattr The caller already initializes it to the basic stats. Just clear not supported default bits where needed. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/orangefs/inode.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c index f038235c64bd..c3334eca18c7 100644 --- a/fs/orangefs/inode.c +++ b/fs/orangefs/inode.c @@ -261,11 +261,8 @@ int orangefs_getattr(const struct path *path, struct kstat *stat, generic_fillattr(inode, stat); /* override block size reported to stat */ - if (request_mask & STATX_SIZE) - stat->result_mask = STATX_BASIC_STATS; - else - stat->result_mask = STATX_BASIC_STATS & - ~STATX_SIZE; + if (!(request_mask & STATX_SIZE)) + stat->result_mask &= ~STATX_SIZE; stat->attributes_mask = STATX_ATTR_IMMUTABLE | STATX_ATTR_APPEND; From 801e523796004a3aaeae7dfc83fe81888f055287 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 21 Jan 2019 16:23:26 +0100 Subject: [PATCH 0876/4277] fs: move generic stat response attr handling to vfs_getattr_nosec generic_fillattr is an optional helper that isn't used by all file systems, move handling purely based on inode flags to vfs_getattr_nosec, which is common code. This fixes setting this flag for file systems not using generic_fillattr like xfs. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/stat.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index adbfcd86c81b..c38e4c2e1221 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -45,11 +45,6 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) stat->ctime = inode->i_ctime; stat->blksize = i_blocksize(inode); stat->blocks = inode->i_blocks; - - if (IS_NOATIME(inode)) - stat->result_mask &= ~STATX_ATIME; - if (IS_AUTOMOUNT(inode)) - stat->attributes |= STATX_ATTR_AUTOMOUNT; } EXPORT_SYMBOL(generic_fillattr); @@ -75,6 +70,13 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, stat->result_mask |= STATX_BASIC_STATS; request_mask &= STATX_ALL; query_flags &= KSTAT_QUERY_FLAGS; + + /* allow the fs to override these if it really wants to */ + if (IS_NOATIME(inode)) + stat->result_mask &= ~STATX_ATIME; + if (IS_AUTOMOUNT(inode)) + stat->attributes |= STATX_ATTR_AUTOMOUNT; + if (inode->i_op->getattr) return inode->i_op->getattr(path, stat, request_mask, query_flags); From a0ce2f0aa6ad97c3d4927bf2ca54bcebdf062d55 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 23 Jan 2019 15:19:17 +0100 Subject: [PATCH 0877/4277] splice: don't merge into linked buffers Before this patch, it was possible for two pipes to affect each other after data had been transferred between them with tee(): ============ $ cat tee_test.c int main(void) { int pipe_a[2]; if (pipe(pipe_a)) err(1, "pipe"); int pipe_b[2]; if (pipe(pipe_b)) err(1, "pipe"); if (write(pipe_a[1], "abcd", 4) != 4) err(1, "write"); if (tee(pipe_a[0], pipe_b[1], 2, 0) != 2) err(1, "tee"); if (write(pipe_b[1], "xx", 2) != 2) err(1, "write"); char buf[5]; if (read(pipe_a[0], buf, 4) != 4) err(1, "read"); buf[4] = 0; printf("got back: '%s'\n", buf); } $ gcc -o tee_test tee_test.c $ ./tee_test got back: 'abxx' $ ============ As suggested by Al Viro, fix it by creating a separate type for non-mergeable pipe buffers, then changing the types of buffers in splice_pipe_to_pipe() and link_pipe(). Cc: Fixes: 7c77f0b3f920 ("splice: implement pipe to pipe splicing") Fixes: 70524490ee2e ("[PATCH] splice: add support for sys_tee()") Suggested-by: Al Viro Signed-off-by: Jann Horn Signed-off-by: Al Viro --- fs/pipe.c | 14 ++++++++++++++ fs/splice.c | 4 ++++ include/linux/pipe_fs_i.h | 1 + 3 files changed, 19 insertions(+) diff --git a/fs/pipe.c b/fs/pipe.c index bdc5d3c0977d..c51750ed4011 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -234,6 +234,14 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { .get = generic_pipe_buf_get, }; +static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = { + .can_merge = 0, + .confirm = generic_pipe_buf_confirm, + .release = anon_pipe_buf_release, + .steal = anon_pipe_buf_steal, + .get = generic_pipe_buf_get, +}; + static const struct pipe_buf_operations packet_pipe_buf_ops = { .can_merge = 0, .confirm = generic_pipe_buf_confirm, @@ -242,6 +250,12 @@ static const struct pipe_buf_operations packet_pipe_buf_ops = { .get = generic_pipe_buf_get, }; +void pipe_buf_mark_unmergeable(struct pipe_buffer *buf) +{ + if (buf->ops == &anon_pipe_buf_ops) + buf->ops = &anon_pipe_buf_nomerge_ops; +} + static ssize_t pipe_read(struct kiocb *iocb, struct iov_iter *to) { diff --git a/fs/splice.c b/fs/splice.c index de2ede048473..90c29675d573 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1597,6 +1597,8 @@ retry: */ obuf->flags &= ~PIPE_BUF_FLAG_GIFT; + pipe_buf_mark_unmergeable(obuf); + obuf->len = len; opipe->nrbufs++; ibuf->offset += obuf->len; @@ -1671,6 +1673,8 @@ static int link_pipe(struct pipe_inode_info *ipipe, */ obuf->flags &= ~PIPE_BUF_FLAG_GIFT; + pipe_buf_mark_unmergeable(obuf); + if (obuf->len > len) obuf->len = len; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 5a3bb3b7c9ad..3ecd7ea212ae 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -182,6 +182,7 @@ void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); +void pipe_buf_mark_unmergeable(struct pipe_buffer *buf); extern const struct pipe_buf_operations nosteal_pipe_buf_ops; From 01e7187b41191376cee8bea8de9f907b001e87b4 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 23 Jan 2019 15:19:18 +0100 Subject: [PATCH 0878/4277] pipe: stop using ->can_merge Al Viro pointed out that since there is only one pipe buffer type to which new data can be appended, it isn't necessary to have a ->can_merge field in struct pipe_buf_operations, we can just check for a magic type. Suggested-by: Al Viro Signed-off-by: Jann Horn Signed-off-by: Al Viro --- fs/pipe.c | 20 ++++++++++++++++---- fs/splice.c | 4 ---- include/linux/pipe_fs_i.h | 7 ------- kernel/relay.c | 1 - kernel/trace/trace.c | 2 -- net/smc/smc_rx.c | 1 - 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index c51750ed4011..0ff09b490ddf 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -226,8 +226,8 @@ void generic_pipe_buf_release(struct pipe_inode_info *pipe, } EXPORT_SYMBOL(generic_pipe_buf_release); +/* New data written to a pipe may be appended to a buffer with this type. */ static const struct pipe_buf_operations anon_pipe_buf_ops = { - .can_merge = 1, .confirm = generic_pipe_buf_confirm, .release = anon_pipe_buf_release, .steal = anon_pipe_buf_steal, @@ -235,7 +235,6 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { }; static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = anon_pipe_buf_release, .steal = anon_pipe_buf_steal, @@ -243,19 +242,32 @@ static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = { }; static const struct pipe_buf_operations packet_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = anon_pipe_buf_release, .steal = anon_pipe_buf_steal, .get = generic_pipe_buf_get, }; +/** + * pipe_buf_mark_unmergeable - mark a &struct pipe_buffer as unmergeable + * @buf: the buffer to mark + * + * Description: + * This function ensures that no future writes will be merged into the + * given &struct pipe_buffer. This is necessary when multiple pipe buffers + * share the same backing page. + */ void pipe_buf_mark_unmergeable(struct pipe_buffer *buf) { if (buf->ops == &anon_pipe_buf_ops) buf->ops = &anon_pipe_buf_nomerge_ops; } +static bool pipe_buf_can_merge(struct pipe_buffer *buf) +{ + return buf->ops == &anon_pipe_buf_ops; +} + static ssize_t pipe_read(struct kiocb *iocb, struct iov_iter *to) { @@ -393,7 +405,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) struct pipe_buffer *buf = pipe->bufs + lastbuf; int offset = buf->offset + buf->len; - if (buf->ops->can_merge && offset + chars <= PAGE_SIZE) { + if (pipe_buf_can_merge(buf) && offset + chars <= PAGE_SIZE) { ret = pipe_buf_confirm(pipe, buf); if (ret) goto out; diff --git a/fs/splice.c b/fs/splice.c index 90c29675d573..fc71e9733f7a 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -138,7 +138,6 @@ error: } const struct pipe_buf_operations page_cache_pipe_buf_ops = { - .can_merge = 0, .confirm = page_cache_pipe_buf_confirm, .release = page_cache_pipe_buf_release, .steal = page_cache_pipe_buf_steal, @@ -156,7 +155,6 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, } static const struct pipe_buf_operations user_page_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = page_cache_pipe_buf_release, .steal = user_page_pipe_buf_steal, @@ -326,7 +324,6 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, EXPORT_SYMBOL(generic_file_splice_read); const struct pipe_buf_operations default_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = generic_pipe_buf_release, .steal = generic_pipe_buf_steal, @@ -341,7 +338,6 @@ static int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe, /* Pipe buffer operations for a socket and similar. */ const struct pipe_buf_operations nosteal_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = generic_pipe_buf_release, .steal = generic_pipe_buf_nosteal, diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 3ecd7ea212ae..787d224ff43e 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -73,13 +73,6 @@ struct pipe_inode_info { * in fs/pipe.c for the pipe and generic variants of these hooks. */ struct pipe_buf_operations { - /* - * This is set to 1, if the generic pipe read/write may coalesce - * data into an existing buffer. If this is set to 0, a new pipe - * page segment is always used for new data. - */ - int can_merge; - /* * ->confirm() verifies that the data in the pipe buffer is there * and that the contents are good. If the pages in the pipe belong diff --git a/kernel/relay.c b/kernel/relay.c index 04f248644e06..db3e419c25a6 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -1175,7 +1175,6 @@ static void relay_pipe_buf_release(struct pipe_inode_info *pipe, } static const struct pipe_buf_operations relay_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = relay_pipe_buf_release, .steal = generic_pipe_buf_steal, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c521b7347482..f380139e972c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5823,7 +5823,6 @@ static void tracing_spd_release_pipe(struct splice_pipe_desc *spd, } static const struct pipe_buf_operations tracing_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = generic_pipe_buf_release, .steal = generic_pipe_buf_steal, @@ -6843,7 +6842,6 @@ static void buffer_pipe_buf_get(struct pipe_inode_info *pipe, /* Pipe buffer operations for a buffer. */ static const struct pipe_buf_operations buffer_pipe_buf_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = buffer_pipe_buf_release, .steal = generic_pipe_buf_steal, diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index bbcf0fe4ae10..413a6abf227e 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -136,7 +136,6 @@ static int smc_rx_pipe_buf_nosteal(struct pipe_inode_info *pipe, } static const struct pipe_buf_operations smc_pipe_ops = { - .can_merge = 0, .confirm = generic_pipe_buf_confirm, .release = smc_rx_pipe_buf_release, .steal = smc_rx_pipe_buf_nosteal, From 4bc59c2f7e306775f3d2e1bbafaa854dd1e09335 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:33:56 +0100 Subject: [PATCH 0879/4277] mfd / platform: cros_ec: Use devm_mfd_add_devices Use devm_mfd_add_devices() for adding cros-ec core MFD child devices. This reduces the need of remove callback from platform/chrome for removing the MFD child devices. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 14 +++----------- drivers/platform/chrome/cros_ec_i2c.c | 10 ---------- drivers/platform/chrome/cros_ec_lpc.c | 4 ---- drivers/platform/chrome/cros_ec_spi.c | 11 ----------- include/linux/mfd/cros_ec.h | 10 ---------- 5 files changed, 3 insertions(+), 46 deletions(-) diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index fe6f83766144..6acfe036d522 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -129,8 +129,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev) } } - err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, - NULL, ec_dev->irq, NULL); + err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, + 1, NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, "Failed to register Embedded Controller subdevice %d\n", @@ -147,7 +147,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) * - the EC is responsive at init time (it is not true for a * sensor hub. */ - err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, + err = devm_mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_pd_cell, 1, NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, @@ -181,14 +181,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev) } EXPORT_SYMBOL(cros_ec_register); -int cros_ec_remove(struct cros_ec_device *ec_dev) -{ - mfd_remove_devices(ec_dev->dev); - - return 0; -} -EXPORT_SYMBOL(cros_ec_remove); - #ifdef CONFIG_PM_SLEEP int cros_ec_suspend(struct cros_ec_device *ec_dev) { diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index ef9b4763356f..9a009eaa4ada 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -317,15 +317,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client, return 0; } -static int cros_ec_i2c_remove(struct i2c_client *client) -{ - struct cros_ec_device *ec_dev = i2c_get_clientdata(client); - - cros_ec_remove(ec_dev); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int cros_ec_i2c_suspend(struct device *dev) { @@ -376,7 +367,6 @@ static struct i2c_driver cros_ec_driver = { .pm = &cros_ec_i2c_pm_ops, }, .probe = cros_ec_i2c_probe, - .remove = cros_ec_i2c_remove, .id_table = cros_ec_i2c_id, }; diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index e1b75775cd4a..14684a56e40f 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -327,7 +327,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) static int cros_ec_lpc_remove(struct platform_device *pdev) { - struct cros_ec_device *ec_dev; struct acpi_device *adev; adev = ACPI_COMPANION(&pdev->dev); @@ -335,9 +334,6 @@ static int cros_ec_lpc_remove(struct platform_device *pdev) acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, cros_ec_lpc_acpi_notify); - ec_dev = platform_get_drvdata(pdev); - cros_ec_remove(ec_dev); - return 0; } diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 2060d1483043..6cfbc2835beb 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -685,16 +685,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) return 0; } -static int cros_ec_spi_remove(struct spi_device *spi) -{ - struct cros_ec_device *ec_dev; - - ec_dev = spi_get_drvdata(spi); - cros_ec_remove(ec_dev); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int cros_ec_spi_suspend(struct device *dev) { @@ -733,7 +723,6 @@ static struct spi_driver cros_ec_driver_spi = { .pm = &cros_ec_spi_pm_ops, }, .probe = cros_ec_spi_probe, - .remove = cros_ec_spi_remove, .id_table = cros_ec_spi_id, }; diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index de8b588c8776..977ebaa78e99 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -281,16 +281,6 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, struct cros_ec_command *msg); -/** - * cros_ec_remove() - Remove a ChromeOS EC. - * @ec_dev: Device to register. - * - * Call this to deregister a ChromeOS EC, then clean up any private data. - * - * Return: 0 on success or negative error code. - */ -int cros_ec_remove(struct cros_ec_device *ec_dev); - /** * cros_ec_register() - Register a new ChromeOS EC, using the provided info. * @ec_dev: Device to register. From ecf8a6cd949ef236ce435ae488ceb6b3354e677e Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:33:57 +0100 Subject: [PATCH 0880/4277] mfd / platform: cros_ec: Move lightbar attributes to its own driver The entire way how cros sysfs attibutes are created is broken. cros_ec_lightbar should be its own driver and its attributes should be associated with a lightbar driver not the mfd driver. In order to retain the path, the lightbar attributes are attached to the cros_class. The patch also adds the sysfs documentation. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- ...sfs-class-chromeos-driver-cros-ec-lightbar | 74 +++++++++++++++ drivers/mfd/cros_ec_dev.c | 24 ++--- drivers/mfd/cros_ec_dev.h | 6 -- drivers/platform/chrome/Kconfig | 11 +++ drivers/platform/chrome/Makefile | 3 +- drivers/platform/chrome/cros_ec_lightbar.c | 95 ++++++++++++++----- include/linux/mfd/cros_ec.h | 1 - 7 files changed, 173 insertions(+), 41 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-lightbar diff --git a/Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-lightbar b/Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-lightbar new file mode 100644 index 000000000000..57a037791403 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-lightbar @@ -0,0 +1,74 @@ +What: /sys/class/chromeos//lightbar/brightness +Date: August 2015 +KernelVersion: 4.2 +Description: + Writing to this file adjusts the overall brightness of + the lightbar, separate from any color intensity. The + valid range is 0 (off) to 255 (maximum brightness). + +What: /sys/class/chromeos//lightbar/interval_msec +Date: August 2015 +KernelVersion: 4.2 +Description: + The lightbar is controlled by an embedded controller (EC), + which also manages the keyboard, battery charging, fans, + and other system hardware. To prevent unprivileged users + from interfering with the other EC functions, the rate at + which the lightbar control files can be read or written is + limited. + + Reading this file will return the number of milliseconds + that must elapse between accessing any of the lightbar + functions through this interface. Going faster will simply + block until the necessary interval has lapsed. The interval + applies uniformly to all accesses of any kind by any user. + +What: /sys/class/chromeos//lightbar/led_rgb +Date: August 2015 +KernelVersion: 4.2 +Description: + This allows you to control each LED segment. If the + lightbar is already running one of the automatic + sequences, you probably won’t see anything change because + your color setting will be almost immediately replaced. + To get useful results, you should stop the lightbar + sequence first. + + The values written to this file are sets of four integers, + indicating LED, RED, GREEN, BLUE. The LED number is 0 to 3 + to select a single segment, or 4 to set all four segments + to the same value at once. The RED, GREEN, and BLUE + numbers should be in the range 0 (off) to 255 (maximum). + You can update more than one segment at a time by writing + more than one set of four integers. + +What: /sys/class/chromeos//lightbar/program +Date: August 2015 +KernelVersion: 4.2 +Description: + This allows you to upload and run custom lightbar sequences. + +What: /sys/class/chromeos//lightbar/sequence +Date: August 2015 +KernelVersion: 4.2 +Description: + The Pixel lightbar has a number of built-in sequences + that it displays under various conditions, such as at + power on, shut down, or while running. Reading from this + file displays the current sequence that the lightbar is + displaying. Writing to this file allows you to change the + sequence. + +What: /sys/class/chromeos//lightbar/userspace_control +Date: August 2015 +KernelVersion: 4.2 +Description: + This allows you to take the control of the lightbar. This + prevents the kernel from going through its normal + sequences. + +What: /sys/class/chromeos//lightbar/version +Date: August 2015 +KernelVersion: 4.2 +Description: + Show the information about the lightbar version. diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 2d0fee488c5a..b227718e0ec2 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -36,7 +36,6 @@ static int ec_major; static const struct attribute_group *cros_ec_groups[] = { &cros_ec_attr_group, - &cros_ec_lightbar_attr_group, &cros_ec_vbc_attr_group, NULL, }; @@ -395,6 +394,10 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = { { .name = "cros-usbpd-charger" } }; +static const struct mfd_cell cros_ec_platform_cells[] = { + { .name = "cros-ec-lightbar" }, +}; + static int ec_device_probe(struct platform_device *pdev) { int retval = -ENOMEM; @@ -470,9 +473,6 @@ static int ec_device_probe(struct platform_device *pdev) retval); } - /* Take control of the lightbar from the EC. */ - lb_manual_suspend_ctrl(ec, 1); - /* We can now add the sysfs class, we know which parameter to show */ retval = cdev_device_add(&ec->cdev, &ec->class_dev); if (retval) { @@ -480,6 +480,15 @@ static int ec_device_probe(struct platform_device *pdev) goto failed; } + retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, + cros_ec_platform_cells, + ARRAY_SIZE(cros_ec_platform_cells), + NULL, 0, NULL); + if (retval) + dev_warn(ec->dev, + "failed to add cros-ec platform devices: %d\n", + retval); + if (cros_ec_debugfs_init(ec)) dev_warn(dev, "failed to create debugfs directory\n"); @@ -494,9 +503,6 @@ static int ec_device_remove(struct platform_device *pdev) { struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); - /* Let the EC take over the lightbar again. */ - lb_manual_suspend_ctrl(ec, 0); - cros_ec_debugfs_remove(ec); mfd_remove_devices(ec->dev); @@ -525,8 +531,6 @@ static __maybe_unused int ec_device_suspend(struct device *dev) cros_ec_debugfs_suspend(ec); - lb_suspend(ec); - return 0; } @@ -536,8 +540,6 @@ static __maybe_unused int ec_device_resume(struct device *dev) cros_ec_debugfs_resume(ec); - lb_resume(ec); - return 0; } diff --git a/drivers/mfd/cros_ec_dev.h b/drivers/mfd/cros_ec_dev.h index 978d836a0248..ec750433455a 100644 --- a/drivers/mfd/cros_ec_dev.h +++ b/drivers/mfd/cros_ec_dev.h @@ -44,10 +44,4 @@ struct cros_ec_readmem { #define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) #define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) -/* Lightbar utilities */ -extern bool ec_has_lightbar(struct cros_ec_dev *ec); -extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable); -extern int lb_suspend(struct cros_ec_dev *ec); -extern int lb_resume(struct cros_ec_dev *ec); - #endif /* _CROS_EC_DEV_H_ */ diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 16b1615958aa..6c05752a3334 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -111,4 +111,15 @@ config CROS_KBD_LED_BACKLIGHT To compile this driver as a module, choose M here: the module will be called cros_kbd_led_backlight. +config CROS_EC_LIGHTBAR + tristate "Chromebook Pixel's lightbar support" + depends on MFD_CROS_EC_CHARDEV + default MFD_CROS_EC_CHARDEV + help + This option exposes the Chromebook Pixel's lightbar to + userspace. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_lightbar. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index cd591bf872bb..3c29a4b405d5 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o -cros_ec_ctl-objs := cros_ec_sysfs.o cros_ec_lightbar.o \ +cros_ec_ctl-objs := cros_ec_sysfs.o \ cros_ec_vbc.o cros_ec_debugfs.o obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o @@ -13,3 +13,4 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o +obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 68193bb53383..80eed6317570 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -33,6 +33,8 @@ #include #include +#define DRV_NAME "cros-ec-lightbar" + /* Rate-limit the lightbar interface to prevent DoS. */ static unsigned long lb_interval_jiffies = 50 * HZ / 1000; @@ -373,7 +375,7 @@ error: return ret; } -int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) +static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) { struct ec_params_lightbar *param; struct cros_ec_command *msg; @@ -408,25 +410,6 @@ error: return ret; } -EXPORT_SYMBOL(lb_manual_suspend_ctrl); - -int lb_suspend(struct cros_ec_dev *ec) -{ - if (userspace_control || ec != ec_with_lightbar) - return 0; - - return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND); -} -EXPORT_SYMBOL(lb_suspend); - -int lb_resume(struct cros_ec_dev *ec) -{ - if (userspace_control || ec != ec_with_lightbar) - return 0; - - return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME); -} -EXPORT_SYMBOL(lb_resume); static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -584,7 +567,7 @@ static struct attribute *__lb_cmds_attrs[] = { NULL, }; -bool ec_has_lightbar(struct cros_ec_dev *ec) +static bool ec_has_lightbar(struct cros_ec_dev *ec) { return !!get_lightbar_version(ec, NULL, NULL); } @@ -616,4 +599,72 @@ struct attribute_group cros_ec_lightbar_attr_group = { .attrs = __lb_cmds_attrs, .is_visible = cros_ec_lightbar_attrs_are_visible, }; -EXPORT_SYMBOL(cros_ec_lightbar_attr_group); + +static int cros_ec_lightbar_probe(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct device *dev = &pd->dev; + int ret; + + /* Take control of the lightbar from the EC. */ + lb_manual_suspend_ctrl(ec_dev, 1); + + ret = sysfs_create_group(&ec_dev->class_dev.kobj, + &cros_ec_lightbar_attr_group); + if (ret < 0) + dev_err(dev, "failed to create %s attributes. err=%d\n", + cros_ec_lightbar_attr_group.name, ret); + + return ret; +} + +static int cros_ec_lightbar_remove(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + + sysfs_remove_group(&ec_dev->class_dev.kobj, + &cros_ec_lightbar_attr_group); + + /* Let the EC take over the lightbar again. */ + lb_manual_suspend_ctrl(ec_dev, 0); + + return 0; +} + +static int __maybe_unused cros_ec_lightbar_resume(struct device *dev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); + + if (userspace_control || ec_dev != ec_with_lightbar) + return 0; + + return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME); +} + +static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); + + if (userspace_control || ec_dev != ec_with_lightbar) + return 0; + + return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND); +} + +static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops, + cros_ec_lightbar_suspend, cros_ec_lightbar_resume); + +static struct platform_driver cros_ec_lightbar_driver = { + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_lightbar_pm_ops, + }, + .probe = cros_ec_lightbar_probe, + .remove = cros_ec_lightbar_remove, +}; + +module_platform_driver(cros_ec_lightbar_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 977ebaa78e99..1e9b569564ea 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -327,7 +327,6 @@ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev); /* sysfs stuff */ extern struct attribute_group cros_ec_attr_group; -extern struct attribute_group cros_ec_lightbar_attr_group; extern struct attribute_group cros_ec_vbc_attr_group; /* debugfs stuff */ From acb9900f9e8074858738f48bee9a705138961258 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:33:58 +0100 Subject: [PATCH 0881/4277] mfd / platform: cros_ec: Move vbc attributes to its own driver The entire way how cros sysfs attibutes are created is broken. cros_ec_vbc should be its own driver and its attributes should be associated with a vbc driver not the mfd driver. In order to retain the path, the vbc attributes are attached to the cros_class. The patch also adds the sysfs documentation. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- .../sysfs-class-chromeos-driver-cros-ec-vbc | 6 +++ drivers/mfd/cros_ec_dev.c | 2 +- drivers/platform/chrome/Kconfig | 11 +++++ drivers/platform/chrome/Makefile | 3 +- drivers/platform/chrome/cros_ec_vbc.c | 43 ++++++++++++++++++- include/linux/mfd/cros_ec.h | 1 - 6 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-vbc diff --git a/Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-vbc b/Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-vbc new file mode 100644 index 000000000000..38c5aaaaa89a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-chromeos-driver-cros-ec-vbc @@ -0,0 +1,6 @@ +What: /sys/class/chromeos//vbc/vboot_context +Date: October 2015 +KernelVersion: 4.4 +Description: + Read/write the verified boot context data included on a + small nvram space on some EC implementations. diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index b227718e0ec2..40c98808fa1c 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -36,7 +36,6 @@ static int ec_major; static const struct attribute_group *cros_ec_groups[] = { &cros_ec_attr_group, - &cros_ec_vbc_attr_group, NULL, }; @@ -396,6 +395,7 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = { static const struct mfd_cell cros_ec_platform_cells[] = { { .name = "cros-ec-lightbar" }, + { .name = "cros-ec-vbc" }, }; static int ec_device_probe(struct platform_device *pdev) diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 6c05752a3334..1e9dc9626e84 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -122,4 +122,15 @@ config CROS_EC_LIGHTBAR To compile this driver as a module, choose M here: the module will be called cros_ec_lightbar. +config CROS_EC_VBC + tristate "ChromeOS EC vboot context support" + depends on MFD_CROS_EC_CHARDEV && OF + default MFD_CROS_EC_CHARDEV + help + This option exposes the ChromeOS EC vboot context nvram to + userspace. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_vbc. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 3c29a4b405d5..4081b7179df7 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o cros_ec_ctl-objs := cros_ec_sysfs.o \ - cros_ec_vbc.o cros_ec_debugfs.o + cros_ec_debugfs.o obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o @@ -14,3 +14,4 @@ obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o +obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 5356f26bc022..da3bbf05e86f 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -22,8 +22,11 @@ #include #include #include +#include #include +#define DRV_NAME "cros-ec-vbc" + static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, struct bin_attribute *att, char *buf, loff_t pos, size_t count) @@ -132,4 +135,42 @@ struct attribute_group cros_ec_vbc_attr_group = { .bin_attrs = cros_ec_vbc_bin_attrs, .is_bin_visible = cros_ec_vbc_is_visible, }; -EXPORT_SYMBOL(cros_ec_vbc_attr_group); + +static int cros_ec_vbc_probe(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct device *dev = &pd->dev; + int ret; + + ret = sysfs_create_group(&ec_dev->class_dev.kobj, + &cros_ec_vbc_attr_group); + if (ret < 0) + dev_err(dev, "failed to create %s attributes. err=%d\n", + cros_ec_vbc_attr_group.name, ret); + + return ret; +} + +static int cros_ec_vbc_remove(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + + sysfs_remove_group(&ec_dev->class_dev.kobj, + &cros_ec_vbc_attr_group); + + return 0; +} + +static struct platform_driver cros_ec_vbc_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_ec_vbc_probe, + .remove = cros_ec_vbc_remove, +}; + +module_platform_driver(cros_ec_vbc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Expose the vboot context nvram to userspace"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 1e9b569564ea..fdc3152cca1d 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -327,7 +327,6 @@ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev); /* sysfs stuff */ extern struct attribute_group cros_ec_attr_group; -extern struct attribute_group cros_ec_vbc_attr_group; /* debugfs stuff */ int cros_ec_debugfs_init(struct cros_ec_dev *ec); From 6fce0a2cf5a050e8a3326556d7d293e69be303be Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:33:59 +0100 Subject: [PATCH 0882/4277] mfd / platform: cros_ec: Move debugfs attributes to its own driver The entire way how cros debugfs attibutes are created is broken. cros_ec_debugfs should be its own driver and its attributes should be associated with a debugfs driver not the mfd driver. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 41 +------------- drivers/platform/chrome/Kconfig | 11 ++++ drivers/platform/chrome/Makefile | 4 +- drivers/platform/chrome/cros_ec_debugfs.c | 66 +++++++++++++++-------- include/linux/mfd/cros_ec.h | 6 --- 5 files changed, 58 insertions(+), 70 deletions(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 40c98808fa1c..9955937b821d 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -394,6 +394,7 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = { }; static const struct mfd_cell cros_ec_platform_cells[] = { + { .name = "cros-ec-debugfs" }, { .name = "cros-ec-lightbar" }, { .name = "cros-ec-vbc" }, }; @@ -489,9 +490,6 @@ static int ec_device_probe(struct platform_device *pdev) "failed to add cros-ec platform devices: %d\n", retval); - if (cros_ec_debugfs_init(ec)) - dev_warn(dev, "failed to create debugfs directory\n"); - return 0; failed: @@ -503,62 +501,25 @@ static int ec_device_remove(struct platform_device *pdev) { struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); - cros_ec_debugfs_remove(ec); - mfd_remove_devices(ec->dev); cdev_del(&ec->cdev); device_unregister(&ec->class_dev); return 0; } -static void ec_device_shutdown(struct platform_device *pdev) -{ - struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); - - /* Be sure to clear up debugfs delayed works */ - cros_ec_debugfs_remove(ec); -} - static const struct platform_device_id cros_ec_id[] = { { DRV_NAME, 0 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, cros_ec_id); -static __maybe_unused int ec_device_suspend(struct device *dev) -{ - struct cros_ec_dev *ec = dev_get_drvdata(dev); - - cros_ec_debugfs_suspend(ec); - - return 0; -} - -static __maybe_unused int ec_device_resume(struct device *dev) -{ - struct cros_ec_dev *ec = dev_get_drvdata(dev); - - cros_ec_debugfs_resume(ec); - - return 0; -} - -static const struct dev_pm_ops cros_ec_dev_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend = ec_device_suspend, - .resume = ec_device_resume, -#endif -}; - static struct platform_driver cros_ec_dev_driver = { .driver = { .name = DRV_NAME, - .pm = &cros_ec_dev_pm_ops, }, .id_table = cros_ec_id, .probe = ec_device_probe, .remove = ec_device_remove, - .shutdown = ec_device_shutdown, }; static int __init cros_ec_dev_init(void) diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 1e9dc9626e84..6cbf5b69d156 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -133,4 +133,15 @@ config CROS_EC_VBC To compile this driver as a module, choose M here: the module will be called cros_ec_vbc. +config CROS_EC_DEBUGFS + tristate "Export ChromeOS EC internals in DebugFS" + depends on MFD_CROS_EC_CHARDEV && DEBUG_FS + default MFD_CROS_EC_CHARDEV + help + This option exposes the ChromeOS EC device internals to + userspace. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_debugfs. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 4081b7179df7..12a5c4d18c17 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -3,8 +3,7 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o -cros_ec_ctl-objs := cros_ec_sysfs.o \ - cros_ec_debugfs.o +cros_ec_ctl-objs := cros_ec_sysfs.o obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o @@ -15,3 +14,4 @@ obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o +obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index c62ee8e610a0..6cacac53dfce 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -23,12 +23,16 @@ #include #include #include +#include #include +#include #include #include #include #include +#define DRV_NAME "cros-ec-debugfs" + #define LOG_SHIFT 14 #define LOG_SIZE (1 << LOG_SHIFT) #define LOG_POLL_SEC 10 @@ -423,8 +427,9 @@ static int cros_ec_create_pdinfo(struct cros_ec_debugfs *debug_info) return 0; } -int cros_ec_debugfs_init(struct cros_ec_dev *ec) +static int cros_ec_debugfs_probe(struct platform_device *pd) { + struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent); struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev); const char *name = ec_platform->ec_name; struct cros_ec_debugfs *debug_info; @@ -453,40 +458,57 @@ int cros_ec_debugfs_init(struct cros_ec_dev *ec) ec->debug_info = debug_info; + dev_set_drvdata(&pd->dev, ec); + return 0; remove_debugfs: debugfs_remove_recursive(debug_info->dir); return ret; } -EXPORT_SYMBOL(cros_ec_debugfs_init); -void cros_ec_debugfs_remove(struct cros_ec_dev *ec) +static int cros_ec_debugfs_remove(struct platform_device *pd) { - if (!ec->debug_info) - return; + struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent); debugfs_remove_recursive(ec->debug_info->dir); cros_ec_cleanup_console_log(ec->debug_info); -} -EXPORT_SYMBOL(cros_ec_debugfs_remove); -void cros_ec_debugfs_suspend(struct cros_ec_dev *ec) -{ - /* - * cros_ec_debugfs_init() failures are non-fatal; it's also possible - * that we initted things but decided that console log wasn't supported. - * We'll use the same set of checks that cros_ec_debugfs_remove() + - * cros_ec_cleanup_console_log() end up using to handle those cases. - */ - if (ec->debug_info && ec->debug_info->log_buffer.buf) - cancel_delayed_work_sync(&ec->debug_info->log_poll_work); + return 0; } -EXPORT_SYMBOL(cros_ec_debugfs_suspend); -void cros_ec_debugfs_resume(struct cros_ec_dev *ec) +static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev) { - if (ec->debug_info && ec->debug_info->log_buffer.buf) - schedule_delayed_work(&ec->debug_info->log_poll_work, 0); + struct cros_ec_dev *ec = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ec->debug_info->log_poll_work); + + return 0; } -EXPORT_SYMBOL(cros_ec_debugfs_resume); + +static int __maybe_unused cros_ec_debugfs_resume(struct device *dev) +{ + struct cros_ec_dev *ec = dev_get_drvdata(dev); + + schedule_delayed_work(&ec->debug_info->log_poll_work, 0); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops, + cros_ec_debugfs_suspend, cros_ec_debugfs_resume); + +static struct platform_driver cros_ec_debugfs_driver = { + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_debugfs_pm_ops, + }, + .probe = cros_ec_debugfs_probe, + .remove = cros_ec_debugfs_remove, +}; + +module_platform_driver(cros_ec_debugfs_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Debug logs for ChromeOS EC"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index fdc3152cca1d..e50860d190db 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -328,10 +328,4 @@ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev); /* sysfs stuff */ extern struct attribute_group cros_ec_attr_group; -/* debugfs stuff */ -int cros_ec_debugfs_init(struct cros_ec_dev *ec); -void cros_ec_debugfs_remove(struct cros_ec_dev *ec); -void cros_ec_debugfs_suspend(struct cros_ec_dev *ec); -void cros_ec_debugfs_resume(struct cros_ec_dev *ec); - #endif /* __LINUX_MFD_CROS_EC_H */ From 6fd7f2bbd4422e7635bc771cd1ec440378158cb1 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:34:00 +0100 Subject: [PATCH 0883/4277] mfd / platform: cros_ec: Move device sysfs attributes to its own driver The entire way how cros debugfs attibutes are created is broken. cros_ec_sysfs should be its own driver and its attributes should be associated with the sysfs driver not the mfd driver. The patch also adds the sysfs documentation. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- .../ABI/testing/sysfs-class-chromeos | 32 +++++++++++++++++ drivers/mfd/Kconfig | 1 - drivers/mfd/cros_ec_dev.c | 7 +--- drivers/platform/chrome/Kconfig | 14 ++++++-- drivers/platform/chrome/Makefile | 3 +- drivers/platform/chrome/cros_ec_lightbar.c | 2 +- drivers/platform/chrome/cros_ec_sysfs.c | 36 ++++++++++++++++++- include/linux/mfd/cros_ec.h | 3 -- 8 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-chromeos diff --git a/Documentation/ABI/testing/sysfs-class-chromeos b/Documentation/ABI/testing/sysfs-class-chromeos new file mode 100644 index 000000000000..5819699d66ec --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-chromeos @@ -0,0 +1,32 @@ +What: /sys/class/chromeos//flashinfo +Date: August 2015 +KernelVersion: 4.2 +Description: + Show the EC flash information. + +What: /sys/class/chromeos//kb_wake_angle +Date: March 2018 +KernelVersion: 4.17 +Description: + Control the keyboard wake lid angle. Values are between + 0 and 360. This file will also show the keyboard wake lid + angle by querying the hardware. + +What: /sys/class/chromeos//reboot +Date: August 2015 +KernelVersion: 4.2 +Description: + Tell the EC to reboot in various ways. Options are: + "cancel": Cancel a pending reboot. + "ro": Jump to RO without rebooting. + "rw": Jump to RW without rebooting. + "cold": Cold reboot. + "disable-jump": Disable jump until next reboot. + "hibernate": Hibernate the EC. + "at-shutdown": Reboot after an AP shutdown. + +What: /sys/class/chromeos//version +Date: August 2015 +KernelVersion: 4.2 +Description: + Show the information about the EC software and hardware. diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f461460a2aeb..2acc105e43cc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -215,7 +215,6 @@ config MFD_CROS_EC config MFD_CROS_EC_CHARDEV tristate "Chrome OS Embedded Controller userspace device interface" depends on MFD_CROS_EC - select CROS_EC_CTL ---help--- This driver adds support to talk with the ChromeOS EC from userspace. diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 9955937b821d..b9ec2a798dbb 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -34,15 +34,9 @@ #define CROS_MAX_DEV 128 static int ec_major; -static const struct attribute_group *cros_ec_groups[] = { - &cros_ec_attr_group, - NULL, -}; - static struct class cros_class = { .owner = THIS_MODULE, .name = "chromeos", - .dev_groups = cros_ec_groups, }; /* Basic communication */ @@ -396,6 +390,7 @@ static const struct mfd_cell cros_usbpd_charger_cells[] = { static const struct mfd_cell cros_ec_platform_cells[] = { { .name = "cros-ec-debugfs" }, { .name = "cros-ec-lightbar" }, + { .name = "cros-ec-sysfs" }, { .name = "cros-ec-vbc" }, }; diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 6cbf5b69d156..5e2fde5ff63d 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -49,9 +49,6 @@ config CHROMEOS_TBMC To compile this driver as a module, choose M here: the module will be called chromeos_tbmc. -config CROS_EC_CTL - tristate - config CROS_EC_I2C tristate "ChromeOS Embedded Controller (I2C)" depends on MFD_CROS_EC && I2C @@ -144,4 +141,15 @@ config CROS_EC_DEBUGFS To compile this driver as a module, choose M here: the module will be called cros_ec_debugfs. +config CROS_EC_SYSFS + tristate "ChromeOS EC control and information through sysfs" + depends on MFD_CROS_EC_CHARDEV && SYSFS + default MFD_CROS_EC_CHARDEV + help + This option exposes some sysfs attributes to control and get + information from ChromeOS EC. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_sysfs. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 12a5c4d18c17..fdbee501931b 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -3,8 +3,6 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o -cros_ec_ctl-objs := cros_ec_sysfs.o -obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o @@ -15,3 +13,4 @@ obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o +obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 80eed6317570..c22318ba93aa 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -594,7 +594,7 @@ static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, return 0; } -struct attribute_group cros_ec_lightbar_attr_group = { +static struct attribute_group cros_ec_lightbar_attr_group = { .name = "lightbar", .attrs = __lb_cmds_attrs, .is_visible = cros_ec_lightbar_attrs_are_visible, diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index f34a50121064..0ff5aa30c070 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -34,6 +34,8 @@ #include #include +#define DRV_NAME "cros-ec-sysfs" + /* Accessor functions */ static ssize_t reboot_show(struct device *dev, @@ -353,7 +355,39 @@ struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, .is_visible = cros_ec_ctrl_visible, }; -EXPORT_SYMBOL(cros_ec_attr_group); + +static int cros_ec_sysfs_probe(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct device *dev = &pd->dev; + int ret; + + ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group); + if (ret < 0) + dev_err(dev, "failed to create attributes. err=%d\n", ret); + + return ret; +} + +static int cros_ec_sysfs_remove(struct platform_device *pd) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + + sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group); + + return 0; +} + +static struct platform_driver cros_ec_sysfs_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_ec_sysfs_probe, + .remove = cros_ec_sysfs_remove, +}; + +module_platform_driver(cros_ec_sysfs_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ChromeOS EC control driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index e50860d190db..8f2a8918bfa3 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -325,7 +325,4 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event); */ u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev); -/* sysfs stuff */ -extern struct attribute_group cros_ec_attr_group; - #endif /* __LINUX_MFD_CROS_EC_H */ From 0545625baa5981bb0a583e6a6045155936d3ea95 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:34:01 +0100 Subject: [PATCH 0884/4277] mfd / platform: cros_ec_vbc: Instantiate only if the EC has a VBC NVRAM The cros-ec-vbc driver is DT-only and there is a DT property that indicates if the EC has the VCB NVRAM, in such case instantiate the driver but don't instantiate on the other cases. To do this move the check code to its parent instead of play with the attribute group visibility. This changes a bit the actual behaviour. Before the patch if an EC doesn't have a VBC NVRAM an empty vbc folder is created in /sys/class/chromeos/, after the patch the empty folder is not created, so, the folder is only created if the vbc is set. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 19 ++++++++++++++++++- drivers/platform/chrome/cros_ec_vbc.c | 16 ---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index b9ec2a798dbb..ed809fc97df8 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -391,12 +392,16 @@ static const struct mfd_cell cros_ec_platform_cells[] = { { .name = "cros-ec-debugfs" }, { .name = "cros-ec-lightbar" }, { .name = "cros-ec-sysfs" }, - { .name = "cros-ec-vbc" }, +}; + +static const struct mfd_cell cros_ec_vbc_cells[] = { + { .name = "cros-ec-vbc" } }; static int ec_device_probe(struct platform_device *pdev) { int retval = -ENOMEM; + struct device_node *node; struct device *dev = &pdev->dev; struct cros_ec_platform *ec_platform = dev_get_platdata(dev); struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); @@ -485,6 +490,18 @@ static int ec_device_probe(struct platform_device *pdev) "failed to add cros-ec platform devices: %d\n", retval); + /* Check whether this EC instance has a VBC NVRAM */ + node = ec->ec_dev->dev->of_node; + if (of_property_read_bool(node, "google,has-vbc-nvram")) { + retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, + cros_ec_vbc_cells, + ARRAY_SIZE(cros_ec_vbc_cells), + NULL, 0, NULL); + if (retval) + dev_warn(ec->dev, "failed to add VBC devices: %d\n", + retval); + } + return 0; failed: diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index da3bbf05e86f..af9bfe6f385c 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -108,21 +108,6 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, return data_sz; } -static umode_t cros_ec_vbc_is_visible(struct kobject *kobj, - struct bin_attribute *a, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct cros_ec_dev *ec = to_cros_ec_dev(dev); - struct device_node *np = ec->ec_dev->dev->of_node; - - if (IS_ENABLED(CONFIG_OF) && np) { - if (of_property_read_bool(np, "google,has-vbc-nvram")) - return a->attr.mode; - } - - return 0; -} - static BIN_ATTR_RW(vboot_context, 16); static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { @@ -133,7 +118,6 @@ static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { struct attribute_group cros_ec_vbc_attr_group = { .name = "vbc", .bin_attrs = cros_ec_vbc_bin_attrs, - .is_bin_visible = cros_ec_vbc_is_visible, }; static int cros_ec_vbc_probe(struct platform_device *pd) From fd68bd0f5d4c2090c95f84e27b05d0836bcd6c0c Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 12 Dec 2018 18:34:02 +0100 Subject: [PATCH 0885/4277] platform/chrome: cros_ec_lightbar: Instantiate only if the EC has a lightbar Due to the way attribute groups visibility work, the function cros_ec_lightbar_attrs_are_visible is called multiple times, once per attribute, and each of these calls makes an EC transaction. For what is worth the EC log reports multiple errors on boot when the lightbar is not available. Instead, check if the EC has a lightbar in the probe function and only instantiate the device. Ideally we should have instantiate the driver only if the EC_FEATURE_LIGHTBAR is defined, but that's not possible because that flag is not in the very first Pixel Chromebook (Link), only on Samus. So, the driver is instantiated by his parent always. This patch changes a bit the actual behaviour. Before the patch if an EC doesn't have a lightbar an empty lightbar folder is created in /sys/class/chromeos/, after the patch the empty folder is not created, so, the folder is only created if the lightbar exists. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/platform/chrome/cros_ec_lightbar.c | 53 ++++++++-------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index c22318ba93aa..c3e4e6e5211d 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -43,7 +43,6 @@ static unsigned long lb_interval_jiffies = 50 * HZ / 1000; * If this is true, we won't do anything during suspend/resume. */ static bool userspace_control; -static struct cros_ec_dev *ec_with_lightbar; static ssize_t interval_msec_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -381,9 +380,6 @@ static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) struct cros_ec_command *msg; int ret; - if (ec != ec_with_lightbar) - return 0; - msg = alloc_lightbar_cmd_msg(ec); if (!msg) return -ENOMEM; @@ -567,45 +563,32 @@ static struct attribute *__lb_cmds_attrs[] = { NULL, }; -static bool ec_has_lightbar(struct cros_ec_dev *ec) -{ - return !!get_lightbar_version(ec, NULL, NULL); -} - -static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct cros_ec_dev *ec = to_cros_ec_dev(dev); - struct platform_device *pdev = to_platform_device(ec->dev); - struct cros_ec_platform *pdata = pdev->dev.platform_data; - int is_cros_ec; - - is_cros_ec = strcmp(pdata->ec_name, CROS_EC_DEV_NAME); - - if (is_cros_ec != 0) - return 0; - - /* Only instantiate this stuff if the EC has a lightbar */ - if (ec_has_lightbar(ec)) { - ec_with_lightbar = ec; - return a->mode; - } - return 0; -} - -static struct attribute_group cros_ec_lightbar_attr_group = { +struct attribute_group cros_ec_lightbar_attr_group = { .name = "lightbar", .attrs = __lb_cmds_attrs, - .is_visible = cros_ec_lightbar_attrs_are_visible, }; static int cros_ec_lightbar_probe(struct platform_device *pd) { struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent); + struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev); struct device *dev = &pd->dev; int ret; + /* + * Only instantiate the lightbar if the EC name is 'cros_ec'. Other EC + * devices like 'cros_pd' doesn't have a lightbar. + */ + if (strcmp(pdata->ec_name, CROS_EC_DEV_NAME) != 0) + return -ENODEV; + + /* + * Ask then for the lightbar version, if it's 0 then the 'cros_ec' + * doesn't have a lightbar. + */ + if (!get_lightbar_version(ec_dev, NULL, NULL)) + return -ENODEV; + /* Take control of the lightbar from the EC. */ lb_manual_suspend_ctrl(ec_dev, 1); @@ -635,7 +618,7 @@ static int __maybe_unused cros_ec_lightbar_resume(struct device *dev) { struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); - if (userspace_control || ec_dev != ec_with_lightbar) + if (userspace_control) return 0; return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME); @@ -645,7 +628,7 @@ static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev) { struct cros_ec_dev *ec_dev = dev_get_drvdata(dev); - if (userspace_control || ec_dev != ec_with_lightbar) + if (userspace_control) return 0; return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND); From 17f808a7fec32cf3610f92a095b562c915fd254a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:32 -0500 Subject: [PATCH 0886/4277] mfd: aat2870-core: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_AAT2870_CORE drivers/mfd/Kconfig: bool "AnalogicTech AAT2870" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Jin Park Signed-off-by: Lee Jones --- drivers/mfd/aat2870-core.c | 40 +++----------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index 3ba19a45f199..9d3d90d386c2 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -20,7 +20,6 @@ */ #include -#include #include #include #include @@ -349,18 +348,10 @@ static void aat2870_init_debugfs(struct aat2870_data *aat2870) "Failed to create debugfs register file\n"); } -static void aat2870_uninit_debugfs(struct aat2870_data *aat2870) -{ - debugfs_remove_recursive(aat2870->dentry_root); -} #else static inline void aat2870_init_debugfs(struct aat2870_data *aat2870) { } - -static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870) -{ -} #endif /* CONFIG_DEBUG_FS */ static int aat2870_i2c_probe(struct i2c_client *client, @@ -440,20 +431,6 @@ out_disable: return ret; } -static int aat2870_i2c_remove(struct i2c_client *client) -{ - struct aat2870_data *aat2870 = i2c_get_clientdata(client); - - aat2870_uninit_debugfs(aat2870); - - mfd_remove_devices(aat2870->dev); - aat2870_disable(aat2870); - if (aat2870->uninit) - aat2870->uninit(aat2870); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int aat2870_i2c_suspend(struct device *dev) { @@ -492,15 +469,14 @@ static const struct i2c_device_id aat2870_i2c_id_table[] = { { "aat2870", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table); static struct i2c_driver aat2870_i2c_driver = { .driver = { - .name = "aat2870", - .pm = &aat2870_pm_ops, + .name = "aat2870", + .pm = &aat2870_pm_ops, + .suppress_bind_attrs = true, }, .probe = aat2870_i2c_probe, - .remove = aat2870_i2c_remove, .id_table = aat2870_i2c_id_table, }; @@ -509,13 +485,3 @@ static int __init aat2870_init(void) return i2c_add_driver(&aat2870_i2c_driver); } subsys_initcall(aat2870_init); - -static void __exit aat2870_exit(void) -{ - i2c_del_driver(&aat2870_i2c_driver); -} -module_exit(aat2870_exit); - -MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jin Park "); From 6b09274bacc76101293d6d8ccbded51448b11fd1 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:33 -0500 Subject: [PATCH 0887/4277] mfd: adp5520: Make it explicitly non-modular The Makefile/Kconfig currently controlling compilation of this code is: drivers/mfd/Makefile:obj-$(CONFIG_PMIC_ADP5520) += adp5520.o drivers/mfd/Kconfig:config PMIC_ADP5520 drivers/mfd/Kconfig: bool "Analog Devices ADP5520/01 MFD PMIC Core Support" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_i2c_driver() uses the same init level priority as builtin_i2c_driver() the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Michael Hennerich Signed-off-by: Lee Jones --- drivers/mfd/adp5520.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index be0497b96720..2cdd39cb8a18 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -7,6 +7,8 @@ * * Copyright 2009 Analog Devices Inc. * + * Author: Michael Hennerich + * * Derived from da903x: * Copyright (C) 2008 Compulab, Ltd. * Mike Rapoport @@ -18,7 +20,7 @@ */ #include -#include +#include #include #include #include @@ -304,18 +306,6 @@ out_free_irq: return ret; } -static int adp5520_remove(struct i2c_client *client) -{ - struct adp5520_chip *chip = dev_get_drvdata(&client->dev); - - if (chip->irq) - free_irq(chip->irq, chip); - - adp5520_remove_subdevs(chip); - adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0); - return 0; -} - #ifdef CONFIG_PM_SLEEP static int adp5520_suspend(struct device *dev) { @@ -346,20 +336,14 @@ static const struct i2c_device_id adp5520_id[] = { { "pmic-adp5501", ID_ADP5501 }, { } }; -MODULE_DEVICE_TABLE(i2c, adp5520_id); static struct i2c_driver adp5520_driver = { .driver = { - .name = "adp5520", - .pm = &adp5520_pm, + .name = "adp5520", + .pm = &adp5520_pm, + .suppress_bind_attrs = true, }, .probe = adp5520_probe, - .remove = adp5520_remove, .id_table = adp5520_id, }; - -module_i2c_driver(adp5520_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver"); -MODULE_LICENSE("GPL"); +builtin_i2c_driver(adp5520_driver); From 1e89d907460628fe8cc8e0bb604879d2e055d744 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:34 -0500 Subject: [PATCH 0888/4277] mfd: as3711: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_AS3711 drivers/mfd/Kconfig: bool "AMS AS3711" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. We don't replace module.h with init.h since the file already has that. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/as3711.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index 67b12417585d..7a74a874b93c 100644 --- a/drivers/mfd/as3711.c +++ b/drivers/mfd/as3711.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -118,7 +117,6 @@ static const struct of_device_id as3711_of_match[] = { {.compatible = "ams,as3711",}, {} }; -MODULE_DEVICE_TABLE(of, as3711_of_match); #endif static int as3711_i2c_probe(struct i2c_client *client, @@ -202,8 +200,6 @@ static const struct i2c_device_id as3711_i2c_id[] = { {} }; -MODULE_DEVICE_TABLE(i2c, as3711_i2c_id); - static struct i2c_driver as3711_i2c_driver = { .driver = { .name = "as3711", @@ -219,13 +215,3 @@ static int __init as3711_i2c_init(void) } /* Initialise early */ subsys_initcall(as3711_i2c_init); - -static void __exit as3711_i2c_exit(void) -{ - i2c_del_driver(&as3711_i2c_driver); -} -module_exit(as3711_i2c_exit); - -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_DESCRIPTION("AS3711 PMIC driver"); -MODULE_LICENSE("GPL v2"); From adef9cf549be912f559e82918ecbe18d54649cee Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:35 -0500 Subject: [PATCH 0889/4277] mfd: db8500-prcmu: Drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_DB8500_PRCMU drivers/mfd/Kconfig: bool "ST-Ericsson DB8500 Power Reset Control Management Unit" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. We replace module.h with init.h and export.h ; the latter since the file does export some symbols. Signed-off-by: Paul Gortmaker Reviewed-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/db8500-prcmu.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index aec20e1c7d3d..65666b624ae8 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1,4 +1,6 @@ /* + * DB8500 PRCM Unit driver + * * Copyright (C) STMicroelectronics 2009 * Copyright (C) ST-Ericsson SA 2010 * @@ -10,7 +12,8 @@ * U8500 PRCM Unit interface driver * */ -#include +#include +#include #include #include #include @@ -3188,9 +3191,4 @@ static int __init db8500_prcmu_init(void) { return platform_driver_register(&db8500_prcmu_driver); } - core_initcall(db8500_prcmu_init); - -MODULE_AUTHOR("Mattias Nilsson "); -MODULE_DESCRIPTION("DB8500 PRCM Unit driver"); -MODULE_LICENSE("GPL v2"); From 11165223e9a617798268a1c743364ead4395b905 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:36 -0500 Subject: [PATCH 0890/4277] mfd: htc-i2cpld: Make it explicitly non-modular The Kconfig for this option is currently: config HTC_I2CPLD bool "HTC I2C PLD chip support" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/htc-i2cpld.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c index 01572b5e79e8..af3c66355270 100644 --- a/drivers/mfd/htc-i2cpld.c +++ b/drivers/mfd/htc-i2cpld.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -614,8 +613,6 @@ static const struct i2c_device_id htcpld_chip_id[] = { { "htcpld-chip", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, htcpld_chip_id); - static struct i2c_driver htcpld_chip_driver = { .driver = { @@ -643,17 +640,4 @@ static int __init htcpld_core_init(void) /* Probe for our chips */ return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe); } - -static void __exit htcpld_core_exit(void) -{ - i2c_del_driver(&htcpld_chip_driver); - platform_driver_unregister(&htcpld_core_driver); -} - -module_init(htcpld_core_init); -module_exit(htcpld_core_exit); - -MODULE_AUTHOR("Cory Maccarrone "); -MODULE_DESCRIPTION("I2C HTC PLD Driver"); -MODULE_LICENSE("GPL"); - +device_initcall(htcpld_core_init); From b51bf15c6cdcbbb2462d7985b86667cdce3a4b92 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:37 -0500 Subject: [PATCH 0891/4277] mfd: max8925-core: Drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_MAX8925 drivers/mfd/Kconfig: bool "Maxim Semiconductor MAX8925 PMIC Support" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/max8925-core.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index fd8b15cd84fd..87c724ba9793 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -10,7 +10,7 @@ */ #include -#include +#include #include #include #include @@ -919,8 +919,3 @@ void max8925_device_exit(struct max8925_chip *chip) free_irq(chip->tsc_irq, chip); mfd_remove_devices(chip->dev); } - - -MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925"); -MODULE_AUTHOR("Haojian Zhuang Date: Sun, 13 Jan 2019 13:36:38 -0500 Subject: [PATCH 0892/4277] mfd: rc5t583: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_RC5T583 drivers/mfd/Kconfig: bool "Ricoh RC5T583 Power Management system device" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Laxman Dewangan Signed-off-by: Lee Jones --- drivers/mfd/rc5t583.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index fd46de02b715..c5cc5cb3dde7 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -298,8 +297,6 @@ static const struct i2c_device_id rc5t583_i2c_id[] = { {} }; -MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id); - static struct i2c_driver rc5t583_i2c_driver = { .driver = { .name = "rc5t583", @@ -313,14 +310,3 @@ static int __init rc5t583_i2c_init(void) return i2c_add_driver(&rc5t583_i2c_driver); } subsys_initcall(rc5t583_i2c_init); - -static void __exit rc5t583_i2c_exit(void) -{ - i2c_del_driver(&rc5t583_i2c_driver); -} - -module_exit(rc5t583_i2c_exit); - -MODULE_AUTHOR("Laxman Dewangan "); -MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver"); -MODULE_LICENSE("GPL v2"); From cf090914da11e067a6f0438651912929c8b038f2 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:39 -0500 Subject: [PATCH 0893/4277] mfd: sta2x11: Drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_STA2X11 drivers/mfd/Kconfig: bool "STMicroelectronics STA2X11" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We replace module.h with init.h and export.h ; the latter since the file does export some symbols. Signed-off-by: Paul Gortmaker Acked-by: Alessandro Rubini Acked-by: Davide Ciminaghi Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/sta2x11-mfd.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 3aeafa228baf..cab9aabcaa1f 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -1,4 +1,6 @@ /* + * STA2x11 mfd for GPIO, SCTL and APBREG + * * Copyright (c) 2009-2011 Wind River Systems, Inc. * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi) * @@ -18,7 +20,8 @@ */ #include -#include +#include +#include #include #include #include @@ -653,8 +656,3 @@ static int __init sta2x11_mfd_init(void) */ subsys_initcall(sta2x11_drivers_init); rootfs_initcall(sta2x11_mfd_init); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Wind River"); -MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG"); -MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl); From 1345da73f6d100568e1fa55376759688c74fcae5 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:40 -0500 Subject: [PATCH 0894/4277] mfd: syscon: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_SYSCON drivers/mfd/Kconfig: bool "System Controller Register R/W Based on Regmap" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/syscon.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index b6d05cd934e6..0ecdffb3d967 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -272,13 +272,3 @@ static int __init syscon_init(void) return platform_driver_register(&syscon_driver); } postcore_initcall(syscon_init); - -static void __exit syscon_exit(void) -{ - platform_driver_unregister(&syscon_driver); -} -module_exit(syscon_exit); - -MODULE_AUTHOR("Dong Aisheng "); -MODULE_DESCRIPTION("System Control driver"); -MODULE_LICENSE("GPL v2"); From 26fce5e099d76651a871e738e858f18bc7829d84 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:41 -0500 Subject: [PATCH 0895/4277] mfd: tps65090: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_TPS65090 drivers/mfd/Kconfig: bool "TI TPS65090 Power Management chips" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/tps65090.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index f13e4cd06e89..6968df4d7828 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -2,7 +2,9 @@ * Core driver for TI TPS65090 PMIC family * * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - + * + * Author: Venu Byravarasu + * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -19,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -171,7 +173,6 @@ static const struct of_device_id tps65090_of_match[] = { { .compatible = "ti,tps65090",}, {}, }; -MODULE_DEVICE_TABLE(of, tps65090_of_match); #endif static int tps65090_i2c_probe(struct i2c_client *client, @@ -236,30 +237,19 @@ err_irq_exit: return ret; } -static int tps65090_i2c_remove(struct i2c_client *client) -{ - struct tps65090 *tps65090 = i2c_get_clientdata(client); - - mfd_remove_devices(tps65090->dev); - if (client->irq) - regmap_del_irq_chip(client->irq, tps65090->irq_data); - - return 0; -} static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, { }, }; -MODULE_DEVICE_TABLE(i2c, tps65090_id_table); static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", + .suppress_bind_attrs = true, .of_match_table = of_match_ptr(tps65090_of_match), }, .probe = tps65090_i2c_probe, - .remove = tps65090_i2c_remove, .id_table = tps65090_id_table, }; @@ -268,13 +258,3 @@ static int __init tps65090_init(void) return i2c_add_driver(&tps65090_driver); } subsys_initcall(tps65090_init); - -static void __exit tps65090_exit(void) -{ - i2c_del_driver(&tps65090_driver); -} -module_exit(tps65090_exit); - -MODULE_DESCRIPTION("TPS65090 core driver"); -MODULE_AUTHOR("Venu Byravarasu "); -MODULE_LICENSE("GPL v2"); From a8799defb4afd64405a077f1aa98552d3e79169a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:42 -0500 Subject: [PATCH 0896/4277] mfd: tps65910: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_TPS65910 drivers/mfd/Kconfig- bool "TI TPS65910 Power Management chip" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. We don't replace module.h with init.h since the file already has that. We do delete an unused moduleparam.h include though. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/tps65910.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index bf16cbe6fd88..aa3d472a10ff 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -1,5 +1,5 @@ /* - * tps65910.c -- TI TPS6591x + * tps65910.c -- TI TPS6591x chip family multi-function driver * * Copyright 2010 Texas Instruments Inc. * @@ -13,8 +13,6 @@ * */ -#include -#include #include #include #include @@ -374,7 +372,6 @@ static const struct of_device_id tps65910_of_match[] = { { .compatible = "ti,tps65911", .data = (void *)TPS65911}, { }, }; -MODULE_DEVICE_TABLE(of, tps65910_of_match); static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, unsigned long *chip_id) @@ -527,8 +524,6 @@ static const struct i2c_device_id tps65910_i2c_id[] = { { "tps65911", TPS65911 }, { } }; -MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id); - static struct i2c_driver tps65910_i2c_driver = { .driver = { @@ -545,14 +540,3 @@ static int __init tps65910_i2c_init(void) } /* init early so consumer devices can complete system boot */ subsys_initcall(tps65910_i2c_init); - -static void __exit tps65910_i2c_exit(void) -{ - i2c_del_driver(&tps65910_i2c_driver); -} -module_exit(tps65910_i2c_exit); - -MODULE_AUTHOR("Graeme Gregory "); -MODULE_AUTHOR("Jorge Eduardo Candelaria "); -MODULE_DESCRIPTION("TPS6591x chip family multi-function driver"); -MODULE_LICENSE("GPL"); From 6b5e1877e487b5bd5814dfc4e56956906792029f Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:43 -0500 Subject: [PATCH 0897/4277] mfd: tps80031: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_TPS80031 drivers/mfd/Kconfig: bool "TI TPS80031/TPS80032 Power Management chips" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. We don't replace module.h with init.h since the file already has that. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Laxman Dewangan Signed-off-by: Lee Jones --- drivers/mfd/tps80031.c | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index 608c7f77830e..865257ade8ac 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -516,40 +515,18 @@ fail_client_reg: return ret; } -static int tps80031_remove(struct i2c_client *client) -{ - struct tps80031 *tps80031 = i2c_get_clientdata(client); - int i; - - if (tps80031_power_off_dev == tps80031) { - tps80031_power_off_dev = NULL; - pm_power_off = NULL; - } - - mfd_remove_devices(tps80031->dev); - - regmap_del_irq_chip(client->irq, tps80031->irq_data); - - for (i = 0; i < TPS80031_NUM_SLAVES; i++) { - if (tps80031->clients[i] != client) - i2c_unregister_device(tps80031->clients[i]); - } - return 0; -} - static const struct i2c_device_id tps80031_id_table[] = { { "tps80031", TPS80031 }, { "tps80032", TPS80032 }, { } }; -MODULE_DEVICE_TABLE(i2c, tps80031_id_table); static struct i2c_driver tps80031_driver = { .driver = { - .name = "tps80031", + .name = "tps80031", + .suppress_bind_attrs = true, }, .probe = tps80031_probe, - .remove = tps80031_remove, .id_table = tps80031_id_table, }; @@ -558,13 +535,3 @@ static int __init tps80031_init(void) return i2c_add_driver(&tps80031_driver); } subsys_initcall(tps80031_init); - -static void __exit tps80031_exit(void) -{ - i2c_del_driver(&tps80031_driver); -} -module_exit(tps80031_exit); - -MODULE_AUTHOR("Laxman Dewangan "); -MODULE_DESCRIPTION("TPS80031 core driver"); -MODULE_LICENSE("GPL v2"); From e85c5f0a1b429d9de68bc6493526afb694908bdd Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:44 -0500 Subject: [PATCH 0898/4277] mfd: wm831x-spi: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_WM831X_SPI drivers/mfd/Kconfig: bool "Wolfson Microelectronics WM831x/2x PMICs with SPI" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/wm831x-spi.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 018ce652ae57..dd4dab419940 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -13,7 +13,7 @@ */ #include -#include +#include #include #include #include @@ -67,15 +67,6 @@ static int wm831x_spi_probe(struct spi_device *spi) return wm831x_device_init(wm831x, spi->irq); } -static int wm831x_spi_remove(struct spi_device *spi) -{ - struct wm831x *wm831x = spi_get_drvdata(spi); - - wm831x_device_exit(wm831x); - - return 0; -} - static int wm831x_spi_suspend(struct device *dev) { struct wm831x *wm831x = dev_get_drvdata(dev); @@ -108,17 +99,16 @@ static const struct spi_device_id wm831x_spi_ids[] = { { "wm8326", WM8326 }, { }, }; -MODULE_DEVICE_TABLE(spi, wm831x_spi_ids); static struct spi_driver wm831x_spi_driver = { .driver = { .name = "wm831x", .pm = &wm831x_spi_pm, .of_match_table = of_match_ptr(wm831x_of_match), + .suppress_bind_attrs = true, }, .id_table = wm831x_spi_ids, .probe = wm831x_spi_probe, - .remove = wm831x_spi_remove, }; static int __init wm831x_spi_init(void) @@ -132,13 +122,3 @@ static int __init wm831x_spi_init(void) return 0; } subsys_initcall(wm831x_spi_init); - -static void __exit wm831x_spi_exit(void) -{ - spi_unregister_driver(&wm831x_spi_driver); -} -module_exit(wm831x_spi_exit); - -MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Brown"); From af5db808a41f1d63ecc835024d6d3b169600f36d Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:45 -0500 Subject: [PATCH 0899/4277] mfd: wm831x-i2c: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_WM831X_I2C drivers/mfd/Kconfig: bool "Wolfson Microelectronics WM831x/2x PMICs with I2C" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm831x-i2c.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 22f7054d1b28..0f3af42f7268 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -13,7 +13,7 @@ */ #include -#include +#include #include #include #include @@ -68,15 +68,6 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, return wm831x_device_init(wm831x, i2c->irq); } -static int wm831x_i2c_remove(struct i2c_client *i2c) -{ - struct wm831x *wm831x = i2c_get_clientdata(i2c); - - wm831x_device_exit(wm831x); - - return 0; -} - static int wm831x_i2c_suspend(struct device *dev) { struct wm831x *wm831x = dev_get_drvdata(dev); @@ -103,7 +94,6 @@ static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8326", WM8326 }, { } }; -MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); static const struct dev_pm_ops wm831x_pm_ops = { .suspend = wm831x_i2c_suspend, @@ -115,9 +105,9 @@ static struct i2c_driver wm831x_i2c_driver = { .name = "wm831x", .pm = &wm831x_pm_ops, .of_match_table = of_match_ptr(wm831x_of_match), + .suppress_bind_attrs = true, }, .probe = wm831x_i2c_probe, - .remove = wm831x_i2c_remove, .id_table = wm831x_i2c_id, }; @@ -132,9 +122,3 @@ static int __init wm831x_i2c_init(void) return ret; } subsys_initcall(wm831x_i2c_init); - -static void __exit wm831x_i2c_exit(void) -{ - i2c_del_driver(&wm831x_i2c_driver); -} -module_exit(wm831x_i2c_exit); From efb5a790dfc33b36bc64dd5a41ffc3ae5a709770 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:46 -0500 Subject: [PATCH 0900/4277] mfd: wm831x-core: Drop unused module infrastructure from non-modular code The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_WM831X drivers/mfd/Kconfig: bool ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Previous demodularizaion work has made wm831x_device_exit() no longer used, so it is also removed from the 831x core code. Signed-off-by: Paul Gortmaker Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm831x-core.c | 15 ++------------- include/linux/mfd/wm831x/core.h | 1 - 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index e70d35ef5c6d..25fbbaf39cb9 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -13,7 +13,8 @@ */ #include -#include +#include +#include #include #include #include @@ -1892,14 +1893,6 @@ err: return ret; } -void wm831x_device_exit(struct wm831x *wm831x) -{ - wm831x_otp_exit(wm831x); - mfd_remove_devices(wm831x->dev); - free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x); - wm831x_irq_exit(wm831x); -} - int wm831x_device_suspend(struct wm831x *wm831x) { int reg, mask; @@ -1944,7 +1937,3 @@ void wm831x_device_shutdown(struct wm831x *wm831x) } } EXPORT_SYMBOL_GPL(wm831x_device_shutdown); - -MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Brown"); diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index b49fa67612f1..6fcb8eb00282 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -418,7 +418,6 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int count, u16 *buf); int wm831x_device_init(struct wm831x *wm831x, int irq); -void wm831x_device_exit(struct wm831x *wm831x); int wm831x_device_suspend(struct wm831x *wm831x); void wm831x_device_shutdown(struct wm831x *wm831x); int wm831x_irq_init(struct wm831x *wm831x, int irq); From fc6430661a88c92c6b8391d09e1b3ae508b88c01 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:47 -0500 Subject: [PATCH 0901/4277] mfd: wm8350-i2c: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_WM8350_I2C drivers/mfd/Kconfig: bool "Wolfson Microelectronics WM8350 with I2C" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. We don't replace module.h with init.h since the file already has that. But we do delete an unused moduleparam.h Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm8350-i2c.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 9358f03b7938..b4194e068e1b 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -13,8 +13,6 @@ * */ -#include -#include #include #include #include @@ -48,30 +46,19 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, return wm8350_device_init(wm8350, i2c->irq, pdata); } -static int wm8350_i2c_remove(struct i2c_client *i2c) -{ - struct wm8350 *wm8350 = i2c_get_clientdata(i2c); - - wm8350_device_exit(wm8350); - - return 0; -} - static const struct i2c_device_id wm8350_i2c_id[] = { { "wm8350", 0 }, { "wm8351", 0 }, { "wm8352", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id); - static struct i2c_driver wm8350_i2c_driver = { .driver = { .name = "wm8350", + .suppress_bind_attrs = true, }, .probe = wm8350_i2c_probe, - .remove = wm8350_i2c_remove, .id_table = wm8350_i2c_id, }; @@ -81,12 +68,3 @@ static int __init wm8350_i2c_init(void) } /* init early so consumer devices can complete system boot */ subsys_initcall(wm8350_i2c_init); - -static void __exit wm8350_i2c_exit(void) -{ - i2c_del_driver(&wm8350_i2c_driver); -} -module_exit(wm8350_i2c_exit); - -MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC"); -MODULE_LICENSE("GPL"); From 0db88688e1bb0180d6348742bdba8927cd0e5670 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:48 -0500 Subject: [PATCH 0902/4277] mfd: wm8350-core: Drop unused module infrastructure from non-modular code The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_WM8350 drivers/mfd/Kconfig: bool ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. We replace module.h with init.h and export.h ; the latter since the file does export some symbols. Previous demodularizaion work has made wm8350_device_exit() no longer used, so it is also removed from the 8350 core code. Signed-off-by: Paul Gortmaker Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm8350-core.c | 30 ++---------------------------- include/linux/mfd/wm8350/core.h | 1 - 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 8a07c5634aee..9e1070f26b11 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -13,7 +13,8 @@ */ #include -#include +#include +#include #include #include #include @@ -442,30 +443,3 @@ err: return ret; } EXPORT_SYMBOL_GPL(wm8350_device_init); - -void wm8350_device_exit(struct wm8350 *wm8350) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++) - platform_device_unregister(wm8350->pmic.led[i].pdev); - - for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) - platform_device_unregister(wm8350->pmic.pdev[i]); - - platform_device_unregister(wm8350->wdt.pdev); - platform_device_unregister(wm8350->rtc.pdev); - platform_device_unregister(wm8350->power.pdev); - platform_device_unregister(wm8350->hwmon.pdev); - platform_device_unregister(wm8350->gpio.pdev); - platform_device_unregister(wm8350->codec.pdev); - - if (wm8350->irq_base) - free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); - - wm8350_irq_exit(wm8350); -} -EXPORT_SYMBOL_GPL(wm8350_device_exit); - -MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 509481d9cf19..202d9bde2c7c 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -643,7 +643,6 @@ struct wm8350_platform_data { */ int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata); -void wm8350_device_exit(struct wm8350 *wm8350); /* * WM8350 device IO From b2b658752c71311f06b6efab5667e98b01b52b6b Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 13 Jan 2019 13:36:49 -0500 Subject: [PATCH 0903/4277] mfd: wm8400-core: Make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_WM8400 drivers/mfd/Kconfig: bool "Wolfson Microelectronics WM8400" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. A trivial function rename from wm8400_module_init to the name wm8400_driver_init is also done to reduce possible confusion. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Linus Walleij Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm8400-core.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 8a98a2fc74e1..79756c83f5f0 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -12,7 +12,7 @@ * */ -#include +#include #include #include #include @@ -150,7 +150,6 @@ static const struct i2c_device_id wm8400_i2c_id[] = { { "wm8400", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id); static struct i2c_driver wm8400_i2c_driver = { .driver = { @@ -161,7 +160,7 @@ static struct i2c_driver wm8400_i2c_driver = { }; #endif -static int __init wm8400_module_init(void) +static int __init wm8400_driver_init(void) { int ret = -ENODEV; @@ -173,15 +172,4 @@ static int __init wm8400_module_init(void) return ret; } -subsys_initcall(wm8400_module_init); - -static void __exit wm8400_module_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8400_i2c_driver); -#endif -} -module_exit(wm8400_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Brown "); +subsys_initcall(wm8400_driver_init); From d57f72875eed3f26afaca176c0f425f209bc99d7 Mon Sep 17 00:00:00 2001 From: Christian Hohnstaedt Date: Mon, 14 Jan 2019 09:16:34 +0100 Subject: [PATCH 0904/4277] mfd: tps65218.c: Add input voltage options These options apply to all regulators in this chip. ti,strict-supply-voltage-supervision: Set STRICT flag in CONFIG1 ti,under-voltage-limit-microvolt: Select 2.75, 2.95, 3.25 or 3.35 V UVLO in CONFIG1 ti,under-voltage-hyst-microvolt: Select 200mV or 400mV UVLOHYS in CONFIG2 Signed-off-by: Christian Hohnstaedt Tested-by: Keerthy Reviewed-by: Keerthy Signed-off-by: Lee Jones --- drivers/mfd/tps65218.c | 89 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65218.h | 4 ++ 2 files changed, 93 insertions(+) diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 8bcdecf494d0..a62ea4cb8be7 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -211,6 +211,83 @@ static const struct of_device_id of_tps65218_match_table[] = { }; MODULE_DEVICE_TABLE(of, of_tps65218_match_table); +static int tps65218_voltage_set_strict(struct tps65218 *tps) +{ + u32 strict; + + if (of_property_read_u32(tps->dev->of_node, + "ti,strict-supply-voltage-supervision", + &strict)) + return 0; + + if (strict != 0 && strict != 1) { + dev_err(tps->dev, + "Invalid ti,strict-supply-voltage-supervision value\n"); + return -EINVAL; + } + + tps65218_update_bits(tps, TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_STRICT, + strict ? TPS65218_CONFIG1_STRICT : 0, + TPS65218_PROTECT_L1); + return 0; +} + +static int tps65218_voltage_set_uv_hyst(struct tps65218 *tps) +{ + u32 hyst; + + if (of_property_read_u32(tps->dev->of_node, + "ti,under-voltage-hyst-microvolt", &hyst)) + return 0; + + if (hyst != 400000 && hyst != 200000) { + dev_err(tps->dev, + "Invalid ti,under-voltage-hyst-microvolt value\n"); + return -EINVAL; + } + + tps65218_update_bits(tps, TPS65218_REG_CONFIG2, + TPS65218_CONFIG2_UVLOHYS, + hyst == 400000 ? TPS65218_CONFIG2_UVLOHYS : 0, + TPS65218_PROTECT_L1); + return 0; +} + +static int tps65218_voltage_set_uvlo(struct tps65218 *tps) +{ + u32 uvlo; + int uvloval; + + if (of_property_read_u32(tps->dev->of_node, + "ti,under-voltage-limit-microvolt", &uvlo)) + return 0; + + switch (uvlo) { + case 2750000: + uvloval = TPS65218_CONFIG1_UVLO_2750000; + break; + case 2950000: + uvloval = TPS65218_CONFIG1_UVLO_2950000; + break; + case 3250000: + uvloval = TPS65218_CONFIG1_UVLO_3250000; + break; + case 3350000: + uvloval = TPS65218_CONFIG1_UVLO_3350000; + break; + default: + dev_err(tps->dev, + "Invalid ti,under-voltage-limit-microvolt value\n"); + return -EINVAL; + } + + tps65218_update_bits(tps, TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_UVLO_MASK, uvloval, + TPS65218_PROTECT_L1); + return 0; +} + static int tps65218_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -249,6 +326,18 @@ static int tps65218_probe(struct i2c_client *client, tps->rev = chipid & TPS65218_CHIPID_REV_MASK; + ret = tps65218_voltage_set_strict(tps); + if (ret) + return ret; + + ret = tps65218_voltage_set_uvlo(tps); + if (ret) + return ret; + + ret = tps65218_voltage_set_uv_hyst(tps); + if (ret) + return ret; + ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65218_cells, ARRAY_SIZE(tps65218_cells), NULL, 0, regmap_irq_get_domain(tps->irq_data)); diff --git a/include/linux/mfd/tps65218.h b/include/linux/mfd/tps65218.h index c204d9a79436..3cbe103495ab 100644 --- a/include/linux/mfd/tps65218.h +++ b/include/linux/mfd/tps65218.h @@ -137,6 +137,10 @@ #define TPS65218_CONFIG1_PGDLY_MASK 0x18 #define TPS65218_CONFIG1_STRICT BIT(2) #define TPS65218_CONFIG1_UVLO_MASK 0x3 +#define TPS65218_CONFIG1_UVLO_2750000 0x0 +#define TPS65218_CONFIG1_UVLO_2950000 0x1 +#define TPS65218_CONFIG1_UVLO_3250000 0x2 +#define TPS65218_CONFIG1_UVLO_3350000 0x3 #define TPS65218_CONFIG2_DC12_RST BIT(7) #define TPS65218_CONFIG2_UVLOHYS BIT(6) From 09fdc98577120d4f47601c3127efde726a2300c6 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 21 Jan 2019 16:11:28 +0200 Subject: [PATCH 0905/4277] mfd: Kconfig: Fix I2C_DESIGNWARE_PLATFORM dependencies INTEL_SOC_PMIC, INTEL_SOC_PMIC_CHTWC and MFD_TPS68470 select the I2C_DESIGNWARE_PLATFORM without its dependencies making it possible to see warning and build error like below: WARNING: unmet direct dependencies detected for I2C_DESIGNWARE_PLATFORM Depends on [n]: I2C [=y] && HAS_IOMEM [=y] && (ACPI [=y] && COMMON_CLK [=n] || !ACPI [=y]) Selected by [y]: - MFD_TPS68470 [=y] && HAS_IOMEM [=y] && ACPI [=y] && I2C [=y]=y /usr/bin/ld: drivers/i2c/busses/i2c-designware-platdrv.o: in function `dw_i2c_plat_resume': i2c-designware-platdrv.c:(.text+0x62): undefined reference to `i2c_dw_prepare_clk' /usr/bin/ld: drivers/i2c/busses/i2c-designware-platdrv.o: in function `dw_i2c_plat_suspend': i2c-designware-platdrv.c:(.text+0x9a): undefined reference to `i2c_dw_prepare_clk' /usr/bin/ld: drivers/i2c/busses/i2c-designware-platdrv.o: in function `dw_i2c_plat_probe': i2c-designware-platdrv.c:(.text+0x41c): undefined reference to `i2c_dw_prepare_clk' /usr/bin/ld: i2c-designware-platdrv.c:(.text+0x438): undefined reference to `i2c_dw_read_comp_param' /usr/bin/ld: i2c-designware-platdrv.c:(.text+0x545): undefined reference to `i2c_dw_probe' /usr/bin/ld: i2c-designware-platdrv.c:(.text+0x727): undefined reference to `i2c_dw_probe_slave' Fix this by making above options to depend on I2C_DESIGNWARE_PLATFORM being built-in. I2C_DESIGNWARE_PLATFORM is a visible symbol with dependencies so in general the select should be avoided. Fixes: acebcff9eda8 ("mfd: intel_soc_pmic: Select designware i2c-bus driver") Fixes: de85d79f4aab ("mfd: Add Cherry Trail Whiskey Cove PMIC driver") Fixes: 9bbf6a15ce19 ("mfd: Add support for TPS68470 device") Cc: Stable # v4.14+ Reported-by: Randy Dunlap Signed-off-by: Jarkko Nikula Acked-by: Randy Dunlap # build-tested Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 211da115d3f6..6e58221f5c28 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -518,10 +518,10 @@ config INTEL_SOC_PMIC bool "Support for Crystal Cove PMIC" depends on ACPI && HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK depends on X86 || COMPILE_TEST + depends on I2C_DESIGNWARE_PLATFORM=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - select I2C_DESIGNWARE_PLATFORM help Select this option to enable support for Crystal Cove PMIC on some Intel SoC systems. The PMIC provides ADC, GPIO, @@ -547,10 +547,10 @@ config INTEL_SOC_PMIC_CHTWC bool "Support for Intel Cherry Trail Whiskey Cove PMIC" depends on ACPI && HAS_IOMEM && I2C=y && COMMON_CLK depends on X86 || COMPILE_TEST + depends on I2C_DESIGNWARE_PLATFORM=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - select I2C_DESIGNWARE_PLATFORM help Select this option to enable support for the Intel Cherry Trail Whiskey Cove PMIC found on some Intel Cherry Trail systems. @@ -1420,9 +1420,9 @@ config MFD_TPS65217 config MFD_TPS68470 bool "TI TPS68470 Power Management / LED chips" depends on ACPI && I2C=y + depends on I2C_DESIGNWARE_PLATFORM=y select MFD_CORE select REGMAP_I2C - select I2C_DESIGNWARE_PLATFORM help If you say yes here you get support for the TPS68470 series of Power Management / LED chips. From cfced786969c2a3e1bca45d7055a00311d93ae6c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jan 2019 18:20:05 +0100 Subject: [PATCH 0906/4277] dma-mapping: remove the default map_resource implementation Instead provide a proper implementation in the direct mapping code, and also wire it up for arm and powerpc, leaving an error return for all the IOMMU or virtual mapping instances for which we'd have to wire up an actual implementation Signed-off-by: Christoph Hellwig Tested-by: Marek Szyprowski --- arch/arm/mm/dma-mapping.c | 2 ++ arch/powerpc/kernel/dma-swiotlb.c | 1 + arch/powerpc/kernel/dma.c | 1 + include/linux/dma-mapping.h | 12 +++++++----- kernel/dma/direct.c | 14 ++++++++++++++ 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f1e2922e447c..3c8534904209 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -188,6 +188,7 @@ const struct dma_map_ops arm_dma_ops = { .unmap_page = arm_dma_unmap_page, .map_sg = arm_dma_map_sg, .unmap_sg = arm_dma_unmap_sg, + .map_resource = dma_direct_map_resource, .sync_single_for_cpu = arm_dma_sync_single_for_cpu, .sync_single_for_device = arm_dma_sync_single_for_device, .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, @@ -211,6 +212,7 @@ const struct dma_map_ops arm_coherent_dma_ops = { .get_sgtable = arm_dma_get_sgtable, .map_page = arm_coherent_dma_map_page, .map_sg = arm_dma_map_sg, + .map_resource = dma_direct_map_resource, .dma_supported = arm_dma_supported, }; EXPORT_SYMBOL(arm_coherent_dma_ops); diff --git a/arch/powerpc/kernel/dma-swiotlb.c b/arch/powerpc/kernel/dma-swiotlb.c index 7d5fc9751622..fbb2506a414e 100644 --- a/arch/powerpc/kernel/dma-swiotlb.c +++ b/arch/powerpc/kernel/dma-swiotlb.c @@ -55,6 +55,7 @@ const struct dma_map_ops powerpc_swiotlb_dma_ops = { .dma_supported = swiotlb_dma_supported, .map_page = dma_direct_map_page, .unmap_page = dma_direct_unmap_page, + .map_resource = dma_direct_map_resource, .sync_single_for_cpu = dma_direct_sync_single_for_cpu, .sync_single_for_device = dma_direct_sync_single_for_device, .sync_sg_for_cpu = dma_direct_sync_sg_for_cpu, diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index b1903ebb2e9c..258b9e8ebb99 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -273,6 +273,7 @@ const struct dma_map_ops dma_nommu_ops = { .dma_supported = dma_nommu_dma_supported, .map_page = dma_nommu_map_page, .unmap_page = dma_nommu_unmap_page, + .map_resource = dma_direct_map_resource, .get_required_mask = dma_nommu_get_required_mask, #ifdef CONFIG_NOT_COHERENT_CACHE .sync_single_for_cpu = dma_nommu_sync_single, diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index f6ded992c183..9842085e6774 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -208,6 +208,8 @@ dma_addr_t dma_direct_map_page(struct device *dev, struct page *page, unsigned long attrs); int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, enum dma_data_direction dir, unsigned long attrs); +dma_addr_t dma_direct_map_resource(struct device *dev, phys_addr_t paddr, + size_t size, enum dma_data_direction dir, unsigned long attrs); #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_SWIOTLB) @@ -346,19 +348,19 @@ static inline dma_addr_t dma_map_resource(struct device *dev, unsigned long attrs) { const struct dma_map_ops *ops = get_dma_ops(dev); - dma_addr_t addr; + dma_addr_t addr = DMA_MAPPING_ERROR; BUG_ON(!valid_dma_direction(dir)); /* Don't allow RAM to be mapped */ BUG_ON(pfn_valid(PHYS_PFN(phys_addr))); - addr = phys_addr; - if (ops && ops->map_resource) + if (dma_is_direct(ops)) + addr = dma_direct_map_resource(dev, phys_addr, size, dir, attrs); + else if (ops->map_resource) addr = ops->map_resource(dev, phys_addr, size, dir, attrs); debug_dma_map_resource(dev, phys_addr, size, dir, addr); - return addr; } @@ -369,7 +371,7 @@ static inline void dma_unmap_resource(struct device *dev, dma_addr_t addr, const struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); - if (ops && ops->unmap_resource) + if (!dma_is_direct(ops) && ops->unmap_resource) ops->unmap_resource(dev, addr, size, dir, attrs); debug_dma_unmap_resource(dev, addr, size, dir); } diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index 355d16acee6d..25bd19974223 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -356,6 +356,20 @@ out_unmap: } EXPORT_SYMBOL(dma_direct_map_sg); +dma_addr_t dma_direct_map_resource(struct device *dev, phys_addr_t paddr, + size_t size, enum dma_data_direction dir, unsigned long attrs) +{ + dma_addr_t dma_addr = paddr; + + if (unlikely(!dma_direct_possible(dev, dma_addr, size))) { + report_addr(dev, dma_addr, size); + return DMA_MAPPING_ERROR; + } + + return dma_addr; +} +EXPORT_SYMBOL(dma_direct_map_resource); + /* * Because 32-bit DMA masks are so common we expect every architecture to be * able to satisfy them - either by not supporting more physical memory, or by From 645386dfe6307dbb28f10a4513792a59beda0efa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jan 2019 17:17:53 +0100 Subject: [PATCH 0907/4277] dma-mapping: don't BUG when calling dma_map_resource on RAM Use WARN_ON_ONCE to print a stack trace and return a proper error code instead. Signed-off-by: Christoph Hellwig Reviewed-by: Robin Murphy Tested-by: Marek Szyprowski --- include/linux/dma-mapping.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 9842085e6774..b904d55247ab 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -353,7 +353,8 @@ static inline dma_addr_t dma_map_resource(struct device *dev, BUG_ON(!valid_dma_direction(dir)); /* Don't allow RAM to be mapped */ - BUG_ON(pfn_valid(PHYS_PFN(phys_addr))); + if (WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr)))) + return DMA_MAPPING_ERROR; if (dma_is_direct(ops)) addr = dma_direct_map_resource(dev, phys_addr, size, dir, attrs); From 55ea54441fb3b6532d5d32417911ff5a10750903 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jan 2019 10:42:49 +0100 Subject: [PATCH 0908/4277] videobuf2: replace a layering violation with dma_map_resource vb2_dc_get_userptr pokes into arm direct mapping details to get the resemblance of a dma address for a a physical address that does is not backed by a page struct. Not only is this not portable to other architectures with dma direct mapping offsets, but also not to uses of IOMMUs of any kind. Switch to the proper dma_map_resource / dma_unmap_resource interface instead. Signed-off-by: Christoph Hellwig Acked-by: Mauro Carvalho Chehab Tested-by: Marek Szyprowski --- .../common/videobuf2/videobuf2-dma-contig.c | 41 ++++--------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index aff0ab7bf83d..82389aead6ed 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -439,42 +439,14 @@ static void vb2_dc_put_userptr(void *buf_priv) set_page_dirty_lock(pages[i]); sg_free_table(sgt); kfree(sgt); + } else { + dma_unmap_resource(buf->dev, buf->dma_addr, buf->size, + buf->dma_dir, 0); } vb2_destroy_framevec(buf->vec); kfree(buf); } -/* - * For some kind of reserved memory there might be no struct page available, - * so all that can be done to support such 'pages' is to try to convert - * pfn to dma address or at the last resort just assume that - * dma address == physical address (like it has been assumed in earlier version - * of videobuf2-dma-contig - */ - -#ifdef __arch_pfn_to_dma -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn) -{ - return (dma_addr_t)__arch_pfn_to_dma(dev, pfn); -} -#elif defined(__pfn_to_bus) -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn) -{ - return (dma_addr_t)__pfn_to_bus(pfn); -} -#elif defined(__pfn_to_phys) -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn) -{ - return (dma_addr_t)__pfn_to_phys(pfn); -} -#else -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn) -{ - /* really, we cannot do anything better at this point */ - return (dma_addr_t)(pfn) << PAGE_SHIFT; -} -#endif - static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { @@ -528,7 +500,12 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, for (i = 1; i < n_pages; i++) if (nums[i-1] + 1 != nums[i]) goto fail_pfnvec; - buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]); + buf->dma_addr = dma_map_resource(buf->dev, + __pfn_to_phys(nums[0]), size, buf->dma_dir, 0); + if (dma_mapping_error(buf->dev, buf->dma_addr)) { + ret = -ENOMEM; + goto fail_pfnvec; + } goto out; } From 8e4d81b98b7859b120dd142c8634f625db118b30 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:21:38 +0100 Subject: [PATCH 0909/4277] dma: debug: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Also delete the variables for the file dentries for the debugfs entries as they are never used at all once they are created. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Robin Murphy [hch: moved dma_debug_dent to function scope and renamed it] Signed-off-by: Christoph Hellwig --- kernel/dma/debug.c | 86 ++++++---------------------------------------- 1 file changed, 11 insertions(+), 75 deletions(-) diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index 23cf5361bcf1..56cd836a902c 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -134,17 +134,6 @@ static u32 nr_total_entries; /* number of preallocated entries requested by kernel cmdline */ static u32 nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES; -/* debugfs dentry's for the stuff above */ -static struct dentry *dma_debug_dent __read_mostly; -static struct dentry *global_disable_dent __read_mostly; -static struct dentry *error_count_dent __read_mostly; -static struct dentry *show_all_errors_dent __read_mostly; -static struct dentry *show_num_errors_dent __read_mostly; -static struct dentry *num_free_entries_dent __read_mostly; -static struct dentry *min_free_entries_dent __read_mostly; -static struct dentry *nr_total_entries_dent __read_mostly; -static struct dentry *filter_dent __read_mostly; - /* per-driver filter related state */ #define NAME_MAX_LEN 64 @@ -840,66 +829,18 @@ static const struct file_operations filter_fops = { .llseek = default_llseek, }; -static int dma_debug_fs_init(void) +static void dma_debug_fs_init(void) { - dma_debug_dent = debugfs_create_dir("dma-api", NULL); - if (!dma_debug_dent) { - pr_err("can not create debugfs directory\n"); - return -ENOMEM; - } + struct dentry *dentry = debugfs_create_dir("dma-api", NULL); - global_disable_dent = debugfs_create_bool("disabled", 0444, - dma_debug_dent, - &global_disable); - if (!global_disable_dent) - goto out_err; - - error_count_dent = debugfs_create_u32("error_count", 0444, - dma_debug_dent, &error_count); - if (!error_count_dent) - goto out_err; - - show_all_errors_dent = debugfs_create_u32("all_errors", 0644, - dma_debug_dent, - &show_all_errors); - if (!show_all_errors_dent) - goto out_err; - - show_num_errors_dent = debugfs_create_u32("num_errors", 0644, - dma_debug_dent, - &show_num_errors); - if (!show_num_errors_dent) - goto out_err; - - num_free_entries_dent = debugfs_create_u32("num_free_entries", 0444, - dma_debug_dent, - &num_free_entries); - if (!num_free_entries_dent) - goto out_err; - - min_free_entries_dent = debugfs_create_u32("min_free_entries", 0444, - dma_debug_dent, - &min_free_entries); - if (!min_free_entries_dent) - goto out_err; - - nr_total_entries_dent = debugfs_create_u32("nr_total_entries", 0444, - dma_debug_dent, - &nr_total_entries); - if (!nr_total_entries_dent) - goto out_err; - - filter_dent = debugfs_create_file("driver_filter", 0644, - dma_debug_dent, NULL, &filter_fops); - if (!filter_dent) - goto out_err; - - return 0; - -out_err: - debugfs_remove_recursive(dma_debug_dent); - - return -ENOMEM; + debugfs_create_bool("disabled", 0444, dentry, &global_disable); + debugfs_create_u32("error_count", 0444, dentry, &error_count); + debugfs_create_u32("all_errors", 0644, dentry, &show_all_errors); + debugfs_create_u32("num_errors", 0644, dentry, &show_num_errors); + debugfs_create_u32("num_free_entries", 0444, dentry, &num_free_entries); + debugfs_create_u32("min_free_entries", 0444, dentry, &min_free_entries); + debugfs_create_u32("nr_total_entries", 0444, dentry, &nr_total_entries); + debugfs_create_file("driver_filter", 0644, dentry, NULL, &filter_fops); } static int device_dma_allocations(struct device *dev, struct dma_debug_entry **out_entry) @@ -985,12 +926,7 @@ static int dma_debug_init(void) spin_lock_init(&dma_entry_hash[i].lock); } - if (dma_debug_fs_init() != 0) { - pr_err("error creating debugfs entries - disabling\n"); - global_disable = true; - - return 0; - } + dma_debug_fs_init(); nr_pages = DIV_ROUND_UP(nr_prealloc_entries, DMA_DEBUG_DYNAMIC_ENTRIES); for (i = 0; i < nr_pages; ++i) From 0a3b192c26da198fce38e1ee242a34f558670246 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 18 Jan 2019 13:44:18 +0000 Subject: [PATCH 0910/4277] dma-debug: add dumping facility via debugfs While debugging a DMA mapping leak, I needed to access debug_dma_dump_mappings() but easily from user space. This patch adds a /sys/kernel/debug/dma-api/dump file which contain all current DMA mapping. Signed-off-by: Corentin Labbe Signed-off-by: Christoph Hellwig --- Documentation/DMA-API.txt | 3 +++ kernel/dma/debug.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index e133ccd60228..78114ee63057 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -696,6 +696,9 @@ dma-api/disabled This read-only file contains the character 'Y' happen when it runs out of memory or if it was disabled at boot time +dma-api/dump This read-only file contains current DMA + mappings. + dma-api/error_count This file is read-only and shows the total numbers of errors found. diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index 56cd836a902c..45d51e8e26f6 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -829,6 +829,33 @@ static const struct file_operations filter_fops = { .llseek = default_llseek, }; +static int dump_show(struct seq_file *seq, void *v) +{ + int idx; + + for (idx = 0; idx < HASH_SIZE; idx++) { + struct hash_bucket *bucket = &dma_entry_hash[idx]; + struct dma_debug_entry *entry; + unsigned long flags; + + spin_lock_irqsave(&bucket->lock, flags); + list_for_each_entry(entry, &bucket->list, list) { + seq_printf(seq, + "%s %s %s idx %d P=%llx N=%lx D=%llx L=%llx %s %s\n", + dev_name(entry->dev), + dev_driver_string(entry->dev), + type2name[entry->type], idx, + phys_addr(entry), entry->pfn, + entry->dev_addr, entry->size, + dir2name[entry->direction], + maperr2str[entry->map_err_type]); + } + spin_unlock_irqrestore(&bucket->lock, flags); + } + return 0; +} +DEFINE_SHOW_ATTRIBUTE(dump); + static void dma_debug_fs_init(void) { struct dentry *dentry = debugfs_create_dir("dma-api", NULL); @@ -841,6 +868,7 @@ static void dma_debug_fs_init(void) debugfs_create_u32("min_free_entries", 0444, dentry, &min_free_entries); debugfs_create_u32("nr_total_entries", 0444, dentry, &nr_total_entries); debugfs_create_file("driver_filter", 0644, dentry, NULL, &filter_fops); + debugfs_create_file("dump", 0444, dentry, NULL, &dump_fops); } static int device_dma_allocations(struct device *dev, struct dma_debug_entry **out_entry) From ac8c1a2e502ef4f784683f276a8a541c86165b21 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:12 +0100 Subject: [PATCH 0911/4277] platform/chrome: cromeos_pstore: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Also fix the module license mismatch and change the term 'Chrome OS' for 'ChromeOS' to be coherent with other drivers. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/chromeos_pstore.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c index b0693fdec8c6..d13770785fb5 100644 --- a/drivers/platform/chrome/chromeos_pstore.c +++ b/drivers/platform/chrome/chromeos_pstore.c @@ -1,12 +1,7 @@ -/* - * chromeos_pstore.c - Driver to instantiate Chromebook ramoops device - * - * Copyright (C) 2013 Google, Inc. - * - * 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, version 2 of the License. - */ +// SPDX-License-Identifier: GPL-2.0 +// Driver to instantiate Chromebook ramoops device. +// +// Copyright (C) 2013 Google, Inc. #include #include @@ -138,5 +133,5 @@ static void __exit chromeos_pstore_exit(void) module_init(chromeos_pstore_init); module_exit(chromeos_pstore_exit); -MODULE_DESCRIPTION("Chrome OS pstore module"); -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS pstore module"); +MODULE_LICENSE("GPL v2"); From cc2db0756fbbbfb8762cb7dc2a2b1c03bc1f0c2b Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:13 +0100 Subject: [PATCH 0912/4277] platform/chrome: cros_ec_debugfs: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_debugfs.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 6cacac53dfce..900c7073c46f 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -1,21 +1,7 @@ -/* - * cros_ec_debugfs - debug logs for Chrome OS EC - * - * Copyright 2015 Google, Inc. - * - * 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 . - */ +// SPDX-License-Identifier: GPL-2.0+ +// Debug logs for the ChromeOS EC +// +// Copyright (C) 2015 Google, Inc. #include #include From 5414dd14aafef0a97b058c1f5be14c03d44c4c16 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:14 +0100 Subject: [PATCH 0913/4277] platform/chrome: cros_ec_lightbar: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_lightbar.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index c3e4e6e5211d..37689ad79544 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -1,21 +1,7 @@ -/* - * cros_ec_lightbar - expose the Chromebook Pixel lightbar to userspace - * - * Copyright (C) 2014 Google, Inc. - * - * 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 . - */ +// SPDX-License-Identifier: GPL-2.0+ +// Expose the Chromebook Pixel lightbar to userspace +// +// Copyright (C) 2014 Google, Inc. #define pr_fmt(fmt) "cros_ec_lightbar: " fmt From 8d4d7948322261cd7b135c162d8d8a7972140dda Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:15 +0100 Subject: [PATCH 0914/4277] platform/chrome: cros_ec_sysfs: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Also change the description for one more appropriate. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_sysfs.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 0ff5aa30c070..01c420727ee1 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -1,21 +1,7 @@ -/* - * cros_ec_sysfs - expose the Chrome OS EC through sysfs - * - * Copyright (C) 2014 Google, Inc. - * - * 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 . - */ +// SPDX-License-Identifier: GPL-2.0+ +// Expose the ChromeOS EC through sysfs +// +// Copyright (C) 2014 Google, Inc. #define pr_fmt(fmt) "cros_ec_sysfs: " fmt @@ -389,5 +375,5 @@ static struct platform_driver cros_ec_sysfs_driver = { module_platform_driver(cros_ec_sysfs_driver); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ChromeOS EC control driver"); +MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs"); MODULE_ALIAS("platform:" DRV_NAME); From 27755cf70914f441fafc33cdb0050ce16b87163f Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:16 +0100 Subject: [PATCH 0915/4277] platform/chrome: cros_ec_vbc: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_vbc.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index af9bfe6f385c..8392a1ec33a7 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -1,22 +1,8 @@ -/* - * cros_ec_vbc - Expose the vboot context nvram to userspace - * - * Copyright (C) 2015 Collabora Ltd. - * - * based on vendor driver, - * - * Copyright (C) 2012 The Chromium OS Authors - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0+ +// Expose the vboot context nvram to userspace +// +// Copyright (C) 2012 Google, Inc. +// Copyright (C) 2015 Collabora Ltd. #include #include From f0c70b9bdb8856928c9c645fe8e842341b8a95da Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:17 +0100 Subject: [PATCH 0916/4277] platform/chrome: cros_ec_i2c: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Also fix the module license mismatch and change the description for a more descriptive phrase. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_i2c.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index 9a009eaa4ada..61d75395f86d 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -1,17 +1,7 @@ -/* - * ChromeOS EC multi-function device (I2C) - * - * Copyright (C) 2012 Google, Inc - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// I2C interface for ChromeOS Embedded Controller +// +// Copyright (C) 2012 Google, Inc #include #include @@ -372,5 +362,5 @@ static struct i2c_driver cros_ec_driver = { module_i2c_driver(cros_ec_driver); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ChromeOS EC multi function device"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("I2C interface for ChromeOS Embedded Controller"); From 1058ca940e400e66405aa6f3da21ab67136f59a8 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:18 +0100 Subject: [PATCH 0917/4277] platform/chrome: cros_ec_lpc: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Also remove the license boiler-plate and redundant driver description. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_lpc.c | 34 ++++++++--------------- drivers/platform/chrome/cros_ec_lpc_mec.c | 26 +++-------------- drivers/platform/chrome/cros_ec_lpc_mec.h | 20 ++----------- drivers/platform/chrome/cros_ec_lpc_reg.c | 26 +++-------------- drivers/platform/chrome/cros_ec_lpc_reg.h | 20 ++----------- 5 files changed, 24 insertions(+), 102 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 14684a56e40f..c9c240fbe7c6 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -1,25 +1,15 @@ -/* - * cros_ec_lpc - LPC access to the Chrome OS Embedded Controller - * - * Copyright (C) 2012-2015 Google, Inc - * - * 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. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// LPC interface for ChromeOS Embedded Controller +// +// Copyright (C) 2012-2015 Google, Inc +// +// This driver uses the ChromeOS EC byte-level message-based protocol for +// communicating the keyboard state (which keys are pressed) from a keyboard EC +// to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, +// but everything else (including deghosting) is done here. The main +// motivation for this is to keep the EC firmware as simple as possible, since +// it cannot be easily upgraded and EC flash/IRAM space is relatively +// expensive. #include #include diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c index c4edfa83e493..296554a82b83 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.c +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c @@ -1,25 +1,7 @@ -/* - * cros_ec_lpc_mec - LPC variant I/O for Microchip EC - * - * Copyright (C) 2016 Google, Inc - * - * 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. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// LPC variant I/O for Microchip EC +// +// Copyright (C) 2016 Google, Inc #include #include diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h index 105068c0e919..68b381f2eb50 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.h +++ b/drivers/platform/chrome/cros_ec_lpc_mec.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * cros_ec_lpc_mec - LPC variant I/O for Microchip EC + * LPC variant I/O for Microchip EC * * Copyright (C) 2016 Google, Inc - * - * 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. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. */ #ifndef __CROS_EC_LPC_MEC_H diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c index fc23d535c404..72fc3f6bff90 100644 --- a/drivers/platform/chrome/cros_ec_lpc_reg.c +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c @@ -1,25 +1,7 @@ -/* - * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller - * - * Copyright (C) 2016 Google, Inc - * - * 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. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. - */ +// SPDX-License-Identifier: GPL-2.0 +// LPC interface for ChromeOS Embedded Controller +// +// Copyright (C) 2016 Google, Inc #include #include diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.h b/drivers/platform/chrome/cros_ec_lpc_reg.h index 1c12c38b306a..416fd2572182 100644 --- a/drivers/platform/chrome/cros_ec_lpc_reg.h +++ b/drivers/platform/chrome/cros_ec_lpc_reg.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller + * LPC interface for ChromeOS Embedded Controller * * Copyright (C) 2016 Google, Inc - * - * 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. - * - * This driver uses the Chrome OS EC byte-level message-based protocol for - * communicating the keyboard state (which keys are pressed) from a keyboard EC - * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, - * but everything else (including deghosting) is done here. The main - * motivation for this is to keep the EC firmware as simple as possible, since - * it cannot be easily upgraded and EC flash/IRAM space is relatively - * expensive. */ #ifndef __CROS_EC_LPC_REG_H From b861297cf3bba9a19cb262646b202d15d5b27025 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:19 +0100 Subject: [PATCH 0918/4277] platform/chrome: cros_ec_proto: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_proto.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index cc7baf0ecb3c..97a068dff192 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -1,18 +1,7 @@ -/* - * ChromeOS EC communication protocol helper functions - * - * Copyright (C) 2015 Google, Inc - * - * 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. - * - */ +// SPDX-License-Identifier: GPL-2.0 +// ChromeOS EC communication protocol helper functions +// +// Copyright (C) 2015 Google, Inc #include #include From 30fc9147aea7f83b8295cf9d36fbb2519ee45a4a Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:20 +0100 Subject: [PATCH 0919/4277] platform/chrome: cros_ec_spi: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Also change the description for one more appropriate. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_spi.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 6cfbc2835beb..ffc38f9d4829 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -1,17 +1,7 @@ -/* - * ChromeOS EC multi-function device (SPI) - * - * Copyright (C) 2012 Google, Inc - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// SPI interface for ChromeOS Embedded Controller +// +// Copyright (C) 2012 Google, Inc #include #include @@ -729,4 +719,4 @@ static struct spi_driver cros_ec_driver_spi = { module_spi_driver(cros_ec_driver_spi); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)"); +MODULE_DESCRIPTION("SPI interface for ChromeOS Embedded Controller"); From 0e27a11f3201a9835d5bb584c9842d8d47326073 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:21 +0100 Subject: [PATCH 0920/4277] platform/chrome: cros_kbd_led_backlight: switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Also change the term 'Chrome OS' for 'ChromeOS' to be coherent with other drivers. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- .../platform/chrome/cros_kbd_led_backlight.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c index ca3e4da852b4..aa409f0201fb 100644 --- a/drivers/platform/chrome/cros_kbd_led_backlight.c +++ b/drivers/platform/chrome/cros_kbd_led_backlight.c @@ -1,18 +1,7 @@ -/* - * Keyboard backlight LED driver for Chrome OS. - * - * Copyright (C) 2012 Google, Inc. - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0+ +// Keyboard backlight LED driver for ChromeOS +// +// Copyright (C) 2012 Google, Inc. #include #include From d6c7901ba88f760f529c07d7b795710dcf3889b7 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:22 +0100 Subject: [PATCH 0921/4277] platform/chrome: cros_ec_lightbar: remove pr_fmt() define This driver no longer has any pr_{level} messages. Remove the pr_fmt(). Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_lightbar.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 37689ad79544..d30a6650b0b5 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -3,8 +3,6 @@ // // Copyright (C) 2014 Google, Inc. -#define pr_fmt(fmt) "cros_ec_lightbar: " fmt - #include #include #include From 73c935de7fde46c0052cf4b7564e956ce79c02e7 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 29 Jan 2019 10:38:23 +0100 Subject: [PATCH 0922/4277] platform/chrome: cros_ec_sysfs: remove pr_fmt() define This driver no longer has any pr_{level} messages. Remove the pr_fmt(). Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck --- drivers/platform/chrome/cros_ec_sysfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 01c420727ee1..fe0b7614ae1b 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -3,8 +3,6 @@ // // Copyright (C) 2014 Google, Inc. -#define pr_fmt(fmt) "cros_ec_sysfs: " fmt - #include #include #include From 12de16ae0d6ca8d41b65dd922f813f74f8497398 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:00 +0100 Subject: [PATCH 0923/4277] PCI: dwc: Remove unnecessary header include (of_gpio.h) Remove unnecessary header include (of_gpio.h) since it doesn't provide any needed symbols. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index c12bf794d69c..c1aa700362a7 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include From 3630c4aaae217efd4ce1b090f57da128686d97ce Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:01 +0100 Subject: [PATCH 0924/4277] PCI: dwc: Remove unnecessary header include (signal.h) Remove unnecessary header include (signal.h) since it doesn't provide any needed symbols. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index c1aa700362a7..cee7b4a04fed 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include From 40e9892ef94ce8b02e6ca7c6ee5405e6c28e946c Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:02 +0100 Subject: [PATCH 0925/4277] PCI: dwc: Rename variable name from data to d on dw_pci_bottom_mask/unmask() Rename variable from data to d to maintain consistency between driver functions, such as dw_msi_mask_irq() and dw_msi_unmask_irq(). No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- .../pci/controller/dwc/pcie-designware-host.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 721d60a5d9e4..36e284c75934 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -149,20 +149,20 @@ static int dw_pci_msi_set_affinity(struct irq_data *irq_data, return -EINVAL; } -static void dw_pci_bottom_mask(struct irq_data *data) +static void dw_pci_bottom_mask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); unsigned int res, bit, ctrl; unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); if (pp->ops->msi_clear_irq) { - pp->ops->msi_clear_irq(pp, data->hwirq); + pp->ops->msi_clear_irq(pp, d->hwirq); } else { - ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; + ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; - bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; + bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_status[ctrl] &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, @@ -172,20 +172,20 @@ static void dw_pci_bottom_mask(struct irq_data *data) raw_spin_unlock_irqrestore(&pp->lock, flags); } -static void dw_pci_bottom_unmask(struct irq_data *data) +static void dw_pci_bottom_unmask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); unsigned int res, bit, ctrl; unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); if (pp->ops->msi_set_irq) { - pp->ops->msi_set_irq(pp, data->hwirq); + pp->ops->msi_set_irq(pp, d->hwirq); } else { - ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; + ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; - bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; + bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_status[ctrl] |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, From 59ea68b3f17294f05c95ffcb6b44442f767fe93c Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:03 +0100 Subject: [PATCH 0926/4277] PCI: dwc: Rename variable name from data to d on dw_pci_setup_msi_msg() Rename variable from data to d to maintain consistency between driver functions, such as dw_pci_setup_msi_msg(). No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 36e284c75934..0c6e19ab707b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -120,9 +120,9 @@ static void dw_chained_msi_isr(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) +static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); u64 msi_target; @@ -135,12 +135,12 @@ static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) msg->address_hi = upper_32_bits(msi_target); if (pp->ops->get_msi_data) - msg->data = pp->ops->get_msi_data(pp, data->hwirq); + msg->data = pp->ops->get_msi_data(pp, d->hwirq); else - msg->data = data->hwirq; + msg->data = d->hwirq; dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); + (int)d->hwirq, msg->address_hi, msg->address_lo); } static int dw_pci_msi_set_affinity(struct irq_data *irq_data, From fd5288a362ab55371089acf92ede0d2b876fccc0 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:04 +0100 Subject: [PATCH 0927/4277] PCI: dwc: Rename variable name from data to d on dw_pci_msi_set_affinity() Rename variable from data to d to maintain consistency between driver functions. No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 0c6e19ab707b..a59fc17eb120 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -143,7 +143,7 @@ static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) (int)d->hwirq, msg->address_hi, msg->address_lo); } -static int dw_pci_msi_set_affinity(struct irq_data *irq_data, +static int dw_pci_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force) { return -EINVAL; From 4cfae0f1f8ce16c67abd2b40f7d1f645cbc0ae4a Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:05 +0100 Subject: [PATCH 0928/4277] PCI: dwc: Rename variable name from data to d on dw_pcie_irq_domain_free() Rename variable from data to d to maintain consistency between driver functions. No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index a59fc17eb120..768e16aad68e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -255,13 +255,13 @@ static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, static void dw_pcie_irq_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { - struct irq_data *data = irq_domain_get_irq_data(domain, virq); - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct pcie_port *pp = irq_data_get_irq_chip_data(d); unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); - bitmap_release_region(pp->msi_irq_in_use, data->hwirq, + bitmap_release_region(pp->msi_irq_in_use, d->hwirq, order_base_2(nr_irqs)); raw_spin_unlock_irqrestore(&pp->lock, flags); From a348d015f0de7a08f88f8089f6f6c4bc5248029b Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:06 +0100 Subject: [PATCH 0929/4277] PCI: dwc: Improve code readability and simplify mask/unmask operations Improve code readability and simplifies mask/unmask operations by inverting the applied logic (no functional change is intended). Replace variable name from irq_status to irq_mask, since its goal is to keep track of which interrupts are masked or not. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 12 ++++++------ drivers/pci/controller/dwc/pcie-designware.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 768e16aad68e..c550c4a7ef89 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -164,9 +164,9 @@ static void dw_pci_bottom_mask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_status[ctrl] &= ~(1 << bit); + pp->irq_mask[ctrl] |= (1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, - ~pp->irq_status[ctrl]); + pp->irq_mask[ctrl]); } raw_spin_unlock_irqrestore(&pp->lock, flags); @@ -187,9 +187,9 @@ static void dw_pci_bottom_unmask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_status[ctrl] |= 1 << bit; + pp->irq_mask[ctrl] &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, - ~pp->irq_status[ctrl]); + pp->irq_mask[ctrl]); } raw_spin_unlock_irqrestore(&pp->lock, flags); @@ -665,13 +665,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* Initialize IRQ Status array */ for (ctrl = 0; ctrl < num_ctrls; ctrl++) { + pp->irq_mask[ctrl] = ~0; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), - 4, ~0); + 4, pp->irq_mask[ctrl]); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), 4, ~0); - pp->irq_status[ctrl] = 0; } /* Setup RC BARs */ diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 9943d8c68335..279000255ad1 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -177,7 +177,7 @@ struct pcie_port { struct irq_domain *msi_domain; dma_addr_t msi_data; u32 num_vectors; - u32 irq_status[MAX_MSI_CTRLS]; + u32 irq_mask[MAX_MSI_CTRLS]; raw_spinlock_t lock; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; From 657722570a555c728c59f17f7825c812c124dc28 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Thu, 31 Jan 2019 19:17:07 +0100 Subject: [PATCH 0930/4277] PCI: dwc: Replace bit rotation operation (1 << bit) with BIT(bit) Replace bit rotation operation (1 << bit) with BIT(bit), which simplifies code reading. No functional change is intended. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi Cc: Joao Pinto Cc: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware-host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index c550c4a7ef89..45ff5e4f8af6 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -164,7 +164,7 @@ static void dw_pci_bottom_mask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_mask[ctrl] |= (1 << bit); + pp->irq_mask[ctrl] |= BIT(bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, pp->irq_mask[ctrl]); } @@ -187,7 +187,7 @@ static void dw_pci_bottom_unmask(struct irq_data *d) res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - pp->irq_mask[ctrl] &= ~(1 << bit); + pp->irq_mask[ctrl] &= ~BIT(bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, pp->irq_mask[ctrl]); } @@ -207,7 +207,7 @@ static void dw_pci_bottom_ack(struct irq_data *d) raw_spin_lock_irqsave(&pp->lock, flags); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, 1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, BIT(bit)); if (pp->ops->msi_irq_ack) pp->ops->msi_irq_ack(d->hwirq, pp); From 7403e48d7abd9a0114e9056e7615cfbbb5123b15 Mon Sep 17 00:00:00 2001 From: Kamil Konieczny Date: Tue, 22 Jan 2019 10:02:31 +0100 Subject: [PATCH 0931/4277] clk: samsung: dt-bindings: Add Exynos5433 IMEM CMU clock IDs Add DT bindings to describe the IMEM CMU clocks for the SlimSSS (Slim Security SubSystem). Signed-off-by: Kamil Konieczny Reviewed-by: Rob Herring Acked-by: Chanwoo Choi [s.nawrocki@samsung.com: edited commit description] Signed-off-by: Sylwester Nawrocki --- include/dt-bindings/clock/exynos5433.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 98bd85ce1e45..7cfce3a62050 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -1406,4 +1406,10 @@ #define CAM1_NR_CLK 113 +/* CMU_IMEM */ +#define CLK_ACLK_SLIMSSS 2 +#define CLK_PCLK_SLIMSSS 35 + +#define IMEM_NR_CLK 36 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS5433_H */ From 4e16c9ef2a0be931ed63448187d9f5ab0e872f3c Mon Sep 17 00:00:00 2001 From: Kamil Konieczny Date: Tue, 22 Jan 2019 10:02:28 +0100 Subject: [PATCH 0932/4277] clk: samsung: exynos5433: Fix definition of CLK_ACLK_IMEM_{200, 266} clocks This fixes typos in CLK_ACLK_IMEM_200 and CLK_ACLK_IMEM_266 clock definition, leading to incorrect parent clock assignment. Reviewed-by: Chanwoo Choi Signed-off-by: Kamil Konieczny [s.nawrocki@samsung.com: edited commit description] Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 751e2c4fb65b..ea47f49abc7f 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -568,10 +568,10 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { GATE(CLK_ACLK_BUS1_400, "aclk_bus1_400", "div_aclk_bus1_400", ENABLE_ACLK_TOP, 25, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), - GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266", + GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_200", ENABLE_ACLK_TOP, 24, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), - GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_200", + GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_266", ENABLE_ACLK_TOP, 23, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_PERIC_66, "aclk_peric_66", "div_aclk_peric_66_b", From b80a40c659ba38e1295ab7e55b2c781abee64739 Mon Sep 17 00:00:00 2001 From: Kamil Konieczny Date: Tue, 22 Jan 2019 10:02:29 +0100 Subject: [PATCH 0933/4277] clk: samsung: exynos5433: Fix name typo in sssx Fix typo in sssx name, there should be three letters 's'. Acked-by: Rob Herring Reviewed-by: Chanwoo Choi Signed-off-by: Kamil Konieczny Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 2 +- include/dt-bindings/clock/exynos5433.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index ea47f49abc7f..24c3360db65b 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -559,7 +559,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { /* ENABLE_ACLK_TOP */ GATE(CLK_ACLK_G3D_400, "aclk_g3d_400", "div_aclk_g3d_400", ENABLE_ACLK_TOP, 30, CLK_IS_CRITICAL, 0), - GATE(CLK_ACLK_IMEM_SSX_266, "aclk_imem_ssx_266", + GATE(CLK_ACLK_IMEM_SSSX_266, "aclk_imem_sssx_266", "div_aclk_imem_sssx_266", ENABLE_ACLK_TOP, 29, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_BUS0_400, "aclk_bus0_400", "div_aclk_bus0_400", diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h index 7cfce3a62050..25ffa53573a5 100644 --- a/include/dt-bindings/clock/exynos5433.h +++ b/include/dt-bindings/clock/exynos5433.h @@ -156,7 +156,7 @@ #define CLK_ACLK_G2D_266 220 #define CLK_ACLK_G2D_400 221 #define CLK_ACLK_G3D_400 222 -#define CLK_ACLK_IMEM_SSX_266 223 +#define CLK_ACLK_IMEM_SSSX_266 223 #define CLK_ACLK_BUS0_400 224 #define CLK_ACLK_BUS1_400 225 #define CLK_ACLK_IMEM_200 226 From 5af01ab3b6d7e0279bb6b90b54ecd921afc83bb2 Mon Sep 17 00:00:00 2001 From: Kamil Konieczny Date: Tue, 22 Jan 2019 10:02:30 +0100 Subject: [PATCH 0934/4277] clk: samsung: dt-bindings: Document Exynos5433 IMEM CMU Document DT bindings of the IMEM CMU providing clocks for the Exynos5433 Security SubSystem (SSS) and SlimSSS IPs. Acked-by: Chanwoo Choi Reviewed-by: Rob Herring Acked-by: Stephen Boyd Signed-off-by: Kamil Konieczny [s.nawrocki@samsung.com: edited commit description] Signed-off-by: Sylwester Nawrocki --- .../bindings/clock/exynos5433-clock.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt index 50d5897c9849..183c327a7d6b 100644 --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt @@ -50,6 +50,8 @@ Required Properties: IPs. - "samsung,exynos5433-cmu-cam1" - clock controller compatible for CMU_CAM1 which generates clocks for Cortex-A5/MIPI_CSIS2/FIMC-LITE_C/FIMC-FD IPs. + - "samsung,exynos5433-cmu-imem" - clock controller compatible for CMU_IMEM + which generates clocks for SSS (Security SubSystem) and SlimSSS IPs. - reg: physical base address of the controller and length of memory mapped region. @@ -168,6 +170,12 @@ Required Properties: - aclk_cam1_400 - aclk_cam1_552 + Input clocks for imem clock controller: + - oscclk + - aclk_imem_sssx_266 + - aclk_imem_266 + - aclk_imem_200 + Optional properties: - power-domains: a phandle to respective power domain node as described by generic PM domain bindings (see power/power_domain.txt for more @@ -469,6 +477,21 @@ Example 2: Examples of clock controller nodes are listed below. power-domains = <&pd_cam1>; }; + cmu_imem: clock-controller@11060000 { + compatible = "samsung,exynos5433-cmu-imem"; + reg = <0x11060000 0x1000>; + #clock-cells = <1>; + + clock-names = "oscclk", + "aclk_imem_sssx_266", + "aclk_imem_266", + "aclk_imem_200"; + clocks = <&xxti>, + <&cmu_top CLK_DIV_ACLK_IMEM_SSSX_266>, + <&cmu_top CLK_DIV_ACLK_IMEM_266>, + <&cmu_top CLK_DIV_ACLK_IMEM_200>; + }; + Example 3: UART controller node that consumes the clock generated by the clock controller. From 81faa30df9b6e9088ea5ca488860616192e7b763 Mon Sep 17 00:00:00 2001 From: Kamil Konieczny Date: Tue, 22 Jan 2019 10:02:32 +0100 Subject: [PATCH 0935/4277] clk: samsung: exynos5433: Add selected IMEM clocks This patch adds clocks required for the Exynos5433 SoC Slim Security SubSystem (SlimSSS) IP block. Acked-by: Chanwoo Choi Signed-off-by: Kamil Konieczny [s.nawrocki@samsung.com: edited commit description] Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 24c3360db65b..dae1c96de933 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -5467,6 +5467,35 @@ static const struct samsung_cmu_info cam1_cmu_info __initconst = { .clk_name = "aclk_cam1_400", }; +/* + * Register offset definitions for CMU_IMEM + */ +#define ENABLE_ACLK_IMEM_SLIMSSS 0x080c +#define ENABLE_PCLK_IMEM_SLIMSSS 0x0908 + +static const unsigned long imem_clk_regs[] __initconst = { + ENABLE_ACLK_IMEM_SLIMSSS, + ENABLE_PCLK_IMEM_SLIMSSS, +}; + +static const struct samsung_gate_clock imem_gate_clks[] __initconst = { + /* ENABLE_ACLK_IMEM_SLIMSSS */ + GATE(CLK_ACLK_SLIMSSS, "aclk_slimsss", "aclk_imem_sssx_266", + ENABLE_ACLK_IMEM_SLIMSSS, 0, CLK_IGNORE_UNUSED, 0), + + /* ENABLE_PCLK_IMEM_SLIMSSS */ + GATE(CLK_PCLK_SLIMSSS, "pclk_slimsss", "aclk_imem_200", + ENABLE_PCLK_IMEM_SLIMSSS, 0, CLK_IGNORE_UNUSED, 0), +}; + +static const struct samsung_cmu_info imem_cmu_info __initconst = { + .gate_clks = imem_gate_clks, + .nr_gate_clks = ARRAY_SIZE(imem_gate_clks), + .nr_clk_ids = IMEM_NR_CLK, + .clk_regs = imem_clk_regs, + .nr_clk_regs = ARRAY_SIZE(imem_clk_regs), + .clk_name = "aclk_imem_200", +}; struct exynos5433_cmu_data { struct samsung_clk_reg_dump *clk_save; @@ -5654,6 +5683,9 @@ static const struct of_device_id exynos5433_cmu_of_match[] = { }, { .compatible = "samsung,exynos5433-cmu-mscl", .data = &mscl_cmu_info, + }, { + .compatible = "samsung,exynos5433-cmu-imem", + .data = &imem_cmu_info, }, { }, }; From d0a060be573bfbf8753a15dca35497db5e968bb0 Mon Sep 17 00:00:00 2001 From: Kristina Martsenko Date: Wed, 30 Jan 2019 12:02:44 +0000 Subject: [PATCH 0936/4277] arm64: add ptrace regsets for ptrauth key management Add two new ptrace regsets, which can be used to request and change the pointer authentication keys of a thread. NT_ARM_PACA_KEYS gives access to the instruction/data address keys, and NT_ARM_PACG_KEYS to the generic authentication key. The keys are also part of the core dump file of the process. The regsets are only exposed if the kernel is compiled with CONFIG_CHECKPOINT_RESTORE=y, as the only intended use case is checkpointing and restoring processes that are using pointer authentication. (This can be changed later if there are other use cases.) Reviewed-by: Dave Martin Signed-off-by: Kristina Martsenko Signed-off-by: Catalin Marinas --- .../arm64/pointer-authentication.txt | 5 + arch/arm64/include/uapi/asm/ptrace.h | 13 ++ arch/arm64/kernel/ptrace.c | 147 ++++++++++++++++++ include/uapi/linux/elf.h | 2 + 4 files changed, 167 insertions(+) diff --git a/Documentation/arm64/pointer-authentication.txt b/Documentation/arm64/pointer-authentication.txt index a25cd21290e9..5baca42ba146 100644 --- a/Documentation/arm64/pointer-authentication.txt +++ b/Documentation/arm64/pointer-authentication.txt @@ -78,6 +78,11 @@ bits can vary between the two. Note that the masks apply to TTBR0 addresses, and are not valid to apply to TTBR1 addresses (e.g. kernel pointers). +Additionally, when CONFIG_CHECKPOINT_RESTORE is also set, the kernel +will expose the NT_ARM_PACA_KEYS and NT_ARM_PACG_KEYS regsets (struct +user_pac_address_keys and struct user_pac_generic_keys). These can be +used to get and set the keys for a thread. + Virtualization -------------- diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h index 28d77c9ed531..d78623acb649 100644 --- a/arch/arm64/include/uapi/asm/ptrace.h +++ b/arch/arm64/include/uapi/asm/ptrace.h @@ -233,6 +233,19 @@ struct user_pac_mask { __u64 insn_mask; }; +/* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */ + +struct user_pac_address_keys { + __uint128_t apiakey; + __uint128_t apibkey; + __uint128_t apdakey; + __uint128_t apdbkey; +}; + +struct user_pac_generic_keys { + __uint128_t apgakey; +}; + #endif /* __ASSEMBLY__ */ #endif /* _UAPI__ASM_PTRACE_H */ diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 9dce33b0e260..a86413be5a2d 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -979,6 +979,131 @@ static int pac_mask_get(struct task_struct *target, return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &uregs, 0, -1); } + +#ifdef CONFIG_CHECKPOINT_RESTORE +static __uint128_t pac_key_to_user(const struct ptrauth_key *key) +{ + return (__uint128_t)key->hi << 64 | key->lo; +} + +static struct ptrauth_key pac_key_from_user(__uint128_t ukey) +{ + struct ptrauth_key key = { + .lo = (unsigned long)ukey, + .hi = (unsigned long)(ukey >> 64), + }; + + return key; +} + +static void pac_address_keys_to_user(struct user_pac_address_keys *ukeys, + const struct ptrauth_keys *keys) +{ + ukeys->apiakey = pac_key_to_user(&keys->apia); + ukeys->apibkey = pac_key_to_user(&keys->apib); + ukeys->apdakey = pac_key_to_user(&keys->apda); + ukeys->apdbkey = pac_key_to_user(&keys->apdb); +} + +static void pac_address_keys_from_user(struct ptrauth_keys *keys, + const struct user_pac_address_keys *ukeys) +{ + keys->apia = pac_key_from_user(ukeys->apiakey); + keys->apib = pac_key_from_user(ukeys->apibkey); + keys->apda = pac_key_from_user(ukeys->apdakey); + keys->apdb = pac_key_from_user(ukeys->apdbkey); +} + +static int pac_address_keys_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + struct ptrauth_keys *keys = &target->thread.keys_user; + struct user_pac_address_keys user_keys; + + if (!system_supports_address_auth()) + return -EINVAL; + + pac_address_keys_to_user(&user_keys, keys); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &user_keys, 0, -1); +} + +static int pac_address_keys_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct ptrauth_keys *keys = &target->thread.keys_user; + struct user_pac_address_keys user_keys; + int ret; + + if (!system_supports_address_auth()) + return -EINVAL; + + pac_address_keys_to_user(&user_keys, keys); + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &user_keys, 0, -1); + if (ret) + return ret; + pac_address_keys_from_user(keys, &user_keys); + + return 0; +} + +static void pac_generic_keys_to_user(struct user_pac_generic_keys *ukeys, + const struct ptrauth_keys *keys) +{ + ukeys->apgakey = pac_key_to_user(&keys->apga); +} + +static void pac_generic_keys_from_user(struct ptrauth_keys *keys, + const struct user_pac_generic_keys *ukeys) +{ + keys->apga = pac_key_from_user(ukeys->apgakey); +} + +static int pac_generic_keys_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + struct ptrauth_keys *keys = &target->thread.keys_user; + struct user_pac_generic_keys user_keys; + + if (!system_supports_generic_auth()) + return -EINVAL; + + pac_generic_keys_to_user(&user_keys, keys); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &user_keys, 0, -1); +} + +static int pac_generic_keys_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct ptrauth_keys *keys = &target->thread.keys_user; + struct user_pac_generic_keys user_keys; + int ret; + + if (!system_supports_generic_auth()) + return -EINVAL; + + pac_generic_keys_to_user(&user_keys, keys); + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &user_keys, 0, -1); + if (ret) + return ret; + pac_generic_keys_from_user(keys, &user_keys); + + return 0; +} +#endif /* CONFIG_CHECKPOINT_RESTORE */ #endif /* CONFIG_ARM64_PTR_AUTH */ enum aarch64_regset { @@ -995,6 +1120,10 @@ enum aarch64_regset { #endif #ifdef CONFIG_ARM64_PTR_AUTH REGSET_PAC_MASK, +#ifdef CONFIG_CHECKPOINT_RESTORE + REGSET_PACA_KEYS, + REGSET_PACG_KEYS, +#endif #endif }; @@ -1074,6 +1203,24 @@ static const struct user_regset aarch64_regsets[] = { .get = pac_mask_get, /* this cannot be set dynamically */ }, +#ifdef CONFIG_CHECKPOINT_RESTORE + [REGSET_PACA_KEYS] = { + .core_note_type = NT_ARM_PACA_KEYS, + .n = sizeof(struct user_pac_address_keys) / sizeof(__uint128_t), + .size = sizeof(__uint128_t), + .align = sizeof(__uint128_t), + .get = pac_address_keys_get, + .set = pac_address_keys_set, + }, + [REGSET_PACG_KEYS] = { + .core_note_type = NT_ARM_PACG_KEYS, + .n = sizeof(struct user_pac_generic_keys) / sizeof(__uint128_t), + .size = sizeof(__uint128_t), + .align = sizeof(__uint128_t), + .get = pac_generic_keys_get, + .set = pac_generic_keys_set, + }, +#endif #endif }; diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index e4d6ddd93567..34c02e4290fe 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -421,6 +421,8 @@ typedef struct elf64_shdr { #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ #define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension registers */ #define NT_ARM_PAC_MASK 0x406 /* ARM pointer authentication code masks */ +#define NT_ARM_PACA_KEYS 0x407 /* ARM pointer authentication address keys */ +#define NT_ARM_PACG_KEYS 0x408 /* ARM pointer authentication generic key */ #define NT_ARC_V2 0x600 /* ARCv2 accumulator/extra registers */ #define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */ #define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */ From 8ccdf4a3775229314c8bd365ac88c2cbdf36be13 Mon Sep 17 00:00:00 2001 From: Jianchao Wang Date: Thu, 24 Jan 2019 18:25:32 +0800 Subject: [PATCH 0937/4277] blk-mq: save queue mapping result into ctx directly Currently, the queue mapping result is saved in a two-dimensional array. In the hot path, to get a hctx, we need do following: q->queue_hw_ctx[q->tag_set->map[type].mq_map[cpu]] This isn't very efficient. We could save the queue mapping result into ctx directly with different hctx type, like, ctx->hctxs[type] Signed-off-by: Jianchao Wang Signed-off-by: Jens Axboe --- block/blk-mq-sched.c | 2 +- block/blk-mq-tag.c | 2 +- block/blk-mq.c | 4 ++-- block/blk-mq.h | 7 ++++--- block/blk.h | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 140933e4a7d1..40905539afed 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -321,7 +321,7 @@ bool __blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio) { struct elevator_queue *e = q->elevator; struct blk_mq_ctx *ctx = blk_mq_get_ctx(q); - struct blk_mq_hw_ctx *hctx = blk_mq_map_queue(q, bio->bi_opf, ctx->cpu); + struct blk_mq_hw_ctx *hctx = blk_mq_map_queue(q, bio->bi_opf, ctx); bool ret = false; enum hctx_type type; diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 2089c6c62f44..a4931fc7be8a 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -170,7 +170,7 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data) data->ctx = blk_mq_get_ctx(data->q); data->hctx = blk_mq_map_queue(data->q, data->cmd_flags, - data->ctx->cpu); + data->ctx); tags = blk_mq_tags_from_data(data); if (data->flags & BLK_MQ_REQ_RESERVED) bt = &tags->breserved_tags; diff --git a/block/blk-mq.c b/block/blk-mq.c index 8f5b533764ca..445d0a2642ae 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -364,7 +364,7 @@ static struct request *blk_mq_get_request(struct request_queue *q, } if (likely(!data->hctx)) data->hctx = blk_mq_map_queue(q, data->cmd_flags, - data->ctx->cpu); + data->ctx); if (data->cmd_flags & REQ_NOWAIT) data->flags |= BLK_MQ_REQ_NOWAIT; @@ -2435,7 +2435,7 @@ static void blk_mq_map_swqueue(struct request_queue *q) continue; hctx = blk_mq_map_queue_type(q, j, i); - + ctx->hctxs[j] = hctx; /* * If the CPU is already set in the mask, then we've * mapped this one already. This can happen if diff --git a/block/blk-mq.h b/block/blk-mq.h index d943d46b0785..9fb06261518e 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -23,6 +23,7 @@ struct blk_mq_ctx { unsigned int cpu; unsigned short index_hw[HCTX_MAX_TYPES]; + struct blk_mq_hw_ctx *hctxs[HCTX_MAX_TYPES]; /* incremented at dispatch time */ unsigned long rq_dispatched[2]; @@ -97,11 +98,11 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue * * blk_mq_map_queue() - map (cmd_flags,type) to hardware queue * @q: request queue * @flags: request command flags - * @cpu: CPU + * @cpu: cpu ctx */ static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, unsigned int flags, - unsigned int cpu) + struct blk_mq_ctx *ctx) { enum hctx_type type = HCTX_TYPE_DEFAULT; @@ -116,7 +117,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, q->tag_set->map[HCTX_TYPE_READ].nr_queues) type = HCTX_TYPE_READ; - return blk_mq_map_queue_type(q, type, cpu); + return ctx->hctxs[type]; } /* diff --git a/block/blk.h b/block/blk.h index 848278c52030..5d636ee41663 100644 --- a/block/blk.h +++ b/block/blk.h @@ -38,7 +38,7 @@ extern struct ida blk_queue_ida; static inline struct blk_flush_queue * blk_get_flush_queue(struct request_queue *q, struct blk_mq_ctx *ctx) { - return blk_mq_map_queue(q, REQ_OP_FLUSH, ctx->cpu)->fq; + return blk_mq_map_queue(q, REQ_OP_FLUSH, ctx)->fq; } static inline void __blk_get_queue(struct request_queue *q) From bb94aea1444b9859faa8d72aff3713fcd11c6696 Mon Sep 17 00:00:00 2001 From: Jianchao Wang Date: Thu, 24 Jan 2019 18:25:33 +0800 Subject: [PATCH 0938/4277] blk-mq: save default hctx into ctx->hctxs for not-supported type Currently, we check whether the hctx type is supported every time in hot path. Actually, this is not necessary, we could save the default hctx into ctx->hctxs if the type is not supported when map swqueues and use it directly with ctx->hctxs[type]. We also needn't check whether the poll is enabled or not, because the caller would clear the REQ_HIPRI in that case. Signed-off-by: Jianchao Wang Signed-off-by: Jens Axboe --- block/blk-mq.c | 9 ++++++++- block/blk-mq.h | 13 +++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index 445d0a2642ae..8a825aebc6b5 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2431,8 +2431,11 @@ static void blk_mq_map_swqueue(struct request_queue *q) ctx = per_cpu_ptr(q->queue_ctx, i); for (j = 0; j < set->nr_maps; j++) { - if (!set->map[j].nr_queues) + if (!set->map[j].nr_queues) { + ctx->hctxs[j] = blk_mq_map_queue_type(q, + HCTX_TYPE_DEFAULT, i); continue; + } hctx = blk_mq_map_queue_type(q, j, i); ctx->hctxs[j] = hctx; @@ -2455,6 +2458,10 @@ static void blk_mq_map_swqueue(struct request_queue *q) */ BUG_ON(!hctx->nr_ctx); } + + for (; j < HCTX_MAX_TYPES; j++) + ctx->hctxs[j] = blk_mq_map_queue_type(q, + HCTX_TYPE_DEFAULT, i); } mutex_unlock(&q->sysfs_lock); diff --git a/block/blk-mq.h b/block/blk-mq.h index 9fb06261518e..14b7efb94f75 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -106,15 +106,12 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, { enum hctx_type type = HCTX_TYPE_DEFAULT; - if ((flags & REQ_HIPRI) && - q->tag_set->nr_maps > HCTX_TYPE_POLL && - q->tag_set->map[HCTX_TYPE_POLL].nr_queues && - test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) + /* + * The caller ensure that if REQ_HIPRI, poll must be enabled. + */ + if (flags & REQ_HIPRI) type = HCTX_TYPE_POLL; - - else if (((flags & REQ_OP_MASK) == REQ_OP_READ) && - q->tag_set->nr_maps > HCTX_TYPE_READ && - q->tag_set->map[HCTX_TYPE_READ].nr_queues) + else if ((flags & REQ_OP_MASK) == REQ_OP_READ) type = HCTX_TYPE_READ; return ctx->hctxs[type]; From 9ec5cd0a40f2e7834ddd55708e8def18f0940244 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 13 Dec 2018 14:04:56 +0100 Subject: [PATCH 0939/4277] ARM: 8817/1: mm: skip cleaning of idmap page tables on LPAE capable cores Currently, init_static_idmap() installs some page table entries to cover the identity mapped part of the kernel image (which is only about 160 bytes in size in a multi_v7_defconfig Thumb2 build), and calls flush_cache_louis() to ensure that the updates are visible to the page table walker on the same core. When running under virtualization, flush_cache_louis() may take more than 10 seconds to complete: [ 0.108192] Setting up static identity map for 0x40300000 - 0x403000a0 [ 13.078127] rcu: Hierarchical SRCU implementation. This is due to the fact that set/way ops are not virtualizable, and so KVM may trap each one, resulting in a substantial delay. Since only LPAE capable CPUs may execute under virtualization, and considering that LPAE capable CPUs are guaranteed to have cache coherent page table walkers (per the architecture), let's only perform this cache maintenance on non-LPAE cores. Cc: Will Deacon Acked-by: Marc Zyngier Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King --- arch/arm/mm/idmap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c index 1d1edd064199..a033f6134a64 100644 --- a/arch/arm/mm/idmap.c +++ b/arch/arm/mm/idmap.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -110,7 +111,8 @@ static int __init init_static_idmap(void) __idmap_text_end, 0); /* Flush L1 for the hardware to see this page table content */ - flush_cache_louis(); + if (!(elf_hwcap & HWCAP_LPAE)) + flush_cache_louis(); return 0; } From 4a4d68fc2657df9a2b4f114081141e2599804586 Mon Sep 17 00:00:00 2001 From: "Wolfram Sang (Renesas)" Date: Wed, 19 Dec 2018 00:12:07 +0100 Subject: [PATCH 0940/4277] ARM: 8818/1: dma-mapping: update comment about handling dma_ops when detaching from IOMMU Update the comment because we don't set the pointer to NULL anymore. Also use the correct pointer name 'dma_ops' instead of 'dma_map_ops'. Fixes: 1874619a7df4 ("ARM: dma-mapping: Set proper DMA ops in arm_iommu_detach_device()") Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Russell King --- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f1e2922e447c..fae7465767b9 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2277,7 +2277,7 @@ EXPORT_SYMBOL_GPL(arm_iommu_attach_device); * @dev: valid struct device pointer * * Detaches the provided device from a previously attached map. - * This voids the dma operations (dma_map_ops pointer) + * This overwrites the dma_ops pointer with appropriate non-IOMMU ops. */ void arm_iommu_detach_device(struct device *dev) { From 091bb549f7722723b284f63ac665e2aedcf9dec9 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 5 Jan 2019 19:35:25 +0100 Subject: [PATCH 0941/4277] ARM: 8819/1: Remove '-p' from LDFLAGS This option is not supported by lld: ld.lld: error: unknown argument: -p This has been a no-op in binutils since 2004 (see commit dea514f51da1 in that tree). Given that the lowest officially supported of binutils for the kernel is 2.20, which was released in 2009, nobody needs this flag around so just remove it. Commit 1a381d4a0a9a ("arm64: remove no-op -p linker flag") did the same for arm64. Signed-off-by: Nathan Chancellor Acked-by: Ard Biesheuvel Acked-by: Nicolas Pitre Reviewed-by: Nick Desaulniers Reviewed-by: Stefan Agner Signed-off-by: Russell King --- arch/arm/Makefile | 2 +- arch/arm/boot/bootp/Makefile | 2 +- arch/arm/boot/compressed/Makefile | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 9db3c584b2cb..ac0187c8d258 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -10,7 +10,7 @@ # # Copyright (C) 1995-2001 by Russell King -LDFLAGS_vmlinux :=-p --no-undefined -X --pic-veneer +LDFLAGS_vmlinux := --no-undefined -X --pic-veneer ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) LDFLAGS_vmlinux += --be8 KBUILD_LDFLAGS_MODULE += --be8 diff --git a/arch/arm/boot/bootp/Makefile b/arch/arm/boot/bootp/Makefile index 83e1a076a5d6..981a8d03f064 100644 --- a/arch/arm/boot/bootp/Makefile +++ b/arch/arm/boot/bootp/Makefile @@ -8,7 +8,7 @@ GCOV_PROFILE := n -LDFLAGS_bootp :=-p --no-undefined -X \ +LDFLAGS_bootp := --no-undefined -X \ --defsym initrd_phys=$(INITRD_PHYS) \ --defsym params_phys=$(PARAMS_PHYS) -T AFLAGS_initrd.o :=-DINITRD=\"$(INITRD)\" diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 6114ae6ea466..9219389bbe61 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -132,8 +132,6 @@ endif ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) LDFLAGS_vmlinux += --be8 endif -# ? -LDFLAGS_vmlinux += -p # Report unresolved symbol references LDFLAGS_vmlinux += --no-undefined # Delete all temporary local symbols From 1c31d4e96b8c205fe3aa8e73e930a0ccbf4b9a2b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 8 Jan 2019 14:27:01 +0100 Subject: [PATCH 0942/4277] ARM: 8820/1: mm: Stop printing the virtual memory layout Since commit ad67b74d2469d9b8 ("printk: hash addresses printed with %p"), the virtual memory layout printed during boot up contains "ptrval" instead of actual addresses: Memory: 501296K/524288K available (6144K kernel code, 528K rwdata, 1944K rodata, 1024K init, 7584K bss, 22992K reserved, 0K cma-reserved) Virtual kernel memory layout: vector : 0xffff0000 - 0xffff1000 ( 4 kB) fixmap : 0xffc00000 - 0xfff00000 (3072 kB) vmalloc : 0xe0800000 - 0xff800000 ( 496 MB) lowmem : 0xc0000000 - 0xe0000000 ( 512 MB) modules : 0xbf000000 - 0xc0000000 ( 16 MB) .text : 0x(ptrval) - 0x(ptrval) (7136 kB) .init : 0x(ptrval) - 0x(ptrval) (1024 kB) .data : 0x(ptrval) - 0x(ptrval) ( 529 kB) .bss : 0x(ptrval) - 0x(ptrval) (7585 kB) Instead of changing the printing to "%px", and leaking virtual memory layout information again, just remove the printing completely, cfr. e.g. commits 071929dbdd865f77 ("arm64: Stop printing the virtual memory layout") and 31833332f7987636 ("m68k/mm: Stop printing the virtual memory layout"). All interesting information (actual section sizes) is already printed by mem_init_print_info() just above anyway. Signed-off-by: Geert Uytterhoeven Reviewed-by: Kees Cook Signed-off-by: Russell King --- arch/arm/mm/init.c | 49 ---------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 478ea8b7db87..57962080e47a 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -494,55 +494,6 @@ void __init mem_init(void) mem_init_print_info(NULL); -#define MLK(b, t) b, t, ((t) - (b)) >> 10 -#define MLM(b, t) b, t, ((t) - (b)) >> 20 -#define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K) - - pr_notice("Virtual kernel memory layout:\n" - " vector : 0x%08lx - 0x%08lx (%4ld kB)\n" -#ifdef CONFIG_HAVE_TCM - " DTCM : 0x%08lx - 0x%08lx (%4ld kB)\n" - " ITCM : 0x%08lx - 0x%08lx (%4ld kB)\n" -#endif - " fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n" - " vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n" - " lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n" -#ifdef CONFIG_HIGHMEM - " pkmap : 0x%08lx - 0x%08lx (%4ld MB)\n" -#endif -#ifdef CONFIG_MODULES - " modules : 0x%08lx - 0x%08lx (%4ld MB)\n" -#endif - " .text : 0x%p" " - 0x%p" " (%4td kB)\n" - " .init : 0x%p" " - 0x%p" " (%4td kB)\n" - " .data : 0x%p" " - 0x%p" " (%4td kB)\n" - " .bss : 0x%p" " - 0x%p" " (%4td kB)\n", - - MLK(VECTORS_BASE, VECTORS_BASE + PAGE_SIZE), -#ifdef CONFIG_HAVE_TCM - MLK(DTCM_OFFSET, (unsigned long) dtcm_end), - MLK(ITCM_OFFSET, (unsigned long) itcm_end), -#endif - MLK(FIXADDR_START, FIXADDR_END), - MLM(VMALLOC_START, VMALLOC_END), - MLM(PAGE_OFFSET, (unsigned long)high_memory), -#ifdef CONFIG_HIGHMEM - MLM(PKMAP_BASE, (PKMAP_BASE) + (LAST_PKMAP) * - (PAGE_SIZE)), -#endif -#ifdef CONFIG_MODULES - MLM(MODULES_VADDR, MODULES_END), -#endif - - MLK_ROUNDUP(_text, _etext), - MLK_ROUNDUP(__init_begin, __init_end), - MLK_ROUNDUP(_sdata, _edata), - MLK_ROUNDUP(__bss_start, __bss_stop)); - -#undef MLK -#undef MLM -#undef MLK_ROUNDUP - /* * Check boundaries twice: Some fundamental inconsistencies can * be detected at build time already. From 8f433ec4d0c41d8d01113bc0bf846f2423df6c71 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 8 Jan 2019 14:28:05 +0100 Subject: [PATCH 0943/4277] ARM: 8821/1: Correct meaning of SCU in HAVE_ARM_SCU help txt According to the ARM Cortex-A5 and Cortex-A9 Technical Reference Manuals, SCU stands for "Snoop Control Unit". Signed-off-by: Geert Uytterhoeven Signed-off-by: Russell King --- arch/arm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 664e918e2624..8986cf07fe1f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1304,7 +1304,7 @@ config SCHED_SMT config HAVE_ARM_SCU bool help - This option enables support for the ARM system coherency unit + This option enables support for the ARM snoop control unit config HAVE_ARM_ARCH_TIMER bool "Architected timer support" From fec9eac6594750ee74c99549f13de3aa9de91b18 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 8 Jan 2019 14:29:23 +0100 Subject: [PATCH 0944/4277] ARM: 8822/1: smp_twd: Remove legacy TWD registration As of commit 7484c727b636a838 ("ARM: realview: delete the RealView board files"), the ARM Timer and Watchdog Unit is instantiated from DT only. Moreover, the driver is selected from ARCH_MULTIPLATFORM platforms only, which implies OF, TIMER_OF, and COMMON_CLK. Hence remove all unused legacy infrastructure from the driver. Signed-off-by: Geert Uytterhoeven Reviewed-by: Linus Walleij Acked-by: Marc Zyngier Signed-off-by: Russell King --- arch/arm/Kconfig | 1 - arch/arm/include/asm/smp_twd.h | 16 --------- arch/arm/kernel/smp_twd.c | 66 ---------------------------------- 3 files changed, 83 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8986cf07fe1f..93f380931d5b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1316,7 +1316,6 @@ config HAVE_ARM_ARCH_TIMER config HAVE_ARM_TWD bool - select TIMER_OF if OF help This options enables support for the ARM timer and watchdog unit diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h index 312784ee9936..c729d2113a24 100644 --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -19,20 +19,4 @@ #define TWD_TIMER_CONTROL_PERIODIC (1 << 1) #define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2) -#include - -struct twd_local_timer { - struct resource res[2]; -}; - -#define DEFINE_TWD_LOCAL_TIMER(name,base,irq) \ -struct twd_local_timer name __initdata = { \ - .res = { \ - DEFINE_RES_MEM(base, 0x10), \ - DEFINE_RES_IRQ(irq), \ - }, \ -}; - -int twd_local_timer_register(struct twd_local_timer *); - #endif diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b30eafeef096..3cdc399b9fc3 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -100,8 +100,6 @@ static void twd_timer_stop(void) disable_percpu_irq(clk->irq); } -#ifdef CONFIG_COMMON_CLK - /* * Updates clockevent frequency when the cpu frequency changes. * Called on the cpu that is changing frequency with interrupts disabled. @@ -143,54 +141,6 @@ static int twd_clk_init(void) } core_initcall(twd_clk_init); -#elif defined (CONFIG_CPU_FREQ) - -#include - -/* - * Updates clockevent frequency when the cpu frequency changes. - * Called on the cpu that is changing frequency with interrupts disabled. - */ -static void twd_update_frequency(void *data) -{ - twd_timer_rate = clk_get_rate(twd_clk); - - clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate); -} - -static int twd_cpufreq_transition(struct notifier_block *nb, - unsigned long state, void *data) -{ - struct cpufreq_freqs *freqs = data; - - /* - * The twd clock events must be reprogrammed to account for the new - * frequency. The timer is local to a cpu, so cross-call to the - * changing cpu. - */ - if (state == CPUFREQ_POSTCHANGE) - smp_call_function_single(freqs->cpu, twd_update_frequency, - NULL, 1); - - return NOTIFY_OK; -} - -static struct notifier_block twd_cpufreq_nb = { - .notifier_call = twd_cpufreq_transition, -}; - -static int twd_cpufreq_init(void) -{ - if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) - return cpufreq_register_notifier(&twd_cpufreq_nb, - CPUFREQ_TRANSITION_NOTIFIER); - - return 0; -} -core_initcall(twd_cpufreq_init); - -#endif - static void twd_calibrate_rate(void) { unsigned long count; @@ -366,21 +316,6 @@ out_free: return err; } -int __init twd_local_timer_register(struct twd_local_timer *tlt) -{ - if (twd_base || twd_evt) - return -EBUSY; - - twd_ppi = tlt->res[1].start; - - twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); - if (!twd_base) - return -ENOMEM; - - return twd_local_timer_common_register(NULL); -} - -#ifdef CONFIG_OF static int __init twd_local_timer_of_register(struct device_node *np) { int err; @@ -406,4 +341,3 @@ out: TIMER_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register); TIMER_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register); TIMER_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register); -#endif From 58ca33824ff850bee93c850830a7b180486f3371 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Wed, 16 Jan 2019 13:31:41 +0100 Subject: [PATCH 0945/4277] ARM: 8823/1: Implement pgprot_device() This is used when mmapping the PCI resource* files in sys. Because ARM currently lacks an implementation of pgprot_device(), it falls back to pgprot_uncached() (Strongly Ordered), but we should be able to use Device memory instead. Doing this speeds up large writes to the resource files by about 40% on one of my systems. It also ensures that mmaps on these resources use the same memory type as ioremap(). Signed-off-by: Vincent Whitchurch Signed-off-by: Russell King --- arch/arm/include/asm/pgtable.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index a757401129f9..48ce1b19069b 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -125,6 +125,9 @@ extern pgprot_t pgprot_s2_device; #define pgprot_stronglyordered(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED) +#define pgprot_device(prot) \ + __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_SHARED | L_PTE_SHARED | L_PTE_DIRTY | L_PTE_XN) + #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN) From 071d184a19f673bbb69a593c2ff4bd7acc0d1fe6 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 22 Jan 2019 21:05:10 +0100 Subject: [PATCH 0946/4277] ARM: 8826/1: mm: initialize pfn limits with find_limits() The max_low_pfn value must be set before sparse_init() is called to keep the early memblock allocations and frees balanced for kmemleak initialization when sparsemem is enabled. This commit accomplishes that by replacing the local variables min, max_low, and max_high with the global limit variables min_low_pfn, max_low_pfn, and max_pfn respectively in bootmem_init(). The global variables are initialized directly by find_limits() and used in the remainder of the function. Fixes: 9099daed9c69 ("mm: kmemleak: avoid using __va() on addresses that don't have a lowmem mapping") Cc: Catalin Marinas Acked-by: Mike Rapoport Signed-off-by: Doug Berger Signed-off-by: Russell King --- arch/arm/mm/init.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 57962080e47a..a6b0805b7977 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -278,15 +278,12 @@ void __init arm_memblock_init(const struct machine_desc *mdesc) void __init bootmem_init(void) { - unsigned long min, max_low, max_high; - memblock_allow_resize(); - max_low = max_high = 0; - find_limits(&min, &max_low, &max_high); + find_limits(&min_low_pfn, &max_low_pfn, &max_pfn); - early_memtest((phys_addr_t)min << PAGE_SHIFT, - (phys_addr_t)max_low << PAGE_SHIFT); + early_memtest((phys_addr_t)min_low_pfn << PAGE_SHIFT, + (phys_addr_t)max_low_pfn << PAGE_SHIFT); /* * Sparsemem tries to allocate bootmem in memory_present(), @@ -304,16 +301,7 @@ void __init bootmem_init(void) * the sparse mem_map arrays initialized by sparse_init() * for memmap_init_zone(), otherwise all PFNs are invalid. */ - zone_sizes_init(min, max_low, max_high); - - /* - * This doesn't seem to be used by the Linux memory manager any - * more, but is used by ll_rw_block. If we can get rid of it, we - * also get rid of some of the stuff above as well. - */ - min_low_pfn = min; - max_low_pfn = max_low; - max_pfn = max_high; + zone_sizes_init(min_low_pfn, max_low_pfn, max_pfn); } /* From baf2df8e15be22b8bd24bdd6fd4575b6641bcfd1 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 24 Jan 2019 21:41:59 +0100 Subject: [PATCH 0947/4277] ARM: 8827/1: fix argument count to match macro definition The macro str8w takes 10 arguments, abort being the 10th. In this particular instantiation the abort argument is passed as 11th argument leading to an error when using LLVM's integrated assembler: :46:47: error: too many positional arguments str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f ^ arch/arm/lib/copy_template.S:277:5: note: while in macro instantiation 18: forward_copy_shift pull=24 push=8 ^ The argument is not used in the macro hence this does not change code generation. Signed-off-by: Stefan Agner Reviewed-by: Nicolas Pitre Signed-off-by: Russell King --- arch/arm/lib/copy_template.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/lib/copy_template.S b/arch/arm/lib/copy_template.S index 652e4d98cd47..2d54491b0e22 100644 --- a/arch/arm/lib/copy_template.S +++ b/arch/arm/lib/copy_template.S @@ -241,7 +241,7 @@ orr r9, r9, ip, lspush #\push mov ip, ip, lspull #\pull orr ip, ip, lr, lspush #\push - str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, abort=19f bge 12b PLD( cmn r2, #96 ) PLD( bge 13b ) From 32fdb046ac43aa884d960165072ca37b26d78543 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 24 Jan 2019 21:42:54 +0100 Subject: [PATCH 0948/4277] ARM: 8828/1: uaccess: use unified assembler language syntax Convert the conditional infix to a postfix to make sure this inline assembly is unified syntax. Since gcc assumes non-unified syntax when emitting ARM instructions, make sure to define the syntax as unified. This allows to use LLVM's integrated assembler. Signed-off-by: Stefan Agner Signed-off-by: Russell King --- arch/arm/include/asm/uaccess.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index 42aa4a22803c..89a28680934b 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -86,7 +86,8 @@ static inline void set_fs(mm_segment_t fs) #define __range_ok(addr, size) ({ \ unsigned long flag, roksum; \ __chk_user_ptr(addr); \ - __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \ + __asm__(".syntax unified\n" \ + "adds %1, %2, %3; sbcscc %1, %1, %0; movcc %0, #0" \ : "=&r" (flag), "=&r" (roksum) \ : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \ : "cc"); \ From eb7ff9023e4f2998d527b37bffe794759800332a Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 24 Jan 2019 21:43:40 +0100 Subject: [PATCH 0949/4277] ARM: 8829/1: spinlock: use unified assembler language syntax Convert the conditional infix to a postfix to make sure this inline assembly is unified syntax. Since gcc assumes non-unified syntax when emitting ARM instructions, make sure to define the syntax as unified. This allows to use LLVM's integrated assembler. Signed-off-by: Stefan Agner Signed-off-by: Russell King --- arch/arm/include/asm/spinlock.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/spinlock.h b/arch/arm/include/asm/spinlock.h index 099c78fcf62d..8f009e788ad4 100644 --- a/arch/arm/include/asm/spinlock.h +++ b/arch/arm/include/asm/spinlock.h @@ -210,11 +210,12 @@ static inline void arch_read_lock(arch_rwlock_t *rw) prefetchw(&rw->lock); __asm__ __volatile__( +" .syntax unified\n" "1: ldrex %0, [%2]\n" " adds %0, %0, #1\n" " strexpl %1, %0, [%2]\n" WFE("mi") -" rsbpls %0, %1, #0\n" +" rsbspl %0, %1, #0\n" " bmi 1b" : "=&r" (tmp), "=&r" (tmp2) : "r" (&rw->lock) From 72cd4064fccaae15ab84d40d4be23667402df4ed Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Fri, 25 Jan 2019 15:18:37 +0100 Subject: [PATCH 0950/4277] ARM: 8830/1: NOMMU: Toggle only bits in EXC_RETURN we are really care of ARMv8M introduces support for Security extension to M class, among other things it affects exception handling, especially, encoding of EXC_RETURN. The new bits have been added: Bit [6] Secure or Non-secure stack Bit [5] Default callee register stacking Bit [0] Exception Secure which conflicts with hard-coded value of EXC_RETURN: In fact, we only care of few bits: Bit [3] Mode (0 - Handler, 1 - Thread) Bit [2] Stack pointer selection (0 - Main, 1 - Process) We can toggle only those bits and left other bits as they were on exception entry. It is basically, what patch does - saves EXC_RETURN when we do transition form Thread to Handler mode (it is first svc), so later saved value is used instead of EXC_RET_THREADMODE_PROCESSSTACK. Signed-off-by: Vladimir Murzin Signed-off-by: Russell King --- arch/arm/include/asm/v7m.h | 2 +- arch/arm/kernel/entry-header.S | 3 ++- arch/arm/kernel/entry-v7m.S | 4 ++++ arch/arm/mm/proc-v7m.S | 3 +++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/v7m.h b/arch/arm/include/asm/v7m.h index 187ccf6496ad..2cb00d15831b 100644 --- a/arch/arm/include/asm/v7m.h +++ b/arch/arm/include/asm/v7m.h @@ -49,7 +49,7 @@ * (0 -> msp; 1 -> psp). Bits [1:0] are fixed to 0b01. */ #define EXC_RET_STACK_MASK 0x00000004 -#define EXC_RET_THREADMODE_PROCESSSTACK 0xfffffffd +#define EXC_RET_THREADMODE_PROCESSSTACK (3 << 2) /* Cache related definitions */ diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 773424843d6e..62db1c9746cb 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -127,7 +127,8 @@ */ .macro v7m_exception_slow_exit ret_r0 cpsid i - ldr lr, =EXC_RET_THREADMODE_PROCESSSTACK + ldr lr, =exc_ret + ldr lr, [lr] @ read original r12, sp, lr, pc and xPSR add r12, sp, #S_IP diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S index abcf47848525..19d2dcd6530d 100644 --- a/arch/arm/kernel/entry-v7m.S +++ b/arch/arm/kernel/entry-v7m.S @@ -146,3 +146,7 @@ ENTRY(vector_table) .rept CONFIG_CPU_V7M_NUM_IRQ .long __irq_entry @ External Interrupts .endr + .align 2 + .globl exc_ret +exc_ret: + .space 4 diff --git a/arch/arm/mm/proc-v7m.S b/arch/arm/mm/proc-v7m.S index 47a5acc64433..92e84181933a 100644 --- a/arch/arm/mm/proc-v7m.S +++ b/arch/arm/mm/proc-v7m.S @@ -139,6 +139,9 @@ __v7m_setup_cont: cpsie i svc #0 1: cpsid i + ldr r0, =exc_ret + orr lr, lr, #EXC_RET_THREADMODE_PROCESSSTACK + str lr, [r0] ldmia sp, {r0-r3, r12} str r5, [r12, #11 * 4] @ restore the original SVC vector entry mov lr, r6 @ restore LR From 49f30235061bf5c5c201c5097061e34510bf110a Mon Sep 17 00:00:00 2001 From: Peng Hao Date: Fri, 25 Jan 2019 15:26:32 +0100 Subject: [PATCH 0951/4277] ARM: 8831/1: NOMMU: pmsa-v8: remove unneeded semicolon Remove unneeded semicolon. [vladimir] proper tags in subject line Signed-off-by: Peng Hao Acked-by: Vladimir Murzin Signed-off-by: Vladimir Murzin Signed-off-by: Russell King --- arch/arm/mm/pmsa-v8.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mm/pmsa-v8.c b/arch/arm/mm/pmsa-v8.c index 617a83def88a..0d7d5fb59247 100644 --- a/arch/arm/mm/pmsa-v8.c +++ b/arch/arm/mm/pmsa-v8.c @@ -165,7 +165,7 @@ static int __init pmsav8_setup_ram(unsigned int number, phys_addr_t start,phys_a return -EINVAL; bar = start; - lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);; + lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED; lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; @@ -181,7 +181,7 @@ static int __init pmsav8_setup_io(unsigned int number, phys_addr_t start,phys_ad return -EINVAL; bar = start; - lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);; + lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED | PMSAv8_BAR_XN; lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_DEVICE_nGnRnE) | PMSAv8_LAR_EN; From 49e30bd07df512cdc42e63c14235e6239e92d749 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Thu, 31 Jan 2019 13:50:29 +0100 Subject: [PATCH 0952/4277] ARM: 8832/1: NOMMU: Limit visibility for CONFIG_FLASH_{MEM_BASE,SIZE} It looks like usage of CONFIG_FLASH_{MEM_BASE,SIZE} is limited with: arch/arm/mm/proc-arm740.S arch/arm/mm/proc-arm940.S arch/arm/mm/proc-arm946.S So it might look confusing to see the option for anything except these. Signed-off-by: Vladimir Murzin Acked-by: Arnd Bergmann Signed-off-by: Russell King --- arch/arm/Kconfig-nommu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu index 1168a03c8525..36c80d3dd93f 100644 --- a/arch/arm/Kconfig-nommu +++ b/arch/arm/Kconfig-nommu @@ -20,10 +20,12 @@ config DRAM_SIZE config FLASH_MEM_BASE hex 'FLASH Base Address' if SET_MEM_PARAM + depends on CPU_ARM740T || CPU_ARM946E || CPU_ARM940T default 0x00400000 config FLASH_SIZE hex 'FLASH Size' if SET_MEM_PARAM + depends on CPU_ARM740T || CPU_ARM946E || CPU_ARM940T default 0x00400000 config PROCESSOR_ID From d0e22329e1a51548bdeeae66bc35fe2dc1cabdf6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Dec 2018 12:54:26 +0000 Subject: [PATCH 0953/4277] ARM: qcom: remove unnecessary boot_lock The boot_lock is something that was required for ARM development platforms to ensure that the delay calibration worked properly. This is not necessary for modern platforms that have better bus bandwidth and do not need to calibrate the delay loop for secondary cores. Remove the boot_lock entirely. Signed-off-by: Russell King --- arch/arm/mach-qcom/platsmp.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 5494c9e0c909..99a6a5e809e0 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -46,8 +46,6 @@ extern void secondary_startup_arm(void); -static DEFINE_SPINLOCK(boot_lock); - #ifdef CONFIG_HOTPLUG_CPU static void qcom_cpu_die(unsigned int cpu) { @@ -55,15 +53,6 @@ static void qcom_cpu_die(unsigned int cpu) } #endif -static void qcom_secondary_init(unsigned int cpu) -{ - /* - * Synchronise with the boot thread. - */ - spin_lock(&boot_lock); - spin_unlock(&boot_lock); -} - static int scss_release_secondary(unsigned int cpu) { struct device_node *node; @@ -280,12 +269,6 @@ static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) per_cpu(cold_boot_done, cpu) = true; } - /* - * set synchronisation state between this boot processor - * and the secondary one - */ - spin_lock(&boot_lock); - /* * Send the secondary CPU a soft interrupt, thereby causing * the boot monitor to read the system wide flags register, @@ -293,12 +276,6 @@ static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) */ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); - /* - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ - spin_unlock(&boot_lock); - return ret; } @@ -334,7 +311,6 @@ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) static const struct smp_operations smp_msm8660_ops __initconst = { .smp_prepare_cpus = qcom_smp_prepare_cpus, - .smp_secondary_init = qcom_secondary_init, .smp_boot_secondary = msm8660_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = qcom_cpu_die, @@ -344,7 +320,6 @@ CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); static const struct smp_operations qcom_smp_kpssv1_ops __initconst = { .smp_prepare_cpus = qcom_smp_prepare_cpus, - .smp_secondary_init = qcom_secondary_init, .smp_boot_secondary = kpssv1_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = qcom_cpu_die, @@ -354,7 +329,6 @@ CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops) static const struct smp_operations qcom_smp_kpssv2_ops __initconst = { .smp_prepare_cpus = qcom_smp_prepare_cpus, - .smp_secondary_init = qcom_secondary_init, .smp_boot_secondary = kpssv2_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = qcom_cpu_die, From 0eb037998afe5514c8534276c152da31d2fabf07 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Dec 2018 12:54:26 +0000 Subject: [PATCH 0954/4277] ARM: oxnas: remove CPU hotplug implementation The CPU hotplug implementation on this platform is cargo-culted from the plat-versatile implementation, and is buggy. Once a CPU hits the "low power" loop, it will wait for pen_release to be set to the CPU number to wake up again - but nothing in this implementation does that. So, once a CPU has entered cpu_die() it will never, ever leave. Remove this useless cargo-culted implementation. Signed-off-by: Russell King --- arch/arm/mach-oxnas/Makefile | 1 - arch/arm/mach-oxnas/hotplug.c | 109 ---------------------------------- arch/arm/mach-oxnas/platsmp.c | 4 -- 3 files changed, 114 deletions(-) delete mode 100644 arch/arm/mach-oxnas/hotplug.c diff --git a/arch/arm/mach-oxnas/Makefile b/arch/arm/mach-oxnas/Makefile index b625906a9970..61a34e1c0f22 100644 --- a/arch/arm/mach-oxnas/Makefile +++ b/arch/arm/mach-oxnas/Makefile @@ -1,2 +1 @@ obj-$(CONFIG_SMP) += platsmp.o headsmp.o -obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-oxnas/hotplug.c b/arch/arm/mach-oxnas/hotplug.c deleted file mode 100644 index 854f29b8cba6..000000000000 --- a/arch/arm/mach-oxnas/hotplug.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2002 ARM Ltd. - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#include -#include - -static inline void cpu_enter_lowpower(void) -{ - unsigned int v; - - asm volatile( - " mcr p15, 0, %1, c7, c5, 0\n" - " mcr p15, 0, %1, c7, c10, 4\n" - /* - * Turn off coherency - */ - " mrc p15, 0, %0, c1, c0, 1\n" - " bic %0, %0, #0x20\n" - " mcr p15, 0, %0, c1, c0, 1\n" - " mrc p15, 0, %0, c1, c0, 0\n" - " bic %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (v) - : "r" (0), "Ir" (CR_C) - : "cc"); -} - -static inline void cpu_leave_lowpower(void) -{ - unsigned int v; - - asm volatile( "mrc p15, 0, %0, c1, c0, 0\n" - " orr %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 1\n" - " orr %0, %0, #0x20\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (v) - : "Ir" (CR_C) - : "cc"); -} - -static inline void platform_do_lowpower(unsigned int cpu, int *spurious) -{ - /* - * there is no power-control hardware on this platform, so all - * we can do is put the core into WFI; this is safe as the calling - * code will have already disabled interrupts - */ - for (;;) { - /* - * here's the WFI - */ - asm(".word 0xe320f003\n" - : - : - : "memory", "cc"); - - if (pen_release == cpu_logical_map(cpu)) { - /* - * OK, proper wakeup, we're done - */ - break; - } - - /* - * Getting here, means that we have come out of WFI without - * having been woken up - this shouldn't happen - * - * Just note it happening - when we're woken, we can report - * its occurrence. - */ - (*spurious)++; - } -} - -/* - * platform-specific code to shutdown a CPU - * - * Called with IRQs disabled - */ -void ox820_cpu_die(unsigned int cpu) -{ - int spurious = 0; - - /* - * we're ready for shutdown now, so do it - */ - cpu_enter_lowpower(); - platform_do_lowpower(cpu, &spurious); - - /* - * bring this CPU back into the world of cache - * coherency, and then restore interrupts - */ - cpu_leave_lowpower(); - - if (spurious) - pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); -} diff --git a/arch/arm/mach-oxnas/platsmp.c b/arch/arm/mach-oxnas/platsmp.c index 442cc8a2f7dc..735141c0e3a3 100644 --- a/arch/arm/mach-oxnas/platsmp.c +++ b/arch/arm/mach-oxnas/platsmp.c @@ -19,7 +19,6 @@ #include extern void ox820_secondary_startup(void); -extern void ox820_cpu_die(unsigned int cpu); static void __iomem *cpu_ctrl; static void __iomem *gic_cpu_ctrl; @@ -94,9 +93,6 @@ unmap_scu: static const struct smp_operations ox820_smp_ops __initconst = { .smp_prepare_cpus = ox820_smp_prepare_cpus, .smp_boot_secondary = ox820_boot_secondary, -#ifdef CONFIG_HOTPLUG_CPU - .cpu_die = ox820_cpu_die, -#endif }; CPU_METHOD_OF_DECLARE(ox820_smp, "oxsemi,ox820-smp", &ox820_smp_ops); From 70678554c4c40f2cf8de5e7cd53fdd2c73387e51 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Dec 2018 12:54:26 +0000 Subject: [PATCH 0955/4277] ARM: actions: remove boot_lock and pen_release The actions SMP implementation has several issues: 1. pen_release is only ever read and compared to -1, and is defined in arch/arm/kernel/smp.c to be -1. This test will always succeed. 2. we are already guaranteed to be single threaded while bringing up a CPU, so the spinlock makes no sense, remove it. 3. owl_secondary_startup() is not referenced nor defined, the prototype is redundant, remove it. Signed-off-by: Russell King --- arch/arm/mach-actions/platsmp.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/arch/arm/mach-actions/platsmp.c b/arch/arm/mach-actions/platsmp.c index 3efaa10efc43..4fd479c948e6 100644 --- a/arch/arm/mach-actions/platsmp.c +++ b/arch/arm/mach-actions/platsmp.c @@ -39,10 +39,6 @@ static void __iomem *sps_base_addr; static void __iomem *timer_base_addr; static int ncores; -static DEFINE_SPINLOCK(boot_lock); - -void owl_secondary_startup(void); - static int s500_wakeup_secondary(unsigned int cpu) { int ret; @@ -84,7 +80,6 @@ static int s500_wakeup_secondary(unsigned int cpu) static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) { - unsigned long timeout; int ret; ret = s500_wakeup_secondary(cpu); @@ -93,21 +88,11 @@ static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle) udelay(10); - spin_lock(&boot_lock); - smp_send_reschedule(cpu); - timeout = jiffies + (1 * HZ); - while (time_before(jiffies, timeout)) { - if (pen_release == -1) - break; - } - writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); - spin_unlock(&boot_lock); - return 0; } From 6213f70e7c10fd4a01b65bad3826648fc78df8a8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Dec 2018 14:02:48 +0000 Subject: [PATCH 0956/4277] ARM: smp: remove arch-provided "pen_release" Consolidating the "pen_release" stuff amongst the various SoC implementations gives credence to having a CPU holding pen for secondary CPUs. However, this is far from the truth. Many SoC implementations cargo-cult copied various bits of the pen release implementation from the initial Realview/Versatile Express implementation without understanding what it was or why it existed. The reason it existed is because these are _development_ platforms, and some board firmware is unable to individually control the startup of secondary CPUs. Moreover, they do not have a way to power down or reset secondary CPUs for hot-unplug. Hence, the pen_release implementation was designed for ARM Ltd's development platforms to provide a working implementation, even though it is very far from what is required. It was decided a while back to reduce the duplication by consolidating the "pen_release" variable, but this only made the situation worse - we have ended up with several implementations that read this variable but do not write it - again, showing the cargo-cult mentality at work, lack of proper review of new code, and in some cases a lack of testing. While it would be preferable to remove pen_release entirely from the kernel, this is not possible without help from the SoC maintainers, which seems to be lacking. However, I want to remove pen_release from arch code to remove the credence that having it gives. This patch removes pen_release from the arch code entirely, adding private per-SoC definitions for it instead, and explicitly stating that write_pen_release() is cargo-cult copied and should not be copied any further. Rename write_pen_release() in a similar fashion as well. Signed-off-by: Russell King --- arch/arm/include/asm/smp.h | 1 - arch/arm/kernel/smp.c | 6 ------ arch/arm/mach-exynos/headsmp.S | 2 +- arch/arm/mach-exynos/platsmp.c | 31 ++++++++++++++++++------------- arch/arm/mach-prima2/common.h | 2 ++ arch/arm/mach-prima2/headsmp.S | 2 +- arch/arm/mach-prima2/hotplug.c | 3 ++- arch/arm/mach-prima2/platsmp.c | 17 ++++++++++------- arch/arm/mach-spear/generic.h | 2 ++ arch/arm/mach-spear/headsmp.S | 2 +- arch/arm/mach-spear/hotplug.c | 4 +++- arch/arm/mach-spear/platsmp.c | 27 ++++++++++++++++----------- 12 files changed, 56 insertions(+), 43 deletions(-) diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 709a55989cb0..451ae684aaf4 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -67,7 +67,6 @@ struct secondary_data { void *stack; }; extern struct secondary_data secondary_data; -extern volatile int pen_release; extern void secondary_startup(void); extern void secondary_startup_arm(void); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 3bf82232b1be..051b0e8e7d30 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -62,12 +62,6 @@ */ struct secondary_data secondary_data; -/* - * control for which core is the next to come out of the secondary - * boot "holding pen" - */ -volatile int pen_release = -1; - enum ipi_msg_type { IPI_WAKEUP, IPI_TIMER, diff --git a/arch/arm/mach-exynos/headsmp.S b/arch/arm/mach-exynos/headsmp.S index 005695c9bf40..0ac2cb9a7355 100644 --- a/arch/arm/mach-exynos/headsmp.S +++ b/arch/arm/mach-exynos/headsmp.S @@ -36,4 +36,4 @@ ENDPROC(exynos4_secondary_startup) .align 2 1: .long . - .long pen_release + .long exynos_pen_release diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index c39ffd2e2fe6..3795412c0189 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -28,6 +28,9 @@ extern void exynos4_secondary_startup(void); +/* XXX exynos_pen_release is cargo culted code - DO NOT COPY XXX */ +volatile int exynos_pen_release = -1; + #ifdef CONFIG_HOTPLUG_CPU static inline void cpu_leave_lowpower(u32 core_id) { @@ -57,7 +60,7 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious) wfi(); - if (pen_release == core_id) { + if (exynos_pen_release == core_id) { /* * OK, proper wakeup, we're done */ @@ -228,15 +231,17 @@ void exynos_core_restart(u32 core_id) } /* - * Write pen_release in a way that is guaranteed to be visible to all - * observers, irrespective of whether they're taking part in coherency + * XXX CARGO CULTED CODE - DO NOT COPY XXX + * + * Write exynos_pen_release in a way that is guaranteed to be visible to + * all observers, irrespective of whether they're taking part in coherency * or not. This is necessary for the hotplug code to work reliably. */ -static void write_pen_release(int val) +static void exynos_write_pen_release(int val) { - pen_release = val; + exynos_pen_release = val; smp_wmb(); - sync_cache_w(&pen_release); + sync_cache_w(&exynos_pen_release); } static DEFINE_SPINLOCK(boot_lock); @@ -247,7 +252,7 @@ static void exynos_secondary_init(unsigned int cpu) * let the primary processor know we're out of the * pen, then head off into the C entry point */ - write_pen_release(-1); + exynos_write_pen_release(-1); /* * Synchronise with the boot thread. @@ -322,12 +327,12 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) /* * The secondary processor is waiting to be released from * the holding pen - release it, then wait for it to flag - * that it has been released by resetting pen_release. + * that it has been released by resetting exynos_pen_release. * - * Note that "pen_release" is the hardware CPU core ID, whereas + * Note that "exynos_pen_release" is the hardware CPU core ID, whereas * "cpu" is Linux's internal ID. */ - write_pen_release(core_id); + exynos_write_pen_release(core_id); if (!exynos_cpu_power_state(core_id)) { exynos_cpu_power_up(core_id); @@ -376,13 +381,13 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) else arch_send_wakeup_ipi_mask(cpumask_of(cpu)); - if (pen_release == -1) + if (exynos_pen_release == -1) break; udelay(10); } - if (pen_release != -1) + if (exynos_pen_release != -1) ret = -ETIMEDOUT; /* @@ -392,7 +397,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) fail: spin_unlock(&boot_lock); - return pen_release != -1 ? ret : 0; + return exynos_pen_release != -1 ? ret : 0; } static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h index 6d77b622d168..457eb7b18160 100644 --- a/arch/arm/mach-prima2/common.h +++ b/arch/arm/mach-prima2/common.h @@ -15,6 +15,8 @@ #include #include +extern volatile int prima2_pen_release; + extern const struct smp_operations sirfsoc_smp_ops; extern void sirfsoc_secondary_startup(void); extern void sirfsoc_cpu_die(unsigned int cpu); diff --git a/arch/arm/mach-prima2/headsmp.S b/arch/arm/mach-prima2/headsmp.S index 209d9fc5c16c..6cf4fc60347b 100644 --- a/arch/arm/mach-prima2/headsmp.S +++ b/arch/arm/mach-prima2/headsmp.S @@ -34,4 +34,4 @@ ENDPROC(sirfsoc_secondary_startup) .align 1: .long . - .long pen_release + .long prima2_pen_release diff --git a/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c index a728c78b996f..b6cf1527e330 100644 --- a/arch/arm/mach-prima2/hotplug.c +++ b/arch/arm/mach-prima2/hotplug.c @@ -11,6 +11,7 @@ #include #include +#include "common.h" static inline void platform_do_lowpower(unsigned int cpu) { @@ -18,7 +19,7 @@ static inline void platform_do_lowpower(unsigned int cpu) for (;;) { __asm__ __volatile__("dsb\n\t" "wfi\n\t" : : : "memory"); - if (pen_release == cpu_logical_map(cpu)) { + if (prima2_pen_release == cpu_logical_map(cpu)) { /* * OK, proper wakeup, we're done */ diff --git a/arch/arm/mach-prima2/platsmp.c b/arch/arm/mach-prima2/platsmp.c index 75ef5d4be554..d1f8b5168083 100644 --- a/arch/arm/mach-prima2/platsmp.c +++ b/arch/arm/mach-prima2/platsmp.c @@ -24,13 +24,16 @@ static void __iomem *clk_base; static DEFINE_SPINLOCK(boot_lock); +/* XXX prima2_pen_release is cargo culted code - DO NOT COPY XXX */ +volatile int prima2_pen_release = -1; + static void sirfsoc_secondary_init(unsigned int cpu) { /* * let the primary processor know we're out of the * pen, then head off into the C entry point */ - pen_release = -1; + prima2_pen_release = -1; smp_wmb(); /* @@ -80,13 +83,13 @@ static int sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle) /* * The secondary processor is waiting to be released from * the holding pen - release it, then wait for it to flag - * that it has been released by resetting pen_release. + * that it has been released by resetting prima2_pen_release. * - * Note that "pen_release" is the hardware CPU ID, whereas + * Note that "prima2_pen_release" is the hardware CPU ID, whereas * "cpu" is Linux's internal ID. */ - pen_release = cpu_logical_map(cpu); - sync_cache_w(&pen_release); + prima2_pen_release = cpu_logical_map(cpu); + sync_cache_w(&prima2_pen_release); /* * Send the secondary CPU SEV, thereby causing the boot monitor to read @@ -97,7 +100,7 @@ static int sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle) timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { smp_rmb(); - if (pen_release == -1) + if (prima2_pen_release == -1) break; udelay(10); @@ -109,7 +112,7 @@ static int sirfsoc_boot_secondary(unsigned int cpu, struct task_struct *idle) */ spin_unlock(&boot_lock); - return pen_release != -1 ? -ENOSYS : 0; + return prima2_pen_release != -1 ? -ENOSYS : 0; } const struct smp_operations sirfsoc_smp_ops __initconst = { diff --git a/arch/arm/mach-spear/generic.h b/arch/arm/mach-spear/generic.h index 909b97c0b237..25b4c5e66e39 100644 --- a/arch/arm/mach-spear/generic.h +++ b/arch/arm/mach-spear/generic.h @@ -20,6 +20,8 @@ #include +extern volatile int spear_pen_release; + extern void spear13xx_timer_init(void); extern void spear3xx_timer_init(void); extern struct pl022_ssp_controller pl022_plat_data; diff --git a/arch/arm/mach-spear/headsmp.S b/arch/arm/mach-spear/headsmp.S index c52192dc3d9f..6e250b6c0aa2 100644 --- a/arch/arm/mach-spear/headsmp.S +++ b/arch/arm/mach-spear/headsmp.S @@ -43,5 +43,5 @@ pen: ldr r7, [r6] .align 1: .long . - .long pen_release + .long spear_pen_release ENDPROC(spear13xx_secondary_startup) diff --git a/arch/arm/mach-spear/hotplug.c b/arch/arm/mach-spear/hotplug.c index 12edd1cf8a12..0dd84f609627 100644 --- a/arch/arm/mach-spear/hotplug.c +++ b/arch/arm/mach-spear/hotplug.c @@ -16,6 +16,8 @@ #include #include +#include "generic.h" + static inline void cpu_enter_lowpower(void) { unsigned int v; @@ -57,7 +59,7 @@ static inline void spear13xx_do_lowpower(unsigned int cpu, int *spurious) for (;;) { wfi(); - if (pen_release == cpu) { + if (spear_pen_release == cpu) { /* * OK, proper wakeup, we're done */ diff --git a/arch/arm/mach-spear/platsmp.c b/arch/arm/mach-spear/platsmp.c index 39038a03836a..b1ff4bb86f6d 100644 --- a/arch/arm/mach-spear/platsmp.c +++ b/arch/arm/mach-spear/platsmp.c @@ -20,16 +20,21 @@ #include #include "generic.h" +/* XXX spear_pen_release is cargo culted code - DO NOT COPY XXX */ +volatile int spear_pen_release = -1; + /* - * Write pen_release in a way that is guaranteed to be visible to all - * observers, irrespective of whether they're taking part in coherency + * XXX CARGO CULTED CODE - DO NOT COPY XXX + * + * Write spear_pen_release in a way that is guaranteed to be visible to + * all observers, irrespective of whether they're taking part in coherency * or not. This is necessary for the hotplug code to work reliably. */ -static void write_pen_release(int val) +static void spear_write_pen_release(int val) { - pen_release = val; + spear_pen_release = val; smp_wmb(); - sync_cache_w(&pen_release); + sync_cache_w(&spear_pen_release); } static DEFINE_SPINLOCK(boot_lock); @@ -42,7 +47,7 @@ static void spear13xx_secondary_init(unsigned int cpu) * let the primary processor know we're out of the * pen, then head off into the C entry point */ - write_pen_release(-1); + spear_write_pen_release(-1); /* * Synchronise with the boot thread. @@ -64,17 +69,17 @@ static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) /* * The secondary processor is waiting to be released from * the holding pen - release it, then wait for it to flag - * that it has been released by resetting pen_release. + * that it has been released by resetting spear_pen_release. * - * Note that "pen_release" is the hardware CPU ID, whereas + * Note that "spear_pen_release" is the hardware CPU ID, whereas * "cpu" is Linux's internal ID. */ - write_pen_release(cpu); + spear_write_pen_release(cpu); timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { smp_rmb(); - if (pen_release == -1) + if (spear_pen_release == -1) break; udelay(10); @@ -86,7 +91,7 @@ static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) */ spin_unlock(&boot_lock); - return pen_release != -1 ? -ENOSYS : 0; + return spear_pen_release != -1 ? -ENOSYS : 0; } /* From 5388a5b82199facacd3d7ac0d05aca6e8f902fed Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 10 Apr 2018 11:35:36 +0100 Subject: [PATCH 0957/4277] ARM: avoid Cortex-A9 livelock on tight dmb loops machine_crash_nonpanic_core() does this: while (1) cpu_relax(); because the kernel has crashed, and we have no known safe way to deal with the CPU. So, we place the CPU into an infinite loop which we expect it to never exit - at least not until the system as a whole is reset by some method. In the absence of erratum 754327, this code assembles to: b . In other words, an infinite loop. When erratum 754327 is enabled, this becomes: 1: dmb b 1b It has been observed that on some systems (eg, OMAP4) where, if a crash is triggered, the system tries to kexec into the panic kernel, but fails after taking the secondary CPU down - placing it into one of these loops. This causes the system to livelock, and the most noticable effect is the system stops after issuing: Loading crashdump kernel... to the system console. The tested as working solution I came up with was to add wfe() to these infinite loops thusly: while (1) { cpu_relax(); wfe(); } which, without 754327 builds to: 1: wfe b 1b or with 754327 is enabled: 1: dmb wfe b 1b Adding "wfe" does two things depending on the environment we're running under: - where we're running on bare metal, and the processor implements "wfe", it stops us spinning endlessly in a loop where we're never going to do any useful work. - if we're running in a VM, it allows the CPU to be given back to the hypervisor and rescheduled for other purposes (maybe a different VM) rather than wasting CPU cycles inside a crashed VM. However, in light of erratum 794072, Will Deacon wanted to see 10 nops as well - which is reasonable to cover the case where we have erratum 754327 enabled _and_ we have a processor that doesn't implement the wfe hint. So, we now end up with: 1: wfe b 1b when erratum 754327 is disabled, or: 1: dmb nop nop nop nop nop nop nop nop nop nop wfe b 1b when erratum 754327 is enabled. We also get the dmb + 10 nop sequence elsewhere in the kernel, in terminating loops. This is reasonable - it means we get the workaround for erratum 794072 when erratum 754327 is enabled, but still relinquish the dead processor - either by placing it in a lower power mode when wfe is implemented as such or by returning it to the hypervisior, or in the case where wfe is a no-op, we use the workaround specified in erratum 794072 to avoid the problem. These as two entirely orthogonal problems - the 10 nops addresses erratum 794072, and the wfe is an optimisation that makes the system more efficient when crashed either in terms of power consumption or by allowing the host/other VMs to make use of the CPU. I don't see any reason not to use kexec() inside a VM - it has the potential to provide automated recovery from a failure of the VMs kernel with the opportunity for saving a crashdump of the failure. A panic() with a reboot timeout won't do that, and reading the libvirt documentation, setting on_reboot to "preserve" won't either (the documentation states "The preserve action for an on_reboot event is treated as a destroy".) Surely it has to be a good thing to avoiding having CPUs spinning inside a VM that is doing no useful work. Acked-by: Will Deacon Signed-off-by: Russell King --- arch/arm/include/asm/barrier.h | 2 ++ arch/arm/include/asm/processor.h | 6 +++++- arch/arm/kernel/machine_kexec.c | 5 ++++- arch/arm/kernel/smp.c | 4 +++- arch/arm/mach-omap2/prm_common.c | 4 +++- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/arch/arm/include/asm/barrier.h b/arch/arm/include/asm/barrier.h index 69772e742a0a..83ae97c049d9 100644 --- a/arch/arm/include/asm/barrier.h +++ b/arch/arm/include/asm/barrier.h @@ -11,6 +11,8 @@ #define sev() __asm__ __volatile__ ("sev" : : : "memory") #define wfe() __asm__ __volatile__ ("wfe" : : : "memory") #define wfi() __asm__ __volatile__ ("wfi" : : : "memory") +#else +#define wfe() do { } while (0) #endif #if __LINUX_ARM_ARCH__ >= 7 diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index 120f4c9bbfde..57fe73ea0f72 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -89,7 +89,11 @@ extern void release_thread(struct task_struct *); unsigned long get_wchan(struct task_struct *p); #if __LINUX_ARM_ARCH__ == 6 || defined(CONFIG_ARM_ERRATA_754327) -#define cpu_relax() smp_mb() +#define cpu_relax() \ + do { \ + smp_mb(); \ + __asm__ __volatile__("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); \ + } while (0) #else #define cpu_relax() barrier() #endif diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index dd2eb5f76b9f..76300f3813e8 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -91,8 +91,11 @@ void machine_crash_nonpanic_core(void *unused) set_cpu_online(smp_processor_id(), false); atomic_dec(&waiting_for_crash_ipi); - while (1) + + while (1) { cpu_relax(); + wfe(); + } } void crash_smp_send_stop(void) diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 3bf82232b1be..7f0b99e1fff3 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -604,8 +604,10 @@ static void ipi_cpu_stop(unsigned int cpu) local_fiq_disable(); local_irq_disable(); - while (1) + while (1) { cpu_relax(); + wfe(); + } } static DEFINE_PER_CPU(struct completion *, cpu_completion); diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 058a37e6d11c..fd6e0671f957 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -523,8 +523,10 @@ void omap_prm_reset_system(void) prm_ll_data->reset_system(); - while (1) + while (1) { cpu_relax(); + wfe(); + } } /** From 7d1179f0dbcd88dea67bdf3d69c93ab39750bbbb Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Fri, 1 Feb 2019 13:54:39 -0800 Subject: [PATCH 0958/4277] docs: kernel-doc: update commands to generate man page (1) The command to generate man pages is truncated in the pdf version of the document. Reformat the command into multiple lines to prevent the truncation. (2) Older versions of git do not support all variants of pathspec syntax. Provide commands to generate man pages for various alternate syntax. Signed-off-by: Frank Rowand Signed-off-by: Jonathan Corbet --- Documentation/doc-guide/kernel-doc.rst | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst index 51be62aa4385..450ac634e92e 100644 --- a/Documentation/doc-guide/kernel-doc.rst +++ b/Documentation/doc-guide/kernel-doc.rst @@ -517,4 +517,17 @@ How to use kernel-doc to generate man pages If you just want to use kernel-doc to generate man pages you can do this from the kernel git tree:: - $ scripts/kernel-doc -man $(git grep -l '/\*\*' -- :^Documentation :^tools) | scripts/split-man.pl /tmp/man + $ scripts/kernel-doc -man \ + $(git grep -l '/\*\*' -- :^Documentation :^tools) \ + | scripts/split-man.pl /tmp/man + +Some older versions of git do not support some of the variants of syntax for +path exclusion. One of the following commands may work for those versions:: + + $ scripts/kernel-doc -man \ + $(git grep -l '/\*\*' -- . ':!Documentation' ':!tools') \ + | scripts/split-man.pl /tmp/man + + $ scripts/kernel-doc -man \ + $(git grep -l '/\*\*' -- . ":(exclude)Documentation" ":(exclude)tools") \ + | scripts/split-man.pl /tmp/man From b5b2187db0cbc991466121fbf25d9e15796ea145 Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Fri, 1 Feb 2019 14:04:16 -0800 Subject: [PATCH 0959/4277] docs: kernel-doc: typo "documentaion" Fix a typo in kernel-doc.rst Signed-off-by: Frank Rowand Signed-off-by: Jonathan Corbet --- Documentation/doc-guide/kernel-doc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst index 450ac634e92e..6513c16b7e4f 100644 --- a/Documentation/doc-guide/kernel-doc.rst +++ b/Documentation/doc-guide/kernel-doc.rst @@ -490,7 +490,7 @@ doc: *title* functions: *[ function ...]* Include documentation for each *function* in *source*. - If no *function* if specified, the documentaion for all functions + If no *function* if specified, the documentation for all functions and types in the *source* will be included. Examples:: From 358b6ba9befa6d62bab30e2d2c6dc1d49caf532f Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 31 Jan 2019 15:06:21 +1100 Subject: [PATCH 0960/4277] docs: Fix SLUB docs typo There is a minor typo in SLUB documentation. Fix typo. Signed-off-by: Tobin C. Harding Acked-by: Mike Rapoport Signed-off-by: Jonathan Corbet --- Documentation/vm/slub.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/vm/slub.rst b/Documentation/vm/slub.rst index 195928808bac..0871c584939b 100644 --- a/Documentation/vm/slub.rst +++ b/Documentation/vm/slub.rst @@ -141,7 +141,7 @@ can be influenced by kernel parameters: (list_lock) where contention may occur. ``slub_min_order`` - specifies a minim order of slabs. A similar effect like + specifies a minimum order of slabs. A similar effect like ``slub_min_objects``. ``slub_max_order`` From 11ede50059d0603e6ae2df5042cf813145ea5736 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 31 Jan 2019 15:06:22 +1100 Subject: [PATCH 0961/4277] docs: Add missing colon sphinx emits warning WARNING: Inline emphasis start-string without end-string. This is caused by a missing colon. Add missing colon, clearing shpinx build warning. Signed-off-by: Tobin C. Harding Acked-by: Mike Rapoport Signed-off-by: Jonathan Corbet --- Documentation/vm/slub.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/vm/slub.rst b/Documentation/vm/slub.rst index 0871c584939b..933ada4368ff 100644 --- a/Documentation/vm/slub.rst +++ b/Documentation/vm/slub.rst @@ -66,7 +66,7 @@ Trying to find an issue in the dentry cache? Try:: to only enable debugging on the dentry cache. You may use an asterisk at the end of the slab name, in order to cover all slabs with the same prefix. For example, here's how you can poison the dentry cache as well as all kmalloc -slabs: +slabs:: slub_debug=P,kmalloc-*,dentry From cd7198fc959e9ce6a90148b0ab7338b1a8b7c17e Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 31 Jan 2019 15:06:23 +1100 Subject: [PATCH 0962/4277] docs: Use underscore not hyphen in label sphinx emits warning WARNING: undefined label: memory-allocation ... This seems to be caused by the use of a hyphen in the label name instead of an underscore. Using an underscore for the label name and the reference clears the warning. Use underscore not hyphen in label and reference. Signed-off-by: Tobin C. Harding Signed-off-by: Jonathan Corbet --- Documentation/core-api/memory-allocation.rst | 2 +- Documentation/vm/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/core-api/memory-allocation.rst b/Documentation/core-api/memory-allocation.rst index 51a200d59f51..7744aa3bf2e0 100644 --- a/Documentation/core-api/memory-allocation.rst +++ b/Documentation/core-api/memory-allocation.rst @@ -1,4 +1,4 @@ -.. _memory-allocation: +.. _memory_allocation: ======================= Memory Allocation Guide diff --git a/Documentation/vm/index.rst b/Documentation/vm/index.rst index 2b3ab3a1ccf3..b58cc3bfe777 100644 --- a/Documentation/vm/index.rst +++ b/Documentation/vm/index.rst @@ -4,7 +4,7 @@ Linux Memory Management Documentation This is a collection of documents about the Linux memory management (mm) subsystem. If you are looking for advice on simply allocating memory, -see the :ref:`memory-allocation`. +see the :ref:`memory_allocation`. User guides for MM features =========================== From 19c1d46dfc7793b53b89c796475399656b9a794a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Wed, 30 Jan 2019 18:25:15 +0100 Subject: [PATCH 0963/4277] doc: Change LXR references to elixir.bootlin.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently, Free Electrons was renamed to Bootlin[1]. Less recently, the Linux Cross Reference (LXR) at lxr.free-electrons.com was replaced by Elixir[2], and lxr.free-electrons.com redirected first to elixir.free-electrons.com and now to elixir.bootlin.com. [1]: https://bootlin.com/blog/free-electrons-becomes-bootlin/ [2]: https://github.com/free-electrons/elixir Signed-off-by: Jonathan Neuschäfer Reviewed-by: Martin Kepplinger Acked-by: Federico Vaga Signed-off-by: Jonathan Corbet --- Documentation/input/devices/xpad.rst | 2 +- Documentation/process/howto.rst | 2 +- Documentation/process/kernel-docs.rst | 2 +- Documentation/translations/it_IT/process/howto.rst | 2 +- Documentation/translations/ja_JP/howto.rst | 2 +- Documentation/translations/ko_KR/howto.rst | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/input/devices/xpad.rst b/Documentation/input/devices/xpad.rst index b8bd65962dd8..173c2acda9fd 100644 --- a/Documentation/input/devices/xpad.rst +++ b/Documentation/input/devices/xpad.rst @@ -218,7 +218,7 @@ References .. [1] http://euc.jp/periphs/xbox-controller.ja.html (ITO Takayuki) .. [2] http://xpad.xbox-scene.com/ .. [3] http://www.markosweb.com/www/xboxhackz.com/ -.. [4] http://lxr.free-electrons.com/ident?i=xpad_device +.. [4] https://elixir.bootlin.com/linux/latest/ident/xpad_device Historic Edits diff --git a/Documentation/process/howto.rst b/Documentation/process/howto.rst index 58b2f46c4f98..bdd2eda81a1c 100644 --- a/Documentation/process/howto.rst +++ b/Documentation/process/howto.rst @@ -225,7 +225,7 @@ Cross-Reference project, which is able to present source code in a self-referential, indexed webpage format. An excellent up-to-date repository of the kernel code may be found at: - http://lxr.free-electrons.com/ + https://elixir.bootlin.com/ The development process diff --git a/Documentation/process/kernel-docs.rst b/Documentation/process/kernel-docs.rst index 3fb28de556e4..ab12dddc773e 100644 --- a/Documentation/process/kernel-docs.rst +++ b/Documentation/process/kernel-docs.rst @@ -565,7 +565,7 @@ Miscellaneous * Name: **Cross-Referencing Linux** - :URL: http://lxr.free-electrons.com/ + :URL: https://elixir.bootlin.com/ :Keywords: Browsing source code. :Description: Another web-based Linux kernel source code browser. Lots of cross references to variables and functions. You can see diff --git a/Documentation/translations/it_IT/process/howto.rst b/Documentation/translations/it_IT/process/howto.rst index 909e6a55bc43..f9a44b1af89d 100644 --- a/Documentation/translations/it_IT/process/howto.rst +++ b/Documentation/translations/it_IT/process/howto.rst @@ -234,7 +234,7 @@ il progetto Linux Cross-Reference, che è in grado di presentare codice sorgente in un formato autoreferenziale ed indicizzato. Un eccellente ed aggiornata fonte di consultazione del codice del kernel la potete trovare qui: - http://lxr.free-electrons.com/ + https://elixir.bootlin.com/ Il processo di sviluppo diff --git a/Documentation/translations/ja_JP/howto.rst b/Documentation/translations/ja_JP/howto.rst index f3116381c26b..2ca9389d5915 100644 --- a/Documentation/translations/ja_JP/howto.rst +++ b/Documentation/translations/ja_JP/howto.rst @@ -245,7 +245,7 @@ Linux カーネルソースツリーの中に含まれる、きれいにし、 できます。この最新の素晴しいカーネルコードのリポジトリは以下で見つかり ます - - http://lxr.free-electrons.com/ + https://elixir.bootlin.com/ 開発プロセス ------------ diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index a8197e072599..9fc869087e9c 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -235,7 +235,7 @@ ReST 마크업을 사용하는 문서들은 Documentation/output 에 생성된 소스코드를 인덱스된 웹 페이지들의 형태로 보여준다. 최신의 멋진 커널 코드 저장소는 다음을 통하여 참조할 수 있다. - http://lxr.free-electrons.com/ + https://elixir.bootlin.com/ 개발 프로세스 From 548a7643866b66526b700fc921a84fed47e4e452 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 24 Jan 2019 17:20:28 +0900 Subject: [PATCH 0964/4277] Documentation/kr: Update Korean translation to delete reference to the kernel-mentors mailing list Translate this commit to Korean: bc0ef4a7e4c3 ("Documentation: Delete reference to the kernel-mentors mailing list") Signed-off-by: SeongJae Park Signed-off-by: Jonathan Corbet --- Documentation/translations/ko_KR/howto.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index 9fc869087e9c..a61f7b3f927f 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -220,13 +220,6 @@ ReST 마크업을 사용하는 문서들은 Documentation/output 에 생성된 가지고 있지 않다면 다음에 무엇을 해야할지에 관한 방향을 배울 수 있을 것이다. -여러분들이 이미 커널 트리에 반영하길 원하는 코드 묶음을 가지고 있지만 -올바른 포맷으로 포장하는데 도움이 필요하다면 그러한 문제를 돕기 위해 -만들어진 kernel-mentors 프로젝트가 있다. 그곳은 메일링 리스트이며 -다음에서 참조할 수 있다. - - https://selenic.com/mailman/listinfo/kernel-mentors - 리눅스 커널 코드에 실제 변경을 하기 전에 반드시 그 코드가 어떻게 동작하는지 이해하고 있어야 한다. 코드를 분석하기 위하여 특정한 툴의 도움을 빌려서라도 코드를 직접 읽는 것보다 좋은 것은 없다(대부분의 From a6bee90a35f4d82bcd59416a33baacf202504616 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 24 Jan 2019 17:20:29 +0900 Subject: [PATCH 0965/4277] Documentation/process/howto/kr: Update Korean translation to remove outdated info about bugzilla mailing lists Translate this commit to Korean: bcd3cf0855c5 ("Documentation/process/howto: Remove outdated info about bugzilla mailing lists") Signed-off-by: SeongJae Park Signed-off-by: Jonathan Corbet --- Documentation/translations/ko_KR/howto.rst | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index a61f7b3f927f..ddbea3ad3e97 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -370,15 +370,7 @@ https://bugzilla.kernel.org 는 리눅스 커널 개발자들이 커널의 버 다른 사람들의 버그들을 수정하기 위하여 시간을 낭비하지 않기 때문이다. 이미 보고된 버그 리포트들을 가지고 작업하기 위해서 https://bugzilla.kernel.org -를 참조하라. 여러분이 앞으로 생겨날 버그 리포트들의 조언자가 되길 원한다면 -bugme-new 메일링 리스트나(새로운 버그 리포트들만이 이곳에서 메일로 전해진다) -bugme-janitor 메일링 리스트(bugzilla에 모든 변화들이 여기서 메일로 전해진다) -에 등록하면 된다. - - https://lists.linux-foundation.org/mailman/listinfo/bugme-new - - https://lists.linux-foundation.org/mailman/listinfo/bugme-janitors - +를 참조하라. 메일링 리스트들 From 6fc48e6085ea72fc4ba3e20679478e9fb97727e5 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 24 Jan 2019 17:20:30 +0900 Subject: [PATCH 0966/4277] Documentation/process/howto.rst/kokr: Update Korean translation to add a missing cross-reference Translate this commit to Korean: dad051395413 ("Documentation/process/howto.rst: add a missing cross-reference") Signed-off-by: SeongJae Park Signed-off-by: Jonathan Corbet --- Documentation/translations/ko_KR/howto.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index ddbea3ad3e97..d21806244d32 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -99,7 +99,7 @@ mtk.manpages@gmail.com의 메인테이너에게 보낼 것을 권장한다. 다음은 커널 소스 트리에 있는 읽어야 할 파일들의 리스트이다. - README + :ref:`Documentation/admin-guide/README.rst ` 이 파일은 리눅스 커널에 관하여 간단한 배경 설명과 커널을 설정하고 빌드하기 위해 필요한 것을 설명한다. 커널에 입문하는 사람들은 여기서 시작해야 한다. From 265083a4ae5bbf963ecd739b9638bebd6e026c6a Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 24 Jan 2019 17:20:31 +0900 Subject: [PATCH 0967/4277] docs/kokr: Update Korean translation to tidy up TOCs and refs to license-rules.rst Transalte this commit to Korean: 9799445af124 ("docs: tidy up TOCs and refs to license-rules.rst") Signed-off-by: SeongJae Park Signed-off-by: Jonathan Corbet --- Documentation/translations/ko_KR/howto.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index d21806244d32..16438515b8e1 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -77,10 +77,12 @@ Documentation/process/howto.rst 리눅스 커널 소스 코드는 GPL로 배포(release)되었다. 소스트리의 메인 디렉토리에 있는 라이센스에 관하여 상세하게 쓰여 있는 COPYING이라는 -파일을 봐라. 여러분이 라이센스에 관한 더 깊은 문제를 가지고 있다면 -리눅스 커널 메일링 리스트에 묻지말고 변호사와 연락하라. 메일링 -리스트들에 있는 사람들은 변호사가 아니기 때문에 법적 문제에 관하여 -그들의 말에 의지해서는 안된다. +파일을 봐라. 리눅스 커널 라이센싱 규칙과 소스 코드 안의 `SPDX +`_ 식별자 사용법은 +:ref:`Documentation/process/license-rules.rst ` 에 설명되어 +있다. 여러분이 라이센스에 관한 더 깊은 문제를 가지고 있다면 리눅스 커널 메일링 +리스트에 묻지말고 변호사와 연락하라. 메일링 리스트들에 있는 사람들은 변호사가 +아니기 때문에 법적 문제에 관하여 그들의 말에 의지해서는 안된다. GPL에 관한 잦은 질문들과 답변들은 다음을 참조하라. From faa6bcbb4c9c2676bd2ad2199c23bf3358356b76 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 24 Jan 2019 17:20:32 +0900 Subject: [PATCH 0968/4277] doc:process:kokr: Update Korean translation to add links where missing Translate this commit to Korean: f77af637f29d ("doc:process: add links where missing") Signed-off-by: SeongJae Park Signed-off-by: Jonathan Corbet --- Documentation/translations/ko_KR/howto.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index 16438515b8e1..528433932589 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -298,9 +298,9 @@ Andrew Morton의 글이 있다. 4.x.y는 "stable" 팀에 의해 관리되며 거의 매번 격주로 배포된다. -커널 트리 문서들 내에 Documentation/process/stable-kernel-rules.rst 파일은 어떤 -종류의 변경들이 -stable 트리로 들어왔는지와 배포 프로세스가 어떻게 -진행되는지를 설명한다. +커널 트리 문서들 내의 :ref:`Documentation/process/stable-kernel-rules.rst ` +파일은 어떤 종류의 변경들이 -stable 트리로 들어왔는지와 +배포 프로세스가 어떻게 진행되는지를 설명한다. 4.x -git 패치들 ~~~~~~~~~~~~~~~ @@ -355,9 +355,10 @@ https://bugzilla.kernel.org 는 리눅스 커널 개발자들이 커널의 버 https://bugzilla.kernel.org/page.cgi?id=faq.html -메인 커널 소스 디렉토리에 있는 admin-guide/reporting-bugs.rst 파일은 커널 버그라고 생각되는 -것을 보고하는 방법에 관한 좋은 템플릿이며 문제를 추적하기 위해서 커널 -개발자들이 필요로 하는 정보가 무엇들인지를 상세히 설명하고 있다. +메인 커널 소스 디렉토리에 있는 :ref:`admin-guide/reporting-bugs.rst ` +파일은 커널 버그라고 생각되는 것을 보고하는 방법에 관한 좋은 템플릿이며 문제를 +추적하기 위해서 커널 개발자들이 필요로 하는 정보가 무엇들인지를 상세히 설명하고 +있다. 버그 리포트들의 관리 @@ -417,7 +418,8 @@ https://bugzilla.kernel.org 는 리눅스 커널 개발자들이 커널의 버 "John 커널해커는 작성했다...."를 유지하며 여러분들의 의견을 그 메일의 윗부분에 작성하지 말고 각 인용한 단락들 사이에 넣어라. -여러분들이 패치들을 메일에 넣는다면 그것들은 Documentation/process/submitting-patches.rst에 +여러분들이 패치들을 메일에 넣는다면 그것들은 +:ref:`Documentation/process/submitting-patches.rst ` 에 나와있는데로 명백히(plain) 읽을 수 있는 텍스트여야 한다. 커널 개발자들은 첨부파일이나 압축된 패치들을 원하지 않는다. 그들은 여러분들의 패치의 각 라인 단위로 코멘트를 하길 원하며 압축하거나 첨부하지 않고 보내는 것이 From b8580e9de48bf32b884910d22330ef2fa027cf01 Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Fri, 1 Feb 2019 17:11:14 -0600 Subject: [PATCH 0969/4277] PCI: Add HXT vendor ID Add the HXT vendor ID to pci_ids.h. Signed-off-by: Shunyong Yang [bhelgaas: split to separate patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya CC: Joey Zheng --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5eaf39dbc388..26420e619dd7 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2573,6 +2573,8 @@ #define PCI_VENDOR_ID_HYGON 0x1d94 +#define PCI_VENDOR_ID_HXT 0x1dbf + #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 From 171e4909eac7912c8d9ffaec9a2872b3271dd6e9 Mon Sep 17 00:00:00 2001 From: Giridhar Malavali Date: Wed, 30 Jan 2019 09:50:44 -0800 Subject: [PATCH 0970/4277] scsi: qla2xxx: Add new FC-NVMe enable BIT to enable FC-NVMe feature This patch adds new BIT detection to enable FC-NVMe feature in the driver. [mkp: fixed Giridhar's SoB] Signed-off-by: Giridhar Malavali Signed-off-by: Himanshu Madhani Reviewed-by: Ewan D. Milne Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 3 +++ drivers/scsi/qla2xxx/qla_mbx.c | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 97f66b142ff2..8858bb1ea1b2 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -3966,6 +3966,9 @@ struct qla_hw_data { uint16_t fw_subminor_version; uint16_t fw_attributes; uint16_t fw_attributes_h; +#define FW_ATTR_H_NVME BIT_10 +#define FW_ATTR_H_NVME_UPDATED BIT_14 + uint16_t fw_attributes_ext[2]; uint32_t fw_memory_size; uint32_t fw_transfer_size; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 191b6b7c8747..f753aa495753 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1109,7 +1109,9 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) * FW supports nvme and driver load parameter requested nvme. * BIT 26 of fw_attributes indicates NVMe support. */ - if ((ha->fw_attributes_h & 0x400) && ql2xnvmeenable) { + if ((ha->fw_attributes_h & + (FW_ATTR_H_NVME | FW_ATTR_H_NVME_UPDATED)) && + ql2xnvmeenable) { vha->flags.nvme_enabled = 1; ql_log(ql_log_info, vha, 0xd302, "%s: FC-NVMe is Enabled (0x%x)\n", From a41e8f25fa8f8f67360d88eb0eebbabe95a64bdf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 19:46:32 +0100 Subject: [PATCH 0971/4277] stable-kernel-rules.rst: add link to networking patch queue The networking maintainer keeps a public list of the patches being queued up for the next round of stable releases. Be sure to check there before asking for a patch to be applied so that you do not waste people's time. Signed-off-by: Greg Kroah-Hartman Acked-by: David S. Miller Signed-off-by: Jonathan Corbet --- Documentation/process/stable-kernel-rules.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/process/stable-kernel-rules.rst b/Documentation/process/stable-kernel-rules.rst index 070e3f04d183..06f743b612c4 100644 --- a/Documentation/process/stable-kernel-rules.rst +++ b/Documentation/process/stable-kernel-rules.rst @@ -38,6 +38,9 @@ Procedure for submitting patches to the -stable tree - If the patch covers files in net/ or drivers/net please follow netdev stable submission guidelines as described in :ref:`Documentation/networking/netdev-FAQ.rst ` + after first checking the stable networking queue at + https://patchwork.ozlabs.org/bundle/davem/stable/?series=&submitter=&state=*&q=&archive= + to ensure the requested patch is not already queued up. - Security patches should not be handled (solely) by the -stable review process but should follow the procedures in :ref:`Documentation/admin-guide/security-bugs.rst `. From 01926f6b321b069ce0a85a68554312b9e58ee87d Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Fri, 1 Feb 2019 17:13:10 -0600 Subject: [PATCH 0972/4277] PCI: Add ACS quirk for HXT SD4800 The design of HXT SD4800 ACS feature is the same as QCOM QDF2xxx. Add an ACS quirk for the SD4800. Signed-off-by: Shunyong Yang [bhelgaas: split to separate patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Sinan Kaya CC: Joey Zheng --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9fabcef345ed..6bafd0fe0f0b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4519,6 +4519,8 @@ static const struct pci_dev_acs_enabled { /* QCOM QDF2xxx root ports */ { PCI_VENDOR_ID_QCOM, 0x0400, pci_quirk_qcom_rp_acs }, { PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs }, + /* HXT SD4800 root ports. The ACS design is same as QCOM QDF2xxx */ + { PCI_VENDOR_ID_HXT, 0x0401, pci_quirk_qcom_rp_acs }, /* Intel PCH root ports */ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, From 22e4d639cd05b98b27c78be497d6d42e4cfebf44 Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Wed, 7 Nov 2018 15:25:05 +0800 Subject: [PATCH 0973/4277] PCI: pciehp: Add HXT quirk for Command Completed errata The HXT SD4800 PCI controller does not set the Command Completed bit unless writes to the Slot Command register change "Control" bits. Add SD4800 to the quirk. Signed-off-by: Shunyong Yang Signed-off-by: Bjorn Helgaas CC: Joey Zheng --- drivers/pci/hotplug/pciehp_hpc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7dd443aea5a5..91db67963aea 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -920,3 +920,5 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0400, PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0401, PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_HXT, 0x0401, + PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); From 8f7e6d134bda563c9b3abac14de11e89eb4c86d7 Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Tue, 22 Jan 2019 10:34:08 +0100 Subject: [PATCH 0974/4277] doc: process: GPL -> GPL-compatible Drivers under MIT, BSD-17-clause, or uncle-Bob's-newest-take-on-PD are all fine, not just GPL. Signed-off-by: Adam Borowski [jc: fixed conflict and refilled paragraph] Reviewed-by: Greg Kroah-Hartman Signed-off-by: Jonathan Corbet --- Documentation/process/stable-api-nonsense.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/process/stable-api-nonsense.rst b/Documentation/process/stable-api-nonsense.rst index 57d95a49c096..a9625ab1fdc2 100644 --- a/Documentation/process/stable-api-nonsense.rst +++ b/Documentation/process/stable-api-nonsense.rst @@ -169,13 +169,13 @@ driver for every different kernel version for every distribution is a nightmare, and trying to keep up with an ever changing kernel interface is also a rough job. -Simple, get your kernel driver into the main kernel tree (remember we -are talking about GPL released drivers here, if your code doesn't fall -under this category, good luck, you are on your own here, you leech). If your -driver is in the tree, and a kernel interface changes, it will be fixed -up by the person who did the kernel change in the first place. This -ensures that your driver is always buildable, and works over time, with -very little effort on your part. +Simple, get your kernel driver into the main kernel tree (remember we are +talking about drivers released under a GPL-compatible license here, if your +code doesn't fall under this category, good luck, you are on your own here, +you leech). If your driver is in the tree, and a kernel interface changes, +it will be fixed up by the person who did the kernel change in the first +place. This ensures that your driver is always buildable, and works over +time, with very little effort on your part. The very good side effects of having your driver in the main kernel tree are: From e18e9dac9cdc9f4d048115cd4b9b0ff40c190555 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 25 Jan 2019 01:57:19 +0000 Subject: [PATCH 0975/4277] scsi: csiostor: Remove set but not used variable 'pln' Fixes gcc '-Wunused-but-set-variable' warning: drivers/scsi/csiostor/csio_attr.c: In function 'csio_fcoe_free_vnp': drivers/scsi/csiostor/csio_attr.c:500:21: warning: variable 'pln' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Acked-by: Varun Prakash Signed-off-by: Martin K. Petersen --- drivers/scsi/csiostor/csio_attr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/csiostor/csio_attr.c b/drivers/scsi/csiostor/csio_attr.c index 8a004036e3d7..017423b09669 100644 --- a/drivers/scsi/csiostor/csio_attr.c +++ b/drivers/scsi/csiostor/csio_attr.c @@ -497,7 +497,6 @@ out: static int csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln) { - struct csio_lnode *pln; struct csio_mb *mbp; struct fw_fcoe_vnp_cmd *rsp; int ret = 0; @@ -514,8 +513,6 @@ csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln) goto out; } - pln = ln->pln; - csio_fcoe_vnp_free_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO, ln->fcf_flowid, ln->vnp_flowid, NULL); From 0ce26a1c31ca928df4dfc7504c8898b71ff9f5d5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 1 Feb 2019 17:24:52 -0600 Subject: [PATCH 0976/4277] PCI: Move Rohm Vendor ID to generic list Move the Rohm Vendor ID to pci_ids.h instead of defining it in several drivers. Signed-off-by: Andy Shevchenko Signed-off-by: Bjorn Helgaas Acked-by: Mark Brown Acked-by: Linus Walleij --- drivers/dma/pch_dma.c | 1 - drivers/gpio/gpio-ml-ioh.c | 2 -- drivers/gpio/gpio-pch.c | 1 - drivers/i2c/busses/i2c-eg20t.c | 1 - drivers/misc/pch_phub.c | 1 - drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 7 ++----- drivers/spi/spi-topcliff-pch.c | 1 - drivers/tty/serial/pch_uart.c | 2 -- drivers/usb/gadget/udc/pch_udc.c | 1 - include/linux/pci_ids.h | 2 ++ 10 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index afd8f27bda96..538b6e0e17bb 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -972,7 +972,6 @@ static void pch_dma_remove(struct pci_dev *pdev) } /* PCI Device ID of DMA device */ -#define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_EG20T_PCH_DMA_8CH 0x8810 #define PCI_DEVICE_ID_EG20T_PCH_DMA_4CH 0x8815 #define PCI_DEVICE_ID_ML7213_DMA1_8CH 0x8026 diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 51c7d1b84c2e..0c076dce9e17 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -31,8 +31,6 @@ #define IOH_IRQ_BASE 0 -#define PCI_VENDOR_ID_ROHM 0x10DB - struct ioh_reg_comn { u32 ien; u32 istatus; diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index ee79e5f88b5a..1d99293096f2 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -437,7 +437,6 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); -#define PCI_VENDOR_ID_ROHM 0x10DB static const struct pci_device_id pch_gpio_pcidev_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) }, { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) }, diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 835d54ac2971..231675b10376 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -177,7 +177,6 @@ static wait_queue_head_t pch_event; static DEFINE_MUTEX(pch_mutex); /* Definition for ML7213 by LAPIS Semiconductor */ -#define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_I2C 0x802D #define PCI_DEVICE_ID_ML7223_I2C 0x8010 #define PCI_DEVICE_ID_ML7831_I2C 0x8817 diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 540845651b8c..309703e9c42e 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -64,7 +64,6 @@ #define CLKCFG_UARTCLKSEL (1 << 18) /* Macros for ML7213 */ -#define PCI_VENDOR_ID_ROHM 0x10db #define PCI_DEVICE_ID_ROHM_ML7213_PHUB 0x801A /* Macros for ML7223 */ diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 43c0c10dfeb7..3a4225837049 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -27,7 +27,6 @@ #define DRV_VERSION "1.01" const char pch_driver_version[] = DRV_VERSION; -#define PCI_DEVICE_ID_INTEL_IOH1_GBE 0x8802 /* Pci device ID */ #define PCH_GBE_MAR_ENTRIES 16 #define PCH_GBE_SHORT_PKT 64 #define DSC_INIT16 0xC000 @@ -37,11 +36,9 @@ const char pch_driver_version[] = DRV_VERSION; #define PCH_GBE_PCI_BAR 1 #define PCH_GBE_RESERVE_MEMORY 0x200000 /* 2MB */ -/* Macros for ML7223 */ -#define PCI_VENDOR_ID_ROHM 0x10db -#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013 +#define PCI_DEVICE_ID_INTEL_IOH1_GBE 0x8802 -/* Macros for ML7831 */ +#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013 #define PCI_DEVICE_ID_ROHM_ML7831_GBE 0x8802 #define PCH_GBE_TX_WEIGHT 64 diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 97d137591b18..d794180e83dc 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -92,7 +92,6 @@ #define PCH_MAX_SPBR 1023 /* Definition for ML7213/ML7223/ML7831 by LAPIS Semiconductor */ -#define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_SPI 0x802c #define PCI_DEVICE_ID_ML7223_SPI 0x800F #define PCI_DEVICE_ID_ML7831_SPI 0x8816 diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 9ed121f08a54..6157213a8359 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -192,8 +192,6 @@ enum { #define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP) #define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE) -#define PCI_VENDOR_ID_ROHM 0x10DB - #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) #define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */ diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 55c8c8abeacd..cded51f36fc1 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -368,7 +368,6 @@ struct pch_udc_dev { #define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939 #define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808 -#define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D #define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 26420e619dd7..70e86148cb1e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1140,6 +1140,8 @@ #define PCI_VENDOR_ID_TCONRAD 0x10da #define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508 +#define PCI_VENDOR_ID_ROHM 0x10db + #define PCI_VENDOR_ID_NVIDIA 0x10de #define PCI_DEVICE_ID_NVIDIA_TNT 0x0020 #define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028 From b4f6dcb9d35688392d668c46e834f72c55900b49 Mon Sep 17 00:00:00 2001 From: Bharat Kumar Gogada Date: Wed, 14 Nov 2018 20:17:01 +0530 Subject: [PATCH 0977/4277] PCI: Enable SERR# forwarding for all bridges As per Figure 6-3 in PCIe r4.0, sec 6.2.6, ERR_ messages will be forwarded from the secondary interface to the primary interface, if the SERR# Enable bit in the Bridge Control register is set. It seems clear that an ACPI hotplug parameter method (_HPP or _HPX) that tells us to "enable SERR in the command register" (ACPI v6.2, sec 6.2.8, 6.2.9.1) refers to PCI_COMMAND_SERR, which enables reporting of errors by the function itself. For bridges, we also interpreted that to mean we should enable PCI_BRIDGE_CTL_SERR, which enables *forwarding* of errors by the bridge. But we didn't enable PCI_BRIDGE_CTL_SERR anywhere else, which means we never enabled it for non-ACPI systems or ACPI systems that didn't supply hotplug parameters. That means errors reported below bridges were often never forwarded up to a Root Port where they could be signaled via AER. Enable PCI_BRIDGE_CTL_SERR for all bridges so we can get better error reporting for downstream devices. Signed-off-by: Bharat Kumar Gogada [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..43e6583417ce 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1856,8 +1856,6 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp) pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, hpp->latency_timer); pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); - if (hpp->enable_serr) - pci_bctl |= PCI_BRIDGE_CTL_SERR; if (hpp->enable_perr) pci_bctl |= PCI_BRIDGE_CTL_PARITY; pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); @@ -2129,6 +2127,24 @@ static void pci_configure_eetlp_prefix(struct pci_dev *dev) #endif } +static void pci_configure_serr(struct pci_dev *dev) +{ + u16 control; + + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + + /* + * A bridge will not forward ERR_ messages coming from an + * endpoint unless SERR# forwarding is enabled. + */ + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &control); + if (!(control & PCI_BRIDGE_CTL_SERR)) { + control |= PCI_BRIDGE_CTL_SERR; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, control); + } + } +} + static void pci_configure_device(struct pci_dev *dev) { struct hotplug_params hpp; @@ -2139,6 +2155,7 @@ static void pci_configure_device(struct pci_dev *dev) pci_configure_relaxed_ordering(dev); pci_configure_ltr(dev); pci_configure_eetlp_prefix(dev); + pci_configure_serr(dev); memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); From 88a242c9874044740ab990d4fccba8bb90cb924b Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Wed, 30 Jan 2019 11:15:17 -0800 Subject: [PATCH 0978/4277] EDAC, skx_common: Separate common code out from skx_edac Parts of skx_edac can be shared with the Intel 10nm server EDAC driver. Carve out the common parts from skx_edac in preparation to support both skx_edac driver and i10nm_edac drivers. Co-developed-by: Tony Luck Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov Cc: James Morse Cc: Mauro Carvalho Chehab Cc: linux-edac Link: https://lkml.kernel.org/r/20190130191519.15393-3-tony.luck@intel.com --- drivers/edac/skx_common.c | 689 ++++++++++++++++++++++++++++++++++++++ drivers/edac/skx_common.h | 152 +++++++++ 2 files changed, 841 insertions(+) create mode 100644 drivers/edac/skx_common.c create mode 100644 drivers/edac/skx_common.h diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c new file mode 100644 index 000000000000..513523ad5649 --- /dev/null +++ b/drivers/edac/skx_common.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common codes for both the skx_edac driver and Intel 10nm server EDAC driver. + * Originally split out from the skx_edac driver. + * + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include "edac_module.h" +#include "skx_common.h" + +static const char * const component_names[] = { + [INDEX_SOCKET] = "ProcessorSocketId", + [INDEX_MEMCTRL] = "MemoryControllerId", + [INDEX_CHANNEL] = "ChannelId", + [INDEX_DIMM] = "DimmSlotId", +}; + +static int component_indices[ARRAY_SIZE(component_names)]; +static int adxl_component_count; +static const char * const *adxl_component_names; +static u64 *adxl_values; +static char *adxl_msg; + +static char skx_msg[MSG_SIZE]; +static skx_decode_f skx_decode; +static u64 skx_tolm, skx_tohm; +static LIST_HEAD(dev_edac_list); + +int __init skx_adxl_get(void) +{ + const char * const *names; + int i, j; + + names = adxl_get_component_names(); + if (!names) { + skx_printk(KERN_NOTICE, "No firmware support for address translation.\n"); + return -ENODEV; + } + + for (i = 0; i < INDEX_MAX; i++) { + for (j = 0; names[j]; j++) { + if (!strcmp(component_names[i], names[j])) { + component_indices[i] = j; + break; + } + } + + if (!names[j]) + goto err; + } + + adxl_component_names = names; + while (*names++) + adxl_component_count++; + + adxl_values = kcalloc(adxl_component_count, sizeof(*adxl_values), + GFP_KERNEL); + if (!adxl_values) { + adxl_component_count = 0; + return -ENOMEM; + } + + adxl_msg = kzalloc(MSG_SIZE, GFP_KERNEL); + if (!adxl_msg) { + adxl_component_count = 0; + kfree(adxl_values); + return -ENOMEM; + } + + return 0; +err: + skx_printk(KERN_ERR, "'%s' is not matched from DSM parameters: ", + component_names[i]); + for (j = 0; names[j]; j++) + skx_printk(KERN_CONT, "%s ", names[j]); + skx_printk(KERN_CONT, "\n"); + + return -ENODEV; +} + +void __exit skx_adxl_put(void) +{ + kfree(adxl_values); + kfree(adxl_msg); +} + +static bool skx_adxl_decode(struct decoded_addr *res) +{ + int i, len = 0; + + if (res->addr >= skx_tohm || (res->addr >= skx_tolm && + res->addr < BIT_ULL(32))) { + edac_dbg(0, "Address 0x%llx out of range\n", res->addr); + return false; + } + + if (adxl_decode(res->addr, adxl_values)) { + edac_dbg(0, "Failed to decode 0x%llx\n", res->addr); + return false; + } + + res->socket = (int)adxl_values[component_indices[INDEX_SOCKET]]; + res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]]; + res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]]; + res->dimm = (int)adxl_values[component_indices[INDEX_DIMM]]; + + for (i = 0; i < adxl_component_count; i++) { + if (adxl_values[i] == ~0x0ull) + continue; + + len += snprintf(adxl_msg + len, MSG_SIZE - len, " %s:0x%llx", + adxl_component_names[i], adxl_values[i]); + if (MSG_SIZE - len <= 0) + break; + } + + return true; +} + +void skx_set_decode(skx_decode_f decode) +{ + skx_decode = decode; +} + +int skx_get_src_id(struct skx_dev *d, u8 *id) +{ + u32 reg; + + if (pci_read_config_dword(d->util_all, 0xf0, ®)) { + skx_printk(KERN_ERR, "Failed to read src id\n"); + return -ENODEV; + } + + *id = GET_BITFIELD(reg, 12, 14); + return 0; +} + +int skx_get_node_id(struct skx_dev *d, u8 *id) +{ + u32 reg; + + if (pci_read_config_dword(d->util_all, 0xf4, ®)) { + skx_printk(KERN_ERR, "Failed to read node id\n"); + return -ENODEV; + } + + *id = GET_BITFIELD(reg, 0, 2); + return 0; +} + +static int get_width(u32 mtr) +{ + switch (GET_BITFIELD(mtr, 8, 9)) { + case 0: + return DEV_X4; + case 1: + return DEV_X8; + case 2: + return DEV_X16; + } + return DEV_UNKNOWN; +} + +/* + * We use the per-socket device @did to count how many sockets are present, + * and to detemine which PCI buses are associated with each socket. Allocate + * and build the full list of all the skx_dev structures that we need here. + */ +int skx_get_all_bus_mappings(unsigned int did, int off, enum type type, + struct list_head **list) +{ + struct pci_dev *pdev, *prev; + struct skx_dev *d; + u32 reg; + int ndev = 0; + + prev = NULL; + for (;;) { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, prev); + if (!pdev) + break; + ndev++; + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + pci_dev_put(pdev); + return -ENOMEM; + } + + if (pci_read_config_dword(pdev, off, ®)) { + kfree(d); + pci_dev_put(pdev); + skx_printk(KERN_ERR, "Failed to read bus idx\n"); + return -ENODEV; + } + + d->bus[0] = GET_BITFIELD(reg, 0, 7); + d->bus[1] = GET_BITFIELD(reg, 8, 15); + if (type == SKX) { + d->seg = pci_domain_nr(pdev->bus); + d->bus[2] = GET_BITFIELD(reg, 16, 23); + d->bus[3] = GET_BITFIELD(reg, 24, 31); + } else { + d->seg = GET_BITFIELD(reg, 16, 23); + } + + edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n", + d->bus[0], d->bus[1], d->bus[2], d->bus[3]); + list_add_tail(&d->list, &dev_edac_list); + prev = pdev; + } + + if (list) + *list = &dev_edac_list; + return ndev; +} + +int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm) +{ + struct pci_dev *pdev; + u32 reg; + + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, NULL); + if (!pdev) { + skx_printk(KERN_ERR, "Can't get tolm/tohm\n"); + return -ENODEV; + } + + if (pci_read_config_dword(pdev, off[0], ®)) { + skx_printk(KERN_ERR, "Failed to read tolm\n"); + goto fail; + } + skx_tolm = reg; + + if (pci_read_config_dword(pdev, off[1], ®)) { + skx_printk(KERN_ERR, "Failed to read lower tohm\n"); + goto fail; + } + skx_tohm = reg; + + if (pci_read_config_dword(pdev, off[2], ®)) { + skx_printk(KERN_ERR, "Failed to read upper tohm\n"); + goto fail; + } + skx_tohm |= (u64)reg << 32; + + pci_dev_put(pdev); + *tolm = skx_tolm; + *tohm = skx_tohm; + edac_dbg(2, "tolm = 0x%llx tohm = 0x%llx\n", skx_tolm, skx_tohm); + return 0; +fail: + pci_dev_put(pdev); + return -ENODEV; +} + +static int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add, + int minval, int maxval, const char *name) +{ + u32 val = GET_BITFIELD(reg, lobit, hibit); + + if (val < minval || val > maxval) { + edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg); + return -EINVAL; + } + return val + add; +} + +#define numrank(reg) skx_get_dimm_attr(reg, 12, 13, 0, 0, 2, "ranks") +#define numrow(reg) skx_get_dimm_attr(reg, 2, 4, 12, 1, 6, "rows") +#define numcol(reg) skx_get_dimm_attr(reg, 0, 1, 10, 0, 2, "cols") + +int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm, + struct skx_imc *imc, int chan, int dimmno) +{ + int banks = 16, ranks, rows, cols, npages; + u64 size; + + ranks = numrank(mtr); + rows = numrow(mtr); + cols = numcol(mtr); + + /* + * Compute size in 8-byte (2^3) words, then shift to MiB (2^20) + */ + size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3); + npages = MiB_TO_PAGES(size); + + edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: 0x%x, col: 0x%x\n", + imc->mc, chan, dimmno, size, npages, + banks, 1 << ranks, rows, cols); + + imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0); + imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9); + imc->chan[chan].dimms[dimmno].fine_grain_bank = GET_BITFIELD(amap, 0, 0); + imc->chan[chan].dimms[dimmno].rowbits = rows; + imc->chan[chan].dimms[dimmno].colbits = cols; + + dimm->nr_pages = npages; + dimm->grain = 32; + dimm->dtype = get_width(mtr); + dimm->mtype = MEM_DDR4; + dimm->edac_mode = EDAC_SECDED; /* likely better than this */ + snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", + imc->src_id, imc->lmc, chan, dimmno); + + return 1; +} + +int skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc, + int chan, int dimmno, const char *mod_str) +{ + int smbios_handle; + u32 dev_handle; + u16 flags; + u64 size = 0; + + dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc, + imc->src_id, 0); + + smbios_handle = nfit_get_smbios_id(dev_handle, &flags); + if (smbios_handle == -EOPNOTSUPP) { + pr_warn_once("%s: Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n", mod_str); + goto unknown_size; + } + + if (smbios_handle < 0) { + skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=0x%x\n", dev_handle); + goto unknown_size; + } + + if (flags & ACPI_NFIT_MEM_MAP_FAILED) { + skx_printk(KERN_ERR, "NVDIMM ADR=0x%x is not mapped\n", dev_handle); + goto unknown_size; + } + + size = dmi_memdev_size(smbios_handle); + if (size == ~0ull) + skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=0x%x/SMBIOS=0x%x\n", + dev_handle, smbios_handle); + +unknown_size: + dimm->nr_pages = size >> PAGE_SHIFT; + dimm->grain = 32; + dimm->dtype = DEV_UNKNOWN; + dimm->mtype = MEM_NVDIMM; + dimm->edac_mode = EDAC_SECDED; /* likely better than this */ + + edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu MiB (%u pages)\n", + imc->mc, chan, dimmno, size >> 20, dimm->nr_pages); + + snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", + imc->src_id, imc->lmc, chan, dimmno); + + return (size == 0 || size == ~0ull) ? 0 : 1; +} + +int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, + const char *ctl_name, const char *mod_str, + get_dimm_config_f get_dimm_config) +{ + struct mem_ctl_info *mci; + struct edac_mc_layer layers[2]; + struct skx_pvt *pvt; + int rc; + + /* Allocate a new MC control structure */ + layers[0].type = EDAC_MC_LAYER_CHANNEL; + layers[0].size = NUM_CHANNELS; + layers[0].is_virt_csrow = false; + layers[1].type = EDAC_MC_LAYER_SLOT; + layers[1].size = NUM_DIMMS; + layers[1].is_virt_csrow = true; + mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers, + sizeof(struct skx_pvt)); + + if (unlikely(!mci)) + return -ENOMEM; + + edac_dbg(0, "MC#%d: mci = %p\n", imc->mc, mci); + + /* Associate skx_dev and mci for future usage */ + imc->mci = mci; + pvt = mci->pvt_info; + pvt->imc = imc; + + mci->ctl_name = kasprintf(GFP_KERNEL, "%s#%d IMC#%d", ctl_name, + imc->node_id, imc->lmc); + if (!mci->ctl_name) { + rc = -ENOMEM; + goto fail0; + } + + mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM; + mci->edac_ctl_cap = EDAC_FLAG_NONE; + mci->edac_cap = EDAC_FLAG_NONE; + mci->mod_name = mod_str; + mci->dev_name = pci_name(pdev); + mci->ctl_page_to_phys = NULL; + + rc = get_dimm_config(mci); + if (rc < 0) + goto fail; + + /* Record ptr to the generic device */ + mci->pdev = &pdev->dev; + + /* Add this new MC control structure to EDAC's list of MCs */ + if (unlikely(edac_mc_add_mc(mci))) { + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); + rc = -EINVAL; + goto fail; + } + + return 0; + +fail: + kfree(mci->ctl_name); +fail0: + edac_mc_free(mci); + imc->mci = NULL; + return rc; +} + +static void skx_unregister_mci(struct skx_imc *imc) +{ + struct mem_ctl_info *mci = imc->mci; + + if (!mci) + return; + + edac_dbg(0, "MC%d: mci = %p\n", imc->mc, mci); + + /* Remove MC sysfs nodes */ + edac_mc_del_mc(mci->pdev); + + edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); + kfree(mci->ctl_name); + edac_mc_free(mci); +} + +static struct mem_ctl_info *get_mci(int src_id, int lmc) +{ + struct skx_dev *d; + + if (lmc > NUM_IMC - 1) { + skx_printk(KERN_ERR, "Bad lmc %d\n", lmc); + return NULL; + } + + list_for_each_entry(d, &dev_edac_list, list) { + if (d->imc[0].src_id == src_id) + return d->imc[lmc].mci; + } + + skx_printk(KERN_ERR, "No mci for src_id %d lmc %d\n", src_id, lmc); + return NULL; +} + +static void skx_mce_output_error(struct mem_ctl_info *mci, + const struct mce *m, + struct decoded_addr *res) +{ + enum hw_event_mc_err_type tp_event; + char *type, *optype; + bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0); + bool overflow = GET_BITFIELD(m->status, 62, 62); + bool uncorrected_error = GET_BITFIELD(m->status, 61, 61); + bool recoverable; + u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52); + u32 mscod = GET_BITFIELD(m->status, 16, 31); + u32 errcode = GET_BITFIELD(m->status, 0, 15); + u32 optypenum = GET_BITFIELD(m->status, 4, 6); + + recoverable = GET_BITFIELD(m->status, 56, 56); + + if (uncorrected_error) { + core_err_cnt = 1; + if (ripv) { + type = "FATAL"; + tp_event = HW_EVENT_ERR_FATAL; + } else { + type = "NON_FATAL"; + tp_event = HW_EVENT_ERR_UNCORRECTED; + } + } else { + type = "CORRECTED"; + tp_event = HW_EVENT_ERR_CORRECTED; + } + + /* + * According with Table 15-9 of the Intel Architecture spec vol 3A, + * memory errors should fit in this mask: + * 000f 0000 1mmm cccc (binary) + * where: + * f = Correction Report Filtering Bit. If 1, subsequent errors + * won't be shown + * mmm = error type + * cccc = channel + * If the mask doesn't match, report an error to the parsing logic + */ + if (!((errcode & 0xef80) == 0x80)) { + optype = "Can't parse: it is not a mem"; + } else { + switch (optypenum) { + case 0: + optype = "generic undef request error"; + break; + case 1: + optype = "memory read error"; + break; + case 2: + optype = "memory write error"; + break; + case 3: + optype = "addr/cmd error"; + break; + case 4: + optype = "memory scrubbing error"; + break; + default: + optype = "reserved"; + break; + } + } + if (adxl_component_count) { + snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", + overflow ? " OVERFLOW" : "", + (uncorrected_error && recoverable) ? " recoverable" : "", + mscod, errcode, adxl_msg); + } else { + snprintf(skx_msg, MSG_SIZE, + "%s%s err_code:0x%04x:0x%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:0x%x col:0x%x", + overflow ? " OVERFLOW" : "", + (uncorrected_error && recoverable) ? " recoverable" : "", + mscod, errcode, + res->socket, res->imc, res->rank, + res->bank_group, res->bank_address, res->row, res->column); + } + + edac_dbg(0, "%s\n", skx_msg); + + /* Call the helper to output message */ + edac_mc_handle_error(tp_event, mci, core_err_cnt, + m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0, + res->channel, res->dimm, -1, + optype, skx_msg); +} + +int skx_mce_check_error(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct mce *mce = (struct mce *)data; + struct decoded_addr res; + struct mem_ctl_info *mci; + char *type; + + if (edac_get_report_status() == EDAC_REPORTING_DISABLED) + return NOTIFY_DONE; + + /* ignore unless this is memory related with an address */ + if ((mce->status & 0xefff) >> 7 != 1 || !(mce->status & MCI_STATUS_ADDRV)) + return NOTIFY_DONE; + + memset(&res, 0, sizeof(res)); + res.addr = mce->addr; + + if (adxl_component_count) { + if (!skx_adxl_decode(&res)) + return NOTIFY_DONE; + + mci = get_mci(res.socket, res.imc); + } else { + if (!skx_decode || !skx_decode(&res)) + return NOTIFY_DONE; + + mci = res.dev->imc[res.imc].mci; + } + + if (!mci) + return NOTIFY_DONE; + + if (mce->mcgstatus & MCG_STATUS_MCIP) + type = "Exception"; + else + type = "Event"; + + skx_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n"); + + skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: 0x%llx " + "Bank %d: 0x%llx\n", mce->extcpu, type, + mce->mcgstatus, mce->bank, mce->status); + skx_mc_printk(mci, KERN_DEBUG, "TSC 0x%llx ", mce->tsc); + skx_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", mce->addr); + skx_mc_printk(mci, KERN_DEBUG, "MISC 0x%llx ", mce->misc); + + skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:0x%x TIME %llu SOCKET " + "%u APIC 0x%x\n", mce->cpuvendor, mce->cpuid, + mce->time, mce->socketid, mce->apicid); + + skx_mce_output_error(mci, mce, &res); + + return NOTIFY_DONE; +} + +void skx_remove(void) +{ + int i, j; + struct skx_dev *d, *tmp; + + edac_dbg(0, "\n"); + + list_for_each_entry_safe(d, tmp, &dev_edac_list, list) { + list_del(&d->list); + for (i = 0; i < NUM_IMC; i++) { + if (d->imc[i].mci) + skx_unregister_mci(&d->imc[i]); + + if (d->imc[i].mdev) + pci_dev_put(d->imc[i].mdev); + + if (d->imc[i].mbase) + iounmap(d->imc[i].mbase); + + for (j = 0; j < NUM_CHANNELS; j++) { + if (d->imc[i].chan[j].cdev) + pci_dev_put(d->imc[i].chan[j].cdev); + } + } + if (d->util_all) + pci_dev_put(d->util_all); + if (d->sad_all) + pci_dev_put(d->sad_all); + if (d->uracu) + pci_dev_put(d->uracu); + + kfree(d); + } +} + +#ifdef CONFIG_EDAC_DEBUG +/* + * Debug feature. + * Exercise the address decode logic by writing an address to + * /sys/kernel/debug/edac/dirname/addr. + */ +static struct dentry *skx_test; + +static int debugfs_u64_set(void *data, u64 val) +{ + struct mce m; + + pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val); + + memset(&m, 0, sizeof(m)); + /* ADDRV + MemRd + Unknown channel */ + m.status = MCI_STATUS_ADDRV + 0x90; + /* One corrected error */ + m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT); + m.addr = val; + skx_mce_check_error(NULL, 0, &m); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); + +void setup_skx_debug(const char *dirname) +{ + skx_test = edac_debugfs_create_dir(dirname); + if (!skx_test) + return; + + if (!edac_debugfs_create_file("addr", 0200, skx_test, + NULL, &fops_u64_wo)) { + debugfs_remove(skx_test); + skx_test = NULL; + } +} + +void teardown_skx_debug(void) +{ + debugfs_remove_recursive(skx_test); +} +#endif /*CONFIG_EDAC_DEBUG*/ diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h new file mode 100644 index 000000000000..d25374e34d4f --- /dev/null +++ b/drivers/edac/skx_common.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Common codes for both the skx_edac driver and Intel 10nm server EDAC driver. + * Originally split out from the skx_edac driver. + * + * Copyright (c) 2018, Intel Corporation. + */ + +#ifndef _SKX_COMM_EDAC_H +#define _SKX_COMM_EDAC_H + +#define MSG_SIZE 1024 + +/* + * Debug macros + */ +#define skx_printk(level, fmt, arg...) \ + edac_printk(level, "skx", fmt, ##arg) + +#define skx_mc_printk(mci, level, fmt, arg...) \ + edac_mc_chipset_printk(mci, level, "skx", fmt, ##arg) + +/* + * Get a bit field at register value , from bit to bit + */ +#define GET_BITFIELD(v, lo, hi) \ + (((v) & GENMASK_ULL((hi), (lo))) >> (lo)) + +#define SKX_NUM_IMC 2 /* Memory controllers per socket */ +#define SKX_NUM_CHANNELS 3 /* Channels per memory controller */ +#define SKX_NUM_DIMMS 2 /* Max DIMMS per channel */ + +#define I10NM_NUM_IMC 4 +#define I10NM_NUM_CHANNELS 2 +#define I10NM_NUM_DIMMS 2 + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define NUM_IMC MAX(SKX_NUM_IMC, I10NM_NUM_IMC) +#define NUM_CHANNELS MAX(SKX_NUM_CHANNELS, I10NM_NUM_CHANNELS) +#define NUM_DIMMS MAX(SKX_NUM_DIMMS, I10NM_NUM_DIMMS) + +#define IS_DIMM_PRESENT(r) GET_BITFIELD(r, 15, 15) +#define IS_NVDIMM_PRESENT(r, i) GET_BITFIELD(r, i, i) + +/* + * Each cpu socket contains some pci devices that provide global + * information, and also some that are local to each of the two + * memory controllers on the die. + */ +struct skx_dev { + struct list_head list; + u8 bus[4]; + int seg; + struct pci_dev *sad_all; + struct pci_dev *util_all; + struct pci_dev *uracu; /* for i10nm CPU */ + u32 mcroute; + struct skx_imc { + struct mem_ctl_info *mci; + struct pci_dev *mdev; /* for i10nm CPU */ + void __iomem *mbase; /* for i10nm CPU */ + u8 mc; /* system wide mc# */ + u8 lmc; /* socket relative mc# */ + u8 src_id, node_id; + struct skx_channel { + struct pci_dev *cdev; + struct skx_dimm { + u8 close_pg; + u8 bank_xor_enable; + u8 fine_grain_bank; + u8 rowbits; + u8 colbits; + } dimms[NUM_DIMMS]; + } chan[NUM_CHANNELS]; + } imc[NUM_IMC]; +}; + +struct skx_pvt { + struct skx_imc *imc; +}; + +enum type { + SKX, + I10NM +}; + +enum { + INDEX_SOCKET, + INDEX_MEMCTRL, + INDEX_CHANNEL, + INDEX_DIMM, + INDEX_MAX +}; + +struct decoded_addr { + struct skx_dev *dev; + u64 addr; + int socket; + int imc; + int channel; + u64 chan_addr; + int sktways; + int chanways; + int dimm; + int rank; + int channel_rank; + u64 rank_address; + int row; + int column; + int bank_address; + int bank_group; +}; + +typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci); +typedef bool (*skx_decode_f)(struct decoded_addr *res); + +int __init skx_adxl_get(void); +void __exit skx_adxl_put(void); +void skx_set_decode(skx_decode_f decode); + +int skx_get_src_id(struct skx_dev *d, u8 *id); +int skx_get_node_id(struct skx_dev *d, u8 *id); + +int skx_get_all_bus_mappings(unsigned int did, int off, enum type, + struct list_head **list); + +int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm); + +int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm, + struct skx_imc *imc, int chan, int dimmno); + +int skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc, + int chan, int dimmno, const char *mod_str); + +int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, + const char *ctl_name, const char *mod_str, + get_dimm_config_f get_dimm_config); + +int skx_mce_check_error(struct notifier_block *nb, unsigned long val, + void *data); + +void skx_remove(void); + +#ifdef CONFIG_EDAC_DEBUG +void setup_skx_debug(const char *dirname); +void teardown_skx_debug(void); +#else +static inline void setup_skx_debug(const char *dirname) {} +static inline void teardown_skx_debug(void) {} +#endif /*CONFIG_EDAC_DEBUG*/ + +#endif /* _SKX_COMM_EDAC_H */ From 98f2fc829e3b55a0ff1b82753fc1e10941535cb9 Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Wed, 30 Jan 2019 11:15:18 -0800 Subject: [PATCH 0979/4277] EDAC, skx_edac: Delete duplicated code Delete the duplicated code from skx_edac.c and rename skx_edac.c to skx_base.c. Update the Makefile to build the skx_edac driver from skx_base.c and skx_common.c. Add SPDX to skx_base.c and clean out unnecessary #include lines. [ bp: Drop the license boilerplate - there's an SPDX identifier now. ] Co-developed-by: Tony Luck Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov Cc: James Morse Cc: Mauro Carvalho Chehab Cc: linux-edac Link: https://lkml.kernel.org/r/20190130191519.15393-4-tony.luck@intel.com --- drivers/edac/Makefile | 4 +- drivers/edac/skx_base.c | 650 +++++++++++++++++++ drivers/edac/skx_edac.c | 1358 --------------------------------------- 3 files changed, 653 insertions(+), 1359 deletions(-) create mode 100644 drivers/edac/skx_base.c delete mode 100644 drivers/edac/skx_edac.c diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index e1f23d4ff860..1c985fa2dcdc 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_EDAC_I5400) += i5400_edac.o obj-$(CONFIG_EDAC_I7300) += i7300_edac.o obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o -obj-$(CONFIG_EDAC_SKX) += skx_edac.o obj-$(CONFIG_EDAC_PND2) += pnd2_edac.o obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o @@ -58,6 +57,9 @@ obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac_mod.o layerscape_edac_mod-y := fsl_ddr_edac.o layerscape_edac.o obj-$(CONFIG_EDAC_LAYERSCAPE) += layerscape_edac_mod.o +skx_edac-y := skx_common.o skx_base.o +obj-$(CONFIG_EDAC_SKX) += skx_edac.o + obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o obj-$(CONFIG_EDAC_CELL) += cell_edac.o obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c new file mode 100644 index 000000000000..adae4c848ca1 --- /dev/null +++ b/drivers/edac/skx_base.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * EDAC driver for Intel(R) Xeon(R) Skylake processors + * Copyright (c) 2016, Intel Corporation. + */ + +#include +#include +#include +#include +#include + +#include "edac_module.h" +#include "skx_common.h" + +#define EDAC_MOD_STR "skx_edac" + +/* + * Debug macros + */ +#define skx_printk(level, fmt, arg...) \ + edac_printk(level, "skx", fmt, ##arg) + +#define skx_mc_printk(mci, level, fmt, arg...) \ + edac_mc_chipset_printk(mci, level, "skx", fmt, ##arg) + +static struct list_head *skx_edac_list; + +static u64 skx_tolm, skx_tohm; +static int skx_num_sockets; +static unsigned int nvdimm_count; + +#define MASK26 0x3FFFFFF /* Mask for 2^26 */ +#define MASK29 0x1FFFFFFF /* Mask for 2^29 */ + +static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx) +{ + struct skx_dev *d; + + list_for_each_entry(d, skx_edac_list, list) { + if (d->seg == pci_domain_nr(bus) && d->bus[idx] == bus->number) + return d; + } + + return NULL; +} + +enum munittype { + CHAN0, CHAN1, CHAN2, SAD_ALL, UTIL_ALL, SAD +}; + +struct munit { + u16 did; + u16 devfn[SKX_NUM_IMC]; + u8 busidx; + u8 per_socket; + enum munittype mtype; +}; + +/* + * List of PCI device ids that we need together with some device + * number and function numbers to tell which memory controller the + * device belongs to. + */ +static const struct munit skx_all_munits[] = { + { 0x2054, { }, 1, 1, SAD_ALL }, + { 0x2055, { }, 1, 1, UTIL_ALL }, + { 0x2040, { PCI_DEVFN(10, 0), PCI_DEVFN(12, 0) }, 2, 2, CHAN0 }, + { 0x2044, { PCI_DEVFN(10, 4), PCI_DEVFN(12, 4) }, 2, 2, CHAN1 }, + { 0x2048, { PCI_DEVFN(11, 0), PCI_DEVFN(13, 0) }, 2, 2, CHAN2 }, + { 0x208e, { }, 1, 0, SAD }, + { } +}; + +static int get_all_munits(const struct munit *m) +{ + struct pci_dev *pdev, *prev; + struct skx_dev *d; + u32 reg; + int i = 0, ndev = 0; + + prev = NULL; + for (;;) { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, m->did, prev); + if (!pdev) + break; + ndev++; + if (m->per_socket == SKX_NUM_IMC) { + for (i = 0; i < SKX_NUM_IMC; i++) + if (m->devfn[i] == pdev->devfn) + break; + if (i == SKX_NUM_IMC) + goto fail; + } + d = get_skx_dev(pdev->bus, m->busidx); + if (!d) + goto fail; + + /* Be sure that the device is enabled */ + if (unlikely(pci_enable_device(pdev) < 0)) { + skx_printk(KERN_ERR, "Couldn't enable device %04x:%04x\n", + PCI_VENDOR_ID_INTEL, m->did); + goto fail; + } + + switch (m->mtype) { + case CHAN0: case CHAN1: case CHAN2: + pci_dev_get(pdev); + d->imc[i].chan[m->mtype].cdev = pdev; + break; + case SAD_ALL: + pci_dev_get(pdev); + d->sad_all = pdev; + break; + case UTIL_ALL: + pci_dev_get(pdev); + d->util_all = pdev; + break; + case SAD: + /* + * one of these devices per core, including cores + * that don't exist on this SKU. Ignore any that + * read a route table of zero, make sure all the + * non-zero values match. + */ + pci_read_config_dword(pdev, 0xB4, ®); + if (reg != 0) { + if (d->mcroute == 0) { + d->mcroute = reg; + } else if (d->mcroute != reg) { + skx_printk(KERN_ERR, "mcroute mismatch\n"); + goto fail; + } + } + ndev--; + break; + } + + prev = pdev; + } + + return ndev; +fail: + pci_dev_put(pdev); + return -ENODEV; +} + +static const struct x86_cpu_id skx_cpuids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_X, 0, 0 }, + { } +}; +MODULE_DEVICE_TABLE(x86cpu, skx_cpuids); + +#define SKX_GET_MTMTR(dev, reg) \ + pci_read_config_dword((dev), 0x87c, &(reg)) + +static bool skx_check_ecc(struct pci_dev *pdev) +{ + u32 mtmtr; + + SKX_GET_MTMTR(pdev, mtmtr); + + return !!GET_BITFIELD(mtmtr, 2, 2); +} + +static int skx_get_dimm_config(struct mem_ctl_info *mci) +{ + struct skx_pvt *pvt = mci->pvt_info; + struct skx_imc *imc = pvt->imc; + u32 mtr, amap, mcddrtcfg; + struct dimm_info *dimm; + int i, j; + int ndimms; + + for (i = 0; i < SKX_NUM_CHANNELS; i++) { + ndimms = 0; + pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap); + pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg); + for (j = 0; j < SKX_NUM_DIMMS; j++) { + dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, + mci->n_layers, i, j, 0); + pci_read_config_dword(imc->chan[i].cdev, + 0x80 + 4 * j, &mtr); + if (IS_DIMM_PRESENT(mtr)) { + ndimms += skx_get_dimm_info(mtr, amap, dimm, imc, i, j); + } else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) { + ndimms += skx_get_nvdimm_info(dimm, imc, i, j, + EDAC_MOD_STR); + nvdimm_count++; + } + } + if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) { + skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc); + return -ENODEV; + } + } + + return 0; +} + +#define SKX_MAX_SAD 24 + +#define SKX_GET_SAD(d, i, reg) \ + pci_read_config_dword((d)->sad_all, 0x60 + 8 * (i), &(reg)) +#define SKX_GET_ILV(d, i, reg) \ + pci_read_config_dword((d)->sad_all, 0x64 + 8 * (i), &(reg)) + +#define SKX_SAD_MOD3MODE(sad) GET_BITFIELD((sad), 30, 31) +#define SKX_SAD_MOD3(sad) GET_BITFIELD((sad), 27, 27) +#define SKX_SAD_LIMIT(sad) (((u64)GET_BITFIELD((sad), 7, 26) << 26) | MASK26) +#define SKX_SAD_MOD3ASMOD2(sad) GET_BITFIELD((sad), 5, 6) +#define SKX_SAD_ATTR(sad) GET_BITFIELD((sad), 3, 4) +#define SKX_SAD_INTERLEAVE(sad) GET_BITFIELD((sad), 1, 2) +#define SKX_SAD_ENABLE(sad) GET_BITFIELD((sad), 0, 0) + +#define SKX_ILV_REMOTE(tgt) (((tgt) & 8) == 0) +#define SKX_ILV_TARGET(tgt) ((tgt) & 7) + +static bool skx_sad_decode(struct decoded_addr *res) +{ + struct skx_dev *d = list_first_entry(skx_edac_list, typeof(*d), list); + u64 addr = res->addr; + int i, idx, tgt, lchan, shift; + u32 sad, ilv; + u64 limit, prev_limit; + int remote = 0; + + /* Simple sanity check for I/O space or out of range */ + if (addr >= skx_tohm || (addr >= skx_tolm && addr < BIT_ULL(32))) { + edac_dbg(0, "Address 0x%llx out of range\n", addr); + return false; + } + +restart: + prev_limit = 0; + for (i = 0; i < SKX_MAX_SAD; i++) { + SKX_GET_SAD(d, i, sad); + limit = SKX_SAD_LIMIT(sad); + if (SKX_SAD_ENABLE(sad)) { + if (addr >= prev_limit && addr <= limit) + goto sad_found; + } + prev_limit = limit + 1; + } + edac_dbg(0, "No SAD entry for 0x%llx\n", addr); + return false; + +sad_found: + SKX_GET_ILV(d, i, ilv); + + switch (SKX_SAD_INTERLEAVE(sad)) { + case 0: + idx = GET_BITFIELD(addr, 6, 8); + break; + case 1: + idx = GET_BITFIELD(addr, 8, 10); + break; + case 2: + idx = GET_BITFIELD(addr, 12, 14); + break; + case 3: + idx = GET_BITFIELD(addr, 30, 32); + break; + } + + tgt = GET_BITFIELD(ilv, 4 * idx, 4 * idx + 3); + + /* If point to another node, find it and start over */ + if (SKX_ILV_REMOTE(tgt)) { + if (remote) { + edac_dbg(0, "Double remote!\n"); + return false; + } + remote = 1; + list_for_each_entry(d, skx_edac_list, list) { + if (d->imc[0].src_id == SKX_ILV_TARGET(tgt)) + goto restart; + } + edac_dbg(0, "Can't find node %d\n", SKX_ILV_TARGET(tgt)); + return false; + } + + if (SKX_SAD_MOD3(sad) == 0) { + lchan = SKX_ILV_TARGET(tgt); + } else { + switch (SKX_SAD_MOD3MODE(sad)) { + case 0: + shift = 6; + break; + case 1: + shift = 8; + break; + case 2: + shift = 12; + break; + default: + edac_dbg(0, "illegal mod3mode\n"); + return false; + } + switch (SKX_SAD_MOD3ASMOD2(sad)) { + case 0: + lchan = (addr >> shift) % 3; + break; + case 1: + lchan = (addr >> shift) % 2; + break; + case 2: + lchan = (addr >> shift) % 2; + lchan = (lchan << 1) | !lchan; + break; + case 3: + lchan = ((addr >> shift) % 2) << 1; + break; + } + lchan = (lchan << 1) | (SKX_ILV_TARGET(tgt) & 1); + } + + res->dev = d; + res->socket = d->imc[0].src_id; + res->imc = GET_BITFIELD(d->mcroute, lchan * 3, lchan * 3 + 2); + res->channel = GET_BITFIELD(d->mcroute, lchan * 2 + 18, lchan * 2 + 19); + + edac_dbg(2, "0x%llx: socket=%d imc=%d channel=%d\n", + res->addr, res->socket, res->imc, res->channel); + return true; +} + +#define SKX_MAX_TAD 8 + +#define SKX_GET_TADBASE(d, mc, i, reg) \ + pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x850 + 4 * (i), &(reg)) +#define SKX_GET_TADWAYNESS(d, mc, i, reg) \ + pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x880 + 4 * (i), &(reg)) +#define SKX_GET_TADCHNILVOFFSET(d, mc, ch, i, reg) \ + pci_read_config_dword((d)->imc[mc].chan[ch].cdev, 0x90 + 4 * (i), &(reg)) + +#define SKX_TAD_BASE(b) ((u64)GET_BITFIELD((b), 12, 31) << 26) +#define SKX_TAD_SKT_GRAN(b) GET_BITFIELD((b), 4, 5) +#define SKX_TAD_CHN_GRAN(b) GET_BITFIELD((b), 6, 7) +#define SKX_TAD_LIMIT(b) (((u64)GET_BITFIELD((b), 12, 31) << 26) | MASK26) +#define SKX_TAD_OFFSET(b) ((u64)GET_BITFIELD((b), 4, 23) << 26) +#define SKX_TAD_SKTWAYS(b) (1 << GET_BITFIELD((b), 10, 11)) +#define SKX_TAD_CHNWAYS(b) (GET_BITFIELD((b), 8, 9) + 1) + +/* which bit used for both socket and channel interleave */ +static int skx_granularity[] = { 6, 8, 12, 30 }; + +static u64 skx_do_interleave(u64 addr, int shift, int ways, u64 lowbits) +{ + addr >>= shift; + addr /= ways; + addr <<= shift; + + return addr | (lowbits & ((1ull << shift) - 1)); +} + +static bool skx_tad_decode(struct decoded_addr *res) +{ + int i; + u32 base, wayness, chnilvoffset; + int skt_interleave_bit, chn_interleave_bit; + u64 channel_addr; + + for (i = 0; i < SKX_MAX_TAD; i++) { + SKX_GET_TADBASE(res->dev, res->imc, i, base); + SKX_GET_TADWAYNESS(res->dev, res->imc, i, wayness); + if (SKX_TAD_BASE(base) <= res->addr && res->addr <= SKX_TAD_LIMIT(wayness)) + goto tad_found; + } + edac_dbg(0, "No TAD entry for 0x%llx\n", res->addr); + return false; + +tad_found: + res->sktways = SKX_TAD_SKTWAYS(wayness); + res->chanways = SKX_TAD_CHNWAYS(wayness); + skt_interleave_bit = skx_granularity[SKX_TAD_SKT_GRAN(base)]; + chn_interleave_bit = skx_granularity[SKX_TAD_CHN_GRAN(base)]; + + SKX_GET_TADCHNILVOFFSET(res->dev, res->imc, res->channel, i, chnilvoffset); + channel_addr = res->addr - SKX_TAD_OFFSET(chnilvoffset); + + if (res->chanways == 3 && skt_interleave_bit > chn_interleave_bit) { + /* Must handle channel first, then socket */ + channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit, + res->chanways, channel_addr); + channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit, + res->sktways, channel_addr); + } else { + /* Handle socket then channel. Preserve low bits from original address */ + channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit, + res->sktways, res->addr); + channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit, + res->chanways, res->addr); + } + + res->chan_addr = channel_addr; + + edac_dbg(2, "0x%llx: chan_addr=0x%llx sktways=%d chanways=%d\n", + res->addr, res->chan_addr, res->sktways, res->chanways); + return true; +} + +#define SKX_MAX_RIR 4 + +#define SKX_GET_RIRWAYNESS(d, mc, ch, i, reg) \ + pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \ + 0x108 + 4 * (i), &(reg)) +#define SKX_GET_RIRILV(d, mc, ch, idx, i, reg) \ + pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \ + 0x120 + 16 * (idx) + 4 * (i), &(reg)) + +#define SKX_RIR_VALID(b) GET_BITFIELD((b), 31, 31) +#define SKX_RIR_LIMIT(b) (((u64)GET_BITFIELD((b), 1, 11) << 29) | MASK29) +#define SKX_RIR_WAYS(b) (1 << GET_BITFIELD((b), 28, 29)) +#define SKX_RIR_CHAN_RANK(b) GET_BITFIELD((b), 16, 19) +#define SKX_RIR_OFFSET(b) ((u64)(GET_BITFIELD((b), 2, 15) << 26)) + +static bool skx_rir_decode(struct decoded_addr *res) +{ + int i, idx, chan_rank; + int shift; + u32 rirway, rirlv; + u64 rank_addr, prev_limit = 0, limit; + + if (res->dev->imc[res->imc].chan[res->channel].dimms[0].close_pg) + shift = 6; + else + shift = 13; + + for (i = 0; i < SKX_MAX_RIR; i++) { + SKX_GET_RIRWAYNESS(res->dev, res->imc, res->channel, i, rirway); + limit = SKX_RIR_LIMIT(rirway); + if (SKX_RIR_VALID(rirway)) { + if (prev_limit <= res->chan_addr && + res->chan_addr <= limit) + goto rir_found; + } + prev_limit = limit; + } + edac_dbg(0, "No RIR entry for 0x%llx\n", res->addr); + return false; + +rir_found: + rank_addr = res->chan_addr >> shift; + rank_addr /= SKX_RIR_WAYS(rirway); + rank_addr <<= shift; + rank_addr |= res->chan_addr & GENMASK_ULL(shift - 1, 0); + + res->rank_address = rank_addr; + idx = (res->chan_addr >> shift) % SKX_RIR_WAYS(rirway); + + SKX_GET_RIRILV(res->dev, res->imc, res->channel, idx, i, rirlv); + res->rank_address = rank_addr - SKX_RIR_OFFSET(rirlv); + chan_rank = SKX_RIR_CHAN_RANK(rirlv); + res->channel_rank = chan_rank; + res->dimm = chan_rank / 4; + res->rank = chan_rank % 4; + + edac_dbg(2, "0x%llx: dimm=%d rank=%d chan_rank=%d rank_addr=0x%llx\n", + res->addr, res->dimm, res->rank, + res->channel_rank, res->rank_address); + return true; +} + +static u8 skx_close_row[] = { + 15, 16, 17, 18, 20, 21, 22, 28, 10, 11, 12, 13, 29, 30, 31, 32, 33 +}; + +static u8 skx_close_column[] = { + 3, 4, 5, 14, 19, 23, 24, 25, 26, 27 +}; + +static u8 skx_open_row[] = { + 14, 15, 16, 20, 28, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33 +}; + +static u8 skx_open_column[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 +}; + +static u8 skx_open_fine_column[] = { + 3, 4, 5, 7, 8, 9, 10, 11, 12, 13 +}; + +static int skx_bits(u64 addr, int nbits, u8 *bits) +{ + int i, res = 0; + + for (i = 0; i < nbits; i++) + res |= ((addr >> bits[i]) & 1) << i; + return res; +} + +static int skx_bank_bits(u64 addr, int b0, int b1, int do_xor, int x0, int x1) +{ + int ret = GET_BITFIELD(addr, b0, b0) | (GET_BITFIELD(addr, b1, b1) << 1); + + if (do_xor) + ret ^= GET_BITFIELD(addr, x0, x0) | (GET_BITFIELD(addr, x1, x1) << 1); + + return ret; +} + +static bool skx_mad_decode(struct decoded_addr *r) +{ + struct skx_dimm *dimm = &r->dev->imc[r->imc].chan[r->channel].dimms[r->dimm]; + int bg0 = dimm->fine_grain_bank ? 6 : 13; + + if (dimm->close_pg) { + r->row = skx_bits(r->rank_address, dimm->rowbits, skx_close_row); + r->column = skx_bits(r->rank_address, dimm->colbits, skx_close_column); + r->column |= 0x400; /* C10 is autoprecharge, always set */ + r->bank_address = skx_bank_bits(r->rank_address, 8, 9, dimm->bank_xor_enable, 22, 28); + r->bank_group = skx_bank_bits(r->rank_address, 6, 7, dimm->bank_xor_enable, 20, 21); + } else { + r->row = skx_bits(r->rank_address, dimm->rowbits, skx_open_row); + if (dimm->fine_grain_bank) + r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_fine_column); + else + r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_column); + r->bank_address = skx_bank_bits(r->rank_address, 18, 19, dimm->bank_xor_enable, 22, 23); + r->bank_group = skx_bank_bits(r->rank_address, bg0, 17, dimm->bank_xor_enable, 20, 21); + } + r->row &= (1u << dimm->rowbits) - 1; + + edac_dbg(2, "0x%llx: row=0x%x col=0x%x bank_addr=%d bank_group=%d\n", + r->addr, r->row, r->column, r->bank_address, + r->bank_group); + return true; +} + +static bool skx_decode(struct decoded_addr *res) +{ + return skx_sad_decode(res) && skx_tad_decode(res) && + skx_rir_decode(res) && skx_mad_decode(res); +} + +static struct notifier_block skx_mce_dec = { + .notifier_call = skx_mce_check_error, + .priority = MCE_PRIO_EDAC, +}; + +/* + * skx_init: + * make sure we are running on the correct cpu model + * search for all the devices we need + * check which DIMMs are present. + */ +static int __init skx_init(void) +{ + const struct x86_cpu_id *id; + const struct munit *m; + const char *owner; + int rc = 0, i, off[3] = {0xd0, 0xd4, 0xd8}; + u8 mc = 0, src_id, node_id; + struct skx_dev *d; + + edac_dbg(2, "\n"); + + owner = edac_get_owner(); + if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) + return -EBUSY; + + id = x86_match_cpu(skx_cpuids); + if (!id) + return -ENODEV; + + rc = skx_get_hi_lo(0x2034, off, &skx_tolm, &skx_tohm); + if (rc) + return rc; + + rc = skx_get_all_bus_mappings(0x2016, 0xcc, SKX, &skx_edac_list); + if (rc < 0) + goto fail; + if (rc == 0) { + edac_dbg(2, "No memory controllers found\n"); + return -ENODEV; + } + skx_num_sockets = rc; + + for (m = skx_all_munits; m->did; m++) { + rc = get_all_munits(m); + if (rc < 0) + goto fail; + if (rc != m->per_socket * skx_num_sockets) { + edac_dbg(2, "Expected %d, got %d of 0x%x\n", + m->per_socket * skx_num_sockets, rc, m->did); + rc = -ENODEV; + goto fail; + } + } + + list_for_each_entry(d, skx_edac_list, list) { + rc = skx_get_src_id(d, &src_id); + if (rc < 0) + goto fail; + rc = skx_get_node_id(d, &node_id); + if (rc < 0) + goto fail; + edac_dbg(2, "src_id=%d node_id=%d\n", src_id, node_id); + for (i = 0; i < SKX_NUM_IMC; i++) { + d->imc[i].mc = mc++; + d->imc[i].lmc = i; + d->imc[i].src_id = src_id; + d->imc[i].node_id = node_id; + rc = skx_register_mci(&d->imc[i], d->imc[i].chan[0].cdev, + "Skylake Socket", EDAC_MOD_STR, + skx_get_dimm_config); + if (rc < 0) + goto fail; + } + } + + skx_set_decode(skx_decode); + + if (nvdimm_count && skx_adxl_get() == -ENODEV) + skx_printk(KERN_NOTICE, "Only decoding DDR4 address!\n"); + + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); + + setup_skx_debug("skx_test"); + + mce_register_decode_chain(&skx_mce_dec); + + return 0; +fail: + skx_remove(); + return rc; +} + +static void __exit skx_exit(void) +{ + edac_dbg(2, "\n"); + mce_unregister_decode_chain(&skx_mce_dec); + teardown_skx_debug(); + if (nvdimm_count) + skx_adxl_put(); + skx_remove(); +} + +module_init(skx_init); +module_exit(skx_exit); + +module_param(edac_op_state, int, 0444); +MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Tony Luck"); +MODULE_DESCRIPTION("MC Driver for Intel Skylake server processors"); diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c deleted file mode 100644 index 93ef161bb5e1..000000000000 --- a/drivers/edac/skx_edac.c +++ /dev/null @@ -1,1358 +0,0 @@ -/* - * EDAC driver for Intel(R) Xeon(R) Skylake processors - * Copyright (c) 2016, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "edac_module.h" - -#define EDAC_MOD_STR "skx_edac" -#define MSG_SIZE 1024 - -/* - * Debug macros - */ -#define skx_printk(level, fmt, arg...) \ - edac_printk(level, "skx", fmt, ##arg) - -#define skx_mc_printk(mci, level, fmt, arg...) \ - edac_mc_chipset_printk(mci, level, "skx", fmt, ##arg) - -/* - * Get a bit field at register value , from bit to bit - */ -#define GET_BITFIELD(v, lo, hi) \ - (((v) & GENMASK_ULL((hi), (lo))) >> (lo)) - -static LIST_HEAD(skx_edac_list); - -static u64 skx_tolm, skx_tohm; -static char *skx_msg; -static unsigned int nvdimm_count; - -enum { - INDEX_SOCKET, - INDEX_MEMCTRL, - INDEX_CHANNEL, - INDEX_DIMM, - INDEX_MAX -}; - -static const char * const component_names[] = { - [INDEX_SOCKET] = "ProcessorSocketId", - [INDEX_MEMCTRL] = "MemoryControllerId", - [INDEX_CHANNEL] = "ChannelId", - [INDEX_DIMM] = "DimmSlotId", -}; - -static int component_indices[ARRAY_SIZE(component_names)]; -static int adxl_component_count; -static const char * const *adxl_component_names; -static u64 *adxl_values; -static char *adxl_msg; - -#define NUM_IMC 2 /* memory controllers per socket */ -#define NUM_CHANNELS 3 /* channels per memory controller */ -#define NUM_DIMMS 2 /* Max DIMMS per channel */ - -#define MASK26 0x3FFFFFF /* Mask for 2^26 */ -#define MASK29 0x1FFFFFFF /* Mask for 2^29 */ - -/* - * Each cpu socket contains some pci devices that provide global - * information, and also some that are local to each of the two - * memory controllers on the die. - */ -struct skx_dev { - struct list_head list; - u8 bus[4]; - int seg; - struct pci_dev *sad_all; - struct pci_dev *util_all; - u32 mcroute; - struct skx_imc { - struct mem_ctl_info *mci; - u8 mc; /* system wide mc# */ - u8 lmc; /* socket relative mc# */ - u8 src_id, node_id; - struct skx_channel { - struct pci_dev *cdev; - struct skx_dimm { - u8 close_pg; - u8 bank_xor_enable; - u8 fine_grain_bank; - u8 rowbits; - u8 colbits; - } dimms[NUM_DIMMS]; - } chan[NUM_CHANNELS]; - } imc[NUM_IMC]; -}; -static int skx_num_sockets; - -struct skx_pvt { - struct skx_imc *imc; -}; - -struct decoded_addr { - struct skx_dev *dev; - u64 addr; - int socket; - int imc; - int channel; - u64 chan_addr; - int sktways; - int chanways; - int dimm; - int rank; - int channel_rank; - u64 rank_address; - int row; - int column; - int bank_address; - int bank_group; -}; - -static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx) -{ - struct skx_dev *d; - - list_for_each_entry(d, &skx_edac_list, list) { - if (d->seg == pci_domain_nr(bus) && d->bus[idx] == bus->number) - return d; - } - - return NULL; -} - -enum munittype { - CHAN0, CHAN1, CHAN2, SAD_ALL, UTIL_ALL, SAD -}; - -struct munit { - u16 did; - u16 devfn[NUM_IMC]; - u8 busidx; - u8 per_socket; - enum munittype mtype; -}; - -/* - * List of PCI device ids that we need together with some device - * number and function numbers to tell which memory controller the - * device belongs to. - */ -static const struct munit skx_all_munits[] = { - { 0x2054, { }, 1, 1, SAD_ALL }, - { 0x2055, { }, 1, 1, UTIL_ALL }, - { 0x2040, { PCI_DEVFN(10, 0), PCI_DEVFN(12, 0) }, 2, 2, CHAN0 }, - { 0x2044, { PCI_DEVFN(10, 4), PCI_DEVFN(12, 4) }, 2, 2, CHAN1 }, - { 0x2048, { PCI_DEVFN(11, 0), PCI_DEVFN(13, 0) }, 2, 2, CHAN2 }, - { 0x208e, { }, 1, 0, SAD }, - { } -}; - -/* - * We use the per-socket device 0x2016 to count how many sockets are present, - * and to detemine which PCI buses are associated with each socket. Allocate - * and build the full list of all the skx_dev structures that we need here. - */ -static int get_all_bus_mappings(void) -{ - struct pci_dev *pdev, *prev; - struct skx_dev *d; - u32 reg; - int ndev = 0; - - prev = NULL; - for (;;) { - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x2016, prev); - if (!pdev) - break; - ndev++; - d = kzalloc(sizeof(*d), GFP_KERNEL); - if (!d) { - pci_dev_put(pdev); - return -ENOMEM; - } - d->seg = pci_domain_nr(pdev->bus); - pci_read_config_dword(pdev, 0xCC, ®); - d->bus[0] = GET_BITFIELD(reg, 0, 7); - d->bus[1] = GET_BITFIELD(reg, 8, 15); - d->bus[2] = GET_BITFIELD(reg, 16, 23); - d->bus[3] = GET_BITFIELD(reg, 24, 31); - edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n", - d->bus[0], d->bus[1], d->bus[2], d->bus[3]); - list_add_tail(&d->list, &skx_edac_list); - skx_num_sockets++; - prev = pdev; - } - - return ndev; -} - -static int get_all_munits(const struct munit *m) -{ - struct pci_dev *pdev, *prev; - struct skx_dev *d; - u32 reg; - int i = 0, ndev = 0; - - prev = NULL; - for (;;) { - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, m->did, prev); - if (!pdev) - break; - ndev++; - if (m->per_socket == NUM_IMC) { - for (i = 0; i < NUM_IMC; i++) - if (m->devfn[i] == pdev->devfn) - break; - if (i == NUM_IMC) - goto fail; - } - d = get_skx_dev(pdev->bus, m->busidx); - if (!d) - goto fail; - - /* Be sure that the device is enabled */ - if (unlikely(pci_enable_device(pdev) < 0)) { - skx_printk(KERN_ERR, "Couldn't enable device %04x:%04x\n", - PCI_VENDOR_ID_INTEL, m->did); - goto fail; - } - - switch (m->mtype) { - case CHAN0: case CHAN1: case CHAN2: - pci_dev_get(pdev); - d->imc[i].chan[m->mtype].cdev = pdev; - break; - case SAD_ALL: - pci_dev_get(pdev); - d->sad_all = pdev; - break; - case UTIL_ALL: - pci_dev_get(pdev); - d->util_all = pdev; - break; - case SAD: - /* - * one of these devices per core, including cores - * that don't exist on this SKU. Ignore any that - * read a route table of zero, make sure all the - * non-zero values match. - */ - pci_read_config_dword(pdev, 0xB4, ®); - if (reg != 0) { - if (d->mcroute == 0) - d->mcroute = reg; - else if (d->mcroute != reg) { - skx_printk(KERN_ERR, - "mcroute mismatch\n"); - goto fail; - } - } - ndev--; - break; - } - - prev = pdev; - } - - return ndev; -fail: - pci_dev_put(pdev); - return -ENODEV; -} - -static const struct x86_cpu_id skx_cpuids[] = { - { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_X, 0, 0 }, - { } -}; -MODULE_DEVICE_TABLE(x86cpu, skx_cpuids); - -static u8 get_src_id(struct skx_dev *d) -{ - u32 reg; - - pci_read_config_dword(d->util_all, 0xF0, ®); - - return GET_BITFIELD(reg, 12, 14); -} - -static u8 skx_get_node_id(struct skx_dev *d) -{ - u32 reg; - - pci_read_config_dword(d->util_all, 0xF4, ®); - - return GET_BITFIELD(reg, 0, 2); -} - -static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval, - int maxval, char *name) -{ - u32 val = GET_BITFIELD(reg, lobit, hibit); - - if (val < minval || val > maxval) { - edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg); - return -EINVAL; - } - return val + add; -} - -#define IS_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15) -#define IS_NVDIMM_PRESENT(mcddrtcfg, i) GET_BITFIELD((mcddrtcfg), (i), (i)) - -#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 0, 2, "ranks") -#define numrow(reg) get_dimm_attr((reg), 2, 4, 12, 1, 6, "rows") -#define numcol(reg) get_dimm_attr((reg), 0, 1, 10, 0, 2, "cols") - -static int get_width(u32 mtr) -{ - switch (GET_BITFIELD(mtr, 8, 9)) { - case 0: - return DEV_X4; - case 1: - return DEV_X8; - case 2: - return DEV_X16; - } - return DEV_UNKNOWN; -} - -static int skx_get_hi_lo(void) -{ - struct pci_dev *pdev; - u32 reg; - - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x2034, NULL); - if (!pdev) { - edac_dbg(0, "Can't get tolm/tohm\n"); - return -ENODEV; - } - - pci_read_config_dword(pdev, 0xD0, ®); - skx_tolm = reg; - pci_read_config_dword(pdev, 0xD4, ®); - skx_tohm = reg; - pci_read_config_dword(pdev, 0xD8, ®); - skx_tohm |= (u64)reg << 32; - - pci_dev_put(pdev); - edac_dbg(2, "tolm=0x%llx tohm=0x%llx\n", skx_tolm, skx_tohm); - - return 0; -} - -static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm, - struct skx_imc *imc, int chan, int dimmno) -{ - int banks = 16, ranks, rows, cols, npages; - u64 size; - - ranks = numrank(mtr); - rows = numrow(mtr); - cols = numcol(mtr); - - /* - * Compute size in 8-byte (2^3) words, then shift to MiB (2^20) - */ - size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3); - npages = MiB_TO_PAGES(size); - - edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: 0x%#x, col: 0x%#x\n", - imc->mc, chan, dimmno, size, npages, - banks, 1 << ranks, rows, cols); - - imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0); - imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9); - imc->chan[chan].dimms[dimmno].fine_grain_bank = GET_BITFIELD(amap, 0, 0); - imc->chan[chan].dimms[dimmno].rowbits = rows; - imc->chan[chan].dimms[dimmno].colbits = cols; - - dimm->nr_pages = npages; - dimm->grain = 32; - dimm->dtype = get_width(mtr); - dimm->mtype = MEM_DDR4; - dimm->edac_mode = EDAC_SECDED; /* likely better than this */ - snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", - imc->src_id, imc->lmc, chan, dimmno); - - return 1; -} - -static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc, - int chan, int dimmno) -{ - int smbios_handle; - u32 dev_handle; - u16 flags; - u64 size = 0; - - nvdimm_count++; - - dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc, - imc->src_id, 0); - - smbios_handle = nfit_get_smbios_id(dev_handle, &flags); - if (smbios_handle == -EOPNOTSUPP) { - pr_warn_once(EDAC_MOD_STR ": Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n"); - goto unknown_size; - } - - if (smbios_handle < 0) { - skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=0x%x\n", dev_handle); - goto unknown_size; - } - - if (flags & ACPI_NFIT_MEM_MAP_FAILED) { - skx_printk(KERN_ERR, "NVDIMM ADR=0x%x is not mapped\n", dev_handle); - goto unknown_size; - } - - size = dmi_memdev_size(smbios_handle); - if (size == ~0ull) - skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=0x%x/SMBIOS=0x%x\n", - dev_handle, smbios_handle); - -unknown_size: - dimm->nr_pages = size >> PAGE_SHIFT; - dimm->grain = 32; - dimm->dtype = DEV_UNKNOWN; - dimm->mtype = MEM_NVDIMM; - dimm->edac_mode = EDAC_SECDED; /* likely better than this */ - - edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu MiB (%u pages)\n", - imc->mc, chan, dimmno, size >> 20, dimm->nr_pages); - - snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", - imc->src_id, imc->lmc, chan, dimmno); - - return (size == 0 || size == ~0ull) ? 0 : 1; -} - -#define SKX_GET_MTMTR(dev, reg) \ - pci_read_config_dword((dev), 0x87c, ®) - -static bool skx_check_ecc(struct pci_dev *pdev) -{ - u32 mtmtr; - - SKX_GET_MTMTR(pdev, mtmtr); - - return !!GET_BITFIELD(mtmtr, 2, 2); -} - -static int skx_get_dimm_config(struct mem_ctl_info *mci) -{ - struct skx_pvt *pvt = mci->pvt_info; - struct skx_imc *imc = pvt->imc; - u32 mtr, amap, mcddrtcfg; - struct dimm_info *dimm; - int i, j; - int ndimms; - - for (i = 0; i < NUM_CHANNELS; i++) { - ndimms = 0; - pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap); - pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg); - for (j = 0; j < NUM_DIMMS; j++) { - dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, - mci->n_layers, i, j, 0); - pci_read_config_dword(imc->chan[i].cdev, - 0x80 + 4*j, &mtr); - if (IS_DIMM_PRESENT(mtr)) - ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j); - else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) - ndimms += get_nvdimm_info(dimm, imc, i, j); - } - if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) { - skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc); - return -ENODEV; - } - } - - return 0; -} - -static void skx_unregister_mci(struct skx_imc *imc) -{ - struct mem_ctl_info *mci = imc->mci; - - if (!mci) - return; - - edac_dbg(0, "MC%d: mci = %p\n", imc->mc, mci); - - /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->pdev); - - edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); - kfree(mci->ctl_name); - edac_mc_free(mci); -} - -static int skx_register_mci(struct skx_imc *imc) -{ - struct mem_ctl_info *mci; - struct edac_mc_layer layers[2]; - struct pci_dev *pdev = imc->chan[0].cdev; - struct skx_pvt *pvt; - int rc; - - /* allocate a new MC control structure */ - layers[0].type = EDAC_MC_LAYER_CHANNEL; - layers[0].size = NUM_CHANNELS; - layers[0].is_virt_csrow = false; - layers[1].type = EDAC_MC_LAYER_SLOT; - layers[1].size = NUM_DIMMS; - layers[1].is_virt_csrow = true; - mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers, - sizeof(struct skx_pvt)); - - if (unlikely(!mci)) - return -ENOMEM; - - edac_dbg(0, "MC#%d: mci = %p\n", imc->mc, mci); - - /* Associate skx_dev and mci for future usage */ - imc->mci = mci; - pvt = mci->pvt_info; - pvt->imc = imc; - - mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d", - imc->node_id, imc->lmc); - if (!mci->ctl_name) { - rc = -ENOMEM; - goto fail0; - } - - mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM; - mci->edac_ctl_cap = EDAC_FLAG_NONE; - mci->edac_cap = EDAC_FLAG_NONE; - mci->mod_name = EDAC_MOD_STR; - mci->dev_name = pci_name(imc->chan[0].cdev); - mci->ctl_page_to_phys = NULL; - - rc = skx_get_dimm_config(mci); - if (rc < 0) - goto fail; - - /* record ptr to the generic device */ - mci->pdev = &pdev->dev; - - /* add this new MC control structure to EDAC's list of MCs */ - if (unlikely(edac_mc_add_mc(mci))) { - edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); - rc = -EINVAL; - goto fail; - } - - return 0; - -fail: - kfree(mci->ctl_name); -fail0: - edac_mc_free(mci); - imc->mci = NULL; - return rc; -} - -#define SKX_MAX_SAD 24 - -#define SKX_GET_SAD(d, i, reg) \ - pci_read_config_dword((d)->sad_all, 0x60 + 8 * (i), ®) -#define SKX_GET_ILV(d, i, reg) \ - pci_read_config_dword((d)->sad_all, 0x64 + 8 * (i), ®) - -#define SKX_SAD_MOD3MODE(sad) GET_BITFIELD((sad), 30, 31) -#define SKX_SAD_MOD3(sad) GET_BITFIELD((sad), 27, 27) -#define SKX_SAD_LIMIT(sad) (((u64)GET_BITFIELD((sad), 7, 26) << 26) | MASK26) -#define SKX_SAD_MOD3ASMOD2(sad) GET_BITFIELD((sad), 5, 6) -#define SKX_SAD_ATTR(sad) GET_BITFIELD((sad), 3, 4) -#define SKX_SAD_INTERLEAVE(sad) GET_BITFIELD((sad), 1, 2) -#define SKX_SAD_ENABLE(sad) GET_BITFIELD((sad), 0, 0) - -#define SKX_ILV_REMOTE(tgt) (((tgt) & 8) == 0) -#define SKX_ILV_TARGET(tgt) ((tgt) & 7) - -static bool skx_sad_decode(struct decoded_addr *res) -{ - struct skx_dev *d = list_first_entry(&skx_edac_list, typeof(*d), list); - u64 addr = res->addr; - int i, idx, tgt, lchan, shift; - u32 sad, ilv; - u64 limit, prev_limit; - int remote = 0; - - /* Simple sanity check for I/O space or out of range */ - if (addr >= skx_tohm || (addr >= skx_tolm && addr < BIT_ULL(32))) { - edac_dbg(0, "Address 0x%llx out of range\n", addr); - return false; - } - -restart: - prev_limit = 0; - for (i = 0; i < SKX_MAX_SAD; i++) { - SKX_GET_SAD(d, i, sad); - limit = SKX_SAD_LIMIT(sad); - if (SKX_SAD_ENABLE(sad)) { - if (addr >= prev_limit && addr <= limit) - goto sad_found; - } - prev_limit = limit + 1; - } - edac_dbg(0, "No SAD entry for 0x%llx\n", addr); - return false; - -sad_found: - SKX_GET_ILV(d, i, ilv); - - switch (SKX_SAD_INTERLEAVE(sad)) { - case 0: - idx = GET_BITFIELD(addr, 6, 8); - break; - case 1: - idx = GET_BITFIELD(addr, 8, 10); - break; - case 2: - idx = GET_BITFIELD(addr, 12, 14); - break; - case 3: - idx = GET_BITFIELD(addr, 30, 32); - break; - } - - tgt = GET_BITFIELD(ilv, 4 * idx, 4 * idx + 3); - - /* If point to another node, find it and start over */ - if (SKX_ILV_REMOTE(tgt)) { - if (remote) { - edac_dbg(0, "Double remote!\n"); - return false; - } - remote = 1; - list_for_each_entry(d, &skx_edac_list, list) { - if (d->imc[0].src_id == SKX_ILV_TARGET(tgt)) - goto restart; - } - edac_dbg(0, "Can't find node %d\n", SKX_ILV_TARGET(tgt)); - return false; - } - - if (SKX_SAD_MOD3(sad) == 0) - lchan = SKX_ILV_TARGET(tgt); - else { - switch (SKX_SAD_MOD3MODE(sad)) { - case 0: - shift = 6; - break; - case 1: - shift = 8; - break; - case 2: - shift = 12; - break; - default: - edac_dbg(0, "illegal mod3mode\n"); - return false; - } - switch (SKX_SAD_MOD3ASMOD2(sad)) { - case 0: - lchan = (addr >> shift) % 3; - break; - case 1: - lchan = (addr >> shift) % 2; - break; - case 2: - lchan = (addr >> shift) % 2; - lchan = (lchan << 1) | !lchan; - break; - case 3: - lchan = ((addr >> shift) % 2) << 1; - break; - } - lchan = (lchan << 1) | (SKX_ILV_TARGET(tgt) & 1); - } - - res->dev = d; - res->socket = d->imc[0].src_id; - res->imc = GET_BITFIELD(d->mcroute, lchan * 3, lchan * 3 + 2); - res->channel = GET_BITFIELD(d->mcroute, lchan * 2 + 18, lchan * 2 + 19); - - edac_dbg(2, "0x%llx: socket=%d imc=%d channel=%d\n", - res->addr, res->socket, res->imc, res->channel); - return true; -} - -#define SKX_MAX_TAD 8 - -#define SKX_GET_TADBASE(d, mc, i, reg) \ - pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x850 + 4 * (i), ®) -#define SKX_GET_TADWAYNESS(d, mc, i, reg) \ - pci_read_config_dword((d)->imc[mc].chan[0].cdev, 0x880 + 4 * (i), ®) -#define SKX_GET_TADCHNILVOFFSET(d, mc, ch, i, reg) \ - pci_read_config_dword((d)->imc[mc].chan[ch].cdev, 0x90 + 4 * (i), ®) - -#define SKX_TAD_BASE(b) ((u64)GET_BITFIELD((b), 12, 31) << 26) -#define SKX_TAD_SKT_GRAN(b) GET_BITFIELD((b), 4, 5) -#define SKX_TAD_CHN_GRAN(b) GET_BITFIELD((b), 6, 7) -#define SKX_TAD_LIMIT(b) (((u64)GET_BITFIELD((b), 12, 31) << 26) | MASK26) -#define SKX_TAD_OFFSET(b) ((u64)GET_BITFIELD((b), 4, 23) << 26) -#define SKX_TAD_SKTWAYS(b) (1 << GET_BITFIELD((b), 10, 11)) -#define SKX_TAD_CHNWAYS(b) (GET_BITFIELD((b), 8, 9) + 1) - -/* which bit used for both socket and channel interleave */ -static int skx_granularity[] = { 6, 8, 12, 30 }; - -static u64 skx_do_interleave(u64 addr, int shift, int ways, u64 lowbits) -{ - addr >>= shift; - addr /= ways; - addr <<= shift; - - return addr | (lowbits & ((1ull << shift) - 1)); -} - -static bool skx_tad_decode(struct decoded_addr *res) -{ - int i; - u32 base, wayness, chnilvoffset; - int skt_interleave_bit, chn_interleave_bit; - u64 channel_addr; - - for (i = 0; i < SKX_MAX_TAD; i++) { - SKX_GET_TADBASE(res->dev, res->imc, i, base); - SKX_GET_TADWAYNESS(res->dev, res->imc, i, wayness); - if (SKX_TAD_BASE(base) <= res->addr && res->addr <= SKX_TAD_LIMIT(wayness)) - goto tad_found; - } - edac_dbg(0, "No TAD entry for 0x%llx\n", res->addr); - return false; - -tad_found: - res->sktways = SKX_TAD_SKTWAYS(wayness); - res->chanways = SKX_TAD_CHNWAYS(wayness); - skt_interleave_bit = skx_granularity[SKX_TAD_SKT_GRAN(base)]; - chn_interleave_bit = skx_granularity[SKX_TAD_CHN_GRAN(base)]; - - SKX_GET_TADCHNILVOFFSET(res->dev, res->imc, res->channel, i, chnilvoffset); - channel_addr = res->addr - SKX_TAD_OFFSET(chnilvoffset); - - if (res->chanways == 3 && skt_interleave_bit > chn_interleave_bit) { - /* Must handle channel first, then socket */ - channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit, - res->chanways, channel_addr); - channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit, - res->sktways, channel_addr); - } else { - /* Handle socket then channel. Preserve low bits from original address */ - channel_addr = skx_do_interleave(channel_addr, skt_interleave_bit, - res->sktways, res->addr); - channel_addr = skx_do_interleave(channel_addr, chn_interleave_bit, - res->chanways, res->addr); - } - - res->chan_addr = channel_addr; - - edac_dbg(2, "0x%llx: chan_addr=0x%llx sktways=%d chanways=%d\n", - res->addr, res->chan_addr, res->sktways, res->chanways); - return true; -} - -#define SKX_MAX_RIR 4 - -#define SKX_GET_RIRWAYNESS(d, mc, ch, i, reg) \ - pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \ - 0x108 + 4 * (i), ®) -#define SKX_GET_RIRILV(d, mc, ch, idx, i, reg) \ - pci_read_config_dword((d)->imc[mc].chan[ch].cdev, \ - 0x120 + 16 * idx + 4 * (i), ®) - -#define SKX_RIR_VALID(b) GET_BITFIELD((b), 31, 31) -#define SKX_RIR_LIMIT(b) (((u64)GET_BITFIELD((b), 1, 11) << 29) | MASK29) -#define SKX_RIR_WAYS(b) (1 << GET_BITFIELD((b), 28, 29)) -#define SKX_RIR_CHAN_RANK(b) GET_BITFIELD((b), 16, 19) -#define SKX_RIR_OFFSET(b) ((u64)(GET_BITFIELD((b), 2, 15) << 26)) - -static bool skx_rir_decode(struct decoded_addr *res) -{ - int i, idx, chan_rank; - int shift; - u32 rirway, rirlv; - u64 rank_addr, prev_limit = 0, limit; - - if (res->dev->imc[res->imc].chan[res->channel].dimms[0].close_pg) - shift = 6; - else - shift = 13; - - for (i = 0; i < SKX_MAX_RIR; i++) { - SKX_GET_RIRWAYNESS(res->dev, res->imc, res->channel, i, rirway); - limit = SKX_RIR_LIMIT(rirway); - if (SKX_RIR_VALID(rirway)) { - if (prev_limit <= res->chan_addr && - res->chan_addr <= limit) - goto rir_found; - } - prev_limit = limit; - } - edac_dbg(0, "No RIR entry for 0x%llx\n", res->addr); - return false; - -rir_found: - rank_addr = res->chan_addr >> shift; - rank_addr /= SKX_RIR_WAYS(rirway); - rank_addr <<= shift; - rank_addr |= res->chan_addr & GENMASK_ULL(shift - 1, 0); - - res->rank_address = rank_addr; - idx = (res->chan_addr >> shift) % SKX_RIR_WAYS(rirway); - - SKX_GET_RIRILV(res->dev, res->imc, res->channel, idx, i, rirlv); - res->rank_address = rank_addr - SKX_RIR_OFFSET(rirlv); - chan_rank = SKX_RIR_CHAN_RANK(rirlv); - res->channel_rank = chan_rank; - res->dimm = chan_rank / 4; - res->rank = chan_rank % 4; - - edac_dbg(2, "0x%llx: dimm=%d rank=%d chan_rank=%d rank_addr=0x%llx\n", - res->addr, res->dimm, res->rank, - res->channel_rank, res->rank_address); - return true; -} - -static u8 skx_close_row[] = { - 15, 16, 17, 18, 20, 21, 22, 28, 10, 11, 12, 13, 29, 30, 31, 32, 33 -}; -static u8 skx_close_column[] = { - 3, 4, 5, 14, 19, 23, 24, 25, 26, 27 -}; -static u8 skx_open_row[] = { - 14, 15, 16, 20, 28, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33 -}; -static u8 skx_open_column[] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -}; -static u8 skx_open_fine_column[] = { - 3, 4, 5, 7, 8, 9, 10, 11, 12, 13 -}; - -static int skx_bits(u64 addr, int nbits, u8 *bits) -{ - int i, res = 0; - - for (i = 0; i < nbits; i++) - res |= ((addr >> bits[i]) & 1) << i; - return res; -} - -static int skx_bank_bits(u64 addr, int b0, int b1, int do_xor, int x0, int x1) -{ - int ret = GET_BITFIELD(addr, b0, b0) | (GET_BITFIELD(addr, b1, b1) << 1); - - if (do_xor) - ret ^= GET_BITFIELD(addr, x0, x0) | (GET_BITFIELD(addr, x1, x1) << 1); - - return ret; -} - -static bool skx_mad_decode(struct decoded_addr *r) -{ - struct skx_dimm *dimm = &r->dev->imc[r->imc].chan[r->channel].dimms[r->dimm]; - int bg0 = dimm->fine_grain_bank ? 6 : 13; - - if (dimm->close_pg) { - r->row = skx_bits(r->rank_address, dimm->rowbits, skx_close_row); - r->column = skx_bits(r->rank_address, dimm->colbits, skx_close_column); - r->column |= 0x400; /* C10 is autoprecharge, always set */ - r->bank_address = skx_bank_bits(r->rank_address, 8, 9, dimm->bank_xor_enable, 22, 28); - r->bank_group = skx_bank_bits(r->rank_address, 6, 7, dimm->bank_xor_enable, 20, 21); - } else { - r->row = skx_bits(r->rank_address, dimm->rowbits, skx_open_row); - if (dimm->fine_grain_bank) - r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_fine_column); - else - r->column = skx_bits(r->rank_address, dimm->colbits, skx_open_column); - r->bank_address = skx_bank_bits(r->rank_address, 18, 19, dimm->bank_xor_enable, 22, 23); - r->bank_group = skx_bank_bits(r->rank_address, bg0, 17, dimm->bank_xor_enable, 20, 21); - } - r->row &= (1u << dimm->rowbits) - 1; - - edac_dbg(2, "0x%llx: row=0x%x col=0x%x bank_addr=%d bank_group=%d\n", - r->addr, r->row, r->column, r->bank_address, - r->bank_group); - return true; -} - -static bool skx_decode(struct decoded_addr *res) -{ - - return skx_sad_decode(res) && skx_tad_decode(res) && - skx_rir_decode(res) && skx_mad_decode(res); -} - -static bool skx_adxl_decode(struct decoded_addr *res) - -{ - int i, len = 0; - - if (res->addr >= skx_tohm || (res->addr >= skx_tolm && - res->addr < BIT_ULL(32))) { - edac_dbg(0, "Address 0x%llx out of range\n", res->addr); - return false; - } - - if (adxl_decode(res->addr, adxl_values)) { - edac_dbg(0, "Failed to decode 0x%llx\n", res->addr); - return false; - } - - res->socket = (int)adxl_values[component_indices[INDEX_SOCKET]]; - res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]]; - res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]]; - res->dimm = (int)adxl_values[component_indices[INDEX_DIMM]]; - - for (i = 0; i < adxl_component_count; i++) { - if (adxl_values[i] == ~0x0ull) - continue; - - len += snprintf(adxl_msg + len, MSG_SIZE - len, " %s:0x%llx", - adxl_component_names[i], adxl_values[i]); - if (MSG_SIZE - len <= 0) - break; - } - - return true; -} - -static void skx_mce_output_error(struct mem_ctl_info *mci, - const struct mce *m, - struct decoded_addr *res) -{ - enum hw_event_mc_err_type tp_event; - char *type, *optype; - bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0); - bool overflow = GET_BITFIELD(m->status, 62, 62); - bool uncorrected_error = GET_BITFIELD(m->status, 61, 61); - bool recoverable; - u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52); - u32 mscod = GET_BITFIELD(m->status, 16, 31); - u32 errcode = GET_BITFIELD(m->status, 0, 15); - u32 optypenum = GET_BITFIELD(m->status, 4, 6); - - recoverable = GET_BITFIELD(m->status, 56, 56); - - if (uncorrected_error) { - core_err_cnt = 1; - if (ripv) { - type = "FATAL"; - tp_event = HW_EVENT_ERR_FATAL; - } else { - type = "NON_FATAL"; - tp_event = HW_EVENT_ERR_UNCORRECTED; - } - } else { - type = "CORRECTED"; - tp_event = HW_EVENT_ERR_CORRECTED; - } - - /* - * According with Table 15-9 of the Intel Architecture spec vol 3A, - * memory errors should fit in this mask: - * 000f 0000 1mmm cccc (binary) - * where: - * f = Correction Report Filtering Bit. If 1, subsequent errors - * won't be shown - * mmm = error type - * cccc = channel - * If the mask doesn't match, report an error to the parsing logic - */ - if (!((errcode & 0xef80) == 0x80)) { - optype = "Can't parse: it is not a mem"; - } else { - switch (optypenum) { - case 0: - optype = "generic undef request error"; - break; - case 1: - optype = "memory read error"; - break; - case 2: - optype = "memory write error"; - break; - case 3: - optype = "addr/cmd error"; - break; - case 4: - optype = "memory scrubbing error"; - break; - default: - optype = "reserved"; - break; - } - } - if (adxl_component_count) { - snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", - overflow ? " OVERFLOW" : "", - (uncorrected_error && recoverable) ? " recoverable" : "", - mscod, errcode, adxl_msg); - } else { - snprintf(skx_msg, MSG_SIZE, - "%s%s err_code:0x%04x:0x%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:0x%x col:0x%x", - overflow ? " OVERFLOW" : "", - (uncorrected_error && recoverable) ? " recoverable" : "", - mscod, errcode, - res->socket, res->imc, res->rank, - res->bank_group, res->bank_address, res->row, res->column); - } - - edac_dbg(0, "%s\n", skx_msg); - - /* Call the helper to output message */ - edac_mc_handle_error(tp_event, mci, core_err_cnt, - m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0, - res->channel, res->dimm, -1, - optype, skx_msg); -} - -static struct mem_ctl_info *get_mci(int src_id, int lmc) -{ - struct skx_dev *d; - - if (lmc > NUM_IMC - 1) { - skx_printk(KERN_ERR, "Bad lmc %d\n", lmc); - return NULL; - } - - list_for_each_entry(d, &skx_edac_list, list) { - if (d->imc[0].src_id == src_id) - return d->imc[lmc].mci; - } - - skx_printk(KERN_ERR, "No mci for src_id %d lmc %d\n", src_id, lmc); - - return NULL; -} - -static int skx_mce_check_error(struct notifier_block *nb, unsigned long val, - void *data) -{ - struct mce *mce = (struct mce *)data; - struct decoded_addr res; - struct mem_ctl_info *mci; - char *type; - - if (edac_get_report_status() == EDAC_REPORTING_DISABLED) - return NOTIFY_DONE; - - /* ignore unless this is memory related with an address */ - if ((mce->status & 0xefff) >> 7 != 1 || !(mce->status & MCI_STATUS_ADDRV)) - return NOTIFY_DONE; - - memset(&res, 0, sizeof(res)); - res.addr = mce->addr; - - if (adxl_component_count) { - if (!skx_adxl_decode(&res)) - return NOTIFY_DONE; - - mci = get_mci(res.socket, res.imc); - } else { - if (!skx_decode(&res)) - return NOTIFY_DONE; - - mci = res.dev->imc[res.imc].mci; - } - - if (!mci) - return NOTIFY_DONE; - - if (mce->mcgstatus & MCG_STATUS_MCIP) - type = "Exception"; - else - type = "Event"; - - skx_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n"); - - skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: 0x%llx " - "Bank %d: %016Lx\n", mce->extcpu, type, - mce->mcgstatus, mce->bank, mce->status); - skx_mc_printk(mci, KERN_DEBUG, "TSC 0x%llx ", mce->tsc); - skx_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", mce->addr); - skx_mc_printk(mci, KERN_DEBUG, "MISC 0x%llx ", mce->misc); - - skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:0x%x TIME %llu SOCKET " - "%u APIC 0x%x\n", mce->cpuvendor, mce->cpuid, - mce->time, mce->socketid, mce->apicid); - - skx_mce_output_error(mci, mce, &res); - - return NOTIFY_DONE; -} - -static struct notifier_block skx_mce_dec = { - .notifier_call = skx_mce_check_error, - .priority = MCE_PRIO_EDAC, -}; - -#ifdef CONFIG_EDAC_DEBUG -/* - * Debug feature. - * Exercise the address decode logic by writing an address to - * /sys/kernel/debug/edac/skx_test/addr. - */ -static struct dentry *skx_test; - -static int debugfs_u64_set(void *data, u64 val) -{ - struct mce m; - - pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val); - - memset(&m, 0, sizeof(m)); - /* ADDRV + MemRd + Unknown channel */ - m.status = MCI_STATUS_ADDRV + 0x90; - /* One corrected error */ - m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT); - m.addr = val; - skx_mce_check_error(NULL, 0, &m); - - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); - -static void setup_skx_debug(void) -{ - skx_test = edac_debugfs_create_dir("skx_test"); - if (!skx_test) - return; - - if (!edac_debugfs_create_file("addr", 0200, skx_test, - NULL, &fops_u64_wo)) { - debugfs_remove(skx_test); - skx_test = NULL; - } -} - -static void teardown_skx_debug(void) -{ - debugfs_remove_recursive(skx_test); -} -#else -static void setup_skx_debug(void) {} -static void teardown_skx_debug(void) {} -#endif /*CONFIG_EDAC_DEBUG*/ - -static void skx_remove(void) -{ - int i, j; - struct skx_dev *d, *tmp; - - edac_dbg(0, "\n"); - - list_for_each_entry_safe(d, tmp, &skx_edac_list, list) { - list_del(&d->list); - for (i = 0; i < NUM_IMC; i++) { - skx_unregister_mci(&d->imc[i]); - for (j = 0; j < NUM_CHANNELS; j++) - pci_dev_put(d->imc[i].chan[j].cdev); - } - pci_dev_put(d->util_all); - pci_dev_put(d->sad_all); - - kfree(d); - } -} - -static void __init skx_adxl_get(void) -{ - const char * const *names; - int i, j; - - names = adxl_get_component_names(); - if (!names) { - skx_printk(KERN_NOTICE, "No firmware support for address translation."); - skx_printk(KERN_CONT, " Only decoding DDR4 address!\n"); - return; - } - - for (i = 0; i < INDEX_MAX; i++) { - for (j = 0; names[j]; j++) { - if (!strcmp(component_names[i], names[j])) { - component_indices[i] = j; - break; - } - } - - if (!names[j]) - goto err; - } - - adxl_component_names = names; - while (*names++) - adxl_component_count++; - - adxl_values = kcalloc(adxl_component_count, sizeof(*adxl_values), - GFP_KERNEL); - if (!adxl_values) { - adxl_component_count = 0; - return; - } - - adxl_msg = kzalloc(MSG_SIZE, GFP_KERNEL); - if (!adxl_msg) { - adxl_component_count = 0; - kfree(adxl_values); - } - - return; -err: - skx_printk(KERN_ERR, "'%s' is not matched from DSM parameters: ", - component_names[i]); - for (j = 0; names[j]; j++) - skx_printk(KERN_CONT, "%s ", names[j]); - skx_printk(KERN_CONT, "\n"); -} - -static void __exit skx_adxl_put(void) -{ - kfree(adxl_values); - kfree(adxl_msg); -} - -/* - * skx_init: - * make sure we are running on the correct cpu model - * search for all the devices we need - * check which DIMMs are present. - */ -static int __init skx_init(void) -{ - const struct x86_cpu_id *id; - const struct munit *m; - const char *owner; - int rc = 0, i; - u8 mc = 0, src_id, node_id; - struct skx_dev *d; - - edac_dbg(2, "\n"); - - owner = edac_get_owner(); - if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) - return -EBUSY; - - id = x86_match_cpu(skx_cpuids); - if (!id) - return -ENODEV; - - rc = skx_get_hi_lo(); - if (rc) - return rc; - - rc = get_all_bus_mappings(); - if (rc < 0) - goto fail; - if (rc == 0) { - edac_dbg(2, "No memory controllers found\n"); - return -ENODEV; - } - - for (m = skx_all_munits; m->did; m++) { - rc = get_all_munits(m); - if (rc < 0) - goto fail; - if (rc != m->per_socket * skx_num_sockets) { - edac_dbg(2, "Expected %d, got %d of 0x%x\n", - m->per_socket * skx_num_sockets, rc, m->did); - rc = -ENODEV; - goto fail; - } - } - - list_for_each_entry(d, &skx_edac_list, list) { - src_id = get_src_id(d); - node_id = skx_get_node_id(d); - edac_dbg(2, "src_id=%d node_id=%d\n", src_id, node_id); - for (i = 0; i < NUM_IMC; i++) { - d->imc[i].mc = mc++; - d->imc[i].lmc = i; - d->imc[i].src_id = src_id; - d->imc[i].node_id = node_id; - rc = skx_register_mci(&d->imc[i]); - if (rc < 0) - goto fail; - } - } - - skx_msg = kzalloc(MSG_SIZE, GFP_KERNEL); - if (!skx_msg) { - rc = -ENOMEM; - goto fail; - } - - if (nvdimm_count) - skx_adxl_get(); - - /* Ensure that the OPSTATE is set correctly for POLL or NMI */ - opstate_init(); - - setup_skx_debug(); - - mce_register_decode_chain(&skx_mce_dec); - - return 0; -fail: - skx_remove(); - return rc; -} - -static void __exit skx_exit(void) -{ - edac_dbg(2, "\n"); - mce_unregister_decode_chain(&skx_mce_dec); - teardown_skx_debug(); - if (nvdimm_count) - skx_adxl_put(); - kfree(skx_msg); - skx_remove(); -} - -module_init(skx_init); -module_exit(skx_exit); - -module_param(edac_op_state, int, 0444); -MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Tony Luck"); -MODULE_DESCRIPTION("MC Driver for Intel Skylake server processors"); From d4dc89d069aab9074e2493a4c2f3969a0a0b91c1 Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Wed, 30 Jan 2019 11:15:19 -0800 Subject: [PATCH 0980/4277] EDAC, i10nm: Add a driver for Intel 10nm server processors This driver supports the Intel 10nm series server integrated memory controller. It gets the memory capacity and topology information by reading the registers in PCI configuration space and memory-mapped I/O. It decodes the memory error address to the platform specific address by using the ACPI Address Translation (ADXL) Device Specific Method (DSM). Co-developed-by: Tony Luck Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov Cc: James Morse Cc: Mauro Carvalho Chehab Cc: linux-edac Link: https://lkml.kernel.org/r/20190130191519.15393-5-tony.luck@intel.com --- drivers/edac/Kconfig | 12 ++ drivers/edac/Makefile | 3 + drivers/edac/i10nm_base.c | 275 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 drivers/edac/i10nm_base.c diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 480fa7926d06..0825c9c26d2b 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -241,6 +241,18 @@ config EDAC_SKX system has non-volatile DIMMs you should also manually select CONFIG_ACPI_NFIT. +config EDAC_I10NM + tristate "Intel 10nm server Integrated MC" + depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG + depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_I10NM can't be y + select DMI + select ACPI_ADXL if ACPI + help + Support for error detection and correction the Intel + 10nm server Integrated Memory Controllers. If your + system has non-volatile DIMMs you should also manually + select CONFIG_ACPI_NFIT. + config EDAC_PND2 tristate "Intel Pondicherry2" depends on PCI && X86_64 && X86_MCE_INTEL diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 1c985fa2dcdc..89ad4a84a0f6 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -60,6 +60,9 @@ obj-$(CONFIG_EDAC_LAYERSCAPE) += layerscape_edac_mod.o skx_edac-y := skx_common.o skx_base.o obj-$(CONFIG_EDAC_SKX) += skx_edac.o +i10nm_edac-y := skx_common.o i10nm_base.o +obj-$(CONFIG_EDAC_I10NM) += i10nm_edac.o + obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o obj-$(CONFIG_EDAC_CELL) += cell_edac.o obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c new file mode 100644 index 000000000000..c334fb7c63df --- /dev/null +++ b/drivers/edac/i10nm_base.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Intel(R) 10nm server memory controller. + * Copyright (c) 2019, Intel Corporation. + * + */ + +#include +#include +#include +#include +#include "edac_module.h" +#include "skx_common.h" + +#define I10NM_REVISION "v0.0.3" +#define EDAC_MOD_STR "i10nm_edac" + +/* Debug macros */ +#define i10nm_printk(level, fmt, arg...) \ + edac_printk(level, "i10nm", fmt, ##arg) + +#define I10NM_GET_SCK_BAR(d, reg) \ + pci_read_config_dword((d)->uracu, 0xd0, &(reg)) +#define I10NM_GET_IMC_BAR(d, i, reg) \ + pci_read_config_dword((d)->uracu, 0xd8 + (i) * 4, &(reg)) +#define I10NM_GET_DIMMMTR(m, i, j) \ + (*(u32 *)((m)->mbase + 0x2080c + (i) * 0x4000 + (j) * 4)) +#define I10NM_GET_MCDDRTCFG(m, i, j) \ + (*(u32 *)((m)->mbase + 0x20970 + (i) * 0x4000 + (j) * 4)) + +#define I10NM_GET_SCK_MMIO_BASE(reg) (GET_BITFIELD(reg, 0, 28) << 23) +#define I10NM_GET_IMC_MMIO_OFFSET(reg) (GET_BITFIELD(reg, 0, 10) << 12) +#define I10NM_GET_IMC_MMIO_SIZE(reg) ((GET_BITFIELD(reg, 13, 23) - \ + GET_BITFIELD(reg, 0, 10) + 1) << 12) + +static struct list_head *i10nm_edac_list; + +static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus, + unsigned int dev, unsigned int fun) +{ + struct pci_dev *pdev; + + pdev = pci_get_domain_bus_and_slot(dom, bus, PCI_DEVFN(dev, fun)); + if (!pdev) { + edac_dbg(2, "No device %02x:%02x.%x\n", + bus, dev, fun); + return NULL; + } + + if (unlikely(pci_enable_device(pdev) < 0)) { + edac_dbg(2, "Failed to enable device %02x:%02x.%x\n", + bus, dev, fun); + return NULL; + } + + pci_dev_get(pdev); + + return pdev; +} + +static int i10nm_get_all_munits(void) +{ + struct pci_dev *mdev; + void __iomem *mbase; + unsigned long size; + struct skx_dev *d; + int i, j = 0; + u32 reg, off; + u64 base; + + list_for_each_entry(d, i10nm_edac_list, list) { + d->util_all = pci_get_dev_wrapper(d->seg, d->bus[1], 29, 1); + if (!d->util_all) + return -ENODEV; + + d->uracu = pci_get_dev_wrapper(d->seg, d->bus[0], 0, 1); + if (!d->uracu) + return -ENODEV; + + if (I10NM_GET_SCK_BAR(d, reg)) { + i10nm_printk(KERN_ERR, "Failed to socket bar\n"); + return -ENODEV; + } + + base = I10NM_GET_SCK_MMIO_BASE(reg); + edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n", + j++, base, reg); + + for (i = 0; i < I10NM_NUM_IMC; i++) { + mdev = pci_get_dev_wrapper(d->seg, d->bus[0], + 12 + i, 0); + if (i == 0 && !mdev) { + i10nm_printk(KERN_ERR, "No IMC found\n"); + return -ENODEV; + } + if (!mdev) + continue; + + d->imc[i].mdev = mdev; + + if (I10NM_GET_IMC_BAR(d, i, reg)) { + i10nm_printk(KERN_ERR, "Failed to get mc bar\n"); + return -ENODEV; + } + + off = I10NM_GET_IMC_MMIO_OFFSET(reg); + size = I10NM_GET_IMC_MMIO_SIZE(reg); + edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n", + i, base + off, size, reg); + + mbase = ioremap(base + off, size); + if (!mbase) { + i10nm_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", + base + off); + return -ENODEV; + } + + d->imc[i].mbase = mbase; + } + } + + return 0; +} + +static const struct x86_cpu_id i10nm_cpuids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_TREMONT_X, 0, 0 }, + { } +}; +MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids); + +static bool i10nm_check_ecc(struct skx_imc *imc, int chan) +{ + u32 mcmtr; + + mcmtr = *(u32 *)(imc->mbase + 0x20ef8 + chan * 0x4000); + edac_dbg(1, "ch%d mcmtr reg %x\n", chan, mcmtr); + + return !!GET_BITFIELD(mcmtr, 2, 2); +} + +static int i10nm_get_dimm_config(struct mem_ctl_info *mci) +{ + struct skx_pvt *pvt = mci->pvt_info; + struct skx_imc *imc = pvt->imc; + struct dimm_info *dimm; + u32 mtr, mcddrtcfg; + int i, j, ndimms; + + for (i = 0; i < I10NM_NUM_CHANNELS; i++) { + if (!imc->mbase) + continue; + + ndimms = 0; + for (j = 0; j < I10NM_NUM_DIMMS; j++) { + dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, + mci->n_layers, i, j, 0); + mtr = I10NM_GET_DIMMMTR(imc, i, j); + mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i, j); + edac_dbg(1, "dimmmtr 0x%x mcddrtcfg 0x%x (mc%d ch%d dimm%d)\n", + mtr, mcddrtcfg, imc->mc, i, j); + + if (IS_DIMM_PRESENT(mtr)) + ndimms += skx_get_dimm_info(mtr, 0, dimm, + imc, i, j); + else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) + ndimms += skx_get_nvdimm_info(dimm, imc, i, j, + EDAC_MOD_STR); + } + if (ndimms && !i10nm_check_ecc(imc, 0)) { + i10nm_printk(KERN_ERR, "ECC is disabled on imc %d\n", + imc->mc); + return -ENODEV; + } + } + + return 0; +} + +static struct notifier_block i10nm_mce_dec = { + .notifier_call = skx_mce_check_error, + .priority = MCE_PRIO_EDAC, +}; + +static int __init i10nm_init(void) +{ + u8 mc = 0, src_id = 0, node_id = 0; + const struct x86_cpu_id *id; + const char *owner; + struct skx_dev *d; + int rc, i, off[3] = {0xd0, 0xc8, 0xcc}; + u64 tolm, tohm; + + edac_dbg(2, "\n"); + + owner = edac_get_owner(); + if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) + return -EBUSY; + + id = x86_match_cpu(i10nm_cpuids); + if (!id) + return -ENODEV; + + rc = skx_get_hi_lo(0x09a2, off, &tolm, &tohm); + if (rc) + return rc; + + rc = skx_get_all_bus_mappings(0x3452, 0xcc, I10NM, &i10nm_edac_list); + if (rc < 0) + goto fail; + if (rc == 0) { + i10nm_printk(KERN_ERR, "No memory controllers found\n"); + return -ENODEV; + } + + rc = i10nm_get_all_munits(); + if (rc < 0) + goto fail; + + list_for_each_entry(d, i10nm_edac_list, list) { + rc = skx_get_src_id(d, &src_id); + if (rc < 0) + goto fail; + + rc = skx_get_node_id(d, &node_id); + if (rc < 0) + goto fail; + + edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id); + for (i = 0; i < I10NM_NUM_IMC; i++) { + if (!d->imc[i].mdev) + continue; + + d->imc[i].mc = mc++; + d->imc[i].lmc = i; + d->imc[i].src_id = src_id; + d->imc[i].node_id = node_id; + + rc = skx_register_mci(&d->imc[i], d->imc[i].mdev, + "Intel_10nm Socket", EDAC_MOD_STR, + i10nm_get_dimm_config); + if (rc < 0) + goto fail; + } + } + + rc = skx_adxl_get(); + if (rc) + goto fail; + + opstate_init(); + mce_register_decode_chain(&i10nm_mce_dec); + setup_skx_debug("i10nm_test"); + + i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION); + + return 0; +fail: + skx_remove(); + return rc; +} + +static void __exit i10nm_exit(void) +{ + edac_dbg(2, "\n"); + teardown_skx_debug(); + mce_unregister_decode_chain(&i10nm_mce_dec); + skx_adxl_put(); + skx_remove(); +} + +module_init(i10nm_init); +module_exit(i10nm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MC Driver for Intel 10nm server processors"); From 671cdde36e26feb509e5cc25c0ca273907eaf914 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 17 Jan 2019 20:49:42 +0530 Subject: [PATCH 0981/4277] watchdog: qcom: Add suspend/resume support This adds the support for qcom watchdog suspend and resume when entering and exiting deep sleep states. Otherwise having watchdog active after suspend would result in unwanted crashes/resets if resume happens after a long time. Signed-off-by: Sai Prakash Ranjan Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/qcom-wdt.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 780971318810..5dfd604477a4 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -245,6 +245,28 @@ static int qcom_wdt_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused qcom_wdt_suspend(struct device *dev) +{ + struct qcom_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + qcom_wdt_stop(&wdt->wdd); + + return 0; +} + +static int __maybe_unused qcom_wdt_resume(struct device *dev) +{ + struct qcom_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + qcom_wdt_start(&wdt->wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(qcom_wdt_pm_ops, qcom_wdt_suspend, qcom_wdt_resume); + static const struct of_device_id qcom_wdt_of_table[] = { { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr }, { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr }, @@ -259,6 +281,7 @@ static struct platform_driver qcom_watchdog_driver = { .driver = { .name = KBUILD_MODNAME, .of_match_table = qcom_wdt_of_table, + .pm = &qcom_wdt_pm_ops, }, }; module_platform_driver(qcom_watchdog_driver); From 59600d045ff411da193a8b5a2999681801ecd430 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Fri, 26 Oct 2018 09:55:53 +0100 Subject: [PATCH 0982/4277] dt-bindings: watchdog: renesas-wdt: Document r8a77470 support RZ/G1C (R8A77470) watchdog implementation is compatible with R-Car Gen2, therefore add relevant documentation. Signed-off-by: Biju Das Reviewed-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Rob Herring Reviewed-by: Geert Uytterhoeven Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/renesas-wdt.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt b/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt index ef2b97b72e08..9f365c1a3399 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt @@ -8,6 +8,7 @@ Required properties: - "renesas,r8a7743-wdt" (RZ/G1M) - "renesas,r8a7744-wdt" (RZ/G1N) - "renesas,r8a7745-wdt" (RZ/G1E) + - "renesas,r8a77470-wdt" (RZ/G1C) - "renesas,r8a774a1-wdt" (RZ/G2M) - "renesas,r8a774c0-wdt" (RZ/G2E) - "renesas,r8a7790-wdt" (R-Car H2) From 12aa377bf80cb3a38053dba70eaa7e39975849b2 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 1 Feb 2019 13:58:38 +0100 Subject: [PATCH 0983/4277] clk: export some clk_hw function symbols for module drivers clk-provider.h provides clk_hw_is_prepared(), clk_hw_is_enabled() and clk_hw_is_prepared() but these symbols are not exported for the modules which prevents a clock driver using them to be compiled as a module. Export them to fix the problem. Acked-by: Stephen Boyd Signed-off-by: Jerome Brunet Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201125841.26785-2-jbrunet@baylibre.com --- drivers/clk/clk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 75d13c0eff12..5558af12f9db 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -394,16 +394,19 @@ bool clk_hw_is_prepared(const struct clk_hw *hw) { return clk_core_is_prepared(hw->core); } +EXPORT_SYMBOL_GPL(clk_hw_is_prepared); bool clk_hw_rate_is_protected(const struct clk_hw *hw) { return clk_core_rate_is_protected(hw->core); } +EXPORT_SYMBOL_GPL(clk_hw_rate_is_protected); bool clk_hw_is_enabled(const struct clk_hw *hw) { return clk_core_is_enabled(hw->core); } +EXPORT_SYMBOL_GPL(clk_hw_is_enabled); bool __clk_is_enabled(struct clk *clk) { From 6e47b5307308d9d48d4990406e8322332634591c Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 1 Feb 2019 13:58:39 +0100 Subject: [PATCH 0984/4277] clk: meson: use CONFIG_ARCH_MESON to enter meson clk directory Use CONFIG_ARCH_MESON to let make enter the meson clock directory. As part of a rework, CONFIG_COMMON_CLK_AMLOGIC is about to be removed. Acked-by: Stephen Boyd Signed-off-by: Jerome Brunet Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201125841.26785-3-jbrunet@baylibre.com --- drivers/clk/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 8a9440a97500..8d780c4390f1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -78,7 +78,7 @@ obj-$(CONFIG_ARCH_K3) += keystone/ obj-$(CONFIG_ARCH_KEYSTONE) += keystone/ obj-$(CONFIG_MACH_LOONGSON32) += loongson1/ obj-y += mediatek/ -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += meson/ +obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_MACH_PIC32) += microchip/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ From cb78ba76296efd145c50d943017d76a36544bb76 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 1 Feb 2019 13:58:40 +0100 Subject: [PATCH 0985/4277] clk: meson: axg-audio does not require syscon The axg audio clock controller uses regmap mmio, not syscon. Fixes: 1cd50181750f ("clk: meson: axg: add the audio clock controller driver") Signed-off-by: Jerome Brunet Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201125841.26785-4-jbrunet@baylibre.com --- drivers/clk/meson/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index efaa70f682b4..2479dab09d70 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -50,7 +50,7 @@ config COMMON_CLK_AXG_AUDIO tristate "Meson AXG Audio Clock Controller Driver" depends on COMMON_CLK_AXG select COMMON_CLK_AMLOGIC_AUDIO - select MFD_SYSCON + select REGMAP_MMIO help Support for the audio clock controller on AmLogic A113D devices, aka axg, Say Y if you want audio subsystem to work. From 889c2b7ec42b8d14d421541f0a3ae1238e34891e Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 1 Feb 2019 13:58:41 +0100 Subject: [PATCH 0986/4277] clk: meson: rework and clean drivers dependencies Initially, the meson clock directory only hosted 2 controllers drivers, for meson8 and gxbb. At the time, both used the same set of clock drivers so managing the dependencies was not a big concern. Since this ancient time, entropy did its job, controllers with different requirement and specific clock drivers have been added. Unfortunately, we did not do a great job at managing the dependencies between the controllers and the different clock drivers. Some drivers, such as clk-phase or vid-pll-div, are compiled even if they are useless on the target (meson8). As we are adding new controllers, we need to be able to pick a driver w/o pulling the whole thing. The patch aims to clean things up by: * providing a dedicated CONFIG_ for each clock drivers * allowing clock drivers to be compiled as a modules, if possible * stating explicitly which drivers are required by each controller. Signed-off-by: Jerome Brunet Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com --- drivers/clk/meson/Kconfig | 84 +++++++--- drivers/clk/meson/Makefile | 27 ++-- drivers/clk/meson/axg-aoclk.c | 4 +- drivers/clk/meson/axg-audio.c | 5 +- drivers/clk/meson/axg.c | 5 +- drivers/clk/meson/clk-dualdiv.c | 10 +- drivers/clk/meson/clk-dualdiv.h | 33 ++++ drivers/clk/meson/clk-input.c | 7 +- drivers/clk/meson/clk-input.h | 19 +++ drivers/clk/meson/clk-mpll.c | 12 +- drivers/clk/meson/clk-mpll.h | 30 ++++ drivers/clk/meson/clk-phase.c | 75 ++++++++- drivers/clk/meson/clk-phase.h | 26 ++++ drivers/clk/meson/clk-pll.c | 13 +- drivers/clk/meson/clk-pll.h | 43 ++++++ drivers/clk/meson/clk-regmap.c | 5 + drivers/clk/meson/clk-regmap.h | 15 ++ drivers/clk/meson/clk-triphase.c | 68 -------- drivers/clk/meson/clkc.h | 146 ------------------ drivers/clk/meson/gxbb-aoclk.c | 4 +- drivers/clk/meson/gxbb.c | 5 +- drivers/clk/meson/meson-aoclk.c | 2 + drivers/clk/meson/meson-aoclk.h | 5 +- drivers/clk/meson/meson8b.c | 3 +- drivers/clk/meson/parm.h | 46 ++++++ drivers/clk/meson/sclk-div.c | 10 +- .../clk/meson/{clkc-audio.h => sclk-div.h} | 16 +- drivers/clk/meson/vid-pll-div.c | 10 +- drivers/clk/meson/vid-pll-div.h | 20 +++ 29 files changed, 466 insertions(+), 282 deletions(-) create mode 100644 drivers/clk/meson/clk-dualdiv.h create mode 100644 drivers/clk/meson/clk-input.h create mode 100644 drivers/clk/meson/clk-mpll.h create mode 100644 drivers/clk/meson/clk-phase.h create mode 100644 drivers/clk/meson/clk-pll.h delete mode 100644 drivers/clk/meson/clk-triphase.c delete mode 100644 drivers/clk/meson/clkc.h create mode 100644 drivers/clk/meson/parm.h rename drivers/clk/meson/{clkc-audio.h => sclk-div.h} (54%) create mode 100644 drivers/clk/meson/vid-pll-div.h diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 2479dab09d70..f2e757aea4f1 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -1,27 +1,47 @@ -config COMMON_CLK_AMLOGIC - bool - depends on ARCH_MESON || COMPILE_TEST - select COMMON_CLK_REGMAP_MESON +config COMMON_CLK_MESON_INPUT + tristate -config COMMON_CLK_AMLOGIC_AUDIO - bool - depends on ARCH_MESON || COMPILE_TEST - select COMMON_CLK_AMLOGIC - -config COMMON_CLK_MESON_AO - bool - depends on OF - depends on ARCH_MESON || COMPILE_TEST - select COMMON_CLK_REGMAP_MESON - select RESET_CONTROLLER - -config COMMON_CLK_REGMAP_MESON - bool +config COMMON_CLK_MESON_REGMAP + tristate select REGMAP +config COMMON_CLK_MESON_DUALDIV + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_MPLL + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_PHASE + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_PLL + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_SCLK_DIV + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_VID_PLL_DIV + tristate + select COMMON_CLK_MESON_REGMAP + +config COMMON_CLK_MESON_AO_CLKC + tristate + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_INPUT + select RESET_CONTROLLER + config COMMON_CLK_MESON8B bool - select COMMON_CLK_AMLOGIC + depends on ARCH_MESON + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select MFD_SYSCON select RESET_CONTROLLER help Support for the clock controller on AmLogic S802 (Meson8), @@ -30,8 +50,14 @@ config COMMON_CLK_MESON8B config COMMON_CLK_GXBB bool - select COMMON_CLK_AMLOGIC - select COMMON_CLK_MESON_AO + depends on ARCH_MESON + select COMMON_CLK_MESON_INPUT + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_DUALDIV + select COMMON_CLK_MESON_VID_PLL_DIV + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_AO_CLKC select MFD_SYSCON help Support for the clock controller on AmLogic S905 devices, aka gxbb. @@ -39,8 +65,13 @@ config COMMON_CLK_GXBB config COMMON_CLK_AXG bool - select COMMON_CLK_AMLOGIC - select COMMON_CLK_MESON_AO + depends on ARCH_MESON + select COMMON_CLK_MESON_INPUT + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_DUALDIV + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_AO_CLKC select MFD_SYSCON help Support for the clock controller on AmLogic A113D devices, aka axg. @@ -48,8 +79,11 @@ config COMMON_CLK_AXG config COMMON_CLK_AXG_AUDIO tristate "Meson AXG Audio Clock Controller Driver" - depends on COMMON_CLK_AXG - select COMMON_CLK_AMLOGIC_AUDIO + depends on ARCH_MESON + select COMMON_CLK_MESON_INPUT + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_PHASE + select COMMON_CLK_MESON_SCLK_DIV select REGMAP_MMIO help Support for the audio clock controller on AmLogic A113D devices, diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 8234e92eea38..e339d8f58488 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -1,13 +1,18 @@ -# -# Makefile for Meson specific clk -# +# Amlogic clock drivers -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o clk-dualdiv.o -obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o -obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o +obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o +obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o +obj-$(CONFIG_COMMON_CLK_MESON_INPUT) += clk-input.o +obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o +obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o +obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o +obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o +obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o +obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o + +# Amlogic Clock controllers + +obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o +obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o -obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o -obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o -obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o diff --git a/drivers/clk/meson/axg-aoclk.c b/drivers/clk/meson/axg-aoclk.c index 5701f5840b75..0086f31288eb 100644 --- a/drivers/clk/meson/axg-aoclk.c +++ b/drivers/clk/meson/axg-aoclk.c @@ -12,10 +12,12 @@ #include #include #include -#include "clkc.h" #include "meson-aoclk.h" #include "axg-aoclk.h" +#include "clk-regmap.h" +#include "clk-dualdiv.h" + #define IN_PREFIX "ao-in-" /* diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index 8ac3a2295473..7ab200b6c3bf 100644 --- a/drivers/clk/meson/axg-audio.c +++ b/drivers/clk/meson/axg-audio.c @@ -14,8 +14,11 @@ #include #include -#include "clkc-audio.h" #include "axg-audio.h" +#include "clk-input.h" +#include "clk-regmap.h" +#include "clk-phase.h" +#include "sclk-div.h" #define AXG_MST_IN_COUNT 8 #define AXG_SLV_SCLK_COUNT 10 diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 1c6539eeee7c..cbbdd93b175d 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -17,7 +17,10 @@ #include #include -#include "clkc.h" +#include "clk-input.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-mpll.h" #include "axg.h" #define IN_PREFIX "ee-in-" diff --git a/drivers/clk/meson/clk-dualdiv.c b/drivers/clk/meson/clk-dualdiv.c index 4d9e161de627..c5ca23a5e3e8 100644 --- a/drivers/clk/meson/clk-dualdiv.c +++ b/drivers/clk/meson/clk-dualdiv.c @@ -22,7 +22,10 @@ */ #include -#include "clkc.h" +#include + +#include "clk-regmap.h" +#include "clk-dualdiv.h" static inline struct meson_clk_dualdiv_data * meson_clk_dualdiv_data(struct clk_regmap *clk) @@ -128,3 +131,8 @@ const struct clk_ops meson_clk_dualdiv_ro_ops = { .recalc_rate = meson_clk_dualdiv_recalc_rate, }; EXPORT_SYMBOL_GPL(meson_clk_dualdiv_ro_ops); + +MODULE_DESCRIPTION("Amlogic dual divider driver"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-dualdiv.h b/drivers/clk/meson/clk-dualdiv.h new file mode 100644 index 000000000000..4aa939018012 --- /dev/null +++ b/drivers/clk/meson/clk-dualdiv.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_CLK_DUALDIV_H +#define __MESON_CLK_DUALDIV_H + +#include +#include "parm.h" + +struct meson_clk_dualdiv_param { + unsigned int n1; + unsigned int n2; + unsigned int m1; + unsigned int m2; + unsigned int dual; +}; + +struct meson_clk_dualdiv_data { + struct parm n1; + struct parm n2; + struct parm m1; + struct parm m2; + struct parm dual; + const struct meson_clk_dualdiv_param *table; +}; + +extern const struct clk_ops meson_clk_dualdiv_ops; +extern const struct clk_ops meson_clk_dualdiv_ro_ops; + +#endif /* __MESON_CLK_DUALDIV_H */ diff --git a/drivers/clk/meson/clk-input.c b/drivers/clk/meson/clk-input.c index 06b3e3bb6a66..086226e9dba6 100644 --- a/drivers/clk/meson/clk-input.c +++ b/drivers/clk/meson/clk-input.c @@ -7,7 +7,8 @@ #include #include #include -#include "clkc.h" +#include +#include "clk-input.h" static const struct clk_ops meson_clk_no_ops = {}; @@ -42,3 +43,7 @@ struct clk_hw *meson_clk_hw_register_input(struct device *dev, return ret ? ERR_PTR(ret) : hw; } EXPORT_SYMBOL_GPL(meson_clk_hw_register_input); + +MODULE_DESCRIPTION("Amlogic clock input helper"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-input.h b/drivers/clk/meson/clk-input.h new file mode 100644 index 000000000000..4a541b9685a6 --- /dev/null +++ b/drivers/clk/meson/clk-input.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_CLK_INPUT_H +#define __MESON_CLK_INPUT_H + +#include + +struct device; + +struct clk_hw *meson_clk_hw_register_input(struct device *dev, + const char *of_name, + const char *clk_name, + unsigned long flags); + +#endif /* __MESON_CLK_INPUT_H */ diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c index 650f75cc15a9..f76850d99e59 100644 --- a/drivers/clk/meson/clk-mpll.c +++ b/drivers/clk/meson/clk-mpll.c @@ -12,7 +12,11 @@ */ #include -#include "clkc.h" +#include +#include + +#include "clk-regmap.h" +#include "clk-mpll.h" #define SDM_DEN 16384 #define N2_MIN 4 @@ -138,9 +142,15 @@ const struct clk_ops meson_clk_mpll_ro_ops = { .recalc_rate = mpll_recalc_rate, .round_rate = mpll_round_rate, }; +EXPORT_SYMBOL_GPL(meson_clk_mpll_ro_ops); const struct clk_ops meson_clk_mpll_ops = { .recalc_rate = mpll_recalc_rate, .round_rate = mpll_round_rate, .set_rate = mpll_set_rate, }; +EXPORT_SYMBOL_GPL(meson_clk_mpll_ops); + +MODULE_DESCRIPTION("Amlogic MPLL driver"); +MODULE_AUTHOR("Michael Turquette "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-mpll.h b/drivers/clk/meson/clk-mpll.h new file mode 100644 index 000000000000..cf79340006dd --- /dev/null +++ b/drivers/clk/meson/clk-mpll.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_CLK_MPLL_H +#define __MESON_CLK_MPLL_H + +#include +#include + +#include "parm.h" + +struct meson_clk_mpll_data { + struct parm sdm; + struct parm sdm_en; + struct parm n2; + struct parm ssen; + struct parm misc; + spinlock_t *lock; + u8 flags; +}; + +#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0) + +extern const struct clk_ops meson_clk_mpll_ro_ops; +extern const struct clk_ops meson_clk_mpll_ops; + +#endif /* __MESON_CLK_MPLL_H */ diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c index cba43748ce3d..80c3ada193a4 100644 --- a/drivers/clk/meson/clk-phase.c +++ b/drivers/clk/meson/clk-phase.c @@ -5,7 +5,10 @@ */ #include -#include "clkc.h" +#include + +#include "clk-regmap.h" +#include "clk-phase.h" #define phase_step(_width) (360 / (1 << (_width))) @@ -15,13 +18,12 @@ meson_clk_phase_data(struct clk_regmap *clk) return (struct meson_clk_phase_data *)clk->data; } -int meson_clk_degrees_from_val(unsigned int val, unsigned int width) +static int meson_clk_degrees_from_val(unsigned int val, unsigned int width) { return phase_step(width) * val; } -EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val); -unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) +static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) { unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); @@ -31,7 +33,6 @@ unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) */ return val % (1 << width); } -EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val); static int meson_clk_phase_get_phase(struct clk_hw *hw) { @@ -61,3 +62,67 @@ const struct clk_ops meson_clk_phase_ops = { .set_phase = meson_clk_phase_set_phase, }; EXPORT_SYMBOL_GPL(meson_clk_phase_ops); + +/* + * This is a special clock for the audio controller. + * The phase of mst_sclk clock output can be controlled independently + * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2). + * Controlling these 3 phases as just one makes things simpler and + * give the same clock view to all the element on the i2s bus. + * If necessary, we can still control the phase in the tdm block + * which makes these independent control redundant. + */ +static inline struct meson_clk_triphase_data * +meson_clk_triphase_data(struct clk_regmap *clk) +{ + return (struct meson_clk_triphase_data *)clk->data; +} + +static void meson_clk_triphase_sync(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + /* Get phase 0 and sync it to phase 1 and 2 */ + val = meson_parm_read(clk->map, &tph->ph0); + meson_parm_write(clk->map, &tph->ph1, val); + meson_parm_write(clk->map, &tph->ph2, val); +} + +static int meson_clk_triphase_get_phase(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + /* Phase are in sync, reading phase 0 is enough */ + val = meson_parm_read(clk->map, &tph->ph0); + + return meson_clk_degrees_from_val(val, tph->ph0.width); +} + +static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + val = meson_clk_degrees_to_val(degrees, tph->ph0.width); + meson_parm_write(clk->map, &tph->ph0, val); + meson_parm_write(clk->map, &tph->ph1, val); + meson_parm_write(clk->map, &tph->ph2, val); + + return 0; +} + +const struct clk_ops meson_clk_triphase_ops = { + .init = meson_clk_triphase_sync, + .get_phase = meson_clk_triphase_get_phase, + .set_phase = meson_clk_triphase_set_phase, +}; +EXPORT_SYMBOL_GPL(meson_clk_triphase_ops); + +MODULE_DESCRIPTION("Amlogic phase driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-phase.h b/drivers/clk/meson/clk-phase.h new file mode 100644 index 000000000000..5579f9ced142 --- /dev/null +++ b/drivers/clk/meson/clk-phase.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_CLK_PHASE_H +#define __MESON_CLK_PHASE_H + +#include +#include "parm.h" + +struct meson_clk_phase_data { + struct parm ph; +}; + +struct meson_clk_triphase_data { + struct parm ph0; + struct parm ph1; + struct parm ph2; +}; + +extern const struct clk_ops meson_clk_phase_ops; +extern const struct clk_ops meson_clk_triphase_ops; + +#endif /* __MESON_CLK_PHASE_H */ diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index afffc1547e20..4a8c68ae8801 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -32,11 +32,9 @@ #include #include #include -#include -#include -#include -#include "clkc.h" +#include "clk-regmap.h" +#include "clk-pll.h" static inline struct meson_clk_pll_data * meson_clk_pll_data(struct clk_regmap *clk) @@ -309,8 +307,15 @@ const struct clk_ops meson_clk_pll_ops = { .enable = meson_clk_pll_enable, .disable = meson_clk_pll_disable }; +EXPORT_SYMBOL_GPL(meson_clk_pll_ops); const struct clk_ops meson_clk_pll_ro_ops = { .recalc_rate = meson_clk_pll_recalc_rate, .is_enabled = meson_clk_pll_is_enabled, }; +EXPORT_SYMBOL_GPL(meson_clk_pll_ro_ops); + +MODULE_DESCRIPTION("Amlogic PLL driver"); +MODULE_AUTHOR("Carlo Caione "); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h new file mode 100644 index 000000000000..5ccf0854d932 --- /dev/null +++ b/drivers/clk/meson/clk-pll.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_CLK_PLL_H +#define __MESON_CLK_PLL_H + +#include +#include +#include "parm.h" + +struct pll_params_table { + u16 m; + u16 n; +}; + +#define PLL_PARAMS(_m, _n) \ + { \ + .m = (_m), \ + .n = (_n), \ + } + +#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0) + +struct meson_clk_pll_data { + struct parm en; + struct parm m; + struct parm n; + struct parm frac; + struct parm l; + struct parm rst; + const struct reg_sequence *init_regs; + unsigned int init_count; + const struct pll_params_table *table; + u8 flags; +}; + +extern const struct clk_ops meson_clk_pll_ro_ops; +extern const struct clk_ops meson_clk_pll_ops; + +#endif /* __MESON_CLK_PLL_H */ diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c index c515f67322a3..dcd1757cc5df 100644 --- a/drivers/clk/meson/clk-regmap.c +++ b/drivers/clk/meson/clk-regmap.c @@ -4,6 +4,7 @@ * Author: Jerome Brunet */ +#include #include "clk-regmap.h" static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) @@ -180,3 +181,7 @@ const struct clk_ops clk_regmap_mux_ro_ops = { .get_parent = clk_regmap_mux_get_parent, }; EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); + +MODULE_DESCRIPTION("Amlogic regmap backed clock driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h index e9c5728d40eb..b7a085bbf072 100644 --- a/drivers/clk/meson/clk-regmap.h +++ b/drivers/clk/meson/clk-regmap.h @@ -111,4 +111,19 @@ clk_get_regmap_mux_data(struct clk_regmap *clk) extern const struct clk_ops clk_regmap_mux_ops; extern const struct clk_ops clk_regmap_mux_ro_ops; +#define MESON_GATE(_name, _reg, _bit) \ +struct clk_regmap _name = { \ + .data = &(struct clk_regmap_gate_data){ \ + .offset = (_reg), \ + .bit_idx = (_bit), \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name, \ + .ops = &clk_regmap_gate_ops, \ + .parent_names = (const char *[]){ "clk81" }, \ + .num_parents = 1, \ + .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ + }, \ +} + #endif /* __CLK_REGMAP_H */ diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c deleted file mode 100644 index 4a59936251e5..000000000000 --- a/drivers/clk/meson/clk-triphase.c +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* - * Copyright (c) 2018 BayLibre, SAS. - * Author: Jerome Brunet - */ - -#include -#include "clkc-audio.h" - -/* - * This is a special clock for the audio controller. - * The phase of mst_sclk clock output can be controlled independently - * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2). - * Controlling these 3 phases as just one makes things simpler and - * give the same clock view to all the element on the i2s bus. - * If necessary, we can still control the phase in the tdm block - * which makes these independent control redundant. - */ -static inline struct meson_clk_triphase_data * -meson_clk_triphase_data(struct clk_regmap *clk) -{ - return (struct meson_clk_triphase_data *)clk->data; -} - -static void meson_clk_triphase_sync(struct clk_hw *hw) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); - unsigned int val; - - /* Get phase 0 and sync it to phase 1 and 2 */ - val = meson_parm_read(clk->map, &tph->ph0); - meson_parm_write(clk->map, &tph->ph1, val); - meson_parm_write(clk->map, &tph->ph2, val); -} - -static int meson_clk_triphase_get_phase(struct clk_hw *hw) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); - unsigned int val; - - /* Phase are in sync, reading phase 0 is enough */ - val = meson_parm_read(clk->map, &tph->ph0); - - return meson_clk_degrees_from_val(val, tph->ph0.width); -} - -static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); - unsigned int val; - - val = meson_clk_degrees_to_val(degrees, tph->ph0.width); - meson_parm_write(clk->map, &tph->ph0, val); - meson_parm_write(clk->map, &tph->ph1, val); - meson_parm_write(clk->map, &tph->ph2, val); - - return 0; -} - -const struct clk_ops meson_clk_triphase_ops = { - .init = meson_clk_triphase_sync, - .get_phase = meson_clk_triphase_get_phase, - .set_phase = meson_clk_triphase_set_phase, -}; -EXPORT_SYMBOL_GPL(meson_clk_triphase_ops); diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h deleted file mode 100644 index e3cd442db739..000000000000 --- a/drivers/clk/meson/clkc.h +++ /dev/null @@ -1,146 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2015 Endless Mobile, Inc. - * Author: Carlo Caione - */ - -#ifndef __CLKC_H -#define __CLKC_H - -#include -#include "clk-regmap.h" - -#define PMASK(width) GENMASK(width - 1, 0) -#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) -#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) - -#define PARM_GET(width, shift, reg) \ - (((reg) & SETPMASK(width, shift)) >> (shift)) -#define PARM_SET(width, shift, reg, val) \ - (((reg) & CLRPMASK(width, shift)) | ((val) << (shift))) - -#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) - -struct parm { - u16 reg_off; - u8 shift; - u8 width; -}; - -static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p) -{ - unsigned int val; - - regmap_read(map, p->reg_off, &val); - return PARM_GET(p->width, p->shift, val); -} - -static inline void meson_parm_write(struct regmap *map, struct parm *p, - unsigned int val) -{ - regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift), - val << p->shift); -} - - -struct pll_params_table { - u16 m; - u16 n; -}; - -#define PLL_PARAMS(_m, _n) \ - { \ - .m = (_m), \ - .n = (_n), \ - } - -#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0) - -struct meson_clk_pll_data { - struct parm en; - struct parm m; - struct parm n; - struct parm frac; - struct parm l; - struct parm rst; - const struct reg_sequence *init_regs; - unsigned int init_count; - const struct pll_params_table *table; - u8 flags; -}; - -#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) - -struct meson_clk_mpll_data { - struct parm sdm; - struct parm sdm_en; - struct parm n2; - struct parm ssen; - struct parm misc; - spinlock_t *lock; - u8 flags; -}; - -#define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0) - -struct meson_clk_phase_data { - struct parm ph; -}; - -int meson_clk_degrees_from_val(unsigned int val, unsigned int width); -unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width); - -struct meson_vid_pll_div_data { - struct parm val; - struct parm sel; -}; - -#define MESON_GATE(_name, _reg, _bit) \ -struct clk_regmap _name = { \ - .data = &(struct clk_regmap_gate_data){ \ - .offset = (_reg), \ - .bit_idx = (_bit), \ - }, \ - .hw.init = &(struct clk_init_data) { \ - .name = #_name, \ - .ops = &clk_regmap_gate_ops, \ - .parent_names = (const char *[]){ "clk81" }, \ - .num_parents = 1, \ - .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ - }, \ -}; - -struct meson_clk_dualdiv_param { - unsigned int n1; - unsigned int n2; - unsigned int m1; - unsigned int m2; - unsigned int dual; -}; - -struct meson_clk_dualdiv_data { - struct parm n1; - struct parm n2; - struct parm m1; - struct parm m2; - struct parm dual; - const struct meson_clk_dualdiv_param *table; -}; - -/* clk_ops */ -extern const struct clk_ops meson_clk_pll_ro_ops; -extern const struct clk_ops meson_clk_pll_ops; -extern const struct clk_ops meson_clk_cpu_ops; -extern const struct clk_ops meson_clk_mpll_ro_ops; -extern const struct clk_ops meson_clk_mpll_ops; -extern const struct clk_ops meson_clk_phase_ops; -extern const struct clk_ops meson_vid_pll_div_ro_ops; -extern const struct clk_ops meson_clk_dualdiv_ops; -extern const struct clk_ops meson_clk_dualdiv_ro_ops; - -struct clk_hw *meson_clk_hw_register_input(struct device *dev, - const char *of_name, - const char *clk_name, - unsigned long flags); - -#endif /* __CLKC_H */ diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index 510b6a7d2f18..449f6ac189d8 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -5,10 +5,12 @@ */ #include #include -#include "clkc.h" #include "meson-aoclk.h" #include "gxbb-aoclk.h" +#include "clk-regmap.h" +#include "clk-dualdiv.h" + #define IN_PREFIX "ao-in-" /* AO Configuration Clock registers offsets */ diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index ecf9a8f6281c..3741db9a9fe1 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -12,9 +12,12 @@ #include #include -#include "clkc.h" #include "gxbb.h" +#include "clk-input.h" #include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-mpll.h" +#include "vid-pll-div.h" #define IN_PREFIX "ee-in-" diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index 7b9d194ccc3b..b67951909e04 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -17,6 +17,8 @@ #include #include "meson-aoclk.h" +#include "clk-input.h" + static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev, unsigned long id) { diff --git a/drivers/clk/meson/meson-aoclk.h b/drivers/clk/meson/meson-aoclk.h index 0758b35d4427..999cde3868f7 100644 --- a/drivers/clk/meson/meson-aoclk.h +++ b/drivers/clk/meson/meson-aoclk.h @@ -11,9 +11,12 @@ #ifndef __MESON_AOCLK_H__ #define __MESON_AOCLK_H__ +#include #include +#include #include -#include "clkc.h" + +#include "clk-regmap.h" struct meson_aoclk_input { const char *name; diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 748552c5f6c8..23b1e355a849 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -16,9 +16,10 @@ #include #include -#include "clkc.h" #include "meson8b.h" #include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-mpll.h" static DEFINE_SPINLOCK(meson_clk_lock); diff --git a/drivers/clk/meson/parm.h b/drivers/clk/meson/parm.h new file mode 100644 index 000000000000..3c9ef1b505ce --- /dev/null +++ b/drivers/clk/meson/parm.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + */ + +#ifndef __MESON_PARM_H +#define __MESON_PARM_H + +#include +#include + +#define PMASK(width) GENMASK(width - 1, 0) +#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) +#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) + +#define PARM_GET(width, shift, reg) \ + (((reg) & SETPMASK(width, shift)) >> (shift)) +#define PARM_SET(width, shift, reg, val) \ + (((reg) & CLRPMASK(width, shift)) | ((val) << (shift))) + +#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) + +struct parm { + u16 reg_off; + u8 shift; + u8 width; +}; + +static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p) +{ + unsigned int val; + + regmap_read(map, p->reg_off, &val); + return PARM_GET(p->width, p->shift, val); +} + +static inline void meson_parm_write(struct regmap *map, struct parm *p, + unsigned int val) +{ + regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift), + val << p->shift); +} + +#endif /* __MESON_PARM_H */ + diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c index bc64019b8eeb..3acf03780221 100644 --- a/drivers/clk/meson/sclk-div.c +++ b/drivers/clk/meson/sclk-div.c @@ -16,7 +16,11 @@ * duty_cycle = (1 + hi) / (1 + val) */ -#include "clkc-audio.h" +#include +#include + +#include "clk-regmap.h" +#include "sclk-div.h" static inline struct meson_sclk_div_data * meson_sclk_div_data(struct clk_regmap *clk) @@ -241,3 +245,7 @@ const struct clk_ops meson_sclk_div_ops = { .init = sclk_div_init, }; EXPORT_SYMBOL_GPL(meson_sclk_div_ops); + +MODULE_DESCRIPTION("Amlogic Sample divider driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/sclk-div.h similarity index 54% rename from drivers/clk/meson/clkc-audio.h rename to drivers/clk/meson/sclk-div.h index 0a7c157ebf81..b64b2a32005f 100644 --- a/drivers/clk/meson/clkc-audio.h +++ b/drivers/clk/meson/sclk-div.h @@ -4,16 +4,11 @@ * Author: Jerome Brunet */ -#ifndef __MESON_CLKC_AUDIO_H -#define __MESON_CLKC_AUDIO_H +#ifndef __MESON_SCLK_DIV_H +#define __MESON_SCLK_DIV_H -#include "clkc.h" - -struct meson_clk_triphase_data { - struct parm ph0; - struct parm ph1; - struct parm ph2; -}; +#include +#include "parm.h" struct meson_sclk_div_data { struct parm div; @@ -22,7 +17,6 @@ struct meson_sclk_div_data { struct clk_duty cached_duty; }; -extern const struct clk_ops meson_clk_triphase_ops; extern const struct clk_ops meson_sclk_div_ops; -#endif /* __MESON_CLKC_AUDIO_H */ +#endif /* __MESON_SCLK_DIV_H */ diff --git a/drivers/clk/meson/vid-pll-div.c b/drivers/clk/meson/vid-pll-div.c index 88af0e282ea0..08bcc01c0923 100644 --- a/drivers/clk/meson/vid-pll-div.c +++ b/drivers/clk/meson/vid-pll-div.c @@ -5,7 +5,10 @@ */ #include -#include "clkc.h" +#include + +#include "clk-regmap.h" +#include "vid-pll-div.h" static inline struct meson_vid_pll_div_data * meson_vid_pll_div_data(struct clk_regmap *clk) @@ -89,3 +92,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw, const struct clk_ops meson_vid_pll_div_ro_ops = { .recalc_rate = meson_vid_pll_div_recalc_rate, }; +EXPORT_SYMBOL_GPL(meson_vid_pll_div_ro_ops); + +MODULE_DESCRIPTION("Amlogic video pll divider driver"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/vid-pll-div.h b/drivers/clk/meson/vid-pll-div.h new file mode 100644 index 000000000000..c0128e33ccf9 --- /dev/null +++ b/drivers/clk/meson/vid-pll-div.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_VID_PLL_DIV_H +#define __MESON_VID_PLL_DIV_H + +#include +#include "parm.h" + +struct meson_vid_pll_div_data { + struct parm val; + struct parm sel; +}; + +extern const struct clk_ops meson_vid_pll_div_ro_ops; + +#endif /* __MESON_VID_PLL_DIV_H */ From d5d30d5a5c60628de5e77e3f292a8f9012d51350 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 2 Feb 2019 16:35:26 -0800 Subject: [PATCH 0987/4277] libnvdimm/dimm: Add a no-BLK quirk based on NVDIMM family As Dexuan reports the NVDIMM_FAMILY_HYPERV platform is incompatible with the existing Linux namespace implementation because it uses NSLABEL_FLAG_LOCAL for x1-width PMEM interleave sets. Quirk it as an platform / DIMM that does not provide BLK-aperture access. Allow the libnvdimm core to assume no potential for aliasing. In case other implementations make the same mistake, provide a "noblk" module parameter to force-enable the quirk. Link: https://lkml.kernel.org/r/PU1P153MB0169977604493B82B662A01CBF920@PU1P153MB0169.APCP153.PROD.OUTLOOK.COM Reported-by: Dexuan Cui Tested-by: Dexuan Cui Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 4 ++++ drivers/nvdimm/dimm_devs.c | 7 +++++++ drivers/nvdimm/label.c | 3 +++ drivers/nvdimm/namespace_devs.c | 6 ++++++ drivers/nvdimm/region_devs.c | 7 +++++++ include/linux/libnvdimm.h | 2 ++ 6 files changed, 29 insertions(+) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 4a7e8b1fa43b..811c399a3a76 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2016,6 +2016,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK; } + /* Quirk to ignore LOCAL for labels on HYPERV DIMMs */ + if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) + set_bit(NDD_NOBLK, &flags); + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) { set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask); set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask); diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 4890310df874..553aa78abeee 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -11,6 +11,7 @@ * General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -25,6 +26,10 @@ static DEFINE_IDA(dimm_ida); +static bool noblk; +module_param(noblk, bool, 0444); +MODULE_PARM_DESC(noblk, "force disable BLK / local alias support"); + /* * Retrieve bus and dimm handle and return if this bus supports * get_config_data commands @@ -551,6 +556,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, nvdimm->dimm_id = dimm_id; nvdimm->provider_data = provider_data; + if (noblk) + flags |= 1 << NDD_NOBLK; nvdimm->flags = flags; nvdimm->cmd_mask = cmd_mask; nvdimm->num_flush = num_flush; diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 6d6e9a12150b..f3d753d3169c 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -392,6 +392,7 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) return 0; /* no label, nothing to reserve */ for_each_clear_bit_le(slot, free, nslot) { + struct nvdimm *nvdimm = to_nvdimm(ndd->dev); struct nd_namespace_label *nd_label; struct nd_region *nd_region = NULL; u8 label_uuid[NSLABEL_UUID_LEN]; @@ -406,6 +407,8 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); flags = __le32_to_cpu(nd_label->flags); + if (test_bit(NDD_NOBLK, &nvdimm->flags)) + flags &= ~NSLABEL_FLAG_LOCAL; nd_label_gen_id(&label_id, label_uuid, flags); res = nvdimm_allocate_dpa(ndd, &label_id, __le64_to_cpu(nd_label->dpa), diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 4b077555ac70..3677b0c4a33d 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -2492,6 +2492,12 @@ static int init_active_labels(struct nd_region *nd_region) if (!label_ent) break; label = nd_label_active(ndd, j); + if (test_bit(NDD_NOBLK, &nvdimm->flags)) { + u32 flags = __le32_to_cpu(label->flags); + + flags &= ~NSLABEL_FLAG_LOCAL; + label->flags = __cpu_to_le32(flags); + } label_ent->label = label; mutex_lock(&nd_mapping->lock); diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index e2818f94f292..3b58baa44b5c 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -1003,6 +1003,13 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, if (test_bit(NDD_UNARMED, &nvdimm->flags)) ro = 1; + + if (test_bit(NDD_NOBLK, &nvdimm->flags) + && dev_type == &nd_blk_device_type) { + dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not BLK capable\n", + caller, dev_name(&nvdimm->dev), i); + return NULL; + } } if (dev_type == &nd_blk_device_type) { diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 5440f11b0907..7da406ae3a2b 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -42,6 +42,8 @@ enum { NDD_SECURITY_OVERWRITE = 3, /* tracking whether or not there is a pending device reference */ NDD_WORK_PENDING = 4, + /* ignore / filter NSLABEL_FLAG_LOCAL for this DIMM, i.e. no aliasing */ + NDD_NOBLK = 5, /* need to set a limit somewhere, but yes, this is likely overkill */ ND_IOCTL_MAX_BUFLEN = SZ_4M, From cbfa447edd6a3825fdb8a4ffae74ff7208f2d2c0 Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Fri, 1 Feb 2019 22:55:51 +0000 Subject: [PATCH 0988/4277] x86/MCE/AMD, EDAC/mce_amd: Add new MP5, NBIO, and PCIE SMCA bank types Add the (HWID, MCATYPE) tuples and names for the new MP5, NBIO, and PCIE SMCA bank types. Also, add their respective error descriptions to the MCE decoding module edac_mce_amd. Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: Arnd Bergmann Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Kees Cook Cc: linux-edac Cc: Mauro Carvalho Chehab Cc: Pu Wen Cc: Qiuxu Zhuo Cc: Shirish S Cc: Thomas Gleixner Cc: Tony Luck Cc: Vishal Verma Cc: x86-ml Link: https://lkml.kernel.org/r/20190201225534.8177-2-Yazen.Ghannam@amd.com --- arch/x86/include/asm/mce.h | 3 +++ arch/x86/kernel/cpu/mce/amd.c | 12 ++++++++++++ drivers/edac/mce_amd.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index c1a812bd5a27..91b65d859ca8 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -312,6 +312,9 @@ enum smca_bank_types { SMCA_PB, /* Parameter Block */ SMCA_PSP, /* Platform Security Processor */ SMCA_SMU, /* System Management Unit */ + SMCA_MP5, /* Microprocessor 5 Unit */ + SMCA_NBIO, /* Northbridge IO Unit */ + SMCA_PCIE, /* PCI Express Unit */ N_SMCA_BANK_TYPES }; diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index ed3327342b40..00f60b8c7e4f 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -93,6 +93,9 @@ static struct smca_bank_name smca_names[] = { [SMCA_PB] = { "param_block", "Parameter Block" }, [SMCA_PSP] = { "psp", "Platform Security Processor" }, [SMCA_SMU] = { "smu", "System Management Unit" }, + [SMCA_MP5] = { "mp5", "Microprocessor 5 Unit" }, + [SMCA_NBIO] = { "nbio", "Northbridge IO Unit" }, + [SMCA_PCIE] = { "pcie", "PCI Express Unit" }, }; static u32 smca_bank_addrs[MAX_NR_BANKS][NR_BLOCKS] __ro_after_init = @@ -162,6 +165,15 @@ static struct smca_hwid smca_hwid_mcatypes[] = { /* System Management Unit MCA type */ { SMCA_SMU, HWID_MCATYPE(0x01, 0x0), 0x1 }, + + /* Microprocessor 5 Unit MCA type */ + { SMCA_MP5, HWID_MCATYPE(0x01, 0x2), 0x3FF }, + + /* Northbridge IO Unit MCA type */ + { SMCA_NBIO, HWID_MCATYPE(0x18, 0x0), 0x1F }, + + /* PCI Express Unit MCA type */ + { SMCA_PCIE, HWID_MCATYPE(0x46, 0x0), 0x1F }, }; struct smca_bank smca_banks[MAX_NR_BANKS]; diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index c605089d899f..5ab4ab3f0ce6 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -285,6 +285,35 @@ static const char * const smca_smu_mce_desc[] = { "SMU RAM ECC or parity error", }; +static const char * const smca_mp5_mce_desc[] = { + "High SRAM ECC or parity error", + "Low SRAM ECC or parity error", + "Data Cache Bank A ECC or parity error", + "Data Cache Bank B ECC or parity error", + "Data Tag Cache Bank A ECC or parity error", + "Data Tag Cache Bank B ECC or parity error", + "Instruction Cache Bank A ECC or parity error", + "Instruction Cache Bank B ECC or parity error", + "Instruction Tag Cache Bank A ECC or parity error", + "Instruction Tag Cache Bank B ECC or parity error", +}; + +static const char * const smca_nbio_mce_desc[] = { + "ECC or Parity error", + "PCIE error", + "SDP ErrEvent error", + "SDP Egress Poison Error", + "IOHC Internal Poison Error", +}; + +static const char * const smca_pcie_mce_desc[] = { + "CCIX PER Message logging", + "CCIX Read Response with Status: Non-Data Error", + "CCIX Write Response with Status: Non-Data Error", + "CCIX Read Response with Status: Data Error", + "CCIX Non-okay write response with data error", +}; + struct smca_mce_desc { const char * const *descs; unsigned int num_descs; @@ -304,6 +333,9 @@ static struct smca_mce_desc smca_mce_descs[] = { [SMCA_PB] = { smca_pb_mce_desc, ARRAY_SIZE(smca_pb_mce_desc) }, [SMCA_PSP] = { smca_psp_mce_desc, ARRAY_SIZE(smca_psp_mce_desc) }, [SMCA_SMU] = { smca_smu_mce_desc, ARRAY_SIZE(smca_smu_mce_desc) }, + [SMCA_MP5] = { smca_mp5_mce_desc, ARRAY_SIZE(smca_mp5_mce_desc) }, + [SMCA_NBIO] = { smca_nbio_mce_desc, ARRAY_SIZE(smca_nbio_mce_desc) }, + [SMCA_PCIE] = { smca_pcie_mce_desc, ARRAY_SIZE(smca_pcie_mce_desc) }, }; static bool f12h_mc0_mce(u16 ec, u8 xec) From 3ad7e748c12cc771df6020a552def3e1727e8a17 Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Fri, 1 Feb 2019 22:55:52 +0000 Subject: [PATCH 0989/4277] x86/MCE/AMD, EDAC/mce_amd: Add new McaTypes for CS, PSP, and SMU units The existing CS, PSP, and SMU SMCA bank types will see new versions (as indicated by their McaTypes) in future SMCA systems. Add the new (HWID, MCATYPE) tuples for these new versions. Reuse the same names as the older versions, since they are logically the same to the user. SMCA systems won't mix and match IP blocks with different McaType versions in the same system, so there isn't a need to distinguish them. The MCA_IPID register is saved when logging an MCA error, and that can be used to triage the error. Also, add the new error descriptions to edac_mce_amd. Some error types (positions in the list) are overloaded compared to the previous McaTypes. Therefore, just create new lists of the error descriptions to keep things simple even if some of the error descriptions are the same between versions. Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: Arnd Bergmann Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Kees Cook Cc: linux-edac Cc: Mauro Carvalho Chehab Cc: Pu Wen Cc: Qiuxu Zhuo Cc: Shirish S Cc: Thomas Gleixner Cc: Tony Luck Cc: Vishal Verma Cc: x86-ml Link: https://lkml.kernel.org/r/20190201225534.8177-3-Yazen.Ghannam@amd.com --- arch/x86/include/asm/mce.h | 3 ++ arch/x86/kernel/cpu/mce/amd.c | 6 ++++ drivers/edac/mce_amd.c | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 91b65d859ca8..299a38536567 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -307,11 +307,14 @@ enum smca_bank_types { SMCA_FP, /* Floating Point */ SMCA_L3_CACHE, /* L3 Cache */ SMCA_CS, /* Coherent Slave */ + SMCA_CS_V2, /* Coherent Slave */ SMCA_PIE, /* Power, Interrupts, etc. */ SMCA_UMC, /* Unified Memory Controller */ SMCA_PB, /* Parameter Block */ SMCA_PSP, /* Platform Security Processor */ + SMCA_PSP_V2, /* Platform Security Processor */ SMCA_SMU, /* System Management Unit */ + SMCA_SMU_V2, /* System Management Unit */ SMCA_MP5, /* Microprocessor 5 Unit */ SMCA_NBIO, /* Northbridge IO Unit */ SMCA_PCIE, /* PCI Express Unit */ diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index 00f60b8c7e4f..bd1331b241ca 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -88,11 +88,14 @@ static struct smca_bank_name smca_names[] = { [SMCA_FP] = { "floating_point", "Floating Point Unit" }, [SMCA_L3_CACHE] = { "l3_cache", "L3 Cache" }, [SMCA_CS] = { "coherent_slave", "Coherent Slave" }, + [SMCA_CS_V2] = { "coherent_slave", "Coherent Slave" }, [SMCA_PIE] = { "pie", "Power, Interrupts, etc." }, [SMCA_UMC] = { "umc", "Unified Memory Controller" }, [SMCA_PB] = { "param_block", "Parameter Block" }, [SMCA_PSP] = { "psp", "Platform Security Processor" }, + [SMCA_PSP_V2] = { "psp", "Platform Security Processor" }, [SMCA_SMU] = { "smu", "System Management Unit" }, + [SMCA_SMU_V2] = { "smu", "System Management Unit" }, [SMCA_MP5] = { "mp5", "Microprocessor 5 Unit" }, [SMCA_NBIO] = { "nbio", "Northbridge IO Unit" }, [SMCA_PCIE] = { "pcie", "PCI Express Unit" }, @@ -153,6 +156,7 @@ static struct smca_hwid smca_hwid_mcatypes[] = { /* Data Fabric MCA types */ { SMCA_CS, HWID_MCATYPE(0x2E, 0x0), 0x1FF }, { SMCA_PIE, HWID_MCATYPE(0x2E, 0x1), 0xF }, + { SMCA_CS_V2, HWID_MCATYPE(0x2E, 0x2), 0x3FFF }, /* Unified Memory Controller MCA type */ { SMCA_UMC, HWID_MCATYPE(0x96, 0x0), 0x3F }, @@ -162,9 +166,11 @@ static struct smca_hwid smca_hwid_mcatypes[] = { /* Platform Security Processor MCA type */ { SMCA_PSP, HWID_MCATYPE(0xFF, 0x0), 0x1 }, + { SMCA_PSP_V2, HWID_MCATYPE(0xFF, 0x1), 0x3FFFF }, /* System Management Unit MCA type */ { SMCA_SMU, HWID_MCATYPE(0x01, 0x0), 0x1 }, + { SMCA_SMU_V2, HWID_MCATYPE(0x01, 0x1), 0x7FF }, /* Microprocessor 5 Unit MCA type */ { SMCA_MP5, HWID_MCATYPE(0x01, 0x2), 0x3FF }, diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 5ab4ab3f0ce6..184c90172d17 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -257,6 +257,23 @@ static const char * const smca_cs_mce_desc[] = { "ECC error on probe filter access", }; +static const char * const smca_cs2_mce_desc[] = { + "Illegal Request", + "Address Violation", + "Security Violation", + "Illegal Response", + "Unexpected Response", + "Request or Probe Parity Error", + "Read Response Parity Error", + "Atomic Request Parity Error", + "SDP read response had no match in the CS queue", + "Probe Filter Protocol Error", + "Probe Filter ECC Error", + "SDP read response had an unexpected RETRY error", + "Counter overflow error", + "Counter underflow error", +}; + static const char * const smca_pie_mce_desc[] = { "HW assert", "Internal PIE register security violation", @@ -281,10 +298,45 @@ static const char * const smca_psp_mce_desc[] = { "PSP RAM ECC or parity error", }; +static const char * const smca_psp2_mce_desc[] = { + "High SRAM ECC or parity error", + "Low SRAM ECC or parity error", + "Instruction Cache Bank 0 ECC or parity error", + "Instruction Cache Bank 1 ECC or parity error", + "Instruction Tag Ram 0 parity error", + "Instruction Tag Ram 1 parity error", + "Data Cache Bank 0 ECC or parity error", + "Data Cache Bank 1 ECC or parity error", + "Data Cache Bank 2 ECC or parity error", + "Data Cache Bank 3 ECC or parity error", + "Data Tag Bank 0 parity error", + "Data Tag Bank 1 parity error", + "Data Tag Bank 2 parity error", + "Data Tag Bank 3 parity error", + "Dirty Data Ram parity error", + "TLB Bank 0 parity error", + "TLB Bank 1 parity error", + "System Hub Read Buffer ECC or parity error", +}; + static const char * const smca_smu_mce_desc[] = { "SMU RAM ECC or parity error", }; +static const char * const smca_smu2_mce_desc[] = { + "High SRAM ECC or parity error", + "Low SRAM ECC or parity error", + "Data Cache Bank A ECC or parity error", + "Data Cache Bank B ECC or parity error", + "Data Tag Cache Bank A ECC or parity error", + "Data Tag Cache Bank B ECC or parity error", + "Instruction Cache Bank A ECC or parity error", + "Instruction Cache Bank B ECC or parity error", + "Instruction Tag Cache Bank A ECC or parity error", + "Instruction Tag Cache Bank B ECC or parity error", + "System Hub Read Buffer ECC or parity error", +}; + static const char * const smca_mp5_mce_desc[] = { "High SRAM ECC or parity error", "Low SRAM ECC or parity error", @@ -328,11 +380,14 @@ static struct smca_mce_desc smca_mce_descs[] = { [SMCA_FP] = { smca_fp_mce_desc, ARRAY_SIZE(smca_fp_mce_desc) }, [SMCA_L3_CACHE] = { smca_l3_mce_desc, ARRAY_SIZE(smca_l3_mce_desc) }, [SMCA_CS] = { smca_cs_mce_desc, ARRAY_SIZE(smca_cs_mce_desc) }, + [SMCA_CS_V2] = { smca_cs2_mce_desc, ARRAY_SIZE(smca_cs2_mce_desc) }, [SMCA_PIE] = { smca_pie_mce_desc, ARRAY_SIZE(smca_pie_mce_desc) }, [SMCA_UMC] = { smca_umc_mce_desc, ARRAY_SIZE(smca_umc_mce_desc) }, [SMCA_PB] = { smca_pb_mce_desc, ARRAY_SIZE(smca_pb_mce_desc) }, [SMCA_PSP] = { smca_psp_mce_desc, ARRAY_SIZE(smca_psp_mce_desc) }, + [SMCA_PSP_V2] = { smca_psp2_mce_desc, ARRAY_SIZE(smca_psp2_mce_desc) }, [SMCA_SMU] = { smca_smu_mce_desc, ARRAY_SIZE(smca_smu_mce_desc) }, + [SMCA_SMU_V2] = { smca_smu2_mce_desc, ARRAY_SIZE(smca_smu2_mce_desc) }, [SMCA_MP5] = { smca_mp5_mce_desc, ARRAY_SIZE(smca_mp5_mce_desc) }, [SMCA_NBIO] = { smca_nbio_mce_desc, ARRAY_SIZE(smca_nbio_mce_desc) }, [SMCA_PCIE] = { smca_pcie_mce_desc, ARRAY_SIZE(smca_pcie_mce_desc) }, From 8a5dd2cd2f2e94878cacc969655a69ca214795ab Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Fri, 1 Feb 2019 22:55:52 +0000 Subject: [PATCH 0990/4277] x86/MCE/AMD, EDAC/mce_amd: Add new error descriptions for some SMCA bank types Some SMCA bank types on future systems will report new error types even though the bank type is not treated as a new version. These new error types will reported by bits that are reserved in past systems. Add the new error descriptions to the lists in edac_mce_amd. Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Kees Cook Cc: linux-edac Cc: Mauro Carvalho Chehab Cc: Shirish S Cc: Thomas Gleixner Cc: Tony Luck Cc: x86-ml Link: https://lkml.kernel.org/r/20190201225534.8177-4-Yazen.Ghannam@amd.com --- arch/x86/kernel/cpu/mce/amd.c | 8 ++++---- drivers/edac/mce_amd.c | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index bd1331b241ca..e64de5149e50 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -144,22 +144,22 @@ static struct smca_hwid smca_hwid_mcatypes[] = { { SMCA_RESERVED, HWID_MCATYPE(0x00, 0x0), 0x0 }, /* ZN Core (HWID=0xB0) MCA types */ - { SMCA_LS, HWID_MCATYPE(0xB0, 0x0), 0x1FFFEF }, + { SMCA_LS, HWID_MCATYPE(0xB0, 0x0), 0x1FFFFF }, { SMCA_IF, HWID_MCATYPE(0xB0, 0x1), 0x3FFF }, { SMCA_L2_CACHE, HWID_MCATYPE(0xB0, 0x2), 0xF }, { SMCA_DE, HWID_MCATYPE(0xB0, 0x3), 0x1FF }, /* HWID 0xB0 MCATYPE 0x4 is Reserved */ - { SMCA_EX, HWID_MCATYPE(0xB0, 0x5), 0x7FF }, + { SMCA_EX, HWID_MCATYPE(0xB0, 0x5), 0xFFF }, { SMCA_FP, HWID_MCATYPE(0xB0, 0x6), 0x7F }, { SMCA_L3_CACHE, HWID_MCATYPE(0xB0, 0x7), 0xFF }, /* Data Fabric MCA types */ { SMCA_CS, HWID_MCATYPE(0x2E, 0x0), 0x1FF }, - { SMCA_PIE, HWID_MCATYPE(0x2E, 0x1), 0xF }, + { SMCA_PIE, HWID_MCATYPE(0x2E, 0x1), 0x1F }, { SMCA_CS_V2, HWID_MCATYPE(0x2E, 0x2), 0x3FFF }, /* Unified Memory Controller MCA type */ - { SMCA_UMC, HWID_MCATYPE(0x96, 0x0), 0x3F }, + { SMCA_UMC, HWID_MCATYPE(0x96, 0x0), 0xFF }, /* Parameter Block MCA type */ { SMCA_PB, HWID_MCATYPE(0x05, 0x0), 0x1 }, diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 184c90172d17..c79e650aa606 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -155,7 +155,7 @@ static const char * const smca_ls_mce_desc[] = { "Store queue parity", "Miss address buffer payload parity", "L1 TLB parity", - "Reserved", + "DC Tag error type 5", "DC tag error type 6", "DC tag error type 1", "Internal error type 1", @@ -222,6 +222,7 @@ static const char * const smca_ex_mce_desc[] = { "Retire status queue parity error", "Scheduling queue parity error", "Branch buffer queue parity error", + "Hardware Assertion error", }; static const char * const smca_fp_mce_desc[] = { @@ -279,6 +280,7 @@ static const char * const smca_pie_mce_desc[] = { "Internal PIE register security violation", "Error on GMI link", "Poison data written to internal PIE register", + "A deferred error was detected in the DF" }; static const char * const smca_umc_mce_desc[] = { @@ -288,6 +290,8 @@ static const char * const smca_umc_mce_desc[] = { "Advanced peripheral bus error", "Command/address parity error", "Write data CRC error", + "DCQ SRAM ECC error", + "AES SRAM ECC error", }; static const char * const smca_pb_mce_desc[] = { From e03447ee718b331be8f3abc388c7bf7d325dfab4 Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Fri, 1 Feb 2019 22:55:53 +0000 Subject: [PATCH 0991/4277] EDAC, mce_amd: Match error descriptions to latest documentation Update the error descriptions to match the latest documentation for easier searching. In some cases the changes are small and in other cases the changes may be total rewording of the description. No functional changes. Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: linux-edac Cc: Mauro Carvalho Chehab Cc: Tony Luck Cc: x86@kernel.org Link: https://lkml.kernel.org/r/20190201225534.8177-5-Yazen.Ghannam@amd.com --- drivers/edac/mce_amd.c | 166 ++++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index c79e650aa606..7e29ceabdf6f 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -151,74 +151,74 @@ static const char * const mc6_mce_desc[] = { /* Scalable MCA error strings */ static const char * const smca_ls_mce_desc[] = { - "Load queue parity", - "Store queue parity", - "Miss address buffer payload parity", - "L1 TLB parity", + "Load queue parity error", + "Store queue parity error", + "Miss address buffer payload parity error", + "Level 1 TLB parity error", "DC Tag error type 5", - "DC tag error type 6", - "DC tag error type 1", + "DC Tag error type 6", + "DC Tag error type 1", "Internal error type 1", "Internal error type 2", - "Sys Read data error thread 0", - "Sys read data error thread 1", - "DC tag error type 2", - "DC data error type 1 (poison consumption)", - "DC data error type 2", - "DC data error type 3", - "DC tag error type 4", - "L2 TLB parity", + "System Read Data Error Thread 0", + "System Read Data Error Thread 1", + "DC Tag error type 2", + "DC Data error type 1 and poison consumption", + "DC Data error type 2", + "DC Data error type 3", + "DC Tag error type 4", + "Level 2 TLB parity error", "PDC parity error", - "DC tag error type 3", - "DC tag error type 5", - "L2 fill data error", + "DC Tag error type 3", + "DC Tag error type 5", + "L2 Fill Data error", }; static const char * const smca_if_mce_desc[] = { - "microtag probe port parity error", - "IC microtag or full tag multi-hit error", - "IC full tag parity", - "IC data array parity", - "Decoupling queue phys addr parity error", - "L0 ITLB parity error", - "L1 ITLB parity error", - "L2 ITLB parity error", - "BPQ snoop parity on Thread 0", - "BPQ snoop parity on Thread 1", - "L1 BTB multi-match error", - "L2 BTB multi-match error", - "L2 Cache Response Poison error", - "System Read Data error", + "Op Cache Microtag Probe Port Parity Error", + "IC Microtag or Full Tag Multi-hit Error", + "IC Full Tag Parity Error", + "IC Data Array Parity Error", + "Decoupling Queue PhysAddr Parity Error", + "L0 ITLB Parity Error", + "L1 ITLB Parity Error", + "L2 ITLB Parity Error", + "BPQ Thread 0 Snoop Parity Error", + "BPQ Thread 1 Snoop Parity Error", + "L1 BTB Multi-Match Error", + "L2 BTB Multi-Match Error", + "L2 Cache Response Poison Error", + "System Read Data Error", }; static const char * const smca_l2_mce_desc[] = { - "L2M tag multi-way-hit error", - "L2M tag ECC error", - "L2M data ECC error", - "HW assert", + "L2M Tag Multiple-Way-Hit error", + "L2M Tag or State Array ECC Error", + "L2M Data Array ECC Error", + "Hardware Assert Error", }; static const char * const smca_de_mce_desc[] = { - "uop cache tag parity error", - "uop cache data parity error", - "Insn buffer parity error", - "uop queue parity error", - "Insn dispatch queue parity error", - "Fetch address FIFO parity", - "Patch RAM data parity", - "Patch RAM sequencer parity", - "uop buffer parity" + "Micro-op cache tag parity error", + "Micro-op cache data parity error", + "Instruction buffer parity error", + "Micro-op queue parity error", + "Instruction dispatch queue parity error", + "Fetch address FIFO parity error", + "Patch RAM data parity error", + "Patch RAM sequencer parity error", + "Micro-op buffer parity error" }; static const char * const smca_ex_mce_desc[] = { - "Watchdog timeout error", - "Phy register file parity", - "Flag register file parity", - "Immediate displacement register file parity", - "Address generator payload parity", - "EX payload parity", - "Checkpoint queue parity", - "Retire dispatch queue parity", + "Watchdog Timeout error", + "Physical register file parity error", + "Flag register file parity error", + "Immediate displacement register file parity error", + "Address generator payload parity error", + "EX payload parity error", + "Checkpoint queue parity error", + "Retire dispatch queue parity error", "Retire status queue parity error", "Scheduling queue parity error", "Branch buffer queue parity error", @@ -226,36 +226,36 @@ static const char * const smca_ex_mce_desc[] = { }; static const char * const smca_fp_mce_desc[] = { - "Physical register file parity", - "Freelist parity error", - "Schedule queue parity", + "Physical register file (PRF) parity error", + "Freelist (FL) parity error", + "Schedule queue parity error", "NSQ parity error", - "Retire queue parity", - "Status register file parity", + "Retire queue (RQ) parity error", + "Status register file (SRF) parity error", "Hardware assertion", }; static const char * const smca_l3_mce_desc[] = { - "Shadow tag macro ECC error", - "Shadow tag macro multi-way-hit error", - "L3M tag ECC error", - "L3M tag multi-way-hit error", - "L3M data ECC error", - "XI parity, L3 fill done channel error", - "L3 victim queue parity", - "L3 HW assert", + "Shadow Tag Macro ECC Error", + "Shadow Tag Macro Multi-way-hit Error", + "L3M Tag ECC Error", + "L3M Tag Multi-way-hit Error", + "L3M Data ECC Error", + "SDP Parity Error or SystemReadDataError from XI", + "L3 Victim Queue Parity Error", + "L3 Hardware Assertion", }; static const char * const smca_cs_mce_desc[] = { - "Illegal request from transport layer", - "Address violation", - "Security violation", - "Illegal response from transport layer", - "Unexpected response", - "Parity error on incoming request or probe response data", - "Parity error on incoming read response data", - "Atomic request parity", - "ECC error on probe filter access", + "Illegal Request", + "Address Violation", + "Security Violation", + "Illegal Response", + "Unexpected Response", + "Request or Probe Parity Error", + "Read Response Parity Error", + "Atomic Request Parity Error", + "Probe Filter ECC Error", }; static const char * const smca_cs2_mce_desc[] = { @@ -276,30 +276,30 @@ static const char * const smca_cs2_mce_desc[] = { }; static const char * const smca_pie_mce_desc[] = { - "HW assert", - "Internal PIE register security violation", - "Error on GMI link", - "Poison data written to internal PIE register", + "Hardware Assert", + "Register security violation", + "Link Error", + "Poison data consumption", "A deferred error was detected in the DF" }; static const char * const smca_umc_mce_desc[] = { "DRAM ECC error", - "Data poison error on DRAM", + "Data poison error", "SDP parity error", "Advanced peripheral bus error", - "Command/address parity error", + "Address/Command parity error", "Write data CRC error", "DCQ SRAM ECC error", "AES SRAM ECC error", }; static const char * const smca_pb_mce_desc[] = { - "Parameter Block RAM ECC error", + "An ECC error in the Parameter Block RAM array", }; static const char * const smca_psp_mce_desc[] = { - "PSP RAM ECC or parity error", + "An ECC or parity error in a PSP RAM instance", }; static const char * const smca_psp2_mce_desc[] = { @@ -324,7 +324,7 @@ static const char * const smca_psp2_mce_desc[] = { }; static const char * const smca_smu_mce_desc[] = { - "SMU RAM ECC or parity error", + "An ECC or parity error in an SMU RAM instance", }; static const char * const smca_smu2_mce_desc[] = { From 0fa89f972da607540497f11afbb47af6fea5bce0 Mon Sep 17 00:00:00 2001 From: Laurentiu Tudor Date: Fri, 18 Jan 2019 12:06:23 +0200 Subject: [PATCH 0992/4277] dmaengine: fsl-edma: dma map slave device address This mapping needs to be created in order for slave dma transfers to work on systems with SMMU. The implementation mostly mimics the one in pl330 dma driver, authored by Robin Murphy. Signed-off-by: Laurentiu Tudor Suggested-by: Robin Murphy Tested-by: Angelo Dureghello Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma-common.c | 66 ++++++++++++++++++++++++++++++++--- drivers/dma/fsl-edma-common.h | 4 +++ drivers/dma/fsl-edma.c | 1 + drivers/dma/mcf-edma.c | 1 + 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index fe529100674f..680b2a00a953 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "fsl-edma-common.h" @@ -173,12 +174,62 @@ int fsl_edma_resume(struct dma_chan *chan) } EXPORT_SYMBOL_GPL(fsl_edma_resume); +static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan) +{ + if (fsl_chan->dma_dir != DMA_NONE) + dma_unmap_resource(fsl_chan->vchan.chan.device->dev, + fsl_chan->dma_dev_addr, + fsl_chan->dma_dev_size, + fsl_chan->dma_dir, 0); + fsl_chan->dma_dir = DMA_NONE; +} + +static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan, + enum dma_transfer_direction dir) +{ + struct device *dev = fsl_chan->vchan.chan.device->dev; + enum dma_data_direction dma_dir; + phys_addr_t addr = 0; + u32 size = 0; + + switch (dir) { + case DMA_MEM_TO_DEV: + dma_dir = DMA_FROM_DEVICE; + addr = fsl_chan->cfg.dst_addr; + size = fsl_chan->cfg.dst_maxburst; + break; + case DMA_DEV_TO_MEM: + dma_dir = DMA_TO_DEVICE; + addr = fsl_chan->cfg.src_addr; + size = fsl_chan->cfg.src_maxburst; + break; + default: + dma_dir = DMA_NONE; + break; + } + + /* Already mapped for this config? */ + if (fsl_chan->dma_dir == dma_dir) + return true; + + fsl_edma_unprep_slave_dma(fsl_chan); + + fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0); + if (dma_mapping_error(dev, fsl_chan->dma_dev_addr)) + return false; + fsl_chan->dma_dev_size = size; + fsl_chan->dma_dir = dma_dir; + + return true; +} + int fsl_edma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg)); + fsl_edma_unprep_slave_dma(fsl_chan); return 0; } @@ -376,6 +427,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( if (!is_slave_direction(direction)) return NULL; + if (!fsl_edma_prep_slave_dma(fsl_chan, direction)) + return NULL; + sg_len = buf_len / period_len; fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) @@ -407,11 +461,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( if (direction == DMA_MEM_TO_DEV) { src_addr = dma_buf_next; - dst_addr = fsl_chan->cfg.dst_addr; + dst_addr = fsl_chan->dma_dev_addr; soff = fsl_chan->cfg.dst_addr_width; doff = 0; } else { - src_addr = fsl_chan->cfg.src_addr; + src_addr = fsl_chan->dma_dev_addr; dst_addr = dma_buf_next; soff = 0; doff = fsl_chan->cfg.src_addr_width; @@ -442,6 +496,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( if (!is_slave_direction(direction)) return NULL; + if (!fsl_edma_prep_slave_dma(fsl_chan, direction)) + return NULL; + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) return NULL; @@ -466,11 +523,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( if (direction == DMA_MEM_TO_DEV) { src_addr = sg_dma_address(sg); - dst_addr = fsl_chan->cfg.dst_addr; + dst_addr = fsl_chan->dma_dev_addr; soff = fsl_chan->cfg.dst_addr_width; doff = 0; } else { - src_addr = fsl_chan->cfg.src_addr; + src_addr = fsl_chan->dma_dev_addr; dst_addr = sg_dma_address(sg); soff = 0; doff = fsl_chan->cfg.src_addr_width; @@ -553,6 +610,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan) fsl_edma_chan_mux(fsl_chan, 0, false); fsl_chan->edesc = NULL; vchan_get_all_descriptors(&fsl_chan->vchan, &head); + fsl_edma_unprep_slave_dma(fsl_chan); spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); vchan_dma_desc_free_list(&fsl_chan->vchan, &head); diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index 8917e8865959..b435d8e1e3a1 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -6,6 +6,7 @@ #ifndef _FSL_EDMA_COMMON_H_ #define _FSL_EDMA_COMMON_H_ +#include #include "virt-dma.h" #define EDMA_CR_EDBG BIT(1) @@ -120,6 +121,9 @@ struct fsl_edma_chan { struct dma_slave_config cfg; u32 attr; struct dma_pool *tcd_pool; + dma_addr_t dma_dev_addr; + u32 dma_dev_size; + enum dma_data_direction dma_dir; }; struct fsl_edma_desc { diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 34d70112fcc9..75e8a7ba3a22 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -254,6 +254,7 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_chan->pm_state = RUNNING; fsl_chan->slave_id = 0; fsl_chan->idle = true; + fsl_chan->dma_dir = DMA_NONE; fsl_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c index 5de1b07eddff..7de54b2fafdb 100644 --- a/drivers/dma/mcf-edma.c +++ b/drivers/dma/mcf-edma.c @@ -214,6 +214,7 @@ static int mcf_edma_probe(struct platform_device *pdev) mcf_chan->edma = mcf_edma; mcf_chan->slave_id = i; mcf_chan->idle = true; + mcf_chan->dma_dir = DMA_NONE; mcf_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev); iowrite32(0x0, ®s->tcd[i].csr); From 3e05ada043828c5880c88789c824e3d40d6830cb Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 23 Jan 2019 09:26:00 +0100 Subject: [PATCH 0993/4277] dmaengine: bcm2835: Return void from abort of transactions bcm2835_dma_abort() returns an int but bcm2835_dma_terminate_all() (its sole caller) does not evaluate the return value. Change the return type to void. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Florian Meier Tested-by: Stefan Wahren Acked-by: Florian Kauer Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index ae10f5614f95..a56fe373e8f9 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -406,7 +406,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg( } } -static int bcm2835_dma_abort(struct bcm2835_chan *c) +static void bcm2835_dma_abort(struct bcm2835_chan *c) { void __iomem *chan_base = c->chan_base; long int timeout = 10000; @@ -416,7 +416,7 @@ static int bcm2835_dma_abort(struct bcm2835_chan *c) * (The ACTIVE flag in the CS register is not a reliable indicator.) */ if (!readl(chan_base + BCM2835_DMA_ADDR)) - return 0; + return; /* Write 0 to the active bit - Pause the DMA */ writel(0, chan_base + BCM2835_DMA_CS); @@ -432,7 +432,6 @@ static int bcm2835_dma_abort(struct bcm2835_chan *c) "failed to complete outstanding writes\n"); writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS); - return 0; } static void bcm2835_dma_start_desc(struct bcm2835_chan *c) From 603fe86be189e1485715c6cc70113b43a8553da3 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 23 Jan 2019 09:26:00 +0100 Subject: [PATCH 0994/4277] dmaengine: bcm2835: Enforce control block alignment Per section 4.2.1.1 of the BCM2835 ARM Peripherals spec, control blocks "must start at a 256 bit aligned address": https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf This rule is currently satisfied only by accident because struct bcm2835_dma_cb has a size of 256 bit and the DMA pool API happens to allocate blocks consecutively. It seems safer to be explicit and tell the DMA pool allocator about the required alignment. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Florian Meier Tested-by: Stefan Wahren Acked-by: Florian Kauer Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index a56fe373e8f9..6d66e233a191 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -503,8 +503,12 @@ static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) dev_dbg(dev, "Allocating DMA channel %d\n", c->ch); + /* + * Control blocks are 256 bit in length and must start at a 256 bit + * (32 byte) aligned address (BCM2835 ARM Peripherals, sec. 4.2.1.1). + */ c->cb_pool = dma_pool_create(dev_name(dev), dev, - sizeof(struct bcm2835_dma_cb), 0, 0); + sizeof(struct bcm2835_dma_cb), 32, 0); if (!c->cb_pool) { dev_err(dev, "unable to allocate descriptor pool\n"); return -ENOMEM; From efdffc1aaf1a6710f350d9c4e2573fcc8d43f8f1 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 23 Jan 2019 09:26:00 +0100 Subject: [PATCH 0995/4277] dmaengine: bcm2835: Drop gratuitous list deletion The BCM2835 DMA driver deletes a channel from a list upon termination without having added it to a list first. Moreover that operation is protected by a spinlock which isn't taken anywhere else. These appear to be remnants of an older version of the driver which accidentally got mainlined. Remove the dead code. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Florian Meier Tested-by: Stefan Wahren Acked-by: Florian Kauer Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 6d66e233a191..138bda32b266 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -42,7 +42,6 @@ struct bcm2835_dmadev { struct dma_device ddev; - spinlock_t lock; void __iomem *base; struct device_dma_parameters dma_parms; }; @@ -64,7 +63,6 @@ struct bcm2835_cb_entry { struct bcm2835_chan { struct virt_dma_chan vc; - struct list_head node; struct dma_slave_config cfg; unsigned int dreq; @@ -777,17 +775,11 @@ static int bcm2835_dma_slave_config(struct dma_chan *chan, static int bcm2835_dma_terminate_all(struct dma_chan *chan) { struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); - struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); unsigned long flags; LIST_HEAD(head); spin_lock_irqsave(&c->vc.lock, flags); - /* Prevent this channel being scheduled */ - spin_lock(&d->lock); - list_del_init(&c->node); - spin_unlock(&d->lock); - /* stop DMA activity */ if (c->desc) { vchan_terminate_vdesc(&c->desc->vd); @@ -820,7 +812,6 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, c->vc.desc_free = bcm2835_dma_desc_free; vchan_init(&c->vc, &d->ddev); - INIT_LIST_HEAD(&c->node); c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); c->ch = chan_id; @@ -923,7 +914,6 @@ static int bcm2835_dma_probe(struct platform_device *pdev) od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); - spin_lock_init(&od->lock); platform_set_drvdata(pdev, od); From 37c22cabf225e89de3e98a6ed02a6c55fcb53fde Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 23 Jan 2019 09:26:00 +0100 Subject: [PATCH 0996/4277] dmaengine: bcm2835: Drop outdated comment on supported transactions Remove an outdated comment claiming the driver only supports cyclic transactions. The driver has been supporting other transaction types for more than two years. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Florian Meier Tested-by: Stefan Wahren Acked-by: Florian Kauer Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 138bda32b266..ceae46858419 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -2,9 +2,6 @@ /* * BCM2835 DMA engine support * - * This driver only supports cyclic DMA transfers - * as needed for the I2S module. - * * Author: Florian Meier * Copyright 2013 * From 6175f6a7ebc77a3c83b425fdb4fc186c771193bf Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 21 Dec 2018 22:34:45 -0600 Subject: [PATCH 0997/4277] dmaengine: fsldma: Add 64-bit I/O accessors for powerpc64 Otherwise 64-bit PPC builds fail with undefined references to these accessors. Cc: Peng Ma Cc: Wen He Fixes: 68997fff94afa (" dmaengine: fsldma: Adding macro FSL_DMA_IN/OUT implement for ARM platform") Signed-off-by: Scott Wood Signed-off-by: Vinod Koul --- drivers/dma/fsldma.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 88db939c04a1..a9b12f82b5c3 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -202,7 +202,12 @@ struct fsldma_chan { #define fsl_iowrite32(v, p) out_le32(p, v) #define fsl_iowrite32be(v, p) out_be32(p, v) -#ifndef __powerpc64__ +#ifdef __powerpc64__ +#define fsl_ioread64(p) in_le64(p) +#define fsl_ioread64be(p) in_be64(p) +#define fsl_iowrite64(v, p) out_le64(p, v) +#define fsl_iowrite64be(v, p) out_be64(p, v) +#else static u64 fsl_ioread64(const u64 __iomem *addr) { u32 fsl_addr = lower_32_bits(addr); From 5f32879ea35523b9842bdbdc0065e13635caada2 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Fri, 11 Jan 2019 19:37:00 +0100 Subject: [PATCH 0998/4277] ovl: During copy up, first copy up data and then xattrs If a file with capability set (and hence security.capability xattr) is written kernel clears security.capability xattr. For overlay, during file copy up if xattrs are copied up first and then data is, copied up. This means data copy up will result in clearing of security.capability xattr file on lower has. And this can result into surprises. If a lower file has CAP_SETUID, then it should not be cleared over copy up (if nothing was actually written to file). This also creates problems with chown logic where it first copies up file and then tries to clear setuid bit. But by that time security.capability xattr is already gone (due to data copy up), and caller gets -ENODATA. This has been reported by Giuseppe here. https://github.com/containers/libpod/issues/2015#issuecomment-447824842 Fix this by copying up data first and then metadta. This is a regression which has been introduced by my commit as part of metadata only copy up patches. TODO: There will be some corner cases where a file is copied up metadata only and later data copy up happens and that will clear security.capability xattr. Something needs to be done about that too. Fixes: bd64e57586d3 ("ovl: During copy up, first copy up metadata and then data") Cc: # v4.19+ Reported-by: Giuseppe Scrivano Signed-off-by: Vivek Goyal Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 9e62dcf06fc4..48119b23375b 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -443,6 +443,24 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) { int err; + /* + * Copy up data first and then xattrs. Writing data after + * xattrs will remove security.capability xattr automatically. + */ + if (S_ISREG(c->stat.mode) && !c->metacopy) { + struct path upperpath, datapath; + + ovl_path_upper(c->dentry, &upperpath); + if (WARN_ON(upperpath.dentry != NULL)) + return -EIO; + upperpath.dentry = temp; + + ovl_path_lowerdata(c->dentry, &datapath); + err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size); + if (err) + return err; + } + err = ovl_copy_xattr(c->lowerpath.dentry, temp); if (err) return err; @@ -460,19 +478,6 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) return err; } - if (S_ISREG(c->stat.mode) && !c->metacopy) { - struct path upperpath, datapath; - - ovl_path_upper(c->dentry, &upperpath); - BUG_ON(upperpath.dentry != NULL); - upperpath.dentry = temp; - - ovl_path_lowerdata(c->dentry, &datapath); - err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size); - if (err) - return err; - } - if (c->metacopy) { err = ovl_check_setxattr(c->dentry, temp, OVL_XATTR_METACOPY, NULL, 0, -EOPNOTSUPP); From 8eed1db1adec6a26cef4acc7e0b2615049e8bd6d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 1 Feb 2019 15:53:42 +0100 Subject: [PATCH 0999/4277] clk: meson: pll: update driver for the g12a The g12a use fractional parameter of 17 useful bits. At the moment, this parameter in encoded using u16 value. Use this opportunity to switch all the pll to parameter to unsigned int. This should save us some annoying trouble shooting when and m and n field eventually grow as well. This patch also introduce pll multiplier range. On the g12a, the hifi and gp0 plls are able to lock as long as the following condition is met: 55 <= m/n <= 255. The param table describing this would be huge which is a waste of memory. Using ranges, we can save memory. Ranges also help find the best pll parameter significantly faster since we don't have to try all the possible settings. Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong [jbrunet: fixed fix pll settings calculation with arm32] Link: https://lkml.kernel.org/r/20190201145345.6795-2-jbrunet@baylibre.com --- drivers/clk/meson/clk-pll.c | 205 ++++++++++++++++++++++++++---------- drivers/clk/meson/clk-pll.h | 10 +- 2 files changed, 155 insertions(+), 60 deletions(-) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 4a8c68ae8801..41e16dd7272a 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "clk-regmap.h" #include "clk-pll.h" @@ -42,12 +43,21 @@ meson_clk_pll_data(struct clk_regmap *clk) return (struct meson_clk_pll_data *)clk->data; } +static int __pll_round_closest_mult(struct meson_clk_pll_data *pll) +{ + if ((pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) && + !MESON_PARM_APPLICABLE(&pll->frac)) + return 1; + + return 0; +} + static unsigned long __pll_params_to_rate(unsigned long parent_rate, - const struct pll_params_table *pllt, - u16 frac, + unsigned int m, unsigned int n, + unsigned int frac, struct meson_clk_pll_data *pll) { - u64 rate = (u64)parent_rate * pllt->m; + u64 rate = (u64)parent_rate * m; if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { u64 frac_rate = (u64)parent_rate * frac; @@ -56,7 +66,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate, (1 << pll->frac.width)); } - return DIV_ROUND_UP_ULL(rate, pllt->n); + return DIV_ROUND_UP_ULL(rate, n); } static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, @@ -64,35 +74,39 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); - struct pll_params_table pllt; - u16 frac; + unsigned int m, n, frac; - pllt.n = meson_parm_read(clk->map, &pll->n); - pllt.m = meson_parm_read(clk->map, &pll->m); + n = meson_parm_read(clk->map, &pll->n); + m = meson_parm_read(clk->map, &pll->m); frac = MESON_PARM_APPLICABLE(&pll->frac) ? meson_parm_read(clk->map, &pll->frac) : 0; - return __pll_params_to_rate(parent_rate, &pllt, frac, pll); + return __pll_params_to_rate(parent_rate, m, n, frac, pll); } -static u16 __pll_params_with_frac(unsigned long rate, - unsigned long parent_rate, - const struct pll_params_table *pllt, - struct meson_clk_pll_data *pll) +static unsigned int __pll_params_with_frac(unsigned long rate, + unsigned long parent_rate, + unsigned int m, + unsigned int n, + struct meson_clk_pll_data *pll) { - u16 frac_max = (1 << pll->frac.width); - u64 val = (u64)rate * pllt->n; + unsigned int frac_max = (1 << pll->frac.width); + u64 val = (u64)rate * n; + + /* Bail out if we are already over the requested rate */ + if (rate < parent_rate * m / n) + return 0; if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); else val = div_u64(val * frac_max, parent_rate); - val -= pllt->m * frac_max; + val -= m * frac_max; - return min((u16)val, (u16)(frac_max - 1)); + return min((unsigned int)val, (frac_max - 1)); } static bool meson_clk_pll_is_better(unsigned long rate, @@ -100,45 +114,123 @@ static bool meson_clk_pll_is_better(unsigned long rate, unsigned long now, struct meson_clk_pll_data *pll) { - if (!(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) || - MESON_PARM_APPLICABLE(&pll->frac)) { - /* Round down */ - if (now < rate && best < now) - return true; - } else { + if (__pll_round_closest_mult(pll)) { /* Round Closest */ if (abs(now - rate) < abs(best - rate)) return true; + } else { + /* Round down */ + if (now < rate && best < now) + return true; } return false; } -static const struct pll_params_table * -meson_clk_get_pll_settings(unsigned long rate, - unsigned long parent_rate, - struct meson_clk_pll_data *pll) +static int meson_clk_get_pll_table_index(unsigned int index, + unsigned int *m, + unsigned int *n, + struct meson_clk_pll_data *pll) { - const struct pll_params_table *table = pll->table; - unsigned long best = 0, now = 0; - unsigned int i, best_i = 0; + if (!pll->table[index].n) + return -EINVAL; - if (!table) - return NULL; + *m = pll->table[index].m; + *n = pll->table[index].n; - for (i = 0; table[i].n; i++) { - now = __pll_params_to_rate(parent_rate, &table[i], 0, pll); + return 0; +} - /* If we get an exact match, don't bother any further */ - if (now == rate) { - return &table[i]; - } else if (meson_clk_pll_is_better(rate, best, now, pll)) { - best = now; - best_i = i; +static unsigned int meson_clk_get_pll_range_m(unsigned long rate, + unsigned long parent_rate, + unsigned int n, + struct meson_clk_pll_data *pll) +{ + u64 val = (u64)rate * n; + + if (__pll_round_closest_mult(pll)) + return DIV_ROUND_CLOSEST_ULL(val, parent_rate); + + return div_u64(val, parent_rate); +} + +static int meson_clk_get_pll_range_index(unsigned long rate, + unsigned long parent_rate, + unsigned int index, + unsigned int *m, + unsigned int *n, + struct meson_clk_pll_data *pll) +{ + *n = index + 1; + + /* Check the predivider range */ + if (*n >= (1 << pll->n.width)) + return -EINVAL; + + if (*n == 1) { + /* Get the boundaries out the way */ + if (rate <= pll->range->min * parent_rate) { + *m = pll->range->min; + return -ENODATA; + } else if (rate >= pll->range->max * parent_rate) { + *m = pll->range->max; + return -ENODATA; } } - return (struct pll_params_table *)&table[best_i]; + *m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll); + + /* the pre-divider gives a multiplier too big - stop */ + if (*m >= (1 << pll->m.width)) + return -EINVAL; + + return 0; +} + +static int meson_clk_get_pll_get_index(unsigned long rate, + unsigned long parent_rate, + unsigned int index, + unsigned int *m, + unsigned int *n, + struct meson_clk_pll_data *pll) +{ + if (pll->range) + return meson_clk_get_pll_range_index(rate, parent_rate, + index, m, n, pll); + else if (pll->table) + return meson_clk_get_pll_table_index(index, m, n, pll); + + return -EINVAL; +} + +static int meson_clk_get_pll_settings(unsigned long rate, + unsigned long parent_rate, + unsigned int *best_m, + unsigned int *best_n, + struct meson_clk_pll_data *pll) +{ + unsigned long best = 0, now = 0; + unsigned int i, m, n; + int ret; + + for (i = 0, ret = 0; !ret; i++) { + ret = meson_clk_get_pll_get_index(rate, parent_rate, + i, &m, &n, pll); + if (ret == -EINVAL) + break; + + now = __pll_params_to_rate(parent_rate, m, n, 0, pll); + if (meson_clk_pll_is_better(rate, best, now, pll)) { + best = now; + *best_m = m; + *best_n = n; + + if (now == rate) + break; + } + } + + return best ? 0 : -EINVAL; } static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -146,15 +238,15 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); - const struct pll_params_table *pllt = - meson_clk_get_pll_settings(rate, *parent_rate, pll); + unsigned int m, n, frac; unsigned long round; - u16 frac; + int ret; - if (!pllt) + ret = meson_clk_get_pll_settings(rate, *parent_rate, &m, &n, pll); + if (ret) return meson_clk_pll_recalc_rate(hw, *parent_rate); - round = __pll_params_to_rate(*parent_rate, pllt, 0, pll); + round = __pll_params_to_rate(*parent_rate, m, n, 0, pll); if (!MESON_PARM_APPLICABLE(&pll->frac) || rate == round) return round; @@ -163,9 +255,9 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, * The rate provided by the setting is not an exact match, let's * try to improve the result using the fractional parameter */ - frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll); + frac = __pll_params_with_frac(rate, *parent_rate, m, n, pll); - return __pll_params_to_rate(*parent_rate, pllt, frac, pll); + return __pll_params_to_rate(*parent_rate, m, n, frac, pll); } static int meson_clk_pll_wait_lock(struct clk_hw *hw) @@ -252,30 +344,27 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_regmap *clk = to_clk_regmap(hw); struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); - const struct pll_params_table *pllt; - unsigned int enabled; + unsigned int enabled, m, n, frac = 0, ret; unsigned long old_rate; - u16 frac = 0; if (parent_rate == 0 || rate == 0) return -EINVAL; old_rate = rate; - pllt = meson_clk_get_pll_settings(rate, parent_rate, pll); - if (!pllt) - return -EINVAL; + ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll); + if (ret) + return ret; enabled = meson_parm_read(clk->map, &pll->en); if (enabled) meson_clk_pll_disable(hw); - meson_parm_write(clk->map, &pll->n, pllt->n); - meson_parm_write(clk->map, &pll->m, pllt->m); - + meson_parm_write(clk->map, &pll->n, n); + meson_parm_write(clk->map, &pll->m, m); if (MESON_PARM_APPLICABLE(&pll->frac)) { - frac = __pll_params_with_frac(rate, parent_rate, pllt, pll); + frac = __pll_params_with_frac(rate, parent_rate, m, n, pll); meson_parm_write(clk->map, &pll->frac, frac); } diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h index 5ccf0854d932..55af2e285b1b 100644 --- a/drivers/clk/meson/clk-pll.h +++ b/drivers/clk/meson/clk-pll.h @@ -12,8 +12,13 @@ #include "parm.h" struct pll_params_table { - u16 m; - u16 n; + unsigned int m; + unsigned int n; +}; + +struct pll_mult_range { + unsigned int min; + unsigned int max; }; #define PLL_PARAMS(_m, _n) \ @@ -34,6 +39,7 @@ struct meson_clk_pll_data { const struct reg_sequence *init_regs; unsigned int init_count; const struct pll_params_table *table; + const struct pll_mult_range *range; u8 flags; }; From 25db146aa7262bc95c314b2df35db107f5eadd55 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Fri, 1 Feb 2019 15:53:43 +0100 Subject: [PATCH 1000/4277] dt-bindings: clk: meson: add g12a periph clock controller bindings Add new clock controller compatible and dt-bindings header for the Everything-Else domain of the g12a SoC Reviewed-by: Rob Herring Signed-off-by: Jian Hu Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201145345.6795-3-jbrunet@baylibre.com --- .../bindings/clock/amlogic,gxbb-clkc.txt | 1 + include/dt-bindings/clock/g12a-clkc.h | 135 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 include/dt-bindings/clock/g12a-clkc.h diff --git a/Documentation/devicetree/bindings/clock/amlogic,gxbb-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,gxbb-clkc.txt index a6871953bf04..5c8b105be4d6 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,gxbb-clkc.txt +++ b/Documentation/devicetree/bindings/clock/amlogic,gxbb-clkc.txt @@ -9,6 +9,7 @@ Required Properties: "amlogic,gxbb-clkc" for GXBB SoC, "amlogic,gxl-clkc" for GXL and GXM SoC, "amlogic,axg-clkc" for AXG SoC. + "amlogic,g12a-clkc" for G12A SoC. - clocks : list of clock phandle, one for each entry clock-names. - clock-names : should contain the following: * "xtal": the platform xtal diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h new file mode 100644 index 000000000000..83b657038d1e --- /dev/null +++ b/include/dt-bindings/clock/g12a-clkc.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR MIT */ +/* + * Meson-G12A clock tree IDs + * + * Copyright (c) 2018 Amlogic, Inc. All rights reserved. + */ + +#ifndef __G12A_CLKC_H +#define __G12A_CLKC_H + +#define CLKID_SYS_PLL 0 +#define CLKID_FIXED_PLL 1 +#define CLKID_FCLK_DIV2 2 +#define CLKID_FCLK_DIV3 3 +#define CLKID_FCLK_DIV4 4 +#define CLKID_FCLK_DIV5 5 +#define CLKID_FCLK_DIV7 6 +#define CLKID_GP0_PLL 7 +#define CLKID_CLK81 10 +#define CLKID_MPLL0 11 +#define CLKID_MPLL1 12 +#define CLKID_MPLL2 13 +#define CLKID_MPLL3 14 +#define CLKID_DDR 15 +#define CLKID_DOS 16 +#define CLKID_AUDIO_LOCKER 17 +#define CLKID_MIPI_DSI_HOST 18 +#define CLKID_ETH_PHY 19 +#define CLKID_ISA 20 +#define CLKID_PL301 21 +#define CLKID_PERIPHS 22 +#define CLKID_SPICC0 23 +#define CLKID_I2C 24 +#define CLKID_SANA 25 +#define CLKID_SD 26 +#define CLKID_RNG0 27 +#define CLKID_UART0 28 +#define CLKID_SPICC1 29 +#define CLKID_HIU_IFACE 30 +#define CLKID_MIPI_DSI_PHY 31 +#define CLKID_ASSIST_MISC 32 +#define CLKID_SD_EMMC_A 33 +#define CLKID_SD_EMMC_B 34 +#define CLKID_SD_EMMC_C 35 +#define CLKID_AUDIO_CODEC 36 +#define CLKID_AUDIO 37 +#define CLKID_ETH 38 +#define CLKID_DEMUX 39 +#define CLKID_AUDIO_IFIFO 40 +#define CLKID_ADC 41 +#define CLKID_UART1 42 +#define CLKID_G2D 43 +#define CLKID_RESET 44 +#define CLKID_PCIE_COMB 45 +#define CLKID_PARSER 46 +#define CLKID_USB 47 +#define CLKID_PCIE_PHY 48 +#define CLKID_AHB_ARB0 49 +#define CLKID_AHB_DATA_BUS 50 +#define CLKID_AHB_CTRL_BUS 51 +#define CLKID_HTX_HDCP22 52 +#define CLKID_HTX_PCLK 53 +#define CLKID_BT656 54 +#define CLKID_USB1_DDR_BRIDGE 55 +#define CLKID_MMC_PCLK 56 +#define CLKID_UART2 57 +#define CLKID_VPU_INTR 58 +#define CLKID_GIC 59 +#define CLKID_SD_EMMC_A_CLK0 60 +#define CLKID_SD_EMMC_B_CLK0 61 +#define CLKID_SD_EMMC_C_CLK0 62 +#define CLKID_HIFI_PLL 74 +#define CLKID_VCLK2_VENCI0 80 +#define CLKID_VCLK2_VENCI1 81 +#define CLKID_VCLK2_VENCP0 82 +#define CLKID_VCLK2_VENCP1 83 +#define CLKID_VCLK2_VENCT0 84 +#define CLKID_VCLK2_VENCT1 85 +#define CLKID_VCLK2_OTHER 86 +#define CLKID_VCLK2_ENCI 87 +#define CLKID_VCLK2_ENCP 88 +#define CLKID_DAC_CLK 89 +#define CLKID_AOCLK 90 +#define CLKID_IEC958 91 +#define CLKID_ENC480P 92 +#define CLKID_RNG1 93 +#define CLKID_VCLK2_ENCT 94 +#define CLKID_VCLK2_ENCL 95 +#define CLKID_VCLK2_VENCLMMC 96 +#define CLKID_VCLK2_VENCL 97 +#define CLKID_VCLK2_OTHER1 98 +#define CLKID_FCLK_DIV2P5 99 +#define CLKID_DMA 105 +#define CLKID_EFUSE 106 +#define CLKID_ROM_BOOT 107 +#define CLKID_RESET_SEC 108 +#define CLKID_SEC_AHB_APB3 109 +#define CLKID_VPU_0_SEL 110 +#define CLKID_VPU_0 112 +#define CLKID_VPU_1_SEL 113 +#define CLKID_VPU_1 115 +#define CLKID_VPU 116 +#define CLKID_VAPB_0_SEL 117 +#define CLKID_VAPB_0 119 +#define CLKID_VAPB_1_SEL 120 +#define CLKID_VAPB_1 122 +#define CLKID_VAPB_SEL 123 +#define CLKID_VAPB 124 +#define CLKID_HDMI_PLL 128 +#define CLKID_VID_PLL 129 +#define CLKID_VCLK 138 +#define CLKID_VCLK2 139 +#define CLKID_VCLK_DIV1 148 +#define CLKID_VCLK_DIV2 149 +#define CLKID_VCLK_DIV4 150 +#define CLKID_VCLK_DIV6 151 +#define CLKID_VCLK_DIV12 152 +#define CLKID_VCLK2_DIV1 153 +#define CLKID_VCLK2_DIV2 154 +#define CLKID_VCLK2_DIV4 155 +#define CLKID_VCLK2_DIV6 156 +#define CLKID_VCLK2_DIV12 157 +#define CLKID_CTS_ENCI 162 +#define CLKID_CTS_ENCP 163 +#define CLKID_CTS_VDAC 164 +#define CLKID_HDMI_TX 165 +#define CLKID_HDMI 168 +#define CLKID_MALI_0_SEL 169 +#define CLKID_MALI_0 171 +#define CLKID_MALI_1_SEL 172 +#define CLKID_MALI_1 174 +#define CLKID_MALI 175 +#define CLKID_MPLL_5OM 177 + +#endif /* __G12A_CLKC_H */ From 085a4ea93d5491b9e5274272b528a1fccf9b2546 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Fri, 1 Feb 2019 15:53:44 +0100 Subject: [PATCH 1001/4277] clk: meson: g12a: add peripheral clock controller Add the peripheral clock controller found in the g12a SoC family Signed-off-by: Jian Hu Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201145345.6795-4-jbrunet@baylibre.com --- drivers/clk/meson/Kconfig | 12 + drivers/clk/meson/Makefile | 1 + drivers/clk/meson/clk-regmap.h | 9 +- drivers/clk/meson/g12a.c | 2399 ++++++++++++++++++++++++++++++++ drivers/clk/meson/g12a.h | 175 +++ 5 files changed, 2594 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/meson/g12a.c create mode 100644 drivers/clk/meson/g12a.h diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index f2e757aea4f1..4f61dd71f26d 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -88,3 +88,15 @@ config COMMON_CLK_AXG_AUDIO help Support for the audio clock controller on AmLogic A113D devices, aka axg, Say Y if you want audio subsystem to work. + +config COMMON_CLK_G12A + bool + depends on ARCH_MESON + select COMMON_CLK_MESON_INPUT + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_MPLL + select COMMON_CLK_MESON_PLL + select MFD_SYSCON + help + Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 + devices, aka g12a. Say Y if you want peripherals to work. diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index e339d8f58488..c3dd3901afe3 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o +obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h index b7a085bbf072..1dd0abe3ba91 100644 --- a/drivers/clk/meson/clk-regmap.h +++ b/drivers/clk/meson/clk-regmap.h @@ -111,7 +111,7 @@ clk_get_regmap_mux_data(struct clk_regmap *clk) extern const struct clk_ops clk_regmap_mux_ops; extern const struct clk_ops clk_regmap_mux_ro_ops; -#define MESON_GATE(_name, _reg, _bit) \ +#define __MESON_GATE(_name, _reg, _bit, _ops) \ struct clk_regmap _name = { \ .data = &(struct clk_regmap_gate_data){ \ .offset = (_reg), \ @@ -119,11 +119,16 @@ struct clk_regmap _name = { \ }, \ .hw.init = &(struct clk_init_data) { \ .name = #_name, \ - .ops = &clk_regmap_gate_ops, \ + .ops = _ops, \ .parent_names = (const char *[]){ "clk81" }, \ .num_parents = 1, \ .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ }, \ } +#define MESON_GATE(_name, _reg, _bit) \ + __MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ops) + +#define MESON_GATE_RO(_name, _reg, _bit) \ + __MESON_GATE(_name, _reg, _bit, &clk_regmap_gate_ro_ops) #endif /* __CLK_REGMAP_H */ diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c new file mode 100644 index 000000000000..4d9ec6026ef6 --- /dev/null +++ b/drivers/clk/meson/g12a.c @@ -0,0 +1,2399 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson-G12A Clock Controller Driver + * + * Copyright (c) 2016 Baylibre SAS. + * Author: Michael Turquette + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Qiufang Dai + * Author: Jian Hu + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-input.h" +#include "clk-mpll.h" +#include "clk-pll.h" +#include "clk-regmap.h" +#include "vid-pll-div.h" +#include "g12a.h" + +#define IN_PREFIX "ee-in-" + +static DEFINE_SPINLOCK(meson_clk_lock); + +static struct clk_regmap g12a_fixed_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_FIX_PLL_CNTL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_FIX_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + }, + .hw.init = &(struct clk_init_data){ + .name = "fixed_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fixed_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_FIX_PLL_CNTL0, + .shift = 16, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "fixed_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + /* + * This clock won't ever change at runtime so + * CLK_SET_RATE_PARENT is not required + */ + }, +}; + +/* + * Internal sys pll emulation configuration parameters + */ +static const struct reg_sequence g12a_sys_init_regs[] = { + { .reg = HHI_SYS_PLL_CNTL1, .def = 0x00000000 }, + { .reg = HHI_SYS_PLL_CNTL2, .def = 0x00000000 }, + { .reg = HHI_SYS_PLL_CNTL3, .def = 0x48681c00 }, + { .reg = HHI_SYS_PLL_CNTL4, .def = 0x88770290 }, + { .reg = HHI_SYS_PLL_CNTL5, .def = 0x39272000 }, + { .reg = HHI_SYS_PLL_CNTL6, .def = 0x56540000 }, +}; + +static struct clk_regmap g12a_sys_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .l = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_SYS_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + .init_regs = g12a_sys_init_regs, + .init_count = ARRAY_SIZE(g12a_sys_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "sys_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_sys_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_SYS_PLL_CNTL0, + .shift = 16, + .width = 3, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "sys_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "sys_pll_dco" }, + .num_parents = 1, + }, +}; + +static const struct pll_mult_range g12a_gp0_pll_mult_range = { + .min = 55, + .max = 255, +}; + +/* + * Internal gp0 pll emulation configuration parameters + */ +static const struct reg_sequence g12a_gp0_init_regs[] = { + { .reg = HHI_GP0_PLL_CNTL1, .def = 0x00000000 }, + { .reg = HHI_GP0_PLL_CNTL2, .def = 0x00000000 }, + { .reg = HHI_GP0_PLL_CNTL3, .def = 0x48681c00 }, + { .reg = HHI_GP0_PLL_CNTL4, .def = 0x33771290 }, + { .reg = HHI_GP0_PLL_CNTL5, .def = 0x39272000 }, + { .reg = HHI_GP0_PLL_CNTL6, .def = 0x56540000 }, +}; + +static struct clk_regmap g12a_gp0_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_GP0_PLL_CNTL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_GP0_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + .range = &g12a_gp0_pll_mult_range, + .init_regs = g12a_gp0_init_regs, + .init_count = ARRAY_SIZE(g12a_gp0_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "gp0_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_gp0_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_GP0_PLL_CNTL0, + .shift = 16, + .width = 3, + .flags = (CLK_DIVIDER_POWER_OF_TWO | + CLK_DIVIDER_ROUND_CLOSEST), + }, + .hw.init = &(struct clk_init_data){ + .name = "gp0_pll", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "gp0_pll_dco" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* + * Internal hifi pll emulation configuration parameters + */ +static const struct reg_sequence g12a_hifi_init_regs[] = { + { .reg = HHI_HIFI_PLL_CNTL1, .def = 0x00000000 }, + { .reg = HHI_HIFI_PLL_CNTL2, .def = 0x00000000 }, + { .reg = HHI_HIFI_PLL_CNTL3, .def = 0x6a285c00 }, + { .reg = HHI_HIFI_PLL_CNTL4, .def = 0x65771290 }, + { .reg = HHI_HIFI_PLL_CNTL5, .def = 0x39272000 }, + { .reg = HHI_HIFI_PLL_CNTL6, .def = 0x56540000 }, +}; + +static struct clk_regmap g12a_hifi_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_HIFI_PLL_CNTL1, + .shift = 0, + .width = 17, + }, + .l = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_HIFI_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + .range = &g12a_gp0_pll_mult_range, + .init_regs = g12a_hifi_init_regs, + .init_count = ARRAY_SIZE(g12a_hifi_init_regs), + .flags = CLK_MESON_PLL_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "hifi_pll_dco", + .ops = &meson_clk_pll_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_hifi_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HIFI_PLL_CNTL0, + .shift = 16, + .width = 2, + .flags = (CLK_DIVIDER_POWER_OF_TWO | + CLK_DIVIDER_ROUND_CLOSEST), + }, + .hw.init = &(struct clk_init_data){ + .name = "hifi_pll", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "hifi_pll_dco" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_hdmi_pll_dco = { + .data = &(struct meson_clk_pll_data){ + .en = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 28, + .width = 1, + }, + .m = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 0, + .width = 8, + }, + .n = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 10, + .width = 5, + }, + .frac = { + .reg_off = HHI_HDMI_PLL_CNTL1, + .shift = 0, + .width = 16, + }, + .l = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 30, + .width = 1, + }, + .rst = { + .reg_off = HHI_HDMI_PLL_CNTL0, + .shift = 29, + .width = 1, + }, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_dco", + .ops = &meson_clk_pll_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + /* + * Display directly handle hdmi pll registers ATM, we need + * NOCACHE to keep our view of the clock as accurate as possible + */ + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_hdmi_pll_od = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_PLL_CNTL0, + .shift = 16, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_od", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll_dco" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_hdmi_pll_od2 = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_PLL_CNTL0, + .shift = 18, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll_od2", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll_od" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_hdmi_pll = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_PLL_CNTL0, + .shift = 20, + .width = 2, + .flags = CLK_DIVIDER_POWER_OF_TWO, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_pll", + .ops = &clk_regmap_divider_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll_od2" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div2_div = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div2_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div3_div = { + .mult = 1, + .div = 3, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div3_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div3 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 20, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div3", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div3_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div4_div = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div4_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div4 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 21, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div4", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div4_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div5_div = { + .mult = 1, + .div = 5, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div5_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div5 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 22, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div5", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div5_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div7_div = { + .mult = 1, + .div = 7, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div7_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div7 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 23, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div7", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div7_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_fclk_div2p5_div = { + .mult = 1, + .div = 5, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2p5_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_fclk_div2p5 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_FIX_PLL_CNTL1, + .bit_idx = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "fclk_div2p5", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "fclk_div2p5_div" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_mpll_50m_div = { + .mult = 1, + .div = 80, + .hw.init = &(struct clk_init_data){ + .name = "mpll_50m_div", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll_50m = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_FIX_PLL_CNTL3, + .mask = 0x1, + .shift = 5, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll_50m", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal", + "mpll_50m_div" }, + .num_parents = 2, + }, +}; + +static struct clk_fixed_factor g12a_mpll_prediv = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "mpll_prediv", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll_dco" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll0_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL1, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll0_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL1, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mpll1_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL3, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll1_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL3, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mpll2_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL5, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll2_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL5, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll2", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll2_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mpll3_div = { + .data = &(struct meson_clk_mpll_data){ + .sdm = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 0, + .width = 14, + }, + .sdm_en = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 30, + .width = 1, + }, + .n2 = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 20, + .width = 9, + }, + .ssen = { + .reg_off = HHI_MPLL_CNTL7, + .shift = 29, + .width = 1, + }, + .lock = &meson_clk_lock, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll3_div", + .ops = &meson_clk_mpll_ops, + .parent_names = (const char *[]){ "mpll_prediv" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_mpll3 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPLL_CNTL7, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpll3", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpll3_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; +static const char * const clk81_parent_names[] = { + IN_PREFIX "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", + "fclk_div3", "fclk_div5" +}; + +static struct clk_regmap g12a_mpeg_clk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MPEG_CLK_CNTL, + .mask = 0x7, + .shift = 12, + .table = mux_table_clk81, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpeg_clk_sel", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = clk81_parent_names, + .num_parents = ARRAY_SIZE(clk81_parent_names), + }, +}; + +static struct clk_regmap g12a_mpeg_clk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MPEG_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mpeg_clk_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mpeg_clk_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_clk81 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MPEG_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "clk81", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mpeg_clk_div" }, + .num_parents = 1, + .flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL), + }, +}; + +static const char * const g12a_sd_emmc_clk0_parent_names[] = { + IN_PREFIX "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + + /* + * Following these parent clocks, we should also have had mpll2, mpll3 + * and gp0_pll but these clocks are too precious to be used here. All + * the necessary rates for MMC and NAND operation can be acheived using + * g12a_ee_core or fclk_div clocks + */ +}; + +/* SDIO clock */ +static struct clk_regmap g12a_sd_emmc_a_clk0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_a_clk0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_a_clk0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_a_clk0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* SDcard clock */ +static struct clk_regmap g12a_sd_emmc_b_clk0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 25, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_b_clk0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_b_clk0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 23, + }, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_b_clk0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* EMMC/NAND clock */ +static struct clk_regmap g12a_sd_emmc_c_clk0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_NAND_CLK_CNTL, + .mask = 0x7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(g12a_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_c_clk0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_NAND_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_sd_emmc_c_clk0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_NAND_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_c_clk0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* VPU Clock */ + +static const char * const g12a_vpu_parent_names[] = { + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", + "mpll1", "vid_pll", "hifi_pll", "gp0_pll", +}; + +static struct clk_regmap g12a_vpu_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VPU_CLK_CNTL, + .mask = 0x3, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vpu_parent_names, + .num_parents = ARRAY_SIZE(g12a_vpu_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vpu_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VPU_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vpu_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vpu_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VPU_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "vpu_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vpu_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vpu_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VPU_CLK_CNTL, + .mask = 0x3, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vpu_parent_names, + .num_parents = ARRAY_SIZE(g12a_vpu_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vpu_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VPU_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vpu_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vpu_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VPU_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data) { + .name = "vpu_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vpu_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vpu = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VPU_CLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "vpu", + .ops = &clk_regmap_mux_ops, + /* + * bit 31 selects from 2 possible parents: + * vpu_0 or vpu_1 + */ + .parent_names = (const char *[]){ "vpu_0", "vpu_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +/* VAPB Clock */ + +static const char * const g12a_vapb_parent_names[] = { + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", + "mpll1", "vid_pll", "mpll2", "fclk_div2p5", +}; + +static struct clk_regmap g12a_vapb_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VAPBCLK_CNTL, + .mask = 0x3, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vapb_parent_names, + .num_parents = ARRAY_SIZE(g12a_vapb_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vapb_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VAPBCLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vapb_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vapb_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VAPBCLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "vapb_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vapb_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vapb_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VAPBCLK_CNTL, + .mask = 0x3, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vapb_parent_names, + .num_parents = ARRAY_SIZE(g12a_vapb_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vapb_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VAPBCLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vapb_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_vapb_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VAPBCLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data) { + .name = "vapb_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vapb_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vapb_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VAPBCLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "vapb_sel", + .ops = &clk_regmap_mux_ops, + /* + * bit 31 selects from 2 possible parents: + * vapb_0 or vapb_1 + */ + .parent_names = (const char *[]){ "vapb_0", "vapb_1" }, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_vapb = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VAPBCLK_CNTL, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data) { + .name = "vapb", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vapb_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* Video Clocks */ + +static struct clk_regmap g12a_vid_pll_div = { + .data = &(struct meson_vid_pll_div_data){ + .val = { + .reg_off = HHI_VID_PLL_CLK_DIV, + .shift = 0, + .width = 15, + }, + .sel = { + .reg_off = HHI_VID_PLL_CLK_DIV, + .shift = 16, + .width = 2, + }, + }, + .hw.init = &(struct clk_init_data) { + .name = "vid_pll_div", + .ops = &meson_vid_pll_div_ro_ops, + .parent_names = (const char *[]){ "hdmi_pll" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static const char * const g12a_vid_pll_parent_names[] = { "vid_pll_div", + "hdmi_pll" }; + +static struct clk_regmap g12a_vid_pll_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_PLL_CLK_DIV, + .mask = 0x1, + .shift = 18, + }, + .hw.init = &(struct clk_init_data){ + .name = "vid_pll_sel", + .ops = &clk_regmap_mux_ops, + /* + * bit 18 selects from 2 possible parents: + * vid_pll_div or hdmi_pll + */ + .parent_names = g12a_vid_pll_parent_names, + .num_parents = ARRAY_SIZE(g12a_vid_pll_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vid_pll = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_PLL_CLK_DIV, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data) { + .name = "vid_pll", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vid_pll_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static const char * const g12a_vclk_parent_names[] = { + "vid_pll", "gp0_pll", "hifi_pll", "mpll1", "fclk_div3", "fclk_div4", + "fclk_div5", "fclk_div7" +}; + +static struct clk_regmap g12a_vclk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_CLK_CNTL, + .mask = 0x7, + .shift = 16, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vclk_parent_names, + .num_parents = ARRAY_SIZE(g12a_vclk_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk2_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VIID_CLK_CNTL, + .mask = 0x7, + .shift = 16, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_vclk_parent_names, + .num_parents = ARRAY_SIZE(g12a_vclk_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk_input = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_DIV, + .bit_idx = 16, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_input", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_input = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_DIV, + .bit_idx = 16, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_input", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VID_CLK_DIV, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vclk_input" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk2_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_VIID_CLK_DIV, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "vclk2_input" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_vclk = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 0, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div2_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 1, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div2_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div4_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 2, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div4_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div6_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 3, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div6_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk_div12_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk_div12_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 0, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div2_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 1, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div2_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div4_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 2, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div4_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div6_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 3, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div6_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_vclk2_div12_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_CNTL, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "vclk2_div12_en", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "vclk2" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div2", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div2_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div4 = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div4", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div4_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div6 = { + .mult = 1, + .div = 6, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div6", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div6_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk_div12 = { + .mult = 1, + .div = 12, + .hw.init = &(struct clk_init_data){ + .name = "vclk_div12", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk_div12_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div2", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div2_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div4 = { + .mult = 1, + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div4", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div4_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div6 = { + .mult = 1, + .div = 6, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div6", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div6_en" }, + .num_parents = 1, + }, +}; + +static struct clk_fixed_factor g12a_vclk2_div12 = { + .mult = 1, + .div = 12, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_div12", + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "vclk2_div12_en" }, + .num_parents = 1, + }, +}; + +static u32 mux_table_cts_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 }; +static const char * const g12a_cts_parent_names[] = { + "vclk_div1", "vclk_div2", "vclk_div4", "vclk_div6", + "vclk_div12", "vclk2_div1", "vclk2_div2", "vclk2_div4", + "vclk2_div6", "vclk2_div12" +}; + +static struct clk_regmap g12a_cts_enci_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_CLK_DIV, + .mask = 0xf, + .shift = 28, + .table = mux_table_cts_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_enci_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_cts_encp_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VID_CLK_DIV, + .mask = 0xf, + .shift = 20, + .table = mux_table_cts_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_encp_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_cts_vdac_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_VIID_CLK_DIV, + .mask = 0xf, + .shift = 28, + .table = mux_table_cts_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_vdac_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +/* TOFIX: add support for cts_tcon */ +static u32 mux_table_hdmi_tx_sel[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 }; +static const char * const g12a_cts_hdmi_tx_parent_names[] = { + "vclk_div1", "vclk_div2", "vclk_div4", "vclk_div6", + "vclk_div12", "vclk2_div1", "vclk2_div2", "vclk2_div4", + "vclk2_div6", "vclk2_div12" +}; + +static struct clk_regmap g12a_hdmi_tx_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_HDMI_CLK_CNTL, + .mask = 0xf, + .shift = 16, + .table = mux_table_hdmi_tx_sel, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_tx_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_cts_hdmi_tx_parent_names, + .num_parents = ARRAY_SIZE(g12a_cts_hdmi_tx_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_cts_enci = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 0, + }, + .hw.init = &(struct clk_init_data) { + .name = "cts_enci", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_enci_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_cts_encp = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 2, + }, + .hw.init = &(struct clk_init_data) { + .name = "cts_encp", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_encp_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_cts_vdac = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "cts_vdac", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_vdac_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_regmap g12a_hdmi_tx = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL2, + .bit_idx = 5, + }, + .hw.init = &(struct clk_init_data) { + .name = "hdmi_tx", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "hdmi_tx_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* HDMI Clocks */ + +static const char * const g12a_hdmi_parent_names[] = { + IN_PREFIX "xtal", "fclk_div4", "fclk_div3", "fclk_div5" +}; + +static struct clk_regmap g12a_hdmi_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_HDMI_CLK_CNTL, + .mask = 0x3, + .shift = 9, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_hdmi_parent_names, + .num_parents = ARRAY_SIZE(g12a_hdmi_parent_names), + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_hdmi_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_HDMI_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "hdmi_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "hdmi_sel" }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_regmap g12a_hdmi = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_HDMI_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "hdmi", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "hdmi_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* + * The MALI IP is clocked by two identical clocks (mali_0 and mali_1) + * muxed by a glitch-free switch. + */ + +static const char * const g12a_mali_0_1_parent_names[] = { + IN_PREFIX "xtal", "gp0_pll", "hihi_pll", "fclk_div2p5", + "fclk_div3", "fclk_div4", "fclk_div5", "fclk_div7" +}; + +static struct clk_regmap g12a_mali_0_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_mali_0_1_parent_names, + .num_parents = 8, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_0_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_mali_1_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 0x7, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_mali_0_1_parent_names, + .num_parents = 8, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_1_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_MALI_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "mali_1_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap g12a_mali_1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_MALI_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali_1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "mali_1_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const char * const g12a_mali_parent_names[] = { + "mali_0", "mali_1" +}; + +static struct clk_regmap g12a_mali = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_MALI_CLK_CNTL, + .mask = 1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "mali", + .ops = &clk_regmap_mux_ops, + .parent_names = g12a_mali_parent_names, + .num_parents = 2, + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +/* Everything Else (EE) domain gates */ +static MESON_GATE(g12a_ddr, HHI_GCLK_MPEG0, 0); +static MESON_GATE(g12a_dos, HHI_GCLK_MPEG0, 1); +static MESON_GATE(g12a_audio_locker, HHI_GCLK_MPEG0, 2); +static MESON_GATE(g12a_mipi_dsi_host, HHI_GCLK_MPEG0, 3); +static MESON_GATE(g12a_eth_phy, HHI_GCLK_MPEG0, 4); +static MESON_GATE(g12a_isa, HHI_GCLK_MPEG0, 5); +static MESON_GATE(g12a_pl301, HHI_GCLK_MPEG0, 6); +static MESON_GATE(g12a_periphs, HHI_GCLK_MPEG0, 7); +static MESON_GATE(g12a_spicc_0, HHI_GCLK_MPEG0, 8); +static MESON_GATE(g12a_i2c, HHI_GCLK_MPEG0, 9); +static MESON_GATE(g12a_sana, HHI_GCLK_MPEG0, 10); +static MESON_GATE(g12a_sd, HHI_GCLK_MPEG0, 11); +static MESON_GATE(g12a_rng0, HHI_GCLK_MPEG0, 12); +static MESON_GATE(g12a_uart0, HHI_GCLK_MPEG0, 13); +static MESON_GATE(g12a_spicc_1, HHI_GCLK_MPEG0, 14); +static MESON_GATE(g12a_hiu_reg, HHI_GCLK_MPEG0, 19); +static MESON_GATE(g12a_mipi_dsi_phy, HHI_GCLK_MPEG0, 20); +static MESON_GATE(g12a_assist_misc, HHI_GCLK_MPEG0, 23); +static MESON_GATE(g12a_emmc_a, HHI_GCLK_MPEG0, 4); +static MESON_GATE(g12a_emmc_b, HHI_GCLK_MPEG0, 25); +static MESON_GATE(g12a_emmc_c, HHI_GCLK_MPEG0, 26); +static MESON_GATE(g12a_audio_codec, HHI_GCLK_MPEG0, 28); + +static MESON_GATE(g12a_audio, HHI_GCLK_MPEG1, 0); +static MESON_GATE(g12a_eth_core, HHI_GCLK_MPEG1, 3); +static MESON_GATE(g12a_demux, HHI_GCLK_MPEG1, 4); +static MESON_GATE(g12a_audio_ififo, HHI_GCLK_MPEG1, 11); +static MESON_GATE(g12a_adc, HHI_GCLK_MPEG1, 13); +static MESON_GATE(g12a_uart1, HHI_GCLK_MPEG1, 16); +static MESON_GATE(g12a_g2d, HHI_GCLK_MPEG1, 20); +static MESON_GATE(g12a_reset, HHI_GCLK_MPEG1, 23); +static MESON_GATE(g12a_pcie_comb, HHI_GCLK_MPEG1, 24); +static MESON_GATE(g12a_parser, HHI_GCLK_MPEG1, 25); +static MESON_GATE(g12a_usb_general, HHI_GCLK_MPEG1, 26); +static MESON_GATE(g12a_pcie_phy, HHI_GCLK_MPEG1, 27); +static MESON_GATE(g12a_ahb_arb0, HHI_GCLK_MPEG1, 29); + +static MESON_GATE(g12a_ahb_data_bus, HHI_GCLK_MPEG2, 1); +static MESON_GATE(g12a_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2); +static MESON_GATE(g12a_htx_hdcp22, HHI_GCLK_MPEG2, 3); +static MESON_GATE(g12a_htx_pclk, HHI_GCLK_MPEG2, 4); +static MESON_GATE(g12a_bt656, HHI_GCLK_MPEG2, 6); +static MESON_GATE(g12a_usb1_to_ddr, HHI_GCLK_MPEG2, 8); +static MESON_GATE(g12a_mmc_pclk, HHI_GCLK_MPEG2, 11); +static MESON_GATE(g12a_uart2, HHI_GCLK_MPEG2, 15); +static MESON_GATE(g12a_vpu_intr, HHI_GCLK_MPEG2, 25); +static MESON_GATE(g12a_gic, HHI_GCLK_MPEG2, 30); + +static MESON_GATE(g12a_vclk2_venci0, HHI_GCLK_OTHER, 1); +static MESON_GATE(g12a_vclk2_venci1, HHI_GCLK_OTHER, 2); +static MESON_GATE(g12a_vclk2_vencp0, HHI_GCLK_OTHER, 3); +static MESON_GATE(g12a_vclk2_vencp1, HHI_GCLK_OTHER, 4); +static MESON_GATE(g12a_vclk2_venct0, HHI_GCLK_OTHER, 5); +static MESON_GATE(g12a_vclk2_venct1, HHI_GCLK_OTHER, 6); +static MESON_GATE(g12a_vclk2_other, HHI_GCLK_OTHER, 7); +static MESON_GATE(g12a_vclk2_enci, HHI_GCLK_OTHER, 8); +static MESON_GATE(g12a_vclk2_encp, HHI_GCLK_OTHER, 9); +static MESON_GATE(g12a_dac_clk, HHI_GCLK_OTHER, 10); +static MESON_GATE(g12a_aoclk_gate, HHI_GCLK_OTHER, 14); +static MESON_GATE(g12a_iec958_gate, HHI_GCLK_OTHER, 16); +static MESON_GATE(g12a_enc480p, HHI_GCLK_OTHER, 20); +static MESON_GATE(g12a_rng1, HHI_GCLK_OTHER, 21); +static MESON_GATE(g12a_vclk2_enct, HHI_GCLK_OTHER, 22); +static MESON_GATE(g12a_vclk2_encl, HHI_GCLK_OTHER, 23); +static MESON_GATE(g12a_vclk2_venclmmc, HHI_GCLK_OTHER, 24); +static MESON_GATE(g12a_vclk2_vencl, HHI_GCLK_OTHER, 25); +static MESON_GATE(g12a_vclk2_other1, HHI_GCLK_OTHER, 26); + +static MESON_GATE_RO(g12a_dma, HHI_GCLK_OTHER2, 0); +static MESON_GATE_RO(g12a_efuse, HHI_GCLK_OTHER2, 1); +static MESON_GATE_RO(g12a_rom_boot, HHI_GCLK_OTHER2, 2); +static MESON_GATE_RO(g12a_reset_sec, HHI_GCLK_OTHER2, 3); +static MESON_GATE_RO(g12a_sec_ahb_apb3, HHI_GCLK_OTHER2, 4); + +/* Array of all clocks provided by this provider */ +static struct clk_hw_onecell_data g12a_hw_onecell_data = { + .hws = { + [CLKID_SYS_PLL] = &g12a_sys_pll.hw, + [CLKID_FIXED_PLL] = &g12a_fixed_pll.hw, + [CLKID_FCLK_DIV2] = &g12a_fclk_div2.hw, + [CLKID_FCLK_DIV3] = &g12a_fclk_div3.hw, + [CLKID_FCLK_DIV4] = &g12a_fclk_div4.hw, + [CLKID_FCLK_DIV5] = &g12a_fclk_div5.hw, + [CLKID_FCLK_DIV7] = &g12a_fclk_div7.hw, + [CLKID_FCLK_DIV2P5] = &g12a_fclk_div2p5.hw, + [CLKID_GP0_PLL] = &g12a_gp0_pll.hw, + [CLKID_MPEG_SEL] = &g12a_mpeg_clk_sel.hw, + [CLKID_MPEG_DIV] = &g12a_mpeg_clk_div.hw, + [CLKID_CLK81] = &g12a_clk81.hw, + [CLKID_MPLL0] = &g12a_mpll0.hw, + [CLKID_MPLL1] = &g12a_mpll1.hw, + [CLKID_MPLL2] = &g12a_mpll2.hw, + [CLKID_MPLL3] = &g12a_mpll3.hw, + [CLKID_DDR] = &g12a_ddr.hw, + [CLKID_DOS] = &g12a_dos.hw, + [CLKID_AUDIO_LOCKER] = &g12a_audio_locker.hw, + [CLKID_MIPI_DSI_HOST] = &g12a_mipi_dsi_host.hw, + [CLKID_ETH_PHY] = &g12a_eth_phy.hw, + [CLKID_ISA] = &g12a_isa.hw, + [CLKID_PL301] = &g12a_pl301.hw, + [CLKID_PERIPHS] = &g12a_periphs.hw, + [CLKID_SPICC0] = &g12a_spicc_0.hw, + [CLKID_I2C] = &g12a_i2c.hw, + [CLKID_SANA] = &g12a_sana.hw, + [CLKID_SD] = &g12a_sd.hw, + [CLKID_RNG0] = &g12a_rng0.hw, + [CLKID_UART0] = &g12a_uart0.hw, + [CLKID_SPICC1] = &g12a_spicc_1.hw, + [CLKID_HIU_IFACE] = &g12a_hiu_reg.hw, + [CLKID_MIPI_DSI_PHY] = &g12a_mipi_dsi_phy.hw, + [CLKID_ASSIST_MISC] = &g12a_assist_misc.hw, + [CLKID_SD_EMMC_A] = &g12a_emmc_a.hw, + [CLKID_SD_EMMC_B] = &g12a_emmc_b.hw, + [CLKID_SD_EMMC_C] = &g12a_emmc_c.hw, + [CLKID_AUDIO_CODEC] = &g12a_audio_codec.hw, + [CLKID_AUDIO] = &g12a_audio.hw, + [CLKID_ETH] = &g12a_eth_core.hw, + [CLKID_DEMUX] = &g12a_demux.hw, + [CLKID_AUDIO_IFIFO] = &g12a_audio_ififo.hw, + [CLKID_ADC] = &g12a_adc.hw, + [CLKID_UART1] = &g12a_uart1.hw, + [CLKID_G2D] = &g12a_g2d.hw, + [CLKID_RESET] = &g12a_reset.hw, + [CLKID_PCIE_COMB] = &g12a_pcie_comb.hw, + [CLKID_PARSER] = &g12a_parser.hw, + [CLKID_USB] = &g12a_usb_general.hw, + [CLKID_PCIE_PHY] = &g12a_pcie_phy.hw, + [CLKID_AHB_ARB0] = &g12a_ahb_arb0.hw, + [CLKID_AHB_DATA_BUS] = &g12a_ahb_data_bus.hw, + [CLKID_AHB_CTRL_BUS] = &g12a_ahb_ctrl_bus.hw, + [CLKID_HTX_HDCP22] = &g12a_htx_hdcp22.hw, + [CLKID_HTX_PCLK] = &g12a_htx_pclk.hw, + [CLKID_BT656] = &g12a_bt656.hw, + [CLKID_USB1_DDR_BRIDGE] = &g12a_usb1_to_ddr.hw, + [CLKID_MMC_PCLK] = &g12a_mmc_pclk.hw, + [CLKID_UART2] = &g12a_uart2.hw, + [CLKID_VPU_INTR] = &g12a_vpu_intr.hw, + [CLKID_GIC] = &g12a_gic.hw, + [CLKID_SD_EMMC_A_CLK0_SEL] = &g12a_sd_emmc_a_clk0_sel.hw, + [CLKID_SD_EMMC_A_CLK0_DIV] = &g12a_sd_emmc_a_clk0_div.hw, + [CLKID_SD_EMMC_A_CLK0] = &g12a_sd_emmc_a_clk0.hw, + [CLKID_SD_EMMC_B_CLK0_SEL] = &g12a_sd_emmc_b_clk0_sel.hw, + [CLKID_SD_EMMC_B_CLK0_DIV] = &g12a_sd_emmc_b_clk0_div.hw, + [CLKID_SD_EMMC_B_CLK0] = &g12a_sd_emmc_b_clk0.hw, + [CLKID_SD_EMMC_C_CLK0_SEL] = &g12a_sd_emmc_c_clk0_sel.hw, + [CLKID_SD_EMMC_C_CLK0_DIV] = &g12a_sd_emmc_c_clk0_div.hw, + [CLKID_SD_EMMC_C_CLK0] = &g12a_sd_emmc_c_clk0.hw, + [CLKID_MPLL0_DIV] = &g12a_mpll0_div.hw, + [CLKID_MPLL1_DIV] = &g12a_mpll1_div.hw, + [CLKID_MPLL2_DIV] = &g12a_mpll2_div.hw, + [CLKID_MPLL3_DIV] = &g12a_mpll3_div.hw, + [CLKID_FCLK_DIV2_DIV] = &g12a_fclk_div2_div.hw, + [CLKID_FCLK_DIV3_DIV] = &g12a_fclk_div3_div.hw, + [CLKID_FCLK_DIV4_DIV] = &g12a_fclk_div4_div.hw, + [CLKID_FCLK_DIV5_DIV] = &g12a_fclk_div5_div.hw, + [CLKID_FCLK_DIV7_DIV] = &g12a_fclk_div7_div.hw, + [CLKID_FCLK_DIV2P5_DIV] = &g12a_fclk_div2p5_div.hw, + [CLKID_HIFI_PLL] = &g12a_hifi_pll.hw, + [CLKID_VCLK2_VENCI0] = &g12a_vclk2_venci0.hw, + [CLKID_VCLK2_VENCI1] = &g12a_vclk2_venci1.hw, + [CLKID_VCLK2_VENCP0] = &g12a_vclk2_vencp0.hw, + [CLKID_VCLK2_VENCP1] = &g12a_vclk2_vencp1.hw, + [CLKID_VCLK2_VENCT0] = &g12a_vclk2_venct0.hw, + [CLKID_VCLK2_VENCT1] = &g12a_vclk2_venct1.hw, + [CLKID_VCLK2_OTHER] = &g12a_vclk2_other.hw, + [CLKID_VCLK2_ENCI] = &g12a_vclk2_enci.hw, + [CLKID_VCLK2_ENCP] = &g12a_vclk2_encp.hw, + [CLKID_DAC_CLK] = &g12a_dac_clk.hw, + [CLKID_AOCLK] = &g12a_aoclk_gate.hw, + [CLKID_IEC958] = &g12a_iec958_gate.hw, + [CLKID_ENC480P] = &g12a_enc480p.hw, + [CLKID_RNG1] = &g12a_rng1.hw, + [CLKID_VCLK2_ENCT] = &g12a_vclk2_enct.hw, + [CLKID_VCLK2_ENCL] = &g12a_vclk2_encl.hw, + [CLKID_VCLK2_VENCLMMC] = &g12a_vclk2_venclmmc.hw, + [CLKID_VCLK2_VENCL] = &g12a_vclk2_vencl.hw, + [CLKID_VCLK2_OTHER1] = &g12a_vclk2_other1.hw, + [CLKID_FIXED_PLL_DCO] = &g12a_fixed_pll_dco.hw, + [CLKID_SYS_PLL_DCO] = &g12a_sys_pll_dco.hw, + [CLKID_GP0_PLL_DCO] = &g12a_gp0_pll_dco.hw, + [CLKID_HIFI_PLL_DCO] = &g12a_hifi_pll_dco.hw, + [CLKID_DMA] = &g12a_dma.hw, + [CLKID_EFUSE] = &g12a_efuse.hw, + [CLKID_ROM_BOOT] = &g12a_rom_boot.hw, + [CLKID_RESET_SEC] = &g12a_reset_sec.hw, + [CLKID_SEC_AHB_APB3] = &g12a_sec_ahb_apb3.hw, + [CLKID_MPLL_PREDIV] = &g12a_mpll_prediv.hw, + [CLKID_VPU_0_SEL] = &g12a_vpu_0_sel.hw, + [CLKID_VPU_0_DIV] = &g12a_vpu_0_div.hw, + [CLKID_VPU_0] = &g12a_vpu_0.hw, + [CLKID_VPU_1_SEL] = &g12a_vpu_1_sel.hw, + [CLKID_VPU_1_DIV] = &g12a_vpu_1_div.hw, + [CLKID_VPU_1] = &g12a_vpu_1.hw, + [CLKID_VPU] = &g12a_vpu.hw, + [CLKID_VAPB_0_SEL] = &g12a_vapb_0_sel.hw, + [CLKID_VAPB_0_DIV] = &g12a_vapb_0_div.hw, + [CLKID_VAPB_0] = &g12a_vapb_0.hw, + [CLKID_VAPB_1_SEL] = &g12a_vapb_1_sel.hw, + [CLKID_VAPB_1_DIV] = &g12a_vapb_1_div.hw, + [CLKID_VAPB_1] = &g12a_vapb_1.hw, + [CLKID_VAPB_SEL] = &g12a_vapb_sel.hw, + [CLKID_VAPB] = &g12a_vapb.hw, + [CLKID_HDMI_PLL_DCO] = &g12a_hdmi_pll_dco.hw, + [CLKID_HDMI_PLL_OD] = &g12a_hdmi_pll_od.hw, + [CLKID_HDMI_PLL_OD2] = &g12a_hdmi_pll_od2.hw, + [CLKID_HDMI_PLL] = &g12a_hdmi_pll.hw, + [CLKID_VID_PLL] = &g12a_vid_pll_div.hw, + [CLKID_VID_PLL_SEL] = &g12a_vid_pll_sel.hw, + [CLKID_VID_PLL_DIV] = &g12a_vid_pll.hw, + [CLKID_VCLK_SEL] = &g12a_vclk_sel.hw, + [CLKID_VCLK2_SEL] = &g12a_vclk2_sel.hw, + [CLKID_VCLK_INPUT] = &g12a_vclk_input.hw, + [CLKID_VCLK2_INPUT] = &g12a_vclk2_input.hw, + [CLKID_VCLK_DIV] = &g12a_vclk_div.hw, + [CLKID_VCLK2_DIV] = &g12a_vclk2_div.hw, + [CLKID_VCLK] = &g12a_vclk.hw, + [CLKID_VCLK2] = &g12a_vclk2.hw, + [CLKID_VCLK_DIV1] = &g12a_vclk_div1.hw, + [CLKID_VCLK_DIV2_EN] = &g12a_vclk_div2_en.hw, + [CLKID_VCLK_DIV4_EN] = &g12a_vclk_div4_en.hw, + [CLKID_VCLK_DIV6_EN] = &g12a_vclk_div6_en.hw, + [CLKID_VCLK_DIV12_EN] = &g12a_vclk_div12_en.hw, + [CLKID_VCLK2_DIV1] = &g12a_vclk2_div1.hw, + [CLKID_VCLK2_DIV2_EN] = &g12a_vclk2_div2_en.hw, + [CLKID_VCLK2_DIV4_EN] = &g12a_vclk2_div4_en.hw, + [CLKID_VCLK2_DIV6_EN] = &g12a_vclk2_div6_en.hw, + [CLKID_VCLK2_DIV12_EN] = &g12a_vclk2_div12_en.hw, + [CLKID_VCLK_DIV2] = &g12a_vclk_div2.hw, + [CLKID_VCLK_DIV4] = &g12a_vclk_div4.hw, + [CLKID_VCLK_DIV6] = &g12a_vclk_div6.hw, + [CLKID_VCLK_DIV12] = &g12a_vclk_div12.hw, + [CLKID_VCLK2_DIV2] = &g12a_vclk2_div2.hw, + [CLKID_VCLK2_DIV4] = &g12a_vclk2_div4.hw, + [CLKID_VCLK2_DIV6] = &g12a_vclk2_div6.hw, + [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw, + [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw, + [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw, + [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw, + [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw, + [CLKID_CTS_ENCI] = &g12a_cts_enci.hw, + [CLKID_CTS_ENCP] = &g12a_cts_encp.hw, + [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw, + [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw, + [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw, + [CLKID_HDMI_DIV] = &g12a_hdmi_div.hw, + [CLKID_HDMI] = &g12a_hdmi.hw, + [CLKID_MALI_0_SEL] = &g12a_mali_0_sel.hw, + [CLKID_MALI_0_DIV] = &g12a_mali_0_div.hw, + [CLKID_MALI_0] = &g12a_mali_0.hw, + [CLKID_MALI_1_SEL] = &g12a_mali_1_sel.hw, + [CLKID_MALI_1_DIV] = &g12a_mali_1_div.hw, + [CLKID_MALI_1] = &g12a_mali_1.hw, + [CLKID_MALI] = &g12a_mali.hw, + [CLKID_MPLL_5OM_DIV] = &g12a_mpll_50m_div.hw, + [CLKID_MPLL_5OM] = &g12a_mpll_50m.hw, + [NR_CLKS] = NULL, + }, + .num = NR_CLKS, +}; + +/* Convenience table to populate regmap in .probe */ +static struct clk_regmap *const g12a_clk_regmaps[] = { + &g12a_clk81, + &g12a_dos, + &g12a_ddr, + &g12a_audio_locker, + &g12a_mipi_dsi_host, + &g12a_eth_phy, + &g12a_isa, + &g12a_pl301, + &g12a_periphs, + &g12a_spicc_0, + &g12a_i2c, + &g12a_sana, + &g12a_sd, + &g12a_rng0, + &g12a_uart0, + &g12a_spicc_1, + &g12a_hiu_reg, + &g12a_mipi_dsi_phy, + &g12a_assist_misc, + &g12a_emmc_a, + &g12a_emmc_b, + &g12a_emmc_c, + &g12a_audio_codec, + &g12a_audio, + &g12a_eth_core, + &g12a_demux, + &g12a_audio_ififo, + &g12a_adc, + &g12a_uart1, + &g12a_g2d, + &g12a_reset, + &g12a_pcie_comb, + &g12a_parser, + &g12a_usb_general, + &g12a_pcie_phy, + &g12a_ahb_arb0, + &g12a_ahb_data_bus, + &g12a_ahb_ctrl_bus, + &g12a_htx_hdcp22, + &g12a_htx_pclk, + &g12a_bt656, + &g12a_usb1_to_ddr, + &g12a_mmc_pclk, + &g12a_vpu_intr, + &g12a_gic, + &g12a_sd_emmc_a_clk0, + &g12a_sd_emmc_b_clk0, + &g12a_sd_emmc_c_clk0, + &g12a_mpeg_clk_div, + &g12a_sd_emmc_a_clk0_div, + &g12a_sd_emmc_b_clk0_div, + &g12a_sd_emmc_c_clk0_div, + &g12a_mpeg_clk_sel, + &g12a_sd_emmc_a_clk0_sel, + &g12a_sd_emmc_b_clk0_sel, + &g12a_sd_emmc_c_clk0_sel, + &g12a_mpll0, + &g12a_mpll1, + &g12a_mpll2, + &g12a_mpll3, + &g12a_mpll0_div, + &g12a_mpll1_div, + &g12a_mpll2_div, + &g12a_mpll3_div, + &g12a_fixed_pll, + &g12a_sys_pll, + &g12a_gp0_pll, + &g12a_hifi_pll, + &g12a_vclk2_venci0, + &g12a_vclk2_venci1, + &g12a_vclk2_vencp0, + &g12a_vclk2_vencp1, + &g12a_vclk2_venct0, + &g12a_vclk2_venct1, + &g12a_vclk2_other, + &g12a_vclk2_enci, + &g12a_vclk2_encp, + &g12a_dac_clk, + &g12a_aoclk_gate, + &g12a_iec958_gate, + &g12a_enc480p, + &g12a_rng1, + &g12a_vclk2_enct, + &g12a_vclk2_encl, + &g12a_vclk2_venclmmc, + &g12a_vclk2_vencl, + &g12a_vclk2_other1, + &g12a_fixed_pll_dco, + &g12a_sys_pll_dco, + &g12a_gp0_pll_dco, + &g12a_hifi_pll_dco, + &g12a_fclk_div2, + &g12a_fclk_div3, + &g12a_fclk_div4, + &g12a_fclk_div5, + &g12a_fclk_div7, + &g12a_fclk_div2p5, + &g12a_dma, + &g12a_efuse, + &g12a_rom_boot, + &g12a_reset_sec, + &g12a_sec_ahb_apb3, + &g12a_vpu_0_sel, + &g12a_vpu_0_div, + &g12a_vpu_0, + &g12a_vpu_1_sel, + &g12a_vpu_1_div, + &g12a_vpu_1, + &g12a_vpu, + &g12a_vapb_0_sel, + &g12a_vapb_0_div, + &g12a_vapb_0, + &g12a_vapb_1_sel, + &g12a_vapb_1_div, + &g12a_vapb_1, + &g12a_vapb_sel, + &g12a_vapb, + &g12a_hdmi_pll_dco, + &g12a_hdmi_pll_od, + &g12a_hdmi_pll_od2, + &g12a_hdmi_pll, + &g12a_vid_pll_div, + &g12a_vid_pll_sel, + &g12a_vid_pll, + &g12a_vclk_sel, + &g12a_vclk2_sel, + &g12a_vclk_input, + &g12a_vclk2_input, + &g12a_vclk_div, + &g12a_vclk2_div, + &g12a_vclk, + &g12a_vclk2, + &g12a_vclk_div1, + &g12a_vclk_div2_en, + &g12a_vclk_div4_en, + &g12a_vclk_div6_en, + &g12a_vclk_div12_en, + &g12a_vclk2_div1, + &g12a_vclk2_div2_en, + &g12a_vclk2_div4_en, + &g12a_vclk2_div6_en, + &g12a_vclk2_div12_en, + &g12a_cts_enci_sel, + &g12a_cts_encp_sel, + &g12a_cts_vdac_sel, + &g12a_hdmi_tx_sel, + &g12a_cts_enci, + &g12a_cts_encp, + &g12a_cts_vdac, + &g12a_hdmi_tx, + &g12a_hdmi_sel, + &g12a_hdmi_div, + &g12a_hdmi, + &g12a_mali_0_sel, + &g12a_mali_0_div, + &g12a_mali_0, + &g12a_mali_1_sel, + &g12a_mali_1_div, + &g12a_mali_1, + &g12a_mali, + &g12a_mpll_50m, +}; + +static const struct of_device_id clkc_match_table[] = { + { .compatible = "amlogic,g12a-clkc" }, + {} +}; + +static int g12a_clkc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw *input; + struct regmap *map; + int ret, i; + + /* Get the hhi system controller node */ + map = syscon_node_to_regmap(of_get_parent(dev->of_node)); + if (IS_ERR(map)) { + dev_err(dev, + "failed to get HHI regmap\n"); + return PTR_ERR(map); + } + + input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); + if (IS_ERR(input)) { + ret = PTR_ERR(input); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get input clock"); + return ret; + } + + /* Populate regmap for the regmap backed clocks */ + for (i = 0; i < ARRAY_SIZE(g12a_clk_regmaps); i++) + g12a_clk_regmaps[i]->map = map; + + for (i = 0; i < g12a_hw_onecell_data.num; i++) { + /* array might be sparse */ + if (!g12a_hw_onecell_data.hws[i]) + continue; + + ret = devm_clk_hw_register(dev, g12a_hw_onecell_data.hws[i]); + if (ret) { + dev_err(dev, "Clock registration failed\n"); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &g12a_hw_onecell_data); +} + +static struct platform_driver g12a_driver = { + .probe = g12a_clkc_probe, + .driver = { + .name = "g12a-clkc", + .of_match_table = clkc_match_table, + }, +}; + +builtin_platform_driver(g12a_driver); diff --git a/drivers/clk/meson/g12a.h b/drivers/clk/meson/g12a.h new file mode 100644 index 000000000000..f399dfe1401c --- /dev/null +++ b/drivers/clk/meson/g12a.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2016 Amlogic, Inc. + * Author: Michael Turquette + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Qiufang Dai + * Author: Jian Hu + * + */ +#ifndef __G12A_H +#define __G12A_H + +/* + * Clock controller register offsets + * + * Register offsets from the data sheet must be multiplied by 4 before + * adding them to the base address to get the right value. + */ +#define HHI_MIPI_CNTL0 0x000 +#define HHI_MIPI_CNTL1 0x004 +#define HHI_MIPI_CNTL2 0x008 +#define HHI_MIPI_STS 0x00C +#define HHI_GP0_PLL_CNTL0 0x040 +#define HHI_GP0_PLL_CNTL1 0x044 +#define HHI_GP0_PLL_CNTL2 0x048 +#define HHI_GP0_PLL_CNTL3 0x04C +#define HHI_GP0_PLL_CNTL4 0x050 +#define HHI_GP0_PLL_CNTL5 0x054 +#define HHI_GP0_PLL_CNTL6 0x058 +#define HHI_GP0_PLL_STS 0x05C +#define HHI_PCIE_PLL_CNTL0 0x098 +#define HHI_PCIE_PLL_CNTL1 0x09C +#define HHI_PCIE_PLL_CNTL2 0x0A0 +#define HHI_PCIE_PLL_CNTL3 0x0A4 +#define HHI_PCIE_PLL_CNTL4 0x0A8 +#define HHI_PCIE_PLL_CNTL5 0x0AC +#define HHI_PCIE_PLL_STS 0x0B8 +#define HHI_HIFI_PLL_CNTL0 0x0D8 +#define HHI_HIFI_PLL_CNTL1 0x0DC +#define HHI_HIFI_PLL_CNTL2 0x0E0 +#define HHI_HIFI_PLL_CNTL3 0x0E4 +#define HHI_HIFI_PLL_CNTL4 0x0E8 +#define HHI_HIFI_PLL_CNTL5 0x0EC +#define HHI_HIFI_PLL_CNTL6 0x0F0 +#define HHI_VIID_CLK_DIV 0x128 +#define HHI_VIID_CLK_CNTL 0x12C +#define HHI_GCLK_MPEG0 0x140 +#define HHI_GCLK_MPEG1 0x144 +#define HHI_GCLK_MPEG2 0x148 +#define HHI_GCLK_OTHER 0x150 +#define HHI_GCLK_OTHER2 0x154 +#define HHI_VID_CLK_DIV 0x164 +#define HHI_MPEG_CLK_CNTL 0x174 +#define HHI_AUD_CLK_CNTL 0x178 +#define HHI_VID_CLK_CNTL 0x17c +#define HHI_TS_CLK_CNTL 0x190 +#define HHI_VID_CLK_CNTL2 0x194 +#define HHI_SYS_CPU_CLK_CNTL0 0x19c +#define HHI_VID_PLL_CLK_DIV 0x1A0 +#define HHI_MALI_CLK_CNTL 0x1b0 +#define HHI_VPU_CLKC_CNTL 0x1b4 +#define HHI_VPU_CLK_CNTL 0x1bC +#define HHI_HDMI_CLK_CNTL 0x1CC +#define HHI_VDEC_CLK_CNTL 0x1E0 +#define HHI_VDEC2_CLK_CNTL 0x1E4 +#define HHI_VDEC3_CLK_CNTL 0x1E8 +#define HHI_VDEC4_CLK_CNTL 0x1EC +#define HHI_HDCP22_CLK_CNTL 0x1F0 +#define HHI_VAPBCLK_CNTL 0x1F4 +#define HHI_VPU_CLKB_CNTL 0x20C +#define HHI_GEN_CLK_CNTL 0x228 +#define HHI_VDIN_MEAS_CLK_CNTL 0x250 +#define HHI_MIPIDSI_PHY_CLK_CNTL 0x254 +#define HHI_NAND_CLK_CNTL 0x25C +#define HHI_SD_EMMC_CLK_CNTL 0x264 +#define HHI_MPLL_CNTL0 0x278 +#define HHI_MPLL_CNTL1 0x27C +#define HHI_MPLL_CNTL2 0x280 +#define HHI_MPLL_CNTL3 0x284 +#define HHI_MPLL_CNTL4 0x288 +#define HHI_MPLL_CNTL5 0x28c +#define HHI_MPLL_CNTL6 0x290 +#define HHI_MPLL_CNTL7 0x294 +#define HHI_MPLL_CNTL8 0x298 +#define HHI_FIX_PLL_CNTL0 0x2A0 +#define HHI_FIX_PLL_CNTL1 0x2A4 +#define HHI_FIX_PLL_CNTL3 0x2AC +#define HHI_SYS_PLL_CNTL0 0x2f4 +#define HHI_SYS_PLL_CNTL1 0x2f8 +#define HHI_SYS_PLL_CNTL2 0x2fc +#define HHI_SYS_PLL_CNTL3 0x300 +#define HHI_SYS_PLL_CNTL4 0x304 +#define HHI_SYS_PLL_CNTL5 0x308 +#define HHI_SYS_PLL_CNTL6 0x30c +#define HHI_HDMI_PLL_CNTL0 0x320 +#define HHI_HDMI_PLL_CNTL1 0x324 +#define HHI_HDMI_PLL_CNTL2 0x328 +#define HHI_HDMI_PLL_CNTL3 0x32c +#define HHI_HDMI_PLL_CNTL4 0x330 +#define HHI_HDMI_PLL_CNTL5 0x334 +#define HHI_HDMI_PLL_CNTL6 0x338 +#define HHI_SPICC_CLK_CNTL 0x3dc + +/* + * CLKID index values + * + * These indices are entirely contrived and do not map onto the hardware. + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/g12a-clkc.h. Only the clocks ids we don't want + * to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. + */ +#define CLKID_MPEG_SEL 8 +#define CLKID_MPEG_DIV 9 +#define CLKID_SD_EMMC_A_CLK0_SEL 63 +#define CLKID_SD_EMMC_A_CLK0_DIV 64 +#define CLKID_SD_EMMC_B_CLK0_SEL 65 +#define CLKID_SD_EMMC_B_CLK0_DIV 66 +#define CLKID_SD_EMMC_C_CLK0_SEL 67 +#define CLKID_SD_EMMC_C_CLK0_DIV 68 +#define CLKID_MPLL0_DIV 69 +#define CLKID_MPLL1_DIV 70 +#define CLKID_MPLL2_DIV 71 +#define CLKID_MPLL3_DIV 72 +#define CLKID_MPLL_PREDIV 73 +#define CLKID_FCLK_DIV2_DIV 75 +#define CLKID_FCLK_DIV3_DIV 76 +#define CLKID_FCLK_DIV4_DIV 77 +#define CLKID_FCLK_DIV5_DIV 78 +#define CLKID_FCLK_DIV7_DIV 79 +#define CLKID_FCLK_DIV2P5_DIV 100 +#define CLKID_FIXED_PLL_DCO 101 +#define CLKID_SYS_PLL_DCO 102 +#define CLKID_GP0_PLL_DCO 103 +#define CLKID_HIFI_PLL_DCO 104 +#define CLKID_VPU_0_DIV 111 +#define CLKID_VPU_1_DIV 114 +#define CLKID_VAPB_0_DIV 118 +#define CLKID_VAPB_1_DIV 121 +#define CLKID_HDMI_PLL_DCO 125 +#define CLKID_HDMI_PLL_OD 126 +#define CLKID_HDMI_PLL_OD2 127 +#define CLKID_VID_PLL_SEL 130 +#define CLKID_VID_PLL_DIV 131 +#define CLKID_VCLK_SEL 132 +#define CLKID_VCLK2_SEL 133 +#define CLKID_VCLK_INPUT 134 +#define CLKID_VCLK2_INPUT 135 +#define CLKID_VCLK_DIV 136 +#define CLKID_VCLK2_DIV 137 +#define CLKID_VCLK_DIV2_EN 140 +#define CLKID_VCLK_DIV4_EN 141 +#define CLKID_VCLK_DIV6_EN 142 +#define CLKID_VCLK_DIV12_EN 143 +#define CLKID_VCLK2_DIV2_EN 144 +#define CLKID_VCLK2_DIV4_EN 145 +#define CLKID_VCLK2_DIV6_EN 146 +#define CLKID_VCLK2_DIV12_EN 147 +#define CLKID_CTS_ENCI_SEL 158 +#define CLKID_CTS_ENCP_SEL 159 +#define CLKID_CTS_VDAC_SEL 160 +#define CLKID_HDMI_TX_SEL 161 +#define CLKID_HDMI_SEL 166 +#define CLKID_HDMI_DIV 167 +#define CLKID_MALI_0_DIV 170 +#define CLKID_MALI_1_DIV 173 +#define CLKID_MPLL_5OM_DIV 176 + +#define NR_CLKS 178 + +/* include the CLKIDs that have been made part of the DT binding */ +#include + +#endif /* __G12A_H */ From 6682bd4d443fad802e11a0a39332025cdfbf5108 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 1 Feb 2019 15:53:45 +0100 Subject: [PATCH 1002/4277] clk: meson: factorise meson64 peripheral clock controller drivers The function used to probe the peripheral clock controller of the arm64 amlogic SoCs is mostly the same. We now have 3 of those controllers so it is time to factorize things a bit. Signed-off-by: Jerome Brunet Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lkml.kernel.org/r/20190201145345.6795-5-jbrunet@baylibre.com --- drivers/clk/meson/Kconfig | 11 +- drivers/clk/meson/Makefile | 1 + drivers/clk/meson/axg.c | 59 ++----- drivers/clk/meson/g12a.c | 58 +------ drivers/clk/meson/gxbb.c | 284 +++++++++++++++++++++++--------- drivers/clk/meson/meson-eeclk.c | 63 +++++++ drivers/clk/meson/meson-eeclk.h | 25 +++ 7 files changed, 319 insertions(+), 182 deletions(-) create mode 100644 drivers/clk/meson/meson-eeclk.c create mode 100644 drivers/clk/meson/meson-eeclk.h diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 4f61dd71f26d..b72439315340 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -35,6 +35,11 @@ config COMMON_CLK_MESON_AO_CLKC select COMMON_CLK_MESON_INPUT select RESET_CONTROLLER +config COMMON_CLK_MESON_EE_CLKC + tristate + select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_INPUT + config COMMON_CLK_MESON8B bool depends on ARCH_MESON @@ -51,13 +56,13 @@ config COMMON_CLK_MESON8B config COMMON_CLK_GXBB bool depends on ARCH_MESON - select COMMON_CLK_MESON_INPUT select COMMON_CLK_MESON_REGMAP select COMMON_CLK_MESON_DUALDIV select COMMON_CLK_MESON_VID_PLL_DIV select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL select COMMON_CLK_MESON_AO_CLKC + select COMMON_CLK_MESON_EE_CLKC select MFD_SYSCON help Support for the clock controller on AmLogic S905 devices, aka gxbb. @@ -66,12 +71,12 @@ config COMMON_CLK_GXBB config COMMON_CLK_AXG bool depends on ARCH_MESON - select COMMON_CLK_MESON_INPUT select COMMON_CLK_MESON_REGMAP select COMMON_CLK_MESON_DUALDIV select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL select COMMON_CLK_MESON_AO_CLKC + select COMMON_CLK_MESON_EE_CLKC select MFD_SYSCON help Support for the clock controller on AmLogic A113D devices, aka axg. @@ -92,10 +97,10 @@ config COMMON_CLK_AXG_AUDIO config COMMON_CLK_G12A bool depends on ARCH_MESON - select COMMON_CLK_MESON_INPUT select COMMON_CLK_MESON_REGMAP select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_EE_CLKC select MFD_SYSCON help Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2 diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index c3dd3901afe3..32d17f18541e 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_AO_CLKC) += meson-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON_DUALDIV) += clk-dualdiv.o +obj-$(CONFIG_COMMON_CLK_MESON_EE_CLKC) += meson-eeclk.o obj-$(CONFIG_COMMON_CLK_MESON_INPUT) += clk-input.o obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index cbbdd93b175d..7a8ef80e5f2c 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -9,21 +9,17 @@ * Author: Qiufang Dai */ -#include #include #include #include -#include #include -#include #include "clk-input.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-mpll.h" #include "axg.h" - -#define IN_PREFIX "ee-in-" +#include "meson-eeclk.h" static DEFINE_SPINLOCK(meson_clk_lock); @@ -1260,55 +1256,20 @@ static struct clk_regmap *const axg_clk_regmaps[] = { &axg_pcie_pll_od, }; +static const struct meson_eeclkc_data axg_clkc_data = { + .regmap_clks = axg_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps), + .hw_onecell_data = &axg_hw_onecell_data, +}; + + static const struct of_device_id clkc_match_table[] = { - { .compatible = "amlogic,axg-clkc" }, + { .compatible = "amlogic,axg-clkc", .data = &axg_clkc_data }, {} }; -static int axg_clkc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct clk_hw *input; - struct regmap *map; - int ret, i; - - /* Get the hhi system controller node if available */ - map = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(map)) { - dev_err(dev, "failed to get HHI regmap\n"); - return PTR_ERR(map); - } - - input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); - if (IS_ERR(input)) { - ret = PTR_ERR(input); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get input clock"); - return ret; - } - - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(axg_clk_regmaps); i++) - axg_clk_regmaps[i]->map = map; - - for (i = 0; i < axg_hw_onecell_data.num; i++) { - /* array might be sparse */ - if (!axg_hw_onecell_data.hws[i]) - continue; - - ret = devm_clk_hw_register(dev, axg_hw_onecell_data.hws[i]); - if (ret) { - dev_err(dev, "Clock registration failed\n"); - return ret; - } - } - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, - &axg_hw_onecell_data); -} - static struct platform_driver axg_driver = { - .probe = axg_clkc_probe, + .probe = meson_eeclkc_probe, .driver = { .name = "axg-clkc", .of_match_table = clkc_match_table, diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c index 4d9ec6026ef6..0e1ce8c03259 100644 --- a/drivers/clk/meson/g12a.c +++ b/drivers/clk/meson/g12a.c @@ -13,19 +13,16 @@ #include #include #include -#include #include -#include #include "clk-input.h" #include "clk-mpll.h" #include "clk-pll.h" #include "clk-regmap.h" #include "vid-pll-div.h" +#include "meson-eeclk.h" #include "g12a.h" -#define IN_PREFIX "ee-in-" - static DEFINE_SPINLOCK(meson_clk_lock); static struct clk_regmap g12a_fixed_pll_dco = { @@ -2340,56 +2337,19 @@ static struct clk_regmap *const g12a_clk_regmaps[] = { &g12a_mpll_50m, }; +static const struct meson_eeclkc_data g12a_clkc_data = { + .regmap_clks = g12a_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(g12a_clk_regmaps), + .hw_onecell_data = &g12a_hw_onecell_data +}; + static const struct of_device_id clkc_match_table[] = { - { .compatible = "amlogic,g12a-clkc" }, + { .compatible = "amlogic,g12a-clkc", .data = &g12a_clkc_data }, {} }; -static int g12a_clkc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct clk_hw *input; - struct regmap *map; - int ret, i; - - /* Get the hhi system controller node */ - map = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(map)) { - dev_err(dev, - "failed to get HHI regmap\n"); - return PTR_ERR(map); - } - - input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); - if (IS_ERR(input)) { - ret = PTR_ERR(input); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get input clock"); - return ret; - } - - /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(g12a_clk_regmaps); i++) - g12a_clk_regmaps[i]->map = map; - - for (i = 0; i < g12a_hw_onecell_data.num; i++) { - /* array might be sparse */ - if (!g12a_hw_onecell_data.hws[i]) - continue; - - ret = devm_clk_hw_register(dev, g12a_hw_onecell_data.hws[i]); - if (ret) { - dev_err(dev, "Clock registration failed\n"); - return ret; - } - } - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, - &g12a_hw_onecell_data); -} - static struct platform_driver g12a_driver = { - .probe = g12a_clkc_probe, + .probe = meson_eeclkc_probe, .driver = { .name = "g12a-clkc", .of_match_table = clkc_match_table, diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 3741db9a9fe1..04df2e208ed6 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -4,19 +4,17 @@ * Michael Turquette */ -#include #include #include #include -#include #include -#include #include "gxbb.h" #include "clk-input.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-mpll.h" +#include "meson-eeclk.h" #include "vid-pll-div.h" #define IN_PREFIX "ee-in-" @@ -2859,22 +2857,6 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = { }; static struct clk_regmap *const gxbb_clk_regmaps[] = { - &gxbb_gp0_pll_dco, - &gxbb_hdmi_pll, - &gxbb_hdmi_pll_od, - &gxbb_hdmi_pll_od2, - &gxbb_hdmi_pll_dco, -}; - -static struct clk_regmap *const gxl_clk_regmaps[] = { - &gxl_gp0_pll_dco, - &gxl_hdmi_pll, - &gxl_hdmi_pll_od, - &gxl_hdmi_pll_od2, - &gxl_hdmi_pll_dco, -}; - -static struct clk_regmap *const gx_clk_regmaps[] = { &gxbb_clk81, &gxbb_ddr, &gxbb_dos, @@ -3061,23 +3043,216 @@ static struct clk_regmap *const gx_clk_regmaps[] = { &gxbb_hdmi_sel, &gxbb_hdmi_div, &gxbb_hdmi, + &gxbb_gp0_pll_dco, + &gxbb_hdmi_pll, + &gxbb_hdmi_pll_od, + &gxbb_hdmi_pll_od2, + &gxbb_hdmi_pll_dco, }; -struct clkc_data { - struct clk_regmap *const *regmap_clks; - unsigned int regmap_clks_count; - struct clk_hw_onecell_data *hw_onecell_data; +static struct clk_regmap *const gxl_clk_regmaps[] = { + &gxbb_clk81, + &gxbb_ddr, + &gxbb_dos, + &gxbb_isa, + &gxbb_pl301, + &gxbb_periphs, + &gxbb_spicc, + &gxbb_i2c, + &gxbb_sar_adc, + &gxbb_smart_card, + &gxbb_rng0, + &gxbb_uart0, + &gxbb_sdhc, + &gxbb_stream, + &gxbb_async_fifo, + &gxbb_sdio, + &gxbb_abuf, + &gxbb_hiu_iface, + &gxbb_assist_misc, + &gxbb_spi, + &gxbb_i2s_spdif, + &gxbb_eth, + &gxbb_demux, + &gxbb_aiu_glue, + &gxbb_iec958, + &gxbb_i2s_out, + &gxbb_amclk, + &gxbb_aififo2, + &gxbb_mixer, + &gxbb_mixer_iface, + &gxbb_adc, + &gxbb_blkmv, + &gxbb_aiu, + &gxbb_uart1, + &gxbb_g2d, + &gxbb_usb0, + &gxbb_usb1, + &gxbb_reset, + &gxbb_nand, + &gxbb_dos_parser, + &gxbb_usb, + &gxbb_vdin1, + &gxbb_ahb_arb0, + &gxbb_efuse, + &gxbb_boot_rom, + &gxbb_ahb_data_bus, + &gxbb_ahb_ctrl_bus, + &gxbb_hdmi_intr_sync, + &gxbb_hdmi_pclk, + &gxbb_usb1_ddr_bridge, + &gxbb_usb0_ddr_bridge, + &gxbb_mmc_pclk, + &gxbb_dvin, + &gxbb_uart2, + &gxbb_sana, + &gxbb_vpu_intr, + &gxbb_sec_ahb_ahb3_bridge, + &gxbb_clk81_a53, + &gxbb_vclk2_venci0, + &gxbb_vclk2_venci1, + &gxbb_vclk2_vencp0, + &gxbb_vclk2_vencp1, + &gxbb_gclk_venci_int0, + &gxbb_gclk_vencp_int, + &gxbb_dac_clk, + &gxbb_aoclk_gate, + &gxbb_iec958_gate, + &gxbb_enc480p, + &gxbb_rng1, + &gxbb_gclk_venci_int1, + &gxbb_vclk2_venclmcc, + &gxbb_vclk2_vencl, + &gxbb_vclk_other, + &gxbb_edp, + &gxbb_ao_media_cpu, + &gxbb_ao_ahb_sram, + &gxbb_ao_ahb_bus, + &gxbb_ao_iface, + &gxbb_ao_i2c, + &gxbb_emmc_a, + &gxbb_emmc_b, + &gxbb_emmc_c, + &gxbb_sar_adc_clk, + &gxbb_mali_0, + &gxbb_mali_1, + &gxbb_cts_amclk, + &gxbb_cts_mclk_i958, + &gxbb_32k_clk, + &gxbb_sd_emmc_a_clk0, + &gxbb_sd_emmc_b_clk0, + &gxbb_sd_emmc_c_clk0, + &gxbb_vpu_0, + &gxbb_vpu_1, + &gxbb_vapb_0, + &gxbb_vapb_1, + &gxbb_vapb, + &gxbb_mpeg_clk_div, + &gxbb_sar_adc_clk_div, + &gxbb_mali_0_div, + &gxbb_mali_1_div, + &gxbb_cts_mclk_i958_div, + &gxbb_32k_clk_div, + &gxbb_sd_emmc_a_clk0_div, + &gxbb_sd_emmc_b_clk0_div, + &gxbb_sd_emmc_c_clk0_div, + &gxbb_vpu_0_div, + &gxbb_vpu_1_div, + &gxbb_vapb_0_div, + &gxbb_vapb_1_div, + &gxbb_mpeg_clk_sel, + &gxbb_sar_adc_clk_sel, + &gxbb_mali_0_sel, + &gxbb_mali_1_sel, + &gxbb_mali, + &gxbb_cts_amclk_sel, + &gxbb_cts_mclk_i958_sel, + &gxbb_cts_i958, + &gxbb_32k_clk_sel, + &gxbb_sd_emmc_a_clk0_sel, + &gxbb_sd_emmc_b_clk0_sel, + &gxbb_sd_emmc_c_clk0_sel, + &gxbb_vpu_0_sel, + &gxbb_vpu_1_sel, + &gxbb_vpu, + &gxbb_vapb_0_sel, + &gxbb_vapb_1_sel, + &gxbb_vapb_sel, + &gxbb_mpll0, + &gxbb_mpll1, + &gxbb_mpll2, + &gxbb_mpll0_div, + &gxbb_mpll1_div, + &gxbb_mpll2_div, + &gxbb_cts_amclk_div, + &gxbb_fixed_pll, + &gxbb_sys_pll, + &gxbb_mpll_prediv, + &gxbb_fclk_div2, + &gxbb_fclk_div3, + &gxbb_fclk_div4, + &gxbb_fclk_div5, + &gxbb_fclk_div7, + &gxbb_vdec_1_sel, + &gxbb_vdec_1_div, + &gxbb_vdec_1, + &gxbb_vdec_hevc_sel, + &gxbb_vdec_hevc_div, + &gxbb_vdec_hevc, + &gxbb_gen_clk_sel, + &gxbb_gen_clk_div, + &gxbb_gen_clk, + &gxbb_fixed_pll_dco, + &gxbb_sys_pll_dco, + &gxbb_gp0_pll, + &gxbb_vid_pll, + &gxbb_vid_pll_sel, + &gxbb_vid_pll_div, + &gxbb_vclk, + &gxbb_vclk_sel, + &gxbb_vclk_div, + &gxbb_vclk_input, + &gxbb_vclk_div1, + &gxbb_vclk_div2_en, + &gxbb_vclk_div4_en, + &gxbb_vclk_div6_en, + &gxbb_vclk_div12_en, + &gxbb_vclk2, + &gxbb_vclk2_sel, + &gxbb_vclk2_div, + &gxbb_vclk2_input, + &gxbb_vclk2_div1, + &gxbb_vclk2_div2_en, + &gxbb_vclk2_div4_en, + &gxbb_vclk2_div6_en, + &gxbb_vclk2_div12_en, + &gxbb_cts_enci, + &gxbb_cts_enci_sel, + &gxbb_cts_encp, + &gxbb_cts_encp_sel, + &gxbb_cts_vdac, + &gxbb_cts_vdac_sel, + &gxbb_hdmi_tx, + &gxbb_hdmi_tx_sel, + &gxbb_hdmi_sel, + &gxbb_hdmi_div, + &gxbb_hdmi, + &gxl_gp0_pll_dco, + &gxl_hdmi_pll, + &gxl_hdmi_pll_od, + &gxl_hdmi_pll_od2, + &gxl_hdmi_pll_dco, }; -static const struct clkc_data gxbb_clkc_data = { +static const struct meson_eeclkc_data gxbb_clkc_data = { .regmap_clks = gxbb_clk_regmaps, - .regmap_clks_count = ARRAY_SIZE(gxbb_clk_regmaps), + .regmap_clk_num = ARRAY_SIZE(gxbb_clk_regmaps), .hw_onecell_data = &gxbb_hw_onecell_data, }; -static const struct clkc_data gxl_clkc_data = { +static const struct meson_eeclkc_data gxl_clkc_data = { .regmap_clks = gxl_clk_regmaps, - .regmap_clks_count = ARRAY_SIZE(gxl_clk_regmaps), + .regmap_clk_num = ARRAY_SIZE(gxl_clk_regmaps), .hw_onecell_data = &gxl_hw_onecell_data, }; @@ -3087,61 +3262,8 @@ static const struct of_device_id clkc_match_table[] = { {}, }; -static int gxbb_clkc_probe(struct platform_device *pdev) -{ - const struct clkc_data *clkc_data; - struct clk_hw *input; - struct regmap *map; - int ret, i; - struct device *dev = &pdev->dev; - - clkc_data = of_device_get_match_data(dev); - if (!clkc_data) - return -EINVAL; - - /* Get the hhi system controller node if available */ - map = syscon_node_to_regmap(of_get_parent(dev->of_node)); - if (IS_ERR(map)) { - dev_err(dev, "failed to get HHI regmap\n"); - return PTR_ERR(map); - } - - input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); - if (IS_ERR(input)) { - ret = PTR_ERR(input); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get input clock"); - return ret; - } - - /* Populate regmap for the common regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(gx_clk_regmaps); i++) - gx_clk_regmaps[i]->map = map; - - /* Populate regmap for soc specific clocks */ - for (i = 0; i < clkc_data->regmap_clks_count; i++) - clkc_data->regmap_clks[i]->map = map; - - /* Register all clks */ - for (i = 0; i < clkc_data->hw_onecell_data->num; i++) { - /* array might be sparse */ - if (!clkc_data->hw_onecell_data->hws[i]) - continue; - - ret = devm_clk_hw_register(dev, - clkc_data->hw_onecell_data->hws[i]); - if (ret) { - dev_err(dev, "Clock registration failed\n"); - return ret; - } - } - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, - clkc_data->hw_onecell_data); -} - static struct platform_driver gxbb_driver = { - .probe = gxbb_clkc_probe, + .probe = meson_eeclkc_probe, .driver = { .name = "gxbb-clkc", .of_match_table = clkc_match_table, diff --git a/drivers/clk/meson/meson-eeclk.c b/drivers/clk/meson/meson-eeclk.c new file mode 100644 index 000000000000..37a34c9c3885 --- /dev/null +++ b/drivers/clk/meson/meson-eeclk.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#include +#include +#include +#include +#include + +#include "clk-input.h" +#include "clk-regmap.h" +#include "meson-eeclk.h" + +int meson_eeclkc_probe(struct platform_device *pdev) +{ + const struct meson_eeclkc_data *data; + struct device *dev = &pdev->dev; + struct clk_hw *input; + struct regmap *map; + int ret, i; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + /* Get the hhi system controller node */ + map = syscon_node_to_regmap(of_get_parent(dev->of_node)); + if (IS_ERR(map)) { + dev_err(dev, + "failed to get HHI regmap\n"); + return PTR_ERR(map); + } + + input = meson_clk_hw_register_input(dev, "xtal", IN_PREFIX "xtal", 0); + if (IS_ERR(input)) { + ret = PTR_ERR(input); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get input clock"); + return ret; + } + + /* Populate regmap for the regmap backed clocks */ + for (i = 0; i < data->regmap_clk_num; i++) + data->regmap_clks[i]->map = map; + + for (i = 0; i < data->hw_onecell_data->num; i++) { + /* array might be sparse */ + if (!data->hw_onecell_data->hws[i]) + continue; + + ret = devm_clk_hw_register(dev, data->hw_onecell_data->hws[i]); + if (ret) { + dev_err(dev, "Clock registration failed\n"); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + data->hw_onecell_data); +} diff --git a/drivers/clk/meson/meson-eeclk.h b/drivers/clk/meson/meson-eeclk.h new file mode 100644 index 000000000000..1b809b1419fe --- /dev/null +++ b/drivers/clk/meson/meson-eeclk.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#ifndef __MESON_CLKC_H +#define __MESON_CLKC_H + +#include +#include "clk-regmap.h" + +#define IN_PREFIX "ee-in-" + +struct platform_device; + +struct meson_eeclkc_data { + struct clk_regmap *const *regmap_clks; + unsigned int regmap_clk_num; + struct clk_hw_onecell_data *hw_onecell_data; +}; + +int meson_eeclkc_probe(struct platform_device *pdev); + +#endif /* __MESON_CLKC_H */ From 50fdb52a7cda63828441cc1e7c05f1d0b77e833a Mon Sep 17 00:00:00 2001 From: Youlin Wang Date: Thu, 24 Jan 2019 12:24:16 -0800 Subject: [PATCH 1003/4277] Documentation: bindings: k3dma: Extend the k3dma driver binding to support hisi-asp Extend the k3dma driver binding to support hisi-asp hardware variants. Cc: Vinod Koul Cc: Rob Herring Cc: Mark Rutland Cc: Zhuangluan Su Cc: Tanglei Han Cc: Ryan Grachek Cc: Manivannan Sadhasivam Cc: dmaengine@vger.kernel.org Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Youlin Wang Signed-off-by: Tanglei Han Signed-off-by: John Stultz Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/k3dma.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/k3dma.txt b/Documentation/devicetree/bindings/dma/k3dma.txt index 4945aeac4dc4..10a2f15b08a3 100644 --- a/Documentation/devicetree/bindings/dma/k3dma.txt +++ b/Documentation/devicetree/bindings/dma/k3dma.txt @@ -3,7 +3,9 @@ See dma.txt first Required properties: -- compatible: Should be "hisilicon,k3-dma-1.0" +- compatible: Must be one of +- "hisilicon,k3-dma-1.0" +- "hisilicon,hisi-pcm-asp-dma-1.0" - reg: Should contain DMA registers location and length. - interrupts: Should contain one interrupt shared by all channel - #dma-cells: see dma.txt, should be 1, para number From e2d896c08ca3136f1f4c0cd225cdf09f87fa528b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 24 Jan 2019 12:24:17 -0800 Subject: [PATCH 1004/4277] Documentation: bindings: dma: Add binding for dma-channel-mask Some dma channels can be reserved for secure mode or other hardware on the SoC, so provide a binding for a bitmask listing the available channels for the kernel to use. This follows the pre-existing bcm,dma-channel-mask binding. Cc: Vinod Koul Cc: Rob Herring Cc: Mark Rutland Cc: Tanglei Han Cc: Zhuangluan Su Cc: Ryan Grachek Cc: Manivannan Sadhasivam Cc: dmaengine@vger.kernel.org Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: John Stultz Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt index 6312fb00ce8d..eeb4e4d1771e 100644 --- a/Documentation/devicetree/bindings/dma/dma.txt +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -16,6 +16,9 @@ Optional properties: - dma-channels: Number of DMA channels supported by the controller. - dma-requests: Number of DMA request signals supported by the controller. +- dma-channel-mask: Bitmask of available DMA channels in ascending order + that are not reserved by firmware and are available to + the kernel. i.e. first channel corresponds to LSB. Example: @@ -29,6 +32,7 @@ Example: #dma-cells = <1>; dma-channels = <32>; dma-requests = <127>; + dma-channel-mask = <0xfffe> }; * DMA router From d4bdc39f5bd00a2fbeb0fbd29c9cb3b595df38a2 Mon Sep 17 00:00:00 2001 From: Youlin Wang Date: Thu, 24 Jan 2019 12:24:18 -0800 Subject: [PATCH 1005/4277] dmaengine: k3dma: Upgrade k3dma driver to support hisi_asp_dma hardware On the hi3660 hardware there are two (at least) DMA controllers, the DMA-P (Peripheral DMA) and the DMA-A (Audio DMA). The two blocks are similar, but have some slight differences. This resulted in the vendor implementing two separate drivers, which after review, they have been able to condense and re-use the existing k3dma driver. Thus, this patch adds support for the new "hisi-pcm-asp-dma-1.0" compatible string in the binding. One difference with the DMA-A controller, is that it does not need to initialize a clock. So we skip this by adding and using soc data flags. After above this driver will support both k3 and hisi_asp dma hardware. Cc: Dan Williams Cc: Vinod Koul Cc: Zhuangluan Su Cc: Ryan Grachek Cc: Manivannan Sadhasivam Cc: dmaengine@vger.kernel.org Acked-by: Manivannan Sadhasivam Signed-off-by: Youlin Wang Signed-off-by: Tanglei Han [jstultz: Reworked to use of_match_data, commit msg improvements] Signed-off-by: John Stultz Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index fdec2b6cfbb0..4dce53227274 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -116,6 +116,14 @@ struct k3_dma_dev { unsigned int irq; }; + +#define K3_FLAG_NOCLK BIT(1) + +struct k3dma_soc_data { + unsigned long flags; +}; + + #define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave) static int k3_dma_config_write(struct dma_chan *chan, @@ -790,8 +798,21 @@ static int k3_dma_transfer_resume(struct dma_chan *chan) return 0; } +static const struct k3dma_soc_data k3_v1_dma_data = { + .flags = 0, +}; + +static const struct k3dma_soc_data asp_v1_dma_data = { + .flags = K3_FLAG_NOCLK, +}; + static const struct of_device_id k3_pdma_dt_ids[] = { - { .compatible = "hisilicon,k3-dma-1.0", }, + { .compatible = "hisilicon,k3-dma-1.0", + .data = &k3_v1_dma_data + }, + { .compatible = "hisilicon,hisi-pcm-asp-dma-1.0", + .data = &asp_v1_dma_data + }, {} }; MODULE_DEVICE_TABLE(of, k3_pdma_dt_ids); @@ -810,6 +831,7 @@ static struct dma_chan *k3_of_dma_simple_xlate(struct of_phandle_args *dma_spec, static int k3_dma_probe(struct platform_device *op) { + const struct k3dma_soc_data *soc_data; struct k3_dma_dev *d; const struct of_device_id *of_id; struct resource *iores; @@ -823,6 +845,10 @@ static int k3_dma_probe(struct platform_device *op) if (!d) return -ENOMEM; + soc_data = device_get_match_data(&op->dev); + if (!soc_data) + return -EINVAL; + d->base = devm_ioremap_resource(&op->dev, iores); if (IS_ERR(d->base)) return PTR_ERR(d->base); @@ -835,10 +861,12 @@ static int k3_dma_probe(struct platform_device *op) "dma-requests", &d->dma_requests); } - d->clk = devm_clk_get(&op->dev, NULL); - if (IS_ERR(d->clk)) { - dev_err(&op->dev, "no dma clk\n"); - return PTR_ERR(d->clk); + if (!(soc_data->flags & K3_FLAG_NOCLK)) { + d->clk = devm_clk_get(&op->dev, NULL); + if (IS_ERR(d->clk)) { + dev_err(&op->dev, "no dma clk\n"); + return PTR_ERR(d->clk); + } } irq = platform_get_irq(op, 0); From 1200e070d6cc5ae4ab702c460b48fefc6668eafa Mon Sep 17 00:00:00 2001 From: Li Yu Date: Thu, 24 Jan 2019 12:24:19 -0800 Subject: [PATCH 1006/4277] dmaengine: k3dma: Delete axi_config Axi_config controls whether DMA resources can be accessed in non-secure mode, such as linux kernel. The register should be set by the bootloader stage and depends on the device. Thus, this patch removes axi_config from k3dma driver. Cc: Dan Williams Cc: Vinod Koul Cc: Tanglei Han Cc: Zhuangluan Su Cc: Ryan Grachek Cc: Manivannan Sadhasivam Cc: dmaengine@vger.kernel.org Acked-by: Manivannan Sadhasivam Signed-off-by: Li Yu Signed-off-by: Guodong Xu [jstultz: Minor tweaks to commit message] Signed-off-by: John Stultz Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 4dce53227274..e415c854e291 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -52,8 +52,6 @@ #define CX_SRC 0x814 #define CX_DST 0x818 #define CX_CFG 0x81c -#define AXI_CFG 0x820 -#define AXI_CFG_DEFAULT 0x201201 #define CX_LLI_CHAIN_EN 0x2 #define CX_CFG_EN 0x1 @@ -169,7 +167,6 @@ static void k3_dma_set_desc(struct k3_dma_phy *phy, struct k3_desc_hw *hw) writel_relaxed(hw->count, phy->base + CX_CNT0); writel_relaxed(hw->saddr, phy->base + CX_SRC); writel_relaxed(hw->daddr, phy->base + CX_DST); - writel_relaxed(AXI_CFG_DEFAULT, phy->base + AXI_CFG); writel_relaxed(hw->config, phy->base + CX_CFG); } From c4994a98faf2797d8977c0d075222962d71c4112 Mon Sep 17 00:00:00 2001 From: Li Yu Date: Thu, 24 Jan 2019 12:24:20 -0800 Subject: [PATCH 1007/4277] dmaengine: k3dma: Add support for dma-channel-mask Add dma-channel-mask as a property for k3dma, it defines available dma channels which a non-secure mode driver can use. One sample usage of this is in Hi3660 SoC. DMA channel 0 is reserved to lpm3, which is a coprocessor for power management. So as a result, any request in kernel (which runs on main processor and in non-secure mode) should start from at least channel 1. Cc: Dan Williams Cc: Vinod Koul Cc: Tanglei Han Cc: Zhuangluan Su Cc: Ryan Grachek Cc: Manivannan Sadhasivam Cc: Guodong Xu Cc: dmaengine@vger.kernel.org Signed-off-by: Li Yu [jstultz: Reworked to use a channel mask] Signed-off-by: John Stultz Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index e415c854e291..5737d92eaeeb 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -111,6 +111,7 @@ struct k3_dma_dev { struct dma_pool *pool; u32 dma_channels; u32 dma_requests; + u32 dma_channel_mask; unsigned int irq; }; @@ -319,6 +320,9 @@ static void k3_dma_tasklet(unsigned long arg) /* check new channel request in d->chan_pending */ spin_lock_irq(&d->lock); for (pch = 0; pch < d->dma_channels; pch++) { + if (!(d->dma_channel_mask & (1 << pch))) + continue; + p = &d->phy[pch]; if (p->vchan == NULL && !list_empty(&d->chan_pending)) { @@ -336,6 +340,9 @@ static void k3_dma_tasklet(unsigned long arg) spin_unlock_irq(&d->lock); for (pch = 0; pch < d->dma_channels; pch++) { + if (!(d->dma_channel_mask & (1 << pch))) + continue; + if (pch_alloc & (1 << pch)) { p = &d->phy[pch]; c = p->vchan; @@ -856,6 +863,13 @@ static int k3_dma_probe(struct platform_device *op) "dma-channels", &d->dma_channels); of_property_read_u32((&op->dev)->of_node, "dma-requests", &d->dma_requests); + ret = of_property_read_u32((&op->dev)->of_node, + "dma-channel-mask", &d->dma_channel_mask); + if (ret) { + dev_warn(&op->dev, + "dma-channel-mask doesn't exist, considering all as available.\n"); + d->dma_channel_mask = (u32)~0UL; + } } if (!(soc_data->flags & K3_FLAG_NOCLK)) { @@ -887,8 +901,12 @@ static int k3_dma_probe(struct platform_device *op) return -ENOMEM; for (i = 0; i < d->dma_channels; i++) { - struct k3_dma_phy *p = &d->phy[i]; + struct k3_dma_phy *p; + if (!(d->dma_channel_mask & BIT(i))) + continue; + + p = &d->phy[i]; p->idx = i; p->base = d->base + i * 0x40; } From 38f7ae9bdfb6570271c7429d8d72784465c6281e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 4 Feb 2019 04:58:52 -0500 Subject: [PATCH 1008/4277] genirq: export irq_chip_set_wake_parent symbol Export the irq_chip_set_wake_parent symbol so that drivers with hierarchical IRQ chips can be built as a module. Signed-off-by: Brian Masney Reported-by: Mark Brown Acked-by: Marc Zyngier Signed-off-by: Linus Walleij --- kernel/irq/chip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 34e969069488..086d5a34b5a0 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -1381,6 +1381,7 @@ int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) return -ENOSYS; } +EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent); #endif /** From 5c713d9394f3aabb73975dbf4db3c6502dc68956 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 4 Feb 2019 04:58:53 -0500 Subject: [PATCH 1009/4277] pinctrl: qcom: spmi-gpio: select IRQ_DOMAIN_HIERARCHY in Kconfig Select IRQ_DOMAIN_HIERARCHY for spmi-gpio in Kconfig since this driver is now setup as a hierarchical IRQ chip. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 836e9f3eae4c..93efbffcd3a4 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -137,6 +137,7 @@ config PINCTRL_QCOM_SPMI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, From e8e4d4e95701a10691c53165c55789e5e50ba3f5 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:20 -0800 Subject: [PATCH 1010/4277] PCI: imx6: Introduce drvdata Introduce driver data struct. This will simplify handling of device specific differences. Signed-off-by: Stefan Agner [andrew.smirnov@gmail.com reformatted drvdata, to simplify future diffs] Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 56 ++++++++++++++++++--------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 52e47dac028f..1e940ba7e4c4 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -41,6 +41,10 @@ enum imx6_pcie_variants { IMX7D, }; +struct imx6_pcie_drvdata { + enum imx6_pcie_variants variant; +}; + struct imx6_pcie { struct dw_pcie *pci; int reset_gpio; @@ -53,7 +57,6 @@ struct imx6_pcie { struct reset_control *pciephy_reset; struct reset_control *apps_reset; struct reset_control *turnoff_reset; - enum imx6_pcie_variants variant; u32 tx_deemph_gen1; u32 tx_deemph_gen2_3p5db; u32 tx_deemph_gen2_6db; @@ -66,6 +69,7 @@ struct imx6_pcie { struct device *pd_pcie; /* power domain for pcie phy */ struct device *pd_pcie_phy; + const struct imx6_pcie_drvdata *drvdata; }; /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ @@ -339,7 +343,7 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { struct device *dev = imx6_pcie->pci->dev; - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX7D: reset_control_assert(imx6_pcie->pciephy_reset); reset_control_assert(imx6_pcie->apps_reset); @@ -381,7 +385,7 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) struct device *dev = pci->dev; int ret = 0; - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); if (ret) { @@ -484,7 +488,7 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) !imx6_pcie->gpio_active_high); } - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); @@ -522,7 +526,7 @@ err_pcie_phy: static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX7D: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); @@ -644,7 +648,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6Q: case IMX6SX: case IMX6QP: @@ -697,7 +701,7 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) tmp |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - if (imx6_pcie->variant != IMX7D) { + if (imx6_pcie->drvdata->variant != IMX7D) { /* * On i.MX7, DIRECT_SPEED_CHANGE behaves differently * from i.MX6 family when no link speed transition @@ -794,7 +798,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: case IMX6QP: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, @@ -820,7 +824,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) } /* Others poke directly at IOMUXC registers */ - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_PM_TURN_OFF, @@ -850,7 +854,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) clk_disable_unprepare(imx6_pcie->pcie_phy); clk_disable_unprepare(imx6_pcie->pcie_bus); - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); break; @@ -866,8 +870,8 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie) { - return (imx6_pcie->variant == IMX7D || - imx6_pcie->variant == IMX6SX); + return (imx6_pcie->drvdata->variant == IMX7D || + imx6_pcie->drvdata->variant == IMX6SX); } static int imx6_pcie_suspend_noirq(struct device *dev) @@ -933,8 +937,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) pci->ops = &dw_pcie_ops; imx6_pcie->pci = pci; - imx6_pcie->variant = - (enum imx6_pcie_variants)of_device_get_match_data(dev); + imx6_pcie->drvdata = of_device_get_match_data(dev); dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); pci->dbi_base = devm_ioremap_resource(dev, dbi_base); @@ -978,7 +981,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->pcie); } - switch (imx6_pcie->variant) { + switch (imx6_pcie->drvdata->variant) { case IMX6SX: imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, "pcie_inbound_axi"); @@ -1084,11 +1087,26 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) imx6_pcie_assert_core_reset(imx6_pcie); } +static const struct imx6_pcie_drvdata drvdata[] = { + [IMX6Q] = { + .variant = IMX6Q, + }, + [IMX6SX] = { + .variant = IMX6SX, + }, + [IMX6QP] = { + .variant = IMX6QP, + }, + [IMX7D] = { + .variant = IMX7D, + }, +}; + static const struct of_device_id imx6_pcie_of_match[] = { - { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, - { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, }, - { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, - { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, }, + { .compatible = "fsl,imx6q-pcie", .data = &drvdata[IMX6Q], }, + { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], }, + { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], }, + { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], }, {}, }; From 2f532d07f0cce72f88a4a0532ae11be6a2745697 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:21 -0800 Subject: [PATCH 1011/4277] PCI: imx6: Mark PHY functions as i.MX6 specific PCIe PHY IP block on i.MX7D differs from the one used on i.MX6 family, so none of the code in the current implementation of imx6_setup_phy_mpll() or imx6_pcie_reset_phy() is applicable. Introduce IMX6_PCIE_FLAG_IMX6_PHY and check for it in the aforementioned functions to make sure they are only executed on appropriate PCIe IP variants. Tested-by: Trent Piepho Signed-off-by: Andrey Smirnov [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 1e940ba7e4c4..26e1e974b0dd 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -41,8 +41,11 @@ enum imx6_pcie_variants { IMX7D, }; +#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) + struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; + u32 flags; }; struct imx6_pcie { @@ -255,6 +258,9 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) { u32 tmp; + if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY)) + return; + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); @@ -572,6 +578,9 @@ static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie) int mult, div; u32 val; + if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY)) + return 0; + switch (phy_rate) { case 125000000: /* @@ -1090,12 +1099,15 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) static const struct imx6_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, + .flags = IMX6_PCIE_FLAG_IMX6_PHY, }, [IMX6SX] = { .variant = IMX6SX, + .flags = IMX6_PCIE_FLAG_IMX6_PHY, }, [IMX6QP] = { .variant = IMX6QP, + .flags = IMX6_PCIE_FLAG_IMX6_PHY, }, [IMX7D] = { .variant = IMX7D, From 4c458bb347ae016e75f6c61dab95ce530f4ff6fc Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:22 -0800 Subject: [PATCH 1012/4277] PCI: imx6: Convert DIRECT_SPEED_CHANGE quirk code to use a flag Both i.MX7D and i.MX8MQ have the same behaviour when it comes to clearing DIRECT_SPEED_CHANGE bit when no speed change occurs, so to handle variants correctly add a flag instead of checking the IP block variant. Signed-off-by: Andrey Smirnov [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 26e1e974b0dd..11b73d733dae 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -42,6 +42,7 @@ enum imx6_pcie_variants { }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) +#define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1) struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; @@ -710,7 +711,8 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) tmp |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - if (imx6_pcie->drvdata->variant != IMX7D) { + if (imx6_pcie->drvdata->flags & + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE) { /* * On i.MX7, DIRECT_SPEED_CHANGE behaves differently * from i.MX6 family when no link speed transition @@ -1099,15 +1101,18 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) static const struct imx6_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, - .flags = IMX6_PCIE_FLAG_IMX6_PHY, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, }, [IMX6SX] = { .variant = IMX6SX, - .flags = IMX6_PCIE_FLAG_IMX6_PHY, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, }, [IMX6QP] = { .variant = IMX6QP, - .flags = IMX6_PCIE_FLAG_IMX6_PHY, + .flags = IMX6_PCIE_FLAG_IMX6_PHY | + IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, }, [IMX7D] = { .variant = IMX7D, From 2d8ed461dbc9bc734185db92d2b9d1bb7b586b30 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 1 Feb 2019 16:15:23 -0800 Subject: [PATCH 1013/4277] PCI: imx6: Add support for i.MX8MQ Add code needed to support i.MX8MQ variant. Signed-off-by: Andrey Smirnov Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach Cc: Bjorn Helgaas Cc: Fabio Estevam Cc: Chris Healy Cc: Lucas Stach Cc: Leonard Crestez Cc: "A.s. Dong" Cc: Richard Zhu --- .../bindings/pci/fsl,imx6q-pcie.txt | 3 +- drivers/pci/controller/dwc/Kconfig | 4 +- drivers/pci/controller/dwc/pci-imx6.c | 77 ++++++++++++++++++- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt index d514c1f2365f..920ca93870a8 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt @@ -9,6 +9,7 @@ Required properties: - "fsl,imx6sx-pcie", - "fsl,imx6qp-pcie" - "fsl,imx7d-pcie" + - "fsl,imx8mq-pcie" - reg: base address and length of the PCIe controller - interrupts: A list of interrupt outputs of the controller. Must contain an entry for each entry in the interrupt-names property. @@ -45,7 +46,7 @@ Additional required properties for imx6sx-pcie: PCIE_PHY power domains - power-domain-names: Must be "pcie", "pcie_phy" -Additional required properties for imx7d-pcie: +Additional required properties for imx7d-pcie and imx8mq-pcie: - power-domains: Must be set to a phandle pointing to PCIE_PHY power domain - resets: Must contain phandles to PCIe-related reset lines exposed by SRC IP block diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 548c58223868..6ea74b1c0d94 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -89,8 +89,8 @@ config PCI_EXYNOS select PCIE_DW_HOST config PCI_IMX6 - bool "Freescale i.MX6/7 PCIe controller" - depends on SOC_IMX6Q || SOC_IMX7D || (ARM && COMPILE_TEST) + bool "Freescale i.MX6/7/8 PCIe controller" + depends on SOC_IMX6Q || SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 11b73d733dae..c1d434ba3642 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -8,6 +8,7 @@ * Author: Sean Cross */ +#include #include #include #include @@ -32,6 +33,12 @@ #include "pcie-designware.h" +#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9) +#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10) +#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) +#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8) +#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000 + #define to_imx6_pcie(x) dev_get_drvdata((x)->dev) enum imx6_pcie_variants { @@ -39,6 +46,7 @@ enum imx6_pcie_variants { IMX6SX, IMX6QP, IMX7D, + IMX8MQ, }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) @@ -58,6 +66,7 @@ struct imx6_pcie { struct clk *pcie_inbound_axi; struct clk *pcie; struct regmap *iomuxc_gpr; + u32 controller_id; struct reset_control *pciephy_reset; struct reset_control *apps_reset; struct reset_control *turnoff_reset; @@ -275,6 +284,7 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); } +#ifdef CONFIG_ARM /* Added for PCI abort handling */ static int imx6q_pcie_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) @@ -308,6 +318,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr, return 1; } +#endif static int imx6_pcie_attach_pd(struct device *dev) { @@ -352,6 +363,7 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) switch (imx6_pcie->drvdata->variant) { case IMX7D: + case IMX8MQ: reset_control_assert(imx6_pcie->pciephy_reset); reset_control_assert(imx6_pcie->apps_reset); break; @@ -386,10 +398,17 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) } } +static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) +{ + WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ); + return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; +} + static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; struct device *dev = pci->dev; + unsigned int offset; int ret = 0; switch (imx6_pcie->drvdata->variant) { @@ -420,6 +439,19 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) break; case IMX7D: break; + case IMX8MQ: + offset = imx6_pcie_grp_offset(imx6_pcie); + /* + * Set the over ride low and enabled + * make sure that REF_CLK is turned on. + */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, offset, + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE, + 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, offset, + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN); + break; } return ret; @@ -496,6 +528,9 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) } switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + reset_control_deassert(imx6_pcie->pciephy_reset); + break; case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); @@ -531,9 +566,37 @@ err_pcie_phy: } } +static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie) +{ + unsigned int mask, val; + + if (imx6_pcie->drvdata->variant == IMX8MQ && + imx6_pcie->controller_id == 1) { + mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; + val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, + PCI_EXP_TYPE_ROOT_PORT); + } else { + mask = IMX6Q_GPR12_DEVICE_TYPE; + val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, + PCI_EXP_TYPE_ROOT_PORT); + } + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val); +} + static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + /* + * TODO: Currently this code assumes external + * oscillator is being used + */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, + imx6_pcie_grp_offset(imx6_pcie), + IMX8MQ_GPR_PCIE_REF_USE_PAD, + IMX8MQ_GPR_PCIE_REF_USE_PAD); + break; case IMX7D: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); @@ -569,8 +632,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) break; } - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); + imx6_pcie_configure_type(imx6_pcie); } static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie) @@ -667,6 +729,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev) IMX6Q_GPR12_PCIE_CTL_2); break; case IMX7D: + case IMX8MQ: reset_control_deassert(imx6_pcie->apps_reset); break; } @@ -1002,6 +1065,10 @@ static int imx6_pcie_probe(struct platform_device *pdev) } break; case IMX7D: + case IMX8MQ: + if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR) + imx6_pcie->controller_id = 1; + imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, "pciephy"); if (IS_ERR(imx6_pcie->pciephy_reset)) { @@ -1117,6 +1184,9 @@ static const struct imx6_pcie_drvdata drvdata[] = { [IMX7D] = { .variant = IMX7D, }, + [IMX8MQ] = { + .variant = IMX8MQ, + }, }; static const struct of_device_id imx6_pcie_of_match[] = { @@ -1124,6 +1194,7 @@ static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], }, { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], }, { .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], }, + { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } , {}, }; @@ -1140,6 +1211,7 @@ static struct platform_driver imx6_pcie_driver = { static int __init imx6_pcie_init(void) { +#ifdef CONFIG_ARM /* * Since probe() can be deferred we need to make sure that * hook_fault_code is not called after __init memory is freed @@ -1149,6 +1221,7 @@ static int __init imx6_pcie_init(void) */ hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0, "external abort on non-linefetch"); +#endif return platform_driver_register(&imx6_pcie_driver); } From 6e02318eaea53eaafe628c4ffc254f57b2704561 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Mon, 17 Dec 2018 22:42:03 -0500 Subject: [PATCH 1014/4277] nvme: add support for the Write Zeroes command Allow write zeroes operations (REQ_OP_WRITE_ZEROES) on the block device, if the device supports an optional command bit set for write zeroes. Add support to setup write zeroes command. Set maximum possible write zeroes sectors in one write zeroes command according to nvme write zeroes command definition. This patch was posted as a part of block-write-zeroes support implementation (https://patchwork.kernel.org/patch/9454859/), but did not make into mainline kernel as it got reverted due to failure on the Linus's machine. In this patch in order to be more cautious, we use NVMe controller's maximum hardware sector size which is calculated based on the controller's MDTS (Maximum Data Transfer Size) field to calculate the maximum sectors for the write zeroes request. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Martin K. Petersen [folded a fix from Keith Busch to properly respect NVME_QUIRK_DEALLOCATE_ZEROES] Signed-off-by: Christoph Hellwig --- drivers/nvme/host/core.c | 52 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 150e49723c15..5c2d2f1e7261 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -611,6 +611,22 @@ static blk_status_t nvme_setup_discard(struct nvme_ns *ns, struct request *req, return BLK_STS_OK; } +static inline blk_status_t nvme_setup_write_zeroes(struct nvme_ns *ns, + struct request *req, struct nvme_command *cmnd) +{ + if (ns->ctrl->quirks & NVME_QUIRK_DEALLOCATE_ZEROES) + return nvme_setup_discard(ns, req, cmnd); + + cmnd->write_zeroes.opcode = nvme_cmd_write_zeroes; + cmnd->write_zeroes.nsid = cpu_to_le32(ns->head->ns_id); + cmnd->write_zeroes.slba = + cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req))); + cmnd->write_zeroes.length = + cpu_to_le16((blk_rq_bytes(req) >> ns->lba_shift) - 1); + cmnd->write_zeroes.control = 0; + return BLK_STS_OK; +} + static inline blk_status_t nvme_setup_rw(struct nvme_ns *ns, struct request *req, struct nvme_command *cmnd) { @@ -705,7 +721,8 @@ blk_status_t nvme_setup_cmd(struct nvme_ns *ns, struct request *req, nvme_setup_flush(ns, cmd); break; case REQ_OP_WRITE_ZEROES: - /* currently only aliased to deallocate for a few ctrls: */ + ret = nvme_setup_write_zeroes(ns, req, cmd); + break; case REQ_OP_DISCARD: ret = nvme_setup_discard(ns, req, cmd); break; @@ -1509,6 +1526,37 @@ static void nvme_config_discard(struct nvme_ns *ns) blk_queue_max_write_zeroes_sectors(queue, UINT_MAX); } +static inline void nvme_config_write_zeroes(struct nvme_ns *ns) +{ + u32 max_sectors; + unsigned short bs = 1 << ns->lba_shift; + + if (!(ns->ctrl->oncs & NVME_CTRL_ONCS_WRITE_ZEROES)) + return; + /* + * Even though NVMe spec explicitly states that MDTS is not + * applicable to the write-zeroes:- "The restriction does not apply to + * commands that do not transfer data between the host and the + * controller (e.g., Write Uncorrectable ro Write Zeroes command).". + * In order to be more cautious use controller's max_hw_sectors value + * to configure the maximum sectors for the write-zeroes which is + * configured based on the controller's MDTS field in the + * nvme_init_identify() if available. + */ + if (ns->ctrl->max_hw_sectors == UINT_MAX) + max_sectors = ((u32)(USHRT_MAX + 1) * bs) >> 9; + else + max_sectors = ((u32)(ns->ctrl->max_hw_sectors + 1) * bs) >> 9; + + blk_queue_max_write_zeroes_sectors(ns->queue, max_sectors); +} + +static inline void nvme_ns_config_oncs(struct nvme_ns *ns) +{ + nvme_config_discard(ns); + nvme_config_write_zeroes(ns); +} + static void nvme_report_ns_ids(struct nvme_ctrl *ctrl, unsigned int nsid, struct nvme_id_ns *id, struct nvme_ns_ids *ids) { @@ -1562,7 +1610,7 @@ static void nvme_update_disk_info(struct gendisk *disk, capacity = 0; set_capacity(disk, capacity); - nvme_config_discard(ns); + nvme_ns_config_oncs(ns); if (id->nsattr & (1 << 0)) set_disk_ro(disk, true); From 794a4cb3d2f7c105112f9b59525533f00ac06006 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Tue, 1 Jan 2019 00:19:30 -0800 Subject: [PATCH 1015/4277] nvme: remove the .stop_ctrl callout It is used now just to flush error recovery and reconnect work items in the RDMA and TCP transports, which can simply be moved to the corresponding teardown routines. Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/core.c | 2 -- drivers/nvme/host/nvme.h | 1 - drivers/nvme/host/rdma.c | 12 +++--------- drivers/nvme/host/tcp.c | 10 +++------- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 5c2d2f1e7261..c365f0aa9433 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3639,8 +3639,6 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl) nvme_stop_keep_alive(ctrl); flush_work(&ctrl->async_event_work); cancel_work_sync(&ctrl->fw_act_work); - if (ctrl->ops->stop_ctrl) - ctrl->ops->stop_ctrl(ctrl); } EXPORT_SYMBOL_GPL(nvme_stop_ctrl); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index ab961bdeea89..a0cc733c753e 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -363,7 +363,6 @@ struct nvme_ctrl_ops { void (*submit_async_event)(struct nvme_ctrl *ctrl); void (*delete_ctrl)(struct nvme_ctrl *ctrl); int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size); - void (*stop_ctrl)(struct nvme_ctrl *ctrl); }; #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 52abc3a6de12..ac365366c2ec 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -942,14 +942,6 @@ static void nvme_rdma_teardown_io_queues(struct nvme_rdma_ctrl *ctrl, } } -static void nvme_rdma_stop_ctrl(struct nvme_ctrl *nctrl) -{ - struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl); - - cancel_work_sync(&ctrl->err_work); - cancel_delayed_work_sync(&ctrl->reconnect_work); -} - static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl) { struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl); @@ -1854,6 +1846,9 @@ static const struct blk_mq_ops nvme_rdma_admin_mq_ops = { static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown) { + cancel_work_sync(&ctrl->err_work); + cancel_delayed_work_sync(&ctrl->reconnect_work); + nvme_rdma_teardown_io_queues(ctrl, shutdown); if (shutdown) nvme_shutdown_ctrl(&ctrl->ctrl); @@ -1902,7 +1897,6 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = { .submit_async_event = nvme_rdma_submit_async_event, .delete_ctrl = nvme_rdma_delete_ctrl, .get_address = nvmf_get_address, - .stop_ctrl = nvme_rdma_stop_ctrl, }; /* diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 5f0a00425242..208ee518af65 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1822,6 +1822,9 @@ static void nvme_tcp_error_recovery_work(struct work_struct *work) static void nvme_tcp_teardown_ctrl(struct nvme_ctrl *ctrl, bool shutdown) { + cancel_work_sync(&to_tcp_ctrl(ctrl)->err_work); + cancel_delayed_work_sync(&to_tcp_ctrl(ctrl)->connect_work); + nvme_tcp_teardown_io_queues(ctrl, shutdown); if (shutdown) nvme_shutdown_ctrl(ctrl); @@ -1859,12 +1862,6 @@ out_fail: nvme_tcp_reconnect_or_remove(ctrl); } -static void nvme_tcp_stop_ctrl(struct nvme_ctrl *ctrl) -{ - cancel_work_sync(&to_tcp_ctrl(ctrl)->err_work); - cancel_delayed_work_sync(&to_tcp_ctrl(ctrl)->connect_work); -} - static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl) { struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl); @@ -2115,7 +2112,6 @@ static const struct nvme_ctrl_ops nvme_tcp_ctrl_ops = { .submit_async_event = nvme_tcp_submit_async_event, .delete_ctrl = nvme_tcp_delete_ctrl, .get_address = nvmf_get_address, - .stop_ctrl = nvme_tcp_stop_ctrl, }; static bool From 77000bc43da17d5d6bc4ebfaf44d52d43bb69492 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 4 Feb 2019 16:31:04 +0100 Subject: [PATCH 1016/4277] uio: remove the unused iov_for_each macro Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- .clang-format | 1 - include/linux/uio.h | 8 -------- 2 files changed, 9 deletions(-) diff --git a/.clang-format b/.clang-format index e6080f5834a3..c144d9c24d5d 100644 --- a/.clang-format +++ b/.clang-format @@ -259,7 +259,6 @@ ForEachMacros: - 'idr_for_each_entry_ul' - 'inet_bind_bucket_for_each' - 'inet_lhash2_for_each_icsk_rcu' - - 'iov_for_each' - 'key_for_each' - 'key_for_each_safe' - 'klp_for_each_func' diff --git a/include/linux/uio.h b/include/linux/uio.h index ecf584f6b82d..87477e1640f9 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -110,14 +110,6 @@ static inline struct iovec iov_iter_iovec(const struct iov_iter *iter) }; } -#define iov_for_each(iov, iter, start) \ - if (iov_iter_type(start) == ITER_IOVEC || \ - iov_iter_type(start) == ITER_KVEC) \ - for (iter = (start); \ - (iter).count && \ - ((iov = iov_iter_iovec(&(iter))), 1); \ - iov_iter_advance(&(iter), (iov).iov_len)) - size_t iov_iter_copy_from_user_atomic(struct page *page, struct iov_iter *i, unsigned long offset, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes); From 67f52a9540e08d3fd98d6588d75d0eb9157b2534 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 1 Feb 2019 12:20:01 -0700 Subject: [PATCH 1017/4277] efi/arm: Don't expect a return value of ptdump_debugfs_register As of commit e2a2e56e4082 ("arm64: dump: no need to check return value of debugfs_create functions") in the arm64 for-next/core branch, ptdump_debugfs_register does not have a return value, which causes a build error here: drivers/firmware/efi/arm-runtime.c:51:9: error: returning 'void' from a function with incompatible result type 'int' return ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables"); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1 error generated. The arm version is still awaiting acceptance [1] but in anticipation of that patch being merged, restructure this function to call ptdump_debugfs_register without expecting a return value. [1]: https://lore.kernel.org/lkml/20190122144114.9816-3-gregkh@linuxfoundation.org/ Acked-by: Ard Biesheuvel Signed-off-by: Nathan Chancellor Signed-off-by: Catalin Marinas --- drivers/firmware/efi/arm-runtime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 23ea1ed409d1..7ae3b797caf8 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -45,10 +45,10 @@ static struct ptdump_info efi_ptdump_info = { static int __init ptdump_init(void) { - if (!efi_enabled(EFI_RUNTIME_SERVICES)) - return 0; + if (efi_enabled(EFI_RUNTIME_SERVICES)) + ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables"); - return ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables"); + return 0; } device_initcall(ptdump_init); From 8aa67d18a466f338f79bac99f44e7891f4c71bbb Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Thu, 31 Jan 2019 18:23:37 +0000 Subject: [PATCH 1018/4277] arm64: entry: Remove unneeded need_resched() loop Since the enabling and disabling of IRQs within preempt_schedule_irq() is contained in a need_resched() loop, we don't need the outer arch code loop. Reported-by: Julien Thierry Reported-by: Will Deacon Reviewed-by: Julien Thierry Acked-by: Will Deacon Signed-off-by: Valentin Schneider Cc: Mark Rutland Cc: Marc Zyngier Cc: Julien Grall Cc: Thomas Gleixner Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Catalin Marinas --- arch/arm64/kernel/entry.S | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 0ec0c46b2c0c..4d0c81f29a60 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -611,7 +611,7 @@ el1_irq: #ifdef CONFIG_PREEMPT ldr x24, [tsk, #TSK_TI_PREEMPT] // get preempt count cbnz x24, 1f // preempt count != 0 - bl el1_preempt + bl preempt_schedule_irq // irq en/disable is done inside 1: #endif #ifdef CONFIG_TRACE_IRQFLAGS @@ -620,15 +620,6 @@ el1_irq: kernel_exit 1 ENDPROC(el1_irq) -#ifdef CONFIG_PREEMPT -el1_preempt: - mov x24, lr -1: bl preempt_schedule_irq // irq en/disable is done inside - ldr x0, [tsk, #TSK_TI_FLAGS] // get new tasks TI_FLAGS - tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling? - ret x24 -#endif - /* * EL0 mode handlers. */ From 03f2c02d8be76d2080c174cd65d2dfcbbf783708 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 14 Jan 2019 10:42:11 -0800 Subject: [PATCH 1019/4277] f2fs: run discard jobs when put_super When we umount f2fs, we need to avoid long delay due to discard commands, which is actually taking tens of seconds, if storage is very slow on UNMAP. So, this patch introduces timeout-based work on it. By default, let me give 5 seconds for discard. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 7 +++++++ fs/f2fs/f2fs.h | 5 ++++- fs/f2fs/segment.c | 11 ++++++++++- fs/f2fs/super.c | 4 +++- fs/f2fs/sysfs.c | 3 +++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index a7ce33199457..91822ce25831 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -86,6 +86,13 @@ Description: The unit size is one block, now only support configuring in range of [1, 512]. +What: /sys/fs/f2fs//umount_discard_timeout +Date: January 2019 +Contact: "Jaegeuk Kim" +Description: + Set timeout to issue discard commands during umount. + Default: 5 secs + What: /sys/fs/f2fs//max_victim_search Date: January 2014 Contact: "Jaegeuk Kim" diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0f564883e078..6b6ec5600089 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -191,6 +191,7 @@ enum { #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ #define DEF_DISABLE_INTERVAL 5 /* 5 secs */ +#define DEF_UMOUNT_DISCARD_TIMEOUT 5 /* 5 secs */ struct cp_control { int reason; @@ -310,6 +311,7 @@ struct discard_policy { bool sync; /* submit discard with REQ_SYNC flag */ bool ordered; /* issue discard by lba order */ unsigned int granularity; /* discard granularity */ + int timeout; /* discard timeout for put_super */ }; struct discard_cmd_control { @@ -1110,6 +1112,7 @@ enum { DISCARD_TIME, GC_TIME, DISABLE_TIME, + UMOUNT_DISCARD_TIMEOUT, MAX_TIME, }; @@ -3006,7 +3009,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi); void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi); -bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); +bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi); void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9b79056d705d..5b2b9be6f28d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1037,6 +1037,7 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->timeout = 0; if (discard_type == DPOLICY_BG) { dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; @@ -1424,7 +1425,14 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, int i, issued = 0; bool io_interrupted = false; + if (dpolicy->timeout != 0) + f2fs_update_time(sbi, dpolicy->timeout); + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + if (dpolicy->timeout != 0 && + f2fs_time_over(sbi, dpolicy->timeout)) + break; + if (i + 1 < dpolicy->granularity) break; @@ -1611,7 +1619,7 @@ void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi) } /* This comes from f2fs_put_super */ -bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) +bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_policy dpolicy; @@ -1619,6 +1627,7 @@ bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity); + dpolicy.timeout = UMOUNT_DISCARD_TIMEOUT; __issue_discard_cmd(sbi, &dpolicy); dropped = __drop_discard_cmd(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ea514acede36..24efd76ca151 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1048,7 +1048,7 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - dropped = f2fs_wait_discard_bios(sbi); + dropped = f2fs_issue_discard_timeout(sbi); if ((f2fs_hw_support_discard(sbi) || f2fs_hw_should_discard(sbi)) && !sbi->discard_blks && !dropped) { @@ -2706,6 +2706,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi) sbi->interval_time[DISCARD_TIME] = DEF_IDLE_INTERVAL; sbi->interval_time[GC_TIME] = DEF_IDLE_INTERVAL; sbi->interval_time[DISABLE_TIME] = DEF_DISABLE_INTERVAL; + sbi->interval_time[UMOUNT_DISCARD_TIMEOUT] = + DEF_UMOUNT_DISCARD_TIMEOUT; clear_sbi_flag(sbi, SBI_NEED_FSCK); for (i = 0; i < NR_COUNT_TYPE; i++) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 5b83e1d66cb3..18c627e8fc90 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -426,6 +426,8 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, discard_idle_interval, interval_time[DISCARD_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_idle_interval, interval_time[GC_TIME]); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, + umount_discard_timeout, interval_time[UMOUNT_DISCARD_TIMEOUT]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold); @@ -483,6 +485,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(idle_interval), ATTR_LIST(discard_idle_interval), ATTR_LIST(gc_idle_interval), + ATTR_LIST(umount_discard_timeout), ATTR_LIST(iostat_enable), ATTR_LIST(readdir_ra), ATTR_LIST(gc_pin_file_thresh), From 1c1522d32ac49065f88e5a8b3d6e3a5613b20118 Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Fri, 1 Feb 2019 22:55:54 +0000 Subject: [PATCH 1020/4277] EDAC, mce_amd: Print ExtErrorCode and description on a single line Save a log line by printing the extended error code and the description on a single line. This is similar to how errors are printed in other subsystems, e.g. "#, description". If we don't have a valid description then only the number/code is printed. Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: linux-edac Cc: Mauro Carvalho Chehab Cc: Tony Luck Cc: x86@kernel.org Link: https://lkml.kernel.org/r/20190201225534.8177-6-Yazen.Ghannam@amd.com --- drivers/edac/mce_amd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 7e29ceabdf6f..f286b880f981 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -965,13 +965,12 @@ static void decode_smca_error(struct mce *m) ip_name = smca_get_long_name(bank_type); - pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec); + pr_emerg(HW_ERR "%s Ext. Error Code: %d", ip_name, xec); /* Only print the decode of valid error codes */ if (xec < smca_mce_descs[bank_type].num_descs && (hwid->xec_bitmap & BIT_ULL(xec))) { - pr_emerg(HW_ERR "%s Error: ", ip_name); - pr_cont("%s.\n", smca_mce_descs[bank_type].descs[xec]); + pr_cont(", %s.\n", smca_mce_descs[bank_type].descs[xec]); } if (bank_type == SMCA_UMC && xec == 0 && decode_dram_ecc) From f1e5b6239bdd46aa3f4e631611800ea7d10826c4 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 7 Jan 2019 11:45:38 -0600 Subject: [PATCH 1021/4277] md-linear: use struct_size() in kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; void *entry[]; }; instance = kzalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kzalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Song Liu --- drivers/md/md-linear.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c index d45c697c0ebe..5998d78aa189 100644 --- a/drivers/md/md-linear.c +++ b/drivers/md/md-linear.c @@ -96,8 +96,7 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) int i, cnt; bool discard_supported = false; - conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(struct dev_info), - GFP_KERNEL); + conf = kzalloc(struct_size(conf, disks, raid_disks), GFP_KERNEL); if (!conf) return NULL; From ebda52fa1be73952ec603b1fad685ce86ccb5ee6 Mon Sep 17 00:00:00 2001 From: Yufen Yu Date: Fri, 1 Feb 2019 10:45:01 +0800 Subject: [PATCH 1022/4277] raid1: simplify raid1_error function Remove redundance set_bit and let code simplify. Signed-off-by: Yufen Yu Signed-off-by: Song Liu --- drivers/md/raid1.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 1d54109071cc..7e63ccc4ae7b 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1603,11 +1603,9 @@ static void raid1_error(struct mddev *mddev, struct md_rdev *rdev) return; } set_bit(Blocked, &rdev->flags); - if (test_and_clear_bit(In_sync, &rdev->flags)) { + if (test_and_clear_bit(In_sync, &rdev->flags)) mddev->degraded++; - set_bit(Faulty, &rdev->flags); - } else - set_bit(Faulty, &rdev->flags); + set_bit(Faulty, &rdev->flags); spin_unlock_irqrestore(&conf->device_lock, flags); /* * if recovery is running, make sure it aborts. From 10f56242e3158f6c17579921ebbba4a0d5188dc2 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:40 +0200 Subject: [PATCH 1023/4277] IB/mlx5: Fix the locking of SRQ objects in ODP events QP and SRQ objects are stored in different containers so the action to get and lock a common resource during ODP event needs to address that. While here get rid of 'refcount' and 'free' fields in mlx5_core_srq struct and use the fields with same semantics in common structure. Fixes: 032080ab43ac ("IB/mlx5: Lock QP during page fault handling") Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/cq.c | 4 ++-- drivers/infiniband/hw/mlx5/odp.c | 13 ++++++++----- drivers/infiniband/hw/mlx5/srq.h | 2 -- drivers/infiniband/hw/mlx5/srq_cmd.c | 16 +++++++--------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index c283c32f30fe..18704e503508 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -187,8 +187,8 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe, wqe_ctr = be16_to_cpu(cqe->wqe_counter); wc->wr_id = srq->wrid[wqe_ctr]; mlx5_ib_free_srq_wqe(srq, wqe_ctr); - if (msrq && atomic_dec_and_test(&msrq->refcount)) - complete(&msrq->free); + if (msrq) + mlx5_core_res_put(&msrq->common); } } else { wq = &qp->rq; diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 3e0d5885c026..2cbe4320513b 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1115,22 +1115,25 @@ invalid_transport_or_opcode: static inline struct mlx5_core_rsc_common *odp_get_rsc(struct mlx5_ib_dev *dev, u32 wq_num, int pf_type) { - enum mlx5_res_type res_type; + struct mlx5_core_rsc_common *common = NULL; + struct mlx5_core_srq *srq; switch (pf_type) { case MLX5_WQE_PF_TYPE_RMP: - res_type = MLX5_RES_SRQ; + srq = mlx5_cmd_get_srq(dev, wq_num); + if (srq) + common = &srq->common; break; case MLX5_WQE_PF_TYPE_REQ_SEND_OR_WRITE: case MLX5_WQE_PF_TYPE_RESP: case MLX5_WQE_PF_TYPE_REQ_READ_OR_ATOMIC: - res_type = MLX5_RES_QP; + common = mlx5_core_res_hold(dev->mdev, wq_num, MLX5_RES_QP); break; default: - return NULL; + break; } - return mlx5_core_res_hold(dev->mdev, wq_num, res_type); + return common; } static inline struct mlx5_ib_qp *res_to_qp(struct mlx5_core_rsc_common *res) diff --git a/drivers/infiniband/hw/mlx5/srq.h b/drivers/infiniband/hw/mlx5/srq.h index 75eb5839ae95..c330af35ff10 100644 --- a/drivers/infiniband/hw/mlx5/srq.h +++ b/drivers/infiniband/hw/mlx5/srq.h @@ -46,8 +46,6 @@ struct mlx5_core_srq { int wqe_shift; void (*event)(struct mlx5_core_srq *srq, enum mlx5_event e); - atomic_t refcount; - struct completion free; u16 uid; }; diff --git a/drivers/infiniband/hw/mlx5/srq_cmd.c b/drivers/infiniband/hw/mlx5/srq_cmd.c index 7aaaffbd4afa..63ac38bb3498 100644 --- a/drivers/infiniband/hw/mlx5/srq_cmd.c +++ b/drivers/infiniband/hw/mlx5/srq_cmd.c @@ -87,7 +87,7 @@ struct mlx5_core_srq *mlx5_cmd_get_srq(struct mlx5_ib_dev *dev, u32 srqn) srq = radix_tree_lookup(&table->tree, srqn); if (srq) - atomic_inc(&srq->refcount); + atomic_inc(&srq->common.refcount); spin_unlock(&table->lock); @@ -594,8 +594,8 @@ int mlx5_cmd_create_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq, if (err) return err; - atomic_set(&srq->refcount, 1); - init_completion(&srq->free); + atomic_set(&srq->common.refcount, 1); + init_completion(&srq->common.free); spin_lock_irq(&table->lock); err = radix_tree_insert(&table->tree, srq->srqn, srq); @@ -627,9 +627,8 @@ int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq) if (err) return err; - if (atomic_dec_and_test(&srq->refcount)) - complete(&srq->free); - wait_for_completion(&srq->free); + mlx5_core_res_put(&srq->common); + wait_for_completion(&srq->common.free); return 0; } @@ -685,7 +684,7 @@ static int srq_event_notifier(struct notifier_block *nb, srq = radix_tree_lookup(&table->tree, srqn); if (srq) - atomic_inc(&srq->refcount); + atomic_inc(&srq->common.refcount); spin_unlock(&table->lock); @@ -694,8 +693,7 @@ static int srq_event_notifier(struct notifier_block *nb, srq->event(srq, eqe->type); - if (atomic_dec_and_test(&srq->refcount)) - complete(&srq->free); + mlx5_core_res_put(&srq->common); return NOTIFY_OK; } From da82334219bc386ef7ea5b4b185a339a973dd513 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:41 +0200 Subject: [PATCH 1024/4277] IB/core: Allocate a bit for SRQ ODP support The ODP support matrix is per operation and per transport. The support for each transport (RC, UD, etc.) is described with a bit field. ODP for SRQ WQEs is considered a different kind of support from ODP for RQ WQs and therefore needs a different capability bit. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- include/rdma/ib_verbs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 5fc3be884444..5eefdea62831 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -268,6 +268,7 @@ enum ib_odp_transport_cap_bits { IB_ODP_SUPPORT_WRITE = 1 << 2, IB_ODP_SUPPORT_READ = 1 << 3, IB_ODP_SUPPORT_ATOMIC = 1 << 4, + IB_ODP_SUPPORT_SRQ_RECV = 1 << 5, }; struct ib_odp_caps { From 52a72e2a395fa3c5ab5df41058a8511e87215730 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:42 +0200 Subject: [PATCH 1025/4277] IB/uverbs: Expose XRC ODP device capabilities Expose XRC ODP capabilities as part of the extended device capabilities. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_cmd.c | 1 + include/rdma/ib_verbs.h | 1 + include/uapi/rdma/ib_user_verbs.h | 2 ++ 3 files changed, 4 insertions(+) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index d4f1a2ef5015..68c4ea514faf 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -3613,6 +3613,7 @@ static int ib_uverbs_ex_query_device(struct uverbs_attr_bundle *attrs) attr.odp_caps.per_transport_caps.uc_odp_caps; resp.odp_caps.per_transport_caps.ud_odp_caps = attr.odp_caps.per_transport_caps.ud_odp_caps; + resp.xrc_odp_caps = attr.odp_caps.per_transport_caps.xrc_odp_caps; resp.timestamp_mask = attr.timestamp_mask; resp.hca_core_clock = attr.hca_core_clock; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 5eefdea62831..8219c07340a9 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -277,6 +277,7 @@ struct ib_odp_caps { uint32_t rc_odp_caps; uint32_t uc_odp_caps; uint32_t ud_odp_caps; + uint32_t xrc_odp_caps; } per_transport_caps; }; diff --git a/include/uapi/rdma/ib_user_verbs.h b/include/uapi/rdma/ib_user_verbs.h index 480d9a60b68e..0474c7400268 100644 --- a/include/uapi/rdma/ib_user_verbs.h +++ b/include/uapi/rdma/ib_user_verbs.h @@ -270,6 +270,8 @@ struct ib_uverbs_ex_query_device_resp { struct ib_uverbs_tm_caps tm_caps; struct ib_uverbs_cq_moderation_caps cq_moderation_caps; __aligned_u64 max_dm_size; + __u32 xrc_odp_caps; + __u32 reserved; }; struct ib_uverbs_query_port { From 586f4e95c71a2443d0aa8d6993407ce0aaf77a09 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:43 +0200 Subject: [PATCH 1026/4277] IB/mlx5: Remove useless check in ODP handler When handling an ODP event for a receive WQE in SRQ the target QP is unknown. Therefore, it is wrong to ask if QP has a SRQ in the page-fault handler. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 2cbe4320513b..07b0f8bd6cd9 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -869,7 +869,6 @@ srcu_unlock: /** * Parse a series of data segments for page fault handling. * - * @qp the QP on which the fault occurred. * @pfault contains page fault information. * @wqe points at the first data segment in the WQE. * @wqe_end points after the end of the WQE. @@ -886,7 +885,7 @@ srcu_unlock: */ static int pagefault_data_segments(struct mlx5_ib_dev *dev, struct mlx5_pagefault *pfault, - struct mlx5_ib_qp *qp, void *wqe, + void *wqe, void *wqe_end, u32 *bytes_mapped, u32 *total_wqe_bytes, int receive_queue) { @@ -897,10 +896,6 @@ static int pagefault_data_segments(struct mlx5_ib_dev *dev, size_t bcnt; int inline_segment; - /* Skip SRQ next-WQE segment. */ - if (receive_queue && qp->ibqp.srq) - wqe += sizeof(struct mlx5_wqe_srq_next_seg); - if (bytes_mapped) *bytes_mapped = 0; if (total_wqe_bytes) @@ -1200,7 +1195,7 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, goto resolve_page_fault; } - ret = pagefault_data_segments(dev, pfault, qp, wqe, wqe_end, + ret = pagefault_data_segments(dev, pfault, wqe, wqe_end, &bytes_mapped, &total_wqe_bytes, !requestor); if (ret == -EAGAIN) { From 6ff7414a17b28e04a600e5552656ea27f6b72578 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:44 +0200 Subject: [PATCH 1027/4277] IB/mlx5: Clean mlx5_ib_mr_responder_pfault_handler() signature In the function mlx5_ib_mr_responder_pfault_handler() 1. The parameter wqe is used as read-only so there is no need to pass it by reference. 2. Remove the unused argument pfault from list of arguments. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 07b0f8bd6cd9..649aaca41af2 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1064,12 +1064,12 @@ static int mlx5_ib_mr_initiator_pfault_handler( } /* - * Parse responder WQE. Advances the wqe pointer to point at the - * scatter-gather list, and set wqe_end to the end of the WQE. + * Parse responder WQE and set wqe_end to the end of the WQE. */ -static int mlx5_ib_mr_responder_pfault_handler( - struct mlx5_ib_dev *dev, struct mlx5_pagefault *pfault, - struct mlx5_ib_qp *qp, void **wqe, void **wqe_end, int wqe_length) +static int mlx5_ib_mr_responder_pfault_handler(struct mlx5_ib_dev *dev, + struct mlx5_ib_qp *qp, void *wqe, + void **wqe_end, + int wqe_length) { struct mlx5_ib_wq *wq = &qp->rq; int wqe_size = 1 << wq->wqe_shift; @@ -1102,7 +1102,7 @@ invalid_transport_or_opcode: return -EFAULT; } - *wqe_end = *wqe + wqe_size; + *wqe_end = wqe + wqe_size; return 0; } @@ -1185,7 +1185,7 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, ret = mlx5_ib_mr_initiator_pfault_handler(dev, pfault, qp, &wqe, &wqe_end, ret); else - ret = mlx5_ib_mr_responder_pfault_handler(dev, pfault, qp, &wqe, + ret = mlx5_ib_mr_responder_pfault_handler(dev, qp, wqe, &wqe_end, ret); if (ret < 0) goto resolve_page_fault; From 29917f4750a0318d682c4b34e8bee2b877530d6e Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:45 +0200 Subject: [PATCH 1028/4277] IB/mlx5: Add XRC initiator ODP support Skip XRC segment in the beginning of a send WQE and fetch ODP XRC capabilities when QP type is IB_QPT_XRC_INI. The rest of the handling is the same as in RC QP. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 649aaca41af2..c3bd9118ec9b 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1020,6 +1020,10 @@ static int mlx5_ib_mr_initiator_pfault_handler( MLX5_WQE_CTRL_OPCODE_MASK; switch (qp->ibqp.qp_type) { + case IB_QPT_XRC_INI: + *wqe += sizeof(struct mlx5_wqe_xrc_seg); + transport_caps = dev->odp_caps.per_transport_caps.xrc_odp_caps; + break; case IB_QPT_RC: transport_caps = dev->odp_caps.per_transport_caps.rc_odp_caps; break; @@ -1039,7 +1043,7 @@ static int mlx5_ib_mr_initiator_pfault_handler( return -EFAULT; } - if (qp->ibqp.qp_type != IB_QPT_RC) { + if (qp->ibqp.qp_type == IB_QPT_UD) { av = *wqe; if (av->dqp_dct & cpu_to_be32(MLX5_EXTENDED_UD_AV)) *wqe += sizeof(struct mlx5_av); From fbeb4075c67080869bed6ed973b2e54514e750a2 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:46 +0200 Subject: [PATCH 1029/4277] IB/mlx5: Let read user wqe also from SRQ buffer Reading a WQE from SRQ is almost identical to reading from regular RQ. The differences are the size of the queue, the size of a WQE and buffer location. Make necessary changes to mlx5_ib_read_user_wqe() to let it read a WQE from a SRQ or RQ by caller choice. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 9 +- drivers/infiniband/hw/mlx5/odp.c | 20 ++- drivers/infiniband/hw/mlx5/qp.c | 202 ++++++++++++++++++++------- 3 files changed, 171 insertions(+), 60 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 7fcc3f095371..f9817284c7a3 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1075,9 +1075,12 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, const struct ib_send_wr **bad_wr); int mlx5_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr, const struct ib_recv_wr **bad_wr); -int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index, - void *buffer, u32 length, - struct mlx5_ib_qp_base *base); +int mlx5_ib_read_user_wqe_sq(struct mlx5_ib_qp *qp, int wqe_index, void *buffer, + int buflen, size_t *bc); +int mlx5_ib_read_user_wqe_rq(struct mlx5_ib_qp *qp, int wqe_index, void *buffer, + int buflen, size_t *bc); +int mlx5_ib_read_user_wqe_srq(struct mlx5_ib_srq *srq, int wqe_index, + void *buffer, int buflen, size_t *bc); struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, const struct ib_cq_init_attr *attr, struct ib_ucontext *context, diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index c3bd9118ec9b..f4e47e71b13e 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1154,6 +1154,7 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, int requestor = pfault->type & MLX5_PFAULT_REQUESTOR; struct mlx5_core_rsc_common *res; struct mlx5_ib_qp *qp; + size_t bytes_copied; res = odp_get_rsc(dev, pfault->wqe.wq_num, pfault->type); if (!res) { @@ -1176,9 +1177,16 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, goto resolve_page_fault; } - ret = mlx5_ib_read_user_wqe(qp, requestor, wqe_index, buffer, - PAGE_SIZE, &qp->trans_qp.base); - if (ret < 0) { + if (requestor) + ret = mlx5_ib_read_user_wqe_sq(qp, wqe_index, + buffer, PAGE_SIZE, + &bytes_copied); + else + ret = mlx5_ib_read_user_wqe_rq(qp, wqe_index, + buffer, PAGE_SIZE, + &bytes_copied); + + if (ret) { mlx5_ib_err(dev, "Failed reading a WQE following page fault, error=%d, wqe_index=%x, qpn=%x\n", ret, wqe_index, pfault->token); goto resolve_page_fault; @@ -1187,10 +1195,12 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, wqe = buffer; if (requestor) ret = mlx5_ib_mr_initiator_pfault_handler(dev, pfault, qp, &wqe, - &wqe_end, ret); + &wqe_end, + bytes_copied); else ret = mlx5_ib_mr_responder_pfault_handler(dev, qp, wqe, - &wqe_end, ret); + &wqe_end, + bytes_copied); if (ret < 0) goto resolve_page_fault; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 529e76f67cb6..8cde49de9b2e 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -109,75 +109,173 @@ static int is_sqp(enum ib_qp_type qp_type) } /** - * mlx5_ib_read_user_wqe() - Copy a user-space WQE to kernel space. + * mlx5_ib_read_user_wqe_common() - Copy a WQE (or part of) from user WQ + * to kernel buffer * - * @qp: QP to copy from. - * @send: copy from the send queue when non-zero, use the receive queue - * otherwise. - * @wqe_index: index to start copying from. For send work queues, the - * wqe_index is in units of MLX5_SEND_WQE_BB. - * For receive work queue, it is the number of work queue - * element in the queue. - * @buffer: destination buffer. - * @length: maximum number of bytes to copy. + * @umem: User space memory where the WQ is + * @buffer: buffer to copy to + * @buflen: buffer length + * @wqe_index: index of WQE to copy from + * @wq_offset: offset to start of WQ + * @wq_wqe_cnt: number of WQEs in WQ + * @wq_wqe_shift: log2 of WQE size + * @bcnt: number of bytes to copy + * @bytes_copied: number of bytes to copy (return value) * - * Copies at least a single WQE, but may copy more data. + * Copies from start of WQE bcnt or less bytes. + * Does not gurantee to copy the entire WQE. * - * Return: the number of bytes copied, or an error code. + * Return: zero on success, or an error code. */ -int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index, - void *buffer, u32 length, - struct mlx5_ib_qp_base *base) +static int mlx5_ib_read_user_wqe_common(struct ib_umem *umem, + void *buffer, + u32 buflen, + int wqe_index, + int wq_offset, + int wq_wqe_cnt, + int wq_wqe_shift, + int bcnt, + size_t *bytes_copied) { - struct ib_device *ibdev = qp->ibqp.device; - struct mlx5_ib_dev *dev = to_mdev(ibdev); - struct mlx5_ib_wq *wq = send ? &qp->sq : &qp->rq; - size_t offset; - size_t wq_end; - struct ib_umem *umem = base->ubuffer.umem; - u32 first_copy_length; - int wqe_length; + size_t offset = wq_offset + ((wqe_index % wq_wqe_cnt) << wq_wqe_shift); + size_t wq_end = wq_offset + (wq_wqe_cnt << wq_wqe_shift); + size_t copy_length; int ret; - if (wq->wqe_cnt == 0) { - mlx5_ib_dbg(dev, "mlx5_ib_read_user_wqe for a QP with wqe_cnt == 0. qp_type: 0x%x\n", - qp->ibqp.qp_type); - return -EINVAL; - } + /* don't copy more than requested, more than buffer length or + * beyond WQ end + */ + copy_length = min_t(u32, buflen, wq_end - offset); + copy_length = min_t(u32, copy_length, bcnt); - offset = wq->offset + ((wqe_index % wq->wqe_cnt) << wq->wqe_shift); - wq_end = wq->offset + (wq->wqe_cnt << wq->wqe_shift); - - if (send && length < sizeof(struct mlx5_wqe_ctrl_seg)) - return -EINVAL; - - if (offset > umem->length || - (send && offset + sizeof(struct mlx5_wqe_ctrl_seg) > umem->length)) - return -EINVAL; - - first_copy_length = min_t(u32, offset + length, wq_end) - offset; - ret = ib_umem_copy_from(buffer, umem, offset, first_copy_length); + ret = ib_umem_copy_from(buffer, umem, offset, copy_length); if (ret) return ret; - if (send) { - struct mlx5_wqe_ctrl_seg *ctrl = buffer; - int ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK; + if (!ret && bytes_copied) + *bytes_copied = copy_length; - wqe_length = ds * MLX5_WQE_DS_UNITS; - } else { - wqe_length = 1 << wq->wqe_shift; - } + return 0; +} - if (wqe_length <= first_copy_length) - return first_copy_length; +int mlx5_ib_read_user_wqe_sq(struct mlx5_ib_qp *qp, + int wqe_index, + void *buffer, + int buflen, + size_t *bc) +{ + struct mlx5_ib_qp_base *base = &qp->trans_qp.base; + struct ib_umem *umem = base->ubuffer.umem; + struct mlx5_ib_wq *wq = &qp->sq; + struct mlx5_wqe_ctrl_seg *ctrl; + size_t bytes_copied; + size_t bytes_copied2; + size_t wqe_length; + int ret; + int ds; - ret = ib_umem_copy_from(buffer + first_copy_length, umem, wq->offset, - wqe_length - first_copy_length); + if (buflen < sizeof(*ctrl)) + return -EINVAL; + + /* at first read as much as possible */ + ret = mlx5_ib_read_user_wqe_common(umem, + buffer, + buflen, + wqe_index, + wq->offset, + wq->wqe_cnt, + wq->wqe_shift, + buflen, + &bytes_copied); if (ret) return ret; - return wqe_length; + /* we need at least control segment size to proceed */ + if (bytes_copied < sizeof(*ctrl)) + return -EINVAL; + + ctrl = buffer; + ds = be32_to_cpu(ctrl->qpn_ds) & MLX5_WQE_CTRL_DS_MASK; + wqe_length = ds * MLX5_WQE_DS_UNITS; + + /* if we copied enough then we are done */ + if (bytes_copied >= wqe_length) { + *bc = bytes_copied; + return 0; + } + + /* otherwise this a wrapped around wqe + * so read the remaining bytes starting + * from wqe_index 0 + */ + ret = mlx5_ib_read_user_wqe_common(umem, + buffer + bytes_copied, + buflen - bytes_copied, + 0, + wq->offset, + wq->wqe_cnt, + wq->wqe_shift, + wqe_length - bytes_copied, + &bytes_copied2); + + if (ret) + return ret; + *bc = bytes_copied + bytes_copied2; + return 0; +} + +int mlx5_ib_read_user_wqe_rq(struct mlx5_ib_qp *qp, + int wqe_index, + void *buffer, + int buflen, + size_t *bc) +{ + struct mlx5_ib_qp_base *base = &qp->trans_qp.base; + struct ib_umem *umem = base->ubuffer.umem; + struct mlx5_ib_wq *wq = &qp->rq; + size_t bytes_copied; + int ret; + + ret = mlx5_ib_read_user_wqe_common(umem, + buffer, + buflen, + wqe_index, + wq->offset, + wq->wqe_cnt, + wq->wqe_shift, + buflen, + &bytes_copied); + + if (ret) + return ret; + *bc = bytes_copied; + return 0; +} + +int mlx5_ib_read_user_wqe_srq(struct mlx5_ib_srq *srq, + int wqe_index, + void *buffer, + int buflen, + size_t *bc) +{ + struct ib_umem *umem = srq->umem; + size_t bytes_copied; + int ret; + + ret = mlx5_ib_read_user_wqe_common(umem, + buffer, + buflen, + wqe_index, + 0, + srq->msrq.max, + srq->msrq.wqe_shift, + buflen, + &bytes_copied); + + if (ret) + return ret; + *bc = bytes_copied; + return 0; } static void mlx5_ib_qp_event(struct mlx5_core_qp *qp, int type) From 08100fad5cac05244be86f4f8bf06593cc011907 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:47 +0200 Subject: [PATCH 1030/4277] IB/mlx5: Add ODP SRQ support Add changes to the WQE page-fault handler to 1. Identify that the event is for a SRQ WQE 2. Pass SRQ object instead of a QP to the function that reads the WQE 3. Parse the SRQ WQE with respect to its structure The rest is handled as for regular RQ WQE. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 86 +++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index f4e47e71b13e..90a03b0303a5 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1070,19 +1070,32 @@ static int mlx5_ib_mr_initiator_pfault_handler( /* * Parse responder WQE and set wqe_end to the end of the WQE. */ -static int mlx5_ib_mr_responder_pfault_handler(struct mlx5_ib_dev *dev, - struct mlx5_ib_qp *qp, void *wqe, - void **wqe_end, - int wqe_length) +static int mlx5_ib_mr_responder_pfault_handler_srq(struct mlx5_ib_dev *dev, + struct mlx5_ib_srq *srq, + void **wqe, void **wqe_end, + int wqe_length) +{ + int wqe_size = 1 << srq->msrq.wqe_shift; + + if (wqe_size > wqe_length) { + mlx5_ib_err(dev, "Couldn't read all of the receive WQE's content\n"); + return -EFAULT; + } + + *wqe_end = *wqe + wqe_size; + *wqe += sizeof(struct mlx5_wqe_srq_next_seg); + + return 0; +} + +static int mlx5_ib_mr_responder_pfault_handler_rq(struct mlx5_ib_dev *dev, + struct mlx5_ib_qp *qp, + void *wqe, void **wqe_end, + int wqe_length) { struct mlx5_ib_wq *wq = &qp->rq; int wqe_size = 1 << wq->wqe_shift; - if (qp->ibqp.srq) { - mlx5_ib_err(dev, "ODP fault on SRQ is not supported\n"); - return -EFAULT; - } - if (qp->wq_sig) { mlx5_ib_err(dev, "ODP fault with WQE signatures is not supported\n"); return -EFAULT; @@ -1142,6 +1155,14 @@ static inline struct mlx5_ib_qp *res_to_qp(struct mlx5_core_rsc_common *res) return to_mibqp(mqp); } +static inline struct mlx5_ib_srq *res_to_srq(struct mlx5_core_rsc_common *res) +{ + struct mlx5_core_srq *msrq = + container_of(res, struct mlx5_core_srq, common); + + return to_mibsrq(msrq); +} + static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, struct mlx5_pagefault *pfault) { @@ -1152,8 +1173,9 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, int resume_with_error = 1; u16 wqe_index = pfault->wqe.wqe_index; int requestor = pfault->type & MLX5_PFAULT_REQUESTOR; - struct mlx5_core_rsc_common *res; - struct mlx5_ib_qp *qp; + struct mlx5_core_rsc_common *res = NULL; + struct mlx5_ib_qp *qp = NULL; + struct mlx5_ib_srq *srq = NULL; size_t bytes_copied; res = odp_get_rsc(dev, pfault->wqe.wq_num, pfault->type); @@ -1166,6 +1188,10 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, case MLX5_RES_QP: qp = res_to_qp(res); break; + case MLX5_RES_SRQ: + case MLX5_RES_XSRQ: + srq = res_to_srq(res); + break; default: mlx5_ib_err(dev, "wqe page fault for unsupported type %d\n", pfault->type); goto resolve_page_fault; @@ -1177,14 +1203,21 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, goto resolve_page_fault; } - if (requestor) - ret = mlx5_ib_read_user_wqe_sq(qp, wqe_index, - buffer, PAGE_SIZE, - &bytes_copied); - else - ret = mlx5_ib_read_user_wqe_rq(qp, wqe_index, - buffer, PAGE_SIZE, - &bytes_copied); + if (qp) { + if (requestor) { + ret = mlx5_ib_read_user_wqe_sq(qp, wqe_index, + buffer, PAGE_SIZE, + &bytes_copied); + } else { + ret = mlx5_ib_read_user_wqe_rq(qp, wqe_index, + buffer, PAGE_SIZE, + &bytes_copied); + } + } else { + ret = mlx5_ib_read_user_wqe_srq(srq, wqe_index, + buffer, PAGE_SIZE, + &bytes_copied); + } if (ret) { mlx5_ib_err(dev, "Failed reading a WQE following page fault, error=%d, wqe_index=%x, qpn=%x\n", @@ -1194,13 +1227,18 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, wqe = buffer; if (requestor) - ret = mlx5_ib_mr_initiator_pfault_handler(dev, pfault, qp, &wqe, - &wqe_end, + ret = mlx5_ib_mr_initiator_pfault_handler(dev, pfault, qp, + &wqe, &wqe_end, bytes_copied); + else if (qp) + ret = mlx5_ib_mr_responder_pfault_handler_rq(dev, qp, + wqe, &wqe_end, + bytes_copied); else - ret = mlx5_ib_mr_responder_pfault_handler(dev, qp, wqe, - &wqe_end, - bytes_copied); + ret = mlx5_ib_mr_responder_pfault_handler_srq(dev, srq, + &wqe, &wqe_end, + bytes_copied); + if (ret < 0) goto resolve_page_fault; From 2e68daceac07f35b1b0d963b8a06150e68af1da5 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:48 +0200 Subject: [PATCH 1031/4277] IB/mlx5: Advertise SRQ ODP support for supported transports ODP support in SRQ is per transport capability. Based on device capabilities set this flag in device structure for future queries. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 90a03b0303a5..7000f86ccce6 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -315,6 +315,9 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) if (MLX5_CAP_ODP(dev->mdev, ud_odp_caps.send)) caps->per_transport_caps.ud_odp_caps |= IB_ODP_SUPPORT_SEND; + if (MLX5_CAP_ODP(dev->mdev, ud_odp_caps.srq_receive)) + caps->per_transport_caps.ud_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV; + if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.send)) caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_SEND; @@ -330,6 +333,9 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.atomic)) caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_ATOMIC; + if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.srq_receive)) + caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV; + if (MLX5_CAP_GEN(dev->mdev, fixed_buffer_size) && MLX5_CAP_GEN(dev->mdev, null_mkey) && MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) From 6141f8fa5bb7626191530ccc9c3937da3f6a075e Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Tue, 22 Jan 2019 08:48:50 +0200 Subject: [PATCH 1032/4277] IB/mlx5: Advertise XRC ODP support Query all per transport caps for XRC and set the appropriate bits in the per transport field of the advertised struct. Signed-off-by: Moni Shoua Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/odp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 7000f86ccce6..8d32d3f91277 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -336,6 +336,24 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.srq_receive)) caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV; + if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.send)) + caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_SEND; + + if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.receive)) + caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_RECV; + + if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.write)) + caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_WRITE; + + if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.read)) + caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_READ; + + if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.atomic)) + caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_ATOMIC; + + if (MLX5_CAP_ODP(dev->mdev, xrc_odp_caps.srq_receive)) + caps->per_transport_caps.xrc_odp_caps |= IB_ODP_SUPPORT_SRQ_RECV; + if (MLX5_CAP_GEN(dev->mdev, fixed_buffer_size) && MLX5_CAP_GEN(dev->mdev, null_mkey) && MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) From a163afc88556e099271a7b423295bc5176fcecce Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 31 Jan 2019 08:30:34 -0800 Subject: [PATCH 1033/4277] IB/core: Remove ib_sg_dma_address() and ib_sg_dma_len() Keeping single line wrapper functions is not useful. Hence remove the ib_sg_dma_address() and ib_sg_dma_len() functions. This patch does not change any functionality. Signed-off-by: Bart Van Assche Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/rw.c | 12 +++++----- drivers/infiniband/ulp/iser/iser_memory.c | 9 ++++---- drivers/infiniband/ulp/srp/ib_srp.c | 17 ++++++-------- include/rdma/ib_verbs.h | 27 ----------------------- net/rds/ib.h | 12 ++++------ net/rds/ib_fmr.c | 8 +++---- net/rds/ib_frmr.c | 4 ++-- net/rds/ib_recv.c | 8 +++---- net/rds/ib_send.c | 15 ++++++------- 9 files changed, 36 insertions(+), 76 deletions(-) diff --git a/drivers/infiniband/core/rw.c b/drivers/infiniband/core/rw.c index d22c4a2ebac6..89a5be3a2f97 100644 --- a/drivers/infiniband/core/rw.c +++ b/drivers/infiniband/core/rw.c @@ -179,7 +179,6 @@ static int rdma_rw_init_map_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp, struct scatterlist *sg, u32 sg_cnt, u32 offset, u64 remote_addr, u32 rkey, enum dma_data_direction dir) { - struct ib_device *dev = qp->pd->device; u32 max_sge = dir == DMA_TO_DEVICE ? qp->max_write_sge : qp->max_read_sge; struct ib_sge *sge; @@ -209,8 +208,8 @@ static int rdma_rw_init_map_wrs(struct rdma_rw_ctx *ctx, struct ib_qp *qp, rdma_wr->wr.sg_list = sge; for (j = 0; j < nr_sge; j++, sg = sg_next(sg)) { - sge->addr = ib_sg_dma_address(dev, sg) + offset; - sge->length = ib_sg_dma_len(dev, sg) - offset; + sge->addr = sg_dma_address(sg) + offset; + sge->length = sg_dma_len(sg) - offset; sge->lkey = qp->pd->local_dma_lkey; total_len += sge->length; @@ -236,14 +235,13 @@ static int rdma_rw_init_single_wr(struct rdma_rw_ctx *ctx, struct ib_qp *qp, struct scatterlist *sg, u32 offset, u64 remote_addr, u32 rkey, enum dma_data_direction dir) { - struct ib_device *dev = qp->pd->device; struct ib_rdma_wr *rdma_wr = &ctx->single.wr; ctx->nr_ops = 1; ctx->single.sge.lkey = qp->pd->local_dma_lkey; - ctx->single.sge.addr = ib_sg_dma_address(dev, sg) + offset; - ctx->single.sge.length = ib_sg_dma_len(dev, sg) - offset; + ctx->single.sge.addr = sg_dma_address(sg) + offset; + ctx->single.sge.length = sg_dma_len(sg) - offset; memset(rdma_wr, 0, sizeof(*rdma_wr)); if (dir == DMA_TO_DEVICE) @@ -294,7 +292,7 @@ int rdma_rw_ctx_init(struct rdma_rw_ctx *ctx, struct ib_qp *qp, u8 port_num, * Skip to the S/G entry that sg_offset falls into: */ for (;;) { - u32 len = ib_sg_dma_len(dev, sg); + u32 len = sg_dma_len(sg); if (sg_offset < len) break; diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index 394d1b9c2ff7..2ba70729d7b0 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -145,9 +145,8 @@ static void iser_data_buf_dump(struct iser_data_buf *data, for_each_sg(data->sg, sg, data->dma_nents, i) iser_dbg("sg[%d] dma_addr:0x%lX page:0x%p " "off:0x%x sz:0x%x dma_len:0x%x\n", - i, (unsigned long)ib_sg_dma_address(ibdev, sg), - sg_page(sg), sg->offset, - sg->length, ib_sg_dma_len(ibdev, sg)); + i, (unsigned long)sg_dma_address(sg), + sg_page(sg), sg->offset, sg->length, sg_dma_len(sg)); } static void iser_dump_page_vec(struct iser_page_vec *page_vec) @@ -204,8 +203,8 @@ iser_reg_dma(struct iser_device *device, struct iser_data_buf *mem, reg->rkey = device->pd->unsafe_global_rkey; else reg->rkey = 0; - reg->sge.addr = ib_sg_dma_address(device->ib_device, &sg[0]); - reg->sge.length = ib_sg_dma_len(device->ib_device, &sg[0]); + reg->sge.addr = sg_dma_address(&sg[0]); + reg->sge.length = sg_dma_len(&sg[0]); iser_dbg("Single DMA entry: lkey=0x%x, rkey=0x%x, addr=0x%llx," " length=0x%x\n", reg->sge.lkey, reg->rkey, diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 29fe46dbdbf1..84184910f038 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1600,9 +1600,8 @@ static int srp_map_sg_entry(struct srp_map_state *state, { struct srp_target_port *target = ch->target; struct srp_device *dev = target->srp_host->srp_dev; - struct ib_device *ibdev = dev->dev; - dma_addr_t dma_addr = ib_sg_dma_address(ibdev, sg); - unsigned int dma_len = ib_sg_dma_len(ibdev, sg); + dma_addr_t dma_addr = sg_dma_address(sg); + unsigned int dma_len = sg_dma_len(sg); unsigned int len = 0; int ret; @@ -1696,13 +1695,11 @@ static int srp_map_sg_dma(struct srp_map_state *state, struct srp_rdma_ch *ch, int count) { struct srp_target_port *target = ch->target; - struct srp_device *dev = target->srp_host->srp_dev; struct scatterlist *sg; int i; for_each_sg(scat, sg, count, i) { - srp_map_desc(state, ib_sg_dma_address(dev->dev, sg), - ib_sg_dma_len(dev->dev, sg), + srp_map_desc(state, sg_dma_address(sg), sg_dma_len(sg), target->global_rkey); } @@ -1852,8 +1849,8 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch, buf->len = cpu_to_be32(data_len); WARN_ON_ONCE((void *)(buf + 1) > (void *)cmd + len); for_each_sg(scat, sg, count, i) { - sge[i].addr = ib_sg_dma_address(ibdev, sg); - sge[i].length = ib_sg_dma_len(ibdev, sg); + sge[i].addr = sg_dma_address(sg); + sge[i].length = sg_dma_len(sg); sge[i].lkey = target->lkey; } req->cmd->num_sge += count; @@ -1874,9 +1871,9 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch, struct srp_direct_buf *buf; buf = (void *)cmd->add_data + cmd->add_cdb_len; - buf->va = cpu_to_be64(ib_sg_dma_address(ibdev, scat)); + buf->va = cpu_to_be64(sg_dma_address(scat)); buf->key = cpu_to_be32(target->global_rkey); - buf->len = cpu_to_be32(ib_sg_dma_len(ibdev, scat)); + buf->len = cpu_to_be32(sg_dma_len(scat)); req->nmdesc = 0; goto map_complete; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 8219c07340a9..f7e8709e48cd 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -3705,33 +3705,6 @@ static inline void ib_dma_unmap_sg_attrs(struct ib_device *dev, { dma_unmap_sg_attrs(dev->dma_device, sg, nents, direction, dma_attrs); } -/** - * ib_sg_dma_address - Return the DMA address from a scatter/gather entry - * @dev: The device for which the DMA addresses were created - * @sg: The scatter/gather entry - * - * Note: this function is obsolete. To do: change all occurrences of - * ib_sg_dma_address() into sg_dma_address(). - */ -static inline u64 ib_sg_dma_address(struct ib_device *dev, - struct scatterlist *sg) -{ - return sg_dma_address(sg); -} - -/** - * ib_sg_dma_len - Return the DMA length from a scatter/gather entry - * @dev: The device for which the DMA addresses were created - * @sg: The scatter/gather entry - * - * Note: this function is obsolete. To do: change all occurrences of - * ib_sg_dma_len() into sg_dma_len(). - */ -static inline unsigned int ib_sg_dma_len(struct ib_device *dev, - struct scatterlist *sg) -{ - return sg_dma_len(sg); -} /** * ib_dma_max_seg_size - Return the size limit of a single DMA transfer diff --git a/net/rds/ib.h b/net/rds/ib.h index 71ff356ee702..1fd1cac85da2 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -331,10 +331,8 @@ static inline void rds_ib_dma_sync_sg_for_cpu(struct ib_device *dev, unsigned int i; for_each_sg(sglist, sg, sg_dma_len, i) { - ib_dma_sync_single_for_cpu(dev, - ib_sg_dma_address(dev, sg), - ib_sg_dma_len(dev, sg), - direction); + ib_dma_sync_single_for_cpu(dev, sg_dma_address(sg), + sg_dma_len(sg), direction); } } #define ib_dma_sync_sg_for_cpu rds_ib_dma_sync_sg_for_cpu @@ -348,10 +346,8 @@ static inline void rds_ib_dma_sync_sg_for_device(struct ib_device *dev, unsigned int i; for_each_sg(sglist, sg, sg_dma_len, i) { - ib_dma_sync_single_for_device(dev, - ib_sg_dma_address(dev, sg), - ib_sg_dma_len(dev, sg), - direction); + ib_dma_sync_single_for_device(dev, sg_dma_address(sg), + sg_dma_len(sg), direction); } } #define ib_dma_sync_sg_for_device rds_ib_dma_sync_sg_for_device diff --git a/net/rds/ib_fmr.c b/net/rds/ib_fmr.c index e0f70c4051b6..31cf37da4510 100644 --- a/net/rds/ib_fmr.c +++ b/net/rds/ib_fmr.c @@ -108,8 +108,8 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, page_cnt = 0; for (i = 0; i < sg_dma_len; ++i) { - unsigned int dma_len = ib_sg_dma_len(dev, &scat[i]); - u64 dma_addr = ib_sg_dma_address(dev, &scat[i]); + unsigned int dma_len = sg_dma_len(&scat[i]); + u64 dma_addr = sg_dma_address(&scat[i]); if (dma_addr & ~PAGE_MASK) { if (i > 0) { @@ -148,8 +148,8 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, page_cnt = 0; for (i = 0; i < sg_dma_len; ++i) { - unsigned int dma_len = ib_sg_dma_len(dev, &scat[i]); - u64 dma_addr = ib_sg_dma_address(dev, &scat[i]); + unsigned int dma_len = sg_dma_len(&scat[i]); + u64 dma_addr = sg_dma_address(&scat[i]); for (j = 0; j < dma_len; j += PAGE_SIZE) dma_pages[page_cnt++] = diff --git a/net/rds/ib_frmr.c b/net/rds/ib_frmr.c index 6431a023ac89..688dcd68d4ea 100644 --- a/net/rds/ib_frmr.c +++ b/net/rds/ib_frmr.c @@ -181,8 +181,8 @@ static int rds_ib_map_frmr(struct rds_ib_device *rds_ibdev, ret = -EINVAL; for (i = 0; i < ibmr->sg_dma_len; ++i) { - unsigned int dma_len = ib_sg_dma_len(dev, &ibmr->sg[i]); - u64 dma_addr = ib_sg_dma_address(dev, &ibmr->sg[i]); + unsigned int dma_len = sg_dma_len(&ibmr->sg[i]); + u64 dma_addr = sg_dma_address(&ibmr->sg[i]); frmr->sg_byte_len += dma_len; if (dma_addr & ~PAGE_MASK) { diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index 2f16146e4ec9..672b91a9e207 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -346,8 +346,8 @@ static int rds_ib_recv_refill_one(struct rds_connection *conn, sge->length = sizeof(struct rds_header); sge = &recv->r_sge[1]; - sge->addr = ib_sg_dma_address(ic->i_cm_id->device, &recv->r_frag->f_sg); - sge->length = ib_sg_dma_len(ic->i_cm_id->device, &recv->r_frag->f_sg); + sge->addr = sg_dma_address(&recv->r_frag->f_sg); + sge->length = sg_dma_len(&recv->r_frag->f_sg); ret = 0; out: @@ -409,9 +409,7 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp) rdsdebug("recv %p ibinc %p page %p addr %lu\n", recv, recv->r_ibinc, sg_page(&recv->r_frag->f_sg), - (long) ib_sg_dma_address( - ic->i_cm_id->device, - &recv->r_frag->f_sg)); + (long)sg_dma_address(&recv->r_frag->f_sg)); /* XXX when can this fail? */ ret = ib_post_recv(ic->i_cm_id->qp, &recv->r_wr, NULL); diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index 2dcb555e6350..dc5897a3a958 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -645,16 +645,16 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm, if (i < work_alloc && scat != &rm->data.op_sg[rm->data.op_count]) { len = min(RDS_FRAG_SIZE, - ib_sg_dma_len(dev, scat) - rm->data.op_dmaoff); + sg_dma_len(scat) - rm->data.op_dmaoff); send->s_wr.num_sge = 2; - send->s_sge[1].addr = ib_sg_dma_address(dev, scat); + send->s_sge[1].addr = sg_dma_address(scat); send->s_sge[1].addr += rm->data.op_dmaoff; send->s_sge[1].length = len; bytes_sent += len; rm->data.op_dmaoff += len; - if (rm->data.op_dmaoff == ib_sg_dma_len(dev, scat)) { + if (rm->data.op_dmaoff == sg_dma_len(scat)) { scat++; rm->data.op_dmasg++; rm->data.op_dmaoff = 0; @@ -808,8 +808,8 @@ int rds_ib_xmit_atomic(struct rds_connection *conn, struct rm_atomic_op *op) } /* Convert our struct scatterlist to struct ib_sge */ - send->s_sge[0].addr = ib_sg_dma_address(ic->i_cm_id->device, op->op_sg); - send->s_sge[0].length = ib_sg_dma_len(ic->i_cm_id->device, op->op_sg); + send->s_sge[0].addr = sg_dma_address(op->op_sg); + send->s_sge[0].length = sg_dma_len(op->op_sg); send->s_sge[0].lkey = ic->i_pd->local_dma_lkey; rdsdebug("rva %Lx rpa %Lx len %u\n", op->op_remote_addr, @@ -921,9 +921,8 @@ int rds_ib_xmit_rdma(struct rds_connection *conn, struct rm_rdma_op *op) for (j = 0; j < send->s_rdma_wr.wr.num_sge && scat != &op->op_sg[op->op_count]; j++) { - len = ib_sg_dma_len(ic->i_cm_id->device, scat); - send->s_sge[j].addr = - ib_sg_dma_address(ic->i_cm_id->device, scat); + len = sg_dma_len(scat); + send->s_sge[j].addr = sg_dma_address(scat); send->s_sge[j].length = len; send->s_sge[j].lkey = ic->i_pd->local_dma_lkey; From 219a3e8676f3132d27b530c7d2d6bcab89536b57 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Mon, 21 Jan 2019 17:59:28 +0800 Subject: [PATCH 1034/4277] integrity, KEYS: add a reference to platform keyring commit 9dc92c45177a ("integrity: Define a trusted platform keyring") introduced a .platform keyring for storing preboot keys, used for verifying kernel image signatures. Currently only IMA-appraisal is able to use the keyring to verify kernel images that have their signature stored in xattr. This patch exposes the .platform keyring, making it accessible for verifying PE signed kernel images as well. Suggested-by: Mimi Zohar Signed-off-by: Kairui Song Cc: David Howells [zohar@linux.ibm.com: fixed checkpatch errors, squashed with patch fix] Signed-off-by: Mimi Zohar --- certs/system_keyring.c | 10 ++++++++++ include/keys/system_keyring.h | 8 ++++++++ security/integrity/digsig.c | 3 +++ 3 files changed, 21 insertions(+) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 81728717523d..da055e901df4 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -24,6 +24,9 @@ static struct key *builtin_trusted_keys; #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING static struct key *secondary_trusted_keys; #endif +#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING +static struct key *platform_trusted_keys; +#endif extern __initconst const u8 system_certificate_list[]; extern __initconst const unsigned long system_certificate_list_size; @@ -266,3 +269,10 @@ error: EXPORT_SYMBOL_GPL(verify_pkcs7_signature); #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ + +#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING +void __init set_platform_trusted_keys(struct key *keyring) +{ + platform_trusted_keys = keyring; +} +#endif diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 359c2f936004..42a93eda331c 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -61,5 +61,13 @@ static inline struct key *get_ima_blacklist_keyring(void) } #endif /* CONFIG_IMA_BLACKLIST_KEYRING */ +#if defined(CONFIG_INTEGRITY_PLATFORM_KEYRING) && \ + defined(CONFIG_SYSTEM_TRUSTED_KEYRING) +extern void __init set_platform_trusted_keys(struct key *keyring); +#else +static inline void set_platform_trusted_keys(struct key *keyring) +{ +} +#endif #endif /* _KEYS_SYSTEM_KEYRING_H */ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index f45d6edecf99..e19c2eb72c51 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -87,6 +87,9 @@ static int __integrity_init_keyring(const unsigned int id, key_perm_t perm, pr_info("Can't allocate %s keyring (%d)\n", keyring_name[id], err); keyring[id] = NULL; + } else { + if (id == INTEGRITY_KEYRING_PLATFORM) + set_platform_trusted_keys(keyring[id]); } return err; From c3c668e742397dfc107e44c09606cc68b37df30d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 1 Feb 2019 11:11:04 +0800 Subject: [PATCH 1035/4277] RDMA/hns: Make some function static Fixes the following sparse warnings: drivers/infiniband/hw/hns/hns_roce_hw_v2.c:5822:5: warning: symbol 'hns_roce_v2_query_srq' was not declared. Should it be static? drivers/infiniband/hw/hns/hns_roce_srq.c:158:6: warning: symbol 'hns_roce_srq_free' was not declared. Should it be static? drivers/infiniband/hw/hns/hns_roce_srq.c:81:5: warning: symbol 'hns_roce_srq_alloc' was not declared. Should it be static? Signed-off-by: YueHaibing Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- drivers/infiniband/hw/hns/hns_roce_srq.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 6a22db12fc3d..8973a0a89301 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -5818,7 +5818,7 @@ static int hns_roce_v2_modify_srq(struct ib_srq *ibsrq, return 0; } -int hns_roce_v2_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) +static int hns_roce_v2_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) { struct hns_roce_dev *hr_dev = to_hr_dev(ibsrq->device); struct hns_roce_srq *srq = to_hr_srq(ibsrq); diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c index 11be2dbf6486..a8ee2f6da967 100644 --- a/drivers/infiniband/hw/hns/hns_roce_srq.c +++ b/drivers/infiniband/hw/hns/hns_roce_srq.c @@ -78,9 +78,9 @@ static int hns_roce_hw2sw_srq(struct hns_roce_dev *dev, HNS_ROCE_CMD_TIMEOUT_MSECS); } -int hns_roce_srq_alloc(struct hns_roce_dev *hr_dev, u32 pdn, u32 cqn, u16 xrcd, - struct hns_roce_mtt *hr_mtt, u64 db_rec_addr, - struct hns_roce_srq *srq) +static int hns_roce_srq_alloc(struct hns_roce_dev *hr_dev, u32 pdn, u32 cqn, + u16 xrcd, struct hns_roce_mtt *hr_mtt, + u64 db_rec_addr, struct hns_roce_srq *srq) { struct hns_roce_srq_table *srq_table = &hr_dev->srq_table; struct hns_roce_cmd_mailbox *mailbox; @@ -155,7 +155,8 @@ err_out: return ret; } -void hns_roce_srq_free(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq) +static void hns_roce_srq_free(struct hns_roce_dev *hr_dev, + struct hns_roce_srq *srq) { struct hns_roce_srq_table *srq_table = &hr_dev->srq_table; int ret; From 278311e417be60f7caef6fcb12bda4da2711ceff Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Mon, 21 Jan 2019 17:59:29 +0800 Subject: [PATCH 1036/4277] kexec, KEYS: Make use of platform keyring for signature verify This patch allows the kexec_file_load syscall to verify the PE signed kernel image signature based on the preboot keys stored in the .platform keyring, as fall back, if the signature verification failed due to not finding the public key in the secondary or builtin keyrings. This commit adds a VERIFY_USE_PLATFORM_KEYRING similar to previous VERIFY_USE_SECONDARY_KEYRING indicating that verify_pkcs7_signature should verify the signature using platform keyring. Also, decrease the error message log level when verification failed with -ENOKEY, so that if called tried multiple time with different keyring it won't generate extra noises. Signed-off-by: Kairui Song Cc: David Howells Acked-by: Dave Young (for kexec_file_load part) [zohar@linux.ibm.com: tweaked the first paragraph of the patch description, and fixed checkpatch warning.] Signed-off-by: Mimi Zohar --- arch/x86/kernel/kexec-bzimage64.c | 14 +++++++++++--- certs/system_keyring.c | 13 ++++++++++++- include/linux/verification.h | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 278cd07228dd..e1215a600064 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -531,9 +531,17 @@ static int bzImage64_cleanup(void *loader_data) #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) { - return verify_pefile_signature(kernel, kernel_len, - VERIFY_USE_SECONDARY_KEYRING, - VERIFYING_KEXEC_PE_SIGNATURE); + int ret; + + ret = verify_pefile_signature(kernel, kernel_len, + VERIFY_USE_SECONDARY_KEYRING, + VERIFYING_KEXEC_PE_SIGNATURE); + if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING)) { + ret = verify_pefile_signature(kernel, kernel_len, + VERIFY_USE_PLATFORM_KEYRING, + VERIFYING_KEXEC_PE_SIGNATURE); + } + return ret; } #endif diff --git a/certs/system_keyring.c b/certs/system_keyring.c index da055e901df4..c05c29ae4d5d 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -240,11 +240,22 @@ int verify_pkcs7_signature(const void *data, size_t len, #else trusted_keys = builtin_trusted_keys; #endif + } else if (trusted_keys == VERIFY_USE_PLATFORM_KEYRING) { +#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING + trusted_keys = platform_trusted_keys; +#else + trusted_keys = NULL; +#endif + if (!trusted_keys) { + ret = -ENOKEY; + pr_devel("PKCS#7 platform keyring is not available\n"); + goto error; + } } ret = pkcs7_validate_trust(pkcs7, trusted_keys); if (ret < 0) { if (ret == -ENOKEY) - pr_err("PKCS#7 signature not signed with a trusted key\n"); + pr_devel("PKCS#7 signature not signed with a trusted key\n"); goto error; } diff --git a/include/linux/verification.h b/include/linux/verification.h index cfa4730d607a..018fb5f13d44 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -17,6 +17,7 @@ * should be used. */ #define VERIFY_USE_SECONDARY_KEYRING ((struct key *)1UL) +#define VERIFY_USE_PLATFORM_KEYRING ((struct key *)2UL) /* * The use to which an asymmetric key is being put. From 107dfa2e56ecdfda654d939cd464893a72086297 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 18 Dec 2018 07:57:41 -0500 Subject: [PATCH 1037/4277] encrypted-keys: fix Opt_err/Opt_error = -1 Properly start the enumeration associated with match_table_t at zero, making Opt_err/Opt_error the last enumeration value. Signed-off-by: Mimi Zohar --- security/keys/encrypted-keys/encrypted.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 389a298274d3..347108f660a1 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -60,11 +60,11 @@ static int blksize; static struct crypto_shash *hash_tfm; enum { - Opt_err = -1, Opt_new, Opt_load, Opt_update + Opt_new, Opt_load, Opt_update, Opt_err }; enum { - Opt_error = -1, Opt_default, Opt_ecryptfs, Opt_enc32 + Opt_default, Opt_ecryptfs, Opt_enc32, Opt_error }; static const match_table_t key_format_tokens = { From c8b37524d3cdbcf07426529cb83b38b1240cb54d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 15 Dec 2018 10:06:10 +0000 Subject: [PATCH 1038/4277] evm: remove set but not used variable 'xattr' Fixes gcc '-Wunused-but-set-variable' warning: security/integrity/evm/evm_main.c: In function 'init_evm': security/integrity/evm/evm_main.c:566:21: warning: variable 'xattr' set but not used [-Wunused-but-set-variable] Commit 21af76631476 ("EVM: turn evm_config_xattrnames into a list") defined and set "xattr", but never used it. [zohar@linux.ibm.com: tweaked the patch description explanation] Signed-off-by: YueHaibing Signed-off-by: Mimi Zohar --- security/integrity/evm/evm_main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 5ecaa3d6fe0b..b6d9f14bc234 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -563,7 +563,6 @@ static int __init init_evm(void) { int error; struct list_head *pos, *q; - struct xattr_list *xattr; evm_init_config(); @@ -580,11 +579,8 @@ static int __init init_evm(void) error: if (error != 0) { if (!list_empty(&evm_config_xattrnames)) { - list_for_each_safe(pos, q, &evm_config_xattrnames) { - xattr = list_entry(pos, struct xattr_list, - list); + list_for_each_safe(pos, q, &evm_config_xattrnames) list_del(pos); - } } } From fdb2410f7702f25f82804a261f90ad03422bd2c3 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 22 Jan 2019 14:06:49 -0600 Subject: [PATCH 1039/4277] ima: define ima_post_create_tmpfile() hook and add missing call If tmpfiles can be made persistent, then newly created tmpfiles need to be treated like any other new files in policy. This patch indicates which newly created tmpfiles are in policy, causing the file hash to be calculated on __fput(). Reported-by: Ignaz Forster [rgoldwyn@suse.com: Call ima_post_create_tmpfile() in vfs_tmpfile() as opposed to do_tmpfile(). This will help the case for overlayfs where copy_up is denied while overwriting a file.] Signed-off-by: Goldwyn Rodrigues Signed-off-by: Mimi Zohar --- fs/namei.c | 1 + include/linux/ima.h | 5 +++++ security/integrity/ima/ima_main.c | 35 +++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 914178cdbe94..373a7ec4b09d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3462,6 +3462,7 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag) inode->i_state |= I_LINKABLE; spin_unlock(&inode->i_lock); } + ima_post_create_tmpfile(inode); return child; out_err: diff --git a/include/linux/ima.h b/include/linux/ima.h index b5e16b8c50b7..dc12fbcf484c 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -18,6 +18,7 @@ struct linux_binprm; #ifdef CONFIG_IMA extern int ima_bprm_check(struct linux_binprm *bprm); extern int ima_file_check(struct file *file, int mask); +extern void ima_post_create_tmpfile(struct inode *inode); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_load_data(enum kernel_load_data_id id); @@ -56,6 +57,10 @@ static inline int ima_file_check(struct file *file, int mask) return 0; } +static inline void ima_post_create_tmpfile(struct inode *inode) +{ +} + static inline void ima_file_free(struct file *file) { return; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 4ffac4f5c647..357edd140c09 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -396,6 +396,33 @@ int ima_file_check(struct file *file, int mask) } EXPORT_SYMBOL_GPL(ima_file_check); +/** + * ima_post_create_tmpfile - mark newly created tmpfile as new + * @file : newly created tmpfile + * + * No measuring, appraising or auditing of newly created tmpfiles is needed. + * Skip calling process_measurement(), but indicate which newly, created + * tmpfiles are in policy. + */ +void ima_post_create_tmpfile(struct inode *inode) +{ + struct integrity_iint_cache *iint; + int must_appraise; + + must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK); + if (!must_appraise) + return; + + /* Nothing to do if we can't allocate memory */ + iint = integrity_inode_get(inode); + if (!iint) + return; + + /* needed for writing the security xattrs */ + set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); + iint->ima_file_status = INTEGRITY_PASS; +} + /** * ima_post_path_mknod - mark as a new inode * @dentry: newly created dentry @@ -413,9 +440,13 @@ void ima_post_path_mknod(struct dentry *dentry) if (!must_appraise) return; + /* Nothing to do if we can't allocate memory */ iint = integrity_inode_get(inode); - if (iint) - iint->flags |= IMA_NEW_FILE; + if (!iint) + return; + + /* needed for re-opening empty files */ + iint->flags |= IMA_NEW_FILE; } /** From e7fde070f39bc058c356cf366cb17ac2d643abb0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Jan 2019 22:37:02 +0200 Subject: [PATCH 1040/4277] evm: Use defined constant for UUID representation Instead of sizeof use pre-defined constant for UUID representation. While here, drop the implementation details of uuid_t type. Signed-off-by: Andy Shevchenko Signed-off-by: Mimi Zohar --- security/integrity/evm/evm_crypto.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 43e2dc3a60d0..c37d08118af5 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -173,8 +173,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); if ((evm_hmac_attrs & EVM_ATTR_FSUUID) && type != EVM_XATTR_PORTABLE_DIGSIG) - crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0], - sizeof(inode->i_sb->s_uuid)); + crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE); crypto_shash_final(desc, digest); } From fa40718804a1c164b62c95ca626f26308be44257 Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Tue, 29 Jan 2019 12:08:49 +0200 Subject: [PATCH 1041/4277] RDMA/rxe: Move rxe_init_av() to rxe_av.c Move the function rxe_init_av() to rxe_av.c file and use it instead of calling rxe_av_from_attr() and rxe_av_fill_ip_info(), also remove the unused rxe_dev parameter from rxe_init_av(). Signed-off-by: Kamal Heib Reviewed-by: Yuval Shaia Reviewed-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_av.c | 6 ++++++ drivers/infiniband/sw/rxe/rxe_loc.h | 1 + drivers/infiniband/sw/rxe/rxe_qp.c | 7 ++----- drivers/infiniband/sw/rxe/rxe_verbs.c | 11 ++--------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c index 26fe8d7dbc55..27a7dec18874 100644 --- a/drivers/infiniband/sw/rxe/rxe_av.c +++ b/drivers/infiniband/sw/rxe/rxe_av.c @@ -34,6 +34,12 @@ #include "rxe.h" #include "rxe_loc.h" +void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av) +{ + rxe_av_from_attr(rdma_ah_get_port_num(attr), av, attr); + rxe_av_fill_ip_info(av, attr); +} + int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr) { struct rxe_port *port; diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index a65fbd0ff42e..b517a6c86922 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -35,6 +35,7 @@ #define RXE_LOC_H /* rxe_av.c */ +void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av); int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr); diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index fd86fd2fbb26..be5d76b2bcca 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -631,14 +631,11 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, qp->attr.qkey = attr->qkey; if (mask & IB_QP_AV) { - rxe_av_from_attr(attr->port_num, &qp->pri_av, &attr->ah_attr); - rxe_av_fill_ip_info(&qp->pri_av, &attr->ah_attr); + rxe_init_av(&attr->ah_attr, &qp->pri_av); } if (mask & IB_QP_ALT_PATH) { - rxe_av_from_attr(attr->alt_port_num, &qp->alt_av, - &attr->alt_ah_attr); - rxe_av_fill_ip_info(&qp->alt_av, &attr->alt_ah_attr); + rxe_init_av(&attr->alt_ah_attr, &qp->alt_av); qp->attr.alt_port_num = attr->alt_port_num; qp->attr.alt_pkey_index = attr->alt_pkey_index; qp->attr.alt_timeout = attr->alt_timeout; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 3d01247a28db..cc5a05124ece 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -210,13 +210,6 @@ static int rxe_dealloc_pd(struct ib_pd *ibpd) return 0; } -static void rxe_init_av(struct rxe_dev *rxe, struct rdma_ah_attr *attr, - struct rxe_av *av) -{ - rxe_av_from_attr(rdma_ah_get_port_num(attr), av, attr); - rxe_av_fill_ip_info(av, attr); -} - static struct ib_ah *rxe_create_ah(struct ib_pd *ibpd, struct rdma_ah_attr *attr, u32 flags, @@ -239,7 +232,7 @@ static struct ib_ah *rxe_create_ah(struct ib_pd *ibpd, rxe_add_ref(pd); ah->pd = pd; - rxe_init_av(rxe, attr, &ah->av); + rxe_init_av(attr, &ah->av); return &ah->ibah; } @@ -253,7 +246,7 @@ static int rxe_modify_ah(struct ib_ah *ibah, struct rdma_ah_attr *attr) if (err) return err; - rxe_init_av(rxe, attr, &ah->av); + rxe_init_av(attr, &ah->av); return 0; } From 668aa15b5bf87f156ec805cb7348c785c56b82ab Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Tue, 29 Jan 2019 12:08:50 +0200 Subject: [PATCH 1042/4277] RDMA/rxe: Improve loopback marking Currently a packet is marked for loopback only if the source and destination addresses equals. This is not enough when multiple gids are present in rxe device's gid table and the traffic is from one gid to another. Fix it by marking the packet for loopback if the destination MAC address is equal to the source MAC address. Signed-off-by: Kamal Heib Reviewed-by: Yuval Shaia Tested-by: Yuval Shaia Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_av.c | 1 + drivers/infiniband/sw/rxe/rxe_net.c | 9 +++------ include/uapi/rdma/rdma_user_rxe.h | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c index 27a7dec18874..81ee756c19b8 100644 --- a/drivers/infiniband/sw/rxe/rxe_av.c +++ b/drivers/infiniband/sw/rxe/rxe_av.c @@ -38,6 +38,7 @@ void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av) { rxe_av_from_attr(rdma_ah_get_port_num(attr), av, attr); rxe_av_fill_ip_info(av, attr); + memcpy(av->dmac, attr->roce.dmac, ETH_ALEN); } int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 19f3c69916b1..3b162e92e8e8 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -384,9 +384,6 @@ static int prepare4(struct rxe_pkt_info *pkt, struct sk_buff *skb, return -EHOSTUNREACH; } - if (!memcmp(saddr, daddr, sizeof(*daddr))) - pkt->mask |= RXE_LOOPBACK_MASK; - prepare_udp_hdr(skb, cpu_to_be16(qp->src_port), cpu_to_be16(ROCE_V2_UDP_DPORT)); @@ -411,9 +408,6 @@ static int prepare6(struct rxe_pkt_info *pkt, struct sk_buff *skb, return -EHOSTUNREACH; } - if (!memcmp(saddr, daddr, sizeof(*daddr))) - pkt->mask |= RXE_LOOPBACK_MASK; - prepare_udp_hdr(skb, cpu_to_be16(qp->src_port), cpu_to_be16(ROCE_V2_UDP_DPORT)); @@ -437,6 +431,9 @@ int rxe_prepare(struct rxe_pkt_info *pkt, struct sk_buff *skb, u32 *crc) *crc = rxe_icrc_hdr(pkt, skb); + if (ether_addr_equal(skb->dev->dev_addr, av->dmac)) + pkt->mask |= RXE_LOOPBACK_MASK; + return err; } diff --git a/include/uapi/rdma/rdma_user_rxe.h b/include/uapi/rdma/rdma_user_rxe.h index 44ef6a3b7afc..aae2e696bb38 100644 --- a/include/uapi/rdma/rdma_user_rxe.h +++ b/include/uapi/rdma/rdma_user_rxe.h @@ -58,8 +58,7 @@ struct rxe_global_route { struct rxe_av { __u8 port_num; __u8 network_type; - __u16 reserved1; - __u32 reserved2; + __u8 dmac[6]; struct rxe_global_route grh; union { struct sockaddr_in _sockaddr_in; From d061effc36f7bd38a12912977a37a50ac9140d11 Mon Sep 17 00:00:00 2001 From: "Wei Hu (Xavier)" Date: Sun, 3 Feb 2019 20:43:13 +0800 Subject: [PATCH 1043/4277] RDMA/hns: Fix the Oops during rmmod or insmod ko when reset occurs In the reset process, the hns3 NIC driver notifies the RoCE driver to perform reset related processing by calling the .reset_notify() interface registered by the RoCE driver in hip08 SoC. In the current version, if a reset occurs simultaneously during the execution of rmmod or insmod ko, there may be Oops error as below: Internal error: Oops: 86000007 [#1] PREEMPT SMP Modules linked in: hns_roce(O) hns3(O) hclge(O) hnae3(O) [last unloaded: hns_roce_hw_v2] CPU: 0 PID: 14 Comm: kworker/0:1 Tainted: G O 4.19.0-ge00d540 #1 Hardware name: Huawei Technologies Co., Ltd. Workqueue: events hclge_reset_service_task [hclge] pstate: 60c00009 (nZCv daif +PAN +UAO) pc : 0xffff00000100b0b8 lr : 0xffff00000100aea0 sp : ffff000009afbab0 x29: ffff000009afbab0 x28: 0000000000000800 x27: 0000000000007ff0 x26: ffff80002f90c004 x25: 00000000000007ff x24: ffff000008f97000 x23: ffff80003efee0a8 x22: 0000000000001000 x21: ffff80002f917ff0 x20: ffff8000286ea070 x19: 0000000000000800 x18: 0000000000000400 x17: 00000000c4d3225d x16: 00000000000021b8 x15: 0000000000000400 x14: 0000000000000400 x13: 0000000000000000 x12: ffff80003fac6e30 x11: 0000800036303000 x10: 0000000000000001 x9 : 0000000000000000 x8 : ffff80003016d000 x7 : 0000000000000000 x6 : 000000000000003f x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000004 x2 : 00000000000007ff x1 : 0000000000000000 x0 : 0000000000000000 Process kworker/0:1 (pid: 14, stack limit = 0x00000000af8f0ad9) Call trace: 0xffff00000100b0b8 0xffff00000100b3a0 hns_roce_init+0x624/0xc88 [hns_roce] 0xffff000001002df8 0xffff000001006960 hclge_notify_roce_client+0x74/0xe0 [hclge] hclge_reset_service_task+0xa58/0xbc0 [hclge] process_one_work+0x1e4/0x458 worker_thread+0x40/0x450 kthread+0x12c/0x130 ret_from_fork+0x10/0x18 Code: bad PC value In the reset process, we will release the resources firstly, and after the hardware reset is completed, we will reapply resources and reconfigure the hardware. We can solve this problem by modifying both the NIC and the RoCE driver. We can modify the concurrent processing in the NIC driver to avoid calling the .reset_notify and .uninit_instance ops at the same time. And we need to modify the RoCE driver to record the reset stage and the driver's init/uninit state, and check the state in the .reset_notify, .init_instance. and uninit_instance functions to avoid NULL pointer operation. Fixes: cb7a94c9c808 ("RDMA/hns: Add reset process for RoCE in hip08") Signed-off-by: Wei Hu (Xavier) Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_device.h | 21 ++++ drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 107 +++++++++++++++++--- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 1 + 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 6fde434b22fd..0f3fe901dfd2 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -217,6 +217,26 @@ enum { HNS_ROCE_DB_PER_PAGE = PAGE_SIZE / 4 }; +enum hns_roce_reset_stage { + HNS_ROCE_STATE_NON_RST, + HNS_ROCE_STATE_RST_BEF_DOWN, + HNS_ROCE_STATE_RST_DOWN, + HNS_ROCE_STATE_RST_UNINIT, + HNS_ROCE_STATE_RST_INIT, + HNS_ROCE_STATE_RST_INITED, +}; + +enum hns_roce_instance_state { + HNS_ROCE_STATE_NON_INIT, + HNS_ROCE_STATE_INIT, + HNS_ROCE_STATE_INITED, + HNS_ROCE_STATE_UNINIT, +}; + +enum { + HNS_ROCE_RST_DIRECT_RETURN = 0, +}; + #define HNS_ROCE_CMD_SUCCESS 1 #define HNS_ROCE_PORT_DOWN 0 @@ -920,6 +940,7 @@ struct hns_roce_dev { spinlock_t bt_cmd_lock; bool active; bool is_reset; + unsigned long reset_cnt; struct hns_roce_ib_iboe iboe; struct list_head pgdir_list; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 8973a0a89301..81be86d98ba0 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -6020,6 +6020,7 @@ MODULE_DEVICE_TABLE(pci, hns_roce_hw_v2_pci_tbl); static int hns_roce_hw_v2_get_cfg(struct hns_roce_dev *hr_dev, struct hnae3_handle *handle) { + struct hns_roce_v2_priv *priv = hr_dev->priv; const struct pci_device_id *id; int i; @@ -6050,10 +6051,13 @@ static int hns_roce_hw_v2_get_cfg(struct hns_roce_dev *hr_dev, hr_dev->cmd_mod = 1; hr_dev->loop_idc = 0; + hr_dev->reset_cnt = handle->ae_algo->ops->ae_dev_reset_cnt(handle); + priv->handle = handle; + return 0; } -static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) +static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) { struct hns_roce_dev *hr_dev; int ret; @@ -6070,7 +6074,6 @@ static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) hr_dev->pci_dev = handle->pdev; hr_dev->dev = &handle->pdev->dev; - handle->priv = hr_dev; ret = hns_roce_hw_v2_get_cfg(hr_dev, handle); if (ret) { @@ -6084,6 +6087,8 @@ static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) goto error_failed_get_cfg; } + handle->priv = hr_dev; + return 0; error_failed_get_cfg: @@ -6095,7 +6100,7 @@ error_failed_kzalloc: return ret; } -static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle, +static void __hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle, bool reset) { struct hns_roce_dev *hr_dev = (struct hns_roce_dev *)handle->priv; @@ -6103,24 +6108,78 @@ static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle, if (!hr_dev) return; + handle->priv = NULL; hns_roce_exit(hr_dev); kfree(hr_dev->priv); ib_dealloc_device(&hr_dev->ib_dev); } -static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle) +static int hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) { - struct hns_roce_dev *hr_dev = (struct hns_roce_dev *)handle->priv; - struct ib_event event; + const struct hnae3_ae_ops *ops = handle->ae_algo->ops; + struct device *dev = &handle->pdev->dev; + int ret; - if (!hr_dev) { - dev_err(&handle->pdev->dev, - "Input parameter handle->priv is NULL!\n"); - return -EINVAL; + handle->rinfo.instance_state = HNS_ROCE_STATE_INIT; + + if (ops->ae_dev_resetting(handle) || ops->get_hw_reset_stat(handle)) { + handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT; + goto reset_chk_err; } + ret = __hns_roce_hw_v2_init_instance(handle); + if (ret) { + handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT; + dev_err(dev, "RoCE instance init failed! ret = %d\n", ret); + if (ops->ae_dev_resetting(handle) || + ops->get_hw_reset_stat(handle)) + goto reset_chk_err; + else + return ret; + } + + handle->rinfo.instance_state = HNS_ROCE_STATE_INITED; + + + return 0; + +reset_chk_err: + dev_err(dev, "Device is busy in resetting state.\n" + "please retry later.\n"); + + return -EBUSY; +} + +static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle, + bool reset) +{ + if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED) + return; + + handle->rinfo.instance_state = HNS_ROCE_STATE_UNINIT; + + __hns_roce_hw_v2_uninit_instance(handle, reset); + + handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT; +} +static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle) +{ + struct hns_roce_dev *hr_dev; + struct ib_event event; + + if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED) { + set_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state); + return 0; + } + + handle->rinfo.reset_state = HNS_ROCE_STATE_RST_DOWN; + clear_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state); + + hr_dev = (struct hns_roce_dev *)handle->priv; + if (!hr_dev) + return 0; + hr_dev->active = false; - hr_dev->is_reset = true; event.event = IB_EVENT_DEVICE_FATAL; event.device = &hr_dev->ib_dev; @@ -6132,17 +6191,29 @@ static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle) static int hns_roce_hw_v2_reset_notify_init(struct hnae3_handle *handle) { + struct device *dev = &handle->pdev->dev; int ret; - ret = hns_roce_hw_v2_init_instance(handle); + if (test_and_clear_bit(HNS_ROCE_RST_DIRECT_RETURN, + &handle->rinfo.state)) { + handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INITED; + return 0; + } + + handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INIT; + + dev_info(&handle->pdev->dev, "In reset process RoCE client reinit.\n"); + ret = __hns_roce_hw_v2_init_instance(handle); if (ret) { /* when reset notify type is HNAE3_INIT_CLIENT In reset notify * callback function, RoCE Engine reinitialize. If RoCE reinit * failed, we should inform NIC driver. */ handle->priv = NULL; - dev_err(&handle->pdev->dev, - "In reset process RoCE reinit failed %d.\n", ret); + dev_err(dev, "In reset process RoCE reinit failed %d.\n", ret); + } else { + handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INITED; + dev_info(dev, "Reset done, RoCE client reinit finished.\n"); } return ret; @@ -6150,8 +6221,14 @@ static int hns_roce_hw_v2_reset_notify_init(struct hnae3_handle *handle) static int hns_roce_hw_v2_reset_notify_uninit(struct hnae3_handle *handle) { + if (test_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state)) + return 0; + + handle->rinfo.reset_state = HNS_ROCE_STATE_RST_UNINIT; + dev_info(&handle->pdev->dev, "In reset process RoCE client uninit.\n"); msleep(100); - hns_roce_hw_v2_uninit_instance(handle, false); + __hns_roce_hw_v2_uninit_instance(handle, false); + return 0; } diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 242eeaeba761..938d36a5fa3d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -1602,6 +1602,7 @@ struct hns_roce_link_table_entry { #define HNS_ROCE_LINK_TABLE_NXT_PTR_M GENMASK(31, 20) struct hns_roce_v2_priv { + struct hnae3_handle *handle; struct hns_roce_v2_cmq cmq; struct hns_roce_link_table tsq; struct hns_roce_link_table tpq; From 6a04aed6afaefd5fd396f23da184298135f31e37 Mon Sep 17 00:00:00 2001 From: "Wei Hu (Xavier)" Date: Sun, 3 Feb 2019 20:43:14 +0800 Subject: [PATCH 1044/4277] RDMA/hns: Fix the chip hanging caused by sending mailbox&CMQ during reset On hi08 chip, There is a possibility of chip hanging and some errors when sending mailbox & doorbell during reset. We can fix it by prohibiting mailbox and doorbell during reset and reset occurred to ensure that hardware can work normally. Fixes: a04ff739f2a9 ("RDMA/hns: Add command queue support for hip08 RoCE driver") Signed-off-by: Wei Hu (Xavier) Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_cmd.c | 32 +++-- drivers/infiniband/hw/hns/hns_roce_device.h | 7 + drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 139 +++++++++++++++++++- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 2 + 4 files changed, 167 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_cmd.c b/drivers/infiniband/hw/hns/hns_roce_cmd.c index a0ba19d4a10e..2acf946d02e5 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cmd.c +++ b/drivers/infiniband/hw/hns/hns_roce_cmd.c @@ -176,17 +176,33 @@ int hns_roce_cmd_mbox(struct hns_roce_dev *hr_dev, u64 in_param, u64 out_param, unsigned long in_modifier, u8 op_modifier, u16 op, unsigned long timeout) { - if (hr_dev->is_reset) - return 0; + int ret; + + if (hr_dev->hw->rst_prc_mbox) { + ret = hr_dev->hw->rst_prc_mbox(hr_dev); + if (ret == CMD_RST_PRC_SUCCESS) + return 0; + else if (ret == CMD_RST_PRC_EBUSY) + return -EBUSY; + } if (hr_dev->cmd.use_events) - return hns_roce_cmd_mbox_wait(hr_dev, in_param, out_param, - in_modifier, op_modifier, op, - timeout); + ret = hns_roce_cmd_mbox_wait(hr_dev, in_param, out_param, + in_modifier, op_modifier, op, + timeout); else - return hns_roce_cmd_mbox_poll(hr_dev, in_param, out_param, - in_modifier, op_modifier, op, - timeout); + ret = hns_roce_cmd_mbox_poll(hr_dev, in_param, out_param, + in_modifier, op_modifier, op, + timeout); + + if (ret == CMD_RST_PRC_EBUSY) + return -EBUSY; + + if (ret && (hr_dev->hw->rst_prc_mbox && + hr_dev->hw->rst_prc_mbox(hr_dev) == CMD_RST_PRC_SUCCESS)) + return 0; + + return ret; } EXPORT_SYMBOL_GPL(hns_roce_cmd_mbox); diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 0f3fe901dfd2..65eb4bcc8c20 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -237,6 +237,12 @@ enum { HNS_ROCE_RST_DIRECT_RETURN = 0, }; +enum { + CMD_RST_PRC_OTHERS, + CMD_RST_PRC_SUCCESS, + CMD_RST_PRC_EBUSY, +}; + #define HNS_ROCE_CMD_SUCCESS 1 #define HNS_ROCE_PORT_DOWN 0 @@ -874,6 +880,7 @@ struct hns_roce_hw { u64 out_param, u32 in_modifier, u8 op_modifier, u16 op, u16 token, int event); int (*chk_mbox)(struct hns_roce_dev *hr_dev, unsigned long timeout); + int (*rst_prc_mbox)(struct hns_roce_dev *hr_dev); int (*set_gid)(struct hns_roce_dev *hr_dev, u8 port, int gid_index, const union ib_gid *gid, const struct ib_gid_attr *attr); int (*set_mac)(struct hns_roce_dev *hr_dev, u8 phy_port, u8 *addr); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 81be86d98ba0..41bccc5a741f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -712,6 +712,110 @@ out: return ret; } +static int hns_roce_v2_cmd_hw_reseted(struct hns_roce_dev *hr_dev, + unsigned long instance_stage, + unsigned long reset_stage) +{ + /* When hardware reset has been completed once or more, we should stop + * sending mailbox&cmq to hardware. If now in .init_instance() + * function, we should exit with error. If now at HNAE3_INIT_CLIENT + * stage of soft reset process, we should exit with error, and then + * HNAE3_INIT_CLIENT related process can rollback the operation like + * notifing hardware to free resources, HNAE3_INIT_CLIENT related + * process will exit with error to notify NIC driver to reschedule soft + * reset process once again. + */ + hr_dev->is_reset = true; + + if (reset_stage == HNS_ROCE_STATE_RST_INIT || + instance_stage == HNS_ROCE_STATE_INIT) + return CMD_RST_PRC_EBUSY; + + return CMD_RST_PRC_SUCCESS; +} + +static int hns_roce_v2_cmd_hw_resetting(struct hns_roce_dev *hr_dev, + unsigned long instance_stage, + unsigned long reset_stage) +{ + struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv; + struct hnae3_handle *handle = priv->handle; + const struct hnae3_ae_ops *ops = handle->ae_algo->ops; + + /* When hardware reset is detected, we should stop sending mailbox&cmq + * to hardware. If now in .init_instance() function, we should + * exit with error. If now at HNAE3_INIT_CLIENT stage of soft reset + * process, we should exit with error, and then HNAE3_INIT_CLIENT + * related process can rollback the operation like notifing hardware to + * free resources, HNAE3_INIT_CLIENT related process will exit with + * error to notify NIC driver to reschedule soft reset process once + * again. + */ + if (!ops->get_hw_reset_stat(handle)) + hr_dev->is_reset = true; + + if (!hr_dev->is_reset || reset_stage == HNS_ROCE_STATE_RST_INIT || + instance_stage == HNS_ROCE_STATE_INIT) + return CMD_RST_PRC_EBUSY; + + return CMD_RST_PRC_SUCCESS; +} + +static int hns_roce_v2_cmd_sw_resetting(struct hns_roce_dev *hr_dev) +{ + struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv; + struct hnae3_handle *handle = priv->handle; + const struct hnae3_ae_ops *ops = handle->ae_algo->ops; + + /* When software reset is detected at .init_instance() function, we + * should stop sending mailbox&cmq to hardware, and exit with + * error. + */ + if (ops->ae_dev_reset_cnt(handle) != hr_dev->reset_cnt) + hr_dev->is_reset = true; + + return CMD_RST_PRC_EBUSY; +} + +static int hns_roce_v2_rst_process_cmd(struct hns_roce_dev *hr_dev) +{ + struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv; + struct hnae3_handle *handle = priv->handle; + const struct hnae3_ae_ops *ops = handle->ae_algo->ops; + unsigned long instance_stage; /* the current instance stage */ + unsigned long reset_stage; /* the current reset stage */ + unsigned long reset_cnt; + bool sw_resetting; + bool hw_resetting; + + if (hr_dev->is_reset) + return CMD_RST_PRC_SUCCESS; + + /* Get information about reset from NIC driver or RoCE driver itself, + * the meaning of the following variables from NIC driver are described + * as below: + * reset_cnt -- The count value of completed hardware reset. + * hw_resetting -- Whether hardware device is resetting now. + * sw_resetting -- Whether NIC's software reset process is running now. + */ + instance_stage = handle->rinfo.instance_state; + reset_stage = handle->rinfo.reset_state; + reset_cnt = ops->ae_dev_reset_cnt(handle); + hw_resetting = ops->get_hw_reset_stat(handle); + sw_resetting = ops->ae_dev_resetting(handle); + + if (reset_cnt != hr_dev->reset_cnt) + return hns_roce_v2_cmd_hw_reseted(hr_dev, instance_stage, + reset_stage); + else if (hw_resetting) + return hns_roce_v2_cmd_hw_resetting(hr_dev, instance_stage, + reset_stage); + else if (sw_resetting && instance_stage == HNS_ROCE_STATE_INIT) + return hns_roce_v2_cmd_sw_resetting(hr_dev); + + return 0; +} + static int hns_roce_cmq_space(struct hns_roce_v2_cmq_ring *ring) { int ntu = ring->next_to_use; @@ -892,8 +996,8 @@ static int hns_roce_cmq_csq_clean(struct hns_roce_dev *hr_dev) return clean; } -static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev, - struct hns_roce_cmq_desc *desc, int num) +static int __hns_roce_cmq_send(struct hns_roce_dev *hr_dev, + struct hns_roce_cmq_desc *desc, int num) { struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv; struct hns_roce_v2_cmq_ring *csq = &priv->cmq.csq; @@ -905,9 +1009,6 @@ static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev, int ret = 0; int ntc; - if (hr_dev->is_reset) - return 0; - spin_lock_bh(&csq->lock); if (num > hns_roce_cmq_space(csq)) { @@ -982,6 +1083,30 @@ static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev, return ret; } +int hns_roce_cmq_send(struct hns_roce_dev *hr_dev, + struct hns_roce_cmq_desc *desc, int num) +{ + int retval; + int ret; + + ret = hns_roce_v2_rst_process_cmd(hr_dev); + if (ret == CMD_RST_PRC_SUCCESS) + return 0; + if (ret == CMD_RST_PRC_EBUSY) + return ret; + + ret = __hns_roce_cmq_send(hr_dev, desc, num); + if (ret) { + retval = hns_roce_v2_rst_process_cmd(hr_dev); + if (retval == CMD_RST_PRC_SUCCESS) + return 0; + else if (retval == CMD_RST_PRC_EBUSY) + return retval; + } + + return ret; +} + static int hns_roce_cmq_query_hw_info(struct hns_roce_dev *hr_dev) { struct hns_roce_query_version *resp; @@ -1857,6 +1982,9 @@ static int hns_roce_v2_chk_mbox(struct hns_roce_dev *hr_dev, status = hns_roce_v2_cmd_complete(hr_dev); if (status != 0x1) { + if (status == CMD_RST_PRC_EBUSY) + return status; + dev_err(dev, "mailbox status 0x%x!\n", status); return -EBUSY; } @@ -5977,6 +6105,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .hw_exit = hns_roce_v2_exit, .post_mbox = hns_roce_v2_post_mbox, .chk_mbox = hns_roce_v2_chk_mbox, + .rst_prc_mbox = hns_roce_v2_rst_process_cmd, .set_gid = hns_roce_v2_set_gid, .set_mac = hns_roce_v2_set_mac, .write_mtpt = hns_roce_v2_write_mtpt, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 938d36a5fa3d..f22094e2ebab 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -96,6 +96,8 @@ #define HNS_ROCE_V2_UC_RC_SGE_NUM_IN_WQE 2 #define HNS_ROCE_V2_RSV_QPS 8 +#define HNS_ROCE_V2_HW_RST_TIMEOUT 1000 + #define HNS_ROCE_CONTEXT_HOP_NUM 1 #define HNS_ROCE_SCCC_HOP_NUM 1 #define HNS_ROCE_MTT_HOP_NUM 1 From d3743fa94ccd177917783726faf54632439ddb54 Mon Sep 17 00:00:00 2001 From: "Wei Hu (Xavier)" Date: Sun, 3 Feb 2019 20:43:15 +0800 Subject: [PATCH 1045/4277] RDMA/hns: Fix the chip hanging caused by sending doorbell during reset On hi08 chip, There is a possibility of chip hanging when sending doorbell during reset. We can fix it by prohibiting doorbell during reset. Fixes: 2d40788825ac ("RDMA/hns: Add support for processing send wr and receive wr") Signed-off-by: Wei Hu (Xavier) Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_device.h | 1 + drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 25 +++++++++++++-------- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 11 +++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 65eb4bcc8c20..8ca8d74dfb6a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -947,6 +947,7 @@ struct hns_roce_dev { spinlock_t bt_cmd_lock; bool active; bool is_reset; + bool dis_db; unsigned long reset_cnt; struct hns_roce_ib_iboe iboe; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 41bccc5a741f..19fefff4f699 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -587,7 +587,7 @@ out: roce_set_field(sq_db.parameter, V2_DB_PARAMETER_SL_M, V2_DB_PARAMETER_SL_S, qp->sl); - hns_roce_write64_k((__le32 *)&sq_db, qp->sq.db_reg_l); + hns_roce_write64(hr_dev, (__le32 *)&sq_db, qp->sq.db_reg_l); qp->sq_next_wqe = ind; qp->next_sge = sge_ind; @@ -717,7 +717,7 @@ static int hns_roce_v2_cmd_hw_reseted(struct hns_roce_dev *hr_dev, unsigned long reset_stage) { /* When hardware reset has been completed once or more, we should stop - * sending mailbox&cmq to hardware. If now in .init_instance() + * sending mailbox&cmq&doorbell to hardware. If now in .init_instance() * function, we should exit with error. If now at HNAE3_INIT_CLIENT * stage of soft reset process, we should exit with error, and then * HNAE3_INIT_CLIENT related process can rollback the operation like @@ -726,6 +726,7 @@ static int hns_roce_v2_cmd_hw_reseted(struct hns_roce_dev *hr_dev, * reset process once again. */ hr_dev->is_reset = true; + hr_dev->dis_db = true; if (reset_stage == HNS_ROCE_STATE_RST_INIT || instance_stage == HNS_ROCE_STATE_INIT) @@ -742,8 +743,8 @@ static int hns_roce_v2_cmd_hw_resetting(struct hns_roce_dev *hr_dev, struct hnae3_handle *handle = priv->handle; const struct hnae3_ae_ops *ops = handle->ae_algo->ops; - /* When hardware reset is detected, we should stop sending mailbox&cmq - * to hardware. If now in .init_instance() function, we should + /* When hardware reset is detected, we should stop sending mailbox&cmq& + * doorbell to hardware. If now in .init_instance() function, we should * exit with error. If now at HNAE3_INIT_CLIENT stage of soft reset * process, we should exit with error, and then HNAE3_INIT_CLIENT * related process can rollback the operation like notifing hardware to @@ -751,6 +752,7 @@ static int hns_roce_v2_cmd_hw_resetting(struct hns_roce_dev *hr_dev, * error to notify NIC driver to reschedule soft reset process once * again. */ + hr_dev->dis_db = true; if (!ops->get_hw_reset_stat(handle)) hr_dev->is_reset = true; @@ -768,9 +770,10 @@ static int hns_roce_v2_cmd_sw_resetting(struct hns_roce_dev *hr_dev) const struct hnae3_ae_ops *ops = handle->ae_algo->ops; /* When software reset is detected at .init_instance() function, we - * should stop sending mailbox&cmq to hardware, and exit with - * error. + * should stop sending mailbox&cmq&doorbell to hardware, and exit + * with error. */ + hr_dev->dis_db = true; if (ops->ae_dev_reset_cnt(handle) != hr_dev->reset_cnt) hr_dev->is_reset = true; @@ -2495,6 +2498,7 @@ static void hns_roce_v2_write_cqc(struct hns_roce_dev *hr_dev, static int hns_roce_v2_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) { + struct hns_roce_dev *hr_dev = to_hr_dev(ibcq->device); struct hns_roce_cq *hr_cq = to_hr_cq(ibcq); u32 notification_flag; u32 doorbell[2]; @@ -2520,7 +2524,7 @@ static int hns_roce_v2_req_notify_cq(struct ib_cq *ibcq, roce_set_bit(doorbell[1], V2_CQ_DB_PARAMETER_NOTIFY_S, notification_flag); - hns_roce_write64_k(doorbell, hr_cq->cq_db_l); + hns_roce_write64(hr_dev, doorbell, hr_cq->cq_db_l); return 0; } @@ -4763,6 +4767,7 @@ static void hns_roce_v2_init_irq_work(struct hns_roce_dev *hr_dev, static void set_eq_cons_index_v2(struct hns_roce_eq *eq) { + struct hns_roce_dev *hr_dev = eq->hr_dev; u32 doorbell[2]; doorbell[0] = 0; @@ -4789,7 +4794,7 @@ static void set_eq_cons_index_v2(struct hns_roce_eq *eq) HNS_ROCE_V2_EQ_DB_PARA_S, (eq->cons_index & HNS_ROCE_V2_CONS_IDX_M)); - hns_roce_write64_k(doorbell, eq->doorbell); + hns_roce_write64(hr_dev, doorbell, eq->doorbell); } static struct hns_roce_aeqe *get_aeqe_v2(struct hns_roce_eq *eq, u32 entry) @@ -6011,6 +6016,7 @@ static int hns_roce_v2_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr, const struct ib_recv_wr **bad_wr) { + struct hns_roce_dev *hr_dev = to_hr_dev(ibsrq->device); struct hns_roce_srq *srq = to_hr_srq(ibsrq); struct hns_roce_v2_wqe_data_seg *dseg; struct hns_roce_v2_db srq_db; @@ -6072,7 +6078,7 @@ static int hns_roce_v2_post_srq_recv(struct ib_srq *ibsrq, srq_db.byte_4 = HNS_ROCE_V2_SRQ_DB << 24 | srq->srqn; srq_db.parameter = srq->head; - hns_roce_write64_k((__le32 *)&srq_db, srq->db_reg_l); + hns_roce_write64(hr_dev, (__le32 *)&srq_db, srq->db_reg_l); } @@ -6309,6 +6315,7 @@ static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle) return 0; hr_dev->active = false; + hr_dev->dis_db = true; event.event = IB_EVENT_DEVICE_FATAL; event.device = &hr_dev->ib_dev; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index f22094e2ebab..6b0486fcbc59 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -1799,4 +1799,15 @@ struct hns_roce_sccc_clr_done { __le32 rsv[5]; }; +static inline void hns_roce_write64(struct hns_roce_dev *hr_dev, __le32 val[2], + void __iomem *dest) +{ + struct hns_roce_v2_priv *priv = (struct hns_roce_v2_priv *)hr_dev->priv; + struct hnae3_handle *handle = priv->handle; + const struct hnae3_ae_ops *ops = handle->ae_algo->ops; + + if (!hr_dev->dis_db && !ops->get_hw_reset_stat(handle)) + hns_roce_write64_k(val, dest); +} + #endif From 95b8e384d8c44c6be590636608f61ac7b39101ec Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Tue, 29 Jan 2019 13:33:07 -0800 Subject: [PATCH 1046/4277] iw_cxgb*: kzalloc the iwcm verbs struct So future additions to that struct get initialized by default. Signed-off-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb3/iwch_provider.c | 2 +- drivers/infiniband/hw/cxgb4/provider.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 07c20cd07f33..804c1fc7bfc1 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1393,7 +1393,7 @@ int iwch_register_device(struct iwch_dev *dev) dev->ibdev.dev.parent = &dev->rdev.rnic_info.pdev->dev; dev->ibdev.uverbs_abi_ver = IWCH_UVERBS_ABI_VERSION; - dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); + dev->ibdev.iwcm = kzalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); if (!dev->ibdev.iwcm) return -ENOMEM; diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index cb5b713bbf39..f59bf7e5a589 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -616,7 +616,7 @@ void c4iw_register_device(struct work_struct *work) dev->ibdev.dev.parent = &dev->rdev.lldi.pdev->dev; dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION; - dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); + dev->ibdev.iwcm = kzalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); if (!dev->ibdev.iwcm) { ret = -ENOMEM; goto err_dealloc_ctx; From f76903d574b26bc596951a5c5e757eb02c67abbd Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Tue, 29 Jan 2019 13:33:11 -0800 Subject: [PATCH 1047/4277] RDMA/IWPM: refactor the IWPM message attribute names In order to add new IWPM_NL attributes, the enums for the IWPM commands attributes are refactored such that a new attribute can be added without breaking ABI version 3. Instead of sharing nl attribute enums for both request and response messages, we create separate enums for each IWPM message request and reply. This allows us to extend any given IWPM message by adding new attributes for just that message. These new enums are created, though, in a way to avoid breaking ABI version 3. Signed-off-by: Steve Wise Reviewed-by: Tatyana Nikolova Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iwpm_msg.c | 40 +++++++++++++++++------------- include/uapi/rdma/rdma_netlink.h | 21 +++++++++++++--- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c index 8861c052155a..3a8753595c8f 100644 --- a/drivers/infiniband/core/iwpm_msg.c +++ b/drivers/infiniband/core/iwpm_msg.c @@ -403,10 +403,12 @@ register_pid_response_exit: /* netlink attribute policy for the received response to add mapping request */ static const struct nla_policy resp_add_policy[IWPM_NLA_RMANAGE_MAPPING_MAX] = { - [IWPM_NLA_MANAGE_MAPPING_SEQ] = { .type = NLA_U32 }, - [IWPM_NLA_MANAGE_ADDR] = { .len = sizeof(struct sockaddr_storage) }, - [IWPM_NLA_MANAGE_MAPPED_LOC_ADDR] = { .len = sizeof(struct sockaddr_storage) }, - [IWPM_NLA_RMANAGE_MAPPING_ERR] = { .type = NLA_U16 } + [IWPM_NLA_RMANAGE_MAPPING_SEQ] = { .type = NLA_U32 }, + [IWPM_NLA_RMANAGE_ADDR] = { + .len = sizeof(struct sockaddr_storage) }, + [IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR] = { + .len = sizeof(struct sockaddr_storage) }, + [IWPM_NLA_RMANAGE_MAPPING_ERR] = { .type = NLA_U16 } }; /* @@ -430,7 +432,7 @@ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb) atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); - msg_seq = nla_get_u32(nltb[IWPM_NLA_MANAGE_MAPPING_SEQ]); + msg_seq = nla_get_u32(nltb[IWPM_NLA_RMANAGE_MAPPING_SEQ]); nlmsg_request = iwpm_find_nlmsg_request(msg_seq); if (!nlmsg_request) { pr_info("%s: Could not find a matching request (seq = %u)\n", @@ -439,9 +441,9 @@ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb) } pm_msg = nlmsg_request->req_buffer; local_sockaddr = (struct sockaddr_storage *) - nla_data(nltb[IWPM_NLA_MANAGE_ADDR]); + nla_data(nltb[IWPM_NLA_RMANAGE_ADDR]); mapped_sockaddr = (struct sockaddr_storage *) - nla_data(nltb[IWPM_NLA_MANAGE_MAPPED_LOC_ADDR]); + nla_data(nltb[IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR]); if (iwpm_compare_sockaddr(local_sockaddr, &pm_msg->loc_addr)) { nlmsg_request->err_code = IWPM_USER_LIB_INFO_ERR; @@ -472,11 +474,15 @@ add_mapping_response_exit: /* netlink attribute policy for the response to add and query mapping request * and response with remote address info */ static const struct nla_policy resp_query_policy[IWPM_NLA_RQUERY_MAPPING_MAX] = { - [IWPM_NLA_QUERY_MAPPING_SEQ] = { .type = NLA_U32 }, - [IWPM_NLA_QUERY_LOCAL_ADDR] = { .len = sizeof(struct sockaddr_storage) }, - [IWPM_NLA_QUERY_REMOTE_ADDR] = { .len = sizeof(struct sockaddr_storage) }, - [IWPM_NLA_RQUERY_MAPPED_LOC_ADDR] = { .len = sizeof(struct sockaddr_storage) }, - [IWPM_NLA_RQUERY_MAPPED_REM_ADDR] = { .len = sizeof(struct sockaddr_storage) }, + [IWPM_NLA_RQUERY_MAPPING_SEQ] = { .type = NLA_U32 }, + [IWPM_NLA_RQUERY_LOCAL_ADDR] = { + .len = sizeof(struct sockaddr_storage) }, + [IWPM_NLA_RQUERY_REMOTE_ADDR] = { + .len = sizeof(struct sockaddr_storage) }, + [IWPM_NLA_RQUERY_MAPPED_LOC_ADDR] = { + .len = sizeof(struct sockaddr_storage) }, + [IWPM_NLA_RQUERY_MAPPED_REM_ADDR] = { + .len = sizeof(struct sockaddr_storage) }, [IWPM_NLA_RQUERY_MAPPING_ERR] = { .type = NLA_U16 } }; @@ -502,7 +508,7 @@ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb, return -EINVAL; atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); - msg_seq = nla_get_u32(nltb[IWPM_NLA_QUERY_MAPPING_SEQ]); + msg_seq = nla_get_u32(nltb[IWPM_NLA_RQUERY_MAPPING_SEQ]); nlmsg_request = iwpm_find_nlmsg_request(msg_seq); if (!nlmsg_request) { pr_info("%s: Could not find a matching request (seq = %u)\n", @@ -511,9 +517,9 @@ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb, } pm_msg = nlmsg_request->req_buffer; local_sockaddr = (struct sockaddr_storage *) - nla_data(nltb[IWPM_NLA_QUERY_LOCAL_ADDR]); + nla_data(nltb[IWPM_NLA_RQUERY_LOCAL_ADDR]); remote_sockaddr = (struct sockaddr_storage *) - nla_data(nltb[IWPM_NLA_QUERY_REMOTE_ADDR]); + nla_data(nltb[IWPM_NLA_RQUERY_REMOTE_ADDR]); mapped_loc_sockaddr = (struct sockaddr_storage *) nla_data(nltb[IWPM_NLA_RQUERY_MAPPED_LOC_ADDR]); mapped_rem_sockaddr = (struct sockaddr_storage *) @@ -588,9 +594,9 @@ int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb) atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); local_sockaddr = (struct sockaddr_storage *) - nla_data(nltb[IWPM_NLA_QUERY_LOCAL_ADDR]); + nla_data(nltb[IWPM_NLA_RQUERY_LOCAL_ADDR]); remote_sockaddr = (struct sockaddr_storage *) - nla_data(nltb[IWPM_NLA_QUERY_REMOTE_ADDR]); + nla_data(nltb[IWPM_NLA_RQUERY_REMOTE_ADDR]); mapped_loc_sockaddr = (struct sockaddr_storage *) nla_data(nltb[IWPM_NLA_RQUERY_MAPPED_LOC_ADDR]); mapped_rem_sockaddr = (struct sockaddr_storage *) diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index 2e18b77a817f..42d53e182d5f 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -83,13 +83,20 @@ enum { IWPM_NLA_MANAGE_MAPPING_UNSPEC = 0, IWPM_NLA_MANAGE_MAPPING_SEQ, IWPM_NLA_MANAGE_ADDR, - IWPM_NLA_MANAGE_MAPPED_LOC_ADDR, + IWPM_NLA_MANAGE_MAPPING_MAX +}; + +enum { + IWPM_NLA_RMANAGE_MAPPING_UNSPEC = 0, + IWPM_NLA_RMANAGE_MAPPING_SEQ, + IWPM_NLA_RMANAGE_ADDR, + IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR, + /* The following maintains bisectability of rdma-core */ + IWPM_NLA_MANAGE_MAPPED_LOC_ADDR = IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR, IWPM_NLA_RMANAGE_MAPPING_ERR, IWPM_NLA_RMANAGE_MAPPING_MAX }; -#define IWPM_NLA_MANAGE_MAPPING_MAX 3 -#define IWPM_NLA_QUERY_MAPPING_MAX 4 #define IWPM_NLA_MAPINFO_SEND_MAX 3 enum { @@ -97,6 +104,14 @@ enum { IWPM_NLA_QUERY_MAPPING_SEQ, IWPM_NLA_QUERY_LOCAL_ADDR, IWPM_NLA_QUERY_REMOTE_ADDR, + IWPM_NLA_QUERY_MAPPING_MAX, +}; + +enum { + IWPM_NLA_RQUERY_MAPPING_UNSPEC = 0, + IWPM_NLA_RQUERY_MAPPING_SEQ, + IWPM_NLA_RQUERY_LOCAL_ADDR, + IWPM_NLA_RQUERY_REMOTE_ADDR, IWPM_NLA_RQUERY_MAPPED_LOC_ADDR, IWPM_NLA_RQUERY_MAPPED_REM_ADDR, IWPM_NLA_RQUERY_MAPPING_ERR, From b0bad9ad514fc1dd8890f1749f5d2425a73270e3 Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Tue, 29 Jan 2019 13:33:16 -0800 Subject: [PATCH 1048/4277] RDMA/IWPM: Support no port mapping requirements A soft iwarp driver that uses the host TCP stack via a kernel mode socket does not need port mapping. In fact, if the port map daemon, iwpmd, is running, then iwpmd must not try and create/bind a socket to the actual port for a soft iwarp connection, since the driver already has that socket bound. Yet if the soft iwarp driver wants to interoperate with hard iwarp devices that -are- using port mapping, then the soft iwarp driver's mappings still need to be maintained and advertised by the iwpm protocol. This patch enhances the rdma driver<->iwcm interface to allow an iwarp driver to specify that it does not want port mapping. The iwpm kernel<->iwpmd interface is also enhanced to pass up this information on map requests. Care is taken to interoperate with the current iwpmd version (ABI version 3) and only use the new NL attributes if iwpmd supports ABI version 4. The ABI version define has also been created in rdma_netlink.h so both kernel and user code can share it. The iwcm and iwpmd negotiate the ABI version to use with a new HELLO netlink message. Signed-off-by: Steve Wise Reviewed-by: Tatyana Nikolova Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iwcm.c | 7 ++- drivers/infiniband/core/iwpm_msg.c | 80 +++++++++++++++++++++++++++-- drivers/infiniband/core/iwpm_util.c | 48 ++++++++++++++++- drivers/infiniband/core/iwpm_util.h | 12 +++++ include/rdma/iw_cm.h | 13 +++++ include/rdma/iw_portmap.h | 15 +++++- include/uapi/rdma/rdma_netlink.h | 24 +++++++++ 7 files changed, 192 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 476abc74178e..350ea2bab78a 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -87,7 +87,8 @@ static struct rdma_nl_cbs iwcm_nl_cb_table[RDMA_NL_IWPM_NUM_OPS] = { [RDMA_NL_IWPM_REMOTE_INFO] = {.dump = iwpm_remote_info_cb}, [RDMA_NL_IWPM_HANDLE_ERR] = {.dump = iwpm_mapping_error_cb}, [RDMA_NL_IWPM_MAPINFO] = {.dump = iwpm_mapping_info_cb}, - [RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb} + [RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb}, + [RDMA_NL_IWPM_HELLO] = {.dump = iwpm_hello_cb} }; static struct workqueue_struct *iwcm_wq; @@ -525,6 +526,8 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active) cm_id->mapped = true; pm_msg.loc_addr = cm_id->local_addr; pm_msg.rem_addr = cm_id->remote_addr; + pm_msg.flags = (cm_id->device->iwcm->driver_flags & IW_F_NO_PORT_MAP) ? + IWPM_FLAGS_NO_PORT_MAP : 0; if (active) status = iwpm_add_and_query_mapping(&pm_msg, RDMA_NL_IWCM); @@ -543,7 +546,7 @@ static int iw_cm_map(struct iw_cm_id *cm_id, bool active) return iwpm_create_mapinfo(&cm_id->local_addr, &cm_id->m_local_addr, - RDMA_NL_IWCM); + RDMA_NL_IWCM, pm_msg.flags); } /* diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c index 3a8753595c8f..2e30e65b0816 100644 --- a/drivers/infiniband/core/iwpm_msg.c +++ b/drivers/infiniband/core/iwpm_msg.c @@ -34,7 +34,7 @@ #include "iwpm_util.h" static const char iwpm_ulib_name[IWPM_ULIBNAME_SIZE] = "iWarpPortMapperUser"; -static int iwpm_ulib_version = 3; +u16 iwpm_ulib_version = IWPM_UABI_VERSION_MIN; static int iwpm_user_pid = IWPM_PID_UNDEFINED; static atomic_t echo_nlmsg_seq; @@ -130,6 +130,7 @@ pid_query_error: * nlmsg attributes: * [IWPM_NLA_MANAGE_MAPPING_SEQ] * [IWPM_NLA_MANAGE_ADDR] + * [IWPM_NLA_MANAGE_FLAGS] */ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) { @@ -173,6 +174,18 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) if (ret) goto add_mapping_error; + /* If flags are required and we're not V4, then return a quiet error */ + if (pm_msg->flags && iwpm_ulib_version == IWPM_UABI_VERSION_MIN) { + ret = -EINVAL; + goto add_mapping_error_nowarn; + } + if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) { + ret = ibnl_put_attr(skb, nlh, sizeof(u32), &pm_msg->flags, + IWPM_NLA_MANAGE_FLAGS); + if (ret) + goto add_mapping_error; + } + nlmsg_end(skb, nlh); nlmsg_request->req_buffer = pm_msg; @@ -187,6 +200,7 @@ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) return ret; add_mapping_error: pr_info("%s: %s (client = %d)\n", __func__, err_str, nl_client); +add_mapping_error_nowarn: if (skb) dev_kfree_skb(skb); if (nlmsg_request) @@ -201,6 +215,7 @@ add_mapping_error: * [IWPM_NLA_QUERY_MAPPING_SEQ] * [IWPM_NLA_QUERY_LOCAL_ADDR] * [IWPM_NLA_QUERY_REMOTE_ADDR] + * [IWPM_NLA_QUERY_FLAGS] */ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) { @@ -251,6 +266,18 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) if (ret) goto query_mapping_error; + /* If flags are required and we're not V4, then return a quite error */ + if (pm_msg->flags && iwpm_ulib_version == IWPM_UABI_VERSION_MIN) { + ret = -EINVAL; + goto query_mapping_error_nowarn; + } + if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) { + ret = ibnl_put_attr(skb, nlh, sizeof(u32), &pm_msg->flags, + IWPM_NLA_QUERY_FLAGS); + if (ret) + goto query_mapping_error; + } + nlmsg_end(skb, nlh); nlmsg_request->req_buffer = pm_msg; @@ -264,6 +291,7 @@ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) return ret; query_mapping_error: pr_info("%s: %s (client = %d)\n", __func__, err_str, nl_client); +query_mapping_error_nowarn: if (skb) dev_kfree_skb(skb); if (nlmsg_request) @@ -379,7 +407,7 @@ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb) /* check device name, ulib name and version */ if (strcmp(pm_msg->dev_name, dev_name) || strcmp(iwpm_ulib_name, iwpm_name) || - iwpm_version != iwpm_ulib_version) { + iwpm_version < IWPM_UABI_VERSION_MIN) { pr_info("%s: Incorrect info (dev = %s name = %s version = %d)\n", __func__, dev_name, iwpm_name, iwpm_version); @@ -387,6 +415,10 @@ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb) goto register_pid_response_exit; } iwpm_user_pid = cb->nlh->nlmsg_pid; + iwpm_ulib_version = iwpm_version; + if (iwpm_ulib_version < IWPM_UABI_VERSION) + pr_warn_once("%s: Down level iwpmd/pid %u. Continuing...", + __func__, iwpm_user_pid); atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); pr_debug("%s: iWarp Port Mapper (pid = %d) is available!\n", __func__, iwpm_user_pid); @@ -661,7 +693,7 @@ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb) iwpm_name = (char *)nla_data(nltb[IWPM_NLA_MAPINFO_ULIB_NAME]); iwpm_version = nla_get_u16(nltb[IWPM_NLA_MAPINFO_ULIB_VER]); if (strcmp(iwpm_ulib_name, iwpm_name) || - iwpm_version != iwpm_ulib_version) { + iwpm_version < IWPM_UABI_VERSION_MIN) { pr_info("%s: Invalid port mapper name = %s version = %d\n", __func__, iwpm_name, iwpm_version); return ret; @@ -675,6 +707,11 @@ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb) iwpm_set_registration(nl_client, IWPM_REG_INCOMPL); atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); iwpm_user_pid = cb->nlh->nlmsg_pid; + + if (iwpm_ulib_version < IWPM_UABI_VERSION) + pr_warn_once("%s: Down level iwpmd/pid %u. Continuing...", + __func__, iwpm_user_pid); + if (!iwpm_mapinfo_available()) return 0; pr_debug("%s: iWarp Port Mapper (pid = %d) is available!\n", @@ -754,3 +791,40 @@ int iwpm_mapping_error_cb(struct sk_buff *skb, struct netlink_callback *cb) up(&nlmsg_request->sem); return 0; } + +/* netlink attribute policy for the received hello request */ +static const struct nla_policy hello_policy[IWPM_NLA_HELLO_MAX] = { + [IWPM_NLA_HELLO_ABI_VERSION] = { .type = NLA_U16 } +}; + +/* + * iwpm_hello_cb - Process a port mapper hello request + */ +int iwpm_hello_cb(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct nlattr *nltb[IWPM_NLA_HELLO_MAX]; + const char *msg_type = "Hello request"; + u8 nl_client; + u16 abi_version; + int ret = -EINVAL; + + if (iwpm_parse_nlmsg(cb, IWPM_NLA_HELLO_MAX, hello_policy, nltb, + msg_type)) { + pr_info("%s: Unable to parse nlmsg\n", __func__); + return ret; + } + abi_version = nla_get_u16(nltb[IWPM_NLA_HELLO_ABI_VERSION]); + nl_client = RDMA_NL_GET_CLIENT(cb->nlh->nlmsg_type); + if (!iwpm_valid_client(nl_client)) { + pr_info("%s: Invalid port mapper client = %d\n", + __func__, nl_client); + return ret; + } + iwpm_set_registration(nl_client, IWPM_REG_INCOMPL); + atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq); + iwpm_ulib_version = min_t(u16, IWPM_UABI_VERSION, abi_version); + pr_debug("Using ABI version %u\n", iwpm_ulib_version); + iwpm_user_pid = cb->nlh->nlmsg_pid; + ret = iwpm_send_hello(nl_client, iwpm_user_pid, iwpm_ulib_version); + return ret; +} diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index cdb63f3f4de7..363938435476 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -114,7 +114,7 @@ static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *, int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr, struct sockaddr_storage *mapped_sockaddr, - u8 nl_client) + u8 nl_client, u32 map_flags) { struct hlist_head *hash_bucket_head = NULL; struct iwpm_mapping_info *map_info; @@ -132,6 +132,7 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr, memcpy(&map_info->mapped_sockaddr, mapped_sockaddr, sizeof(struct sockaddr_storage)); map_info->nl_client = nl_client; + map_info->map_flags = map_flags; spin_lock_irqsave(&iwpm_mapinfo_lock, flags); if (iwpm_hash_bucket) { @@ -686,6 +687,14 @@ int iwpm_send_mapinfo(u8 nl_client, int iwpm_pid) if (ret) goto send_mapping_info_unlock; + if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) { + ret = ibnl_put_attr(skb, nlh, sizeof(u32), + &map_info->map_flags, + IWPM_NLA_MAPINFO_FLAGS); + if (ret) + goto send_mapping_info_unlock; + } + nlmsg_end(skb, nlh); iwpm_print_sockaddr(&map_info->local_sockaddr, @@ -754,3 +763,40 @@ int iwpm_mapinfo_available(void) spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags); return full_bucket; } + +int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; + u32 msg_seq; + const char *err_str = ""; + int ret = -EINVAL; + + skb = iwpm_create_nlmsg(RDMA_NL_IWPM_HELLO, &nlh, nl_client); + if (!skb) { + err_str = "Unable to create a nlmsg"; + goto hello_num_error; + } + nlh->nlmsg_seq = iwpm_get_nlmsg_seq(); + msg_seq = 0; + err_str = "Unable to put attribute of abi_version into nlmsg"; + ret = ibnl_put_attr(skb, nlh, sizeof(u16), &abi_version, + IWPM_NLA_HELLO_ABI_VERSION); + if (ret) + goto hello_num_error; + nlmsg_end(skb, nlh); + + ret = rdma_nl_unicast(skb, iwpm_pid); + if (ret) { + skb = NULL; + err_str = "Unable to send a nlmsg"; + goto hello_num_error; + } + pr_debug("%s: Sent hello abi_version = %u\n", __func__, abi_version); + return 0; +hello_num_error: + pr_info("%s: %s\n", __func__, err_str); + if (skb) + dev_kfree_skb(skb); + return ret; +} diff --git a/drivers/infiniband/core/iwpm_util.h b/drivers/infiniband/core/iwpm_util.h index af1fc14a0d3d..7e2bcc72f66c 100644 --- a/drivers/infiniband/core/iwpm_util.h +++ b/drivers/infiniband/core/iwpm_util.h @@ -78,6 +78,7 @@ struct iwpm_mapping_info { struct sockaddr_storage local_sockaddr; struct sockaddr_storage mapped_sockaddr; u8 nl_client; + u32 map_flags; }; struct iwpm_remote_info { @@ -266,4 +267,15 @@ int iwpm_parse_nlmsg(struct netlink_callback *cb, int policy_max, * @msg: Message to print */ void iwpm_print_sockaddr(struct sockaddr_storage *sockaddr, char *msg); + +/** + * iwpm_send_hello - Send hello response to iwpmd + * + * @nl_client: The index of the netlink client + * @abi_version: The kernel's abi_version + * + * Returns 0 on success or a negative error code + */ +int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version); +extern u16 iwpm_ulib_version; #endif diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h index 5cd7701db148..48512abd3162 100644 --- a/include/rdma/iw_cm.h +++ b/include/rdma/iw_cm.h @@ -105,6 +105,18 @@ struct iw_cm_conn_param { u32 qpn; }; +enum iw_flags { + + /* + * This flag allows the iwcm and iwpmd to still advertise + * mappings but the real and mapped port numbers are the + * same. Further, iwpmd will not bind any user socket to + * reserve the port. This is required for soft iwarp + * to play in the port mapped iwarp space. + */ + IW_F_NO_PORT_MAP = (1 << 0), +}; + struct iw_cm_verbs { void (*add_ref)(struct ib_qp *qp); @@ -127,6 +139,7 @@ struct iw_cm_verbs { int (*destroy_listen)(struct iw_cm_id *cm_id); char ifname[IFNAMSIZ]; + enum iw_flags driver_flags; }; /** diff --git a/include/rdma/iw_portmap.h b/include/rdma/iw_portmap.h index fda31673a562..84fac196ef80 100644 --- a/include/rdma/iw_portmap.h +++ b/include/rdma/iw_portmap.h @@ -58,6 +58,7 @@ struct iwpm_sa_data { struct sockaddr_storage mapped_loc_addr; struct sockaddr_storage rem_addr; struct sockaddr_storage mapped_rem_addr; + u32 flags; }; /** @@ -205,9 +206,11 @@ int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr, * @local_addr: Local ip/tcp address * @mapped_addr: Mapped local ip/tcp address * @nl_client: The index of the netlink client + * @map_flags: IWPM mapping flags */ int iwpm_create_mapinfo(struct sockaddr_storage *local_addr, - struct sockaddr_storage *mapped_addr, u8 nl_client); + struct sockaddr_storage *mapped_addr, u8 nl_client, + u32 map_flags); /** * iwpm_remove_mapinfo - Remove local and mapped IPv4/IPv6 address @@ -221,4 +224,14 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_addr, int iwpm_remove_mapinfo(struct sockaddr_storage *local_addr, struct sockaddr_storage *mapped_addr); +/** + * iwpm_hello_cb - Process a hello message from iwpmd + * + * @skb: + * @cb: Contains the received message (payload and netlink header) + * + * Using the received port mapper pid, send the kernel's abi_version + * after adjusting it to support the iwpmd version. + */ +int iwpm_hello_cb(struct sk_buff *skb, struct netlink_callback *cb); #endif /* _IW_PORTMAP_H */ diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index 42d53e182d5f..0f5263767fb4 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -35,6 +35,19 @@ enum { RDMA_NL_RDMA_CM_NUM_ATTR, }; +/* The minimum version that the iwpm kernel supports */ +#define IWPM_UABI_VERSION_MIN 3 + +/* The latest version that the iwpm kernel supports */ +#define IWPM_UABI_VERSION 4 + +/* iwarp port mapper message flags */ +enum { + + /* Do not map the port for this IWPM request */ + IWPM_FLAGS_NO_PORT_MAP = (1 << 0), +}; + /* iwarp port mapper op-codes */ enum { RDMA_NL_IWPM_REG_PID = 0, @@ -45,6 +58,7 @@ enum { RDMA_NL_IWPM_HANDLE_ERR, RDMA_NL_IWPM_MAPINFO, RDMA_NL_IWPM_MAPINFO_NUM, + RDMA_NL_IWPM_HELLO, RDMA_NL_IWPM_NUM_OPS }; @@ -83,6 +97,7 @@ enum { IWPM_NLA_MANAGE_MAPPING_UNSPEC = 0, IWPM_NLA_MANAGE_MAPPING_SEQ, IWPM_NLA_MANAGE_ADDR, + IWPM_NLA_MANAGE_FLAGS, IWPM_NLA_MANAGE_MAPPING_MAX }; @@ -98,12 +113,14 @@ enum { }; #define IWPM_NLA_MAPINFO_SEND_MAX 3 +#define IWPM_NLA_REMOVE_MAPPING_MAX 3 enum { IWPM_NLA_QUERY_MAPPING_UNSPEC = 0, IWPM_NLA_QUERY_MAPPING_SEQ, IWPM_NLA_QUERY_LOCAL_ADDR, IWPM_NLA_QUERY_REMOTE_ADDR, + IWPM_NLA_QUERY_FLAGS, IWPM_NLA_QUERY_MAPPING_MAX, }; @@ -129,6 +146,7 @@ enum { IWPM_NLA_MAPINFO_UNSPEC = 0, IWPM_NLA_MAPINFO_LOCAL_ADDR, IWPM_NLA_MAPINFO_MAPPED_ADDR, + IWPM_NLA_MAPINFO_FLAGS, IWPM_NLA_MAPINFO_MAX }; @@ -147,6 +165,12 @@ enum { IWPM_NLA_ERR_MAX }; +enum { + IWPM_NLA_HELLO_UNSPEC = 0, + IWPM_NLA_HELLO_ABI_VERSION, + IWPM_NLA_HELLO_MAX +}; + /* * Local service operations: * RESOLVE - The client requests the local service to resolve a path. From f80d2f0846b7b9ceb1f2a5951229ee4391edaebd Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:43 -0800 Subject: [PATCH 1049/4277] scsi: target/core: Remove the write_pending_status() callback function Due to the patch that makes TMF handling synchronous the write_pending_status() callback function is no longer called. Hence remove it. Acked-by: Felipe Balbi Reviewed-by: Sagi Grimberg Reviewed-by: Andy Grover Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Bryant G. Ly Cc: Nicholas Bellinger Cc: Mike Christie Cc: Himanshu Madhani Cc: Quinn Tran Cc: Saurav Kashyap Cc: Michael S. Tsirkin Cc: Juergen Gross Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- Documentation/target/tcm_mod_builder.py | 8 ----- drivers/infiniband/ulp/srpt/ib_srpt.c | 9 ------ drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 6 ---- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 31 -------------------- drivers/target/iscsi/iscsi_target_configfs.c | 13 -------- drivers/target/loopback/tcm_loop.c | 6 ---- drivers/target/sbp/sbp_target.c | 6 ---- drivers/target/target_core_configfs.c | 4 --- drivers/target/target_core_xcopy.c | 6 ---- drivers/target/tcm_fc/tcm_fc.h | 1 - drivers/target/tcm_fc/tfc_cmd.c | 7 ----- drivers/target/tcm_fc/tfc_conf.c | 1 - drivers/usb/gadget/function/f_tcm.c | 9 ------ drivers/vhost/scsi.c | 6 ---- drivers/xen/xen-scsiback.c | 6 ---- include/target/target_core_fabric.h | 1 - 16 files changed, 120 deletions(-) diff --git a/Documentation/target/tcm_mod_builder.py b/Documentation/target/tcm_mod_builder.py index 94bf6944bb1e..95d6e31f1e3a 100755 --- a/Documentation/target/tcm_mod_builder.py +++ b/Documentation/target/tcm_mod_builder.py @@ -297,7 +297,6 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " .sess_get_index = " + fabric_mod_name + "_sess_get_index,\n" buf += " .sess_get_initiator_sid = NULL,\n" buf += " .write_pending = " + fabric_mod_name + "_write_pending,\n" - buf += " .write_pending_status = " + fabric_mod_name + "_write_pending_status,\n" buf += " .set_default_node_attributes = " + fabric_mod_name + "_set_default_node_attrs,\n" buf += " .get_cmd_state = " + fabric_mod_name + "_get_cmd_state,\n" buf += " .queue_data_in = " + fabric_mod_name + "_queue_data_in,\n" @@ -479,13 +478,6 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "}\n\n" bufi += "int " + fabric_mod_name + "_write_pending(struct se_cmd *);\n" - if re.search('write_pending_status\)\(', fo): - buf += "int " + fabric_mod_name + "_write_pending_status(struct se_cmd *se_cmd)\n" - buf += "{\n" - buf += " return 0;\n" - buf += "}\n\n" - bufi += "int " + fabric_mod_name + "_write_pending_status(struct se_cmd *);\n" - if re.search('set_default_node_attributes\)\(', fo): buf += "void " + fabric_mod_name + "_set_default_node_attrs(struct se_node_acl *nacl)\n" buf += "{\n" diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index e9c336cff8f5..8cee8c6c6be9 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -2703,14 +2703,6 @@ static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, return ret; } -static int srpt_write_pending_status(struct se_cmd *se_cmd) -{ - struct srpt_send_ioctx *ioctx; - - ioctx = container_of(se_cmd, struct srpt_send_ioctx, cmd); - return ioctx->state == SRPT_STATE_NEED_DATA; -} - /* * srpt_write_pending - Start data transfer from initiator to target (write). */ @@ -3806,7 +3798,6 @@ static const struct target_core_fabric_ops srpt_template = { .sess_get_index = srpt_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = srpt_write_pending, - .write_pending_status = srpt_write_pending_status, .set_default_node_attributes = srpt_set_default_node_attrs, .get_cmd_state = srpt_get_tcm_cmd_state, .queue_data_in = srpt_queue_data_in, diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index cc9cae469c4b..7ca277e28d63 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -3788,11 +3788,6 @@ static int ibmvscsis_write_pending(struct se_cmd *se_cmd) return 0; } -static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl) { } @@ -4053,7 +4048,6 @@ static const struct target_core_fabric_ops ibmvscsis_ops = { .release_cmd = ibmvscsis_release_cmd, .sess_get_index = ibmvscsis_sess_get_index, .write_pending = ibmvscsis_write_pending, - .write_pending_status = ibmvscsis_write_pending_status, .set_default_node_attributes = ibmvscsis_set_default_node_attrs, .get_cmd_state = ibmvscsis_get_cmd_state, .queue_data_in = ibmvscsis_queue_data_in, diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 283e6b80abb5..8a3075d17c63 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -420,26 +420,6 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd) return qlt_rdy_to_xfer(cmd); } -static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd) -{ - unsigned long flags; - /* - * Check for WRITE_PENDING status to determine if we need to wait for - * CTIO aborts to be posted via hardware in tcm_qla2xxx_handle_data(). - */ - spin_lock_irqsave(&se_cmd->t_state_lock, flags); - if (se_cmd->t_state == TRANSPORT_WRITE_PENDING || - se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) { - spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); - wait_for_completion_timeout(&se_cmd->t_transport_stop_comp, - 50); - return 0; - } - spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); - - return 0; -} - static void tcm_qla2xxx_set_default_node_attrs(struct se_node_acl *nacl) { return; @@ -537,15 +517,6 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work) cmd->qpair->tgt_counters.qla_core_ret_ctio++; if (!cmd->write_data_transferred) { - /* - * Check if se_cmd has already been aborted via LUN_RESET, and - * waiting upon completion in tcm_qla2xxx_write_pending_status() - */ - if (cmd->se_cmd.transport_state & CMD_T_ABORTED) { - complete(&cmd->se_cmd.t_transport_stop_comp); - return; - } - switch (cmd->dif_err_code) { case DIF_ERR_GRD: cmd->se_cmd.pi_err = @@ -1902,7 +1873,6 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { .sess_get_index = tcm_qla2xxx_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = tcm_qla2xxx_write_pending, - .write_pending_status = tcm_qla2xxx_write_pending_status, .set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs, .get_cmd_state = tcm_qla2xxx_get_cmd_state, .queue_data_in = tcm_qla2xxx_queue_data_in, @@ -1943,7 +1913,6 @@ static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .sess_get_index = tcm_qla2xxx_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = tcm_qla2xxx_write_pending, - .write_pending_status = tcm_qla2xxx_write_pending_status, .set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs, .get_cmd_state = tcm_qla2xxx_get_cmd_state, .queue_data_in = tcm_qla2xxx_queue_data_in, diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index a5481dfeae8d..cac94c94ef5d 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1389,18 +1389,6 @@ static int lio_write_pending(struct se_cmd *se_cmd) return 0; } -static int lio_write_pending_status(struct se_cmd *se_cmd) -{ - struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); - int ret; - - spin_lock_bh(&cmd->istate_lock); - ret = !(cmd->cmd_flags & ICF_GOT_LAST_DATAOUT); - spin_unlock_bh(&cmd->istate_lock); - - return ret; -} - static int lio_queue_status(struct se_cmd *se_cmd) { struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); @@ -1564,7 +1552,6 @@ const struct target_core_fabric_ops iscsi_ops = { .sess_get_index = lio_sess_get_index, .sess_get_initiator_sid = lio_sess_get_initiator_sid, .write_pending = lio_write_pending, - .write_pending_status = lio_write_pending_status, .set_default_node_attributes = lio_set_default_node_attributes, .get_cmd_state = iscsi_get_cmd_state, .queue_data_in = lio_queue_data_in, diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 7bd7c0c0db6f..89c6e3719d2b 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -560,11 +560,6 @@ static int tcm_loop_write_pending(struct se_cmd *se_cmd) return 0; } -static int tcm_loop_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static int tcm_loop_queue_data_in(struct se_cmd *se_cmd) { struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, @@ -1159,7 +1154,6 @@ static const struct target_core_fabric_ops loop_ops = { .release_cmd = tcm_loop_release_cmd, .sess_get_index = tcm_loop_sess_get_index, .write_pending = tcm_loop_write_pending, - .write_pending_status = tcm_loop_write_pending_status, .set_default_node_attributes = tcm_loop_set_default_node_attributes, .get_cmd_state = tcm_loop_get_cmd_state, .queue_data_in = tcm_loop_queue_data_in, diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c index 08cee13dfb9a..b0d3583998f0 100644 --- a/drivers/target/sbp/sbp_target.c +++ b/drivers/target/sbp/sbp_target.c @@ -1749,11 +1749,6 @@ static int sbp_write_pending(struct se_cmd *se_cmd) return 0; } -static int sbp_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static void sbp_set_default_node_attrs(struct se_node_acl *nacl) { return; @@ -2329,7 +2324,6 @@ static const struct target_core_fabric_ops sbp_ops = { .release_cmd = sbp_release_cmd, .sess_get_index = sbp_sess_get_index, .write_pending = sbp_write_pending, - .write_pending_status = sbp_write_pending_status, .set_default_node_attributes = sbp_set_default_node_attrs, .get_cmd_state = sbp_get_cmd_state, .queue_data_in = sbp_queue_data_in, diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 72016d0dfca5..1124c84a3b7f 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -401,10 +401,6 @@ static int target_fabric_tf_ops_check(const struct target_core_fabric_ops *tfo) pr_err("Missing tfo->write_pending()\n"); return -EINVAL; } - if (!tfo->write_pending_status) { - pr_err("Missing tfo->write_pending_status()\n"); - return -EINVAL; - } if (!tfo->set_default_node_attributes) { pr_err("Missing tfo->set_default_node_attributes()\n"); return -EINVAL; diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index c2e1fc927fdf..9be1418e919f 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -442,11 +442,6 @@ static int xcopy_pt_write_pending(struct se_cmd *se_cmd) return 0; } -static int xcopy_pt_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static int xcopy_pt_queue_data_in(struct se_cmd *se_cmd) { return 0; @@ -463,7 +458,6 @@ static const struct target_core_fabric_ops xcopy_pt_tfo = { .release_cmd = xcopy_pt_release_cmd, .check_stop_free = xcopy_pt_check_stop_free, .write_pending = xcopy_pt_write_pending, - .write_pending_status = xcopy_pt_write_pending_status, .queue_data_in = xcopy_pt_queue_data_in, .queue_status = xcopy_pt_queue_status, }; diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index 11d27b93b413..b8ced4458118 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -158,7 +158,6 @@ void ft_release_cmd(struct se_cmd *); int ft_queue_status(struct se_cmd *); int ft_queue_data_in(struct se_cmd *); int ft_write_pending(struct se_cmd *); -int ft_write_pending_status(struct se_cmd *); int ft_get_cmd_state(struct se_cmd *); void ft_queue_tm_resp(struct se_cmd *); void ft_aborted_task(struct se_cmd *); diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index a183d4da7db2..f0529ba58f4c 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -184,13 +184,6 @@ int ft_queue_status(struct se_cmd *se_cmd) return 0; } -int ft_write_pending_status(struct se_cmd *se_cmd) -{ - struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); - - return cmd->write_data_len != se_cmd->data_length; -} - /* * Send TX_RDY (transfer ready). */ diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 1ce49518d440..c873a052fcb0 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -437,7 +437,6 @@ static const struct target_core_fabric_ops ft_fabric_ops = { .sess_get_index = ft_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = ft_write_pending, - .write_pending_status = ft_write_pending_status, .set_default_node_attributes = ft_set_default_node_attr, .get_cmd_state = ft_get_cmd_state, .queue_data_in = ft_queue_data_in, diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 34f5982cab78..7f01f78b1d23 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1292,14 +1292,6 @@ static u32 usbg_sess_get_index(struct se_session *se_sess) return 0; } -/* - * XXX Error recovery: return != 0 if we expect writes. Dunno when that could be - */ -static int usbg_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static void usbg_set_default_node_attrs(struct se_node_acl *nacl) { } @@ -1725,7 +1717,6 @@ static const struct target_core_fabric_ops usbg_ops = { .sess_get_index = usbg_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = usbg_send_write_request, - .write_pending_status = usbg_write_pending_status, .set_default_node_attributes = usbg_set_default_node_attrs, .get_cmd_state = usbg_get_cmd_state, .queue_data_in = usbg_send_read_response, diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 8e10ab436d1f..ad7899f99b97 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -346,11 +346,6 @@ static int vhost_scsi_write_pending(struct se_cmd *se_cmd) return 0; } -static int vhost_scsi_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static void vhost_scsi_set_default_node_attrs(struct se_node_acl *nacl) { return; @@ -2298,7 +2293,6 @@ static const struct target_core_fabric_ops vhost_scsi_ops = { .sess_get_index = vhost_scsi_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = vhost_scsi_write_pending, - .write_pending_status = vhost_scsi_write_pending_status, .set_default_node_attributes = vhost_scsi_set_default_node_attrs, .get_cmd_state = vhost_scsi_get_cmd_state, .queue_data_in = vhost_scsi_queue_data_in, diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index c9e23a126218..e59937293a32 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -1404,11 +1404,6 @@ static int scsiback_write_pending(struct se_cmd *se_cmd) return 0; } -static int scsiback_write_pending_status(struct se_cmd *se_cmd) -{ - return 0; -} - static void scsiback_set_default_node_attrs(struct se_node_acl *nacl) { } @@ -1818,7 +1813,6 @@ static const struct target_core_fabric_ops scsiback_ops = { .sess_get_index = scsiback_sess_get_index, .sess_get_initiator_sid = NULL, .write_pending = scsiback_write_pending, - .write_pending_status = scsiback_write_pending_status, .set_default_node_attributes = scsiback_set_default_node_attrs, .get_cmd_state = scsiback_get_cmd_state, .queue_data_in = scsiback_queue_data_in, diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index ee5ddd81cd8d..691c6b5e6520 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -74,7 +74,6 @@ struct target_core_fabric_ops { u32 (*sess_get_initiator_sid)(struct se_session *, unsigned char *, u32); int (*write_pending)(struct se_cmd *); - int (*write_pending_status)(struct se_cmd *); void (*set_default_node_attributes)(struct se_node_acl *); int (*get_cmd_state)(struct se_cmd *); int (*queue_data_in)(struct se_cmd *); From 3f0661a492a199b7de2fb63eceba8c79039bff83 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:44 -0800 Subject: [PATCH 1050/4277] scsi: target/core: Remove several state tests from the TMF code Whether or not a session is being torn down does not affect whether or not SCSI commands are in the task set. Hence remove the "tearing down" checks from the TMF code. The TRANSPORT_ISTATE_PROCESSING check is left out because it is now safe to wait for a command that is in that state. The CMD_T_PRE_EXECUTE is left out because abort processing is postponed until after commands have left the pre-execute state since the patch that makes TMF processing synchronous. See also commit 1c21a48055a6 ("target: Avoid early CMD_T_PRE_EXECUTE failures during ABORT_TASK"). Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/target_core_tmr.c | 25 ------------------------- drivers/target/target_core_transport.c | 2 -- include/target/target_core_base.h | 1 - 3 files changed, 28 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index ad0061e09d4c..df1ca827de68 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -114,21 +114,6 @@ static bool __target_check_io_state(struct se_cmd *se_cmd, spin_unlock(&se_cmd->t_state_lock); return false; } - if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) { - if (se_cmd->scsi_status) { - pr_debug("Attempted to abort io tag: %llu early failure" - " status: 0x%02x\n", se_cmd->tag, - se_cmd->scsi_status); - spin_unlock(&se_cmd->t_state_lock); - return false; - } - } - if (sess->sess_tearing_down) { - pr_debug("Attempted to abort io tag: %llu already shutdown," - " skipping\n", se_cmd->tag); - spin_unlock(&se_cmd->t_state_lock); - return false; - } se_cmd->transport_state |= CMD_T_ABORTED; if ((tmr_sess != se_cmd->se_sess) && tas) @@ -239,16 +224,6 @@ static void core_tmr_drain_tmr_list( spin_unlock(&sess->sess_cmd_lock); continue; } - if (cmd->t_state == TRANSPORT_ISTATE_PROCESSING) { - spin_unlock(&cmd->t_state_lock); - spin_unlock(&sess->sess_cmd_lock); - continue; - } - if (sess->sess_tearing_down) { - spin_unlock(&cmd->t_state_lock); - spin_unlock(&sess->sess_cmd_lock); - continue; - } cmd->transport_state |= CMD_T_ABORTED; spin_unlock(&cmd->t_state_lock); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ef9e75b359d4..b25d27e8ca20 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2056,7 +2056,6 @@ void target_execute_cmd(struct se_cmd *cmd) spin_lock_irq(&cmd->t_state_lock); cmd->t_state = TRANSPORT_PROCESSING; - cmd->transport_state &= ~CMD_T_PRE_EXECUTE; cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); @@ -2765,7 +2764,6 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref) ret = -ESHUTDOWN; goto out; } - se_cmd->transport_state |= CMD_T_PRE_EXECUTE; list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); percpu_ref_get(&se_sess->cmd_count); out: diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 69b7b955902c..19a5bf4214fc 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -502,7 +502,6 @@ struct se_cmd { #define CMD_T_STOP (1 << 5) #define CMD_T_TAS (1 << 10) #define CMD_T_FABRIC_STOP (1 << 11) -#define CMD_T_PRE_EXECUTE (1 << 12) spinlock_t t_state_lock; struct kref cmd_kref; struct completion t_transport_stop_comp; From 2281c95fe751325874d135b237ecdcd3bc34cc26 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:45 -0800 Subject: [PATCH 1051/4277] scsi: target/core: Simplify the LUN RESET implementation Due to the task management handling rework it is safe to wait for a TMF that is not in the active state. Hence remove the CMD_T_ACTIVE test from core_tmr_drain_tmr_list(). Additionally, call __target_check_io_state() instead of open coding it. Reviewed-by: Hannes Reinecke Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/target_core_tmr.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index df1ca827de68..3a1bb799a9ab 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -217,23 +217,13 @@ static void core_tmr_drain_tmr_list( continue; spin_lock(&sess->sess_cmd_lock); - spin_lock(&cmd->t_state_lock); - if (!(cmd->transport_state & CMD_T_ACTIVE) || - (cmd->transport_state & CMD_T_FABRIC_STOP)) { - spin_unlock(&cmd->t_state_lock); - spin_unlock(&sess->sess_cmd_lock); - continue; - } - cmd->transport_state |= CMD_T_ABORTED; - spin_unlock(&cmd->t_state_lock); + rc = __target_check_io_state(cmd, sess, 0); + spin_unlock(&sess->sess_cmd_lock); - rc = kref_get_unless_zero(&cmd->cmd_kref); if (!rc) { printk("LUN_RESET TMR: non-zero kref_get_unless_zero\n"); - spin_unlock(&sess->sess_cmd_lock); continue; } - spin_unlock(&sess->sess_cmd_lock); list_move_tail(&tmr_p->tmr_list, &drain_tmr_list); } From 83f85b8ec305be9d65284de2921d8eeb6c7fcf12 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:46 -0800 Subject: [PATCH 1052/4277] scsi: target/core: Inline transport_lun_remove_cmd() Remove the code that clears .se_lun from transport_cmd_check_stop_to_fabric() such that the transport_lun_remove_cmd() call can be moved into target_release_cmd_kref(). Because this guarantees that transport_lun_remove_cmd() will be called exactly once, it is safe to change the cmpxchg() call into a test of se_cmd.lun_ref_active. Inline transport_lun_remove_cmd() because it is not worth to keep it as a separate function. Reviewed-by: Hannes Reinecke Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 31 +++----------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index b25d27e8ca20..6471ede00336 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -664,11 +664,6 @@ static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd) target_remove_from_state_list(cmd); - /* - * Clear struct se_cmd->se_lun before the handoff to FE. - */ - cmd->se_lun = NULL; - spin_lock_irqsave(&cmd->t_state_lock, flags); /* * Determine if frontend context caller is requesting the stopping of @@ -696,17 +691,6 @@ static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd) return cmd->se_tfo->check_stop_free(cmd); } -static void transport_lun_remove_cmd(struct se_cmd *cmd) -{ - struct se_lun *lun = cmd->se_lun; - - if (!lun) - return; - - if (cmpxchg(&cmd->lun_ref_active, true, false)) - percpu_ref_put(&lun->lun_ref); -} - static void target_complete_failure_work(struct work_struct *work) { struct se_cmd *cmd = container_of(work, struct se_cmd, work); @@ -797,8 +781,6 @@ static void target_handle_abort(struct se_cmd *cmd) WARN_ON_ONCE(kref_read(&cmd->cmd_kref) == 0); - transport_lun_remove_cmd(cmd); - transport_cmd_check_stop_to_fabric(cmd); } @@ -1711,7 +1693,6 @@ static void target_complete_tmr_failure(struct work_struct *work) se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST; se_cmd->se_tfo->queue_tm_rsp(se_cmd); - transport_lun_remove_cmd(se_cmd); transport_cmd_check_stop_to_fabric(se_cmd); } @@ -1902,7 +1883,6 @@ void transport_generic_request_failure(struct se_cmd *cmd, goto queue_full; check_stop: - transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); return; @@ -2200,7 +2180,6 @@ queue_status: transport_handle_queue_full(cmd, cmd->se_dev, ret, false); return; } - transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); } @@ -2295,7 +2274,6 @@ static void target_complete_ok_work(struct work_struct *work) if (ret) goto queue_full; - transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); return; } @@ -2321,7 +2299,6 @@ static void target_complete_ok_work(struct work_struct *work) if (ret) goto queue_full; - transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); return; } @@ -2357,7 +2334,6 @@ queue_rsp: if (ret) goto queue_full; - transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); return; } @@ -2393,7 +2369,6 @@ queue_status: break; } - transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); return; @@ -2720,9 +2695,6 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) */ if (cmd->state_active) target_remove_from_state_list(cmd); - - if (cmd->se_lun) - transport_lun_remove_cmd(cmd); } if (aborted) cmd->free_compl = &compl; @@ -2794,6 +2766,9 @@ static void target_release_cmd_kref(struct kref *kref) struct completion *abrt_compl = se_cmd->abrt_compl; unsigned long flags; + if (se_cmd->lun_ref_active) + percpu_ref_put(&se_cmd->se_lun->lun_ref); + if (se_sess) { spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); list_del_init(&se_cmd->se_cmd_list); From 94ebb47160085343ecaaeab90fa7ffbb86030613 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:47 -0800 Subject: [PATCH 1053/4277] scsi: target/core: Add target_send_busy() Introduce a function that sends the SCSI status "BUSY" back to the initiator. The next patch will add a call to this function in the srpt target driver. Reviewed-by: Hannes Reinecke Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 16 ++++++++++++++++ include/target/target_core_fabric.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 6471ede00336..e3f7e21e6614 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3246,6 +3246,22 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, } EXPORT_SYMBOL(transport_send_check_condition_and_sense); +/** + * target_send_busy - Send SCSI BUSY status back to the initiator + * @cmd: SCSI command for which to send a BUSY reply. + * + * Note: Only call this function if target_submit_cmd*() failed. + */ +int target_send_busy(struct se_cmd *cmd) +{ + WARN_ON_ONCE(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB); + + cmd->scsi_status = SAM_STAT_BUSY; + trace_target_cmd_complete(cmd); + return cmd->se_tfo->queue_status(cmd); +} +EXPORT_SYMBOL(target_send_busy); + static void target_tmr_work(struct work_struct *work) { struct se_cmd *cmd = container_of(work, struct se_cmd, work); diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 691c6b5e6520..8ed90407f062 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -173,6 +173,7 @@ int transport_generic_free_cmd(struct se_cmd *, int); bool transport_wait_for_tasks(struct se_cmd *); int transport_send_check_condition_and_sense(struct se_cmd *, sense_reason_t, int); +int target_send_busy(struct se_cmd *cmd); int target_get_sess_cmd(struct se_cmd *, bool); int target_put_sess_cmd(struct se_cmd *); void target_sess_cmd_list_set_waiting(struct se_session *); From 8b8807b9e982e4ba05da8c0092f3c101daec5b2c Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:48 -0800 Subject: [PATCH 1054/4277] scsi: RDMA/srpt: Fix handling of command / TMF submission failure If submitting an SRP IU to the target core fails, send the SCSI response "BUSY" to the initiator instead of not sending any response. Reviewed-by: Hannes Reinecke Cc: Doug Ledford Cc: Jason Gunthorpe Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/infiniband/ulp/srpt/ib_srpt.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 8cee8c6c6be9..4fc901d1c0c1 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1505,7 +1505,7 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch, pr_err("0x%llx: parsing SRP descriptor table failed.\n", srp_cmd->tag); } - goto release_ioctx; + goto busy; } rc = target_submit_cmd_map_sgls(cmd, ch->sess, srp_cmd->cdb, @@ -1516,13 +1516,12 @@ static void srpt_handle_cmd(struct srpt_rdma_ch *ch, if (rc != 0) { pr_debug("target_submit_cmd() returned %d for tag %#llx\n", rc, srp_cmd->tag); - goto release_ioctx; + goto busy; } return; -release_ioctx: - send_ioctx->state = SRPT_STATE_DONE; - srpt_release_cmd(cmd); +busy: + target_send_busy(cmd); } static int srp_tmr_to_tcm(int fn) From 337ec69ed761ae02a5d1226d607c487fedbd8dd7 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:49 -0800 Subject: [PATCH 1055/4277] scsi: RDMA/srpt: Fix handling of TMF submission failure If submitting a TMF to the target core fails, send the "FUNCTION REJECTED" response to the initiator. Reviewed-by: Hannes Reinecke Cc: Doug Ledford Cc: Jason Gunthorpe Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/infiniband/ulp/srpt/ib_srpt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 4fc901d1c0c1..0dc2df925f76 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1581,11 +1581,9 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, TARGET_SCF_ACK_KREF); if (rc != 0) { send_ioctx->cmd.se_tmr_req->response = TMR_FUNCTION_REJECTED; - goto fail; + cmd->se_tfo->queue_tm_rsp(cmd); } return; -fail: - transport_send_check_condition_and_sense(cmd, 0, 0); // XXX: } /** From fd1b6687091450b0ca3303c97f45ecd87401c3d7 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:50 -0800 Subject: [PATCH 1056/4277] scsi: RDMA/srpt: Rework I/O context allocation Instead of maintaining a list of free I/O contexts, use an sbitmap data structure to track which I/O contexts are in use and which are free. This makes the ib_srpt driver more consistent with other LIO drivers. Cc: Doug Ledford Cc: Jason Gunthorpe Cc: Nicholas Bellinger Cc: Mike Christie Cc: Hannes Reinecke Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/infiniband/ulp/srpt/ib_srpt.c | 47 +++++++++++---------------- drivers/infiniband/ulp/srpt/ib_srpt.h | 4 --- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 0dc2df925f76..d426e01905f9 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1217,22 +1217,15 @@ static int srpt_ch_qp_err(struct srpt_rdma_ch *ch) static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch) { struct srpt_send_ioctx *ioctx; - unsigned long flags; + int tag, cpu; BUG_ON(!ch); - ioctx = NULL; - spin_lock_irqsave(&ch->spinlock, flags); - if (!list_empty(&ch->free_list)) { - ioctx = list_first_entry(&ch->free_list, - struct srpt_send_ioctx, free_list); - list_del(&ioctx->free_list); - } - spin_unlock_irqrestore(&ch->spinlock, flags); - - if (!ioctx) - return ioctx; + tag = sbitmap_queue_get(&ch->sess->sess_tag_pool, &cpu); + if (tag < 0) + return NULL; + ioctx = ch->ioctx_ring[tag]; BUG_ON(ioctx->ch != ch); ioctx->state = SRPT_STATE_NEW; WARN_ON_ONCE(ioctx->recv_ioctx); @@ -1245,6 +1238,8 @@ static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch) */ memset(&ioctx->cmd, 0, sizeof(ioctx->cmd)); memset(&ioctx->sense_data, 0, sizeof(ioctx->sense_data)); + ioctx->cmd.map_tag = tag; + ioctx->cmd.map_cpu = cpu; return ioctx; } @@ -2148,7 +2143,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, struct srpt_rdma_ch *ch = NULL; char i_port_id[36]; u32 it_iu_len; - int i, ret; + int i, tag_num, tag_size, ret; WARN_ON_ONCE(irqs_disabled()); @@ -2248,11 +2243,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, goto free_rsp_cache; } - INIT_LIST_HEAD(&ch->free_list); - for (i = 0; i < ch->rq_size; i++) { + for (i = 0; i < ch->rq_size; i++) ch->ioctx_ring[i]->ch = ch; - list_add_tail(&ch->ioctx_ring[i]->free_list, &ch->free_list); - } if (!sdev->use_srq) { u16 imm_data_offset = req->req_flags & SRP_IMMED_REQUESTED ? be16_to_cpu(req->imm_data_offset) : 0; @@ -2306,18 +2298,20 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, pr_debug("registering session %s\n", ch->sess_name); + tag_num = ch->rq_size; + tag_size = 1; /* ib_srpt does not use se_sess->sess_cmd_map */ if (sport->port_guid_tpg.se_tpg_wwn) - ch->sess = target_setup_session(&sport->port_guid_tpg, 0, 0, - TARGET_PROT_NORMAL, + ch->sess = target_setup_session(&sport->port_guid_tpg, tag_num, + tag_size, TARGET_PROT_NORMAL, ch->sess_name, ch, NULL); if (sport->port_gid_tpg.se_tpg_wwn && IS_ERR_OR_NULL(ch->sess)) - ch->sess = target_setup_session(&sport->port_gid_tpg, 0, 0, - TARGET_PROT_NORMAL, i_port_id, ch, - NULL); + ch->sess = target_setup_session(&sport->port_gid_tpg, tag_num, + tag_size, TARGET_PROT_NORMAL, i_port_id, + ch, NULL); /* Retry without leading "0x" */ if (sport->port_gid_tpg.se_tpg_wwn && IS_ERR_OR_NULL(ch->sess)) - ch->sess = target_setup_session(&sport->port_gid_tpg, 0, 0, - TARGET_PROT_NORMAL, + ch->sess = target_setup_session(&sport->port_gid_tpg, tag_num, + tag_size, TARGET_PROT_NORMAL, i_port_id + 2, ch, NULL); if (IS_ERR_OR_NULL(ch->sess)) { WARN_ON_ONCE(ch->sess == NULL); @@ -3279,7 +3273,6 @@ static void srpt_release_cmd(struct se_cmd *se_cmd) struct srpt_send_ioctx, cmd); struct srpt_rdma_ch *ch = ioctx->ch; struct srpt_recv_ioctx *recv_ioctx = ioctx->recv_ioctx; - unsigned long flags; WARN_ON_ONCE(ioctx->state != SRPT_STATE_DONE && !(ioctx->cmd.transport_state & CMD_T_ABORTED)); @@ -3295,9 +3288,7 @@ static void srpt_release_cmd(struct se_cmd *se_cmd) ioctx->n_rw_ctx = 0; } - spin_lock_irqsave(&ch->spinlock, flags); - list_add(&ioctx->free_list, &ch->free_list); - spin_unlock_irqrestore(&ch->spinlock, flags); + target_free_tag(se_cmd->se_sess, se_cmd); } /** diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h index 39b3e50baf3d..ee9f20e9177a 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.h +++ b/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -207,7 +207,6 @@ struct srpt_rw_ctx { * @rw_ctxs: RDMA read/write contexts. * @imm_sg: Scatterlist for immediate data. * @rdma_cqe: RDMA completion queue element. - * @free_list: Node in srpt_rdma_ch.free_list. * @state: I/O context state. * @cmd: Target core command data structure. * @sense_data: SCSI sense data. @@ -227,7 +226,6 @@ struct srpt_send_ioctx { struct scatterlist imm_sg; struct ib_cqe rdma_cqe; - struct list_head free_list; enum srpt_command_state state; struct se_cmd cmd; u8 n_rdma; @@ -277,7 +275,6 @@ enum rdma_ch_state { * @req_lim_delta: Number of credits not yet sent back to the initiator. * @imm_data_offset: Offset from start of SRP_CMD for immediate data. * @spinlock: Protects free_list and state. - * @free_list: Head of list with free send I/O contexts. * @state: channel state. See also enum rdma_ch_state. * @using_rdma_cm: Whether the RDMA/CM or IB/CM is used for this channel. * @processing_wait_list: Whether or not cmd_wait_list is being processed. @@ -318,7 +315,6 @@ struct srpt_rdma_ch { atomic_t req_lim_delta; u16 imm_data_offset; spinlock_t spinlock; - struct list_head free_list; enum rdma_ch_state state; struct kmem_cache *rsp_buf_cache; struct srpt_send_ioctx **ioctx_ring; From 40ca8757291ca7a8775498112d320205b2a2e571 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:51 -0800 Subject: [PATCH 1057/4277] scsi: RDMA/srpt: Fix a credit leak for aborted commands Make sure that the next time a response is sent to the initiator that the credit it had allocated for the aborted request gets freed. Cc: Doug Ledford Cc: Jason Gunthorpe Cc: Nicholas Bellinger Cc: Mike Christie Cc: Hannes Reinecke Cc: Christoph Hellwig Fixes: 131e6abc674e ("target: Add TFO->abort_task for aborted task resources release") # v3.15 Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/infiniband/ulp/srpt/ib_srpt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index d426e01905f9..1a039f16d315 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -2870,8 +2870,19 @@ static void srpt_queue_tm_rsp(struct se_cmd *cmd) srpt_queue_response(cmd); } +/* + * This function is called for aborted commands if no response is sent to the + * initiator. Make sure that the credits freed by aborting a command are + * returned to the initiator the next time a response is sent by incrementing + * ch->req_lim_delta. + */ static void srpt_aborted_task(struct se_cmd *cmd) { + struct srpt_send_ioctx *ioctx = container_of(cmd, + struct srpt_send_ioctx, cmd); + struct srpt_rdma_ch *ch = ioctx->ch; + + atomic_inc(&ch->req_lim_delta); } static int srpt_queue_status(struct se_cmd *cmd) From b53655b80cfcf33c0b753cf17dc3d42a97299c2f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:52 -0800 Subject: [PATCH 1058/4277] scsi: target/iscsi: Remove an incorrect comment The single iscsit_start_nopin_response_timer() caller does not hold any locks. Hence remove the comment above this function. Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target_util.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 86987da86dd6..762448e2f8b7 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -956,9 +956,6 @@ void iscsit_mod_nopin_response_timer(struct iscsi_conn *conn) spin_unlock_bh(&conn->nopin_timer_lock); } -/* - * Called with conn->nopin_timer_lock held. - */ void iscsit_start_nopin_response_timer(struct iscsi_conn *conn) { struct iscsi_session *sess = conn->sess; From 618baaf7881e4ed25197369505378685b7b7a05d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:53 -0800 Subject: [PATCH 1059/4277] scsi: target/iscsi: Convert comments about locking into runtime checks Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target.c | 10 ++++------ drivers/target/iscsi/iscsi_target_erl0.c | 5 ++--- drivers/target/iscsi/iscsi_target_erl1.c | 16 +++++++--------- drivers/target/iscsi/iscsi_target_util.c | 16 +++++++--------- drivers/target/target_core_alua.c | 5 ++--- drivers/target/target_core_device.c | 6 +++--- drivers/target/target_core_pr.c | 15 ++++++--------- 7 files changed, 31 insertions(+), 42 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 984941e036c8..98984c3adfba 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -308,9 +308,6 @@ bool iscsit_check_np_match( return false; } -/* - * Called with mutex np_lock held - */ static struct iscsi_np *iscsit_get_np( struct sockaddr_storage *sockaddr, int network_transport) @@ -318,6 +315,8 @@ static struct iscsi_np *iscsit_get_np( struct iscsi_np *np; bool match; + lockdep_assert_held(&np_lock); + list_for_each_entry(np, &g_np_list, np_list) { spin_lock_bh(&np->np_thread_lock); if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { @@ -2655,9 +2654,6 @@ static int iscsit_handle_immediate_data( return IMMEDIATE_DATA_NORMAL_OPERATION; } -/* - * Called with sess->conn_lock held. - */ /* #warning iscsi_build_conn_drop_async_message() only sends out on connections with active network interface */ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) @@ -2666,6 +2662,8 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) struct iscsi_conn *conn_p; bool found = false; + lockdep_assert_held(&conn->sess->conn_lock); + /* * Only send a Asynchronous Message on connections whos network * interface is still functional. diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 1193cf884a28..8890c0721053 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -802,14 +802,13 @@ void iscsit_start_time2retain_handler(struct iscsi_session *sess) jiffies + sess->sess_ops->DefaultTime2Retain * HZ); } -/* - * Called with spin_lock_bh(&struct se_portal_group->session_lock) held - */ int iscsit_stop_time2retain_timer(struct iscsi_session *sess) { struct iscsi_portal_group *tpg = sess->tpg; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; + lockdep_assert_held(&se_tpg->session_lock); + if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED) return -1; diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 1b54a9c70851..38210ab09887 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -797,14 +797,14 @@ static struct iscsi_ooo_cmdsn *iscsit_allocate_ooo_cmdsn(void) return ooo_cmdsn; } -/* - * Called with sess->cmdsn_mutex held. - */ static int iscsit_attach_ooo_cmdsn( struct iscsi_session *sess, struct iscsi_ooo_cmdsn *ooo_cmdsn) { struct iscsi_ooo_cmdsn *ooo_tail, *ooo_tmp; + + lockdep_assert_held(&sess->cmdsn_mutex); + /* * We attach the struct iscsi_ooo_cmdsn entry to the out of order * list in increasing CmdSN order. @@ -871,15 +871,14 @@ void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *conn) mutex_unlock(&sess->cmdsn_mutex); } -/* - * Called with sess->cmdsn_mutex held. - */ int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess) { int ooo_count = 0; struct iscsi_cmd *cmd = NULL; struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; + lockdep_assert_held(&sess->cmdsn_mutex); + list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, &sess->sess_ooo_cmdsn_list, ooo_list) { if (ooo_cmdsn->cmdsn != sess->exp_cmd_sn) @@ -1232,9 +1231,6 @@ void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd) spin_unlock_bh(&cmd->dataout_timeout_lock); } -/* - * Called with cmd->dataout_timeout_lock held. - */ void iscsit_start_dataout_timer( struct iscsi_cmd *cmd, struct iscsi_conn *conn) @@ -1242,6 +1238,8 @@ void iscsit_start_dataout_timer( struct iscsi_session *sess = conn->sess; struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); + lockdep_assert_held(&cmd->dataout_timeout_lock); + if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING) return; diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 762448e2f8b7..58886d4b33cb 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -56,9 +56,6 @@ extern struct list_head g_tiqn_list; extern spinlock_t tiqn_lock; -/* - * Called with cmd->r2t_lock held. - */ int iscsit_add_r2t_to_list( struct iscsi_cmd *cmd, u32 offset, @@ -68,6 +65,8 @@ int iscsit_add_r2t_to_list( { struct iscsi_r2t *r2t; + lockdep_assert_held(&cmd->r2t_lock); + r2t = kmem_cache_zalloc(lio_r2t_cache, GFP_ATOMIC); if (!r2t) { pr_err("Unable to allocate memory for struct iscsi_r2t.\n"); @@ -128,11 +127,10 @@ struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *cmd) return NULL; } -/* - * Called with cmd->r2t_lock held. - */ void iscsit_free_r2t(struct iscsi_r2t *r2t, struct iscsi_cmd *cmd) { + lockdep_assert_held(&cmd->r2t_lock); + list_del(&r2t->r2t_list); kmem_cache_free(lio_r2t_cache, r2t); } @@ -1013,13 +1011,13 @@ void iscsit_handle_nopin_timeout(struct timer_list *t) iscsit_dec_conn_usage_count(conn); } -/* - * Called with conn->nopin_timer_lock held. - */ void __iscsit_start_nopin_timer(struct iscsi_conn *conn) { struct iscsi_session *sess = conn->sess; struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); + + lockdep_assert_held(&conn->nopin_timer_lock); + /* * NOPIN timeout is disabled. */ diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 6b0d9beacf90..e09f0cf86bed 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -910,9 +910,6 @@ static int core_alua_write_tpg_metadata( return (ret < 0) ? -EIO : 0; } -/* - * Called with tg_pt_gp->tg_pt_gp_transition_mutex held - */ static int core_alua_update_tpg_primary_metadata( struct t10_alua_tg_pt_gp *tg_pt_gp) { @@ -921,6 +918,8 @@ static int core_alua_update_tpg_primary_metadata( char *path; int len, rc; + lockdep_assert_held(&tg_pt_gp->tg_pt_gp_transition_mutex); + md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL); if (!md_buf) { pr_err("Unable to allocate buf for ALUA metadata\n"); diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 93c56f4a9911..1f8482b6473b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -404,9 +404,6 @@ int core_enable_device_list_for_node( return 0; } -/* - * Called with se_node_acl->lun_entry_mutex held. - */ void core_disable_device_list_for_node( struct se_lun *lun, struct se_dev_entry *orig, @@ -418,6 +415,9 @@ void core_disable_device_list_for_node( * reference to se_device->dev_group. */ struct se_device *dev = rcu_dereference_raw(lun->lun_se_dev); + + lockdep_assert_held(&nacl->lun_entry_mutex); + /* * If the MappedLUN entry is being disabled, the entry in * lun->lun_deve_list must be removed now before clearing the diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 397f38cb7f4e..1597a9ebadca 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1290,9 +1290,6 @@ static int core_scsi3_check_implicit_release( return ret; } -/* - * Called with struct t10_reservation->registration_lock held. - */ static void __core_scsi3_free_registration( struct se_device *dev, struct t10_pr_registration *pr_reg, @@ -1308,6 +1305,8 @@ static void __core_scsi3_free_registration( struct se_dev_entry *deve; char i_buf[PR_REG_ISID_ID_LEN]; + lockdep_assert_held(&pr_tmpl->registration_lock); + memset(i_buf, 0, PR_REG_ISID_ID_LEN); core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN); @@ -2450,9 +2449,6 @@ core_scsi3_emulate_pro_reserve(struct se_cmd *cmd, int type, int scope, } } -/* - * Called with struct se_device->dev_reservation_lock held. - */ static void __core_scsi3_complete_pro_release( struct se_device *dev, struct se_node_acl *se_nacl, @@ -2464,6 +2460,8 @@ static void __core_scsi3_complete_pro_release( char i_buf[PR_REG_ISID_ID_LEN]; int pr_res_type = 0, pr_res_scope = 0; + lockdep_assert_held(&dev->dev_reservation_lock); + memset(i_buf, 0, PR_REG_ISID_ID_LEN); core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN); /* @@ -2760,9 +2758,6 @@ core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key) return 0; } -/* - * Called with struct se_device->dev_reservation_lock held. - */ static void __core_scsi3_complete_pro_preempt( struct se_device *dev, struct t10_pr_registration *pr_reg, @@ -2775,6 +2770,8 @@ static void __core_scsi3_complete_pro_preempt( const struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; char i_buf[PR_REG_ISID_ID_LEN]; + lockdep_assert_held(&dev->dev_reservation_lock); + memset(i_buf, 0, PR_REG_ISID_ID_LEN); core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN); /* From 0300b1147e528fe6f6b0632b0ef353a87810dabf Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:54 -0800 Subject: [PATCH 1060/4277] scsi: target/iscsi: Fix spelling of "unsolicited" Change "unsoliticed" into "unsolicited". Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Sagi Grimberg Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/infiniband/ulp/isert/ib_isert.c | 2 +- drivers/target/iscsi/cxgbit/cxgbit_target.c | 2 +- drivers/target/iscsi/iscsi_target.c | 8 ++++---- drivers/target/iscsi/iscsi_target.h | 2 +- drivers/target/iscsi/iscsi_target_erl1.c | 2 +- include/target/iscsi/iscsi_transport.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index e3dd13798d79..989f1ac4245c 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1186,7 +1186,7 @@ sequence_cmd: rc = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn); if (!rc && dump_payload == false && unsol_data) - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); else if (dump_payload && imm_data) target_put_sess_cmd(&cmd->se_cmd); diff --git a/drivers/target/iscsi/cxgbit/cxgbit_target.c b/drivers/target/iscsi/cxgbit/cxgbit_target.c index 25eb3891e34b..29b350a0b58f 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_target.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_target.c @@ -960,7 +960,7 @@ after_immediate_data: target_put_sess_cmd(&cmd->se_cmd); return 0; } else if (cmd->unsolicited_data) { - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); } } else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) { diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 98984c3adfba..7059ffdb0c20 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1194,7 +1194,7 @@ attach_cmd: } EXPORT_SYMBOL(iscsit_setup_scsi_cmd); -void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd) +void iscsit_set_unsolicited_dataout(struct iscsi_cmd *cmd) { iscsit_set_dataout_sequence_values(cmd); @@ -1202,7 +1202,7 @@ void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd) iscsit_start_dataout_timer(cmd, cmd->conn); spin_unlock_bh(&cmd->dataout_timeout_lock); } -EXPORT_SYMBOL(iscsit_set_unsoliticed_dataout); +EXPORT_SYMBOL(iscsit_set_unsolicited_dataout); int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr) @@ -1236,7 +1236,7 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, */ if (!cmd->immediate_data) { if (!cmd->sense_reason && cmd->unsolicited_data) - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); if (!cmd->sense_reason) return 0; @@ -1308,7 +1308,7 @@ after_immediate_data: target_put_sess_cmd(&cmd->se_cmd); return rc; } else if (cmd->unsolicited_data) - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); } else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) { /* diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h index 48bac0acf8c7..c95f56a3ce31 100644 --- a/drivers/target/iscsi/iscsi_target.h +++ b/drivers/target/iscsi/iscsi_target.h @@ -31,7 +31,7 @@ extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *, struct iscsi_portal_group *, bool); extern int iscsit_del_np(struct iscsi_np *); extern int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8, unsigned char *); -extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *); +extern void iscsit_set_unsolicited_dataout(struct iscsi_cmd *); extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *); extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *); extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *); diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 38210ab09887..c27335c29640 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -979,7 +979,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo) if (cmd->se_cmd.transport_state & CMD_T_ABORTED) return 0; - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); } return transport_handle_cdb_direct(&cmd->se_cmd); diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h index 91948bc5b185..8b4b8ce2c1ae 100644 --- a/include/target/iscsi/iscsi_transport.h +++ b/include/target/iscsi/iscsi_transport.h @@ -53,7 +53,7 @@ extern void iscsit_put_transport(struct iscsit_transport *); */ extern int iscsit_setup_scsi_cmd(struct iscsi_conn *, struct iscsi_cmd *, unsigned char *); -extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *); +extern void iscsit_set_unsolicited_dataout(struct iscsi_cmd *); extern int iscsit_process_scsi_cmd(struct iscsi_conn *, struct iscsi_cmd *, struct iscsi_scsi_req *); extern int From 1e65cc1631c187c4607e9a2067769791be529ce6 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:55 -0800 Subject: [PATCH 1061/4277] scsi: target/iscsi: Rename a function and a function pointer Having both a function and a function pointer member with the same name (iscsit_release_cmd) is confusing. Hence rename the function pointer member. Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/cxgbit/cxgbit.h | 2 +- drivers/target/iscsi/cxgbit/cxgbit_ddp.c | 2 +- drivers/target/iscsi/cxgbit/cxgbit_main.c | 2 +- drivers/target/iscsi/iscsi_target_util.c | 4 ++-- include/target/iscsi/iscsi_transport.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/target/iscsi/cxgbit/cxgbit.h b/drivers/target/iscsi/cxgbit/cxgbit.h index 417b9e66b0cd..3cca22e19964 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit.h +++ b/drivers/target/iscsi/cxgbit/cxgbit.h @@ -345,7 +345,7 @@ struct cxgbit_device *cxgbit_find_device(struct net_device *, u8 *); int cxgbit_ddp_init(struct cxgbit_device *); int cxgbit_setup_conn_pgidx(struct cxgbit_sock *, u32); int cxgbit_reserve_ttt(struct cxgbit_sock *, struct iscsi_cmd *); -void cxgbit_release_cmd(struct iscsi_conn *, struct iscsi_cmd *); +void cxgbit_unmap_cmd(struct iscsi_conn *, struct iscsi_cmd *); static inline struct cxgbi_ppm *cdev2ppm(struct cxgbit_device *cdev) diff --git a/drivers/target/iscsi/cxgbit/cxgbit_ddp.c b/drivers/target/iscsi/cxgbit/cxgbit_ddp.c index 76a262674c8d..d57fd3ed3fa5 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_ddp.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_ddp.c @@ -263,7 +263,7 @@ out: r2t->targ_xfer_tag = ttinfo->tag; } -void cxgbit_release_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd) +void cxgbit_unmap_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd) { struct cxgbit_cmd *ccmd = iscsit_priv_cmd(cmd); diff --git a/drivers/target/iscsi/cxgbit/cxgbit_main.c b/drivers/target/iscsi/cxgbit/cxgbit_main.c index c011c826fc26..4a7bb0b49d17 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_main.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_main.c @@ -678,7 +678,7 @@ static struct iscsit_transport cxgbit_transport = { .iscsit_get_r2t_ttt = cxgbit_get_r2t_ttt, .iscsit_get_rx_pdu = cxgbit_get_rx_pdu, .iscsit_validate_params = cxgbit_validate_params, - .iscsit_release_cmd = cxgbit_release_cmd, + .iscsit_unmap_cmd = cxgbit_unmap_cmd, .iscsit_aborted_task = iscsit_aborted_task, .iscsit_get_sup_prot_ops = cxgbit_get_sup_prot_ops, }; diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 58886d4b33cb..3ac494f63a0b 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -760,8 +760,8 @@ void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool check_queues) iscsit_remove_cmd_from_response_queue(cmd, conn); } - if (conn && conn->conn_transport->iscsit_release_cmd) - conn->conn_transport->iscsit_release_cmd(conn, cmd); + if (conn && conn->conn_transport->iscsit_unmap_cmd) + conn->conn_transport->iscsit_unmap_cmd(conn, cmd); } void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h index 8b4b8ce2c1ae..75bee29fd7dd 100644 --- a/include/target/iscsi/iscsi_transport.h +++ b/include/target/iscsi/iscsi_transport.h @@ -26,7 +26,7 @@ struct iscsit_transport { void (*iscsit_aborted_task)(struct iscsi_conn *, struct iscsi_cmd *); int (*iscsit_xmit_pdu)(struct iscsi_conn *, struct iscsi_cmd *, struct iscsi_datain_req *, const void *, u32); - void (*iscsit_release_cmd)(struct iscsi_conn *, struct iscsi_cmd *); + void (*iscsit_unmap_cmd)(struct iscsi_conn *, struct iscsi_cmd *); void (*iscsit_get_rx_pdu)(struct iscsi_conn *); int (*iscsit_validate_params)(struct iscsi_conn *); void (*iscsit_get_r2t_ttt)(struct iscsi_conn *, struct iscsi_cmd *, From 32e36bfbcf31452a854263e7c7f32fbefc4b44d8 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:56 -0800 Subject: [PATCH 1062/4277] scsi: target/iscsi: Avoid iscsit_release_commands_from_conn() deadlock When using SCSI passthrough in combination with the iSCSI target driver then cmd->t_state_lock may be obtained from interrupt context. Hence, all code that obtains cmd->t_state_lock from thread context must disable interrupts first. This patch avoids that lockdep reports the following: WARNING: inconsistent lock state 4.18.0-dbg+ #1 Not tainted -------------------------------- inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage. iscsi_ttx/1800 [HC1[1]:SC0[2]:HE0:SE0] takes: 000000006e7b0ceb (&(&cmd->t_state_lock)->rlock){?...}, at: target_complete_cmd+0x47/0x2c0 [target_core_mod] {HARDIRQ-ON-W} state was registered at: lock_acquire+0xd2/0x260 _raw_spin_lock+0x32/0x50 iscsit_close_connection+0x97e/0x1020 [iscsi_target_mod] iscsit_take_action_for_connection_exit+0x108/0x200 [iscsi_target_mod] iscsi_target_rx_thread+0x180/0x190 [iscsi_target_mod] kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30 irq event stamp: 1281 hardirqs last enabled at (1279): [] __local_bh_enable_ip+0xa9/0x160 hardirqs last disabled at (1281): [] interrupt_entry+0xb5/0xd0 softirqs last enabled at (1278): [] lock_sock_nested+0x51/0xc0 softirqs last disabled at (1280): [] ip6_finish_output2+0x124/0xe40 [ipv6] other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&cmd->t_state_lock)->rlock); lock(&(&cmd->t_state_lock)->rlock); *** DEADLOCK *** 3 locks held by iscsi_ttx/1800: *0: 00000000c3b711b7 (sk_lock-AF_INET6){+.+.}, at: tcp_sendmsg+0x1e/0x50 *1: 00000000fa81046f (rcu_read_lock){....}, at: inet6_csk_xmit+0xc7/0x2e0 [ipv6] *2: 00000000c091d70d (rcu_read_lock_bh){....}, at: ip6_finish_output2+0x124/0xe40 [ipv6] stack backtrace: CPU: 0 PID: 1800 Comm: iscsi_ttx Not tainted 4.18.0-dbg+ #1 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.0.0-prebuilt.qemu-project.org 04/01/2014 Call Trace: dump_stack+0xa4/0xf5 print_usage_bug+0x25b/0x27b mark_lock+0x70f/0x7b0 __lock_acquire+0xbc2/0x1b50 lock_acquire+0xd2/0x260 _raw_spin_lock_irqsave+0x4a/0x60 target_complete_cmd+0x47/0x2c0 [target_core_mod] target_complete_cmd_with_length+0x70/0xa0 [target_core_mod] pscsi_req_done+0x335/0x530 [target_core_pscsi] __blk_mq_end_request+0xa5/0x140 scsi_end_request+0x112/0x320 [scsi_mod] scsi_io_completion+0x183/0xa30 [scsi_mod] scsi_finish_command+0x1c0/0x280 [scsi_mod] scsi_softirq_done+0x19a/0x230 [scsi_mod] __blk_mq_complete_request_remote+0x2f/0x40 flush_smp_call_function_queue+0x12a/0x220 generic_smp_call_function_single_interrupt+0x13/0x30 smp_call_function_single_interrupt+0x7a/0x350 call_function_single_interrupt+0xf/0x20 RIP: 0010:__asan_load4+0x1e/0x80 debug_lockdep_rcu_enabled+0x26/0x40 ip6_finish_output2+0x15a/0xe40 [ipv6] ip6_finish_output+0x308/0x440 [ipv6] ip6_output+0x11d/0x3b0 [ipv6] ip6_xmit+0x639/0xc50 [ipv6] inet6_csk_xmit+0x198/0x2e0 [ipv6] __tcp_transmit_skb+0xc1b/0x15b0 tcp_write_xmit+0x42e/0x1f20 __tcp_push_pending_frames+0x59/0x150 tcp_push+0x189/0x270 tcp_sendmsg_locked+0x7b9/0x1680 tcp_sendmsg+0x2c/0x50 inet_sendmsg+0x71/0x250 sock_sendmsg+0x4c/0x60 tx_data+0x12b/0x1f0 [iscsi_target_mod] iscsit_send_tx_data+0x77/0xe0 [iscsi_target_mod] iscsit_xmit_pdu+0x2c5/0x740 [iscsi_target_mod] iscsit_response_queue+0x941/0xd40 [iscsi_target_mod] iscsi_target_tx_thread+0x23b/0x350 [iscsi_target_mod] kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30 Fixes: 064cdd2d91c2 ("target: Fix race between iscsi-target connection shutdown + ABORT_TASK") Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 7059ffdb0c20..408d77eb717c 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -4038,9 +4038,9 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) struct se_cmd *se_cmd = &cmd->se_cmd; if (se_cmd->se_tfo != NULL) { - spin_lock(&se_cmd->t_state_lock); + spin_lock_irq(&se_cmd->t_state_lock); se_cmd->transport_state |= CMD_T_FABRIC_STOP; - spin_unlock(&se_cmd->t_state_lock); + spin_unlock_irq(&se_cmd->t_state_lock); } } spin_unlock_bh(&conn->cmd_lock); From 658c31224de24ec0e00987b7d0c93128f455331f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:57 -0800 Subject: [PATCH 1063/4277] scsi: target/iscsi: Simplify iscsit_dump_data_payload() Use a single loop to dump immediate data, padding and digest instead of using separate rx_data() calls for each type of data. Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target_erl1.c | 41 ++++++------------------ 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index c27335c29640..e02e1aaf63c5 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -48,14 +48,20 @@ int iscsit_dump_data_payload( u32 buf_len, int dump_padding_digest) { - char *buf, pad_bytes[4]; + char *buf; int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got; - u32 length, padding, offset = 0, size; + u32 length, offset = 0, size; struct kvec iov; if (conn->sess->sess_ops->RDMAExtensions) return 0; + if (dump_padding_digest) { + buf_len = ALIGN(buf_len, 4); + if (conn->conn_ops->DataDigest) + buf_len += ISCSI_CRC_LEN; + } + length = min(buf_len, OFFLOAD_BUF_SIZE); buf = kzalloc(length, GFP_ATOMIC); @@ -75,41 +81,12 @@ int iscsit_dump_data_payload( rx_got = rx_data(conn, &iov, 1, size); if (rx_got != size) { ret = DATAOUT_CANNOT_RECOVER; - goto out; + break; } offset += size; } - if (!dump_padding_digest) - goto out; - - padding = ((-buf_len) & 3); - if (padding != 0) { - iov.iov_len = padding; - iov.iov_base = pad_bytes; - - rx_got = rx_data(conn, &iov, 1, padding); - if (rx_got != padding) { - ret = DATAOUT_CANNOT_RECOVER; - goto out; - } - } - - if (conn->conn_ops->DataDigest) { - u32 data_crc; - - iov.iov_len = ISCSI_CRC_LEN; - iov.iov_base = &data_crc; - - rx_got = rx_data(conn, &iov, 1, ISCSI_CRC_LEN); - if (rx_got != ISCSI_CRC_LEN) { - ret = DATAOUT_CANNOT_RECOVER; - goto out; - } - } - -out: kfree(buf); return ret; } From 47eefded1f70c1b6c77bee18bd1bf5d17642dcb9 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 25 Jan 2019 10:34:58 -0800 Subject: [PATCH 1064/4277] scsi: target/iscsi: Simplify iscsit_handle_text_cmd() Treat text_in and padding as a single buffer instead of two buffers. Cc: Nicholas Bellinger Cc: Mike Christie Cc: Christoph Hellwig Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 408d77eb717c..e14ac29209c8 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2240,28 +2240,25 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rx_size = payload_length; if (payload_length) { u32 checksum = 0, data_crc = 0; - u32 padding = 0, pad_bytes = 0; + u32 padding = 0; int niov = 0, rx_got; - struct kvec iov[3]; + struct kvec iov[2]; - text_in = kzalloc(payload_length, GFP_KERNEL); + rx_size = ALIGN(payload_length, 4); + text_in = kzalloc(rx_size, GFP_KERNEL); if (!text_in) goto reject; cmd->text_in_ptr = text_in; - memset(iov, 0, 3 * sizeof(struct kvec)); + memset(iov, 0, sizeof(iov)); iov[niov].iov_base = text_in; - iov[niov++].iov_len = payload_length; + iov[niov++].iov_len = rx_size; - padding = ((-payload_length) & 3); - if (padding != 0) { - iov[niov].iov_base = &pad_bytes; - iov[niov++].iov_len = padding; - rx_size += padding; + padding = rx_size - payload_length; + if (padding) pr_debug("Receiving %u additional bytes" " for padding.\n", padding); - } if (conn->conn_ops->DataDigest) { iov[niov].iov_base = &checksum; iov[niov++].iov_len = ISCSI_CRC_LEN; @@ -2273,9 +2270,9 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, goto reject; if (conn->conn_ops->DataDigest) { - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, text_in, - payload_length, padding, - &pad_bytes, &data_crc); + iscsit_do_crypto_hash_buf(conn->conn_rx_hash, + text_in, rx_size, 0, NULL, + &data_crc); if (checksum != data_crc) { pr_err("Text data CRC32C DataDigest" From 27752647f88a1b03019e03f21f32b60452af7d4b Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Mon, 28 Jan 2019 22:04:26 +0800 Subject: [PATCH 1065/4277] scsi: ufs: Print uic error history in time order uic errors are currently printed out of time order. Make things more readable by printing logs in time order, and printing "No record" if history is empty. Signed-off-by: Stanley Chu Reviewed-by: Avri Altman Reviewed-by: Bean Huo Tested-by: Bean Huo Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9ba7671b84f8..f90badcb8318 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -393,15 +393,20 @@ static void ufshcd_print_uic_err_hist(struct ufs_hba *hba, struct ufs_uic_err_reg_hist *err_hist, char *err_name) { int i; + bool found = false; for (i = 0; i < UIC_ERR_REG_HIST_LENGTH; i++) { - int p = (i + err_hist->pos - 1) % UIC_ERR_REG_HIST_LENGTH; + int p = (i + err_hist->pos) % UIC_ERR_REG_HIST_LENGTH; if (err_hist->reg[p] == 0) continue; dev_err(hba->dev, "%s[%d] = 0x%x at %lld us\n", err_name, i, err_hist->reg[p], ktime_to_us(err_hist->tstamp[p])); + found = true; } + + if (!found) + dev_err(hba->dev, "No record of %s uic errors\n", err_name); } static void ufshcd_print_host_regs(struct ufs_hba *hba) From d7e6db20944c8fb26073bc3ba9d5328992b82858 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Wed, 30 Jan 2019 16:07:34 +0900 Subject: [PATCH 1066/4277] scsi: sd: Improve sd_print_capacity() There is no need to call twice string_get_size() when the capacity messages are not going to be printed. Reverse the message output condition to return early and avoid executing string_get_size() when it is not necessary. Signed-off-by: Damien Le Moal Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 4fbb8310e268..3db9b1fe7516 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2565,25 +2565,25 @@ sd_print_capacity(struct scsi_disk *sdkp, int sector_size = sdkp->device->sector_size; char cap_str_2[10], cap_str_10[10]; + if (!sdkp->first_scan && old_capacity == sdkp->capacity) + return; + string_get_size(sdkp->capacity, sector_size, STRING_UNITS_2, cap_str_2, sizeof(cap_str_2)); string_get_size(sdkp->capacity, sector_size, - STRING_UNITS_10, cap_str_10, - sizeof(cap_str_10)); + STRING_UNITS_10, cap_str_10, sizeof(cap_str_10)); - if (sdkp->first_scan || old_capacity != sdkp->capacity) { + sd_printk(KERN_NOTICE, sdkp, + "%llu %d-byte logical blocks: (%s/%s)\n", + (unsigned long long)sdkp->capacity, + sector_size, cap_str_10, cap_str_2); + + if (sdkp->physical_block_size != sector_size) sd_printk(KERN_NOTICE, sdkp, - "%llu %d-byte logical blocks: (%s/%s)\n", - (unsigned long long)sdkp->capacity, - sector_size, cap_str_10, cap_str_2); + "%u-byte physical blocks\n", + sdkp->physical_block_size); - if (sdkp->physical_block_size != sector_size) - sd_printk(KERN_NOTICE, sdkp, - "%u-byte physical blocks\n", - sdkp->physical_block_size); - - sd_zbc_print_zones(sdkp); - } + sd_zbc_print_zones(sdkp); } /* called with buffer of length 512 */ From e13949aef43a3cf9658fbfbe41803bd04ff82cfd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 2 Feb 2019 10:40:52 +0000 Subject: [PATCH 1067/4277] scsi: aacraid: clean up some indentation and formatting issues There are several issues with badly indented statements. Fix these and clean up the formatting. Signed-off-by: Colin Ian King Acked-by: Mahesh Rajashekhara Signed-off-by: Martin K. Petersen --- drivers/scsi/aacraid/commsup.c | 25 +++++++++++++------------ drivers/scsi/aacraid/linit.c | 3 +-- drivers/scsi/aacraid/src.c | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index d5a6aa9676c8..0bdc6b0f725f 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1376,18 +1376,19 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) container = 0; retry_next: - if (device_config_needed == NOTHING) - for (; container < dev->maximum_num_containers; ++container) { - if ((dev->fsa_dev[container].config_waiting_on == 0) && - (dev->fsa_dev[container].config_needed != NOTHING) && - time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) { - device_config_needed = - dev->fsa_dev[container].config_needed; - dev->fsa_dev[container].config_needed = NOTHING; - channel = CONTAINER_TO_CHANNEL(container); - id = CONTAINER_TO_ID(container); - lun = CONTAINER_TO_LUN(container); - break; + if (device_config_needed == NOTHING) { + for (; container < dev->maximum_num_containers; ++container) { + if ((dev->fsa_dev[container].config_waiting_on == 0) && + (dev->fsa_dev[container].config_needed != NOTHING) && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) { + device_config_needed = + dev->fsa_dev[container].config_needed; + dev->fsa_dev[container].config_needed = NOTHING; + channel = CONTAINER_TO_CHANNEL(container); + id = CONTAINER_TO_ID(container); + lun = CONTAINER_TO_LUN(container); + break; + } } } if (device_config_needed == NOTHING) diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 634ddb90e7aa..dd701d5243b1 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -852,8 +852,7 @@ static u8 aac_eh_tmf_hard_reset_fib(struct aac_hba_map_info *info, address = (u64)fib->hw_error_pa; rst->error_ptr_hi = cpu_to_le32((u32)(address >> 32)); - rst->error_ptr_lo = cpu_to_le32 - ((u32)(address & 0xffffffff)); + rst->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff)); rst->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE); fib->hbacmd_size = sizeof(*rst); diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index 8377aec0649d..97bb9e9d201c 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -1157,7 +1157,7 @@ out: dev_err(&dev->pdev->dev, "%s: %s status = %d", __func__, state_str[state], rc); -return rc; + return rc; } /** * aac_srcv_init - initialize an SRCv card From 89408ac8aa863d44df8c87d03c717048f4a48b50 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Mon, 4 Feb 2019 13:11:10 +0100 Subject: [PATCH 1068/4277] scsi: MAINTAINERS: Add reviewers for UFS patches According to git log and the linux-scsi archives, Vinayak has been inactive for several years. Removing him as maintainer will make the get_maintainer.pl script generate an extensive list of recipients. Add three reviewers as well to vet future UFS patches. Signed-off-by: Marc Gonzalez Acked-by: Joao Pinto Acked-by: Avri Altman Acked-by: Alim Akhtar Signed-off-by: Martin K. Petersen --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 70c93b141393..c91204732508 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15666,7 +15666,9 @@ F: drivers/visorbus/ F: drivers/staging/unisys/ UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER -M: Vinayak Holikatti +R: Alim Akhtar +R: Avri Altman +R: Pedro Sousa L: linux-scsi@vger.kernel.org S: Supported F: Documentation/scsi/ufs.txt From 7c429da45c33035e8d9d17a8e56802c2b1e90d03 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 30 Jan 2019 18:48:13 +0100 Subject: [PATCH 1069/4277] scsi: MAINTAINERS: Update dwc driver maintainer to Pedro Sousa Currently I am managing the Synopsys drivers & tools team (full-time) and so I am passing the DWC UFS driver maintenance to Pedro Sousa. Signed-off-by: Joao Pinto Cc: Pedro Sousa Cc: Marc Gonzalez Cc: Alex Lemberg Signed-off-by: Martin K. Petersen --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index c91204732508..6c445a485804 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15675,7 +15675,7 @@ F: Documentation/scsi/ufs.txt F: drivers/scsi/ufs/ UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER DWC HOOKS -M: Joao Pinto +M: Pedro Sousa L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/ufs/*dwc* From daa0681118c9f4fc4afffb3757a1e085fba2d069 Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Tue, 29 Jan 2019 01:38:12 -0800 Subject: [PATCH 1070/4277] scsi: megaraid_sas: Rework code to get PD and LD list During FW init, combine the code to get the PD and LD list from FW into a single function. This patch is in preparation for adding support for HOST_DEVICE_LIST DCMD. Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 44 ++++++++++++++++------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index f7bdd783360a..8cd84abcce2b 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -5319,6 +5319,33 @@ fallback: instance->reply_map[cpu] = cpu % instance->msix_vectors; } +/** + * megasas_get_device_list - Get the PD and LD device list from FW. + * @instance: Adapter soft state + * @return: Success or failure + * + * Issue DCMDs to Firmware to get the PD and LD list. + */ +static +int megasas_get_device_list(struct megasas_instance *instance) +{ + memset(instance->pd_list, 0, + (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); + memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); + + if (megasas_get_pd_list(instance) < 0) { + dev_err(&instance->pdev->dev, "failed to get PD list\n"); + return FAILED; + } + + if (megasas_ld_list_query(instance, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) { + dev_err(&instance->pdev->dev, "failed to get LD list\n"); + return FAILED; + } + + return SUCCESS; +} /** * megasas_init_fw - Initializes the FW * @instance: Adapter soft state @@ -5571,18 +5598,13 @@ static int megasas_init_fw(struct megasas_instance *instance) megasas_setup_jbod_map(instance); - /** for passthrough - * the following function will get the PD LIST. - */ - memset(instance->pd_list, 0, - (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); - if (megasas_get_pd_list(instance) < 0) { - dev_err(&instance->pdev->dev, "failed to get PD list\n"); + if (megasas_get_device_list(instance) != SUCCESS) { + dev_err(&instance->pdev->dev, + "%s: megasas_get_device_list failed\n", + __func__); goto fail_get_ld_pd_list; } - memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); - /* stream detection initialization */ if (instance->adapter_type >= VENTURA_SERIES) { fusion->stream_detect_by_ld = @@ -5612,10 +5634,6 @@ static int megasas_init_fw(struct megasas_instance *instance) } } - if (megasas_ld_list_query(instance, - MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) - goto fail_get_ld_pd_list; - /* * Compute the max allowed sectors per IO: The controller info has two * limits on max sectors. Driver should use the minimum of these two. From 44abbaf64b5d8feae04c9cb5bdc60d6c22da3035 Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Tue, 29 Jan 2019 01:38:13 -0800 Subject: [PATCH 1071/4277] scsi: megaraid_sas: Rework device add code in AEN path In preparation of adding support for the DEVICE_LIST DCMD, this patch refactors the code in the AEN event handling path. Add new function to update the PD and LD list in driver. Move the code to scan PD and VD channels into separate function. Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 173 +++++++++++++--------- 1 file changed, 107 insertions(+), 66 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 8cd84abcce2b..e590d3a902cc 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -7884,6 +7884,103 @@ static inline void megasas_remove_scsi_device(struct scsi_device *sdev) scsi_device_put(sdev); } +/** + * megasas_update_device_list - Update the PD and LD device list from FW + * after an AEN event notification + * @instance: Adapter soft state + * @event_type: Indicates type of event (PD or LD event) + * + * @return: Success or failure + * + * Issue DCMDs to Firmware to update the internal device list in driver. + */ +static +int megasas_update_device_list(struct megasas_instance *instance, + int event_type) +{ + int dcmd_ret = DCMD_SUCCESS; + + if (event_type & SCAN_PD_CHANNEL) { + dcmd_ret = megasas_get_pd_list(instance); + + if (dcmd_ret != DCMD_SUCCESS) + goto out; + } + + if (event_type & SCAN_VD_CHANNEL) { + if (!instance->requestorId || + (instance->requestorId && + megasas_get_ld_vf_affiliation(instance, 0))) { + dcmd_ret = megasas_ld_list_query(instance, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); + if (dcmd_ret != DCMD_SUCCESS) + goto out; + } + } + +out: + return dcmd_ret; +} + +/** + * megasas_add_remove_devices - Add/remove devices to SCSI mid-layer + * after an AEN event notification + * @instance: Adapter soft state + * @scan_type: Indicates type of devices (PD/LD) to add + * @return void + */ +static +void megasas_add_remove_devices(struct megasas_instance *instance, + int scan_type) +{ + int i, j; + u16 pd_index = 0; + u16 ld_index = 0; + struct Scsi_Host *host; + struct scsi_device *sdev1; + + host = instance->host; + + if (scan_type & SCAN_PD_CHANNEL) { + for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + pd_index = i * MEGASAS_MAX_DEV_PER_CHANNEL + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) { + if (!sdev1) + scsi_add_device(host, i, j, 0); + else + scsi_device_put(sdev1); + } else { + if (sdev1) + megasas_remove_scsi_device(sdev1); + } + } + } + } + + if (scan_type & SCAN_VD_CHANNEL) { + for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, + MEGASAS_MAX_PD_CHANNELS + i, j, 0); + if (instance->ld_ids[ld_index] != 0xff) { + if (!sdev1) + scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); + else + scsi_device_put(sdev1); + } else { + if (sdev1) + megasas_remove_scsi_device(sdev1); + } + } + } + } + +} + static void megasas_aen_polling(struct work_struct *work) { @@ -7891,11 +7988,7 @@ megasas_aen_polling(struct work_struct *work) container_of(work, struct megasas_aen_event, hotplug_work.work); struct megasas_instance *instance = ev->instance; union megasas_evt_class_locale class_locale; - struct Scsi_Host *host; - struct scsi_device *sdev1; - u16 pd_index = 0; - u16 ld_index = 0; - int i, j, doscan = 0; + int event_type = 0; u32 seq_num, wait_time = MEGASAS_RESET_WAIT_TIME; int error; u8 dcmd_ret = DCMD_SUCCESS; @@ -7914,7 +8007,6 @@ megasas_aen_polling(struct work_struct *work) mutex_lock(&instance->reset_mutex); instance->ev = NULL; - host = instance->host; if (instance->evt_detail) { megasas_decode_evt(instance); @@ -7922,40 +8014,20 @@ megasas_aen_polling(struct work_struct *work) case MR_EVT_PD_INSERTED: case MR_EVT_PD_REMOVED: - dcmd_ret = megasas_get_pd_list(instance); - if (dcmd_ret == DCMD_SUCCESS) - doscan = SCAN_PD_CHANNEL; + event_type = SCAN_PD_CHANNEL; break; case MR_EVT_LD_OFFLINE: case MR_EVT_CFG_CLEARED: case MR_EVT_LD_DELETED: case MR_EVT_LD_CREATED: - if (!instance->requestorId || - (instance->requestorId && megasas_get_ld_vf_affiliation(instance, 0))) - dcmd_ret = megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); - - if (dcmd_ret == DCMD_SUCCESS) - doscan = SCAN_VD_CHANNEL; - + event_type = SCAN_VD_CHANNEL; break; case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: case MR_EVT_FOREIGN_CFG_IMPORTED: case MR_EVT_LD_STATE_CHANGE: - dcmd_ret = megasas_get_pd_list(instance); - - if (dcmd_ret != DCMD_SUCCESS) - break; - - if (!instance->requestorId || - (instance->requestorId && megasas_get_ld_vf_affiliation(instance, 0))) - dcmd_ret = megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); - - if (dcmd_ret != DCMD_SUCCESS) - break; - - doscan = SCAN_VD_CHANNEL | SCAN_PD_CHANNEL; + event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL; dev_info(&instance->pdev->dev, "scanning for scsi%d...\n", instance->host->host_no); break; @@ -7971,7 +8043,7 @@ megasas_aen_polling(struct work_struct *work) } break; default: - doscan = 0; + event_type = 0; break; } } else { @@ -7981,44 +8053,13 @@ megasas_aen_polling(struct work_struct *work) return; } + if (event_type) + dcmd_ret = megasas_update_device_list(instance, event_type); + mutex_unlock(&instance->reset_mutex); - if (doscan & SCAN_PD_CHANNEL) { - for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { - for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { - pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j; - sdev1 = scsi_device_lookup(host, i, j, 0); - if (instance->pd_list[pd_index].driveState == - MR_PD_STATE_SYSTEM) { - if (!sdev1) - scsi_add_device(host, i, j, 0); - else - scsi_device_put(sdev1); - } else { - if (sdev1) - megasas_remove_scsi_device(sdev1); - } - } - } - } - - if (doscan & SCAN_VD_CHANNEL) { - for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) { - for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { - ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; - sdev1 = scsi_device_lookup(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); - if (instance->ld_ids[ld_index] != 0xff) { - if (!sdev1) - scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); - else - scsi_device_put(sdev1); - } else { - if (sdev1) - megasas_remove_scsi_device(sdev1); - } - } - } - } + if (event_type && dcmd_ret == DCMD_SUCCESS) + megasas_add_remove_devices(instance, event_type); if (dcmd_ret == DCMD_SUCCESS) seq_num = le32_to_cpu(instance->evt_detail->seq_num) + 1; From f6fe57310811780d55d79e30da51db98677f1a90 Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Tue, 29 Jan 2019 01:38:14 -0800 Subject: [PATCH 1072/4277] scsi: megaraid_sas: Add support for DEVICE_LIST DCMD in driver This patch adds support for the new DEVICE_LIST DCMD. Driver currently sends two separate DCMDs for getting the list of PDs and LDs that are exposed to host. The new DCMD provides a single interface to get a list of both PDs and LDs that are exposed to the host. Based on the list of target IDs that are returned by this DCMD, driver will add the devices (PD/LD) to SML. Driver will check for FW support for this new DCMD and based on the support will either send the new DCMD or will fall back to the earlier method of sending two separate DCMDs for PD and LD list. Signed-off-by: Kashyap Desai Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas.h | 49 ++++- drivers/scsi/megaraid/megaraid_sas_base.c | 222 ++++++++++++++++++-- drivers/scsi/megaraid/megaraid_sas_fusion.c | 1 + drivers/scsi/megaraid/megaraid_sas_fusion.h | 1 + 4 files changed, 250 insertions(+), 23 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 16536c41f0c5..fe1173f02c54 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -790,6 +790,37 @@ struct MR_LD_TARGETID_LIST { u8 targetId[MAX_LOGICAL_DRIVES_EXT]; }; +struct MR_HOST_DEVICE_LIST_ENTRY { + struct { + union { + struct { +#if defined(__BIG_ENDIAN_BITFIELD) + u8 reserved:7; + u8 is_sys_pd:1; +#else + u8 is_sys_pd:1; + u8 reserved:7; +#endif + } bits; + u8 byte; + } u; + } flags; + u8 scsi_type; + __le16 target_id; + u8 reserved[2]; + __le64 sas_addr[2]; +} __packed; + +struct MR_HOST_DEVICE_LIST { + __le32 size; + __le32 count; + struct MR_HOST_DEVICE_LIST_ENTRY host_device_list[1]; +} __packed; + +#define HOST_DEVICE_LIST_SZ (sizeof(struct MR_HOST_DEVICE_LIST) + \ + (sizeof(struct MR_HOST_DEVICE_LIST_ENTRY) * \ + (MEGASAS_MAX_PD + MAX_LOGICAL_DRIVES_EXT - 1))) + /* * SAS controller properties @@ -870,13 +901,17 @@ struct megasas_ctrl_prop { u8 viewSpace; struct { #if defined(__BIG_ENDIAN_BITFIELD) - u16 reserved2:11; + u16 reserved3:9; + u16 enable_fw_dev_list:1; + u16 reserved2:1; u16 enable_snap_dump:1; u16 reserved1:4; #else u16 reserved1:4; u16 enable_snap_dump:1; - u16 reserved2:11; + u16 reserved2:1; + u16 enable_fw_dev_list:1; + u16 reserved3:9; #endif } on_off_properties2; }; @@ -1685,7 +1720,8 @@ union megasas_sgl_frame { typedef union _MFI_CAPABILITIES { struct { #if defined(__BIG_ENDIAN_BITFIELD) - u32 reserved:17; + u32 reserved:16; + u32 support_fw_exposed_dev_list:1; u32 support_nvme_passthru:1; u32 support_64bit_mode:1; u32 support_pd_map_target_id:1; @@ -1717,7 +1753,8 @@ typedef union _MFI_CAPABILITIES { u32 support_pd_map_target_id:1; u32 support_64bit_mode:1; u32 support_nvme_passthru:1; - u32 reserved:17; + u32 support_fw_exposed_dev_list:1; + u32 reserved:16; #endif } mfi_capabilities; __le32 reg; @@ -2202,6 +2239,9 @@ struct megasas_instance { struct MR_LD_TARGETID_LIST *ld_targetid_list_buf; dma_addr_t ld_targetid_list_buf_h; + struct MR_HOST_DEVICE_LIST *host_device_list_buf; + dma_addr_t host_device_list_buf_h; + struct MR_SNAPDUMP_PROPERTIES *snapdump_prop; dma_addr_t snapdump_prop_h; @@ -2337,6 +2377,7 @@ struct megasas_instance { u8 task_abort_tmo; u8 max_reset_tmo; u8 snapdump_wait_time; + u8 enable_fw_dev_list; }; struct MR_LD_VF_MAP { u32 size; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index e590d3a902cc..ed730d054052 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -4634,6 +4634,123 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) return ret; } +/** + * dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET + * dcmd.mbox - reserved + * dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure + * Desc: This DCMD will return the combined device list + * Status: MFI_STAT_OK - List returned successfully + * MFI_STAT_INVALID_CMD - Firmware support for the feature has been + * disabled + * @instance: Adapter soft state + * @is_probe: Driver probe check + * Return: 0 if DCMD succeeded + * non-zero if failed + */ +int +megasas_host_device_list_query(struct megasas_instance *instance, + bool is_probe) +{ + int ret, i, target_id; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_HOST_DEVICE_LIST *ci; + u32 count; + dma_addr_t ci_h; + + ci = instance->host_device_list_buf; + ci_h = instance->host_device_list_buf_h; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + dev_warn(&instance->pdev->dev, + "%s: failed to get cmd\n", + __func__); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->mbox.b[0] = is_probe ? 0 : 1; + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = cpu_to_le32(HOST_DEVICE_LIST_SZ); + dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_DEVICE_LIST_GET); + + megasas_set_dma_settings(instance, dcmd, ci_h, HOST_DEVICE_LIST_SZ); + + if (!instance->mask_interrupts) { + ret = megasas_issue_blocked_cmd(instance, cmd, + MFI_IO_TIMEOUT_SECS); + } else { + ret = megasas_issue_polled(instance, cmd); + cmd->flags |= DRV_DCMD_SKIP_REFIRE; + } + + switch (ret) { + case DCMD_SUCCESS: + /* Fill the internal pd_list and ld_ids array based on + * targetIds returned by FW + */ + count = le32_to_cpu(ci->count); + + memset(instance->local_pd_list, 0, + MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); + memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT); + for (i = 0; i < count; i++) { + target_id = le16_to_cpu(ci->host_device_list[i].target_id); + if (ci->host_device_list[i].flags.u.bits.is_sys_pd) { + instance->local_pd_list[target_id].tid = target_id; + instance->local_pd_list[target_id].driveType = + ci->host_device_list[i].scsi_type; + instance->local_pd_list[target_id].driveState = + MR_PD_STATE_SYSTEM; + } else { + instance->ld_ids[target_id] = target_id; + } + } + + memcpy(instance->pd_list, instance->local_pd_list, + sizeof(instance->pd_list)); + break; + + case DCMD_TIMEOUT: + switch (dcmd_timeout_ocr_possible(instance)) { + case INITIATE_OCR: + cmd->flags |= DRV_DCMD_SKIP_REFIRE; + megasas_reset_fusion(instance->host, + MFI_IO_TIMEOUT_OCR); + break; + case KILL_ADAPTER: + megaraid_sas_kill_hba(instance); + break; + case IGNORE_TIMEOUT: + dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", + __func__, __LINE__); + break; + } + break; + case DCMD_FAILED: + dev_err(&instance->pdev->dev, + "%s: MR_DCMD_CTRL_DEVICE_LIST_GET failed\n", + __func__); + break; + } + + if (ret != DCMD_TIMEOUT) + megasas_return_cmd(instance, cmd); + + return ret; +} + /* * megasas_update_ext_vd_details : Update details w.r.t Extended VD * instance : Controller's instance @@ -4861,6 +4978,9 @@ megasas_get_ctrl_info(struct megasas_instance *instance) (ci->properties.on_off_properties2.enable_snap_dump ? MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME : 0); + instance->enable_fw_dev_list = + ci->properties.on_off_properties2.enable_fw_dev_list; + dev_info(&instance->pdev->dev, "controller type\t: %s(%dMB)\n", instance->is_imr ? "iMR" : "MR", @@ -5325,6 +5445,8 @@ fallback: * @return: Success or failure * * Issue DCMDs to Firmware to get the PD and LD list. + * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination + * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. */ static int megasas_get_device_list(struct megasas_instance *instance) @@ -5333,15 +5455,20 @@ int megasas_get_device_list(struct megasas_instance *instance) (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); - if (megasas_get_pd_list(instance) < 0) { - dev_err(&instance->pdev->dev, "failed to get PD list\n"); - return FAILED; - } + if (instance->enable_fw_dev_list) { + if (megasas_host_device_list_query(instance, true)) + return FAILED; + } else { + if (megasas_get_pd_list(instance) < 0) { + dev_err(&instance->pdev->dev, "failed to get PD list\n"); + return FAILED; + } - if (megasas_ld_list_query(instance, - MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) { - dev_err(&instance->pdev->dev, "failed to get LD list\n"); - return FAILED; + if (megasas_ld_list_query(instance, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) { + dev_err(&instance->pdev->dev, "failed to get LD list\n"); + return FAILED; + } } return SUCCESS; @@ -6442,6 +6569,18 @@ int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) if (!instance->snapdump_prop) dev_err(&pdev->dev, "Failed to allocate snapdump properties buffer\n"); + + instance->host_device_list_buf = dma_alloc_coherent(&pdev->dev, + HOST_DEVICE_LIST_SZ, + &instance->host_device_list_buf_h, + GFP_KERNEL); + + if (!instance->host_device_list_buf) { + dev_err(&pdev->dev, + "Failed to allocate targetid list buffer\n"); + return -ENOMEM; + } + } instance->pd_list_buf = @@ -6591,6 +6730,13 @@ void megasas_free_ctrl_dma_buffers(struct megasas_instance *instance) sizeof(struct MR_SNAPDUMP_PROPERTIES), instance->snapdump_prop, instance->snapdump_prop_h); + + if (instance->host_device_list_buf) + dma_free_coherent(&pdev->dev, + HOST_DEVICE_LIST_SZ, + instance->host_device_list_buf, + instance->host_device_list_buf_h); + } /* @@ -6764,7 +6910,9 @@ static int megasas_probe_one(struct pci_dev *pdev, /* * Trigger SCSI to scan our drives */ - scsi_scan_host(host); + if (!instance->enable_fw_dev_list || + (instance->host_device_list_buf->count > 0)) + scsi_scan_host(host); /* * Initiate AEN (Asynchronous Event Notification) @@ -7893,6 +8041,8 @@ static inline void megasas_remove_scsi_device(struct scsi_device *sdev) * @return: Success or failure * * Issue DCMDs to Firmware to update the internal device list in driver. + * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination + * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. */ static int megasas_update_device_list(struct megasas_instance *instance, @@ -7900,22 +8050,28 @@ int megasas_update_device_list(struct megasas_instance *instance, { int dcmd_ret = DCMD_SUCCESS; - if (event_type & SCAN_PD_CHANNEL) { - dcmd_ret = megasas_get_pd_list(instance); - + if (instance->enable_fw_dev_list) { + dcmd_ret = megasas_host_device_list_query(instance, false); if (dcmd_ret != DCMD_SUCCESS) goto out; - } + } else { + if (event_type & SCAN_PD_CHANNEL) { + dcmd_ret = megasas_get_pd_list(instance); - if (event_type & SCAN_VD_CHANNEL) { - if (!instance->requestorId || - (instance->requestorId && - megasas_get_ld_vf_affiliation(instance, 0))) { - dcmd_ret = megasas_ld_list_query(instance, - MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); if (dcmd_ret != DCMD_SUCCESS) goto out; } + + if (event_type & SCAN_VD_CHANNEL) { + if (!instance->requestorId || + (instance->requestorId && + megasas_get_ld_vf_affiliation(instance, 0))) { + dcmd_ret = megasas_ld_list_query(instance, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); + if (dcmd_ret != DCMD_SUCCESS) + goto out; + } + } } out: @@ -7936,11 +8092,39 @@ void megasas_add_remove_devices(struct megasas_instance *instance, int i, j; u16 pd_index = 0; u16 ld_index = 0; + u16 channel = 0, id = 0; struct Scsi_Host *host; struct scsi_device *sdev1; + struct MR_HOST_DEVICE_LIST *targetid_list = NULL; + struct MR_HOST_DEVICE_LIST_ENTRY *targetid_entry = NULL; host = instance->host; + if (instance->enable_fw_dev_list) { + targetid_list = instance->host_device_list_buf; + for (i = 0; i < targetid_list->count; i++) { + targetid_entry = &targetid_list->host_device_list[i]; + if (targetid_entry->flags.u.bits.is_sys_pd) { + channel = le16_to_cpu(targetid_entry->target_id) / + MEGASAS_MAX_DEV_PER_CHANNEL; + id = le16_to_cpu(targetid_entry->target_id) % + MEGASAS_MAX_DEV_PER_CHANNEL; + } else { + channel = MEGASAS_MAX_PD_CHANNELS + + (le16_to_cpu(targetid_entry->target_id) / + MEGASAS_MAX_DEV_PER_CHANNEL); + id = le16_to_cpu(targetid_entry->target_id) % + MEGASAS_MAX_DEV_PER_CHANNEL; + } + sdev1 = scsi_device_lookup(host, channel, id, 0); + if (!sdev1) { + scsi_add_device(host, channel, id, 0); + } else { + scsi_device_put(sdev1); + } + } + } + if (scan_type & SCAN_PD_CHANNEL) { for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 211c17c33aa0..26ab86874a8b 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1072,6 +1072,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) drv_ops->mfi_capabilities.support_qd_throttling = 1; drv_ops->mfi_capabilities.support_pd_map_target_id = 1; drv_ops->mfi_capabilities.support_nvme_passthru = 1; + drv_ops->mfi_capabilities.support_fw_exposed_dev_list = 1; if (instance->consistent_mask_64bit) drv_ops->mfi_capabilities.support_64bit_mode = 1; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index ca73c50fe723..1481bf029490 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -724,6 +724,7 @@ struct MPI2_IOC_INIT_REQUEST { #define MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111 0x03200200 #define MR_DCMD_LD_VF_MAP_GET_ALL_LDS 0x03150200 #define MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES 0x01200100 +#define MR_DCMD_CTRL_DEVICE_LIST_GET 0x01190600 struct MR_DEV_HANDLE_INFO { __le16 curDevHdl; From 1244790d178bb09cfb59a70fa3d294d342631736 Mon Sep 17 00:00:00 2001 From: Suganath Prabu S Date: Tue, 29 Jan 2019 07:14:40 -0500 Subject: [PATCH 1073/4277] scsi: mpt3sas: Rename mpi endpoint device ID macro. MPI Endpoint is a PCIe switch based on MPI2. Renaming device ID macro from MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP to MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP. Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h | 2 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h index 398fa6fde960..a53a758de6b0 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h @@ -548,7 +548,7 @@ typedef struct _MPI2_CONFIG_REPLY { #define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086) #define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) #define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) -#define MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP (0x02B0) +#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP (0x02B0) /*MPI v2.5 SAS products */ #define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 6be39dc27103..c3a0f7051075 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -10256,7 +10256,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) case MPI2_MFGPAGE_DEVID_SAS2308_1: case MPI2_MFGPAGE_DEVID_SAS2308_2: case MPI2_MFGPAGE_DEVID_SAS2308_3: - case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: return MPI2_VERSION; case MPI25_MFGPAGE_DEVID_SAS3004: case MPI25_MFGPAGE_DEVID_SAS3008: @@ -10343,7 +10343,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->is_warpdrive = 1; ioc->hide_ir_msg = 1; break; - case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: ioc->is_mcpu_endpoint = 1; break; default: @@ -10783,7 +10783,7 @@ static const struct pci_device_id mpt3sas_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP, PCI_ANY_ID, PCI_ANY_ID }, /* SSS6200 */ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, From 8f8384503e40c65cfe36b82772c5b7a6403abc5b Mon Sep 17 00:00:00 2001 From: Suganath Prabu S Date: Tue, 29 Jan 2019 07:14:41 -0500 Subject: [PATCH 1074/4277] scsi: mpt3sas: Add support for NVMe Switch Adapter Added device ID for NVMe Switch Adapter (Ambrosia). VID: 0x1000 DID: 0x02B1 Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h | 1 + drivers/scsi/mpt3sas/mpt3sas_scsih.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h index a53a758de6b0..a2f4a55c51be 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h @@ -549,6 +549,7 @@ typedef struct _MPI2_CONFIG_REPLY { #define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) #define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) #define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP (0x02B0) +#define MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1 (0x02B1) /*MPI v2.5 SAS products */ #define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index c3a0f7051075..4a73753dbb9b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -10257,6 +10257,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) case MPI2_MFGPAGE_DEVID_SAS2308_2: case MPI2_MFGPAGE_DEVID_SAS2308_3: case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1: return MPI2_VERSION; case MPI25_MFGPAGE_DEVID_SAS3004: case MPI25_MFGPAGE_DEVID_SAS3008: @@ -10344,6 +10345,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->hide_ir_msg = 1; break; case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP: + case MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1: ioc->is_mcpu_endpoint = 1; break; default: @@ -10785,6 +10787,8 @@ static const struct pci_device_id mpt3sas_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID }, { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP, PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SWITCH_MPI_EP_1, + PCI_ANY_ID, PCI_ANY_ID }, /* SSS6200 */ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, PCI_ANY_ID, PCI_ANY_ID }, From eb9c7ce560453349652394ce493ac2d2b47f2b65 Mon Sep 17 00:00:00 2001 From: Suganath Prabu S Date: Tue, 29 Jan 2019 07:14:42 -0500 Subject: [PATCH 1075/4277] scsi: mpt3sas: Add support for ATLAS PCIe switch Add Atlas PCIe Switch Management Port device PNPID, Vendor Id: 0x1000 device Id: 0x00B2 This device is based on MPI 2.6 spec and it exposes one SES device to accept management commands for the PCIe switch. Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.h | 3 +++ drivers/scsi/mpt3sas/mpt3sas_scsih.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 800351932cc3..db12c1b2738c 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -193,6 +193,9 @@ struct mpt3sas_nvme_cmd { #define SAS2_PCI_DEVICE_B0_REVISION (0x01) #define SAS3_PCI_DEVICE_C0_REVISION (0x02) +/* Atlas PCIe Switch Management Port */ +#define MPI26_ATLAS_PCIe_SWITCH_DEVID (0x00B2) + /* * Intel HBA branding */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 4a73753dbb9b..8bb5b8f9f4d2 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -10283,6 +10283,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: case MPI26_MFGPAGE_DEVID_SAS3616: + case MPI26_ATLAS_PCIe_SWITCH_DEVID: case MPI26_MFGPAGE_DEVID_CFG_SEC_3916: case MPI26_MFGPAGE_DEVID_HARD_SEC_3916: case MPI26_MFGPAGE_DEVID_CFG_SEC_3816: @@ -10373,6 +10374,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) case MPI26_MFGPAGE_DEVID_SAS3516_1: case MPI26_MFGPAGE_DEVID_SAS3416: case MPI26_MFGPAGE_DEVID_SAS3616: + case MPI26_ATLAS_PCIe_SWITCH_DEVID: ioc->is_gen35_ioc = 1; break; case MPI26_MFGPAGE_DEVID_CFG_SEC_3816: @@ -10853,6 +10855,10 @@ static const struct pci_device_id mpt3sas_pci_table[] = { { MPI2_MFGPAGE_VENDORID_LSI, MPI26_MFGPAGE_DEVID_HARD_SEC_3916, PCI_ANY_ID, PCI_ANY_ID }, + /* Atlas PCIe Switch Management Port */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI26_ATLAS_PCIe_SWITCH_DEVID, + PCI_ANY_ID, PCI_ANY_ID }, + /* Sea SI 0x00E5 Configurable Secure * 0x00E6 Hard Secure */ From c6ded86a161dcf46697e79afafe8c7155d98ad63 Mon Sep 17 00:00:00 2001 From: Suganath Prabu S Date: Tue, 29 Jan 2019 07:14:43 -0500 Subject: [PATCH 1076/4277] scsi: mpt3sas: Update driver version to 27.102.00.00 Updated driver version to 27.102.00.00 from 27.101.00.00. Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index db12c1b2738c..19158cb59101 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -75,9 +75,9 @@ #define MPT3SAS_DRIVER_NAME "mpt3sas" #define MPT3SAS_AUTHOR "Avago Technologies " #define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" -#define MPT3SAS_DRIVER_VERSION "27.101.00.00" +#define MPT3SAS_DRIVER_VERSION "27.102.00.00" #define MPT3SAS_MAJOR_VERSION 27 -#define MPT3SAS_MINOR_VERSION 101 +#define MPT3SAS_MINOR_VERSION 102 #define MPT3SAS_BUILD_VERSION 0 #define MPT3SAS_RELEASE_VERSION 00 From bd54211b8e199ffb701ec98bf4f301e4a6f38a92 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 4 Feb 2019 23:12:08 -0500 Subject: [PATCH 1077/4277] XArray tests: RCU lock prohibits GFP_KERNEL Drop and reacquire the RCU read lock while using GFP_KERNEL. Reported-by: Li RongQing Signed-off-by: Matthew Wilcox --- lib/test_xarray.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/test_xarray.c b/lib/test_xarray.c index c596a957f764..671a93ee09e6 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c @@ -107,8 +107,11 @@ static noinline void check_xas_retry(struct xarray *xa) XA_BUG_ON(xa, xas.xa_node != XAS_RESTART); XA_BUG_ON(xa, xas_next_entry(&xas, ULONG_MAX) != xa_mk_value(0)); XA_BUG_ON(xa, xas.xa_node != NULL); + rcu_read_unlock(); XA_BUG_ON(xa, xa_store_index(xa, 1, GFP_KERNEL) != NULL); + + rcu_read_lock(); XA_BUG_ON(xa, !xa_is_internal(xas_reload(&xas))); xas.xa_node = XAS_RESTART; XA_BUG_ON(xa, xas_next_entry(&xas, ULONG_MAX) != xa_mk_value(0)); From 809ab9371ca0a96b44d9866ad82849410759a45b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sat, 26 Jan 2019 00:52:26 -0500 Subject: [PATCH 1078/4277] XArray: Update xa_erase family descriptions xa_erase does not allocate memory and doesn't have a gfp parameter. Update the descriptions of all four variants to be more useful. Signed-off-by: Matthew Wilcox --- include/linux/xarray.h | 12 ++++++------ lib/xarray.c | 17 ++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 5d9d318bcf7a..e11841537631 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -526,9 +526,9 @@ static inline void *xa_store_irq(struct xarray *xa, unsigned long index, * @xa: XArray. * @index: Index of entry. * - * This function is the equivalent of calling xa_store() with %NULL as - * the third argument. The XArray does not need to allocate memory, so - * the user does not need to provide GFP flags. + * After this function returns, loading from @index will return %NULL. + * If the index is part of a multi-index entry, all indices will be erased + * and none of the entries will be part of a multi-index entry. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. @@ -550,9 +550,9 @@ static inline void *xa_erase_bh(struct xarray *xa, unsigned long index) * @xa: XArray. * @index: Index of entry. * - * This function is the equivalent of calling xa_store() with %NULL as - * the third argument. The XArray does not need to allocate memory, so - * the user does not need to provide GFP flags. + * After this function returns, loading from @index will return %NULL. + * If the index is part of a multi-index entry, all indices will be erased + * and none of the entries will be part of a multi-index entry. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. diff --git a/lib/xarray.c b/lib/xarray.c index 81c3171ddde9..fb783bf2a441 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1294,13 +1294,12 @@ static void *xas_result(struct xa_state *xas, void *curr) * @xa: XArray. * @index: Index into array. * - * If the entry at this index is a multi-index entry then all indices will - * be erased, and the entry will no longer be a multi-index entry. - * This function expects the xa_lock to be held on entry. + * After this function returns, loading from @index will return %NULL. + * If the index is part of a multi-index entry, all indices will be erased + * and none of the entries will be part of a multi-index entry. * - * Context: Any context. Expects xa_lock to be held on entry. May - * release and reacquire xa_lock if @gfp flags permit. - * Return: The old entry at this index. + * Context: Any context. Expects xa_lock to be held on entry. + * Return: The entry which used to be at this index. */ void *__xa_erase(struct xarray *xa, unsigned long index) { @@ -1314,9 +1313,9 @@ EXPORT_SYMBOL(__xa_erase); * @xa: XArray. * @index: Index of entry. * - * This function is the equivalent of calling xa_store() with %NULL as - * the third argument. The XArray does not need to allocate memory, so - * the user does not need to provide GFP flags. + * After this function returns, loading from @index will return %NULL. + * If the index is part of a multi-index entry, all indices will be erased + * and none of the entries will be part of a multi-index entry. * * Context: Any context. Takes and releases the xa_lock. * Return: The entry which used to be at this index. From e47ff893bc674c32ac21094d623533ac6e585ca7 Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Mon, 28 Jan 2019 16:17:58 -0800 Subject: [PATCH 1079/4277] Input: sx8654 - convert #defined flags to BIT(x) Some of the #defined register values are one-bit flags. Convert them to use the BIT(x) macro instead of 1 byte hexadecimal values. This improves readability and clarifies the intent. Signed-off-by: Richard Leitner Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index de83ed1da36e..477533cd40ab 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -27,6 +27,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -46,7 +47,7 @@ #define I2C_REG_SOFTRESET 0x3f #define I2C_REG_SX8650_STAT 0x05 -#define SX8650_STAT_CONVIRQ 0x80 +#define SX8650_STAT_CONVIRQ BIT(7) /* commands */ #define CMD_READ_REGISTER 0x40 @@ -56,8 +57,8 @@ #define SOFTRESET_VALUE 0xde /* bits for I2C_REG_IRQSRC */ -#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 -#define IRQ_PENRELEASE 0x04 +#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(3) +#define IRQ_PENRELEASE BIT(2) /* bits for RegTouch1 */ #define CONDIRQ 0x20 @@ -65,8 +66,8 @@ #define FILT_7SA 0x03 /* bits for I2C_REG_CHANMASK */ -#define CONV_X 0x80 -#define CONV_Y 0x40 +#define CONV_X BIT(7) +#define CONV_Y BIT(6) /* coordinates rate: higher nibble of CTRL0 register */ #define RATE_MANUAL 0x00 From 3c1697e6bc5cbe51e8e86446ab0482318a762637 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 28 Jan 2019 16:34:37 -0800 Subject: [PATCH 1080/4277] Input: sx8654 - do not override interrupt trigger We should rely on the interrupt trigger (level vs edge) set up by the firmware or board code instead of forcing what we consider appropriate. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sx8654.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 477533cd40ab..dbdf4898aa17 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -403,7 +403,7 @@ static int sx8654_probe(struct i2c_client *client, error = devm_request_threaded_irq(&client->dev, client->irq, NULL, sx8654->data->irqh, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + IRQF_ONESHOT, client->name, sx8654); if (error) { dev_err(&client->dev, From db4a0073cc82a95d8d1a9b05fde82355fcce77d8 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 22 Jan 2019 22:59:35 +0300 Subject: [PATCH 1081/4277] clk: renesas: rcar-gen3: Add RPC clocks The RPCSRC internal clock is controlled by the RPCCKCR.DIV[4:3] on all the R-Car gen3 SoCs except V3M (R8A77970) but the encoding of this field is different between SoCs; it makes sense to support the most common case of this encoding in the R-Car gen3 CPG driver... After adding the RPCSRC clock, we can add the RPC[D2] clocks derived from it and controlled by the RPCCKCR register on all the R-Car gen3 SoCs except V3M (R8A77970); the composite clock driver seems handy for this task, using the spinlock added in the previous patch... Signed-off-by: Sergei Shtylyov Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rcar-gen3-cpg.c | 101 ++++++++++++++++++++++++++++ drivers/clk/renesas/rcar-gen3-cpg.h | 4 ++ 2 files changed, 105 insertions(+) diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 0818691253af..9a8071a8114d 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -422,6 +422,92 @@ free_clock: return clk; } +struct rpc_clock { + struct clk_divider div; + struct clk_gate gate; + /* + * One notifier covers both RPC and RPCD2 clocks as they are both + * controlled by the same RPCCKCR register... + */ + struct cpg_simple_notifier csn; +}; + +static const struct clk_div_table cpg_rpcsrc_div_table[] = { + { 2, 5 }, { 3, 6 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_rpc_div_table[] = { + { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 }, +}; + +static struct clk * __init cpg_rpc_clk_register(const char *name, + void __iomem *base, const char *parent_name, + struct raw_notifier_head *notifiers) +{ + struct rpc_clock *rpc; + struct clk *clk; + + rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); + if (!rpc) + return ERR_PTR(-ENOMEM); + + rpc->div.reg = base + CPG_RPCCKCR; + rpc->div.width = 3; + rpc->div.table = cpg_rpc_div_table; + rpc->div.lock = &cpg_lock; + + rpc->gate.reg = base + CPG_RPCCKCR; + rpc->gate.bit_idx = 8; + rpc->gate.flags = CLK_GATE_SET_TO_DISABLE; + rpc->gate.lock = &cpg_lock; + + rpc->csn.reg = base + CPG_RPCCKCR; + + clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, + &rpc->div.hw, &clk_divider_ops, + &rpc->gate.hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) { + kfree(rpc); + return clk; + } + + cpg_simple_notifier_register(notifiers, &rpc->csn); + return clk; +} + +struct rpcd2_clock { + struct clk_fixed_factor fixed; + struct clk_gate gate; +}; + +static struct clk * __init cpg_rpcd2_clk_register(const char *name, + void __iomem *base, + const char *parent_name) +{ + struct rpcd2_clock *rpcd2; + struct clk *clk; + + rpcd2 = kzalloc(sizeof(*rpcd2), GFP_KERNEL); + if (!rpcd2) + return ERR_PTR(-ENOMEM); + + rpcd2->fixed.mult = 1; + rpcd2->fixed.div = 2; + + rpcd2->gate.reg = base + CPG_RPCCKCR; + rpcd2->gate.bit_idx = 9; + rpcd2->gate.flags = CLK_GATE_SET_TO_DISABLE; + rpcd2->gate.lock = &cpg_lock; + + clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, + &rpcd2->fixed.hw, &clk_fixed_factor_ops, + &rpcd2->gate.hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) + kfree(rpcd2); + + return clk; +} + static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata; static unsigned int cpg_clk_extalr __initdata; @@ -600,6 +686,21 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, } break; + case CLK_TYPE_GEN3_RPCSRC: + return clk_register_divider_table(NULL, core->name, + __clk_get_name(parent), 0, + base + CPG_RPCCKCR, 3, 2, 0, + cpg_rpcsrc_div_table, + &cpg_lock); + + case CLK_TYPE_GEN3_RPC: + return cpg_rpc_clk_register(core->name, base, + __clk_get_name(parent), notifiers); + + case CLK_TYPE_GEN3_RPCD2: + return cpg_rpcd2_clk_register(core->name, base, + __clk_get_name(parent)); + default: return ERR_PTR(-EINVAL); } diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index f4fb6cf16688..eac1b057455a 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -23,6 +23,9 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_Z2, CLK_TYPE_GEN3_OSC, /* OSC EXTAL predivider and fixed divider */ CLK_TYPE_GEN3_RCKSEL, /* Select parent/divider using RCKCR.CKSEL */ + CLK_TYPE_GEN3_RPCSRC, + CLK_TYPE_GEN3_RPC, + CLK_TYPE_GEN3_RPCD2, /* SoC specific definitions start here */ CLK_TYPE_GEN3_SOC_BASE, @@ -57,6 +60,7 @@ struct rcar_gen3_cpg_pll_config { u8 osc_prediv; }; +#define CPG_RPCCKCR 0x238 #define CPG_RCKCR 0x240 struct clk *rcar_gen3_cpg_clk_register(struct device *dev, From 94e3935b5756a790396b57c7a5116460db511b29 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 22 Jan 2019 23:01:07 +0300 Subject: [PATCH 1082/4277] clk: renesas: r8a77980: Add RPC clocks Describe the RPCSRC internal clock and the RPC[D2] clocks derived from it, as well as the RPC-IF module clock, in the R-Car V3H (R8A77980) CPG/MSSR driver. Signed-off-by: Sergei Shtylyov Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a77980-cpg-mssr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/renesas/r8a77980-cpg-mssr.c b/drivers/clk/renesas/r8a77980-cpg-mssr.c index 25a3083b6764..f9e07fcc0d96 100644 --- a/drivers/clk/renesas/r8a77980-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77980-cpg-mssr.c @@ -41,6 +41,7 @@ enum clk_ids { CLK_S2, CLK_S3, CLK_SDSRC, + CLK_RPCSRC, CLK_OCO, /* Module Clocks */ @@ -65,8 +66,14 @@ static const struct cpg_core_clk r8a77980_core_clks[] __initconst = { DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1), DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1), DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), + DEF_BASE(".rpcsrc", CLK_RPCSRC, CLK_TYPE_GEN3_RPCSRC, CLK_PLL1), DEF_RATE(".oco", CLK_OCO, 32768), + DEF_BASE("rpc", R8A77980_CLK_RPC, CLK_TYPE_GEN3_RPC, + CLK_RPCSRC), + DEF_BASE("rpcd2", R8A77980_CLK_RPCD2, CLK_TYPE_GEN3_RPCD2, + R8A77980_CLK_RPC), + /* Core Clock Outputs */ DEF_FIXED("ztr", R8A77980_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), DEF_FIXED("ztrd2", R8A77980_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), @@ -164,6 +171,7 @@ static const struct mssr_mod_clk r8a77980_mod_clks[] __initconst = { DEF_MOD("gpio1", 911, R8A77980_CLK_CP), DEF_MOD("gpio0", 912, R8A77980_CLK_CP), DEF_MOD("can-fd", 914, R8A77980_CLK_S3D2), + DEF_MOD("rpc-if", 917, R8A77980_CLK_RPC), DEF_MOD("i2c4", 927, R8A77980_CLK_S0D6), DEF_MOD("i2c3", 928, R8A77980_CLK_S0D6), DEF_MOD("i2c2", 929, R8A77980_CLK_S3D2), From d1de227de8145e661e940377e050286098197eeb Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 4 Feb 2019 09:20:05 +0000 Subject: [PATCH 1083/4277] clk: renesas: r8a774c0: Add TMU clock This patch adds the TMU clocks to the R8A774C0 SoC. Signed-off-by: Biju Das Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a774c0-cpg-mssr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/renesas/r8a774c0-cpg-mssr.c b/drivers/clk/renesas/r8a774c0-cpg-mssr.c index 4f3111b3113e..aeadb4d66f3e 100644 --- a/drivers/clk/renesas/r8a774c0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774c0-cpg-mssr.c @@ -122,6 +122,11 @@ static const struct cpg_core_clk r8a774c0_core_clks[] __initconst = { }; static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { + DEF_MOD("tmu4", 121, R8A774C0_CLK_S0D6C), + DEF_MOD("tmu3", 122, R8A774C0_CLK_S3D2C), + DEF_MOD("tmu2", 123, R8A774C0_CLK_S3D2C), + DEF_MOD("tmu1", 124, R8A774C0_CLK_S3D2C), + DEF_MOD("tmu0", 125, R8A774C0_CLK_CP), DEF_MOD("scif5", 202, R8A774C0_CLK_S3D4C), DEF_MOD("scif4", 203, R8A774C0_CLK_S3D4C), DEF_MOD("scif3", 204, R8A774C0_CLK_S3D4C), From fdbbd6b74c9278f65302af113e73cf61d36d3037 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Sat, 26 Jan 2019 03:01:44 +0900 Subject: [PATCH 1084/4277] pinctrl: sh-pfc: r8a77990: Add DRIF pins, groups and functions This patch adds DRIF{0,1,2,3} pins, groups and functions to the R8A77990 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Yoshihiro Kaneko Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/sh-pfc/pfc-r8a77990.c | 255 +++++++++++++++++++++++++- 1 file changed, 253 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c index 0a9aa62f50bb..151640c30e9d 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77990.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77990.c @@ -1599,6 +1599,199 @@ static const unsigned int canfd1_data_mux[] = { CANFD1_TX_MARK, CANFD1_RX_MARK, }; +/* - DRIF0 --------------------------------------------------------------- */ +static const unsigned int drif0_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 19), +}; + +static const unsigned int drif0_ctrl_a_mux[] = { + RIF0_CLK_A_MARK, RIF0_SYNC_A_MARK, +}; + +static const unsigned int drif0_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 17), +}; + +static const unsigned int drif0_data0_a_mux[] = { + RIF0_D0_A_MARK, +}; + +static const unsigned int drif0_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 18), +}; + +static const unsigned int drif0_data1_a_mux[] = { + RIF0_D1_A_MARK, +}; + +static const unsigned int drif0_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 15), +}; + +static const unsigned int drif0_ctrl_b_mux[] = { + RIF0_CLK_B_MARK, RIF0_SYNC_B_MARK, +}; + +static const unsigned int drif0_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(3, 13), +}; + +static const unsigned int drif0_data0_b_mux[] = { + RIF0_D0_B_MARK, +}; + +static const unsigned int drif0_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(3, 14), +}; + +static const unsigned int drif0_data1_b_mux[] = { + RIF0_D1_B_MARK, +}; + +/* - DRIF1 --------------------------------------------------------------- */ +static const unsigned int drif1_ctrl_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 1), +}; + +static const unsigned int drif1_ctrl_mux[] = { + RIF1_CLK_MARK, RIF1_SYNC_MARK, +}; + +static const unsigned int drif1_data0_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 2), +}; + +static const unsigned int drif1_data0_mux[] = { + RIF1_D0_MARK, +}; + +static const unsigned int drif1_data1_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 3), +}; + +static const unsigned int drif1_data1_mux[] = { + RIF1_D1_MARK, +}; + +/* - DRIF2 --------------------------------------------------------------- */ +static const unsigned int drif2_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7), +}; + +static const unsigned int drif2_ctrl_a_mux[] = { + RIF2_CLK_A_MARK, RIF2_SYNC_A_MARK, +}; + +static const unsigned int drif2_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(2, 8), +}; + +static const unsigned int drif2_data0_a_mux[] = { + RIF2_D0_A_MARK, +}; + +static const unsigned int drif2_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(2, 9), +}; + +static const unsigned int drif2_data1_a_mux[] = { + RIF2_D1_A_MARK, +}; + +static const unsigned int drif2_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5), +}; + +static const unsigned int drif2_ctrl_b_mux[] = { + RIF2_CLK_B_MARK, RIF2_SYNC_B_MARK, +}; + +static const unsigned int drif2_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(1, 6), +}; + +static const unsigned int drif2_data0_b_mux[] = { + RIF2_D0_B_MARK, +}; + +static const unsigned int drif2_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(1, 7), +}; + +static const unsigned int drif2_data1_b_mux[] = { + RIF2_D1_B_MARK, +}; + +/* - DRIF3 --------------------------------------------------------------- */ +static const unsigned int drif3_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11), +}; + +static const unsigned int drif3_ctrl_a_mux[] = { + RIF3_CLK_A_MARK, RIF3_SYNC_A_MARK, +}; + +static const unsigned int drif3_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(2, 12), +}; + +static const unsigned int drif3_data0_a_mux[] = { + RIF3_D0_A_MARK, +}; + +static const unsigned int drif3_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(2, 13), +}; + +static const unsigned int drif3_data1_a_mux[] = { + RIF3_D1_A_MARK, +}; + +static const unsigned int drif3_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9), +}; + +static const unsigned int drif3_ctrl_b_mux[] = { + RIF3_CLK_B_MARK, RIF3_SYNC_B_MARK, +}; + +static const unsigned int drif3_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(0, 10), +}; + +static const unsigned int drif3_data0_b_mux[] = { + RIF3_D0_B_MARK, +}; + +static const unsigned int drif3_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(0, 11), +}; + +static const unsigned int drif3_data1_b_mux[] = { + RIF3_D1_B_MARK, +}; + /* - DU --------------------------------------------------------------------- */ static const unsigned int du_rgb666_pins[] = { /* R[7:2], G[7:2], B[7:2] */ @@ -3574,7 +3767,7 @@ static const unsigned int vin5_clk_b_mux[] = { static const struct { struct sh_pfc_pin_group common[245]; - struct sh_pfc_pin_group automotive[2]; + struct sh_pfc_pin_group automotive[23]; } pinmux_groups = { .common = { SH_PFC_PIN_GROUP(audio_clk_a), @@ -3826,6 +4019,27 @@ static const struct { .automotive = { SH_PFC_PIN_GROUP(canfd0_data), SH_PFC_PIN_GROUP(canfd1_data), + SH_PFC_PIN_GROUP(drif0_ctrl_a), + SH_PFC_PIN_GROUP(drif0_data0_a), + SH_PFC_PIN_GROUP(drif0_data1_a), + SH_PFC_PIN_GROUP(drif0_ctrl_b), + SH_PFC_PIN_GROUP(drif0_data0_b), + SH_PFC_PIN_GROUP(drif0_data1_b), + SH_PFC_PIN_GROUP(drif1_ctrl), + SH_PFC_PIN_GROUP(drif1_data0), + SH_PFC_PIN_GROUP(drif1_data1), + SH_PFC_PIN_GROUP(drif2_ctrl_a), + SH_PFC_PIN_GROUP(drif2_data0_a), + SH_PFC_PIN_GROUP(drif2_data1_a), + SH_PFC_PIN_GROUP(drif2_ctrl_b), + SH_PFC_PIN_GROUP(drif2_data0_b), + SH_PFC_PIN_GROUP(drif2_data1_b), + SH_PFC_PIN_GROUP(drif3_ctrl_a), + SH_PFC_PIN_GROUP(drif3_data0_a), + SH_PFC_PIN_GROUP(drif3_data1_a), + SH_PFC_PIN_GROUP(drif3_ctrl_b), + SH_PFC_PIN_GROUP(drif3_data0_b), + SH_PFC_PIN_GROUP(drif3_data1_b), } }; @@ -3880,6 +4094,39 @@ static const char * const canfd1_groups[] = { "canfd1_data", }; +static const char * const drif0_groups[] = { + "drif0_ctrl_a", + "drif0_data0_a", + "drif0_data1_a", + "drif0_ctrl_b", + "drif0_data0_b", + "drif0_data1_b", +}; + +static const char * const drif1_groups[] = { + "drif1_ctrl", + "drif1_data0", + "drif1_data1", +}; + +static const char * const drif2_groups[] = { + "drif2_ctrl_a", + "drif2_data0_a", + "drif2_data1_a", + "drif2_ctrl_b", + "drif2_data0_b", + "drif2_data1_b", +}; + +static const char * const drif3_groups[] = { + "drif3_ctrl_a", + "drif3_data0_a", + "drif3_data1_a", + "drif3_ctrl_b", + "drif3_data0_b", + "drif3_data1_b", +}; + static const char * const du_groups[] = { "du_rgb666", "du_rgb888", @@ -4219,7 +4466,7 @@ static const char * const vin5_groups[] = { static const struct { struct sh_pfc_function common[45]; - struct sh_pfc_function automotive[2]; + struct sh_pfc_function automotive[6]; } pinmux_functions = { .common = { SH_PFC_FUNCTION(audio_clk), @@ -4271,6 +4518,10 @@ static const struct { .automotive = { SH_PFC_FUNCTION(canfd0), SH_PFC_FUNCTION(canfd1), + SH_PFC_FUNCTION(drif0), + SH_PFC_FUNCTION(drif1), + SH_PFC_FUNCTION(drif2), + SH_PFC_FUNCTION(drif3), } }; From e814e688413aabd7b0d75e2a8ed1caa472951dec Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 25 Jan 2019 14:11:42 +0100 Subject: [PATCH 1085/4277] i2c: of: Try to find an I2C adapter matching the parent If an I2C adapter doesn't match the provided device tree node, also try matching the parent's device tree node. This allows finding an adapter based on the device node of the parent device that was used to register it. This fixes a regression on Tegra124-based Chromebooks (Nyan) where the eDP controller registers an I2C adapter that is used to read to EDID. After commit 993a815dcbb2 ("dt-bindings: panel: Add missing .txt suffix") this stopped working because the I2C adapter could no longer be found. The approach in this patch fixes the regression without introducing the issues that the above commit solved. Fixes: 17ab7806de0c ("drm: don't link DP aux i2c adapter to the hardware device node") Signed-off-by: Thierry Reding Tested-by: Tristan Bastian Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-of.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index 6cb7ad608bcd..0f01cdba9d2c 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -121,6 +121,17 @@ static int of_dev_node_match(struct device *dev, void *data) return dev->of_node == data; } +static int of_dev_or_parent_node_match(struct device *dev, void *data) +{ + if (dev->of_node == data) + return 1; + + if (dev->parent) + return dev->parent->of_node == data; + + return 0; +} + /* must call put_device() when done with returned i2c_client device */ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) { @@ -145,7 +156,8 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) struct device *dev; struct i2c_adapter *adapter; - dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match); + dev = bus_find_device(&i2c_bus_type, NULL, node, + of_dev_or_parent_node_match); if (!dev) return NULL; From 9f21ef41a5c55c8d6f3c2f8a8773126f698f2a7d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 28 Jan 2019 23:25:33 +0100 Subject: [PATCH 1086/4277] i2c: Add DT bindings for Xscale I2C masters This adds a device tree binding for Intel XScale I2C masters. We define compatible strings for the iop3xx and ixp4xx chip families. Signed-off-by: Linus Walleij Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-xscale.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-xscale.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-xscale.txt b/Documentation/devicetree/bindings/i2c/i2c-xscale.txt new file mode 100644 index 000000000000..dcc8390e0d24 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-xscale.txt @@ -0,0 +1,20 @@ +i2c Controller on XScale platforms such as IOP3xx and IXP4xx + +Required properties: +- compatible : Must be one of + "intel,iop3xx-i2c" + "intel,ixp4xx-i2c"; +- reg +- #address-cells = <1>; +- #size-cells = <0>; + +Optional properties: +- Child nodes conforming to i2c bus binding + +Example: + +i2c@c8011000 { + compatible = "intel,ixp4xx-i2c"; + reg = <0xc8011000 0x18>; + interrupts = <33 IRQ_TYPE_LEVEL_LOW>; +}; From cac28ac2a87c38a026d6d7601b06fe16f41ed751 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 28 Jan 2019 23:25:34 +0100 Subject: [PATCH 1087/4277] i2c: iop3xx: Add device tree probing This adds device tree probing support for the IOP3xx I2C master. Signed-off-by: Linus Walleij Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-iop3xx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 85cbe4b55578..a34cb3848280 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -471,6 +471,7 @@ iop3xx_i2c_probe(struct platform_device *pdev) new_adapter->owner = THIS_MODULE; new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; new_adapter->dev.parent = &pdev->dev; + new_adapter->dev.of_node = pdev->dev.of_node; new_adapter->nr = pdev->id; /* @@ -508,12 +509,19 @@ out: return ret; } +static const struct of_device_id i2c_iop3xx_match[] = { + { .compatible = "intel,iop3xx-i2c", }, + { .compatible = "intel,ixp4xx-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_iop3xx_match); static struct platform_driver iop3xx_i2c_driver = { .probe = iop3xx_i2c_probe, .remove = iop3xx_i2c_remove, .driver = { .name = "IOP3xx-I2C", + .of_match_table = i2c_iop3xx_match, }, }; From 2292822e1576c89191a65c3d0da584d75d3c033f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Jan 2019 13:16:53 +0100 Subject: [PATCH 1088/4277] i2c: algo-bit: include main i2c header We are using symbols from it, so we should include it directly. Found after sorting includes in a driver. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- include/linux/i2c-algo-bit.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/i2c-algo-bit.h b/include/linux/i2c-algo-bit.h index 63904ba6887e..d64cebc6e65a 100644 --- a/include/linux/i2c-algo-bit.h +++ b/include/linux/i2c-algo-bit.h @@ -25,6 +25,8 @@ #ifndef _LINUX_I2C_ALGO_BIT_H #define _LINUX_I2C_ALGO_BIT_H +#include + /* --- Defines for bit-adapters --------------------------------------- */ /* * This struct contains the hw-dependent functions of bit-style adapters to From 738ac0679b969776a638daf2cfb5011049d467da Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Jan 2019 13:16:54 +0100 Subject: [PATCH 1089/4277] i2c: algo-bit: convert to SPDX header And use kernel style for the remaining comments in the header. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/algos/i2c-algo-bit.c | 25 ++++++++----------------- include/linux/i2c-algo-bit.h | 31 ++++++++----------------------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index c33dcfb87993..5e5990a83da5 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -1,21 +1,12 @@ -/* ------------------------------------------------------------------------- - * i2c-algo-bit.c i2c driver algorithms for bit-shift adapters - * ------------------------------------------------------------------------- +// SPDX-License-Identifier: GPL-2.0+ +/* + * i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters + * * Copyright (C) 1995-2000 Simon G. Vogl - - 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. - * ------------------------------------------------------------------------- */ - -/* With some changes from Frodo Looijaard , Kyösti Mälkki - and Jean Delvare */ + * + * With some changes from Frodo Looijaard , Kyösti Mälkki + * and Jean Delvare + */ #include #include diff --git a/include/linux/i2c-algo-bit.h b/include/linux/i2c-algo-bit.h index d64cebc6e65a..69045df78e2d 100644 --- a/include/linux/i2c-algo-bit.h +++ b/include/linux/i2c-algo-bit.h @@ -1,26 +1,11 @@ -/* ------------------------------------------------------------------------- */ -/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */ -/* ------------------------------------------------------------------------- */ -/* Copyright (C) 1995-99 Simon G. Vogl - - 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. */ -/* ------------------------------------------------------------------------- */ - -/* With some changes from Kyösti Mälkki and even - Frodo Looijaard */ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * i2c-algo-bit.h: i2c driver algorithms for bit-shift adapters + * + * Copyright (C) 1995-99 Simon G. Vogl + * With some changes from Kyösti Mälkki and even + * Frodo Looijaard + */ #ifndef _LINUX_I2C_ALGO_BIT_H #define _LINUX_I2C_ALGO_BIT_H From 2f8aa465b918fee7d148617d451cffeee1fdd934 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Jan 2019 13:16:55 +0100 Subject: [PATCH 1090/4277] i2c: gpio: sort includes Less risk for duplications when adding new ones. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index c008d209f0b8..33f9d7ed1223 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -9,15 +9,15 @@ */ #include #include -#include +#include #include -#include +#include #include #include -#include -#include -#include #include +#include +#include +#include struct i2c_gpio_private_data { struct gpio_desc *sda; From 70ae5669b5b3f357a5c1d64152508506624a5c5d Mon Sep 17 00:00:00 2001 From: qii wang Date: Mon, 21 Jan 2019 15:59:29 +0800 Subject: [PATCH 1091/4277] dt-bindings: i2c: Add Mediatek MT7629 i2c binding Add MT7629 i2c binding to binding file. Signed-off-by: qii wang Reviewed-by: Rob Herring Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-mtk.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mtk.txt b/Documentation/devicetree/bindings/i2c/i2c-mtk.txt index e199695b1c96..ee4c32454198 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mtk.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-mtk.txt @@ -10,6 +10,7 @@ Required properties: "mediatek,mt6589-i2c": for MediaTek MT6589 "mediatek,mt7622-i2c": for MediaTek MT7622 "mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623 + "mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629 "mediatek,mt8173-i2c": for MediaTek MT8173 - reg: physical base address of the controller and dma base, length of memory mapped region. From 93caa0dab77cb4bd6c8efd82042f5a6b73d2b87e Mon Sep 17 00:00:00 2001 From: qii wang Date: Mon, 21 Jan 2019 15:59:30 +0800 Subject: [PATCH 1092/4277] i2c: mediatek: speeds is replaced by macros definitions Different speeds have been defined by macros, so we use macros definitions. Signed-off-by: qii wang Reviewed-by: Nicolas Boichat Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index a74ef76705e0..7396449703a1 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -456,7 +456,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, control_reg = readw(i2c->base + OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); - if ((i2c->speed_hz > 400000) || (left_num >= 1)) + if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1)) control_reg |= I2C_CONTROL_RS; if (i2c->op == I2C_MASTER_WRRD) @@ -465,7 +465,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, writew(control_reg, i2c->base + OFFSET_CONTROL); /* set start condition */ - if (i2c->speed_hz <= 100000) + if (i2c->speed_hz <= I2C_DEFAULT_SPEED) writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF); else writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF); From 0056a54b335335f0231a80ecf81d03095037437e Mon Sep 17 00:00:00 2001 From: qii wang Date: Mon, 21 Jan 2019 15:59:31 +0800 Subject: [PATCH 1093/4277] i2c: mediatek: remove completion_done() Completion_done() is useless when we don't use its return value, so we remove it. Signed-off-by: qii wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 7396449703a1..660de1ee68ed 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -642,8 +642,6 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, return -ETIMEDOUT; } - completion_done(&i2c->msg_complete); - if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) { dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr); mtk_i2c_init_hw(i2c); From c6324fadeac80da60ab99d22cfe19873a31dd30d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 21 Jan 2019 15:16:56 +0100 Subject: [PATCH 1094/4277] i2c: gpio: fault-injector: sort debugfs files alphabetically There is no value in chronological sorting. Make it easier to follow for humans. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 33f9d7ed1223..362d3b9dd3b6 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -181,12 +181,12 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) if (!priv->debug_dir) return; - debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); - debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, priv, &fops_incomplete_addr_phase); debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, priv, &fops_incomplete_write_byte); + debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); + debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); } static void i2c_gpio_fault_injector_exit(struct platform_device *pdev) From 05bd07280d5f70920c3619998b3a79742e4cde30 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 21 Jan 2019 15:16:57 +0100 Subject: [PATCH 1095/4277] i2c: gpio: fault-injector: better SPHINX style in docs We don't use SPHINX currently in I2C documentation; but preparing the I2C fault injector docs already is a good idea since it makes it easier to read. Especially as new stuff is going to be added soon. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- Documentation/i2c/gpio-fault-injection | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/i2c/gpio-fault-injection b/Documentation/i2c/gpio-fault-injection index a4ce62090fd5..1a44e3edc0c4 100644 --- a/Documentation/i2c/gpio-fault-injection +++ b/Documentation/i2c/gpio-fault-injection @@ -1,3 +1,4 @@ +========================= Linux I2C fault injection ========================= @@ -13,6 +14,9 @@ mounted at /sys/kernel/debug. There will be a separate subdirectory per GPIO driven I2C bus. Each subdirectory will contain files to trigger the fault injection. They will be described now along with their intended use-cases. +Wire states +=========== + "scl" ----- @@ -34,10 +38,10 @@ I2C specification version 4, section 3.1.16) using the helpers of the Linux I2C core (see 'struct bus_recovery_info'). However, the bus recovery will not succeed because SDA is still pinned low until you manually release it again with "echo 1 > sda". A test with an automatic release can be done with the -following class of fault injectors. +"incomplete transfers" class of fault injectors. -Introduction to incomplete transfers ------------------------------------- +Incomplete transfers +==================== The following fault injectors create situations where SDA will be held low by a device. Bus recovery should be able to fix these situations. But please note: From 8d43d57036679a8635952c9ef54989a7b48e8c00 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 10 Dec 2018 11:15:16 +0100 Subject: [PATCH 1096/4277] KVM: s390: clarify kvm related kernel message As suggested by our ID dept. here are some kernel message updates. Signed-off-by: Michael Mueller Reviewed-by: Christian Borntraeger Signed-off-by: Christian Borntraeger --- arch/s390/kvm/kvm-s390.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 7f4bc58a53b9..11b4be3dad15 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -432,7 +432,7 @@ int kvm_arch_init(void *opaque) /* Register floating interrupt controller interface. */ rc = kvm_register_device_ops(&kvm_flic_ops, KVM_DEV_TYPE_FLIC); if (rc) { - pr_err("Failed to register FLIC rc=%d\n", rc); + pr_err("A FLIC registration call failed with rc=%d\n", rc); goto out_debug_unreg; } return 0; @@ -4293,12 +4293,12 @@ static int __init kvm_s390_init(void) int i; if (!sclp.has_sief2) { - pr_info("SIE not available\n"); + pr_info("SIE is not available\n"); return -ENODEV; } if (nested && hpage) { - pr_info("nested (vSIE) and hpage (huge page backing) can currently not be activated concurrently"); + pr_info("A KVM host that supports nesting cannot back its KVM guests with huge pages\n"); return -EINVAL; } From b7d455712927cc8042f97fffba5d51052856ddaf Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:32 +0100 Subject: [PATCH 1097/4277] KVM: s390: drop obsolete else path The explicit else path specified in set_intercept_indicators_io is not required as the function returns in case the first branch is taken anyway. Signed-off-by: Michael Mueller Reviewed-by: Cornelia Huck Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-2-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/interrupt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index fcb55b02990e..19d556512452 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -345,7 +345,7 @@ static void set_intercept_indicators_io(struct kvm_vcpu *vcpu) { if (!(pending_irqs_no_gisa(vcpu) & IRQ_PEND_IO_MASK)) return; - else if (psw_ioint_disabled(vcpu)) + if (psw_ioint_disabled(vcpu)) kvm_s390_set_cpuflags(vcpu, CPUSTAT_IO_INT); else vcpu->arch.sie_block->lctl |= LCTL_CR6; From 689bdf9e9c337121d948d48605de0659c088e6bb Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:33 +0100 Subject: [PATCH 1098/4277] KVM: s390: make bitmap declaration consistent Use a consistent bitmap declaration throughout the code. Signed-off-by: Michael Mueller Reviewed-by: Cornelia Huck Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-3-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 2 +- arch/s390/kvm/interrupt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index d5d24889c3bc..3cba08f73dc6 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -591,7 +591,7 @@ struct kvm_s390_float_interrupt { struct kvm_s390_mchk_info mchk; struct kvm_s390_ext_info srv_signal; int next_rr_cpu; - unsigned long idle_mask[BITS_TO_LONGS(KVM_MAX_VCPUS)]; + DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS); struct mutex ais_lock; u8 simm; u8 nimm; diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 19d556512452..167a3068ef84 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2831,7 +2831,7 @@ static void store_local_irq(struct kvm_s390_local_interrupt *li, int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) { int scn; - unsigned long sigp_emerg_pending[BITS_TO_LONGS(KVM_MAX_VCPUS)]; + DECLARE_BITMAP(sigp_emerg_pending, KVM_MAX_VCPUS); struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; unsigned long pending_irqs; struct kvm_s390_irq irq; From 246b72183b3538907d5c1e6d383c6956385d068d Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:34 +0100 Subject: [PATCH 1099/4277] KVM: s390: move bitmap idle_mask into arch struct top level The vcpu idle_mask state is used by but not specific to the emulated floating interruptions. The state is relevant to gisa related interruptions as well. Signed-off-by: Michael Mueller Reviewed-by: Pierre Morel Reviewed-by: Cornelia Huck Acked-by: Halil Pasic Message-Id: <20190131085247.13826-4-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 2 +- arch/s390/kvm/interrupt.c | 11 +++++------ arch/s390/kvm/kvm-s390.h | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 3cba08f73dc6..c259a67c4298 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -591,7 +591,6 @@ struct kvm_s390_float_interrupt { struct kvm_s390_mchk_info mchk; struct kvm_s390_ext_info srv_signal; int next_rr_cpu; - DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS); struct mutex ais_lock; u8 simm; u8 nimm; @@ -838,6 +837,7 @@ struct kvm_arch{ /* subset of available cpu features enabled by user space */ DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS); struct kvm_s390_gisa *gisa; + DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS); }; #define KVM_HVA_ERR_BAD (-1UL) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 167a3068ef84..2a3eb9f076c3 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -318,13 +318,13 @@ static unsigned long deliverable_irqs(struct kvm_vcpu *vcpu) static void __set_cpu_idle(struct kvm_vcpu *vcpu) { kvm_s390_set_cpuflags(vcpu, CPUSTAT_WAIT); - set_bit(vcpu->vcpu_id, vcpu->kvm->arch.float_int.idle_mask); + set_bit(vcpu->vcpu_id, vcpu->kvm->arch.idle_mask); } static void __unset_cpu_idle(struct kvm_vcpu *vcpu) { kvm_s390_clear_cpuflags(vcpu, CPUSTAT_WAIT); - clear_bit(vcpu->vcpu_id, vcpu->kvm->arch.float_int.idle_mask); + clear_bit(vcpu->vcpu_id, vcpu->kvm->arch.idle_mask); } static void __reset_intercept_indicators(struct kvm_vcpu *vcpu) @@ -1726,7 +1726,6 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) */ static void __floating_irq_kick(struct kvm *kvm, u64 type) { - struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; struct kvm_vcpu *dst_vcpu; int sigcpu, online_vcpus, nr_tries = 0; @@ -1735,11 +1734,11 @@ static void __floating_irq_kick(struct kvm *kvm, u64 type) return; /* find idle VCPUs first, then round robin */ - sigcpu = find_first_bit(fi->idle_mask, online_vcpus); + sigcpu = find_first_bit(kvm->arch.idle_mask, online_vcpus); if (sigcpu == online_vcpus) { do { - sigcpu = fi->next_rr_cpu; - fi->next_rr_cpu = (fi->next_rr_cpu + 1) % online_vcpus; + sigcpu = kvm->arch.float_int.next_rr_cpu++; + kvm->arch.float_int.next_rr_cpu %= online_vcpus; /* avoid endless loops if all vcpus are stopped */ if (nr_tries++ >= online_vcpus) return; diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 1f6e36cdce0d..72ef87799593 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -67,7 +67,7 @@ static inline int is_vcpu_stopped(struct kvm_vcpu *vcpu) static inline int is_vcpu_idle(struct kvm_vcpu *vcpu) { - return test_bit(vcpu->vcpu_id, vcpu->kvm->arch.float_int.idle_mask); + return test_bit(vcpu->vcpu_id, vcpu->kvm->arch.idle_mask); } static inline int kvm_is_ucontrol(struct kvm *kvm) From 672128bfee082d37e7eda630f98c46aacda4963c Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:35 +0100 Subject: [PATCH 1100/4277] KVM: s390: coding style kvm_s390_gisa_init/clear() The change helps to reduce line length and increases code readability. Signed-off-by: Michael Mueller Reviewed-by: Cornelia Huck Reviewed-by: Pierre Morel Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-5-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/interrupt.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 2a3eb9f076c3..005dbe7252e7 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2885,20 +2885,20 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) void kvm_s390_gisa_clear(struct kvm *kvm) { - if (kvm->arch.gisa) { - memset(kvm->arch.gisa, 0, sizeof(struct kvm_s390_gisa)); - kvm->arch.gisa->next_alert = (u32)(u64)kvm->arch.gisa; - VM_EVENT(kvm, 3, "gisa 0x%pK cleared", kvm->arch.gisa); - } + if (!kvm->arch.gisa) + return; + memset(kvm->arch.gisa, 0, sizeof(struct kvm_s390_gisa)); + kvm->arch.gisa->next_alert = (u32)(u64)kvm->arch.gisa; + VM_EVENT(kvm, 3, "gisa 0x%pK cleared", kvm->arch.gisa); } void kvm_s390_gisa_init(struct kvm *kvm) { - if (css_general_characteristics.aiv) { - kvm->arch.gisa = &kvm->arch.sie_page2->gisa; - VM_EVENT(kvm, 3, "gisa 0x%pK initialized", kvm->arch.gisa); - kvm_s390_gisa_clear(kvm); - } + if (!css_general_characteristics.aiv) + return; + kvm->arch.gisa = &kvm->arch.sie_page2->gisa; + kvm_s390_gisa_clear(kvm); + VM_EVENT(kvm, 3, "gisa 0x%pK initialized", kvm->arch.gisa); } void kvm_s390_gisa_destroy(struct kvm *kvm) From 96723d323a080d98a2b330ef2d7aa020bf589d9e Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:36 +0100 Subject: [PATCH 1101/4277] KVM: s390: use pending_irqs_no_gisa() where appropriate Interruption types that are not represented in GISA shall use pending_irqs_no_gisa() to test pending interruptions. Signed-off-by: Michael Mueller Reviewed-by: Cornelia Huck Reviewed-by: Pierre Morel Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-6-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/interrupt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 005dbe7252e7..cb48736867ed 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -353,7 +353,7 @@ static void set_intercept_indicators_io(struct kvm_vcpu *vcpu) static void set_intercept_indicators_ext(struct kvm_vcpu *vcpu) { - if (!(pending_irqs(vcpu) & IRQ_PEND_EXT_MASK)) + if (!(pending_irqs_no_gisa(vcpu) & IRQ_PEND_EXT_MASK)) return; if (psw_extint_disabled(vcpu)) kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT); @@ -363,7 +363,7 @@ static void set_intercept_indicators_ext(struct kvm_vcpu *vcpu) static void set_intercept_indicators_mchk(struct kvm_vcpu *vcpu) { - if (!(pending_irqs(vcpu) & IRQ_PEND_MCHK_MASK)) + if (!(pending_irqs_no_gisa(vcpu) & IRQ_PEND_MCHK_MASK)) return; if (psw_mchk_disabled(vcpu)) vcpu->arch.sie_block->ictl |= ICTL_LPSW; From bb2fb8cdcf2d9f1845ffaa077013b45e14642019 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:37 +0100 Subject: [PATCH 1102/4277] KVM: s390: remove kvm_s390_ from gisa static inline functions This will shorten the length of code lines. All GISA related static inline functions are local to interrupt.c. Signed-off-by: Michael Mueller Reviewed-by: Cornelia Huck Reviewed-by: Pierre Morel Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-7-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/interrupt.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index cb48736867ed..942cc7d33766 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -217,22 +217,22 @@ static inline u8 int_word_to_isc(u32 int_word) */ #define IPM_BIT_OFFSET (offsetof(struct kvm_s390_gisa, ipm) * BITS_PER_BYTE) -static inline void kvm_s390_gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) +static inline void gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) { set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); } -static inline u8 kvm_s390_gisa_get_ipm(struct kvm_s390_gisa *gisa) +static inline u8 gisa_get_ipm(struct kvm_s390_gisa *gisa) { return READ_ONCE(gisa->ipm); } -static inline void kvm_s390_gisa_clear_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) +static inline void gisa_clear_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) { clear_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); } -static inline int kvm_s390_gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) +static inline int gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) { return test_and_clear_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); } @@ -246,7 +246,7 @@ static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu) static inline unsigned long pending_irqs(struct kvm_vcpu *vcpu) { return pending_irqs_no_gisa(vcpu) | - kvm_s390_gisa_get_ipm(vcpu->kvm->arch.gisa) << IRQ_PEND_IO_ISC_7; + gisa_get_ipm(vcpu->kvm->arch.gisa) << IRQ_PEND_IO_ISC_7; } static inline int isc_to_irq_type(unsigned long isc) @@ -999,7 +999,7 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu, } if (vcpu->kvm->arch.gisa && - kvm_s390_gisa_tac_ipm_gisc(vcpu->kvm->arch.gisa, isc)) { + gisa_tac_ipm_gisc(vcpu->kvm->arch.gisa, isc)) { /* * in case an adapter interrupt was not delivered * in SIE context KVM will handle the delivery @@ -1541,10 +1541,10 @@ static int get_top_gisa_isc(struct kvm *kvm, u64 isc_mask, u32 schid) if (!kvm->arch.gisa) goto out; - active_mask = (isc_mask & kvm_s390_gisa_get_ipm(kvm->arch.gisa) << 24) << 32; + active_mask = (isc_mask & gisa_get_ipm(kvm->arch.gisa) << 24) << 32; while (active_mask) { isc = __fls(active_mask) ^ (BITS_PER_LONG - 1); - if (kvm_s390_gisa_tac_ipm_gisc(kvm->arch.gisa, isc)) + if (gisa_tac_ipm_gisc(kvm->arch.gisa, isc)) return isc; clear_bit_inv(isc, &active_mask); } @@ -1584,7 +1584,7 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, /* both types of interrupts present */ if (int_word_to_isc(inti->io.io_int_word) <= isc) { /* classical IO int with higher priority */ - kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc); + gisa_set_ipm_gisc(kvm->arch.gisa, isc); goto out; } gisa_out: @@ -1596,7 +1596,7 @@ gisa_out: kvm_s390_reinject_io_int(kvm, inti); inti = tmp_inti; } else - kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc); + gisa_set_ipm_gisc(kvm->arch.gisa, isc); out: return inti; } @@ -1694,7 +1694,7 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) if (kvm->arch.gisa && inti->type & KVM_S390_INT_IO_AI_MASK) { VM_EVENT(kvm, 4, "%s isc %1u", "inject: I/O (AI/gisa)", isc); - kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc); + gisa_set_ipm_gisc(kvm->arch.gisa, isc); kfree(inti); return 0; } @@ -2025,15 +2025,14 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) max_irqs = len / sizeof(struct kvm_s390_irq); - if (kvm->arch.gisa && - kvm_s390_gisa_get_ipm(kvm->arch.gisa)) { + if (kvm->arch.gisa && gisa_get_ipm(kvm->arch.gisa)) { for (i = 0; i <= MAX_ISC; i++) { if (n == max_irqs) { /* signal userspace to try again */ ret = -ENOMEM; goto out_nolock; } - if (kvm_s390_gisa_tac_ipm_gisc(kvm->arch.gisa, i)) { + if (gisa_tac_ipm_gisc(kvm->arch.gisa, i)) { irq = (struct kvm_s390_irq *) &buf[n]; irq->type = KVM_S390_INT_IO(1, 0, 0, 0); irq->u.io.io_int_word = isc_to_int_word(i); From 982cff425959901dfb4df7433622e5c0510f9d37 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:38 +0100 Subject: [PATCH 1103/4277] KVM: s390: introduce struct kvm_s390_gisa_interrupt Use this struct analog to the kvm interruption structs for kvm emulated floating and local interruptions. GIB handling will add further fields to this structure as required. Signed-off-by: Michael Mueller Reviewed-by: Cornelia Huck Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-8-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 6 +++- arch/s390/kvm/interrupt.c | 52 ++++++++++++++++++-------------- arch/s390/kvm/kvm-s390.c | 2 +- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index c259a67c4298..0941571d34eb 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -803,6 +803,10 @@ struct kvm_s390_vsie { struct page *pages[KVM_MAX_VCPUS]; }; +struct kvm_s390_gisa_interrupt { + struct kvm_s390_gisa *origin; +}; + struct kvm_arch{ void *sca; int use_esca; @@ -836,8 +840,8 @@ struct kvm_arch{ atomic64_t cmma_dirty_pages; /* subset of available cpu features enabled by user space */ DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS); - struct kvm_s390_gisa *gisa; DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS); + struct kvm_s390_gisa_interrupt gisa_int; }; #define KVM_HVA_ERR_BAD (-1UL) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 942cc7d33766..ee91d1de0036 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -246,7 +246,8 @@ static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu) static inline unsigned long pending_irqs(struct kvm_vcpu *vcpu) { return pending_irqs_no_gisa(vcpu) | - gisa_get_ipm(vcpu->kvm->arch.gisa) << IRQ_PEND_IO_ISC_7; + gisa_get_ipm(vcpu->kvm->arch.gisa_int.origin) << + IRQ_PEND_IO_ISC_7; } static inline int isc_to_irq_type(unsigned long isc) @@ -956,6 +957,7 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu, { struct list_head *isc_list; struct kvm_s390_float_interrupt *fi; + struct kvm_s390_gisa_interrupt *gi = &vcpu->kvm->arch.gisa_int; struct kvm_s390_interrupt_info *inti = NULL; struct kvm_s390_io_info io; u32 isc; @@ -998,8 +1000,7 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu, goto out; } - if (vcpu->kvm->arch.gisa && - gisa_tac_ipm_gisc(vcpu->kvm->arch.gisa, isc)) { + if (gi->origin && gisa_tac_ipm_gisc(gi->origin, isc)) { /* * in case an adapter interrupt was not delivered * in SIE context KVM will handle the delivery @@ -1533,18 +1534,19 @@ static struct kvm_s390_interrupt_info *get_top_io_int(struct kvm *kvm, static int get_top_gisa_isc(struct kvm *kvm, u64 isc_mask, u32 schid) { + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; unsigned long active_mask; int isc; if (schid) goto out; - if (!kvm->arch.gisa) + if (!gi->origin) goto out; - active_mask = (isc_mask & gisa_get_ipm(kvm->arch.gisa) << 24) << 32; + active_mask = (isc_mask & gisa_get_ipm(gi->origin) << 24) << 32; while (active_mask) { isc = __fls(active_mask) ^ (BITS_PER_LONG - 1); - if (gisa_tac_ipm_gisc(kvm->arch.gisa, isc)) + if (gisa_tac_ipm_gisc(gi->origin, isc)) return isc; clear_bit_inv(isc, &active_mask); } @@ -1567,6 +1569,7 @@ out: struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, u64 isc_mask, u32 schid) { + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; struct kvm_s390_interrupt_info *inti, *tmp_inti; int isc; @@ -1584,7 +1587,7 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, /* both types of interrupts present */ if (int_word_to_isc(inti->io.io_int_word) <= isc) { /* classical IO int with higher priority */ - gisa_set_ipm_gisc(kvm->arch.gisa, isc); + gisa_set_ipm_gisc(gi->origin, isc); goto out; } gisa_out: @@ -1596,7 +1599,7 @@ gisa_out: kvm_s390_reinject_io_int(kvm, inti); inti = tmp_inti; } else - gisa_set_ipm_gisc(kvm->arch.gisa, isc); + gisa_set_ipm_gisc(gi->origin, isc); out: return inti; } @@ -1685,6 +1688,7 @@ static int __inject_float_mchk(struct kvm *kvm, static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) { + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; struct kvm_s390_float_interrupt *fi; struct list_head *list; int isc; @@ -1692,9 +1696,9 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) kvm->stat.inject_io++; isc = int_word_to_isc(inti->io.io_int_word); - if (kvm->arch.gisa && inti->type & KVM_S390_INT_IO_AI_MASK) { + if (gi->origin && inti->type & KVM_S390_INT_IO_AI_MASK) { VM_EVENT(kvm, 4, "%s isc %1u", "inject: I/O (AI/gisa)", isc); - gisa_set_ipm_gisc(kvm->arch.gisa, isc); + gisa_set_ipm_gisc(gi->origin, isc); kfree(inti); return 0; } @@ -1752,7 +1756,8 @@ static void __floating_irq_kick(struct kvm *kvm, u64 type) kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_STOP_INT); break; case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: - if (!(type & KVM_S390_INT_IO_AI_MASK && kvm->arch.gisa)) + if (!(type & KVM_S390_INT_IO_AI_MASK && + kvm->arch.gisa_int.origin)) kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_IO_INT); break; default: @@ -2002,6 +2007,7 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm) static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) { + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; struct kvm_s390_interrupt_info *inti; struct kvm_s390_float_interrupt *fi; struct kvm_s390_irq *buf; @@ -2025,14 +2031,14 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) max_irqs = len / sizeof(struct kvm_s390_irq); - if (kvm->arch.gisa && gisa_get_ipm(kvm->arch.gisa)) { + if (gi->origin && gisa_get_ipm(gi->origin)) { for (i = 0; i <= MAX_ISC; i++) { if (n == max_irqs) { /* signal userspace to try again */ ret = -ENOMEM; goto out_nolock; } - if (gisa_tac_ipm_gisc(kvm->arch.gisa, i)) { + if (gisa_tac_ipm_gisc(gi->origin, i)) { irq = (struct kvm_s390_irq *) &buf[n]; irq->type = KVM_S390_INT_IO(1, 0, 0, 0); irq->u.io.io_int_word = isc_to_int_word(i); @@ -2884,25 +2890,27 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) void kvm_s390_gisa_clear(struct kvm *kvm) { - if (!kvm->arch.gisa) + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; + + if (!gi->origin) return; - memset(kvm->arch.gisa, 0, sizeof(struct kvm_s390_gisa)); - kvm->arch.gisa->next_alert = (u32)(u64)kvm->arch.gisa; - VM_EVENT(kvm, 3, "gisa 0x%pK cleared", kvm->arch.gisa); + memset(gi->origin, 0, sizeof(struct kvm_s390_gisa)); + gi->origin->next_alert = (u32)(u64)gi->origin; + VM_EVENT(kvm, 3, "gisa 0x%pK cleared", gi->origin); } void kvm_s390_gisa_init(struct kvm *kvm) { + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; + if (!css_general_characteristics.aiv) return; - kvm->arch.gisa = &kvm->arch.sie_page2->gisa; + gi->origin = &kvm->arch.sie_page2->gisa; kvm_s390_gisa_clear(kvm); - VM_EVENT(kvm, 3, "gisa 0x%pK initialized", kvm->arch.gisa); + VM_EVENT(kvm, 3, "gisa 0x%pK initialized", gi->origin); } void kvm_s390_gisa_destroy(struct kvm *kvm) { - if (!kvm->arch.gisa) - return; - kvm->arch.gisa = NULL; + kvm->arch.gisa_int.origin = NULL; } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 11b4be3dad15..5eaffb3e1738 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2812,7 +2812,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, vcpu->arch.sie_block->icpua = id; spin_lock_init(&vcpu->arch.local_int.lock); - vcpu->arch.sie_block->gd = (u32)(u64)kvm->arch.gisa; + vcpu->arch.sie_block->gd = (u32)(u64)kvm->arch.gisa_int.origin; if (vcpu->arch.sie_block->gd && sclp.has_gisaf) vcpu->arch.sie_block->gd |= GISA_FORMAT1; seqcount_init(&vcpu->arch.cputm_seqcount); From 3dec19221788ec29fff91f3a6bebfc7b8132b0a1 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:39 +0100 Subject: [PATCH 1104/4277] s390/cio: add function chsc_sgib() This patch implements the Set Guest Information Block operation to request association or disassociation of a Guest Information Block (GIB) with the Adapter Interruption Facility. The operation is required to receive GIB alert interrupts for guest adapters in conjunction with AIV and GISA. Signed-off-by: Michael Mueller Reviewed-by: Sebastian Ott Reviewed-by: Pierre Morel Reviewed-by: Christian Borntraeger Acked-by: Halil Pasic Acked-by: Janosch Frank Acked-by: Cornelia Huck Message-Id: <20190131085247.13826-9-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/cio.h | 1 + drivers/s390/cio/chsc.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/s390/cio/chsc.h | 1 + 3 files changed, 39 insertions(+) diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h index 225667652069..1727180e8ca1 100644 --- a/arch/s390/include/asm/cio.h +++ b/arch/s390/include/asm/cio.h @@ -331,5 +331,6 @@ extern void css_schedule_reprobe(void); /* Function from drivers/s390/cio/chsc.c */ int chsc_sstpc(void *page, unsigned int op, u16 ctrl, u64 *clock_delta); int chsc_sstpi(void *page, void *result, size_t size); +int chsc_sgib(u32 origin); #endif diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index a0baee25134c..4159c63a5fd2 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1382,3 +1382,40 @@ int chsc_pnso_brinfo(struct subchannel_id schid, return chsc_error_from_response(brinfo_area->response.code); } EXPORT_SYMBOL_GPL(chsc_pnso_brinfo); + +int chsc_sgib(u32 origin) +{ + struct { + struct chsc_header request; + u16 op; + u8 reserved01[2]; + u8 reserved02:4; + u8 fmt:4; + u8 reserved03[7]; + /* operation data area begin */ + u8 reserved04[4]; + u32 gib_origin; + u8 reserved05[10]; + u8 aix; + u8 reserved06[4029]; + struct chsc_header response; + u8 reserved07[4]; + } *sgib_area; + int ret; + + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + sgib_area = chsc_page; + sgib_area->request.length = 0x0fe0; + sgib_area->request.code = 0x0021; + sgib_area->op = 0x1; + sgib_area->gib_origin = origin; + + ret = chsc(sgib_area); + if (ret == 0) + ret = chsc_error_from_response(sgib_area->response.code); + spin_unlock_irq(&chsc_page_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(chsc_sgib); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 78aba8d94eec..e57d68e325a3 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -164,6 +164,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp); int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd); int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, u64 summary_indicator_addr, u64 subchannel_indicator_addr); +int chsc_sgib(u32 origin); int chsc_error_from_response(int response); int chsc_siosl(struct subchannel_id schid); From 1282c21eb3dac324b8531bf2e0e0fbd9d5b6516b Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:40 +0100 Subject: [PATCH 1105/4277] KVM: s390: add the GIB and its related life-cyle functions The Guest Information Block (GIB) links the GISA of all guests that have adapter interrupts pending. These interrupts cannot be delivered because all vcpus of these guests are currently in WAIT state or have masked the respective Interruption Sub Class (ISC). If enabled, a GIB alert is issued on the host to schedule these guests to run suitable vcpus to consume the pending interruptions. This mechanism allows to process adapter interrupts for currently not running guests. The GIB is created during host initialization and associated with the Adapter Interruption Facility in case an Adapter Interruption Virtualization Facility is available. The GIB initialization and thus the activation of the related code will be done in an upcoming patch of this series. Signed-off-by: Michael Mueller Reviewed-by: Janosch Frank Reviewed-by: Christian Borntraeger Reviewed-by: Cornelia Huck Reviewed-by: Pierre Morel Acked-by: Halil Pasic Message-Id: <20190131085247.13826-10-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 9 +++++++ arch/s390/kvm/interrupt.c | 43 ++++++++++++++++++++++++++++++++ arch/s390/kvm/kvm-s390.c | 1 + arch/s390/kvm/kvm-s390.h | 2 ++ 4 files changed, 55 insertions(+) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 0941571d34eb..397af0d33539 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -784,6 +784,15 @@ struct kvm_s390_gisa { }; }; +struct kvm_s390_gib { + u32 alert_list_origin; + u32 reserved01; + u8:5; + u8 nisc:3; + u8 reserved03[3]; + u32 reserved04[5]; +}; + /* * sie_page2 has to be allocated as DMA because fac_list, crycb and * gisa need 31bit addresses in the sie control block. diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index ee91d1de0036..5efcd9e2cf8f 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -7,6 +7,9 @@ * Author(s): Carsten Otte */ +#define KMSG_COMPONENT "kvm-s390" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include @@ -31,6 +34,8 @@ #define PFAULT_DONE 0x0680 #define VIRTIO_PARAM 0x0d00 +static struct kvm_s390_gib *gib; + /* handle external calls via sigp interpretation facility */ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id) { @@ -2914,3 +2919,41 @@ void kvm_s390_gisa_destroy(struct kvm *kvm) { kvm->arch.gisa_int.origin = NULL; } + +void kvm_s390_gib_destroy(void) +{ + if (!gib) + return; + chsc_sgib(0); + free_page((unsigned long)gib); + gib = NULL; +} + +int kvm_s390_gib_init(u8 nisc) +{ + int rc = 0; + + if (!css_general_characteristics.aiv) { + KVM_EVENT(3, "%s", "gib not initialized, no AIV facility"); + goto out; + } + + gib = (struct kvm_s390_gib *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!gib) { + rc = -ENOMEM; + goto out; + } + + gib->nisc = nisc; + if (chsc_sgib((u32)(u64)gib)) { + pr_err("Associating the GIB with the AIV facility failed\n"); + free_page((unsigned long)gib); + gib = NULL; + rc = -EIO; + goto out; + } + + KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc); +out: + return rc; +} diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 5eaffb3e1738..ede89172b8f4 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -444,6 +444,7 @@ out_debug_unreg: void kvm_arch_exit(void) { + kvm_s390_gib_destroy(); debug_unregister(kvm_s390_dbf); } diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 72ef87799593..6d9448dbd052 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -381,6 +381,8 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, void kvm_s390_gisa_init(struct kvm *kvm); void kvm_s390_gisa_clear(struct kvm *kvm); void kvm_s390_gisa_destroy(struct kvm *kvm); +int kvm_s390_gib_init(u8 nisc); +void kvm_s390_gib_destroy(void); /* implemented in guestdbg.c */ void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu); From 25c84dbaec6a5079ab64cf8b633ec811f8e43fdd Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:41 +0100 Subject: [PATCH 1106/4277] KVM: s390: add kvm reference to struct sie_page2 Adding the kvm reference to struct sie_page2 will allow to determine the kvm a given gisa belongs to: container_of(gisa, struct sie_page2, gisa)->kvm This functionality will be required to process a gisa in gib alert interruption context. Signed-off-by: Michael Mueller Reviewed-by: Pierre Morel Reviewed-by: Cornelia Huck Reviewed-by: David Hildenbrand Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-11-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 3 ++- arch/s390/kvm/kvm-s390.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 397af0d33539..7077762ab460 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -801,7 +801,8 @@ struct sie_page2 { __u64 fac_list[S390_ARCH_FAC_LIST_SIZE_U64]; /* 0x0000 */ struct kvm_s390_crypto_cb crycb; /* 0x0800 */ struct kvm_s390_gisa gisa; /* 0x0900 */ - u8 reserved920[0x1000 - 0x920]; /* 0x0920 */ + struct kvm *kvm; /* 0x0920 */ + u8 reserved928[0x1000 - 0x928]; /* 0x0928 */ }; struct kvm_s390_vsie { diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index ede89172b8f4..0de062e989e2 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2210,6 +2210,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) if (!kvm->arch.sie_page2) goto out_err; + kvm->arch.sie_page2->kvm = kvm; kvm->arch.model.fac_list = kvm->arch.sie_page2->fac_list; for (i = 0; i < kvm_s390_fac_size(); i++) { From 6cff2e1046015f2729f6cb68be1c999c4a9b259f Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:42 +0100 Subject: [PATCH 1107/4277] KVM: s390: add functions to (un)register GISC with GISA Add the Interruption Alert Mask (IAM) to the architecture specific kvm struct. This mask in the GISA is used to define for which ISC a GIB alert will be issued. The functions kvm_s390_gisc_register() and kvm_s390_gisc_unregister() are used to (un)register a GISC (guest ISC) with a virtual machine and its GISA. Upon successful completion, kvm_s390_gisc_register() returns the ISC to be used for GIB alert interruptions. A negative return code indicates an error during registration. Theses functions will be used by other adapter types like AP and PCI to request pass-through interruption support. Signed-off-by: Michael Mueller Acked-by: Pierre Morel Acked-by: Halil Pasic Reviewed-by: Cornelia Huck Message-Id: <20190131085247.13826-12-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 13 ++++ arch/s390/kvm/interrupt.c | 112 +++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 7077762ab460..2cfff617cb21 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -781,6 +781,9 @@ struct kvm_s390_gisa { u8 reserved03[11]; u32 airq_count; } g1; + struct { + u64 word[4]; + } u64; }; }; @@ -813,8 +816,15 @@ struct kvm_s390_vsie { struct page *pages[KVM_MAX_VCPUS]; }; +struct kvm_s390_gisa_iam { + u8 mask; + spinlock_t ref_lock; + u32 ref_count[MAX_ISC + 1]; +}; + struct kvm_s390_gisa_interrupt { struct kvm_s390_gisa *origin; + struct kvm_s390_gisa_iam alert; }; struct kvm_arch{ @@ -885,6 +895,9 @@ void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm, extern int sie64a(struct kvm_s390_sie_block *, u64 *); extern char sie_exit; +extern int kvm_s390_gisc_register(struct kvm *kvm, u32 gisc); +extern int kvm_s390_gisc_unregister(struct kvm *kvm, u32 gisc); + static inline void kvm_arch_hardware_disable(void) {} static inline void kvm_arch_check_processor_compat(void *rtn) {} static inline void kvm_arch_sync_events(struct kvm *kvm) {} diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 5efcd9e2cf8f..040745b23224 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -222,6 +222,33 @@ static inline u8 int_word_to_isc(u32 int_word) */ #define IPM_BIT_OFFSET (offsetof(struct kvm_s390_gisa, ipm) * BITS_PER_BYTE) +/** + * gisa_set_iam - change the GISA interruption alert mask + * + * @gisa: gisa to operate on + * @iam: new IAM value to use + * + * Change the IAM atomically with the next alert address and the IPM + * of the GISA if the GISA is not part of the GIB alert list. All three + * fields are located in the first long word of the GISA. + * + * Returns: 0 on success + * -EBUSY in case the gisa is part of the alert list + */ +static inline int gisa_set_iam(struct kvm_s390_gisa *gisa, u8 iam) +{ + u64 word, _word; + + do { + word = READ_ONCE(gisa->u64.word[0]); + if ((u64)gisa != word >> 32) + return -EBUSY; + _word = (word & ~0xffUL) | iam; + } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); + + return 0; +} + static inline void gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) { set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); @@ -2911,6 +2938,8 @@ void kvm_s390_gisa_init(struct kvm *kvm) if (!css_general_characteristics.aiv) return; gi->origin = &kvm->arch.sie_page2->gisa; + gi->alert.mask = 0; + spin_lock_init(&gi->alert.ref_lock); kvm_s390_gisa_clear(kvm); VM_EVENT(kvm, 3, "gisa 0x%pK initialized", gi->origin); } @@ -2920,6 +2949,89 @@ void kvm_s390_gisa_destroy(struct kvm *kvm) kvm->arch.gisa_int.origin = NULL; } +/** + * kvm_s390_gisc_register - register a guest ISC + * + * @kvm: the kernel vm to work with + * @gisc: the guest interruption sub class to register + * + * The function extends the vm specific alert mask to use. + * The effective IAM mask in the GISA is updated as well + * in case the GISA is not part of the GIB alert list. + * It will be updated latest when the IAM gets restored + * by gisa_get_ipm_or_restore_iam(). + * + * Returns: the nonspecific ISC (NISC) the gib alert mechanism + * has registered with the channel subsystem. + * -ENODEV in case the vm uses no GISA + * -ERANGE in case the guest ISC is invalid + */ +int kvm_s390_gisc_register(struct kvm *kvm, u32 gisc) +{ + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; + + if (!gi->origin) + return -ENODEV; + if (gisc > MAX_ISC) + return -ERANGE; + + spin_lock(&gi->alert.ref_lock); + gi->alert.ref_count[gisc]++; + if (gi->alert.ref_count[gisc] == 1) { + gi->alert.mask |= 0x80 >> gisc; + gisa_set_iam(gi->origin, gi->alert.mask); + } + spin_unlock(&gi->alert.ref_lock); + + return gib->nisc; +} +EXPORT_SYMBOL_GPL(kvm_s390_gisc_register); + +/** + * kvm_s390_gisc_unregister - unregister a guest ISC + * + * @kvm: the kernel vm to work with + * @gisc: the guest interruption sub class to register + * + * The function reduces the vm specific alert mask to use. + * The effective IAM mask in the GISA is updated as well + * in case the GISA is not part of the GIB alert list. + * It will be updated latest when the IAM gets restored + * by gisa_get_ipm_or_restore_iam(). + * + * Returns: the nonspecific ISC (NISC) the gib alert mechanism + * has registered with the channel subsystem. + * -ENODEV in case the vm uses no GISA + * -ERANGE in case the guest ISC is invalid + * -EINVAL in case the guest ISC is not registered + */ +int kvm_s390_gisc_unregister(struct kvm *kvm, u32 gisc) +{ + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; + int rc = 0; + + if (!gi->origin) + return -ENODEV; + if (gisc > MAX_ISC) + return -ERANGE; + + spin_lock(&gi->alert.ref_lock); + if (gi->alert.ref_count[gisc] == 0) { + rc = -EINVAL; + goto out; + } + gi->alert.ref_count[gisc]--; + if (gi->alert.ref_count[gisc] == 0) { + gi->alert.mask &= ~(0x80 >> gisc); + gisa_set_iam(gi->origin, gi->alert.mask); + } +out: + spin_unlock(&gi->alert.ref_lock); + + return rc; +} +EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister); + void kvm_s390_gib_destroy(void) { if (!gib) From 174dd4f88875f6ebad7ad1dff3c3e9530a1e3506 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:43 +0100 Subject: [PATCH 1108/4277] KVM: s390: kvm_s390_gisa_clear() now clears the IPM only Function kvm_s390_gisa_clear() now clears the Interruption Pending Mask of the GISA asap. If the GISA is in the alert list at this time it stays in the list but is removed by process_gib_alert_list(). Signed-off-by: Michael Mueller Acked-by: Halil Pasic Reviewed-by: Pierre Morel Message-Id: <20190131085247.13826-13-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/interrupt.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 040745b23224..9faaa8f96fc3 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -249,6 +249,25 @@ static inline int gisa_set_iam(struct kvm_s390_gisa *gisa, u8 iam) return 0; } +/** + * gisa_clear_ipm - clear the GISA interruption pending mask + * + * @gisa: gisa to operate on + * + * Clear the IPM atomically with the next alert address and the IAM + * of the GISA unconditionally. All three fields are located in the + * first long word of the GISA. + */ +static inline void gisa_clear_ipm(struct kvm_s390_gisa *gisa) +{ + u64 word, _word; + + do { + word = READ_ONCE(gisa->u64.word[0]); + _word = word & ~(0xffUL << 24); + } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); +} + static inline void gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) { set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); @@ -2926,8 +2945,7 @@ void kvm_s390_gisa_clear(struct kvm *kvm) if (!gi->origin) return; - memset(gi->origin, 0, sizeof(struct kvm_s390_gisa)); - gi->origin->next_alert = (u32)(u64)gi->origin; + gisa_clear_ipm(gi->origin); VM_EVENT(kvm, 3, "gisa 0x%pK cleared", gi->origin); } @@ -2940,7 +2958,8 @@ void kvm_s390_gisa_init(struct kvm *kvm) gi->origin = &kvm->arch.sie_page2->gisa; gi->alert.mask = 0; spin_lock_init(&gi->alert.ref_lock); - kvm_s390_gisa_clear(kvm); + memset(gi->origin, 0, sizeof(struct kvm_s390_gisa)); + gi->origin->next_alert = (u32)(u64)gi->origin; VM_EVENT(kvm, 3, "gisa 0x%pK initialized", gi->origin); } From 9f30f62163786a0b80e0886046b5c66e714e7e71 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:44 +0100 Subject: [PATCH 1109/4277] KVM: s390: add gib_alert_irq_handler() The patch implements a handler for GIB alert interruptions on the host. Its task is to alert guests that interrupts are pending for them. A GIB alert interrupt statistic counter is added as well: $ cat /proc/interrupts CPU0 CPU1 ... GAL: 23 37 [I/O] GIB Alert ... Signed-off-by: Michael Mueller Acked-by: Halil Pasic Reviewed-by: Pierre Morel Message-Id: <20190131085247.13826-14-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/irq.h | 1 + arch/s390/include/asm/isc.h | 1 + arch/s390/include/asm/kvm_host.h | 3 + arch/s390/kernel/irq.c | 1 + arch/s390/kvm/interrupt.c | 169 ++++++++++++++++++++++++++++++- arch/s390/kvm/kvm-s390.c | 2 + 6 files changed, 175 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 2f7f27e5493f..afaf5e3c57fd 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -62,6 +62,7 @@ enum interruption_class { IRQIO_MSI, IRQIO_VIR, IRQIO_VAI, + IRQIO_GAL, NMI_NMI, CPU_RST, NR_ARCH_IRQS diff --git a/arch/s390/include/asm/isc.h b/arch/s390/include/asm/isc.h index 6cb9e2ed05b6..b2cc1ec78d06 100644 --- a/arch/s390/include/asm/isc.h +++ b/arch/s390/include/asm/isc.h @@ -21,6 +21,7 @@ /* Adapter interrupts. */ #define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */ #define PCI_ISC 2 /* PCI I/O subchannels */ +#define GAL_ISC 5 /* GIB alert */ #define AP_ISC 6 /* adjunct processor (crypto) devices */ /* Functions for registration of I/O interruption subclasses */ diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 2cfff617cb21..c5f51566ecd6 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -825,6 +825,9 @@ struct kvm_s390_gisa_iam { struct kvm_s390_gisa_interrupt { struct kvm_s390_gisa *origin; struct kvm_s390_gisa_iam alert; + struct hrtimer timer; + u64 expires; + DECLARE_BITMAP(kicked_mask, KVM_MAX_VCPUS); }; struct kvm_arch{ diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 0e8d68bac82c..0cd5a5f96729 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -88,6 +88,7 @@ static const struct irq_class irqclass_sub_desc[] = { {.irq = IRQIO_MSI, .name = "MSI", .desc = "[I/O] MSI Interrupt" }, {.irq = IRQIO_VIR, .name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, {.irq = IRQIO_VAI, .name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"}, + {.irq = IRQIO_GAL, .name = "GAL", .desc = "[I/O] GIB Alert"}, {.irq = NMI_NMI, .name = "NMI", .desc = "[NMI] Machine Check"}, {.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"}, }; diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 9faaa8f96fc3..dc446203e5a4 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "kvm-s390.h" #include "gaccess.h" #include "trace-s390.h" @@ -268,6 +269,38 @@ static inline void gisa_clear_ipm(struct kvm_s390_gisa *gisa) } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); } +/** + * gisa_get_ipm_or_restore_iam - return IPM or restore GISA IAM + * + * @gi: gisa interrupt struct to work on + * + * Atomically restores the interruption alert mask if none of the + * relevant ISCs are pending and return the IPM. + * + * Returns: the relevant pending ISCs + */ +static inline u8 gisa_get_ipm_or_restore_iam(struct kvm_s390_gisa_interrupt *gi) +{ + u8 pending_mask, alert_mask; + u64 word, _word; + + do { + word = READ_ONCE(gi->origin->u64.word[0]); + alert_mask = READ_ONCE(gi->alert.mask); + pending_mask = (u8)(word >> 24) & alert_mask; + if (pending_mask) + return pending_mask; + _word = (word & ~0xffUL) | alert_mask; + } while (cmpxchg(&gi->origin->u64.word[0], word, _word) != word); + + return 0; +} + +static inline int gisa_in_alert_list(struct kvm_s390_gisa *gisa) +{ + return READ_ONCE(gisa->next_alert) != (u32)(u64)gisa; +} + static inline void gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) { set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); @@ -1141,6 +1174,7 @@ static u64 __calculate_sltime(struct kvm_vcpu *vcpu) int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) { + struct kvm_s390_gisa_interrupt *gi = &vcpu->kvm->arch.gisa_int; u64 sltime; vcpu->stat.exit_wait_state++; @@ -1154,6 +1188,11 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) return -EOPNOTSUPP; /* disabled wait */ } + if (gi->origin && + (gisa_get_ipm_or_restore_iam(gi) & + vcpu->arch.sie_block->gcr[6] >> 24)) + return 0; + if (!ckc_interrupts_enabled(vcpu) && !cpu_timer_interrupts_enabled(vcpu)) { VCPU_EVENT(vcpu, 3, "%s", "enabled wait w/o timer"); @@ -2939,6 +2978,93 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) return n; } +static void __airqs_kick_single_vcpu(struct kvm *kvm, u8 deliverable_mask) +{ + int vcpu_id, online_vcpus = atomic_read(&kvm->online_vcpus); + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; + struct kvm_vcpu *vcpu; + + for_each_set_bit(vcpu_id, kvm->arch.idle_mask, online_vcpus) { + vcpu = kvm_get_vcpu(kvm, vcpu_id); + if (psw_ioint_disabled(vcpu)) + continue; + deliverable_mask &= (u8)(vcpu->arch.sie_block->gcr[6] >> 24); + if (deliverable_mask) { + /* lately kicked but not yet running */ + if (test_and_set_bit(vcpu_id, gi->kicked_mask)) + return; + kvm_s390_vcpu_wakeup(vcpu); + return; + } + } +} + +static enum hrtimer_restart gisa_vcpu_kicker(struct hrtimer *timer) +{ + struct kvm_s390_gisa_interrupt *gi = + container_of(timer, struct kvm_s390_gisa_interrupt, timer); + struct kvm *kvm = + container_of(gi->origin, struct sie_page2, gisa)->kvm; + u8 pending_mask; + + pending_mask = gisa_get_ipm_or_restore_iam(gi); + if (pending_mask) { + __airqs_kick_single_vcpu(kvm, pending_mask); + hrtimer_forward_now(timer, ns_to_ktime(gi->expires)); + return HRTIMER_RESTART; + }; + + return HRTIMER_NORESTART; +} + +#define NULL_GISA_ADDR 0x00000000UL +#define NONE_GISA_ADDR 0x00000001UL +#define GISA_ADDR_MASK 0xfffff000UL + +static void process_gib_alert_list(void) +{ + struct kvm_s390_gisa_interrupt *gi; + struct kvm_s390_gisa *gisa; + struct kvm *kvm; + u32 final, origin = 0UL; + + do { + /* + * If the NONE_GISA_ADDR is still stored in the alert list + * origin, we will leave the outer loop. No further GISA has + * been added to the alert list by millicode while processing + * the current alert list. + */ + final = (origin & NONE_GISA_ADDR); + /* + * Cut off the alert list and store the NONE_GISA_ADDR in the + * alert list origin to avoid further GAL interruptions. + * A new alert list can be build up by millicode in parallel + * for guests not in the yet cut-off alert list. When in the + * final loop, store the NULL_GISA_ADDR instead. This will re- + * enable GAL interruptions on the host again. + */ + origin = xchg(&gib->alert_list_origin, + (!final) ? NONE_GISA_ADDR : NULL_GISA_ADDR); + /* + * Loop through the just cut-off alert list and start the + * gisa timers to kick idle vcpus to consume the pending + * interruptions asap. + */ + while (origin & GISA_ADDR_MASK) { + gisa = (struct kvm_s390_gisa *)(u64)origin; + origin = gisa->next_alert; + gisa->next_alert = (u32)(u64)gisa; + kvm = container_of(gisa, struct sie_page2, gisa)->kvm; + gi = &kvm->arch.gisa_int; + if (hrtimer_active(&gi->timer)) + hrtimer_cancel(&gi->timer); + hrtimer_start(&gi->timer, 0, HRTIMER_MODE_REL); + } + } while (!final); + +} + void kvm_s390_gisa_clear(struct kvm *kvm) { struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; @@ -2958,6 +3084,9 @@ void kvm_s390_gisa_init(struct kvm *kvm) gi->origin = &kvm->arch.sie_page2->gisa; gi->alert.mask = 0; spin_lock_init(&gi->alert.ref_lock); + gi->expires = 50 * 1000; /* 50 usec */ + hrtimer_init(&gi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + gi->timer.function = gisa_vcpu_kicker; memset(gi->origin, 0, sizeof(struct kvm_s390_gisa)); gi->origin->next_alert = (u32)(u64)gi->origin; VM_EVENT(kvm, 3, "gisa 0x%pK initialized", gi->origin); @@ -2965,7 +3094,17 @@ void kvm_s390_gisa_init(struct kvm *kvm) void kvm_s390_gisa_destroy(struct kvm *kvm) { - kvm->arch.gisa_int.origin = NULL; + struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; + + if (!gi->origin) + return; + if (gi->alert.mask) + KVM_EVENT(3, "vm 0x%pK has unexpected iam 0x%02x", + kvm, gi->alert.mask); + while (gisa_in_alert_list(gi->origin)) + cpu_relax(); + hrtimer_cancel(&gi->timer); + gi->origin = NULL; } /** @@ -3051,11 +3190,23 @@ out: } EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister); +static void gib_alert_irq_handler(struct airq_struct *airq) +{ + inc_irq_stat(IRQIO_GAL); + process_gib_alert_list(); +} + +static struct airq_struct gib_alert_irq = { + .handler = gib_alert_irq_handler, + .lsi_ptr = &gib_alert_irq.lsi_mask, +}; + void kvm_s390_gib_destroy(void) { if (!gib) return; chsc_sgib(0); + unregister_adapter_interrupt(&gib_alert_irq); free_page((unsigned long)gib); gib = NULL; } @@ -3075,16 +3226,30 @@ int kvm_s390_gib_init(u8 nisc) goto out; } + gib_alert_irq.isc = nisc; + if (register_adapter_interrupt(&gib_alert_irq)) { + pr_err("Registering the GIB alert interruption handler failed\n"); + rc = -EIO; + goto out_free_gib; + } + gib->nisc = nisc; if (chsc_sgib((u32)(u64)gib)) { pr_err("Associating the GIB with the AIV facility failed\n"); free_page((unsigned long)gib); gib = NULL; rc = -EIO; - goto out; + goto out_unreg_gal; } KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc); + goto out; + +out_unreg_gal: + unregister_adapter_interrupt(&gib_alert_irq); +out_free_gib: + free_page((unsigned long)gib); + gib = NULL; out: return rc; } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 0de062e989e2..0099fbda2e98 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3460,6 +3460,8 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu) kvm_s390_patch_guest_per_regs(vcpu); } + clear_bit(vcpu->vcpu_id, vcpu->kvm->arch.gisa_int.kicked_mask); + vcpu->arch.sie_block->icptcode = 0; cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags); VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags); From b1d1e76ed9ee5a0f7671da257d4f0595d1f5162e Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:45 +0100 Subject: [PATCH 1110/4277] KVM: s390: start using the GIB By initializing the GIB, it will be used by the kvm host. Signed-off-by: Michael Mueller Reviewed-by: Pierre Morel Reviewed-by: Halil Pasic Message-Id: <20190131085247.13826-15-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/kvm-s390.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 0099fbda2e98..2e47c724679e 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -435,8 +435,15 @@ int kvm_arch_init(void *opaque) pr_err("A FLIC registration call failed with rc=%d\n", rc); goto out_debug_unreg; } + + rc = kvm_s390_gib_init(GAL_ISC); + if (rc) + goto out_gib_destroy; + return 0; +out_gib_destroy: + kvm_s390_gib_destroy(); out_debug_unreg: debug_unregister(kvm_s390_dbf); return rc; From b9fa6d6ee9b88eedf2d2261f931841664aa6980e Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 31 Jan 2019 09:52:46 +0100 Subject: [PATCH 1111/4277] KVM: s390: fix possible null pointer dereference in pending_irqs() Assure a GISA is in use before accessing the IPM to avoid a null pointer dereference issue. Signed-off-by: Michael Mueller Reported-by: Halil Pasic Reviewed-by: Pierre Morel Reviewed-by: Cornelia Huck Message-Id: <20190131085247.13826-16-mimu@linux.ibm.com> Signed-off-by: Christian Borntraeger --- arch/s390/kvm/interrupt.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index dc446203e5a4..82162867f378 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -329,9 +329,13 @@ static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu) static inline unsigned long pending_irqs(struct kvm_vcpu *vcpu) { - return pending_irqs_no_gisa(vcpu) | - gisa_get_ipm(vcpu->kvm->arch.gisa_int.origin) << - IRQ_PEND_IO_ISC_7; + struct kvm_s390_gisa_interrupt *gi = &vcpu->kvm->arch.gisa_int; + unsigned long pending_mask; + + pending_mask = pending_irqs_no_gisa(vcpu); + if (gi->origin) + pending_mask |= gisa_get_ipm(gi->origin) << IRQ_PEND_IO_ISC_7; + return pending_mask; } static inline int isc_to_irq_type(unsigned long isc) From d0051ca584609bd4100ece6ed9f04ae0430dd23b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 21 Jan 2019 18:07:59 +0100 Subject: [PATCH 1112/4277] i2c: rcar: comment maintenance Update copyright years and remove a comment which grew incorrect and useless over time. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index d6c0f50a6dab..498ba4b87833 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -2,8 +2,8 @@ /* * Driver for the Renesas R-Car I2C unit * - * Copyright (C) 2014-15 Wolfram Sang - * Copyright (C) 2011-2015 Renesas Electronics Corporation + * Copyright (C) 2014-19 Wolfram Sang + * Copyright (C) 2011-2019 Renesas Electronics Corporation * * Copyright (C) 2012-14 Renesas Solutions Corp. * Kuninori Morimoto @@ -355,9 +355,6 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) rcar_i2c_prepare_msg(priv); } -/* - * interrupt functions - */ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) { struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE From 2e2b7615e310994b4d59c98b7d114449a9f019e5 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 18 Dec 2018 12:02:42 +0000 Subject: [PATCH 1113/4277] dt-bindings: PCI: rcar: Add device tree support for r8a774c0 Add PCIe support for the RZ/G2E (a.k.a. R8A774C0). Signed-off-by: Fabrizio Castro Signed-off-by: Lorenzo Pieralisi Reviewed-by: Geert Uytterhoeven Reviewed-by: Rob Herring Reviewed-by: Simon Horman --- Documentation/devicetree/bindings/pci/rcar-pci.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pci/rcar-pci.txt b/Documentation/devicetree/bindings/pci/rcar-pci.txt index 976ef7bfff93..6904882a0e94 100644 --- a/Documentation/devicetree/bindings/pci/rcar-pci.txt +++ b/Documentation/devicetree/bindings/pci/rcar-pci.txt @@ -3,6 +3,7 @@ Required properties: compatible: "renesas,pcie-r8a7743" for the R8A7743 SoC; "renesas,pcie-r8a7744" for the R8A7744 SoC; + "renesas,pcie-r8a774c0" for the R8A774C0 SoC; "renesas,pcie-r8a7779" for the R8A7779 SoC; "renesas,pcie-r8a7790" for the R8A7790 SoC; "renesas,pcie-r8a7791" for the R8A7791 SoC; @@ -13,7 +14,8 @@ compatible: "renesas,pcie-r8a7743" for the R8A7743 SoC; "renesas,pcie-r8a77990" for the R8A77990 SoC; "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 or RZ/G1 compatible device. - "renesas,pcie-rcar-gen3" for a generic R-Car Gen3 compatible device. + "renesas,pcie-rcar-gen3" for a generic R-Car Gen3 or + RZ/G2 compatible device. When compatible with the generic version, nodes must list the SoC-specific version corresponding to the platform first From 96f984d3127e283265a6245dacc399967655fe54 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 20 Dec 2018 15:34:51 +0100 Subject: [PATCH 1114/4277] ACPI / scan: Create platform device for BSG2150 ACPI nodes The Point of View TAB-P1006W-232-3G tablet has an ACPI firmware node with a HID of BSG2150 describing the 2 Bosch sensors used in the device a BMC150 compatible accelerometer and a BMC150 compatible magnetometer. The ACPI firmware node actually contains 3 I2cSerialBusV2 resources, but this seems to be a copy and paste job from the BSG1160 firmware node on other devices, since there is no i2c-client listening to the 0x68 address listed in the third resource and the 0x68 address is identical to the address of the third resource in the BSG1160 nodes, where as the other 2 addresses are different. Add the ID to the I2C multi instantiate list, so that the i2c-multi-instantiate.c driver can handle it; And add the necessary info to the i2c-multi-instantiate.c driver to enumerate all I2C slaves correctly. To avoid triggering the: if (i < multi->num_clients) { dev_err(dev, "Error finding driver, idx %d\n", i); Error this commit lists the 3th device in the i2c_inst_data with a type of "bsg2150_dummy_dev". Signed-off-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Andy Shevchenko --- drivers/acpi/scan.c | 1 + drivers/platform/x86/i2c-multi-instantiate.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5efd4219f112..446c959a8f08 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1545,6 +1545,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) */ static const struct acpi_device_id i2c_multi_instantiate_ids[] = { {"BSG1160", }, + {"BSG2150", }, {"INT33FE", }, {"INT3515", }, {} diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c index 3d893e0ac250..197d8a192721 100644 --- a/drivers/platform/x86/i2c-multi-instantiate.c +++ b/drivers/platform/x86/i2c-multi-instantiate.c @@ -159,6 +159,14 @@ static const struct i2c_inst_data bsg1160_data[] = { {} }; +static const struct i2c_inst_data bsg2150_data[] = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + /* The resources describe a 3th client, but it is not really there. */ + { "bsg2150_dummy_dev" }, + {} +}; + static const struct i2c_inst_data int3515_data[] = { { "tps6598x", IRQ_RESOURCE_APIC, 0 }, { "tps6598x", IRQ_RESOURCE_APIC, 1 }, @@ -173,6 +181,7 @@ static const struct i2c_inst_data int3515_data[] = { */ static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { { "BSG1160", (unsigned long)bsg1160_data }, + { "BSG2150", (unsigned long)bsg2150_data }, { "INT3515", (unsigned long)int3515_data }, { } }; From 38f250b7d4a04f61bab4aeee3cb86908744f188d Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Wed, 12 Dec 2018 14:41:25 +0800 Subject: [PATCH 1115/4277] platform/x86: dell-wmi: Ignore new keyboard backlight change event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a new wmi event generated by dell-wmi when pressing keyboard backlight hotkey: [ 3285.474172] dell_wmi: Unknown key with type 0x0010 and code 0x003f pressed This event is for notification purpose, let's ignore it. The keyboard backlight hotkey uses another event so it still works without event 0x3f. Signed-off-by: Kai-Heng Feng Reviewed-by: Pali Rohár Signed-off-by: Andy Shevchenko --- drivers/platform/x86/dell-wmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 16c7f3d9a335..c3166ba73e9a 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -267,6 +267,9 @@ static const struct key_entry dell_wmi_keymap_type_0010[] = { /* Fn-lock switched to multimedia keys */ { KE_IGNORE, 0x1, { KEY_RESERVED } }, + /* Keyboard backlight change notification */ + { KE_IGNORE, 0x3f, { KEY_RESERVED } }, + /* Mic mute */ { KE_KEY, 0x150, { KEY_MICMUTE } }, From 8335ebc1e98a63933b07d8fde3aff8511de75e8c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 20 Dec 2018 22:21:50 +0100 Subject: [PATCH 1116/4277] platform/x86: touchscreen_dmi: Add info for the PoV Wintab P1006w (v1.0) tablet Add touchscreen info for the Point of View Wintab P1006w (v1.0) tablet. Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 8c5d47c0aea6..ce8ebbb1059b 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -369,6 +369,24 @@ static const struct ts_dmi_data pov_mobii_wintab_p800w_v21_data = { .properties = pov_mobii_wintab_p800w_v21_props, }; +static const struct property_entry pov_mobii_wintab_p1006w_v10_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 3), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1984), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1520), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl3692-pov-mobii-wintab-p1006w-v10.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data pov_mobii_wintab_p1006w_v10_data = { + .acpi_name = "MSSL1680:00", + .properties = pov_mobii_wintab_p1006w_v10_props, +}; + static const struct property_entry teclast_x3_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), @@ -706,6 +724,17 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"), }, }, + { + /* Point of View mobii wintab p1006w (v1.0) */ + .driver_data = (void *)&pov_mobii_wintab_p1006w_v10_data, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"), + /* Note 105b is Foxcon's USB/PCI vendor id */ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"), + }, + }, { /* Teclast X3 Plus */ .driver_data = (void *)&teclast_x3_plus_data, From bbb97d728f7768645ccc6a65542bd2fc74024b4f Mon Sep 17 00:00:00 2001 From: Kai Renzig Date: Sun, 3 Feb 2019 19:34:23 +0100 Subject: [PATCH 1117/4277] platform/x86: touchscreen_dmi: Add info for the Chuwi Hi8 Air tablet Add touchscreen platform data for the Chuwi Hi8 Air tablet. Signed-off-by: Kai Renzig [andy: fixed driver name and rewrote subject] Signed-off-by: Andy Shevchenko --- drivers/platform/x86/touchscreen_dmi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index ce8ebbb1059b..167a156e3cc7 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -41,6 +41,20 @@ static const struct ts_dmi_data chuwi_hi8_data = { .properties = chuwi_hi8_props, }; +static const struct property_entry chuwi_hi8_air_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-hi8-air.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + { } +}; + +static const struct ts_dmi_data chuwi_hi8_air_data = { + .acpi_name = "MSSL1680:00", + .properties = chuwi_hi8_air_props, +}; + static const struct property_entry chuwi_hi8_pro_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 6), PROPERTY_ENTRY_U32("touchscreen-min-y", 3), @@ -515,6 +529,15 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_BIOS_VERSION, "H1D_S806_206"), }, }, + { + /* Chuwi Hi8 Air (CWI543) */ + .driver_data = (void *)&chuwi_hi8_air_data, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + DMI_MATCH(DMI_PRODUCT_NAME, "Hi8 Air"), + }, + }, { /* Chuwi Hi8 Pro (CWI513) */ .driver_data = (void *)&chuwi_hi8_pro_data, From 0bbbe97f2f851138891148a99400109da956c694 Mon Sep 17 00:00:00 2001 From: Anthony Wong Date: Sat, 19 Jan 2019 14:21:15 +0800 Subject: [PATCH 1118/4277] platform/x86: ideapad: Add ideapad 330-15ICH to no_hw_rfkill Lenovo ideapad 330-15ICH does not have hardware radio switch but driver wrongly reports all radios as hard-blocked, add it to no_hw_rfkill to fix it. BugLink: https://bugs.launchpad.net/bugs/1811815 Signed-off-by: Anthony Wong Signed-off-by: Andy Shevchenko --- drivers/platform/x86/ideapad-laptop.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 1589dffab9fa..21d469b9b4fb 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1090,6 +1090,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ISK"), }, }, + { + .ident = "Lenovo ideapad 330-15ICH", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 330-15ICH"), + }, + }, { .ident = "Lenovo ideapad Y700-14ISK", .matches = { From 871f1f2bcb01d205472ddac70d38e8b08e902f15 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 3 Feb 2019 10:42:33 +0100 Subject: [PATCH 1119/4277] platform/x86: intel_int0002_vgpio: Only implement irq_set_wake on Bay Trail Commit c3b8e884defa ("platform/x86: intel_int0002_vgpio: Implement irq_set_wake"), was written to fix some wakeup issues on Bay Trail (BYT) devices. We've received a bug report that this causes a suspend regression on some Cherry Trail (CHT) based devices. To fix the issues this causes on CHT devices, this commit modifies the irq_set_wake support so that we only implement irq_set_wake on BYT devices, Fixes: c3b8e884defa ("platform/x86: intel_int0002_vgpio: ... irq_set_wake") Reported-and-tested-by: Maxim Mikityanskiy Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_int0002_vgpio.c | 32 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index 4b8f7305fc8a..1694a9aec77c 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -51,11 +51,14 @@ #define GPE0A_STS_PORT 0x420 #define GPE0A_EN_PORT 0x428 -#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } +#define BAYTRAIL 0x01 +#define CHERRYTRAIL 0x02 + +#define ICPU(model, data) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, data } static const struct x86_cpu_id int0002_cpu_ids[] = { - ICPU(INTEL_FAM6_ATOM_SILVERMONT), /* Valleyview, Bay Trail */ - ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */ + ICPU(INTEL_FAM6_ATOM_SILVERMONT, BAYTRAIL), /* Valleyview, Bay Trail */ + ICPU(INTEL_FAM6_ATOM_AIRMONT, CHERRYTRAIL), /* Braswell, Cherry Trail */ {} }; @@ -135,7 +138,7 @@ static irqreturn_t int0002_irq(int irq, void *data) return IRQ_HANDLED; } -static struct irq_chip int0002_irqchip = { +static struct irq_chip int0002_byt_irqchip = { .name = DRV_NAME, .irq_ack = int0002_irq_ack, .irq_mask = int0002_irq_mask, @@ -143,10 +146,22 @@ static struct irq_chip int0002_irqchip = { .irq_set_wake = int0002_irq_set_wake, }; +static struct irq_chip int0002_cht_irqchip = { + .name = DRV_NAME, + .irq_ack = int0002_irq_ack, + .irq_mask = int0002_irq_mask, + .irq_unmask = int0002_irq_unmask, + /* + * No set_wake, on CHT the IRQ is typically shared with the ACPI SCI + * and we don't want to mess with the ACPI SCI irq settings. + */ +}; + static int int0002_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct x86_cpu_id *cpu_id; + struct irq_chip *irq_chip; struct gpio_chip *chip; int irq, ret; @@ -195,14 +210,19 @@ static int int0002_probe(struct platform_device *pdev) return ret; } - ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq, + if (cpu_id->driver_data == BAYTRAIL) + irq_chip = &int0002_byt_irqchip; + else + irq_chip = &int0002_cht_irqchip; + + ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq, IRQ_TYPE_NONE); if (ret) { dev_err(dev, "Error adding irqchip: %d\n", ret); return ret; } - gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL); + gpiochip_set_chained_irqchip(chip, irq_chip, irq, NULL); return 0; } From e50af8332785355de3cb40d9f5e8c45dbfc86f53 Mon Sep 17 00:00:00 2001 From: Rajneesh Bhardwaj Date: Fri, 1 Feb 2019 13:02:25 +0530 Subject: [PATCH 1120/4277] platform/x86: intel_pmc_core: Handle CFL regmap properly Only Coffeelake should use Cannonlake regmap other than Cannonlake platform. This allows Coffeelake special handling only when there is no matching PCI device and default reg map selected as per CPUID is for Sunrisepoint PCH. This change is needed to enable support for newer SoCs such as Icelake. Cc: "David E. Box" Cc: Srinivas Pandruvada Fixes: 661405bd817b ("platform/x86: intel_pmc_core: Special case for Coffeelake") Acked-by: "David E. Box" Signed-off-by: Rajneesh Bhardwaj Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 22dbf115782e..37f605da9333 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -768,7 +768,7 @@ static int __init pmc_core_probe(void) * Sunrisepoint PCH regmap can't be used. Use Cannonlake PCH regmap * in this case. */ - if (!pci_dev_present(pmc_pci_ids)) + if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids)) pmcdev->map = &cnp_reg_map; if (lpit_read_residency_count_address(&slp_s0_addr)) From 0e68eeea9894feeba2edf7ec63e4551b87f39621 Mon Sep 17 00:00:00 2001 From: Rajneesh Bhardwaj Date: Fri, 1 Feb 2019 13:02:26 +0530 Subject: [PATCH 1121/4277] platform/x86: intel_pmc_core: Fix PCH IP sts reading A previous commit "platform/x86: intel_pmc_core: Make the driver PCH family agnostic " provided better abstraction to this driver but has some fundamental issues. e.g. the following condition for (index = 0; index < pmcdev->map->ppfear_buckets && index < PPFEAR_MAX_NUM_ENTRIES; index++, iter++) is wrong because for CNL, PPFEAR_MAX_NUM_ENTRIES is hardcoded as 5 which is _wrong_ and even though ppfear_buckets is 8, the loop fails to read all eight registers needed for CNL PCH i.e. PPFEAR0 and PPFEAR1. This patch refactors the pfear show logic to correctly read PCH IP power gating status for Cannonlake and beyond. Cc: "David E. Box" Cc: Srinivas Pandruvada Fixes: c977b98bbef5 ("platform/x86: intel_pmc_core: Make the driver PCH family agnostic") Signed-off-by: Rajneesh Bhardwaj Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_core.c | 3 ++- drivers/platform/x86/intel_pmc_core.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 37f605da9333..9f143cdbea05 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -380,7 +380,8 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused) index < PPFEAR_MAX_NUM_ENTRIES; index++, iter++) pf_regs[index] = pmc_core_reg_read_byte(pmcdev, iter); - for (index = 0; map[index].name; index++) + for (index = 0; map[index].name && + index < pmcdev->map->ppfear_buckets * 8; index++) pmc_core_display_map(s, index, pf_regs[index / 8], map); return 0; diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h index 89554cba5758..1a0104d2cbf0 100644 --- a/drivers/platform/x86/intel_pmc_core.h +++ b/drivers/platform/x86/intel_pmc_core.h @@ -32,7 +32,7 @@ #define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x64 #define PMC_BASE_ADDR_MASK ~(SPT_PMC_MMIO_REG_LEN - 1) #define MTPMC_MASK 0xffff0000 -#define PPFEAR_MAX_NUM_ENTRIES 5 +#define PPFEAR_MAX_NUM_ENTRIES 12 #define SPT_PPFEAR_NUM_ENTRIES 5 #define SPT_PMC_READ_DISABLE_BIT 0x16 #define SPT_PMC_MSG_FULL_STS_BIT 0x18 From d6827015e671cd17871c9b7a0fabe06c044f7470 Mon Sep 17 00:00:00 2001 From: Rajneesh Bhardwaj Date: Fri, 1 Feb 2019 13:02:27 +0530 Subject: [PATCH 1122/4277] platform/x86: intel_pmc_core: Fix PCH IP name For Cannonlake and Icelake, the IP name for Res_6 should be SPF i.e. South Port F. No functional change is intended other than just renaming the IP appropriately. Cc: "David E. Box" Cc: Srinivas Pandruvada Fixes: 291101f6a735 ("platform/x86: intel_pmc_core: Add CannonLake PCH support") Signed-off-by: Rajneesh Bhardwaj Signed-off-by: Andy Shevchenko --- drivers/platform/x86/intel_pmc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 9f143cdbea05..80936e6bdc61 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -203,7 +203,7 @@ static const struct pmc_bit_map cnp_pfear_map[] = { {"CNVI", BIT(3)}, {"UFS0", BIT(4)}, {"EMMC", BIT(5)}, - {"Res_6", BIT(6)}, + {"SPF", BIT(6)}, {"SBR6", BIT(7)}, {"SBR7", BIT(0)}, From c3424f59a0cb994a098c3701bf14617580a18290 Mon Sep 17 00:00:00 2001 From: Weiyi Lu Date: Fri, 14 Dec 2018 10:04:16 +0800 Subject: [PATCH 1123/4277] dt-bindings: clock: add clock for MT2712 Add new clock according to 3rd ECO design change. It's the parent clock of audio clock mux. Signed-off-by: Weiyi Lu Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/mt2712-clk.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/clock/mt2712-clk.h b/include/dt-bindings/clock/mt2712-clk.h index 76265836a1e1..c3b29dff9c0e 100644 --- a/include/dt-bindings/clock/mt2712-clk.h +++ b/include/dt-bindings/clock/mt2712-clk.h @@ -228,7 +228,8 @@ #define CLK_TOP_NFI2X_EN 189 #define CLK_TOP_NFIECC_EN 190 #define CLK_TOP_NFI1X_CK_EN 191 -#define CLK_TOP_NR_CLK 192 +#define CLK_TOP_APLL2_D3 192 +#define CLK_TOP_NR_CLK 193 /* INFRACFG */ From 51ff86dd1069a9ea52672efb20b89dbaff6dffe1 Mon Sep 17 00:00:00 2001 From: Weiyi Lu Date: Fri, 14 Dec 2018 10:04:17 +0800 Subject: [PATCH 1124/4277] clk: mediatek: update clock driver of MT2712 According to 3rd ECO design change, 1. Add new fixed factor clock of audio. 2. Add the parent clocks for audio clock mux. Signed-off-by: Weiyi Lu Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-mt2712.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c index 991d4093726e..e36f4aab634d 100644 --- a/drivers/clk/mediatek/clk-mt2712.c +++ b/drivers/clk/mediatek/clk-mt2712.c @@ -223,6 +223,8 @@ static const struct mtk_fixed_factor top_divs[] = { 4), FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1_ck", 1, 3), + FACTOR(CLK_TOP_APLL2_D3, "apll2_d3", "apll2_ck", 1, + 3), }; static const char * const axi_parents[] = { @@ -594,7 +596,8 @@ static const char * const a1sys_hp_parents[] = { "apll1_ck", "apll1_d2", "apll1_d4", - "apll1_d8" + "apll1_d8", + "apll1_d3" }; static const char * const a2sys_hp_parents[] = { @@ -602,7 +605,8 @@ static const char * const a2sys_hp_parents[] = { "apll2_ck", "apll2_d2", "apll2_d4", - "apll2_d8" + "apll2_d8", + "apll2_d3" }; static const char * const asm_l_parents[] = { From b7e29924a1a628aec60d18651b493fa1601bf944 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 25 Jan 2019 12:34:36 -0300 Subject: [PATCH 1125/4277] clk: ingenic: jz4740: Fix gating of UDC clock The UDC clock is gated when the bit is cleared, not when it is set. Signed-off-by: Paul Cercueil Tested-by: Artur Rojek Fixes: 2b555a4b9cae ("clk: ingenic: Add missing flag for UDC clock") Signed-off-by: Stephen Boyd --- drivers/clk/ingenic/jz4740-cgu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 4479c102e899..b86edd328249 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -165,7 +165,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 29, 1 }, .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, - .gate = { CGU_REG_SCR, 6 }, + .gate = { CGU_REG_SCR, 6, true }, }, /* Gate-only clocks */ From f0c04c276739ed8acbb41b4868e942a55b128dca Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 5 Feb 2019 18:04:49 +0000 Subject: [PATCH 1126/4277] rtc: ds1672: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#138801 ("Unintended sign extension") Fixes: edf1aaa31fc5 ("[PATCH] RTC subsystem: DS1672 driver") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1672.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 9caaccccaa57..b1ebca099b0d 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -58,7 +58,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) "%s: raw read data - counters=%02x,%02x,%02x,%02x\n", __func__, buf[0], buf[1], buf[2], buf[3]); - time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; rtc_time_to_tm(time, tm); From edb190cb173416b9d7f59b3887ecc43f6d94a9ad Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 11 Jan 2019 07:09:02 +0000 Subject: [PATCH 1127/4277] rtc: snvs: make sure clock is enabled for interrupt handle During system suspend, the SNVS RTC's clock will be disabled in noirq suspend phase, but SNVS RTC's alarm interrupt could still arrive, system will hang if SNVS RTC driver tries to access register without clock enabled, this patch fixes the issue of this scenario. Signed-off-by: Anson Huang Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-snvs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index b2483a749ac4..0b9eff19149b 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -239,6 +239,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) u32 lpsr; u32 events = 0; + if (data->clk) + clk_enable(data->clk); + regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); if (lpsr & SNVS_LPSR_LPTA) { @@ -253,6 +256,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) /* clear interrupt status */ regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr); + if (data->clk) + clk_disable(data->clk); + return events ? IRQ_HANDLED : IRQ_NONE; } From 761acdda5c2cc89208a94941f69579923420c8e9 Mon Sep 17 00:00:00 2001 From: "Oliver.Rohe@wago.com" Date: Wed, 9 Jan 2019 10:59:40 +0000 Subject: [PATCH 1128/4277] rtc: rs5c372: r2221: fix to use the correct XSTP bit The Ricoh chips have slightly different register layouts and the r2221 chip uses bit 5 as the oscillator halt sensor bit. Signed-off-by: Olive Rohe Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rs5c372.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index c5038329058c..ff35dcef338b 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -52,8 +52,8 @@ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 # define RS5C372_CTRL2_24 (1 << 5) -# define R2025_CTRL2_XST (1 << 5) -# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */ +# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ +# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ # define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ @@ -519,20 +519,30 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) unsigned char buf[2]; int addr, i, ret = 0; - if (rs5c372->type == rtc_r2025sd) { - if (rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST) - return ret; - rs5c372->regs[RS5C_REG_CTRL2] |= R2025_CTRL2_XST; - } else { - if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP)) - return ret; - rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; - } - addr = RS5C_ADDR(RS5C_REG_CTRL1); buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; + /* handle xstp bit */ + switch (rs5c372->type) { + case rtc_r2025sd: + if (buf[1] & R2x2x_CTRL2_XSTP) + return ret; + rs5c372->regs[RS5C_REG_CTRL2] |= R2x2x_CTRL2_XSTP; + break; + case rtc_r2221tl: + if (!(buf[1] & R2x2x_CTRL2_XSTP)) + return ret; + rs5c372->regs[RS5C_REG_CTRL2] &= ~R2x2x_CTRL2_XSTP; + break; + + default: + if (!(buf[1] & RS5C_CTRL2_XSTP)) + return ret; + rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; + break; + } + /* use 24hr mode */ switch (rs5c372->type) { case rtc_rs5c372a: From bf3b4f066d992718b1f86f21249d6781fdc8340d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 30 Jan 2019 16:30:51 -0800 Subject: [PATCH 1129/4277] IB/mlx5: Do not use hw_access_flags for be and CPU data Avoid that sparse reports the following for the mlx5 driver: drivers/infiniband/hw/mlx5/qp.c:2671:34: warning: invalid assignment: |= drivers/infiniband/hw/mlx5/qp.c:2671:34: left side has type restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2671:34: right side has type int drivers/infiniband/hw/mlx5/qp.c:2679:34: warning: invalid assignment: |= drivers/infiniband/hw/mlx5/qp.c:2679:34: left side has type restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2679:34: right side has type int drivers/infiniband/hw/mlx5/qp.c:2680:34: warning: invalid assignment: |= drivers/infiniband/hw/mlx5/qp.c:2680:34: left side has type restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2680:34: right side has type int drivers/infiniband/hw/mlx5/qp.c:2684:34: warning: invalid assignment: |= drivers/infiniband/hw/mlx5/qp.c:2684:34: left side has type restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2684:34: right side has type int drivers/infiniband/hw/mlx5/qp.c:2686:28: warning: cast from restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2686:28: warning: incorrect type in argument 1 (different base types) drivers/infiniband/hw/mlx5/qp.c:2686:28: expected unsigned int [usertype] val drivers/infiniband/hw/mlx5/qp.c:2686:28: got restricted __be32 [usertype] drivers/infiniband/hw/mlx5/qp.c:2686:28: warning: cast from restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2686:28: warning: cast from restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2686:28: warning: cast from restricted __be32 drivers/infiniband/hw/mlx5/qp.c:2686:28: warning: cast from restricted __be32 This patch does not change any functionality. Fixes: a60109dc9a95 ("IB/mlx5: Add support for extended atomic operations") Signed-off-by: Bart Van Assche Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/qp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index c6ccd4d36a90..664d5c6e72e4 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -2747,10 +2747,10 @@ int mlx5_ib_destroy_qp(struct ib_qp *qp) static int to_mlx5_access_flags(struct mlx5_ib_qp *qp, const struct ib_qp_attr *attr, - int attr_mask, __be32 *hw_access_flags) + int attr_mask, __be32 *hw_access_flags_be) { u8 dest_rd_atomic; - u32 access_flags; + u32 access_flags, hw_access_flags = 0; struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.device); @@ -2768,7 +2768,7 @@ static int to_mlx5_access_flags(struct mlx5_ib_qp *qp, access_flags &= IB_ACCESS_REMOTE_WRITE; if (access_flags & IB_ACCESS_REMOTE_READ) - *hw_access_flags |= MLX5_QP_BIT_RRE; + hw_access_flags |= MLX5_QP_BIT_RRE; if (access_flags & IB_ACCESS_REMOTE_ATOMIC) { int atomic_mode; @@ -2776,14 +2776,14 @@ static int to_mlx5_access_flags(struct mlx5_ib_qp *qp, if (atomic_mode < 0) return -EOPNOTSUPP; - *hw_access_flags |= MLX5_QP_BIT_RAE; - *hw_access_flags |= atomic_mode << MLX5_ATOMIC_MODE_OFFSET; + hw_access_flags |= MLX5_QP_BIT_RAE; + hw_access_flags |= atomic_mode << MLX5_ATOMIC_MODE_OFFSET; } if (access_flags & IB_ACCESS_REMOTE_WRITE) - *hw_access_flags |= MLX5_QP_BIT_RWE; + hw_access_flags |= MLX5_QP_BIT_RWE; - *hw_access_flags = cpu_to_be32(*hw_access_flags); + *hw_access_flags_be = cpu_to_be32(hw_access_flags); return 0; } @@ -3484,7 +3484,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, } if (attr_mask & (IB_QP_ACCESS_FLAGS | IB_QP_MAX_DEST_RD_ATOMIC)) { - __be32 access_flags = 0; + __be32 access_flags; err = to_mlx5_access_flags(qp, attr, attr_mask, &access_flags); if (err) From a78e8723a50530d15faa25cc0b6f009bcd251c20 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 16 Jan 2019 09:55:41 +0200 Subject: [PATCH 1130/4277] RDMA/cma: Remove CM_ID statistics provided by rdma-cm module Netlink statistics exported by rdma-cm never had any working user space component published to the mailing list or to any open source project. Canvassing various proprietary users, and the original requester, we find that there are no real users of this interface. This patch simply removes all occurrences of RDMA CM netlink in favour of modern nldev implementation, which provides the same information and accompanied by widely used user space component. Signed-off-by: Leon Romanovsky Reviewed-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 83 ------------------------------- drivers/infiniband/core/netlink.c | 4 +- include/uapi/rdma/rdma_netlink.h | 17 +------ 3 files changed, 3 insertions(+), 101 deletions(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 81bded0d37d1..e15546ae4d0f 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -4616,85 +4616,6 @@ static void cma_remove_one(struct ib_device *device, void *client_data) kfree(cma_dev); } -static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct nlmsghdr *nlh; - struct rdma_cm_id_stats *id_stats; - struct rdma_id_private *id_priv; - struct rdma_cm_id *id = NULL; - struct cma_device *cma_dev; - int i_dev = 0, i_id = 0; - - /* - * We export all of the IDs as a sequence of messages. Each - * ID gets its own netlink message. - */ - mutex_lock(&lock); - - list_for_each_entry(cma_dev, &dev_list, list) { - if (i_dev < cb->args[0]) { - i_dev++; - continue; - } - - i_id = 0; - list_for_each_entry(id_priv, &cma_dev->id_list, list) { - if (i_id < cb->args[1]) { - i_id++; - continue; - } - - id_stats = ibnl_put_msg(skb, &nlh, cb->nlh->nlmsg_seq, - sizeof *id_stats, RDMA_NL_RDMA_CM, - RDMA_NL_RDMA_CM_ID_STATS, - NLM_F_MULTI); - if (!id_stats) - goto out; - - memset(id_stats, 0, sizeof *id_stats); - id = &id_priv->id; - id_stats->node_type = id->route.addr.dev_addr.dev_type; - id_stats->port_num = id->port_num; - id_stats->bound_dev_if = - id->route.addr.dev_addr.bound_dev_if; - - if (ibnl_put_attr(skb, nlh, - rdma_addr_size(cma_src_addr(id_priv)), - cma_src_addr(id_priv), - RDMA_NL_RDMA_CM_ATTR_SRC_ADDR)) - goto out; - if (ibnl_put_attr(skb, nlh, - rdma_addr_size(cma_dst_addr(id_priv)), - cma_dst_addr(id_priv), - RDMA_NL_RDMA_CM_ATTR_DST_ADDR)) - goto out; - - id_stats->pid = task_pid_vnr(id_priv->res.task); - id_stats->port_space = id->ps; - id_stats->cm_state = id_priv->state; - id_stats->qp_num = id_priv->qp_num; - id_stats->qp_type = id->qp_type; - - i_id++; - nlmsg_end(skb, nlh); - } - - cb->args[1] = 0; - i_dev++; - } - -out: - mutex_unlock(&lock); - cb->args[0] = i_dev; - cb->args[1] = i_id; - - return skb->len; -} - -static const struct rdma_nl_cbs cma_cb_table[RDMA_NL_RDMA_CM_NUM_OPS] = { - [RDMA_NL_RDMA_CM_ID_STATS] = { .dump = cma_get_id_stats}, -}; - static int cma_init_net(struct net *net) { struct cma_pernet *pernet = cma_pernet(net); @@ -4743,7 +4664,6 @@ static int __init cma_init(void) if (ret) goto err; - rdma_nl_register(RDMA_NL_RDMA_CM, cma_cb_table); cma_configfs_init(); return 0; @@ -4759,7 +4679,6 @@ err_wq: static void __exit cma_cleanup(void) { cma_configfs_exit(); - rdma_nl_unregister(RDMA_NL_RDMA_CM); ib_unregister_client(&cma_client); unregister_netdevice_notifier(&cma_nb); ib_sa_unregister_client(&sa_client); @@ -4767,7 +4686,5 @@ static void __exit cma_cleanup(void) destroy_workqueue(cma_wq); } -MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_RDMA_CM, 1); - module_init(cma_init); module_exit(cma_cleanup); diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 724f5a62e82f..eecfc0b377c9 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -56,7 +56,6 @@ EXPORT_SYMBOL(rdma_nl_chk_listeners); static bool is_nl_msg_valid(unsigned int type, unsigned int op) { static const unsigned int max_num_ops[RDMA_NL_NUM_CLIENTS] = { - [RDMA_NL_RDMA_CM] = RDMA_NL_RDMA_CM_NUM_OPS, [RDMA_NL_IWCM] = RDMA_NL_IWPM_NUM_OPS, [RDMA_NL_LS] = RDMA_NL_LS_NUM_OPS, [RDMA_NL_NLDEV] = RDMA_NLDEV_NUM_OPS, @@ -181,8 +180,7 @@ static int rdma_nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, return -EINVAL; } /* FIXME: Convert IWCM to properly handle doit callbacks */ - if ((nlh->nlmsg_flags & NLM_F_DUMP) || index == RDMA_NL_RDMA_CM || - index == RDMA_NL_IWCM) { + if ((nlh->nlmsg_flags & NLM_F_DUMP) || index == RDMA_NL_IWCM) { struct netlink_dump_control c = { .dump = cb_table[op].dump, }; diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index 0f5263767fb4..3a9e681e4257 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -5,8 +5,7 @@ #include enum { - RDMA_NL_RDMA_CM = 1, - RDMA_NL_IWCM, + RDMA_NL_IWCM = 2, RDMA_NL_RSVD, RDMA_NL_LS, /* RDMA Local Services */ RDMA_NL_NLDEV, /* RDMA device interface */ @@ -14,8 +13,7 @@ enum { }; enum { - RDMA_NL_GROUP_CM = 1, - RDMA_NL_GROUP_IWPM, + RDMA_NL_GROUP_IWPM = 2, RDMA_NL_GROUP_LS, RDMA_NL_NUM_GROUPS }; @@ -24,17 +22,6 @@ enum { #define RDMA_NL_GET_OP(type) (type & ((1 << 10) - 1)) #define RDMA_NL_GET_TYPE(client, op) ((client << 10) + op) -enum { - RDMA_NL_RDMA_CM_ID_STATS = 0, - RDMA_NL_RDMA_CM_NUM_OPS -}; - -enum { - RDMA_NL_RDMA_CM_ATTR_SRC_ADDR = 1, - RDMA_NL_RDMA_CM_ATTR_DST_ADDR, - RDMA_NL_RDMA_CM_NUM_ATTR, -}; - /* The minimum version that the iwpm kernel supports */ #define IWPM_UABI_VERSION_MIN 3 From a2bfd708b17adb6e597e70d4eca824667f2d4e3c Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Tue, 5 Feb 2019 11:33:22 -0800 Subject: [PATCH 1131/4277] RDMA/iwpm: move kdoc comments to functions Move the iwpm kdoc comments from the prototype declarations to above the function bodies. There are no functional changes in this patch. Signed-off-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iwpm_msg.c | 116 ++++++++++++++++------ drivers/infiniband/core/iwpm_util.c | 40 ++++++++ include/rdma/iw_portmap.h | 149 ---------------------------- 3 files changed, 123 insertions(+), 182 deletions(-) diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c index 2e30e65b0816..2452b0ddcf0d 100644 --- a/drivers/infiniband/core/iwpm_msg.c +++ b/drivers/infiniband/core/iwpm_msg.c @@ -38,14 +38,21 @@ u16 iwpm_ulib_version = IWPM_UABI_VERSION_MIN; static int iwpm_user_pid = IWPM_PID_UNDEFINED; static atomic_t echo_nlmsg_seq; +/** + * iwpm_valid_pid - Check if the userspace iwarp port mapper pid is valid + * + * Returns true if the pid is greater than zero, otherwise returns false + */ int iwpm_valid_pid(void) { return iwpm_user_pid > 0; } -/* - * iwpm_register_pid - Send a netlink query to user space - * for the iwarp port mapper pid +/** + * iwpm_register_pid - Send a netlink query to userspace + * to get the iwarp port mapper pid + * @pm_msg: Contains driver info to send to the userspace port mapper + * @nl_client: The index of the netlink client * * nlmsg attributes: * [IWPM_NLA_REG_PID_SEQ] @@ -124,13 +131,19 @@ pid_query_error: return ret; } -/* - * iwpm_add_mapping - Send a netlink add mapping message - * to the port mapper +/** + * iwpm_add_mapping - Send a netlink add mapping request to + * the userspace port mapper + * @pm_msg: Contains the local ip/tcp address info to send + * @nl_client: The index of the netlink client + * * nlmsg attributes: * [IWPM_NLA_MANAGE_MAPPING_SEQ] * [IWPM_NLA_MANAGE_ADDR] * [IWPM_NLA_MANAGE_FLAGS] + * + * If the request is successful, the pm_msg stores + * the port mapper response (mapped address info) */ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client) { @@ -208,9 +221,12 @@ add_mapping_error_nowarn: return ret; } -/* - * iwpm_add_and_query_mapping - Send a netlink add and query - * mapping message to the port mapper +/** + * iwpm_add_and_query_mapping - Process the port mapper response to + * iwpm_add_and_query_mapping request + * @pm_msg: Contains the local ip/tcp address info to send + * @nl_client: The index of the netlink client + * * nlmsg attributes: * [IWPM_NLA_QUERY_MAPPING_SEQ] * [IWPM_NLA_QUERY_LOCAL_ADDR] @@ -299,9 +315,13 @@ query_mapping_error_nowarn: return ret; } -/* - * iwpm_remove_mapping - Send a netlink remove mapping message - * to the port mapper +/** + * iwpm_remove_mapping - Send a netlink remove mapping request + * to the userspace port mapper + * + * @local_addr: Local ip/tcp address to remove + * @nl_client: The index of the netlink client + * * nlmsg attributes: * [IWPM_NLA_MANAGE_MAPPING_SEQ] * [IWPM_NLA_MANAGE_ADDR] @@ -372,9 +392,14 @@ static const struct nla_policy resp_reg_policy[IWPM_NLA_RREG_PID_MAX] = { [IWPM_NLA_RREG_PID_ERR] = { .type = NLA_U16 } }; -/* - * iwpm_register_pid_cb - Process a port mapper response to - * iwpm_register_pid() +/** + * iwpm_register_pid_cb - Process the port mapper response to + * iwpm_register_pid query + * @skb: + * @cb: Contains the received message (payload and netlink header) + * + * If successful, the function receives the userspace port mapper pid + * which is used in future communication with the port mapper */ int iwpm_register_pid_cb(struct sk_buff *skb, struct netlink_callback *cb) { @@ -443,9 +468,11 @@ static const struct nla_policy resp_add_policy[IWPM_NLA_RMANAGE_MAPPING_MAX] = { [IWPM_NLA_RMANAGE_MAPPING_ERR] = { .type = NLA_U16 } }; -/* - * iwpm_add_mapping_cb - Process a port mapper response to - * iwpm_add_mapping() +/** + * iwpm_add_mapping_cb - Process the port mapper response to + * iwpm_add_mapping request + * @skb: + * @cb: Contains the received message (payload and netlink header) */ int iwpm_add_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb) { @@ -518,9 +545,11 @@ static const struct nla_policy resp_query_policy[IWPM_NLA_RQUERY_MAPPING_MAX] = [IWPM_NLA_RQUERY_MAPPING_ERR] = { .type = NLA_U16 } }; -/* - * iwpm_add_and_query_mapping_cb - Process a port mapper response to - * iwpm_add_and_query_mapping() +/** + * iwpm_add_and_query_mapping_cb - Process the port mapper response to + * iwpm_add_and_query_mapping request + * @skb: + * @cb: Contains the received message (payload and netlink header) */ int iwpm_add_and_query_mapping_cb(struct sk_buff *skb, struct netlink_callback *cb) @@ -598,9 +627,13 @@ query_mapping_response_exit: return 0; } -/* - * iwpm_remote_info_cb - Process a port mapper message, containing - * the remote connecting peer address info +/** + * iwpm_remote_info_cb - Process remote connecting peer address info, which + * the port mapper has received from the connecting peer + * @skb: + * @cb: Contains the received message (payload and netlink header) + * + * Stores the IPv4/IPv6 address info in a hash table */ int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb) { @@ -673,8 +706,14 @@ static const struct nla_policy resp_mapinfo_policy[IWPM_NLA_MAPINFO_REQ_MAX] = { [IWPM_NLA_MAPINFO_ULIB_VER] = { .type = NLA_U16 } }; -/* - * iwpm_mapping_info_cb - Process a port mapper request for mapping info +/** + * iwpm_mapping_info_cb - Process a notification that the userspace + * port mapper daemon is started + * @skb: + * @cb: Contains the received message (payload and netlink header) + * + * Using the received port mapper pid, send all the local mapping + * info records to the userspace port mapper */ int iwpm_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb) { @@ -727,9 +766,11 @@ static const struct nla_policy ack_mapinfo_policy[IWPM_NLA_MAPINFO_NUM_MAX] = { [IWPM_NLA_MAPINFO_ACK_NUM] = { .type = NLA_U32 } }; -/* - * iwpm_ack_mapping_info_cb - Process a port mapper ack for - * the provided mapping info records +/** + * iwpm_ack_mapping_info_cb - Process the port mapper ack for + * the provided local mapping info records + * @skb: + * @cb: Contains the received message (payload and netlink header) */ int iwpm_ack_mapping_info_cb(struct sk_buff *skb, struct netlink_callback *cb) { @@ -755,8 +796,11 @@ static const struct nla_policy map_error_policy[IWPM_NLA_ERR_MAX] = { [IWPM_NLA_ERR_CODE] = { .type = NLA_U16 }, }; -/* - * iwpm_mapping_error_cb - Process a port mapper error message +/** + * iwpm_mapping_error_cb - Process port mapper notification for error + * + * @skb: + * @cb: Contains the received message (payload and netlink header) */ int iwpm_mapping_error_cb(struct sk_buff *skb, struct netlink_callback *cb) { @@ -797,8 +841,14 @@ static const struct nla_policy hello_policy[IWPM_NLA_HELLO_MAX] = { [IWPM_NLA_HELLO_ABI_VERSION] = { .type = NLA_U16 } }; -/* - * iwpm_hello_cb - Process a port mapper hello request +/** + * iwpm_hello_cb - Process a hello message from iwpmd + * + * @skb: + * @cb: Contains the received message (payload and netlink header) + * + * Using the received port mapper pid, send the kernel's abi_version + * after adjusting it to support the iwpmd version. */ int iwpm_hello_cb(struct sk_buff *skb, struct netlink_callback *cb) { diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index 363938435476..7b97f6e2075f 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -51,6 +51,12 @@ static DEFINE_SPINLOCK(iwpm_reminfo_lock); static DEFINE_MUTEX(iwpm_admin_lock); static struct iwpm_admin_data iwpm_admin; +/** + * iwpm_init - Allocate resources for the iwarp port mapper + * @nl_client: The index of the netlink client + * + * Should be called when network interface goes up. + */ int iwpm_init(u8 nl_client) { int ret = 0; @@ -87,6 +93,12 @@ init_exit: static void free_hash_bucket(void); static void free_reminfo_bucket(void); +/** + * iwpm_exit - Deallocate resources for the iwarp port mapper + * @nl_client: The index of the netlink client + * + * Should be called when network interface goes down. + */ int iwpm_exit(u8 nl_client) { @@ -112,6 +124,14 @@ int iwpm_exit(u8 nl_client) static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *, struct sockaddr_storage *); +/** + * iwpm_create_mapinfo - Store local and mapped IPv4/IPv6 address + * info in a hash table + * @local_addr: Local ip/tcp address + * @mapped_addr: Mapped local ip/tcp address + * @nl_client: The index of the netlink client + * @map_flags: IWPM mapping flags + */ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr, struct sockaddr_storage *mapped_sockaddr, u8 nl_client, u32 map_flags) @@ -151,6 +171,15 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr, return ret; } +/** + * iwpm_remove_mapinfo - Remove local and mapped IPv4/IPv6 address + * info from the hash table + * @local_addr: Local ip/tcp address + * @mapped_local_addr: Mapped local ip/tcp address + * + * Returns err code if mapping info is not found in the hash table, + * otherwise returns 0 + */ int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr, struct sockaddr_storage *mapped_local_addr) { @@ -251,6 +280,17 @@ void iwpm_add_remote_info(struct iwpm_remote_info *rem_info) spin_unlock_irqrestore(&iwpm_reminfo_lock, flags); } +/** + * iwpm_get_remote_info - Get the remote connecting peer address info + * + * @mapped_loc_addr: Mapped local address of the listening peer + * @mapped_rem_addr: Mapped remote address of the connecting peer + * @remote_addr: To store the remote address of the connecting peer + * @nl_client: The index of the netlink client + * + * The remote address info is retrieved and provided to the client in + * the remote_addr. After that it is removed from the hash table + */ int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr, struct sockaddr_storage *mapped_rem_addr, struct sockaddr_storage *remote_addr, diff --git a/include/rdma/iw_portmap.h b/include/rdma/iw_portmap.h index 84fac196ef80..b9fee7feeeb5 100644 --- a/include/rdma/iw_portmap.h +++ b/include/rdma/iw_portmap.h @@ -61,177 +61,28 @@ struct iwpm_sa_data { u32 flags; }; -/** - * iwpm_init - Allocate resources for the iwarp port mapper - * - * Should be called when network interface goes up. - */ int iwpm_init(u8); - -/** - * iwpm_exit - Deallocate resources for the iwarp port mapper - * - * Should be called when network interface goes down. - */ int iwpm_exit(u8); - -/** - * iwpm_valid_pid - Check if the userspace iwarp port mapper pid is valid - * - * Returns true if the pid is greater than zero, otherwise returns false - */ int iwpm_valid_pid(void); - -/** - * iwpm_register_pid - Send a netlink query to userspace - * to get the iwarp port mapper pid - * @pm_msg: Contains driver info to send to the userspace port mapper - * @nl_client: The index of the netlink client - */ int iwpm_register_pid(struct iwpm_dev_data *pm_msg, u8 nl_client); - -/** - * iwpm_add_mapping - Send a netlink add mapping request to - * the userspace port mapper - * @pm_msg: Contains the local ip/tcp address info to send - * @nl_client: The index of the netlink client - * - * If the request is successful, the pm_msg stores - * the port mapper response (mapped address info) - */ int iwpm_add_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client); - -/** - * iwpm_add_and_query_mapping - Send a netlink add and query mapping request - * to the userspace port mapper - * @pm_msg: Contains the local and remote ip/tcp address info to send - * @nl_client: The index of the netlink client - * - * If the request is successful, the pm_msg stores the - * port mapper response (mapped local and remote address info) - */ int iwpm_add_and_query_mapping(struct iwpm_sa_data *pm_msg, u8 nl_client); - -/** - * iwpm_remove_mapping - Send a netlink remove mapping request - * to the userspace port mapper - * - * @local_addr: Local ip/tcp address to remove - * @nl_client: The index of the netlink client - */ int iwpm_remove_mapping(struct sockaddr_storage *local_addr, u8 nl_client); - -/** - * iwpm_register_pid_cb - Process the port mapper response to - * iwpm_register_pid query - * @skb: - * @cb: Contains the received message (payload and netlink header) - * - * If successful, the function receives the userspace port mapper pid - * which is used in future communication with the port mapper - */ int iwpm_register_pid_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_add_mapping_cb - Process the port mapper response to - * iwpm_add_mapping request - * @skb: - * @cb: Contains the received message (payload and netlink header) - */ int iwpm_add_mapping_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_add_and_query_mapping_cb - Process the port mapper response to - * iwpm_add_and_query_mapping request - * @skb: - * @cb: Contains the received message (payload and netlink header) - */ int iwpm_add_and_query_mapping_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_remote_info_cb - Process remote connecting peer address info, which - * the port mapper has received from the connecting peer - * - * @cb: Contains the received message (payload and netlink header) - * - * Stores the IPv4/IPv6 address info in a hash table - */ int iwpm_remote_info_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_mapping_error_cb - Process port mapper notification for error - * - * @skb: - * @cb: Contains the received message (payload and netlink header) - */ int iwpm_mapping_error_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_mapping_info_cb - Process a notification that the userspace - * port mapper daemon is started - * @skb: - * @cb: Contains the received message (payload and netlink header) - * - * Using the received port mapper pid, send all the local mapping - * info records to the userspace port mapper - */ int iwpm_mapping_info_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_ack_mapping_info_cb - Process the port mapper ack for - * the provided local mapping info records - * @skb: - * @cb: Contains the received message (payload and netlink header) - */ int iwpm_ack_mapping_info_cb(struct sk_buff *, struct netlink_callback *); - -/** - * iwpm_get_remote_info - Get the remote connecting peer address info - * - * @mapped_loc_addr: Mapped local address of the listening peer - * @mapped_rem_addr: Mapped remote address of the connecting peer - * @remote_addr: To store the remote address of the connecting peer - * @nl_client: The index of the netlink client - * - * The remote address info is retrieved and provided to the client in - * the remote_addr. After that it is removed from the hash table - */ int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr, struct sockaddr_storage *mapped_rem_addr, struct sockaddr_storage *remote_addr, u8 nl_client); - -/** - * iwpm_create_mapinfo - Store local and mapped IPv4/IPv6 address - * info in a hash table - * @local_addr: Local ip/tcp address - * @mapped_addr: Mapped local ip/tcp address - * @nl_client: The index of the netlink client - * @map_flags: IWPM mapping flags - */ int iwpm_create_mapinfo(struct sockaddr_storage *local_addr, struct sockaddr_storage *mapped_addr, u8 nl_client, u32 map_flags); - -/** - * iwpm_remove_mapinfo - Remove local and mapped IPv4/IPv6 address - * info from the hash table - * @local_addr: Local ip/tcp address - * @mapped_addr: Mapped local ip/tcp address - * - * Returns err code if mapping info is not found in the hash table, - * otherwise returns 0 - */ int iwpm_remove_mapinfo(struct sockaddr_storage *local_addr, struct sockaddr_storage *mapped_addr); -/** - * iwpm_hello_cb - Process a hello message from iwpmd - * - * @skb: - * @cb: Contains the received message (payload and netlink header) - * - * Using the received port mapper pid, send the kernel's abi_version - * after adjusting it to support the iwpmd version. - */ int iwpm_hello_cb(struct sk_buff *skb, struct netlink_callback *cb); #endif /* _IW_PORTMAP_H */ From 385156c5f2a61834666f079ee66338f177c65c28 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:29:44 -0800 Subject: [PATCH 1132/4277] IB/hfi: Move RC functions into a header file This patch moves some RC helper functions into a header file so that they can be called from both RC and TID RDMA functions. In addition, a common function for rewinding a request is created in rdmavt so that it can be shared between qib and hfi1 driver. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 119 ++++++++++++----------------- drivers/infiniband/hw/hfi1/rc.h | 50 ++++++++++++ drivers/infiniband/hw/qib/qib_rc.c | 7 +- drivers/infiniband/sw/rdmavt/rc.c | 13 ++++ include/rdma/rdmavt_qp.h | 10 +++ 5 files changed, 123 insertions(+), 76 deletions(-) create mode 100644 drivers/infiniband/hw/hfi1/rc.h diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 092d5eba980f..6e74cd3814b8 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -51,28 +51,48 @@ #include "hfi.h" #include "qp.h" +#include "rc.h" #include "verbs_txreq.h" #include "trace.h" -/* cut down ridiculously long IB macro names */ -#define OP(x) RC_OP(x) - -static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, - struct rvt_swqe *wqe, - struct hfi1_ibport *ibp); - -static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, - u32 psn, u32 pmtu) +struct rvt_ack_entry *find_prev_entry(struct rvt_qp *qp, u32 psn, u8 *prev, + u8 *prev_ack, bool *scheduled) + __must_hold(&qp->s_lock) { - u32 len; + struct rvt_ack_entry *e = NULL; + u8 i, p; + bool s = true; - len = delta_psn(psn, wqe->psn) * pmtu; - ss->sge = wqe->sg_list[0]; - ss->sg_list = wqe->sg_list + 1; - ss->num_sge = wqe->wr.num_sge; - ss->total_len = wqe->length; - rvt_skip_sge(ss, len, false); - return wqe->length - len; + for (i = qp->r_head_ack_queue; ; i = p) { + if (i == qp->s_tail_ack_queue) + s = false; + if (i) + p = i - 1; + else + p = rvt_size_atomic(ib_to_rvt(qp->ibqp.device)); + if (p == qp->r_head_ack_queue) { + e = NULL; + break; + } + e = &qp->s_ack_queue[p]; + if (!e->opcode) { + e = NULL; + break; + } + if (cmp_psn(psn, e->psn) >= 0) { + if (p == qp->s_tail_ack_queue && + cmp_psn(psn, e->lpsn) <= 0) + s = false; + break; + } + } + if (prev) + *prev = p; + if (prev_ack) + *prev_ack = i; + if (scheduled) + *scheduled = s; + return e; } /** @@ -1229,9 +1249,9 @@ static inline void update_last_psn(struct rvt_qp *qp, u32 psn) * This is similar to hfi1_send_complete but has to check to be sure * that the SGEs are not being referenced if the SWQE is being resent. */ -static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, - struct rvt_swqe *wqe, - struct hfi1_ibport *ibp) +struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, + struct rvt_swqe *wqe, + struct hfi1_ibport *ibp) { lockdep_assert_held(&qp->s_lock); /* @@ -1314,8 +1334,8 @@ static struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, * May be called at interrupt level, with the QP s_lock held. * Returns 1 if OK, 0 if current operation should be aborted (NAK). */ -static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, - u64 val, struct hfi1_ctxtdata *rcd) +int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, + u64 val, struct hfi1_ctxtdata *rcd) { struct hfi1_ibport *ibp; enum ib_wc_status status; @@ -1754,16 +1774,6 @@ bail: return; } -static inline void rc_defered_ack(struct hfi1_ctxtdata *rcd, - struct rvt_qp *qp) -{ - if (list_empty(&qp->rspwait)) { - qp->r_flags |= RVT_R_RSP_NAK; - rvt_get_qp(qp); - list_add_tail(&qp->rspwait, &rcd->qp_wait_list); - } -} - static inline void rc_cancel_ack(struct rvt_qp *qp) { qp->r_adefered = 0; @@ -1796,8 +1806,9 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct rvt_ack_entry *e; unsigned long flags; - u8 i, prev; - int old_req; + u8 prev; + u8 mra; /* most recent ACK */ + bool old_req; trace_hfi1_rcv_error(qp, psn); if (diff > 0) { @@ -1843,29 +1854,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, spin_lock_irqsave(&qp->s_lock, flags); - for (i = qp->r_head_ack_queue; ; i = prev) { - if (i == qp->s_tail_ack_queue) - old_req = 0; - if (i) - prev = i - 1; - else - prev = rvt_size_atomic(ib_to_rvt(qp->ibqp.device)); - if (prev == qp->r_head_ack_queue) { - e = NULL; - break; - } - e = &qp->s_ack_queue[prev]; - if (!e->opcode) { - e = NULL; - break; - } - if (cmp_psn(psn, e->psn) >= 0) { - if (prev == qp->s_tail_ack_queue && - cmp_psn(psn, e->lpsn) <= 0) - old_req = 0; - break; - } - } + e = find_prev_entry(qp, psn, &prev, &mra, &old_req); + switch (opcode) { case OP(RDMA_READ_REQUEST): { struct ib_reth *reth; @@ -1940,7 +1930,7 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, * Resend the most recent ACK if this request is * after all the previous RDMA reads and atomics. */ - if (i == qp->r_head_ack_queue) { + if (mra == qp->r_head_ack_queue) { spin_unlock_irqrestore(&qp->s_lock, flags); qp->r_nak_state = 0; qp->r_ack_psn = qp->r_psn - 1; @@ -1951,7 +1941,7 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, * Resend the RDMA read or atomic op which * ACKs this duplicate request. */ - qp->s_tail_ack_queue = i; + qp->s_tail_ack_queue = mra; break; } qp->s_ack_state = OP(ACKNOWLEDGE); @@ -1968,17 +1958,6 @@ send_ack: return 0; } -static inline void update_ack_queue(struct rvt_qp *qp, unsigned n) -{ - unsigned next; - - next = n + 1; - if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) - next = 0; - qp->s_tail_ack_queue = next; - qp->s_ack_state = OP(ACKNOWLEDGE); -} - static void log_cca_event(struct hfi1_pportdata *ppd, u8 sl, u32 rlid, u32 lqpn, u32 rqpn, u8 svc_type) { diff --git a/drivers/infiniband/hw/hfi1/rc.h b/drivers/infiniband/hw/hfi1/rc.h new file mode 100644 index 000000000000..4329eadcb3df --- /dev/null +++ b/drivers/infiniband/hw/hfi1/rc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2018 Intel Corporation. + * + */ + +#ifndef HFI1_RC_H +#define HFI1_RC_H + +/* cut down ridiculously long IB macro names */ +#define OP(x) IB_OPCODE_RC_##x + +static inline void update_ack_queue(struct rvt_qp *qp, unsigned int n) +{ + unsigned int next; + + next = n + 1; + if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) + next = 0; + qp->s_tail_ack_queue = next; + qp->s_ack_state = OP(ACKNOWLEDGE); +} + +static inline void rc_defered_ack(struct hfi1_ctxtdata *rcd, + struct rvt_qp *qp) +{ + if (list_empty(&qp->rspwait)) { + qp->r_flags |= RVT_R_RSP_NAK; + rvt_get_qp(qp); + list_add_tail(&qp->rspwait, &rcd->qp_wait_list); + } +} + +static inline u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, + u32 psn, u32 pmtu) +{ + u32 len; + + len = delta_psn(psn, wqe->psn) * pmtu; + return rvt_restart_sge(ss, wqe, len); +} + +struct rvt_ack_entry *find_prev_entry(struct rvt_qp *qp, u32 psn, u8 *prev, + u8 *prev_ack, bool *scheduled); +int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, u64 val, + struct hfi1_ctxtdata *rcd); +struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct hfi1_ibport *ibp); + +#endif /* HFI1_RC_H */ diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c index 6fa002940451..50dd9811b088 100644 --- a/drivers/infiniband/hw/qib/qib_rc.c +++ b/drivers/infiniband/hw/qib/qib_rc.c @@ -45,12 +45,7 @@ static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 len; len = ((psn - wqe->psn) & QIB_PSN_MASK) * pmtu; - ss->sge = wqe->sg_list[0]; - ss->sg_list = wqe->sg_list + 1; - ss->num_sge = wqe->wr.num_sge; - ss->total_len = wqe->length; - rvt_skip_sge(ss, len, false); - return wqe->length - len; + return rvt_restart_sge(ss, wqe, len); } /** diff --git a/drivers/infiniband/sw/rdmavt/rc.c b/drivers/infiniband/sw/rdmavt/rc.c index 6131cc558bdb..8d71647820a8 100644 --- a/drivers/infiniband/sw/rdmavt/rc.c +++ b/drivers/infiniband/sw/rdmavt/rc.c @@ -187,3 +187,16 @@ void rvt_get_credit(struct rvt_qp *qp, u32 aeth) } } EXPORT_SYMBOL(rvt_get_credit); + +/* rvt_restart_sge - rewind the sge state for a wqe */ +u32 rvt_restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 len) +{ + ss->sge = wqe->sg_list[0]; + ss->sg_list = wqe->sg_list + 1; + ss->num_sge = wqe->wr.num_sge; + ss->total_len = wqe->length; + rvt_skip_sge(ss, len, false); + return wqe->length - len; +} +EXPORT_SYMBOL(rvt_restart_sge); + diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index cbafb1878669..56a9221378d9 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -628,6 +628,16 @@ __be32 rvt_compute_aeth(struct rvt_qp *qp); */ void rvt_get_credit(struct rvt_qp *qp, u32 aeth); +/** + * rvt_restart_sge - rewind the sge state for a wqe + * @ss: the sge state pointer + * @wqe: the wqe to rewind + * @len: the data length from the start of the wqe in bytes + * + * Returns the remaining data length. + */ +u32 rvt_restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 len); + /** * @qp - the qp pair * @len - the length From 37356e78328186814e994e0ad1a1cfd6a142bef4 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Tue, 5 Feb 2019 14:13:13 -0800 Subject: [PATCH 1133/4277] IB/hfi1: TID RDMA flow allocation The hfi1 hardware flow is a hardware flow-control mechanism for a KDETH data packet that is received on a hfi1 port. It validates the packet by checking both the generation and sequence. Each QP that uses the TID RDMA mechanism will allocate a hardware flow from its receiving context for any incoming KDETH data packets. This patch implements: (1) a function to allocate hardware flow (2) a function to free hardware flow (3) a function to initialize hardware flow generation for a receiving context (4) a wait mechanism if the hardware flow is not available (4) a function to remove the qp from the wait queue for hardware flow when the qp is reset or destroyed. Signed-off-by: Mitko Haralanov Signed-off-by: Ashutosh Dixit Signed-off-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/common.h | 4 + drivers/infiniband/hw/hfi1/hfi.h | 15 + drivers/infiniband/hw/hfi1/init.c | 7 + drivers/infiniband/hw/hfi1/qp.c | 3 + drivers/infiniband/hw/hfi1/qp.h | 2 + drivers/infiniband/hw/hfi1/tid_rdma.c | 440 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 17 + drivers/infiniband/hw/hfi1/verbs.h | 3 + 8 files changed, 491 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/common.h b/drivers/infiniband/hw/hfi1/common.h index 40d3cfb58bd1..7310a5dba420 100644 --- a/drivers/infiniband/hw/hfi1/common.h +++ b/drivers/infiniband/hw/hfi1/common.h @@ -340,6 +340,10 @@ struct diag_pkt { #define HFI1_PSM_IOC_BASE_SEQ 0x0 +/* Number of BTH.PSN bits used for sequence number in expected rcvs */ +#define HFI1_KDETH_BTH_SEQ_SHIFT 11 +#define HFI1_KDETH_BTH_SEQ_MASK (BIT(HFI1_KDETH_BTH_SEQ_SHIFT) - 1) + static inline __u64 rhf_to_cpu(const __le32 *rbuf) { return __le64_to_cpu(*((__le64 *)rbuf)); diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 9aa0357e17b7..78aa344c7403 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -198,6 +198,14 @@ struct exp_tid_set { }; typedef int (*rhf_rcv_function_ptr)(struct hfi1_packet *packet); + +struct tid_queue { + struct list_head queue_head; + /* queue head for QP TID resource waiters */ + u32 enqueue; /* count of tid enqueues */ + u32 dequeue; /* count of tid dequeues */ +}; + struct hfi1_ctxtdata { /* rcvhdrq base, needs mmap before useful */ void *rcvhdrq; @@ -291,6 +299,10 @@ struct hfi1_ctxtdata { /* PSM Specific fields */ /* lock protecting all Expected TID data */ struct mutex exp_mutex; + /* lock protecting all Expected TID data of kernel contexts */ + spinlock_t exp_lock; + /* Queue for QP's waiting for HW TID flows */ + struct tid_queue flow_queue; /* when waiting for rcv or pioavail */ wait_queue_head_t wait; /* uuid from PSM */ @@ -323,6 +335,9 @@ struct hfi1_ctxtdata { */ u8 subctxt_cnt; + /* Bit mask to track free TID RDMA HW flows */ + unsigned long flow_mask; + struct tid_flow_state flows[RXE_NUM_TID_FLOWS]; }; /** diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index a8dbd0f191f5..56830a514b92 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -370,6 +370,8 @@ int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa, rcd->rhf_rcv_function_map = normal_rhf_rcv_functions; mutex_init(&rcd->exp_mutex); + spin_lock_init(&rcd->exp_lock); + INIT_LIST_HEAD(&rcd->flow_queue.queue_head); hfi1_cdbg(PROC, "setting up context %u\n", rcd->ctxt); @@ -472,6 +474,9 @@ int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa, GFP_KERNEL, numa); if (!rcd->opstats) goto bail; + + /* Initialize TID flow generations for the context */ + hfi1_kern_init_ctxt_generations(rcd); } *context = rcd; @@ -771,6 +776,8 @@ static void enable_chip(struct hfi1_devdata *dd) rcvmask |= HFI1_RCVCTRL_NO_RHQ_DROP_ENB; if (HFI1_CAP_KGET_MASK(rcd->flags, NODROP_EGR_FULL)) rcvmask |= HFI1_RCVCTRL_NO_EGR_DROP_ENB; + if (HFI1_CAP_IS_KSET(TID_RDMA)) + rcvmask |= HFI1_RCVCTRL_TIDFLOW_ENB; hfi1_rcvctrl(dd, rcvmask, rcd); sc_enable(rcd->sc); hfi1_rcd_put(rcd); diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index f822f92b415f..69c38af49492 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -738,6 +738,7 @@ void flush_qp_waiters(struct rvt_qp *qp) { lockdep_assert_held(&qp->s_lock); flush_iowait(qp); + hfi1_tid_rdma_flush_wait(qp); } void stop_send_queue(struct rvt_qp *qp) @@ -745,6 +746,8 @@ void stop_send_queue(struct rvt_qp *qp) struct hfi1_qp_priv *priv = qp->priv; iowait_cancel_work(&priv->s_iowait); + if (cancel_work_sync(&priv->tid_rdma.trigger_work)) + rvt_put_qp(qp); } void quiesce_qp(struct rvt_qp *qp) diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h index 7adb6dff6813..ce25a27aa4a1 100644 --- a/drivers/infiniband/hw/hfi1/qp.h +++ b/drivers/infiniband/hw/hfi1/qp.h @@ -63,11 +63,13 @@ extern const struct rvt_operation_params hfi1_post_parms[]; * HFI1_S_AHG_VALID - ahg header valid on chip * HFI1_S_AHG_CLEAR - have send engine clear ahg state * HFI1_S_WAIT_PIO_DRAIN - qp waiting for PIOs to drain + * HFI1_S_WAIT_TID_SPACE - a QP is waiting for TID resource * HFI1_S_MIN_BIT_MASK - the lowest bit that can be used by hfi1 */ #define HFI1_S_AHG_VALID 0x80000000 #define HFI1_S_AHG_CLEAR 0x40000000 #define HFI1_S_WAIT_PIO_DRAIN 0x20000000 +#define HFI1_S_WAIT_TID_SPACE 0x10000000 #define HFI1_S_MIN_BIT_MASK 0x01000000 /* diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index e8f57c0cd8bc..70671212808f 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -5,10 +5,28 @@ */ #include "hfi.h" +#include "qp.h" #include "verbs.h" #include "tid_rdma.h" #include "trace.h" +#define RCV_TID_FLOW_TABLE_CTRL_FLOW_VALID_SMASK BIT_ULL(32) +#define RCV_TID_FLOW_TABLE_CTRL_HDR_SUPP_EN_SMASK BIT_ULL(33) +#define RCV_TID_FLOW_TABLE_CTRL_KEEP_AFTER_SEQ_ERR_SMASK BIT_ULL(34) +#define RCV_TID_FLOW_TABLE_CTRL_KEEP_ON_GEN_ERR_SMASK BIT_ULL(35) +#define RCV_TID_FLOW_TABLE_STATUS_SEQ_MISMATCH_SMASK BIT_ULL(37) +#define RCV_TID_FLOW_TABLE_STATUS_GEN_MISMATCH_SMASK BIT_ULL(38) + +#define GENERATION_MASK 0xFFFFF + +static u32 mask_generation(u32 a) +{ + return a & GENERATION_MASK; +} + +/* Reserved generation value to set to unused flows for kernel contexts */ +#define KERN_GENERATION_RESERVED mask_generation(U32_MAX) + /* * J_KEY for kernel contexts when TID RDMA is used. * See generate_jkey() in hfi.h for more information. @@ -60,6 +78,8 @@ * C - Capcode */ +static void tid_rdma_trigger_resume(struct work_struct *work); + static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { return @@ -251,6 +271,12 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, spin_lock_init(&qpriv->opfn.lock); INIT_WORK(&qpriv->opfn.opfn_work, opfn_send_conn_request); + INIT_WORK(&qpriv->tid_rdma.trigger_work, tid_rdma_trigger_resume); + qpriv->flow_state.psn = 0; + qpriv->flow_state.index = RXE_NUM_TID_FLOWS; + qpriv->flow_state.last_index = RXE_NUM_TID_FLOWS; + qpriv->flow_state.generation = KERN_GENERATION_RESERVED; + INIT_LIST_HEAD(&qpriv->tid_wait); return 0; } @@ -262,3 +288,417 @@ void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp) if (qp->ibqp.qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) cancel_work_sync(&priv->opfn.opfn_work); } + +/* Flow and tid waiter functions */ +/** + * DOC: lock ordering + * + * There are two locks involved with the queuing + * routines: the qp s_lock and the exp_lock. + * + * Since the tid space allocation is called from + * the send engine, the qp s_lock is already held. + * + * The allocation routines will get the exp_lock. + * + * The first_qp() call is provided to allow the head of + * the rcd wait queue to be fetched under the exp_lock and + * followed by a drop of the exp_lock. + * + * Any qp in the wait list will have the qp reference count held + * to hold the qp in memory. + */ + +/* + * return head of rcd wait list + * + * Must hold the exp_lock. + * + * Get a reference to the QP to hold the QP in memory. + * + * The caller must release the reference when the local + * is no longer being used. + */ +static struct rvt_qp *first_qp(struct hfi1_ctxtdata *rcd, + struct tid_queue *queue) + __must_hold(&rcd->exp_lock) +{ + struct hfi1_qp_priv *priv; + + lockdep_assert_held(&rcd->exp_lock); + priv = list_first_entry_or_null(&queue->queue_head, + struct hfi1_qp_priv, + tid_wait); + if (!priv) + return NULL; + rvt_get_qp(priv->owner); + return priv->owner; +} + +/** + * kernel_tid_waiters - determine rcd wait + * @rcd: the receive context + * @qp: the head of the qp being processed + * + * This routine will return false IFF + * the list is NULL or the head of the + * list is the indicated qp. + * + * Must hold the qp s_lock and the exp_lock. + * + * Return: + * false if either of the conditions below are statisfied: + * 1. The list is empty or + * 2. The indicated qp is at the head of the list and the + * HFI1_S_WAIT_TID_SPACE bit is set in qp->s_flags. + * true is returned otherwise. + */ +static bool kernel_tid_waiters(struct hfi1_ctxtdata *rcd, + struct tid_queue *queue, struct rvt_qp *qp) + __must_hold(&rcd->exp_lock) __must_hold(&qp->s_lock) +{ + struct rvt_qp *fqp; + bool ret = true; + + lockdep_assert_held(&qp->s_lock); + lockdep_assert_held(&rcd->exp_lock); + fqp = first_qp(rcd, queue); + if (!fqp || (fqp == qp && (qp->s_flags & HFI1_S_WAIT_TID_SPACE))) + ret = false; + rvt_put_qp(fqp); + return ret; +} + +/** + * dequeue_tid_waiter - dequeue the qp from the list + * @qp - the qp to remove the wait list + * + * This routine removes the indicated qp from the + * wait list if it is there. + * + * This should be done after the hardware flow and + * tid array resources have been allocated. + * + * Must hold the qp s_lock and the rcd exp_lock. + * + * It assumes the s_lock to protect the s_flags + * field and to reliably test the HFI1_S_WAIT_TID_SPACE flag. + */ +static void dequeue_tid_waiter(struct hfi1_ctxtdata *rcd, + struct tid_queue *queue, struct rvt_qp *qp) + __must_hold(&rcd->exp_lock) __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *priv = qp->priv; + + lockdep_assert_held(&qp->s_lock); + lockdep_assert_held(&rcd->exp_lock); + if (list_empty(&priv->tid_wait)) + return; + list_del_init(&priv->tid_wait); + qp->s_flags &= ~HFI1_S_WAIT_TID_SPACE; + queue->dequeue++; + rvt_put_qp(qp); +} + +/** + * queue_qp_for_tid_wait - suspend QP on tid space + * @rcd: the receive context + * @qp: the qp + * + * The qp is inserted at the tail of the rcd + * wait queue and the HFI1_S_WAIT_TID_SPACE s_flag is set. + * + * Must hold the qp s_lock and the exp_lock. + */ +static void queue_qp_for_tid_wait(struct hfi1_ctxtdata *rcd, + struct tid_queue *queue, struct rvt_qp *qp) + __must_hold(&rcd->exp_lock) __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *priv = qp->priv; + + lockdep_assert_held(&qp->s_lock); + lockdep_assert_held(&rcd->exp_lock); + if (list_empty(&priv->tid_wait)) { + qp->s_flags |= HFI1_S_WAIT_TID_SPACE; + list_add_tail(&priv->tid_wait, &queue->queue_head); + priv->tid_enqueue = ++queue->enqueue; + trace_hfi1_qpsleep(qp, HFI1_S_WAIT_TID_SPACE); + rvt_get_qp(qp); + } +} + +/** + * __trigger_tid_waiter - trigger tid waiter + * @qp: the qp + * + * This is a private entrance to schedule the qp + * assuming the caller is holding the qp->s_lock. + */ +static void __trigger_tid_waiter(struct rvt_qp *qp) + __must_hold(&qp->s_lock) +{ + lockdep_assert_held(&qp->s_lock); + if (!(qp->s_flags & HFI1_S_WAIT_TID_SPACE)) + return; + trace_hfi1_qpwakeup(qp, HFI1_S_WAIT_TID_SPACE); + hfi1_schedule_send(qp); +} + +/** + * tid_rdma_schedule_tid_wakeup - schedule wakeup for a qp + * @qp - the qp + * + * trigger a schedule or a waiting qp in a deadlock + * safe manner. The qp reference is held prior + * to this call via first_qp(). + * + * If the qp trigger was already scheduled (!rval) + * the the reference is dropped, otherwise the resume + * or the destroy cancel will dispatch the reference. + */ +static void tid_rdma_schedule_tid_wakeup(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv; + struct hfi1_ibport *ibp; + struct hfi1_pportdata *ppd; + struct hfi1_devdata *dd; + bool rval; + + if (!qp) + return; + + priv = qp->priv; + ibp = to_iport(qp->ibqp.device, qp->port_num); + ppd = ppd_from_ibp(ibp); + dd = dd_from_ibdev(qp->ibqp.device); + + rval = queue_work_on(priv->s_sde ? + priv->s_sde->cpu : + cpumask_first(cpumask_of_node(dd->node)), + ppd->hfi1_wq, + &priv->tid_rdma.trigger_work); + if (!rval) + rvt_put_qp(qp); +} + +/** + * tid_rdma_trigger_resume - field a trigger work request + * @work - the work item + * + * Complete the off qp trigger processing by directly + * calling the progress routine. + */ +static void tid_rdma_trigger_resume(struct work_struct *work) +{ + struct tid_rdma_qp_params *tr; + struct hfi1_qp_priv *priv; + struct rvt_qp *qp; + + tr = container_of(work, struct tid_rdma_qp_params, trigger_work); + priv = container_of(tr, struct hfi1_qp_priv, tid_rdma); + qp = priv->owner; + spin_lock_irq(&qp->s_lock); + if (qp->s_flags & HFI1_S_WAIT_TID_SPACE) { + spin_unlock_irq(&qp->s_lock); + hfi1_do_send(priv->owner, true); + } else { + spin_unlock_irq(&qp->s_lock); + } + rvt_put_qp(qp); +} + +/** + * tid_rdma_flush_wait - unwind any tid space wait + * + * This is called when resetting a qp to + * allow a destroy or reset to get rid + * of any tid space linkage and reference counts. + */ +static void _tid_rdma_flush_wait(struct rvt_qp *qp, struct tid_queue *queue) + __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *priv; + + if (!qp) + return; + lockdep_assert_held(&qp->s_lock); + priv = qp->priv; + qp->s_flags &= ~HFI1_S_WAIT_TID_SPACE; + spin_lock(&priv->rcd->exp_lock); + if (!list_empty(&priv->tid_wait)) { + list_del_init(&priv->tid_wait); + qp->s_flags &= ~HFI1_S_WAIT_TID_SPACE; + queue->dequeue++; + rvt_put_qp(qp); + } + spin_unlock(&priv->rcd->exp_lock); +} + +void hfi1_tid_rdma_flush_wait(struct rvt_qp *qp) + __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *priv = qp->priv; + + _tid_rdma_flush_wait(qp, &priv->rcd->flow_queue); +} + +/* Flow functions */ +/** + * kern_reserve_flow - allocate a hardware flow + * @rcd - the context to use for allocation + * @last - the index of the preferred flow. Use RXE_NUM_TID_FLOWS to + * signify "don't care". + * + * Use a bit mask based allocation to reserve a hardware + * flow for use in receiving KDETH data packets. If a preferred flow is + * specified the function will attempt to reserve that flow again, if + * available. + * + * The exp_lock must be held. + * + * Return: + * On success: a value postive value between 0 and RXE_NUM_TID_FLOWS - 1 + * On failure: -EAGAIN + */ +static int kern_reserve_flow(struct hfi1_ctxtdata *rcd, int last) + __must_hold(&rcd->exp_lock) +{ + int nr; + + /* Attempt to reserve the preferred flow index */ + if (last >= 0 && last < RXE_NUM_TID_FLOWS && + !test_and_set_bit(last, &rcd->flow_mask)) + return last; + + nr = ffz(rcd->flow_mask); + BUILD_BUG_ON(RXE_NUM_TID_FLOWS >= + (sizeof(rcd->flow_mask) * BITS_PER_BYTE)); + if (nr > (RXE_NUM_TID_FLOWS - 1)) + return -EAGAIN; + set_bit(nr, &rcd->flow_mask); + return nr; +} + +static void kern_set_hw_flow(struct hfi1_ctxtdata *rcd, u32 generation, + u32 flow_idx) +{ + u64 reg; + + reg = ((u64)generation << HFI1_KDETH_BTH_SEQ_SHIFT) | + RCV_TID_FLOW_TABLE_CTRL_FLOW_VALID_SMASK | + RCV_TID_FLOW_TABLE_CTRL_KEEP_AFTER_SEQ_ERR_SMASK | + RCV_TID_FLOW_TABLE_CTRL_KEEP_ON_GEN_ERR_SMASK | + RCV_TID_FLOW_TABLE_STATUS_SEQ_MISMATCH_SMASK | + RCV_TID_FLOW_TABLE_STATUS_GEN_MISMATCH_SMASK; + + if (generation != KERN_GENERATION_RESERVED) + reg |= RCV_TID_FLOW_TABLE_CTRL_HDR_SUPP_EN_SMASK; + + write_uctxt_csr(rcd->dd, rcd->ctxt, + RCV_TID_FLOW_TABLE + 8 * flow_idx, reg); +} + +static u32 kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, u32 flow_idx) + __must_hold(&rcd->exp_lock) +{ + u32 generation = rcd->flows[flow_idx].generation; + + kern_set_hw_flow(rcd, generation, flow_idx); + return generation; +} + +static u32 kern_flow_generation_next(u32 gen) +{ + u32 generation = mask_generation(gen + 1); + + if (generation == KERN_GENERATION_RESERVED) + generation = mask_generation(generation + 1); + return generation; +} + +static void kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, u32 flow_idx) + __must_hold(&rcd->exp_lock) +{ + rcd->flows[flow_idx].generation = + kern_flow_generation_next(rcd->flows[flow_idx].generation); + kern_set_hw_flow(rcd, KERN_GENERATION_RESERVED, flow_idx); +} + +int hfi1_kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp) +{ + struct hfi1_qp_priv *qpriv = (struct hfi1_qp_priv *)qp->priv; + struct tid_flow_state *fs = &qpriv->flow_state; + struct rvt_qp *fqp; + unsigned long flags; + int ret = 0; + + /* The QP already has an allocated flow */ + if (fs->index != RXE_NUM_TID_FLOWS) + return ret; + + spin_lock_irqsave(&rcd->exp_lock, flags); + if (kernel_tid_waiters(rcd, &rcd->flow_queue, qp)) + goto queue; + + ret = kern_reserve_flow(rcd, fs->last_index); + if (ret < 0) + goto queue; + fs->index = ret; + fs->last_index = fs->index; + + /* Generation received in a RESYNC overrides default flow generation */ + if (fs->generation != KERN_GENERATION_RESERVED) + rcd->flows[fs->index].generation = fs->generation; + fs->generation = kern_setup_hw_flow(rcd, fs->index); + fs->psn = 0; + fs->flags = 0; + dequeue_tid_waiter(rcd, &rcd->flow_queue, qp); + /* get head before dropping lock */ + fqp = first_qp(rcd, &rcd->flow_queue); + spin_unlock_irqrestore(&rcd->exp_lock, flags); + + tid_rdma_schedule_tid_wakeup(fqp); + return 0; +queue: + queue_qp_for_tid_wait(rcd, &rcd->flow_queue, qp); + spin_unlock_irqrestore(&rcd->exp_lock, flags); + return -EAGAIN; +} + +void hfi1_kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp) +{ + struct hfi1_qp_priv *qpriv = (struct hfi1_qp_priv *)qp->priv; + struct tid_flow_state *fs = &qpriv->flow_state; + struct rvt_qp *fqp; + unsigned long flags; + + if (fs->index >= RXE_NUM_TID_FLOWS) + return; + spin_lock_irqsave(&rcd->exp_lock, flags); + kern_clear_hw_flow(rcd, fs->index); + clear_bit(fs->index, &rcd->flow_mask); + fs->index = RXE_NUM_TID_FLOWS; + fs->psn = 0; + fs->generation = KERN_GENERATION_RESERVED; + + /* get head before dropping lock */ + fqp = first_qp(rcd, &rcd->flow_queue); + spin_unlock_irqrestore(&rcd->exp_lock, flags); + + if (fqp == qp) { + __trigger_tid_waiter(fqp); + rvt_put_qp(fqp); + } else { + tid_rdma_schedule_tid_wakeup(fqp); + } +} + +void hfi1_kern_init_ctxt_generations(struct hfi1_ctxtdata *rcd) +{ + int i; + + for (i = 0; i < RXE_NUM_TID_FLOWS; i++) { + rcd->flows[i].generation = mask_generation(prandom_u32()); + kern_set_hw_flow(rcd, KERN_GENERATION_RESERVED, i); + } +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index ee8151558e3f..3bc0aaf9568f 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -21,10 +21,21 @@ struct tid_rdma_params { }; struct tid_rdma_qp_params { + struct work_struct trigger_work; struct tid_rdma_params local; struct tid_rdma_params __rcu *remote; }; +/* Track state for each hardware flow */ +struct tid_flow_state { + u32 generation; + u32 psn; + u32 r_next_psn; /* next PSN to be received (in TID space) */ + u8 index; + u8 last_index; + u8 flags; +}; + bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data); bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data); bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data); @@ -37,4 +48,10 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, struct ib_qp_init_attr *init_attr); void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp); +void hfi1_tid_rdma_flush_wait(struct rvt_qp *qp); + +int hfi1_kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp); +void hfi1_kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp); +void hfi1_kern_init_ctxt_generations(struct hfi1_ctxtdata *rcd); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index c8baa1e38ff6..9065e470bebb 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -159,9 +159,12 @@ struct hfi1_qp_priv { struct sdma_engine *s_sde; /* current sde */ struct send_context *s_sendcontext; /* current sendcontext */ struct hfi1_ctxtdata *rcd; /* QP's receive context */ + u32 tid_enqueue; /* saved when tid waited */ u8 s_sc; /* SC[0..4] for next packet */ struct iowait s_iowait; + struct list_head tid_wait; /* for queueing tid space */ struct hfi1_opfn_data opfn; + struct tid_flow_state flow_state; struct tid_rdma_qp_params tid_rdma; struct rvt_qp *owner; u8 hdr_type; /* 9B or 16B */ From 838b6fd2d9ca29998869e4d1ecf4566efe807666 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:30:07 -0800 Subject: [PATCH 1134/4277] IB/hfi1: TID RDMA RcvArray programming and TID allocation TID entries are used by hfi1 hardware to receive data payload from incoming packets directly into a user buffer and thus avoid data copying by software. This patch implements the functions for TID allocation, freeing, and programming TID RcvArray entries in hardware for kernel clients. TID entries are managed via lists of TID groups similar to PSM. Furthermore, to track TID resource allocation for each request, software flows are also allocated and freed as needed. Since software flows consume large amount of memory for tracking TID allocation and freeing, it is generally desirable to allocate them dynamically in the send queue and only for TID RDMA requests, but pre-allocate them for receive queue because the send queue could have thousands of entries while the receive queue has only a limited number of entries. Signed-off-by: Mitko Haralanov Signed-off-by: Ashutosh Dixit Signed-off-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/hfi.h | 2 + drivers/infiniband/hw/hfi1/init.c | 3 +- drivers/infiniband/hw/hfi1/tid_rdma.c | 875 +++++++++++++++++++++- drivers/infiniband/hw/hfi1/tid_rdma.h | 101 +++ drivers/infiniband/hw/hfi1/user_exp_rcv.h | 1 - drivers/infiniband/hw/hfi1/verbs.c | 29 +- drivers/infiniband/hw/hfi1/verbs.h | 34 + drivers/infiniband/sw/rdmavt/qp.c | 2 +- include/rdma/rdmavt_qp.h | 2 + 9 files changed, 1032 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 78aa344c7403..1412ed157c98 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -303,6 +303,8 @@ struct hfi1_ctxtdata { spinlock_t exp_lock; /* Queue for QP's waiting for HW TID flows */ struct tid_queue flow_queue; + /* Queue for QP's waiting for HW receive array entries */ + struct tid_queue rarr_queue; /* when waiting for rcv or pioavail */ wait_queue_head_t wait; /* uuid from PSM */ diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 56830a514b92..d13304f7340d 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -372,6 +372,7 @@ int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa, mutex_init(&rcd->exp_mutex); spin_lock_init(&rcd->exp_lock); INIT_LIST_HEAD(&rcd->flow_queue.queue_head); + INIT_LIST_HEAD(&rcd->rarr_queue.queue_head); hfi1_cdbg(PROC, "setting up context %u\n", rcd->ctxt); @@ -1596,7 +1597,7 @@ static void cleanup_device_data(struct hfi1_devdata *dd) struct hfi1_ctxtdata *rcd = dd->rcd[ctxt]; if (rcd) { - hfi1_clear_tids(rcd); + hfi1_free_ctxt_rcv_groups(rcd); hfi1_free_ctxt(rcd); } } diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 70671212808f..1d02b12590f6 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -8,6 +8,7 @@ #include "qp.h" #include "verbs.h" #include "tid_rdma.h" +#include "exp_rcv.h" #include "trace.h" #define RCV_TID_FLOW_TABLE_CTRL_FLOW_VALID_SMASK BIT_ULL(32) @@ -35,8 +36,14 @@ static u32 mask_generation(u32 a) #define HFI1_KERNEL_MIN_JKEY HFI1_ADMIN_JKEY_RANGE #define HFI1_KERNEL_MAX_JKEY (2 * HFI1_ADMIN_JKEY_RANGE - 1) +/* Maximum number of segments in flight per QP request. */ #define TID_RDMA_MAX_READ_SEGS_PER_REQ 6 #define TID_RDMA_MAX_WRITE_SEGS_PER_REQ 4 +#define MAX_REQ max_t(u16, TID_RDMA_MAX_READ_SEGS_PER_REQ, \ + TID_RDMA_MAX_WRITE_SEGS_PER_REQ) +#define MAX_FLOWS roundup_pow_of_two(MAX_REQ + 1) + +#define MAX_EXPECTED_PAGES (MAX_EXPECTED_BUFFER / PAGE_SIZE) #define TID_OPFN_QP_CTXT_MASK 0xff #define TID_OPFN_QP_CTXT_SHIFT 56 @@ -79,6 +86,11 @@ static u32 mask_generation(u32 a) */ static void tid_rdma_trigger_resume(struct work_struct *work); +static void hfi1_kern_exp_rcv_free_flows(struct tid_rdma_request *req); +static int hfi1_kern_exp_rcv_alloc_flows(struct tid_rdma_request *req, + gfp_t gfp); +static void hfi1_init_trdma_req(struct rvt_qp *qp, + struct tid_rdma_request *req); static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { @@ -230,7 +242,7 @@ int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit) BUILD_BUG_ON(TID_RDMA_JKEY > HFI1_KERNEL_MAX_JKEY); rcd->jkey = TID_RDMA_JKEY; hfi1_set_ctxt_jkey(rcd->dd, rcd, rcd->jkey); - return 0; + return hfi1_alloc_ctxt_rcv_groups(rcd); } /** @@ -266,6 +278,7 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, struct ib_qp_init_attr *init_attr) { struct hfi1_qp_priv *qpriv = qp->priv; + int i, ret; qpriv->rcd = qp_to_rcd(rdi, qp); @@ -278,15 +291,75 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->flow_state.generation = KERN_GENERATION_RESERVED; INIT_LIST_HEAD(&qpriv->tid_wait); + if (init_attr->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { + struct hfi1_devdata *dd = qpriv->rcd->dd; + + qpriv->pages = kzalloc_node(TID_RDMA_MAX_PAGES * + sizeof(*qpriv->pages), + GFP_KERNEL, dd->node); + if (!qpriv->pages) + return -ENOMEM; + for (i = 0; i < qp->s_size; i++) { + struct hfi1_swqe_priv *priv; + struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, i); + + priv = kzalloc_node(sizeof(*priv), GFP_KERNEL, + dd->node); + if (!priv) + return -ENOMEM; + + hfi1_init_trdma_req(qp, &priv->tid_req); + priv->tid_req.e.swqe = wqe; + wqe->priv = priv; + } + for (i = 0; i < rvt_max_atomic(rdi); i++) { + struct hfi1_ack_priv *priv; + + priv = kzalloc_node(sizeof(*priv), GFP_KERNEL, + dd->node); + if (!priv) + return -ENOMEM; + + hfi1_init_trdma_req(qp, &priv->tid_req); + priv->tid_req.e.ack = &qp->s_ack_queue[i]; + + ret = hfi1_kern_exp_rcv_alloc_flows(&priv->tid_req, + GFP_KERNEL); + if (ret) { + kfree(priv); + return ret; + } + qp->s_ack_queue[i].priv = priv; + } + } + return 0; } void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp) { - struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_qp_priv *qpriv = qp->priv; + struct rvt_swqe *wqe; + u32 i; - if (qp->ibqp.qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) - cancel_work_sync(&priv->opfn.opfn_work); + if (qp->ibqp.qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { + for (i = 0; i < qp->s_size; i++) { + wqe = rvt_get_swqe_ptr(qp, i); + kfree(wqe->priv); + wqe->priv = NULL; + } + for (i = 0; i < rvt_max_atomic(rdi); i++) { + struct hfi1_ack_priv *priv = qp->s_ack_queue[i].priv; + + if (priv) + hfi1_kern_exp_rcv_free_flows(&priv->tid_req); + kfree(priv); + qp->s_ack_queue[i].priv = NULL; + } + cancel_work_sync(&qpriv->opfn.opfn_work); + kfree(qpriv->pages); + qpriv->pages = NULL; + } } /* Flow and tid waiter functions */ @@ -540,6 +613,7 @@ void hfi1_tid_rdma_flush_wait(struct rvt_qp *qp) struct hfi1_qp_priv *priv = qp->priv; _tid_rdma_flush_wait(qp, &priv->rcd->flow_queue); + _tid_rdma_flush_wait(qp, &priv->rcd->rarr_queue); } /* Flow functions */ @@ -702,3 +776,796 @@ void hfi1_kern_init_ctxt_generations(struct hfi1_ctxtdata *rcd) kern_set_hw_flow(rcd, KERN_GENERATION_RESERVED, i); } } + +/* TID allocation functions */ +static u8 trdma_pset_order(struct tid_rdma_pageset *s) +{ + u8 count = s->count; + + return ilog2(count) + 1; +} + +/** + * tid_rdma_find_phys_blocks_4k - get groups base on mr info + * @npages - number of pages + * @pages - pointer to an array of page structs + * @list - page set array to return + * + * This routine returns the number of groups associated with + * the current sge information. This implementation is based + * on the expected receive find_phys_blocks() adjusted to + * use the MR information vs. the pfn. + * + * Return: + * the number of RcvArray entries + */ +static u32 tid_rdma_find_phys_blocks_4k(struct tid_rdma_flow *flow, + struct page **pages, + u32 npages, + struct tid_rdma_pageset *list) +{ + u32 pagecount, pageidx, setcount = 0, i; + void *vaddr, *this_vaddr; + + if (!npages) + return 0; + + /* + * Look for sets of physically contiguous pages in the user buffer. + * This will allow us to optimize Expected RcvArray entry usage by + * using the bigger supported sizes. + */ + vaddr = page_address(pages[0]); + for (pageidx = 0, pagecount = 1, i = 1; i <= npages; i++) { + this_vaddr = i < npages ? page_address(pages[i]) : NULL; + /* + * If the vaddr's are not sequential, pages are not physically + * contiguous. + */ + if (this_vaddr != (vaddr + PAGE_SIZE)) { + /* + * At this point we have to loop over the set of + * physically contiguous pages and break them down it + * sizes supported by the HW. + * There are two main constraints: + * 1. The max buffer size is MAX_EXPECTED_BUFFER. + * If the total set size is bigger than that + * program only a MAX_EXPECTED_BUFFER chunk. + * 2. The buffer size has to be a power of two. If + * it is not, round down to the closes power of + * 2 and program that size. + */ + while (pagecount) { + int maxpages = pagecount; + u32 bufsize = pagecount * PAGE_SIZE; + + if (bufsize > MAX_EXPECTED_BUFFER) + maxpages = + MAX_EXPECTED_BUFFER >> + PAGE_SHIFT; + else if (!is_power_of_2(bufsize)) + maxpages = + rounddown_pow_of_two(bufsize) >> + PAGE_SHIFT; + + list[setcount].idx = pageidx; + list[setcount].count = maxpages; + pagecount -= maxpages; + pageidx += maxpages; + setcount++; + } + pageidx = i; + pagecount = 1; + vaddr = this_vaddr; + } else { + vaddr += PAGE_SIZE; + pagecount++; + } + } + /* insure we always return an even number of sets */ + if (setcount & 1) + list[setcount++].count = 0; + return setcount; +} + +/** + * tid_flush_pages - dump out pages into pagesets + * @list - list of pagesets + * @idx - pointer to current page index + * @pages - number of pages to dump + * @sets - current number of pagesset + * + * This routine flushes out accumuated pages. + * + * To insure an even number of sets the + * code may add a filler. + * + * This can happen with when pages is not + * a power of 2 or pages is a power of 2 + * less than the maximum pages. + * + * Return: + * The new number of sets + */ + +static u32 tid_flush_pages(struct tid_rdma_pageset *list, + u32 *idx, u32 pages, u32 sets) +{ + while (pages) { + u32 maxpages = pages; + + if (maxpages > MAX_EXPECTED_PAGES) + maxpages = MAX_EXPECTED_PAGES; + else if (!is_power_of_2(maxpages)) + maxpages = rounddown_pow_of_two(maxpages); + list[sets].idx = *idx; + list[sets++].count = maxpages; + *idx += maxpages; + pages -= maxpages; + } + /* might need a filler */ + if (sets & 1) + list[sets++].count = 0; + return sets; +} + +/** + * tid_rdma_find_phys_blocks_8k - get groups base on mr info + * @pages - pointer to an array of page structs + * @npages - number of pages + * @list - page set array to return + * + * This routine parses an array of pages to compute pagesets + * in an 8k compatible way. + * + * pages are tested two at a time, i, i + 1 for contiguous + * pages and i - 1 and i contiguous pages. + * + * If any condition is false, any accumlated pages are flushed and + * v0,v1 are emitted as separate PAGE_SIZE pagesets + * + * Otherwise, the current 8k is totaled for a future flush. + * + * Return: + * The number of pagesets + * list set with the returned number of pagesets + * + */ +static u32 tid_rdma_find_phys_blocks_8k(struct tid_rdma_flow *flow, + struct page **pages, + u32 npages, + struct tid_rdma_pageset *list) +{ + u32 idx, sets = 0, i; + u32 pagecnt = 0; + void *v0, *v1, *vm1; + + if (!npages) + return 0; + for (idx = 0, i = 0, vm1 = NULL; i < npages; i += 2) { + /* get a new v0 */ + v0 = page_address(pages[i]); + v1 = i + 1 < npages ? + page_address(pages[i + 1]) : NULL; + /* compare i, i + 1 vaddr */ + if (v1 != (v0 + PAGE_SIZE)) { + /* flush out pages */ + sets = tid_flush_pages(list, &idx, pagecnt, sets); + /* output v0,v1 as two pagesets */ + list[sets].idx = idx++; + list[sets++].count = 1; + if (v1) { + list[sets].count = 1; + list[sets++].idx = idx++; + } else { + list[sets++].count = 0; + } + vm1 = NULL; + pagecnt = 0; + continue; + } + /* i,i+1 consecutive, look at i-1,i */ + if (vm1 && v0 != (vm1 + PAGE_SIZE)) { + /* flush out pages */ + sets = tid_flush_pages(list, &idx, pagecnt, sets); + pagecnt = 0; + } + /* pages will always be a multiple of 8k */ + pagecnt += 2; + /* save i-1 */ + vm1 = v1; + /* move to next pair */ + } + /* dump residual pages at end */ + sets = tid_flush_pages(list, &idx, npages - idx, sets); + /* by design cannot be odd sets */ + WARN_ON(sets & 1); + return sets; +} + +/** + * Find pages for one segment of a sge array represented by @ss. The function + * does not check the sge, the sge must have been checked for alignment with a + * prior call to hfi1_kern_trdma_ok. Other sge checking is done as part of + * rvt_lkey_ok and rvt_rkey_ok. Also, the function only modifies the local sge + * copy maintained in @ss->sge, the original sge is not modified. + * + * Unlike IB RDMA WRITE, we can't decrement ss->num_sge here because we are not + * releasing the MR reference count at the same time. Otherwise, we'll "leak" + * references to the MR. This difference requires that we keep track of progress + * into the sg_list. This is done by the cur_seg cursor in the tid_rdma_request + * structure. + */ +static u32 kern_find_pages(struct tid_rdma_flow *flow, + struct page **pages, + struct rvt_sge_state *ss, bool *last) +{ + struct tid_rdma_request *req = flow->req; + struct rvt_sge *sge = &ss->sge; + u32 length = flow->req->seg_len; + u32 len = PAGE_SIZE; + u32 i = 0; + + while (length && req->isge < ss->num_sge) { + pages[i++] = virt_to_page(sge->vaddr); + + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; + if (!sge->sge_length) { + if (++req->isge < ss->num_sge) + *sge = ss->sg_list[req->isge - 1]; + } else if (sge->length == 0 && sge->mr->lkey) { + if (++sge->n >= RVT_SEGSZ) { + ++sge->m; + sge->n = 0; + } + sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = sge->mr->map[sge->m]->segs[sge->n].length; + } + length -= len; + } + + flow->length = flow->req->seg_len - length; + *last = req->isge == ss->num_sge ? false : true; + return i; +} + +static void dma_unmap_flow(struct tid_rdma_flow *flow) +{ + struct hfi1_devdata *dd; + int i; + struct tid_rdma_pageset *pset; + + dd = flow->req->rcd->dd; + for (i = 0, pset = &flow->pagesets[0]; i < flow->npagesets; + i++, pset++) { + if (pset->count && pset->addr) { + dma_unmap_page(&dd->pcidev->dev, + pset->addr, + PAGE_SIZE * pset->count, + DMA_FROM_DEVICE); + pset->mapped = 0; + } + } +} + +static int dma_map_flow(struct tid_rdma_flow *flow, struct page **pages) +{ + int i; + struct hfi1_devdata *dd = flow->req->rcd->dd; + struct tid_rdma_pageset *pset; + + for (i = 0, pset = &flow->pagesets[0]; i < flow->npagesets; + i++, pset++) { + if (pset->count) { + pset->addr = dma_map_page(&dd->pcidev->dev, + pages[pset->idx], + 0, + PAGE_SIZE * pset->count, + DMA_FROM_DEVICE); + + if (dma_mapping_error(&dd->pcidev->dev, pset->addr)) { + dma_unmap_flow(flow); + return -ENOMEM; + } + pset->mapped = 1; + } + } + return 0; +} + +static inline bool dma_mapped(struct tid_rdma_flow *flow) +{ + return !!flow->pagesets[0].mapped; +} + +/* + * Get pages pointers and identify contiguous physical memory chunks for a + * segment. All segments are of length flow->req->seg_len. + */ +static int kern_get_phys_blocks(struct tid_rdma_flow *flow, + struct page **pages, + struct rvt_sge_state *ss, bool *last) +{ + u8 npages; + + /* Reuse previously computed pagesets, if any */ + if (flow->npagesets) { + if (!dma_mapped(flow)) + return dma_map_flow(flow, pages); + return 0; + } + + npages = kern_find_pages(flow, pages, ss, last); + + if (flow->req->qp->pmtu == enum_to_mtu(OPA_MTU_4096)) + flow->npagesets = + tid_rdma_find_phys_blocks_4k(flow, pages, npages, + flow->pagesets); + else + flow->npagesets = + tid_rdma_find_phys_blocks_8k(flow, pages, npages, + flow->pagesets); + + return dma_map_flow(flow, pages); +} + +static inline void kern_add_tid_node(struct tid_rdma_flow *flow, + struct hfi1_ctxtdata *rcd, char *s, + struct tid_group *grp, u8 cnt) +{ + struct kern_tid_node *node = &flow->tnode[flow->tnode_cnt++]; + + WARN_ON_ONCE(flow->tnode_cnt >= + (TID_RDMA_MAX_SEGMENT_SIZE >> PAGE_SHIFT)); + if (WARN_ON_ONCE(cnt & 1)) + dd_dev_err(rcd->dd, + "unexpected odd allocation cnt %u map 0x%x used %u", + cnt, grp->map, grp->used); + + node->grp = grp; + node->map = grp->map; + node->cnt = cnt; +} + +/* + * Try to allocate pageset_count TID's from TID groups for a context + * + * This function allocates TID's without moving groups between lists or + * modifying grp->map. This is done as follows, being cogizant of the lists + * between which the TID groups will move: + * 1. First allocate complete groups of 8 TID's since this is more efficient, + * these groups will move from group->full without affecting used + * 2. If more TID's are needed allocate from used (will move from used->full or + * stay in used) + * 3. If we still don't have the required number of TID's go back and look again + * at a complete group (will move from group->used) + */ +static int kern_alloc_tids(struct tid_rdma_flow *flow) +{ + struct hfi1_ctxtdata *rcd = flow->req->rcd; + struct hfi1_devdata *dd = rcd->dd; + u32 ngroups, pageidx = 0; + struct tid_group *group = NULL, *used; + u8 use; + + flow->tnode_cnt = 0; + ngroups = flow->npagesets / dd->rcv_entries.group_size; + if (!ngroups) + goto used_list; + + /* First look at complete groups */ + list_for_each_entry(group, &rcd->tid_group_list.list, list) { + kern_add_tid_node(flow, rcd, "complete groups", group, + group->size); + + pageidx += group->size; + if (!--ngroups) + break; + } + + if (pageidx >= flow->npagesets) + goto ok; + +used_list: + /* Now look at partially used groups */ + list_for_each_entry(used, &rcd->tid_used_list.list, list) { + use = min_t(u32, flow->npagesets - pageidx, + used->size - used->used); + kern_add_tid_node(flow, rcd, "used groups", used, use); + + pageidx += use; + if (pageidx >= flow->npagesets) + goto ok; + } + + /* + * Look again at a complete group, continuing from where we left. + * However, if we are at the head, we have reached the end of the + * complete groups list from the first loop above + */ + if (group && &group->list == &rcd->tid_group_list.list) + goto bail_eagain; + group = list_prepare_entry(group, &rcd->tid_group_list.list, + list); + if (list_is_last(&group->list, &rcd->tid_group_list.list)) + goto bail_eagain; + group = list_next_entry(group, list); + use = min_t(u32, flow->npagesets - pageidx, group->size); + kern_add_tid_node(flow, rcd, "complete continue", group, use); + pageidx += use; + if (pageidx >= flow->npagesets) + goto ok; +bail_eagain: + return -EAGAIN; +ok: + return 0; +} + +static void kern_program_rcv_group(struct tid_rdma_flow *flow, int grp_num, + u32 *pset_idx) +{ + struct hfi1_ctxtdata *rcd = flow->req->rcd; + struct hfi1_devdata *dd = rcd->dd; + struct kern_tid_node *node = &flow->tnode[grp_num]; + struct tid_group *grp = node->grp; + struct tid_rdma_pageset *pset; + u32 pmtu_pg = flow->req->qp->pmtu >> PAGE_SHIFT; + u32 rcventry, npages = 0, pair = 0, tidctrl; + u8 i, cnt = 0; + + for (i = 0; i < grp->size; i++) { + rcventry = grp->base + i; + + if (node->map & BIT(i) || cnt >= node->cnt) { + rcv_array_wc_fill(dd, rcventry); + continue; + } + pset = &flow->pagesets[(*pset_idx)++]; + if (pset->count) { + hfi1_put_tid(dd, rcventry, PT_EXPECTED, + pset->addr, trdma_pset_order(pset)); + } else { + hfi1_put_tid(dd, rcventry, PT_INVALID, 0, 0); + } + npages += pset->count; + + rcventry -= rcd->expected_base; + tidctrl = pair ? 0x3 : rcventry & 0x1 ? 0x2 : 0x1; + /* + * A single TID entry will be used to use a rcvarr pair (with + * tidctrl 0x3), if ALL these are true (a) the bit pos is even + * (b) the group map shows current and the next bits as free + * indicating two consecutive rcvarry entries are available (c) + * we actually need 2 more entries + */ + pair = !(i & 0x1) && !((node->map >> i) & 0x3) && + node->cnt >= cnt + 2; + if (!pair) { + if (!pset->count) + tidctrl = 0x1; + flow->tid_entry[flow->tidcnt++] = + EXP_TID_SET(IDX, rcventry >> 1) | + EXP_TID_SET(CTRL, tidctrl) | + EXP_TID_SET(LEN, npages); + /* Efficient DIV_ROUND_UP(npages, pmtu_pg) */ + flow->npkts += (npages + pmtu_pg - 1) >> ilog2(pmtu_pg); + npages = 0; + } + + if (grp->used == grp->size - 1) + tid_group_move(grp, &rcd->tid_used_list, + &rcd->tid_full_list); + else if (!grp->used) + tid_group_move(grp, &rcd->tid_group_list, + &rcd->tid_used_list); + + grp->used++; + grp->map |= BIT(i); + cnt++; + } +} + +static void kern_unprogram_rcv_group(struct tid_rdma_flow *flow, int grp_num) +{ + struct hfi1_ctxtdata *rcd = flow->req->rcd; + struct hfi1_devdata *dd = rcd->dd; + struct kern_tid_node *node = &flow->tnode[grp_num]; + struct tid_group *grp = node->grp; + u32 rcventry; + u8 i, cnt = 0; + + for (i = 0; i < grp->size; i++) { + rcventry = grp->base + i; + + if (node->map & BIT(i) || cnt >= node->cnt) { + rcv_array_wc_fill(dd, rcventry); + continue; + } + + hfi1_put_tid(dd, rcventry, PT_INVALID, 0, 0); + + grp->used--; + grp->map &= ~BIT(i); + cnt++; + + if (grp->used == grp->size - 1) + tid_group_move(grp, &rcd->tid_full_list, + &rcd->tid_used_list); + else if (!grp->used) + tid_group_move(grp, &rcd->tid_used_list, + &rcd->tid_group_list); + } + if (WARN_ON_ONCE(cnt & 1)) { + struct hfi1_ctxtdata *rcd = flow->req->rcd; + struct hfi1_devdata *dd = rcd->dd; + + dd_dev_err(dd, "unexpected odd free cnt %u map 0x%x used %u", + cnt, grp->map, grp->used); + } +} + +static void kern_program_rcvarray(struct tid_rdma_flow *flow) +{ + u32 pset_idx = 0; + int i; + + flow->npkts = 0; + flow->tidcnt = 0; + for (i = 0; i < flow->tnode_cnt; i++) + kern_program_rcv_group(flow, i, &pset_idx); +} + +/** + * hfi1_kern_exp_rcv_setup() - setup TID's and flow for one segment of a + * TID RDMA request + * + * @req: TID RDMA request for which the segment/flow is being set up + * @ss: sge state, maintains state across successive segments of a sge + * @last: set to true after the last sge segment has been processed + * + * This function + * (1) finds a free flow entry in the flow circular buffer + * (2) finds pages and continuous physical chunks constituing one segment + * of an sge + * (3) allocates TID group entries for those chunks + * (4) programs rcvarray entries in the hardware corresponding to those + * TID's + * (5) computes a tidarray with formatted TID entries which can be sent + * to the sender + * (6) Reserves and programs HW flows. + * (7) It also manages queing the QP when TID/flow resources are not + * available. + * + * @req points to struct tid_rdma_request of which the segments are a part. The + * function uses qp, rcd and seg_len members of @req. In the absence of errors, + * req->flow_idx is the index of the flow which has been prepared in this + * invocation of function call. With flow = &req->flows[req->flow_idx], + * flow->tid_entry contains the TID array which the sender can use for TID RDMA + * sends and flow->npkts contains number of packets required to send the + * segment. + * + * hfi1_check_sge_align should be called prior to calling this function and if + * it signals error TID RDMA cannot be used for this sge and this function + * should not be called. + * + * For the queuing, caller must hold the flow->req->qp s_lock from the send + * engine and the function will procure the exp_lock. + * + * Return: + * The function returns -EAGAIN if sufficient number of TID/flow resources to + * map the segment could not be allocated. In this case the function should be + * called again with previous arguments to retry the TID allocation. There are + * no other error returns. The function returns 0 on success. + */ +int hfi1_kern_exp_rcv_setup(struct tid_rdma_request *req, + struct rvt_sge_state *ss, bool *last) + __must_hold(&req->qp->s_lock) +{ + struct tid_rdma_flow *flow = &req->flows[req->setup_head]; + struct hfi1_ctxtdata *rcd = req->rcd; + struct hfi1_qp_priv *qpriv = req->qp->priv; + unsigned long flags; + struct rvt_qp *fqp; + u16 clear_tail = req->clear_tail; + + lockdep_assert_held(&req->qp->s_lock); + /* + * We return error if either (a) we don't have space in the flow + * circular buffer, or (b) we already have max entries in the buffer. + * Max entries depend on the type of request we are processing and the + * negotiated TID RDMA parameters. + */ + if (!CIRC_SPACE(req->setup_head, clear_tail, MAX_FLOWS) || + CIRC_CNT(req->setup_head, clear_tail, MAX_FLOWS) >= + req->n_flows) + return -EINVAL; + + /* + * Get pages, identify contiguous physical memory chunks for the segment + * If we can not determine a DMA address mapping we will treat it just + * like if we ran out of space above. + */ + if (kern_get_phys_blocks(flow, qpriv->pages, ss, last)) { + hfi1_wait_kmem(flow->req->qp); + return -ENOMEM; + } + + spin_lock_irqsave(&rcd->exp_lock, flags); + if (kernel_tid_waiters(rcd, &rcd->rarr_queue, flow->req->qp)) + goto queue; + + /* + * At this point we know the number of pagesets and hence the number of + * TID's to map the segment. Allocate the TID's from the TID groups. If + * we cannot allocate the required number we exit and try again later + */ + if (kern_alloc_tids(flow)) + goto queue; + /* + * Finally program the TID entries with the pagesets, compute the + * tidarray and enable the HW flow + */ + kern_program_rcvarray(flow); + + /* + * Setup the flow state with relevant information. + * This information is used for tracking the sequence of data packets + * for the segment. + * The flow is setup here as this is the most accurate time and place + * to do so. Doing at a later time runs the risk of the flow data in + * qpriv getting out of sync. + */ + memset(&flow->flow_state, 0x0, sizeof(flow->flow_state)); + flow->idx = qpriv->flow_state.index; + flow->flow_state.generation = qpriv->flow_state.generation; + flow->flow_state.spsn = qpriv->flow_state.psn; + flow->flow_state.lpsn = flow->flow_state.spsn + flow->npkts - 1; + flow->flow_state.r_next_psn = + full_flow_psn(flow, flow->flow_state.spsn); + qpriv->flow_state.psn += flow->npkts; + + dequeue_tid_waiter(rcd, &rcd->rarr_queue, flow->req->qp); + /* get head before dropping lock */ + fqp = first_qp(rcd, &rcd->rarr_queue); + spin_unlock_irqrestore(&rcd->exp_lock, flags); + tid_rdma_schedule_tid_wakeup(fqp); + + req->setup_head = (req->setup_head + 1) & (MAX_FLOWS - 1); + return 0; +queue: + queue_qp_for_tid_wait(rcd, &rcd->rarr_queue, flow->req->qp); + spin_unlock_irqrestore(&rcd->exp_lock, flags); + return -EAGAIN; +} + +static void hfi1_tid_rdma_reset_flow(struct tid_rdma_flow *flow) +{ + flow->npagesets = 0; +} + +/* + * This function is called after one segment has been successfully sent to + * release the flow and TID HW/SW resources for that segment. The segments for a + * TID RDMA request are setup and cleared in FIFO order which is managed using a + * circular buffer. + */ +int hfi1_kern_exp_rcv_clear(struct tid_rdma_request *req) + __must_hold(&req->qp->s_lock) +{ + struct tid_rdma_flow *flow = &req->flows[req->clear_tail]; + struct hfi1_ctxtdata *rcd = req->rcd; + unsigned long flags; + int i; + struct rvt_qp *fqp; + + lockdep_assert_held(&req->qp->s_lock); + /* Exit if we have nothing in the flow circular buffer */ + if (!CIRC_CNT(req->setup_head, req->clear_tail, MAX_FLOWS)) + return -EINVAL; + + spin_lock_irqsave(&rcd->exp_lock, flags); + + for (i = 0; i < flow->tnode_cnt; i++) + kern_unprogram_rcv_group(flow, i); + /* To prevent double unprogramming */ + flow->tnode_cnt = 0; + /* get head before dropping lock */ + fqp = first_qp(rcd, &rcd->rarr_queue); + spin_unlock_irqrestore(&rcd->exp_lock, flags); + + dma_unmap_flow(flow); + + hfi1_tid_rdma_reset_flow(flow); + req->clear_tail = (req->clear_tail + 1) & (MAX_FLOWS - 1); + + if (fqp == req->qp) { + __trigger_tid_waiter(fqp); + rvt_put_qp(fqp); + } else { + tid_rdma_schedule_tid_wakeup(fqp); + } + + return 0; +} + +/* + * This function is called to release all the tid entries for + * a request. + */ +void hfi1_kern_exp_rcv_clear_all(struct tid_rdma_request *req) + __must_hold(&req->qp->s_lock) +{ + /* Use memory barrier for proper ordering */ + while (CIRC_CNT(req->setup_head, req->clear_tail, MAX_FLOWS)) { + if (hfi1_kern_exp_rcv_clear(req)) + break; + } +} + +/** + * hfi1_kern_exp_rcv_free_flows - free priviously allocated flow information + * @req - the tid rdma request to be cleaned + */ +static void hfi1_kern_exp_rcv_free_flows(struct tid_rdma_request *req) +{ + kfree(req->flows); + req->flows = NULL; +} + +/** + * __trdma_clean_swqe - clean up for large sized QPs + * @qp: the queue patch + * @wqe: the send wqe + */ +void __trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe) +{ + struct hfi1_swqe_priv *p = wqe->priv; + + hfi1_kern_exp_rcv_free_flows(&p->tid_req); +} + +/* + * This can be called at QP create time or in the data path. + */ +static int hfi1_kern_exp_rcv_alloc_flows(struct tid_rdma_request *req, + gfp_t gfp) +{ + struct tid_rdma_flow *flows; + int i; + + if (likely(req->flows)) + return 0; + flows = kmalloc_node(MAX_FLOWS * sizeof(*flows), gfp, + req->rcd->numa_id); + if (!flows) + return -ENOMEM; + /* mini init */ + for (i = 0; i < MAX_FLOWS; i++) { + flows[i].req = req; + flows[i].npagesets = 0; + flows[i].pagesets[0].mapped = 0; + } + req->flows = flows; + return 0; +} + +static void hfi1_init_trdma_req(struct rvt_qp *qp, + struct tid_rdma_request *req) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + + /* + * Initialize various TID RDMA request variables. + * These variables are "static", which is why they + * can be pre-initialized here before the WRs has + * even been submitted. + * However, non-NULL values for these variables do not + * imply that this WQE has been enabled for TID RDMA. + * Drivers should check the WQE's opcode to determine + * if a request is a TID RDMA one or not. + */ + req->qp = qp; + req->rcd = qpriv->rcd; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 3bc0aaf9568f..524baf8c8fac 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -6,7 +6,16 @@ #ifndef HFI1_TID_RDMA_H #define HFI1_TID_RDMA_H +#include +#include "common.h" + +/* Add a convenience helper */ +#define CIRC_ADD(val, add, size) (((val) + (add)) & ((size) - 1)) +#define CIRC_NEXT(val, size) CIRC_ADD(val, 1, size) +#define CIRC_PREV(val, size) CIRC_ADD(val, -1, size) + #define TID_RDMA_MAX_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */ +#define TID_RDMA_MAX_PAGES (BIT(18) >> PAGE_SHIFT) struct tid_rdma_params { struct rcu_head rcu_head; @@ -36,6 +45,81 @@ struct tid_flow_state { u8 flags; }; +struct tid_rdma_request { + struct rvt_qp *qp; + struct hfi1_ctxtdata *rcd; + union { + struct rvt_swqe *swqe; + struct rvt_ack_entry *ack; + } e; + + struct tid_rdma_flow *flows; /* array of tid flows */ + u16 n_flows; /* size of the flow buffer window */ + u16 setup_head; /* flow index we are setting up */ + u16 clear_tail; /* flow index we are clearing */ + u16 flow_idx; /* flow index most recently set up */ + + u32 seg_len; + + u32 isge; /* index of "current" sge */ +}; + +/* + * When header suppression is used, PSNs associated with a "flow" are + * relevant (and not the PSNs maintained by verbs). Track per-flow + * PSNs here for a TID RDMA segment. + * + */ +struct flow_state { + u32 flags; + u32 resp_ib_psn; /* The IB PSN of the response for this flow */ + u32 generation; /* generation of flow */ + u32 spsn; /* starting PSN in TID space */ + u32 lpsn; /* last PSN in TID space */ + u32 r_next_psn; /* next PSN to be received (in TID space) */ +}; + +struct tid_rdma_pageset { + dma_addr_t addr : 48; /* Only needed for the first page */ + u8 idx: 8; + u8 count : 7; + u8 mapped: 1; +}; + +/** + * kern_tid_node - used for managing TID's in TID groups + * + * @grp_idx: rcd relative index to tid_group + * @map: grp->map captured prior to programming this TID group in HW + * @cnt: Only @cnt of available group entries are actually programmed + */ +struct kern_tid_node { + struct tid_group *grp; + u8 map; + u8 cnt; +}; + +/* Overall info for a TID RDMA segment */ +struct tid_rdma_flow { + /* + * While a TID RDMA segment is being transferred, it uses a QP number + * from the "KDETH section of QP numbers" (which is different from the + * QP number that originated the request). Bits 11-15 of these QP + * numbers identify the "TID flow" for the segment. + */ + struct flow_state flow_state; + struct tid_rdma_request *req; + u32 length; + u8 tnode_cnt; + u8 tidcnt; + u8 idx; + u8 npagesets; + u8 npkts; + struct kern_tid_node tnode[TID_RDMA_MAX_PAGES]; + struct tid_rdma_pageset pagesets[TID_RDMA_MAX_PAGES]; + u32 tid_entry[TID_RDMA_MAX_PAGES]; +}; + bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data); bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data); bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data); @@ -43,6 +127,23 @@ void tid_rdma_conn_error(struct rvt_qp *qp); void tid_rdma_opfn_init(struct rvt_qp *qp, struct tid_rdma_params *p); int hfi1_kern_exp_rcv_init(struct hfi1_ctxtdata *rcd, int reinit); +int hfi1_kern_exp_rcv_setup(struct tid_rdma_request *req, + struct rvt_sge_state *ss, bool *last); +int hfi1_kern_exp_rcv_clear(struct tid_rdma_request *req); +void hfi1_kern_exp_rcv_clear_all(struct tid_rdma_request *req); +void __trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe); + +/** + * trdma_clean_swqe - clean flows for swqe if large send queue + * @qp: the qp + * @wqe: the send wqe + */ +static inline void trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe) +{ + if (!wqe->priv) + return; + __trdma_clean_swqe(qp, wqe); +} int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, struct ib_qp_init_attr *init_attr); diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h index e383cc01a2bf..43b105de1d54 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h @@ -48,7 +48,6 @@ */ #include "hfi.h" - #include "exp_rcv.h" struct tid_pageset { diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 571bfd549c2a..02c1873a976c 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -504,11 +504,28 @@ static void verbs_sdma_complete( hfi1_put_txreq(tx); } +void hfi1_wait_kmem(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct ib_qp *ibqp = &qp->ibqp; + struct ib_device *ibdev = ibqp->device; + struct hfi1_ibdev *dev = to_idev(ibdev); + + if (list_empty(&priv->s_iowait.list)) { + if (list_empty(&dev->memwait)) + mod_timer(&dev->mem_timer, jiffies + 1); + qp->s_flags |= RVT_S_WAIT_KMEM; + list_add_tail(&priv->s_iowait.list, &dev->memwait); + priv->s_iowait.lock = &dev->iowait_lock; + trace_hfi1_qpsleep(qp, RVT_S_WAIT_KMEM); + rvt_get_qp(qp); + } +} + static int wait_kmem(struct hfi1_ibdev *dev, struct rvt_qp *qp, struct hfi1_pkt_state *ps) { - struct hfi1_qp_priv *priv = qp->priv; unsigned long flags; int ret = 0; @@ -517,15 +534,7 @@ static int wait_kmem(struct hfi1_ibdev *dev, write_seqlock(&dev->iowait_lock); list_add_tail(&ps->s_txreq->txreq.list, &ps->wait->tx_head); - if (list_empty(&priv->s_iowait.list)) { - if (list_empty(&dev->memwait)) - mod_timer(&dev->mem_timer, jiffies + 1); - qp->s_flags |= RVT_S_WAIT_KMEM; - list_add_tail(&priv->s_iowait.list, &dev->memwait); - priv->s_iowait.lock = &dev->iowait_lock; - trace_hfi1_qpsleep(qp, RVT_S_WAIT_KMEM); - rvt_get_qp(qp); - } + hfi1_wait_kmem(qp); write_sequnlock(&dev->iowait_lock); hfi1_qp_unbusy(qp, ps->wait); ret = -EBUSY; diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 9065e470bebb..94f198b47239 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -159,6 +159,7 @@ struct hfi1_qp_priv { struct sdma_engine *s_sde; /* current sde */ struct send_context *s_sendcontext; /* current sendcontext */ struct hfi1_ctxtdata *rcd; /* QP's receive context */ + struct page **pages; /* for TID page scan */ u32 tid_enqueue; /* saved when tid waited */ u8 s_sc; /* SC[0..4] for next packet */ struct iowait s_iowait; @@ -173,6 +174,14 @@ struct hfi1_qp_priv { u8 timeout_shift; /* account for number of packets per segment */ }; +struct hfi1_swqe_priv { + struct tid_rdma_request tid_req; +}; + +struct hfi1_ack_priv { + struct tid_rdma_request tid_req; +}; + /* * This structure is used to hold commonly lookedup and computed values during * the send engine progress. @@ -321,6 +330,21 @@ static inline u32 delta_psn(u32 a, u32 b) return (((int)a - (int)b) << PSN_SHIFT) >> PSN_SHIFT; } +/* + * Look through all the active flows for a TID RDMA request and find + * the one (if it exists) that contains the specified PSN. + */ +static inline u32 __full_flow_psn(struct flow_state *state, u32 psn) +{ + return mask_psn((state->generation << HFI1_KDETH_BTH_SEQ_SHIFT) | + (psn & HFI1_KDETH_BTH_SEQ_MASK)); +} + +static inline u32 full_flow_psn(struct tid_rdma_flow *flow, u32 psn) +{ + return __full_flow_psn(&flow->flow_state, psn); +} + struct verbs_txreq; void hfi1_put_txreq(struct verbs_txreq *tx); @@ -403,6 +427,16 @@ static inline bool opa_bth_is_migration(struct ib_other_headers *ohdr) return ohdr->bth[1] & cpu_to_be32(OPA_BTH_MIG_REQ); } +void hfi1_wait_kmem(struct rvt_qp *qp); + +static inline void hfi1_trdma_send_complete(struct rvt_qp *qp, + struct rvt_swqe *wqe, + enum ib_wc_status status) +{ + trdma_clean_swqe(qp, wqe); + rvt_send_complete(qp, wqe, status); +} + extern const enum ib_wc_opcode ib_hfi1_wc_opcode[]; extern const u8 hdr_len_by_opcode[]; diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 16247d2a671d..c8e70cf69a8a 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -1642,11 +1642,11 @@ int rvt_destroy_qp(struct ib_qp *ibqp) kref_put(&qp->ip->ref, rvt_release_mmap_info); else vfree(qp->r_rq.wq); - vfree(qp->s_wq); rdi->driver_f.qp_priv_free(rdi, qp); kfree(qp->s_ack_queue); rdma_destroy_ah_attr(&qp->remote_ah_attr); rdma_destroy_ah_attr(&qp->alt_ah_attr); + vfree(qp->s_wq); kfree(qp); return 0; } diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index 56a9221378d9..9095a0b71250 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -174,6 +174,7 @@ struct rvt_swqe { u32 lpsn; /* last packet sequence number */ u32 ssn; /* send sequence number */ u32 length; /* total length of data in sg_list */ + void *priv; /* driver dependent field */ struct rvt_sge sg_list[0]; }; @@ -235,6 +236,7 @@ struct rvt_ack_entry { u32 lpsn; u8 opcode; u8 sent; + void *priv; }; #define RC_QP_SCALING_INTERVAL 5 From 2f16a696a05d34ba8c920b2133a51f18107fdb8b Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:30:18 -0800 Subject: [PATCH 1135/4277] IB/hfi1: Add the counter n_tidwait This patch adds the counter n_tidwait to count the number of times the TID resource allocator has to wait for TID resources. Reviewed-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/chip.c | 2 ++ drivers/infiniband/hw/hfi1/chip.h | 1 + drivers/infiniband/hw/hfi1/tid_rdma.c | 9 +++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 ++++ drivers/infiniband/hw/hfi1/verbs.h | 1 + 5 files changed, 17 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 4d40311f082e..612f04190ed8 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -4253,6 +4253,8 @@ static struct cntr_entry dev_cntrs[DEV_CNTR_LAST] = { access_sw_pio_drain), [C_SW_KMEM_WAIT] = CNTR_ELEM("KmemWait", 0, 0, CNTR_NORMAL, access_sw_kmem_wait), +[C_SW_TID_WAIT] = CNTR_ELEM("TidWait", 0, 0, CNTR_NORMAL, + hfi1_access_sw_tid_wait), [C_SW_SEND_SCHED] = CNTR_ELEM("SendSched", 0, 0, CNTR_NORMAL, access_sw_send_schedule), [C_SDMA_DESC_FETCHED_CNT] = CNTR_ELEM("SDEDscFdCn", diff --git a/drivers/infiniband/hw/hfi1/chip.h b/drivers/infiniband/hw/hfi1/chip.h index ba3d99e6e33b..6c27c1c6a868 100644 --- a/drivers/infiniband/hw/hfi1/chip.h +++ b/drivers/infiniband/hw/hfi1/chip.h @@ -927,6 +927,7 @@ enum { C_SW_PIO_WAIT, C_SW_PIO_DRAIN, C_SW_KMEM_WAIT, + C_SW_TID_WAIT, C_SW_SEND_SCHED, C_SDMA_DESC_FETCHED_CNT, C_SDMA_INT_CNT, diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 1d02b12590f6..43c595f30b3e 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -495,6 +495,7 @@ static void queue_qp_for_tid_wait(struct hfi1_ctxtdata *rcd, qp->s_flags |= HFI1_S_WAIT_TID_SPACE; list_add_tail(&priv->tid_wait, &queue->queue_head); priv->tid_enqueue = ++queue->enqueue; + rcd->dd->verbs_dev.n_tidwait++; trace_hfi1_qpsleep(qp, HFI1_S_WAIT_TID_SPACE); rvt_get_qp(qp); } @@ -1569,3 +1570,11 @@ static void hfi1_init_trdma_req(struct rvt_qp *qp, req->qp = qp; req->rcd = qpriv->rcd; } + +u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry, + void *context, int vl, int mode, u64 data) +{ + struct hfi1_devdata *dd = context; + + return dd->verbs_dev.n_tidwait; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 524baf8c8fac..3dbeaa8cb5b3 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -155,4 +155,8 @@ int hfi1_kern_setup_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp); void hfi1_kern_clear_hw_flow(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp); void hfi1_kern_init_ctxt_generations(struct hfi1_ctxtdata *rcd); +struct cntr_entry; +u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry, + void *context, int vl, int mode, u64 data); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 94f198b47239..20729454f181 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -243,6 +243,7 @@ struct hfi1_ibdev { struct kmem_cache *verbs_txreq_cache; u64 n_txwait; u64 n_kmem_wait; + u64 n_tidwait; /* protect iowait lists */ seqlock_t iowait_lock ____cacheline_aligned_in_smp; From 84f4a40d46d83003bc762df0d3dd051087cc30ea Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:30:28 -0800 Subject: [PATCH 1136/4277] IB/hfi1: Add static trace for flow and TID management functions This patch adds the static trace for the flow and TID management functions to help debugging in the filed. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 19 ++ drivers/infiniband/hw/hfi1/trace.c | 16 ++ drivers/infiniband/hw/hfi1/trace_tid.h | 234 +++++++++++++++++++++++++ 3 files changed, 269 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 43c595f30b3e..506b5a59ded5 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -817,8 +817,11 @@ static u32 tid_rdma_find_phys_blocks_4k(struct tid_rdma_flow *flow, * using the bigger supported sizes. */ vaddr = page_address(pages[0]); + trace_hfi1_tid_flow_page(flow->req->qp, flow, 0, 0, 0, vaddr); for (pageidx = 0, pagecount = 1, i = 1; i <= npages; i++) { this_vaddr = i < npages ? page_address(pages[i]) : NULL; + trace_hfi1_tid_flow_page(flow->req->qp, flow, i, 0, 0, + this_vaddr); /* * If the vaddr's are not sequential, pages are not physically * contiguous. @@ -851,6 +854,9 @@ static u32 tid_rdma_find_phys_blocks_4k(struct tid_rdma_flow *flow, list[setcount].idx = pageidx; list[setcount].count = maxpages; + trace_hfi1_tid_pageset(flow->req->qp, setcount, + list[setcount].idx, + list[setcount].count); pagecount -= maxpages; pageidx += maxpages; setcount++; @@ -946,8 +952,10 @@ static u32 tid_rdma_find_phys_blocks_8k(struct tid_rdma_flow *flow, for (idx = 0, i = 0, vm1 = NULL; i < npages; i += 2) { /* get a new v0 */ v0 = page_address(pages[i]); + trace_hfi1_tid_flow_page(flow->req->qp, flow, i, 1, 0, v0); v1 = i + 1 < npages ? page_address(pages[i + 1]) : NULL; + trace_hfi1_tid_flow_page(flow->req->qp, flow, i, 1, 1, v1); /* compare i, i + 1 vaddr */ if (v1 != (v0 + PAGE_SIZE)) { /* flush out pages */ @@ -1093,6 +1101,8 @@ static int kern_get_phys_blocks(struct tid_rdma_flow *flow, /* Reuse previously computed pagesets, if any */ if (flow->npagesets) { + trace_hfi1_tid_flow_alloc(flow->req->qp, flow->req->setup_head, + flow); if (!dma_mapped(flow)) return dma_map_flow(flow, pages); return 0; @@ -1128,6 +1138,8 @@ static inline void kern_add_tid_node(struct tid_rdma_flow *flow, node->grp = grp; node->map = grp->map; node->cnt = cnt; + trace_hfi1_tid_node_add(flow->req->qp, s, flow->tnode_cnt - 1, + grp->base, grp->map, grp->used, cnt); } /* @@ -1199,6 +1211,8 @@ used_list: if (pageidx >= flow->npagesets) goto ok; bail_eagain: + trace_hfi1_msg_alloc_tids(flow->req->qp, " insufficient tids: needed ", + (u64)flow->npagesets); return -EAGAIN; ok: return 0; @@ -1250,6 +1264,10 @@ static void kern_program_rcv_group(struct tid_rdma_flow *flow, int grp_num, EXP_TID_SET(IDX, rcventry >> 1) | EXP_TID_SET(CTRL, tidctrl) | EXP_TID_SET(LEN, npages); + trace_hfi1_tid_entry_alloc(/* entry */ + flow->req->qp, flow->tidcnt - 1, + flow->tid_entry[flow->tidcnt - 1]); + /* Efficient DIV_ROUND_UP(npages, pmtu_pg) */ flow->npkts += (npages + pmtu_pg - 1) >> ilog2(pmtu_pg); npages = 0; @@ -1316,6 +1334,7 @@ static void kern_program_rcvarray(struct tid_rdma_flow *flow) flow->tidcnt = 0; for (i = 0; i < flow->tnode_cnt; i++) kern_program_rcv_group(flow, i, &pset_idx); + trace_hfi1_tid_flow_alloc(flow->req->qp, flow->req->setup_head, flow); } /** diff --git a/drivers/infiniband/hw/hfi1/trace.c b/drivers/infiniband/hw/hfi1/trace.c index 7c8aed0ffc07..f1154c3c294e 100644 --- a/drivers/infiniband/hw/hfi1/trace.c +++ b/drivers/infiniband/hw/hfi1/trace.c @@ -46,6 +46,7 @@ */ #define CREATE_TRACE_POINTS #include "trace.h" +#include "exp_rcv.h" static u8 __get_ib_hdr_len(struct ib_header *hdr) { @@ -394,6 +395,21 @@ const char *print_u32_array( return ret; } +u8 hfi1_trace_get_tid_ctrl(u32 ent) +{ + return EXP_TID_GET(ent, CTRL); +} + +u16 hfi1_trace_get_tid_len(u32 ent) +{ + return EXP_TID_GET(ent, LEN); +} + +u16 hfi1_trace_get_tid_idx(u32 ent) +{ + return EXP_TID_GET(ent, IDX); +} + __hfi1_trace_fn(AFFINITY); __hfi1_trace_fn(PKT); __hfi1_trace_fn(PROC); diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h index 57a973c97cde..c1da744f44a5 100644 --- a/drivers/infiniband/hw/hfi1/trace_tid.h +++ b/drivers/infiniband/hw/hfi1/trace_tid.h @@ -21,10 +21,21 @@ __print_symbolic(type, \ #undef TRACE_SYSTEM #define TRACE_SYSTEM hfi1_tid +u8 hfi1_trace_get_tid_ctrl(u32 ent); +u16 hfi1_trace_get_tid_len(u32 ent); +u16 hfi1_trace_get_tid_idx(u32 ent); + #define OPFN_PARAM_PRN "[%s] qpn 0x%x %s OPFN: qp 0x%x, max read %u, " \ "max write %u, max length %u, jkey 0x%x timeout %u " \ "urg %u" +#define TID_FLOW_PRN "[%s] qpn 0x%x flow %d: idx %d resp_ib_psn 0x%x " \ + "generation 0x%x fpsn 0x%x-%x r_next_psn 0x%x " \ + "npagesets %u tnode_cnt %u tidcnt %u length %u" + +#define TID_NODE_PRN "[%s] qpn 0x%x %s idx %u grp base 0x%x map 0x%x " \ + "used %u cnt %u" + DECLARE_EVENT_CLASS(/* class */ hfi1_exp_tid_reg_unreg, TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, @@ -323,6 +334,229 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, msg, more) ); +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_alloc_tids, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + +DECLARE_EVENT_CLASS(/* tid_flow_page */ + hfi1_tid_flow_page_template, + TP_PROTO(struct rvt_qp *qp, struct tid_rdma_flow *flow, u32 index, + char mtu8k, char v1, void *vaddr), + TP_ARGS(qp, flow, index, mtu8k, v1, vaddr), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(char, mtu8k) + __field(char, v1) + __field(u32, index) + __field(u64, page) + __field(u64, vaddr) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->mtu8k = mtu8k; + __entry->v1 = v1; + __entry->index = index; + __entry->page = vaddr ? (u64)virt_to_page(vaddr) : 0ULL; + __entry->vaddr = (u64)vaddr; + ), + TP_printk(/* print */ + "[%s] qpn 0x%x page[%u]: page 0x%llx %s 0x%llx", + __get_str(dev), + __entry->qpn, + __entry->index, + __entry->page, + __entry->mtu8k ? (__entry->v1 ? "v1" : "v0") : "vaddr", + __entry->vaddr + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_page_template, hfi1_tid_flow_page, + TP_PROTO(struct rvt_qp *qp, struct tid_rdma_flow *flow, u32 index, + char mtu8k, char v1, void *vaddr), + TP_ARGS(qp, flow, index, mtu8k, v1, vaddr) +); + +DECLARE_EVENT_CLASS(/* tid_pageset */ + hfi1_tid_pageset_template, + TP_PROTO(struct rvt_qp *qp, u32 index, u16 idx, u16 count), + TP_ARGS(qp, index, idx, count), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u32, index) + __field(u16, idx) + __field(u16, count) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->index = index; + __entry->idx = idx; + __entry->count = count; + ), + TP_printk(/* print */ + "[%s] qpn 0x%x list[%u]: idx %u count %u", + __get_str(dev), + __entry->qpn, + __entry->index, + __entry->idx, + __entry->count + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_pageset_template, hfi1_tid_pageset, + TP_PROTO(struct rvt_qp *qp, u32 index, u16 idx, u16 count), + TP_ARGS(qp, index, idx, count) +); + +DECLARE_EVENT_CLASS(/* tid_fow */ + hfi1_tid_flow_template, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(int, index) + __field(int, idx) + __field(u32, resp_ib_psn) + __field(u32, generation) + __field(u32, fspsn) + __field(u32, flpsn) + __field(u32, r_next_psn) + __field(u32, npagesets) + __field(u32, tnode_cnt) + __field(u32, tidcnt) + __field(u32, length) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->index = index; + __entry->idx = flow->idx; + __entry->resp_ib_psn = flow->flow_state.resp_ib_psn; + __entry->generation = flow->flow_state.generation; + __entry->fspsn = full_flow_psn(flow, + flow->flow_state.spsn); + __entry->flpsn = full_flow_psn(flow, + flow->flow_state.lpsn); + __entry->r_next_psn = flow->flow_state.r_next_psn; + __entry->npagesets = flow->npagesets; + __entry->tnode_cnt = flow->tnode_cnt; + __entry->tidcnt = flow->tidcnt; + __entry->length = flow->length; + ), + TP_printk(/* print */ + TID_FLOW_PRN, + __get_str(dev), + __entry->qpn, + __entry->index, + __entry->idx, + __entry->resp_ib_psn, + __entry->generation, + __entry->fspsn, + __entry->flpsn, + __entry->r_next_psn, + __entry->npagesets, + __entry->tnode_cnt, + __entry->tidcnt, + __entry->length + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_alloc, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DECLARE_EVENT_CLASS(/* tid_node */ + hfi1_tid_node_template, + TP_PROTO(struct rvt_qp *qp, const char *msg, u32 index, u32 base, + u8 map, u8 used, u8 cnt), + TP_ARGS(qp, msg, index, base, map, used, cnt), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __string(msg, msg) + __field(u32, index) + __field(u32, base) + __field(u8, map) + __field(u8, used) + __field(u8, cnt) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __assign_str(msg, msg); + __entry->index = index; + __entry->base = base; + __entry->map = map; + __entry->used = used; + __entry->cnt = cnt; + ), + TP_printk(/* print */ + TID_NODE_PRN, + __get_str(dev), + __entry->qpn, + __get_str(msg), + __entry->index, + __entry->base, + __entry->map, + __entry->used, + __entry->cnt + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_node_template, hfi1_tid_node_add, + TP_PROTO(struct rvt_qp *qp, const char *msg, u32 index, u32 base, + u8 map, u8 used, u8 cnt), + TP_ARGS(qp, msg, index, base, map, used, cnt) +); + +DECLARE_EVENT_CLASS(/* tid_entry */ + hfi1_tid_entry_template, + TP_PROTO(struct rvt_qp *qp, int index, u32 ent), + TP_ARGS(qp, index, ent), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(int, index) + __field(u8, ctrl) + __field(u16, idx) + __field(u16, len) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->index = index; + __entry->ctrl = hfi1_trace_get_tid_ctrl(ent); + __entry->idx = hfi1_trace_get_tid_idx(ent); + __entry->len = hfi1_trace_get_tid_len(ent); + ), + TP_printk(/* print */ + "[%s] qpn 0x%x TID entry %d: idx %u len %u ctrl 0x%x", + __get_str(dev), + __entry->qpn, + __entry->index, + __entry->idx, + __entry->len, + __entry->ctrl + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_entry_template, hfi1_tid_entry_alloc, + TP_PROTO(struct rvt_qp *qp, int index, u32 entry), + TP_ARGS(qp, index, entry) +); + #endif /* __HFI1_TRACE_TID_H */ #undef TRACE_INCLUDE_PATH From 742a3826cf82395e304df99f6494d04b0dd03a84 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:30:40 -0800 Subject: [PATCH 1137/4277] IB/hfi1: Add functions to build TID RDMA READ request This patch adds the helper functions to build the TID RDMA READ request on the requester side. The key is to allocate TID resources (TID flow and TID entries) and send the resource information to the responder side along with the read request. Since the TID resources are limited, each TID RDMA READ request has to be split into segments with a default segment size of 256K. A software flow is allocated to track the data transaction for each segment. The work request opcode, packet opcode, and packet formats for TID RDMA READ protocol are also defined in this patch. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 200 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 32 +++++ drivers/infiniband/hw/hfi1/verbs.h | 9 ++ include/rdma/ib_hdrs.h | 9 +- include/rdma/tid_rdma_defs.h | 52 +++++++ 5 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 include/rdma/tid_rdma_defs.h diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 506b5a59ded5..56c8c10b5a85 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -6,11 +6,27 @@ #include "hfi.h" #include "qp.h" +#include "rc.h" #include "verbs.h" #include "tid_rdma.h" #include "exp_rcv.h" #include "trace.h" +/** + * DOC: TID RDMA READ protocol + * + * This is an end-to-end protocol at the hfi1 level between two nodes that + * improves performance by avoiding data copy on the requester side. It + * converts a qualified RDMA READ request into a TID RDMA READ request on + * the requester side and thereafter handles the request and response + * differently. To be qualified, the RDMA READ request should meet the + * following: + * -- The total data length should be greater than 256K; + * -- The total data length should be a multiple of 4K page size; + * -- Each local scatter-gather entry should be 4K page aligned; + * -- Each local scatter-gather entry should be a multiple of 4K page size; + */ + #define RCV_TID_FLOW_TABLE_CTRL_FLOW_VALID_SMASK BIT_ULL(32) #define RCV_TID_FLOW_TABLE_CTRL_HDR_SUPP_EN_SMASK BIT_ULL(33) #define RCV_TID_FLOW_TABLE_CTRL_KEEP_AFTER_SEQ_ERR_SMASK BIT_ULL(34) @@ -18,6 +34,9 @@ #define RCV_TID_FLOW_TABLE_STATUS_SEQ_MISMATCH_SMASK BIT_ULL(37) #define RCV_TID_FLOW_TABLE_STATUS_GEN_MISMATCH_SMASK BIT_ULL(38) +/* Maximum number of packets within a flow generation. */ +#define MAX_TID_FLOW_PSN BIT(HFI1_KDETH_BTH_SEQ_SHIFT) + #define GENERATION_MASK 0xFFFFF static u32 mask_generation(u32 a) @@ -45,6 +64,9 @@ static u32 mask_generation(u32 a) #define MAX_EXPECTED_PAGES (MAX_EXPECTED_BUFFER / PAGE_SIZE) +#define TID_RDMA_DESTQP_FLOW_SHIFT 11 +#define TID_RDMA_DESTQP_FLOW_MASK 0x1f + #define TID_OPFN_QP_CTXT_MASK 0xff #define TID_OPFN_QP_CTXT_SHIFT 56 #define TID_OPFN_QP_KDETH_MASK 0xff @@ -1597,3 +1619,181 @@ u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry, return dd->verbs_dev.n_tidwait; } + +/* TID RDMA READ functions */ +u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, u32 *bth1, + u32 *bth2, u32 *len) +{ + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct tid_rdma_flow *flow = &req->flows[req->flow_idx]; + struct rvt_qp *qp = req->qp; + struct hfi1_qp_priv *qpriv = qp->priv; + struct hfi1_swqe_priv *wpriv = wqe->priv; + struct tid_rdma_read_req *rreq = &ohdr->u.tid_rdma.r_req; + struct tid_rdma_params *remote; + u32 req_len = 0; + void *req_addr = NULL; + + /* This is the IB psn used to send the request */ + *bth2 = mask_psn(flow->flow_state.ib_spsn + flow->pkt); + + /* TID Entries for TID RDMA READ payload */ + req_addr = &flow->tid_entry[flow->tid_idx]; + req_len = sizeof(*flow->tid_entry) * + (flow->tidcnt - flow->tid_idx); + + memset(&ohdr->u.tid_rdma.r_req, 0, sizeof(ohdr->u.tid_rdma.r_req)); + wpriv->ss.sge.vaddr = req_addr; + wpriv->ss.sge.sge_length = req_len; + wpriv->ss.sge.length = wpriv->ss.sge.sge_length; + /* + * We can safely zero these out. Since the first SGE covers the + * entire packet, nothing else should even look at the MR. + */ + wpriv->ss.sge.mr = NULL; + wpriv->ss.sge.m = 0; + wpriv->ss.sge.n = 0; + + wpriv->ss.sg_list = NULL; + wpriv->ss.total_len = wpriv->ss.sge.sge_length; + wpriv->ss.num_sge = 1; + + /* Construct the TID RDMA READ REQ packet header */ + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + + KDETH_RESET(rreq->kdeth0, KVER, 0x1); + KDETH_RESET(rreq->kdeth1, JKEY, remote->jkey); + rreq->reth.vaddr = cpu_to_be64(wqe->rdma_wr.remote_addr + + req->cur_seg * req->seg_len + flow->sent); + rreq->reth.rkey = cpu_to_be32(wqe->rdma_wr.rkey); + rreq->reth.length = cpu_to_be32(*len); + rreq->tid_flow_psn = + cpu_to_be32((flow->flow_state.generation << + HFI1_KDETH_BTH_SEQ_SHIFT) | + ((flow->flow_state.spsn + flow->pkt) & + HFI1_KDETH_BTH_SEQ_MASK)); + rreq->tid_flow_qp = + cpu_to_be32(qpriv->tid_rdma.local.qp | + ((flow->idx & TID_RDMA_DESTQP_FLOW_MASK) << + TID_RDMA_DESTQP_FLOW_SHIFT) | + qpriv->rcd->ctxt); + rreq->verbs_qp = cpu_to_be32(qp->remote_qpn); + *bth1 &= ~RVT_QPN_MASK; + *bth1 |= remote->qp; + *bth2 |= IB_BTH_REQ_ACK; + rcu_read_unlock(); + + /* We are done with this segment */ + flow->sent += *len; + req->cur_seg++; + qp->s_state = TID_OP(READ_REQ); + req->ack_pending++; + req->flow_idx = (req->flow_idx + 1) & (MAX_FLOWS - 1); + qpriv->pending_tid_r_segs++; + qp->s_num_rd_atomic++; + + /* Set the TID RDMA READ request payload size */ + *len = req_len; + + return sizeof(ohdr->u.tid_rdma.r_req) / sizeof(u32); +} + +/* + * @len: contains the data length to read upon entry and the read request + * payload length upon exit. + */ +u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, u32 *bth1, + u32 *bth2, u32 *len) + __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct tid_rdma_flow *flow = NULL; + u32 hdwords = 0; + bool last; + bool retry = true; + u32 npkts = rvt_div_round_up_mtu(qp, *len); + + /* + * Check sync conditions. Make sure that there are no pending + * segments before freeing the flow. + */ +sync_check: + if (req->state == TID_REQUEST_SYNC) { + if (qpriv->pending_tid_r_segs) + goto done; + + hfi1_kern_clear_hw_flow(req->rcd, qp); + req->state = TID_REQUEST_ACTIVE; + } + + /* + * If the request for this segment is resent, the tid resources should + * have been allocated before. In this case, req->flow_idx should + * fall behind req->setup_head. + */ + if (req->flow_idx == req->setup_head) { + retry = false; + if (req->state == TID_REQUEST_RESEND) { + /* + * This is the first new segment for a request whose + * earlier segments have been re-sent. We need to + * set up the sge pointer correctly. + */ + restart_sge(&qp->s_sge, wqe, req->s_next_psn, + qp->pmtu); + req->isge = 0; + req->state = TID_REQUEST_ACTIVE; + } + + /* + * Check sync. The last PSN of each generation is reserved for + * RESYNC. + */ + if ((qpriv->flow_state.psn + npkts) > MAX_TID_FLOW_PSN - 1) { + req->state = TID_REQUEST_SYNC; + goto sync_check; + } + + /* Allocate the flow if not yet */ + if (hfi1_kern_setup_hw_flow(qpriv->rcd, qp)) + goto done; + + /* + * The following call will advance req->setup_head after + * allocating the tid entries. + */ + if (hfi1_kern_exp_rcv_setup(req, &qp->s_sge, &last)) { + req->state = TID_REQUEST_QUEUED; + + /* + * We don't have resources for this segment. The QP has + * already been queued. + */ + goto done; + } + } + + /* req->flow_idx should only be one slot behind req->setup_head */ + flow = &req->flows[req->flow_idx]; + flow->pkt = 0; + flow->tid_idx = 0; + flow->sent = 0; + if (!retry) { + /* Set the first and last IB PSN for the flow in use.*/ + flow->flow_state.ib_spsn = req->s_next_psn; + flow->flow_state.ib_lpsn = + flow->flow_state.ib_spsn + flow->npkts - 1; + } + + /* Calculate the next segment start psn.*/ + req->s_next_psn += flow->npkts; + + /* Build the packet header */ + hdwords = hfi1_build_tid_rdma_read_packet(wqe, ohdr, bth1, bth2, len); +done: + return hdwords; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 3dbeaa8cb5b3..f692f3ff9419 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -45,6 +45,19 @@ struct tid_flow_state { u8 flags; }; +enum tid_rdma_req_state { + TID_REQUEST_INACTIVE = 0, + TID_REQUEST_INIT, + TID_REQUEST_INIT_RESEND, + TID_REQUEST_ACTIVE, + TID_REQUEST_RESEND, + TID_REQUEST_RESEND_ACTIVE, + TID_REQUEST_QUEUED, + TID_REQUEST_SYNC, + TID_REQUEST_RNR_NAK, + TID_REQUEST_COMPLETE, +}; + struct tid_rdma_request { struct rvt_qp *qp; struct hfi1_ctxtdata *rcd; @@ -60,8 +73,13 @@ struct tid_rdma_request { u16 flow_idx; /* flow index most recently set up */ u32 seg_len; + u32 s_next_psn; /* IB PSN of next segment start for read */ + u32 cur_seg; /* index of current segment */ u32 isge; /* index of "current" sge */ + u32 ack_pending; /* num acks pending for this request */ + + enum tid_rdma_req_state state; }; /* @@ -77,6 +95,10 @@ struct flow_state { u32 spsn; /* starting PSN in TID space */ u32 lpsn; /* last PSN in TID space */ u32 r_next_psn; /* next PSN to be received (in TID space) */ + + /* For tid rdma read */ + u32 ib_spsn; /* starting PSN in Verbs space */ + u32 ib_lpsn; /* last PSn in Verbs space */ }; struct tid_rdma_pageset { @@ -110,11 +132,14 @@ struct tid_rdma_flow { struct flow_state flow_state; struct tid_rdma_request *req; u32 length; + u32 sent; u8 tnode_cnt; u8 tidcnt; + u8 tid_idx; u8 idx; u8 npagesets; u8 npkts; + u8 pkt; struct kern_tid_node tnode[TID_RDMA_MAX_PAGES]; struct tid_rdma_pageset pagesets[TID_RDMA_MAX_PAGES]; u32 tid_entry[TID_RDMA_MAX_PAGES]; @@ -159,4 +184,11 @@ struct cntr_entry; u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry, void *context, int vl, int mode, u64 data); +u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, + u32 *bth1, u32 *bth2, u32 *len); +u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, u32 *bth1, + u32 *bth2, u32 *len); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 20729454f181..2965b0957855 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -170,12 +170,16 @@ struct hfi1_qp_priv { struct rvt_qp *owner; u8 hdr_type; /* 9B or 16B */ unsigned long tid_timer_timeout_jiffies; + + /* For TID RDMA READ */ + u32 pending_tid_r_segs; /* Num of pending tid read segments */ u16 pkts_ps; /* packets per segment */ u8 timeout_shift; /* account for number of packets per segment */ }; struct hfi1_swqe_priv { struct tid_rdma_request tid_req; + struct rvt_sge_state ss; /* Used for TID RDMA READ Request */ }; struct hfi1_ack_priv { @@ -331,6 +335,11 @@ static inline u32 delta_psn(u32 a, u32 b) return (((int)a - (int)b) << PSN_SHIFT) >> PSN_SHIFT; } +static inline struct tid_rdma_request *wqe_to_tid_req(struct rvt_swqe *wqe) +{ + return &((struct hfi1_swqe_priv *)wqe->priv)->tid_req; +} + /* * Look through all the active flows for a TID RDMA request and find * the one (if it exists) that contains the specified PSN. diff --git a/include/rdma/ib_hdrs.h b/include/rdma/ib_hdrs.h index 6e35416170a3..58a0a0f99e7f 100644 --- a/include/rdma/ib_hdrs.h +++ b/include/rdma/ib_hdrs.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2016 Intel Corporation. + * Copyright(c) 2016 - 2018 Intel Corporation. * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -100,6 +100,8 @@ struct ib_atomic_eth { __be64 compare_data; /* potentially unaligned */ } __packed; +#include + union ib_ehdrs { struct { __be32 deth[2]; @@ -117,6 +119,11 @@ union ib_ehdrs { __be32 aeth; __be32 ieth; struct ib_atomic_eth atomic_eth; + /* TID RDMA headers */ + union { + struct tid_rdma_read_req r_req; + struct tid_rdma_read_resp r_rsp; + } tid_rdma; } __packed; struct ib_other_headers { diff --git a/include/rdma/tid_rdma_defs.h b/include/rdma/tid_rdma_defs.h new file mode 100644 index 000000000000..1c431ea32b52 --- /dev/null +++ b/include/rdma/tid_rdma_defs.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2018 Intel Corporation. + * + */ + +#ifndef TID_RDMA_DEFS_H +#define TID_RDMA_DEFS_H + +#include + +struct tid_rdma_read_req { + __le32 kdeth0; + __le32 kdeth1; + struct ib_reth reth; + __be32 tid_flow_psn; + __be32 tid_flow_qp; + __be32 verbs_qp; +}; + +struct tid_rdma_read_resp { + __le32 kdeth0; + __le32 kdeth1; + __be32 aeth; + __be32 reserved[4]; + __be32 verbs_psn; + __be32 verbs_qp; +}; + +/* + * TID RDMA Opcodes + */ +#define IB_OPCODE_TID_RDMA 0xe0 +enum { + IB_OPCODE_READ_REQ = 0x4, + IB_OPCODE_READ_RESP = 0x5, + + IB_OPCODE(TID_RDMA, READ_REQ), + IB_OPCODE(TID_RDMA, READ_RESP), +}; + +#define TID_OP(x) IB_OPCODE_TID_RDMA_##x + +/* + * Define TID RDMA specific WR opcodes. The ib_wr_opcode + * enum already provides some reserved values for use by + * low level drivers. Two of those are used but renamed + * to be more descriptive. + */ +#define IB_WR_TID_RDMA_READ IB_WR_RESERVED2 + +#endif /* TID_RDMA_DEFS_H */ From 6b6cf9357f78057292ae662438f441cb84f93a25 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:30:51 -0800 Subject: [PATCH 1138/4277] IB/hfi1: Set PbcInsertHcrc for TID RDMA packets All TID RDMA packets are in KDETH packet format and therefore the PbcInsertHcrc must be set properly before sending the packet to hardware. Otherwise, the packets will be dropped by the receiver. By default, HCRC is not inserted for 9B packets without KDETH, and this patch adds that back for TID RDMA packets. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/verbs.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 02c1873a976c..8887a71edb98 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -308,7 +308,7 @@ static inline opcode_handler qp_ok(struct hfi1_packet *packet) static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc) { #ifdef CONFIG_FAULT_INJECTION - if ((opcode & IB_OPCODE_MSP) == IB_OPCODE_MSP) + if ((opcode & IB_OPCODE_MSP) == IB_OPCODE_MSP) { /* * In order to drop non-IB traffic we * set PbcInsertHrc to NONE (0x2). @@ -319,8 +319,9 @@ static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc) * packet will not be delivered to the * correct context. */ + pbc &= ~PBC_INSERT_HCRC_SMASK; pbc |= (u64)PBC_IHCRC_NONE << PBC_INSERT_HCRC_SHIFT; - else + } else { /* * In order to drop regular verbs * traffic we set the PbcTestEbp @@ -330,6 +331,7 @@ static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc) * triggered and will be dropped. */ pbc |= PBC_TEST_EBP; + } #endif return pbc; } @@ -683,6 +685,15 @@ bail_txadd: return ret; } +static u64 update_hcrc(u8 opcode, u64 pbc) +{ + if ((opcode & IB_OPCODE_TID_RDMA) == IB_OPCODE_TID_RDMA) { + pbc &= ~PBC_INSERT_HCRC_SMASK; + pbc |= (u64)PBC_IHCRC_LKDETH << PBC_INSERT_HCRC_SHIFT; + } + return pbc; +} + int hfi1_verbs_send_dma(struct rvt_qp *qp, struct hfi1_pkt_state *ps, u64 pbc) { @@ -728,6 +739,9 @@ int hfi1_verbs_send_dma(struct rvt_qp *qp, struct hfi1_pkt_state *ps, qp->srate_mbps, vl, plen); + + /* Update HCRC based on packet opcode */ + pbc = update_hcrc(ps->opcode, pbc); } tx->wqe = qp->s_wqe; ret = build_verbs_tx_desc(tx->sde, len, tx, ahg_info, pbc); @@ -876,6 +890,9 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps, if (unlikely(hfi1_dbg_should_fault_tx(qp, ps->opcode))) pbc = hfi1_fault_tx(qp, ps->opcode, pbc); pbc = create_pbc(ppd, pbc, qp->srate_mbps, vl, plen); + + /* Update HCRC based on packet opcode */ + pbc = update_hcrc(ps->opcode, pbc); } if (cb) iowait_pio_inc(&priv->s_iowait); From d0d564a1caacc7f3f28f3e351ed89ed000e2de75 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:31:02 -0800 Subject: [PATCH 1139/4277] IB/hfi1: Add functions to receive TID RDMA READ request This patch adds the functions to receive TID RDMA READ request. The TID resource information will be stored and tracked. Duplicate request will also be handled properly. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 329 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 8 + drivers/infiniband/hw/hfi1/verbs.h | 5 + 3 files changed, 342 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 56c8c10b5a85..d8a46b7ddca0 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -1797,3 +1797,332 @@ sync_check: done: return hdwords; } + +/* + * Validate and accept the TID RDMA READ request parameters. + * Return 0 if the request is accepted successfully; + * Return 1 otherwise. + */ +static int tid_rdma_rcv_read_request(struct rvt_qp *qp, + struct rvt_ack_entry *e, + struct hfi1_packet *packet, + struct ib_other_headers *ohdr, + u32 bth0, u32 psn, u64 vaddr, u32 len) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + u32 flow_psn, i, tidlen = 0, pktlen, tlen; + + req = ack_to_tid_req(e); + + /* Validate the payload first */ + flow = &req->flows[req->setup_head]; + + /* payload length = packet length - (header length + ICRC length) */ + pktlen = packet->tlen - (packet->hlen + 4); + if (pktlen > sizeof(flow->tid_entry)) + return 1; + memcpy(flow->tid_entry, packet->ebuf, pktlen); + flow->tidcnt = pktlen / sizeof(*flow->tid_entry); + + /* + * Walk the TID_ENTRY list to make sure we have enough space for a + * complete segment. Also calculate the number of required packets. + */ + flow->npkts = rvt_div_round_up_mtu(qp, len); + for (i = 0; i < flow->tidcnt; i++) { + tlen = EXP_TID_GET(flow->tid_entry[i], LEN); + if (!tlen) + return 1; + + /* + * For tid pair (tidctr == 3), the buffer size of the pair + * should be the sum of the buffer size described by each + * tid entry. However, only the first entry needs to be + * specified in the request (see WFR HAS Section 8.5.7.1). + */ + tidlen += tlen; + } + if (tidlen * PAGE_SIZE < len) + return 1; + + /* Empty the flow array */ + req->clear_tail = req->setup_head; + flow->pkt = 0; + flow->tid_idx = 0; + flow->tid_offset = 0; + flow->sent = 0; + flow->tid_qpn = be32_to_cpu(ohdr->u.tid_rdma.r_req.tid_flow_qp); + flow->idx = (flow->tid_qpn >> TID_RDMA_DESTQP_FLOW_SHIFT) & + TID_RDMA_DESTQP_FLOW_MASK; + flow_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_req.tid_flow_psn)); + flow->flow_state.generation = flow_psn >> HFI1_KDETH_BTH_SEQ_SHIFT; + flow->flow_state.spsn = flow_psn & HFI1_KDETH_BTH_SEQ_MASK; + flow->length = len; + + flow->flow_state.lpsn = flow->flow_state.spsn + + flow->npkts - 1; + flow->flow_state.ib_spsn = psn; + flow->flow_state.ib_lpsn = flow->flow_state.ib_spsn + flow->npkts - 1; + + /* Set the initial flow index to the current flow. */ + req->flow_idx = req->setup_head; + + /* advance circular buffer head */ + req->setup_head = (req->setup_head + 1) & (MAX_FLOWS - 1); + + /* + * Compute last PSN for request. + */ + e->opcode = (bth0 >> 24) & 0xff; + e->psn = psn; + e->lpsn = psn + flow->npkts - 1; + e->sent = 0; + + req->n_flows = qpriv->tid_rdma.local.max_read; + req->state = TID_REQUEST_ACTIVE; + req->cur_seg = 0; + req->comp_seg = 0; + req->ack_seg = 0; + req->isge = 0; + req->seg_len = qpriv->tid_rdma.local.max_len; + req->total_len = len; + req->total_segs = 1; + req->r_flow_psn = e->psn; + + return 0; +} + +static int tid_rdma_rcv_error(struct hfi1_packet *packet, + struct ib_other_headers *ohdr, + struct rvt_qp *qp, u32 psn, int diff) +{ + struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct hfi1_ctxtdata *rcd = ((struct hfi1_qp_priv *)qp->priv)->rcd; + struct rvt_ack_entry *e; + struct tid_rdma_request *req; + unsigned long flags; + u8 prev; + bool old_req; + + if (diff > 0) { + /* sequence error */ + if (!qp->r_nak_state) { + ibp->rvp.n_rc_seqnak++; + qp->r_nak_state = IB_NAK_PSN_ERROR; + qp->r_ack_psn = qp->r_psn; + rc_defered_ack(rcd, qp); + } + goto done; + } + + ibp->rvp.n_rc_dupreq++; + + spin_lock_irqsave(&qp->s_lock, flags); + e = find_prev_entry(qp, psn, &prev, NULL, &old_req); + if (!e || e->opcode != TID_OP(READ_REQ)) + goto unlock; + + req = ack_to_tid_req(e); + req->r_flow_psn = psn; + + if (e->opcode == TID_OP(READ_REQ)) { + struct ib_reth *reth; + u32 offset; + u32 len; + u32 rkey; + u64 vaddr; + int ok; + u32 bth0; + + reth = &ohdr->u.tid_rdma.r_req.reth; + /* + * The requester always restarts from the start of the original + * request. + */ + offset = delta_psn(psn, e->psn) * qp->pmtu; + len = be32_to_cpu(reth->length); + if (psn != e->psn || len != req->total_len) + goto unlock; + + if (e->rdma_sge.mr) { + rvt_put_mr(e->rdma_sge.mr); + e->rdma_sge.mr = NULL; + } + + rkey = be32_to_cpu(reth->rkey); + vaddr = get_ib_reth_vaddr(reth); + + qp->r_len = len; + ok = rvt_rkey_ok(qp, &e->rdma_sge, len, vaddr, rkey, + IB_ACCESS_REMOTE_READ); + if (unlikely(!ok)) + goto unlock; + + /* + * If all the response packets for the current request have + * been sent out and this request is complete (old_request + * == false) and the TID flow may be unusable (the + * req->clear_tail is advanced). However, when an earlier + * request is received, this request will not be complete any + * more (qp->s_tail_ack_queue is moved back, see below). + * Consequently, we need to update the TID flow info everytime + * a duplicate request is received. + */ + bth0 = be32_to_cpu(ohdr->bth[0]); + if (tid_rdma_rcv_read_request(qp, e, packet, ohdr, bth0, psn, + vaddr, len)) + goto unlock; + + /* + * True if the request is already scheduled (between + * qp->s_tail_ack_queue and qp->r_head_ack_queue); + */ + if (old_req) + goto unlock; + } + /* Re-process old requests.*/ + qp->s_tail_ack_queue = prev; + /* + * Since the qp->s_tail_ack_queue is modified, the + * qp->s_ack_state must be changed to re-initialize + * qp->s_ack_rdma_sge; Otherwise, we will end up in + * wrong memory region. + */ + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->r_state = e->opcode; + qp->r_nak_state = 0; + qp->s_flags |= RVT_S_RESP_PENDING; + hfi1_schedule_send(qp); +unlock: + spin_unlock_irqrestore(&qp->s_lock, flags); +done: + return 1; +} + +void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet) +{ + /* HANDLER FOR TID RDMA READ REQUEST packet (Responder side)*/ + + /* + * 1. Verify TID RDMA READ REQ as per IB_OPCODE_RC_RDMA_READ + * (see hfi1_rc_rcv()) + * 2. Put TID RDMA READ REQ into the response queueu (s_ack_queue) + * - Setup struct tid_rdma_req with request info + * - Initialize struct tid_rdma_flow info; + * - Copy TID entries; + * 3. Set the qp->s_ack_state. + * 4. Set RVT_S_RESP_PENDING in s_flags. + * 5. Kick the send engine (hfi1_schedule_send()) + */ + struct hfi1_ctxtdata *rcd = packet->rcd; + struct rvt_qp *qp = packet->qp; + struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_ack_entry *e; + unsigned long flags; + struct ib_reth *reth; + struct hfi1_qp_priv *qpriv = qp->priv; + u32 bth0, psn, len, rkey; + bool is_fecn; + u8 next; + u64 vaddr; + int diff; + u8 nack_state = IB_NAK_INVALID_REQUEST; + + bth0 = be32_to_cpu(ohdr->bth[0]); + if (hfi1_ruc_check_hdr(ibp, packet)) + return; + + is_fecn = process_ecn(qp, packet); + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + + if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) + rvt_comm_est(qp); + + if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ))) + goto nack_inv; + + reth = &ohdr->u.tid_rdma.r_req.reth; + vaddr = be64_to_cpu(reth->vaddr); + len = be32_to_cpu(reth->length); + /* The length needs to be in multiples of PAGE_SIZE */ + if (!len || len & ~PAGE_MASK || len > qpriv->tid_rdma.local.max_len) + goto nack_inv; + + diff = delta_psn(psn, qp->r_psn); + if (unlikely(diff)) { + if (tid_rdma_rcv_error(packet, ohdr, qp, psn, diff)) + return; + goto send_ack; + } + + /* We've verified the request, insert it into the ack queue. */ + next = qp->r_head_ack_queue + 1; + if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) + next = 0; + spin_lock_irqsave(&qp->s_lock, flags); + if (unlikely(next == qp->s_tail_ack_queue)) { + if (!qp->s_ack_queue[next].sent) { + nack_state = IB_NAK_REMOTE_OPERATIONAL_ERROR; + goto nack_inv_unlock; + } + update_ack_queue(qp, next); + } + e = &qp->s_ack_queue[qp->r_head_ack_queue]; + if (e->rdma_sge.mr) { + rvt_put_mr(e->rdma_sge.mr); + e->rdma_sge.mr = NULL; + } + + rkey = be32_to_cpu(reth->rkey); + qp->r_len = len; + + if (unlikely(!rvt_rkey_ok(qp, &e->rdma_sge, qp->r_len, vaddr, + rkey, IB_ACCESS_REMOTE_READ))) + goto nack_acc; + + /* Accept the request parameters */ + if (tid_rdma_rcv_read_request(qp, e, packet, ohdr, bth0, psn, vaddr, + len)) + goto nack_inv_unlock; + + qp->r_state = e->opcode; + qp->r_nak_state = 0; + /* + * We need to increment the MSN here instead of when we + * finish sending the result since a duplicate request would + * increment it more than once. + */ + qp->r_msn++; + qp->r_psn += e->lpsn - e->psn + 1; + + qp->r_head_ack_queue = next; + + /* Schedule the send tasklet. */ + qp->s_flags |= RVT_S_RESP_PENDING; + hfi1_schedule_send(qp); + + spin_unlock_irqrestore(&qp->s_lock, flags); + if (is_fecn) + goto send_ack; + return; + +nack_inv_unlock: + spin_unlock_irqrestore(&qp->s_lock, flags); +nack_inv: + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + qp->r_nak_state = nack_state; + qp->r_ack_psn = qp->r_psn; + /* Queue NAK for later */ + rc_defered_ack(rcd, qp); + return; +nack_acc: + spin_unlock_irqrestore(&qp->s_lock, flags); + rvt_rc_error(qp, IB_WC_LOC_PROT_ERR); + qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; + qp->r_ack_psn = qp->r_psn; +send_ack: + hfi1_send_rc_ack(packet, is_fecn); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index f692f3ff9419..439329398ccc 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -73,9 +73,14 @@ struct tid_rdma_request { u16 flow_idx; /* flow index most recently set up */ u32 seg_len; + u32 total_len; + u32 r_flow_psn; /* IB PSN of next segment start */ u32 s_next_psn; /* IB PSN of next segment start for read */ + u32 total_segs; /* segments required to complete a request */ u32 cur_seg; /* index of current segment */ + u32 comp_seg; /* index of last completed segment */ + u32 ack_seg; /* index of last ack'ed segment */ u32 isge; /* index of "current" sge */ u32 ack_pending; /* num acks pending for this request */ @@ -131,6 +136,8 @@ struct tid_rdma_flow { */ struct flow_state flow_state; struct tid_rdma_request *req; + u32 tid_qpn; + u32 tid_offset; u32 length; u32 sent; u8 tnode_cnt; @@ -190,5 +197,6 @@ u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe, u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe, struct ib_other_headers *ohdr, u32 *bth1, u32 *bth2, u32 *len); +void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet); #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 2965b0957855..5e910c508360 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -340,6 +340,11 @@ static inline struct tid_rdma_request *wqe_to_tid_req(struct rvt_swqe *wqe) return &((struct hfi1_swqe_priv *)wqe->priv)->tid_req; } +static inline struct tid_rdma_request *ack_to_tid_req(struct rvt_ack_entry *e) +{ + return &((struct hfi1_ack_priv *)e->priv)->tid_req; +} + /* * Look through all the active flows for a TID RDMA request and find * the one (if it exists) that contains the specified PSN. From 1db21b50502856c1da5628e3644bd29710e928f0 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:31:12 -0800 Subject: [PATCH 1140/4277] IB/hfi1: Add a function to build TID RDMA READ response This patch adds the function to build TID RDMA READ response packet. The previously received TID resource information will be used to build the KDETH packet, which will direct the delivery of packet payload by hardware. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 67 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 3 ++ 2 files changed, 70 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index d8a46b7ddca0..888954a79dac 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2126,3 +2126,70 @@ nack_acc: send_ack: hfi1_send_rc_ack(packet, is_fecn); } + +u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_other_headers *ohdr, u32 *bth0, + u32 *bth1, u32 *bth2, u32 *len, bool *last) +{ + struct hfi1_ack_priv *epriv = e->priv; + struct tid_rdma_request *req = &epriv->tid_req; + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_flow *flow = &req->flows[req->clear_tail]; + u32 tidentry = flow->tid_entry[flow->tid_idx]; + u32 tidlen = EXP_TID_GET(tidentry, LEN) << PAGE_SHIFT; + struct tid_rdma_read_resp *resp = &ohdr->u.tid_rdma.r_rsp; + u32 next_offset, om = KDETH_OM_LARGE; + bool last_pkt; + u32 hdwords = 0; + struct tid_rdma_params *remote; + + *len = min_t(u32, qp->pmtu, tidlen - flow->tid_offset); + flow->sent += *len; + next_offset = flow->tid_offset + *len; + last_pkt = (flow->sent >= flow->length); + + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + if (!remote) { + rcu_read_unlock(); + goto done; + } + KDETH_RESET(resp->kdeth0, KVER, 0x1); + KDETH_SET(resp->kdeth0, SH, !last_pkt); + KDETH_SET(resp->kdeth0, INTR, !!(!last_pkt && remote->urg)); + KDETH_SET(resp->kdeth0, TIDCTRL, EXP_TID_GET(tidentry, CTRL)); + KDETH_SET(resp->kdeth0, TID, EXP_TID_GET(tidentry, IDX)); + KDETH_SET(resp->kdeth0, OM, om == KDETH_OM_LARGE); + KDETH_SET(resp->kdeth0, OFFSET, flow->tid_offset / om); + KDETH_RESET(resp->kdeth1, JKEY, remote->jkey); + resp->verbs_qp = cpu_to_be32(qp->remote_qpn); + rcu_read_unlock(); + + resp->aeth = rvt_compute_aeth(qp); + resp->verbs_psn = cpu_to_be32(mask_psn(flow->flow_state.ib_spsn + + flow->pkt)); + + *bth0 = TID_OP(READ_RESP) << 24; + *bth1 = flow->tid_qpn; + *bth2 = mask_psn(((flow->flow_state.spsn + flow->pkt++) & + HFI1_KDETH_BTH_SEQ_MASK) | + (flow->flow_state.generation << + HFI1_KDETH_BTH_SEQ_SHIFT)); + *last = last_pkt; + if (last_pkt) + /* Advance to next flow */ + req->clear_tail = (req->clear_tail + 1) & + (MAX_FLOWS - 1); + + if (next_offset >= tidlen) { + flow->tid_offset = 0; + flow->tid_idx++; + } else { + flow->tid_offset = next_offset; + } + + hdwords = sizeof(ohdr->u.tid_rdma.r_rsp) / sizeof(u32); + +done: + return hdwords; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 439329398ccc..01ded7c0c302 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -198,5 +198,8 @@ u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe, struct ib_other_headers *ohdr, u32 *bth1, u32 *bth2, u32 *len); void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet); +u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_other_headers *ohdr, u32 *bth0, + u32 *bth1, u32 *bth2, u32 *len, bool *last); #endif /* HFI1_TID_RDMA_H */ From 9905bf06e890c2a845ac8fd19d7e6b8987ef8df6 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Tue, 5 Feb 2019 14:13:30 -0800 Subject: [PATCH 1141/4277] IB/hfi1: Add functions to receive TID RDMA READ response This patch adds the functions to receive TID RDMA READ response. The TID resource information in the KDETH packet header will direct the hardware to deliver the packet payload to the user buffer automatically and the software will handle the packet header for the last packet of a segment as all other packet headers are suppressed by default. The TID entries will be freed when all packets for a segment have been received. This patch also adds the functions to handle KDETH eflag errors, including flow sequence and generation errors, when a TID RDMA READ response packet is received . The flow sequence error can be recovered by software checking of the flow sequence and will disappear when the hardware flow is programmed with a new generation number. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 521 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 6 + drivers/infiniband/hw/hfi1/verbs.h | 2 + 3 files changed, 529 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 888954a79dac..da8b63ec0f8d 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -67,6 +67,8 @@ static u32 mask_generation(u32 a) #define TID_RDMA_DESTQP_FLOW_SHIFT 11 #define TID_RDMA_DESTQP_FLOW_MASK 0x1f +#define TID_FLOW_SW_PSN BIT(0) + #define TID_OPFN_QP_CTXT_MASK 0xff #define TID_OPFN_QP_CTXT_SHIFT 56 #define TID_OPFN_QP_KDETH_MASK 0xff @@ -1620,6 +1622,34 @@ u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry, return dd->verbs_dev.n_tidwait; } +static struct tid_rdma_flow * +__find_flow_ranged(struct tid_rdma_request *req, u16 head, u16 tail, + u32 psn, u16 *fidx) +{ + for ( ; CIRC_CNT(head, tail, MAX_FLOWS); + tail = CIRC_NEXT(tail, MAX_FLOWS)) { + struct tid_rdma_flow *flow = &req->flows[tail]; + u32 spsn, lpsn; + + spsn = full_flow_psn(flow, flow->flow_state.spsn); + lpsn = full_flow_psn(flow, flow->flow_state.lpsn); + + if (cmp_psn(psn, spsn) >= 0 && cmp_psn(psn, lpsn) <= 0) { + if (fidx) + *fidx = tail; + return flow; + } + } + return NULL; +} + +static struct tid_rdma_flow *find_flow(struct tid_rdma_request *req, + u32 psn, u16 *fidx) +{ + return __find_flow_ranged(req, req->setup_head, req->clear_tail, psn, + fidx); +} + /* TID RDMA READ functions */ u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe, struct ib_other_headers *ohdr, u32 *bth1, @@ -2193,3 +2223,494 @@ u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, done: return hdwords; } + +static inline struct tid_rdma_request * +find_tid_request(struct rvt_qp *qp, u32 psn, enum ib_wr_opcode opcode) + __must_hold(&qp->s_lock) +{ + struct rvt_swqe *wqe; + struct tid_rdma_request *req = NULL; + u32 i, end; + + end = qp->s_cur + 1; + if (end == qp->s_size) + end = 0; + for (i = qp->s_acked; i != end;) { + wqe = rvt_get_swqe_ptr(qp, i); + if (cmp_psn(psn, wqe->psn) >= 0 && + cmp_psn(psn, wqe->lpsn) <= 0) { + if (wqe->wr.opcode == opcode) + req = wqe_to_tid_req(wqe); + break; + } + if (++i == qp->s_size) + i = 0; + } + + return req; +} + +void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet) +{ + /* HANDLER FOR TID RDMA READ RESPONSE packet (Requestor side */ + + /* + * 1. Find matching SWQE + * 2. Check that the entire segment has been read. + * 3. Remove HFI1_S_WAIT_TID_RESP from s_flags. + * 4. Free the TID flow resources. + * 5. Kick the send engine (hfi1_schedule_send()) + */ + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_ctxtdata *rcd = packet->rcd; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + u32 opcode, aeth; + bool is_fecn; + unsigned long flags; + u32 kpsn, ipsn; + + is_fecn = process_ecn(qp, packet); + kpsn = mask_psn(be32_to_cpu(ohdr->bth[2])); + aeth = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.aeth); + opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff; + + spin_lock_irqsave(&qp->s_lock, flags); + ipsn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn)); + req = find_tid_request(qp, ipsn, IB_WR_TID_RDMA_READ); + if (unlikely(!req)) + goto ack_op_err; + + flow = &req->flows[req->clear_tail]; + /* When header suppression is disabled */ + if (cmp_psn(ipsn, flow->flow_state.ib_lpsn)) + goto ack_done; + req->ack_pending--; + priv->pending_tid_r_segs--; + qp->s_num_rd_atomic--; + if ((qp->s_flags & RVT_S_WAIT_FENCE) && + !qp->s_num_rd_atomic) { + qp->s_flags &= ~(RVT_S_WAIT_FENCE | + RVT_S_WAIT_ACK); + hfi1_schedule_send(qp); + } + if (qp->s_flags & RVT_S_WAIT_RDMAR) { + qp->s_flags &= ~(RVT_S_WAIT_RDMAR | RVT_S_WAIT_ACK); + hfi1_schedule_send(qp); + } + + /* Release the tid resources */ + hfi1_kern_exp_rcv_clear(req); + + if (!do_rc_ack(qp, aeth, ipsn, opcode, 0, rcd)) + goto ack_done; + + /* If not done yet, build next read request */ + if (++req->comp_seg >= req->total_segs) { + priv->tid_r_comp++; + req->state = TID_REQUEST_COMPLETE; + } + + /* + * Clear the hw flow under two conditions: + * 1. This request is a sync point and it is complete; + * 2. Current request is completed and there are no more requests. + */ + if ((req->state == TID_REQUEST_SYNC && + req->comp_seg == req->cur_seg) || + priv->tid_r_comp == priv->tid_r_reqs) { + hfi1_kern_clear_hw_flow(priv->rcd, qp); + if (req->state == TID_REQUEST_SYNC) + req->state = TID_REQUEST_ACTIVE; + } + + hfi1_schedule_send(qp); + goto ack_done; + +ack_op_err: + /* + * The test indicates that the send engine has finished its cleanup + * after sending the request and it's now safe to put the QP into error + * state. However, if the wqe queue is empty (qp->s_acked == qp->s_tail + * == qp->s_head), it would be unsafe to complete the wqe pointed by + * qp->s_acked here. Putting the qp into error state will safely flush + * all remaining requests. + */ + if (qp->s_last == qp->s_acked) + rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); + +ack_done: + spin_unlock_irqrestore(&qp->s_lock, flags); + if (is_fecn) + hfi1_send_rc_ack(packet, is_fecn); +} + +void hfi1_kern_read_tid_flow_free(struct rvt_qp *qp) + __must_hold(&qp->s_lock) +{ + u32 n = qp->s_acked; + struct rvt_swqe *wqe; + struct tid_rdma_request *req; + struct hfi1_qp_priv *priv = qp->priv; + + lockdep_assert_held(&qp->s_lock); + /* Free any TID entries */ + while (n != qp->s_tail) { + wqe = rvt_get_swqe_ptr(qp, n); + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { + req = wqe_to_tid_req(wqe); + hfi1_kern_exp_rcv_clear_all(req); + } + + if (++n == qp->s_size) + n = 0; + } + /* Free flow */ + hfi1_kern_clear_hw_flow(priv->rcd, qp); +} + +static bool tid_rdma_tid_err(struct hfi1_ctxtdata *rcd, + struct hfi1_packet *packet, u8 rcv_type, + u8 opcode) +{ + struct rvt_qp *qp = packet->qp; + u32 ipsn; + struct ib_other_headers *ohdr = packet->ohdr; + + if (rcv_type >= RHF_RCV_TYPE_IB) + goto done; + + spin_lock(&qp->s_lock); + /* + * For TID READ response, error out QP after freeing the tid + * resources. + */ + if (opcode == TID_OP(READ_RESP)) { + ipsn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn)); + if (cmp_psn(ipsn, qp->s_last_psn) > 0 && + cmp_psn(ipsn, qp->s_psn) < 0) { + hfi1_kern_read_tid_flow_free(qp); + spin_unlock(&qp->s_lock); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + goto done; + } + } + + spin_unlock(&qp->s_lock); +done: + return true; +} + +static void restart_tid_rdma_read_req(struct hfi1_ctxtdata *rcd, + struct rvt_qp *qp, struct rvt_swqe *wqe) +{ + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + + /* Start from the right segment */ + qp->r_flags |= RVT_R_RDMAR_SEQ; + req = wqe_to_tid_req(wqe); + flow = &req->flows[req->clear_tail]; + hfi1_restart_rc(qp, flow->flow_state.ib_spsn, 0); + if (list_empty(&qp->rspwait)) { + qp->r_flags |= RVT_R_RSP_SEND; + rvt_get_qp(qp); + list_add_tail(&qp->rspwait, &rcd->qp_wait_list); + } +} + +/* + * Handle the KDETH eflags for TID RDMA READ response. + * + * Return true if the last packet for a segment has been received and it is + * time to process the response normally; otherwise, return true. + * + * The caller must hold the packet->qp->r_lock and the rcu_read_lock. + */ +static bool handle_read_kdeth_eflags(struct hfi1_ctxtdata *rcd, + struct hfi1_packet *packet, u8 rcv_type, + u8 rte, u32 psn, u32 ibpsn) + __must_hold(&packet->qp->r_lock) __must_hold(RCU) +{ + struct hfi1_pportdata *ppd = rcd->ppd; + struct hfi1_devdata *dd = ppd->dd; + struct hfi1_ibport *ibp; + struct rvt_swqe *wqe; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + u32 ack_psn; + struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *priv = qp->priv; + bool ret = true; + int diff = 0; + u32 fpsn; + + lockdep_assert_held(&qp->r_lock); + /* If the psn is out of valid range, drop the packet */ + if (cmp_psn(ibpsn, qp->s_last_psn) < 0 || + cmp_psn(ibpsn, qp->s_psn) > 0) + return ret; + + spin_lock(&qp->s_lock); + /* + * Note that NAKs implicitly ACK outstanding SEND and RDMA write + * requests and implicitly NAK RDMA read and atomic requests issued + * before the NAK'ed request. + */ + ack_psn = ibpsn - 1; + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); + ibp = to_iport(qp->ibqp.device, qp->port_num); + + /* Complete WQEs that the PSN finishes. */ + while ((int)delta_psn(ack_psn, wqe->lpsn) >= 0) { + /* + * If this request is a RDMA read or atomic, and the NACK is + * for a later operation, this NACK NAKs the RDMA read or + * atomic. + */ + if (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_READ || + wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || + wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) { + /* Retry this request. */ + if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) { + qp->r_flags |= RVT_R_RDMAR_SEQ; + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { + restart_tid_rdma_read_req(rcd, qp, + wqe); + } else { + hfi1_restart_rc(qp, qp->s_last_psn + 1, + 0); + if (list_empty(&qp->rspwait)) { + qp->r_flags |= RVT_R_RSP_SEND; + rvt_get_qp(qp); + list_add_tail(/* wait */ + &qp->rspwait, + &rcd->qp_wait_list); + } + } + } + /* + * No need to process the NAK since we are + * restarting an earlier request. + */ + break; + } + + wqe = do_rc_completion(qp, wqe, ibp); + if (qp->s_acked == qp->s_tail) + break; + } + + /* Handle the eflags for the request */ + if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) + goto s_unlock; + + req = wqe_to_tid_req(wqe); + switch (rcv_type) { + case RHF_RCV_TYPE_EXPECTED: + switch (rte) { + case RHF_RTE_EXPECTED_FLOW_SEQ_ERR: + /* + * On the first occurrence of a Flow Sequence error, + * the flag TID_FLOW_SW_PSN is set. + * + * After that, the flow is *not* reprogrammed and the + * protocol falls back to SW PSN checking. This is done + * to prevent continuous Flow Sequence errors for any + * packets that could be still in the fabric. + */ + flow = find_flow(req, psn, NULL); + if (!flow) { + /* + * We can't find the IB PSN matching the + * received KDETH PSN. The only thing we can + * do at this point is report the error to + * the QP. + */ + hfi1_kern_read_tid_flow_free(qp); + spin_unlock(&qp->s_lock); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + return ret; + } + if (priv->flow_state.flags & TID_FLOW_SW_PSN) { + diff = cmp_psn(psn, + priv->flow_state.r_next_psn); + if (diff > 0) { + if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) + restart_tid_rdma_read_req(rcd, + qp, + wqe); + + /* Drop the packet.*/ + goto s_unlock; + } else if (diff < 0) { + /* + * If a response packet for a restarted + * request has come back, reset the + * restart flag. + */ + if (qp->r_flags & RVT_R_RDMAR_SEQ) + qp->r_flags &= + ~RVT_R_RDMAR_SEQ; + + /* Drop the packet.*/ + goto s_unlock; + } + + /* + * If SW PSN verification is successful and + * this is the last packet in the segment, tell + * the caller to process it as a normal packet. + */ + fpsn = full_flow_psn(flow, + flow->flow_state.lpsn); + if (cmp_psn(fpsn, psn) == 0) { + ret = false; + if (qp->r_flags & RVT_R_RDMAR_SEQ) + qp->r_flags &= + ~RVT_R_RDMAR_SEQ; + } + priv->flow_state.r_next_psn++; + } else { + u64 reg; + u32 last_psn; + + /* + * The only sane way to get the amount of + * progress is to read the HW flow state. + */ + reg = read_uctxt_csr(dd, rcd->ctxt, + RCV_TID_FLOW_TABLE + + (8 * flow->idx)); + last_psn = mask_psn(reg); + + priv->flow_state.r_next_psn = last_psn; + priv->flow_state.flags |= TID_FLOW_SW_PSN; + /* + * If no request has been restarted yet, + * restart the current one. + */ + if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) + restart_tid_rdma_read_req(rcd, qp, + wqe); + } + + break; + + case RHF_RTE_EXPECTED_FLOW_GEN_ERR: + /* + * Since the TID flow is able to ride through + * generation mismatch, drop this stale packet. + */ + break; + + default: + break; + } + break; + + case RHF_RCV_TYPE_ERROR: + switch (rte) { + case RHF_RTE_ERROR_OP_CODE_ERR: + case RHF_RTE_ERROR_KHDR_MIN_LEN_ERR: + case RHF_RTE_ERROR_KHDR_HCRC_ERR: + case RHF_RTE_ERROR_KHDR_KVER_ERR: + case RHF_RTE_ERROR_CONTEXT_ERR: + case RHF_RTE_ERROR_KHDR_TID_ERR: + default: + break; + } + default: + break; + } +s_unlock: + spin_unlock(&qp->s_lock); + return ret; +} + +bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, + struct hfi1_pportdata *ppd, + struct hfi1_packet *packet) +{ + struct hfi1_ibport *ibp = &ppd->ibport_data; + struct hfi1_devdata *dd = ppd->dd; + struct rvt_dev_info *rdi = &dd->verbs_dev.rdi; + u8 rcv_type = rhf_rcv_type(packet->rhf); + u8 rte = rhf_rcv_type_err(packet->rhf); + struct ib_header *hdr = packet->hdr; + struct ib_other_headers *ohdr = NULL; + int lnh = be16_to_cpu(hdr->lrh[0]) & 3; + u16 lid = be16_to_cpu(hdr->lrh[1]); + u8 opcode; + u32 qp_num, psn, ibpsn; + struct rvt_qp *qp; + unsigned long flags; + bool ret = true; + + if (packet->rhf & (RHF_VCRC_ERR | RHF_ICRC_ERR)) + return ret; + + packet->ohdr = &hdr->u.oth; + ohdr = packet->ohdr; + trace_input_ibhdr(rcd->dd, packet, !!(rhf_dc_info(packet->rhf))); + + /* Get the destination QP number. */ + qp_num = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_qp) & + RVT_QPN_MASK; + if (lid >= be16_to_cpu(IB_MULTICAST_LID_BASE)) + goto drop; + + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff; + + rcu_read_lock(); + qp = rvt_lookup_qpn(rdi, &ibp->rvp, qp_num); + if (!qp) + goto rcu_unlock; + + packet->qp = qp; + + /* Check for valid receive state. */ + spin_lock_irqsave(&qp->r_lock, flags); + if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) { + ibp->rvp.n_pkt_drops++; + goto r_unlock; + } + + if (packet->rhf & RHF_TID_ERR) { + /* For TIDERR and RC QPs preemptively schedule a NAK */ + u32 tlen = rhf_pkt_len(packet->rhf); /* in bytes */ + + /* Sanity check packet */ + if (tlen < 24) + goto r_unlock; + + /* + * Check for GRH. We should never get packets with GRH in this + * path. + */ + if (lnh == HFI1_LRH_GRH) + goto r_unlock; + + if (tid_rdma_tid_err(rcd, packet, rcv_type, opcode)) + goto r_unlock; + } + + /* handle TID RDMA READ */ + if (opcode == TID_OP(READ_RESP)) { + ibpsn = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn); + ibpsn = mask_psn(ibpsn); + ret = handle_read_kdeth_eflags(rcd, packet, rcv_type, rte, psn, + ibpsn); + } + +r_unlock: + spin_unlock_irqrestore(&qp->r_lock, flags); +rcu_unlock: + rcu_read_unlock(); +drop: + return ret; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 01ded7c0c302..d428236aef68 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -177,6 +177,8 @@ static inline void trdma_clean_swqe(struct rvt_qp *qp, struct rvt_swqe *wqe) __trdma_clean_swqe(qp, wqe); } +void hfi1_kern_read_tid_flow_free(struct rvt_qp *qp); + int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, struct ib_qp_init_attr *init_attr); void hfi1_qp_priv_tid_free(struct rvt_dev_info *rdi, struct rvt_qp *qp); @@ -201,5 +203,9 @@ void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet); u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, struct ib_other_headers *ohdr, u32 *bth0, u32 *bth1, u32 *bth2, u32 *len, bool *last); +void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet); +bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, + struct hfi1_pportdata *ppd, + struct hfi1_packet *packet); #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 5e910c508360..c1574c0ed9a7 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -172,6 +172,8 @@ struct hfi1_qp_priv { unsigned long tid_timer_timeout_jiffies; /* For TID RDMA READ */ + u32 tid_r_reqs; /* Num of tid reads requested */ + u32 tid_r_comp; /* Num of tid reads completed */ u32 pending_tid_r_segs; /* Num of pending tid read segments */ u16 pkts_ps; /* packets per segment */ u8 timeout_shift; /* account for number of packets per segment */ From 22d136d7566f193fc67cdfd9228aaa7d122a3a45 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Thu, 24 Jan 2019 06:36:34 -0800 Subject: [PATCH 1142/4277] IB/hfi1: Add TID RDMA handlers This commit adds the TID RDMA READ pointers to the receiving opcode handlers. It also adds TID RDMA READ header sizes to header size table. A function to print the RHF EFLAGS errors is created so that it can be shared by both IB and TID RDMA receiving functions. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/driver.c | 58 ++++++++----- drivers/infiniband/hw/hfi1/hfi.h | 2 +- drivers/infiniband/hw/hfi1/verbs.c | 125 ++++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/verbs.h | 4 + 4 files changed, 167 insertions(+), 22 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/driver.c b/drivers/infiniband/hw/hfi1/driver.c index a8ad70730203..2a9d2912f5db 100644 --- a/drivers/infiniband/hw/hfi1/driver.c +++ b/drivers/infiniband/hw/hfi1/driver.c @@ -1575,25 +1575,32 @@ drop: return -EINVAL; } -void handle_eflags(struct hfi1_packet *packet) +static void show_eflags_errs(struct hfi1_packet *packet) { struct hfi1_ctxtdata *rcd = packet->rcd; u32 rte = rhf_rcv_type_err(packet->rhf); + dd_dev_err(rcd->dd, + "receive context %d: rhf 0x%016llx, errs [ %s%s%s%s%s%s%s%s] rte 0x%x\n", + rcd->ctxt, packet->rhf, + packet->rhf & RHF_K_HDR_LEN_ERR ? "k_hdr_len " : "", + packet->rhf & RHF_DC_UNC_ERR ? "dc_unc " : "", + packet->rhf & RHF_DC_ERR ? "dc " : "", + packet->rhf & RHF_TID_ERR ? "tid " : "", + packet->rhf & RHF_LEN_ERR ? "len " : "", + packet->rhf & RHF_ECC_ERR ? "ecc " : "", + packet->rhf & RHF_VCRC_ERR ? "vcrc " : "", + packet->rhf & RHF_ICRC_ERR ? "icrc " : "", + rte); +} + +void handle_eflags(struct hfi1_packet *packet) +{ + struct hfi1_ctxtdata *rcd = packet->rcd; + rcv_hdrerr(rcd, rcd->ppd, packet); if (rhf_err_flags(packet->rhf)) - dd_dev_err(rcd->dd, - "receive context %d: rhf 0x%016llx, errs [ %s%s%s%s%s%s%s%s] rte 0x%x\n", - rcd->ctxt, packet->rhf, - packet->rhf & RHF_K_HDR_LEN_ERR ? "k_hdr_len " : "", - packet->rhf & RHF_DC_UNC_ERR ? "dc_unc " : "", - packet->rhf & RHF_DC_ERR ? "dc " : "", - packet->rhf & RHF_TID_ERR ? "tid " : "", - packet->rhf & RHF_LEN_ERR ? "len " : "", - packet->rhf & RHF_ECC_ERR ? "ecc " : "", - packet->rhf & RHF_VCRC_ERR ? "vcrc " : "", - packet->rhf & RHF_ICRC_ERR ? "icrc " : "", - rte); + show_eflags_errs(packet); } /* @@ -1699,11 +1706,14 @@ static int kdeth_process_expected(struct hfi1_packet *packet) if (unlikely(hfi1_dbg_should_fault_rx(packet))) return RHF_RCV_CONTINUE; - if (unlikely(rhf_err_flags(packet->rhf))) - handle_eflags(packet); + if (unlikely(rhf_err_flags(packet->rhf))) { + struct hfi1_ctxtdata *rcd = packet->rcd; - dd_dev_err(packet->rcd->dd, - "Unhandled expected packet received. Dropping.\n"); + if (hfi1_handle_kdeth_eflags(rcd, rcd->ppd, packet)) + return RHF_RCV_CONTINUE; + } + + hfi1_kdeth_expected_rcv(packet); return RHF_RCV_CONTINUE; } @@ -1712,11 +1722,17 @@ static int kdeth_process_eager(struct hfi1_packet *packet) hfi1_setup_9B_packet(packet); if (unlikely(hfi1_dbg_should_fault_rx(packet))) return RHF_RCV_CONTINUE; - if (unlikely(rhf_err_flags(packet->rhf))) - handle_eflags(packet); - dd_dev_err(packet->rcd->dd, - "Unhandled eager packet received. Dropping.\n"); + trace_hfi1_rcvhdr(packet); + if (unlikely(rhf_err_flags(packet->rhf))) { + struct hfi1_ctxtdata *rcd = packet->rcd; + + show_eflags_errs(packet); + if (hfi1_handle_kdeth_eflags(rcd, rcd->ppd, packet)) + return RHF_RCV_CONTINUE; + } + + hfi1_kdeth_eager_rcv(packet); return RHF_RCV_CONTINUE; } diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 1412ed157c98..6582184cc985 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -2120,7 +2120,7 @@ static inline u64 hfi1_pkt_default_send_ctxt_mask(struct hfi1_devdata *dd, SEND_CTXT_CHECK_ENABLE_DISALLOW_PBC_TEST_SMASK | #endif HFI1_PKT_USER_SC_INTEGRITY; - else + else if (ctxt_type != SC_KERNEL) base_sc_integrity |= HFI1_PKT_KERNEL_SC_INTEGRITY; /* turn on send-side job key checks if !A0 */ diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 8887a71edb98..2d59fcde4db6 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -200,6 +200,8 @@ const u8 hdr_len_by_opcode[256] = { [IB_OPCODE_RC_FETCH_ADD] = 12 + 8 + 28, [IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE] = 12 + 8 + 4, [IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = 12 + 8 + 4, + [IB_OPCODE_TID_RDMA_READ_REQ] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_READ_RESP] = 12 + 8 + 36, /* UC */ [IB_OPCODE_UC_SEND_FIRST] = 12 + 8, [IB_OPCODE_UC_SEND_MIDDLE] = 12 + 8, @@ -243,6 +245,11 @@ static const opcode_handler opcode_handler_tbl[256] = { [IB_OPCODE_RC_FETCH_ADD] = &hfi1_rc_rcv, [IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE] = &hfi1_rc_rcv, [IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = &hfi1_rc_rcv, + + /* TID RDMA has separate handlers for different opcodes.*/ + [IB_OPCODE_TID_RDMA_READ_REQ] = &hfi1_rc_rcv_tid_rdma_read_req, + [IB_OPCODE_TID_RDMA_READ_RESP] = &hfi1_rc_rcv_tid_rdma_read_resp, + /* UC */ [IB_OPCODE_UC_SEND_FIRST] = &hfi1_uc_rcv, [IB_OPCODE_UC_SEND_MIDDLE] = &hfi1_uc_rcv, @@ -336,6 +343,124 @@ static u64 hfi1_fault_tx(struct rvt_qp *qp, u8 opcode, u64 pbc) return pbc; } +static opcode_handler tid_qp_ok(int opcode, struct hfi1_packet *packet) +{ + if (packet->qp->ibqp.qp_type != IB_QPT_RC || + !(ib_rvt_state_ops[packet->qp->state] & RVT_PROCESS_RECV_OK)) + return NULL; + if ((opcode & RVT_OPCODE_QP_MASK) == IB_OPCODE_TID_RDMA) + return opcode_handler_tbl[opcode]; + return NULL; +} + +void hfi1_kdeth_eager_rcv(struct hfi1_packet *packet) +{ + struct hfi1_ctxtdata *rcd = packet->rcd; + struct ib_header *hdr = packet->hdr; + u32 tlen = packet->tlen; + struct hfi1_pportdata *ppd = rcd->ppd; + struct hfi1_ibport *ibp = &ppd->ibport_data; + struct rvt_dev_info *rdi = &ppd->dd->verbs_dev.rdi; + opcode_handler opcode_handler; + unsigned long flags; + u32 qp_num; + int lnh; + u8 opcode; + + /* DW == LRH (2) + BTH (3) + KDETH (9) + CRC (1) */ + if (unlikely(tlen < 15 * sizeof(u32))) + goto drop; + + lnh = be16_to_cpu(hdr->lrh[0]) & 3; + if (lnh != HFI1_LRH_BTH) + goto drop; + + packet->ohdr = &hdr->u.oth; + trace_input_ibhdr(rcd->dd, packet, !!(rhf_dc_info(packet->rhf))); + + opcode = (be32_to_cpu(packet->ohdr->bth[0]) >> 24); + inc_opstats(tlen, &rcd->opstats->stats[opcode]); + + /* verbs_qp can be picked up from any tid_rdma header struct */ + qp_num = be32_to_cpu(packet->ohdr->u.tid_rdma.r_req.verbs_qp) & + RVT_QPN_MASK; + + rcu_read_lock(); + packet->qp = rvt_lookup_qpn(rdi, &ibp->rvp, qp_num); + if (!packet->qp) + goto drop_rcu; + spin_lock_irqsave(&packet->qp->r_lock, flags); + opcode_handler = tid_qp_ok(opcode, packet); + if (likely(opcode_handler)) + opcode_handler(packet); + else + goto drop_unlock; + spin_unlock_irqrestore(&packet->qp->r_lock, flags); + rcu_read_unlock(); + + return; +drop_unlock: + spin_unlock_irqrestore(&packet->qp->r_lock, flags); +drop_rcu: + rcu_read_unlock(); +drop: + ibp->rvp.n_pkt_drops++; +} + +void hfi1_kdeth_expected_rcv(struct hfi1_packet *packet) +{ + struct hfi1_ctxtdata *rcd = packet->rcd; + struct ib_header *hdr = packet->hdr; + u32 tlen = packet->tlen; + struct hfi1_pportdata *ppd = rcd->ppd; + struct hfi1_ibport *ibp = &ppd->ibport_data; + struct rvt_dev_info *rdi = &ppd->dd->verbs_dev.rdi; + opcode_handler opcode_handler; + unsigned long flags; + u32 qp_num; + int lnh; + u8 opcode; + + /* DW == LRH (2) + BTH (3) + KDETH (9) + CRC (1) */ + if (unlikely(tlen < 15 * sizeof(u32))) + goto drop; + + lnh = be16_to_cpu(hdr->lrh[0]) & 3; + if (lnh != HFI1_LRH_BTH) + goto drop; + + packet->ohdr = &hdr->u.oth; + trace_input_ibhdr(rcd->dd, packet, !!(rhf_dc_info(packet->rhf))); + + opcode = (be32_to_cpu(packet->ohdr->bth[0]) >> 24); + inc_opstats(tlen, &rcd->opstats->stats[opcode]); + + /* verbs_qp can be picked up from any tid_rdma header struct */ + qp_num = be32_to_cpu(packet->ohdr->u.tid_rdma.r_rsp.verbs_qp) & + RVT_QPN_MASK; + + rcu_read_lock(); + packet->qp = rvt_lookup_qpn(rdi, &ibp->rvp, qp_num); + if (!packet->qp) + goto drop_rcu; + spin_lock_irqsave(&packet->qp->r_lock, flags); + opcode_handler = tid_qp_ok(opcode, packet); + if (likely(opcode_handler)) + opcode_handler(packet); + else + goto drop_unlock; + spin_unlock_irqrestore(&packet->qp->r_lock, flags); + rcu_read_unlock(); + + return; +drop_unlock: + spin_unlock_irqrestore(&packet->qp->r_lock, flags); +drop_rcu: + rcu_read_unlock(); +drop: + ibp->rvp.n_pkt_drops++; +} + static int hfi1_do_pkey_check(struct hfi1_packet *packet) { struct hfi1_ctxtdata *rcd = packet->rcd; diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index c1574c0ed9a7..7642b59ad5d2 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -427,6 +427,10 @@ int hfi1_register_ib_device(struct hfi1_devdata *); void hfi1_unregister_ib_device(struct hfi1_devdata *); +void hfi1_kdeth_eager_rcv(struct hfi1_packet *packet); + +void hfi1_kdeth_expected_rcv(struct hfi1_packet *packet); + void hfi1_ib_rcv(struct hfi1_packet *packet); void hfi1_16B_rcv(struct hfi1_packet *packet); From b126078e8957f3aea4a44b8916f2f3752b5c392d Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:31:46 -0800 Subject: [PATCH 1143/4277] IB/hfi1: Add functions for restarting TID RDMA READ request This patch adds functions to retry TID RDMA READ request. Since TID RDMA READ request could be retried from any segment boundary, it requires a number of tracking fields in various structures and those fields should be reset properly. The qp->s_num_rd_atomic field is reset before retry and therefore should be incremented for each new or retried RDMA READ or atomic request. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 94 +++++++++++++++++++++------ drivers/infiniband/hw/hfi1/tid_rdma.c | 82 +++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 2 + 3 files changed, 158 insertions(+), 20 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 6e74cd3814b8..e478a0b93eb9 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -503,16 +503,14 @@ no_flow_control: * Don't allow more operations to be started * than the QP limits allow. */ - if (newreq) { - if (qp->s_num_rd_atomic >= - qp->s_max_rd_atomic) { - qp->s_flags |= RVT_S_WAIT_RDMAR; - goto bail; - } - qp->s_num_rd_atomic++; - if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) - qp->s_lsn++; + if (qp->s_num_rd_atomic >= + qp->s_max_rd_atomic) { + qp->s_flags |= RVT_S_WAIT_RDMAR; + goto bail; } + qp->s_num_rd_atomic++; + if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) + qp->s_lsn++; put_ib_reth_vaddr( wqe->rdma_wr.remote_addr, &ohdr->u.rc.reth); @@ -534,14 +532,12 @@ no_flow_control: * Don't allow more operations to be started * than the QP limits allow. */ - if (newreq) { - if (qp->s_num_rd_atomic >= - qp->s_max_rd_atomic) { - qp->s_flags |= RVT_S_WAIT_RDMAR; - goto bail; - } - qp->s_num_rd_atomic++; + if (qp->s_num_rd_atomic >= + qp->s_max_rd_atomic) { + qp->s_flags |= RVT_S_WAIT_RDMAR; + goto bail; } + qp->s_num_rd_atomic++; /* FALLTHROUGH */ case IB_WR_OPFN: @@ -970,6 +966,43 @@ void hfi1_send_rc_ack(struct hfi1_packet *packet, bool is_fecn) return; } +/** + * update_num_rd_atomic - update the qp->s_num_rd_atomic + * @qp: the QP + * @psn: the packet sequence number to restart at + * @wqe: the wqe + * + * This is called from reset_psn() to update qp->s_num_rd_atomic + * for the current wqe. + * Called at interrupt level with the QP s_lock held. + */ +static void update_num_rd_atomic(struct rvt_qp *qp, u32 psn, + struct rvt_swqe *wqe) +{ + u32 opcode = wqe->wr.opcode; + + if (opcode == IB_WR_RDMA_READ || + opcode == IB_WR_ATOMIC_CMP_AND_SWP || + opcode == IB_WR_ATOMIC_FETCH_AND_ADD) { + qp->s_num_rd_atomic++; + } else if (opcode == IB_WR_TID_RDMA_READ) { + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct hfi1_qp_priv *priv = qp->priv; + + if (cmp_psn(psn, wqe->lpsn) <= 0) { + u32 cur_seg; + + cur_seg = (psn - wqe->psn) / priv->pkts_ps; + req->ack_pending = cur_seg - req->comp_seg; + priv->pending_tid_r_segs += req->ack_pending; + qp->s_num_rd_atomic += req->ack_pending; + } else { + priv->pending_tid_r_segs += req->total_segs; + qp->s_num_rd_atomic += req->total_segs; + } + } +} + /** * reset_psn - reset the QP state to send starting from PSN * @qp: the QP @@ -984,9 +1017,12 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) u32 n = qp->s_acked; struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, n); u32 opcode; + struct hfi1_qp_priv *priv = qp->priv; lockdep_assert_held(&qp->s_lock); qp->s_cur = n; + priv->pending_tid_r_segs = 0; + qp->s_num_rd_atomic = 0; /* * If we are starting the request from the beginning, @@ -996,9 +1032,9 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) qp->s_state = OP(SEND_LAST); goto done; } + update_num_rd_atomic(qp, psn, wqe); /* Find the work request opcode corresponding to the given PSN. */ - opcode = wqe->wr.opcode; for (;;) { int diff; @@ -1008,8 +1044,11 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) break; wqe = rvt_get_swqe_ptr(qp, n); diff = cmp_psn(psn, wqe->psn); - if (diff < 0) + if (diff < 0) { + /* Point wqe back to the previous one*/ + wqe = rvt_get_swqe_ptr(qp, qp->s_cur); break; + } qp->s_cur = n; /* * If we are starting the request from the beginning, @@ -1019,8 +1058,10 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) qp->s_state = OP(SEND_LAST); goto done; } - opcode = wqe->wr.opcode; + + update_num_rd_atomic(qp, psn, wqe); } + opcode = wqe->wr.opcode; /* * Set the state to restart in the middle of a request. @@ -1042,6 +1083,10 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) qp->s_state = OP(RDMA_READ_RESPONSE_MIDDLE); break; + case IB_WR_TID_RDMA_READ: + qp->s_state = TID_OP(READ_RESP); + break; + default: /* * This case shouldn't happen since its only @@ -1095,6 +1140,14 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) wqe = do_rc_completion(qp, wqe, ibp); qp->s_flags &= ~RVT_S_WAIT_ACK; } else { + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { + struct tid_rdma_request *req; + + req = wqe_to_tid_req(wqe); + hfi1_kern_exp_rcv_clear_all(req); + hfi1_kern_clear_hw_flow(priv->rcd, qp); + } + rvt_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR); rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); @@ -1108,7 +1161,8 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) } ibp = to_iport(qp->ibqp.device, qp->port_num); - if (wqe->wr.opcode == IB_WR_RDMA_READ) + if (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_READ) ibp->rvp.n_rc_resends++; else ibp->rvp.n_rc_resends += delta_psn(qp->s_psn, psn); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index da8b63ec0f8d..f767c5c20566 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -1622,6 +1622,27 @@ u64 hfi1_access_sw_tid_wait(const struct cntr_entry *entry, return dd->verbs_dev.n_tidwait; } +static struct tid_rdma_flow *find_flow_ib(struct tid_rdma_request *req, + u32 psn, u16 *fidx) +{ + u16 head, tail; + struct tid_rdma_flow *flow; + + head = req->setup_head; + tail = req->clear_tail; + for ( ; CIRC_CNT(head, tail, MAX_FLOWS); + tail = CIRC_NEXT(tail, MAX_FLOWS)) { + flow = &req->flows[tail]; + if (cmp_psn(psn, flow->flow_state.ib_spsn) >= 0 && + cmp_psn(psn, flow->flow_state.ib_lpsn) <= 0) { + if (fidx) + *fidx = tail; + return flow; + } + } + return NULL; +} + static struct tid_rdma_flow * __find_flow_ranged(struct tid_rdma_request *req, u16 head, u16 tail, u32 psn, u16 *fidx) @@ -2714,3 +2735,64 @@ rcu_unlock: drop: return ret; } + +/* + * "Rewind" the TID request information. + * This means that we reset the state back to ACTIVE, + * find the proper flow, set the flow index to that flow, + * and reset the flow information. + */ +void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, + u32 *bth2) +{ + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct tid_rdma_flow *flow; + int diff; + u32 tididx = 0; + u16 fidx; + + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { + *bth2 = mask_psn(qp->s_psn); + flow = find_flow_ib(req, *bth2, &fidx); + if (!flow) + return; + } else { + return; + } + + diff = delta_psn(*bth2, flow->flow_state.ib_spsn); + + flow->sent = 0; + flow->pkt = 0; + flow->tid_idx = 0; + flow->tid_offset = 0; + if (diff) { + for (tididx = 0; tididx < flow->tidcnt; tididx++) { + u32 tidentry = flow->tid_entry[tididx], tidlen, + tidnpkts, npkts; + + flow->tid_offset = 0; + tidlen = EXP_TID_GET(tidentry, LEN) * PAGE_SIZE; + tidnpkts = rvt_div_round_up_mtu(qp, tidlen); + npkts = min_t(u32, diff, tidnpkts); + flow->pkt += npkts; + flow->sent += (npkts == tidnpkts ? tidlen : + npkts * qp->pmtu); + flow->tid_offset += npkts * qp->pmtu; + diff -= npkts; + if (!diff) + break; + } + } + + if (flow->tid_offset == + EXP_TID_GET(flow->tid_entry[tididx], LEN) * PAGE_SIZE) { + tididx++; + flow->tid_offset = 0; + } + flow->tid_idx = tididx; + /* Move flow_idx to correct index */ + req->flow_idx = fidx; + + req->state = TID_REQUEST_ACTIVE; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index d428236aef68..beb5982ce6ad 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -207,5 +207,7 @@ void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet); bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, struct hfi1_pportdata *ppd, struct hfi1_packet *packet); +void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, + u32 *bth2); #endif /* HFI1_TID_RDMA_H */ From 039cd3daf19b9acbf080054d765cbceac842b6a0 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:31:57 -0800 Subject: [PATCH 1144/4277] IB/hfi1: Increment the retry timeout value for TID RDMA READ request The RC retry timeout value is based on the estimated time for the response packet to come back. However, for TID RDMA READ request, due to the use of header suppression, the driver is normally not notified for each incoming response packet until the last TID RDMA READ response packet. Consequently, the retry timeout value should be extended to cover the transaction time for the entire length of a segment (default 256K) instead of that for a single packet. This patch addresses the issue by introducing new retry timer functions to account for multiple packets and wrapper functions for backward compatibility. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/sw/rdmavt/qp.c | 11 ++++++----- include/rdma/rdma_vt.h | 12 +++++++++--- include/rdma/rdmavt_qp.h | 6 +++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index c8e70cf69a8a..2769ebdf89fb 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -2393,11 +2393,12 @@ static inline unsigned long rvt_aeth_to_usec(u32 aeth) } /* - * rvt_add_retry_timer - add/start a retry timer + * rvt_add_retry_timer_ext - add/start a retry timer * @qp - the QP + * @shift - timeout shift to wait for multiple packets * add a retry timer on the QP */ -void rvt_add_retry_timer(struct rvt_qp *qp) +void rvt_add_retry_timer_ext(struct rvt_qp *qp, u8 shift) { struct ib_qp *ibqp = &qp->ibqp; struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); @@ -2405,11 +2406,11 @@ void rvt_add_retry_timer(struct rvt_qp *qp) lockdep_assert_held(&qp->s_lock); qp->s_flags |= RVT_S_TIMER; /* 4.096 usec. * (1 << qp->timeout) */ - qp->s_timer.expires = jiffies + qp->timeout_jiffies + - rdi->busy_jiffies; + qp->s_timer.expires = jiffies + rdi->busy_jiffies + + (qp->timeout_jiffies << shift); add_timer(&qp->s_timer); } -EXPORT_SYMBOL(rvt_add_retry_timer); +EXPORT_SYMBOL(rvt_add_retry_timer_ext); /** * rvt_add_rnr_timer - add/start an rnr timer diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index 168e40be183c..87d66c9630d7 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -574,9 +574,10 @@ static inline struct rvt_qp *rvt_lookup_qpn(struct rvt_dev_info *rdi, /** * rvt_mod_retry_timer - mod a retry timer * @qp - the QP + * @shift - timeout shift to wait for multiple packets * Modify a potentially already running retry timer */ -static inline void rvt_mod_retry_timer(struct rvt_qp *qp) +static inline void rvt_mod_retry_timer_ext(struct rvt_qp *qp, u8 shift) { struct ib_qp *ibqp = &qp->ibqp; struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); @@ -584,8 +585,13 @@ static inline void rvt_mod_retry_timer(struct rvt_qp *qp) lockdep_assert_held(&qp->s_lock); qp->s_flags |= RVT_S_TIMER; /* 4.096 usec. * (1 << qp->timeout) */ - mod_timer(&qp->s_timer, jiffies + qp->timeout_jiffies + - rdi->busy_jiffies); + mod_timer(&qp->s_timer, jiffies + rdi->busy_jiffies + + (qp->timeout_jiffies << shift)); +} + +static inline void rvt_mod_retry_timer(struct rvt_qp *qp) +{ + return rvt_mod_retry_timer_ext(qp, 0); } struct rvt_dev_info *rvt_alloc_device(size_t size, int nports); diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index 9095a0b71250..d8d88d023092 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -688,7 +688,11 @@ enum hrtimer_restart rvt_rc_rnr_retry(struct hrtimer *t); void rvt_add_rnr_timer(struct rvt_qp *qp, u32 aeth); void rvt_del_timers_sync(struct rvt_qp *qp); void rvt_stop_rc_timers(struct rvt_qp *qp); -void rvt_add_retry_timer(struct rvt_qp *qp); +void rvt_add_retry_timer_ext(struct rvt_qp *qp, u8 shift); +static inline void rvt_add_retry_timer(struct rvt_qp *qp) +{ + rvt_add_retry_timer_ext(qp, 0); +} void rvt_copy_sge(struct rvt_qp *qp, struct rvt_sge_state *ss, void *data, u32 length, From 24b11923da4c7dbf5690d3ac74710affaf564196 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:32:09 -0800 Subject: [PATCH 1145/4277] IB/hfi1: Integrate TID RDMA READ protocol into RC protocol This patch integrates the TID RDMA READ protocol into the IB RC protocol. This protocol is an end-to-end protocol between the hfi1 drivers on two OPA nodes that converts a qualified RDMA READ request into a TID RDMA READ request to avoid data copying on the requester side. The following codes are added in this patch: - Send the TID RDMA READ request; - Complete the TID RDMA READ send request; - Send the TID RDMA READ response; - Complete the TID RDMA READ request; Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/qp.c | 1 + drivers/infiniband/hw/hfi1/rc.c | 295 +++++++++++++++++++++++--- drivers/infiniband/hw/hfi1/tid_rdma.c | 33 +++ drivers/infiniband/hw/hfi1/tid_rdma.h | 1 + drivers/infiniband/hw/hfi1/verbs.c | 1 + 5 files changed, 307 insertions(+), 24 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index 69c38af49492..5fea7319167e 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -761,6 +761,7 @@ void quiesce_qp(struct rvt_qp *qp) void notify_qp_reset(struct rvt_qp *qp) { + hfi1_qp_kern_exp_rcv_clear_all(qp); qp->r_adefered = 0; clear_ahg(qp); diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index e478a0b93eb9..a5aacf8e5b93 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -112,12 +112,14 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, { struct rvt_ack_entry *e; u32 hwords; - u32 len; - u32 bth0, bth2; + u32 len = 0; + u32 bth0 = 0, bth2 = 0; u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT); int middle = 0; u32 pmtu = qp->pmtu; struct hfi1_qp_priv *priv = qp->priv; + bool last_pkt; + u32 delta; lockdep_assert_held(&qp->s_lock); /* Don't send an ACK if we aren't supposed to. */ @@ -190,6 +192,26 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, hwords++; qp->s_ack_rdma_psn = e->psn; bth2 = mask_psn(qp->s_ack_rdma_psn++); + } else if (e->opcode == TID_OP(READ_REQ)) { + /* + * If a TID RDMA read response is being resent and + * we haven't seen the duplicate request yet, + * then stop sending the remaining responses the + * responder has seen until the requester re-sends it. + */ + len = e->rdma_sge.sge_length; + if (len && !e->rdma_sge.mr) { + qp->s_tail_ack_queue = qp->r_head_ack_queue; + goto bail; + } + /* Copy SGE state in case we need to resend */ + ps->s_txreq->mr = e->rdma_sge.mr; + if (ps->s_txreq->mr) + rvt_get_mr(ps->s_txreq->mr); + qp->s_ack_rdma_sge.sge = e->rdma_sge; + qp->s_ack_rdma_sge.num_sge = 1; + qp->s_ack_state = TID_OP(READ_RESP); + goto read_resp; } else { /* COMPARE_SWAP or FETCH_ADD */ ps->s_txreq->ss = NULL; @@ -227,6 +249,28 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, bth2 = mask_psn(qp->s_ack_rdma_psn++); break; + case TID_OP(READ_RESP): +read_resp: + e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + ps->s_txreq->ss = &qp->s_ack_rdma_sge; + delta = hfi1_build_tid_rdma_read_resp(qp, e, ohdr, &bth0, + &bth1, &bth2, &len, + &last_pkt); + if (delta == 0) + goto error_qp; + hwords += delta; + if (last_pkt) { + e->sent = 1; + /* + * Increment qp->s_tail_ack_queue through s_ack_state + * transition. + */ + qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); + } + break; + case TID_OP(READ_REQ): + goto bail; + default: normal: /* @@ -256,7 +300,14 @@ normal: ps->s_txreq->hdr_dwords = hwords; hfi1_make_ruc_header(qp, ohdr, bth0, bth1, bth2, middle, ps); return 1; - +error_qp: + spin_unlock_irqrestore(&qp->s_lock, ps->flags); + spin_lock_irqsave(&qp->r_lock, ps->flags); + spin_lock(&qp->s_lock); + rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_lock, ps->flags); + spin_lock_irqsave(&qp->s_lock, ps->flags); bail: qp->s_ack_state = OP(ACKNOWLEDGE); /* @@ -283,16 +334,20 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) struct hfi1_qp_priv *priv = qp->priv; struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); struct ib_other_headers *ohdr; - struct rvt_sge_state *ss; + struct rvt_sge_state *ss = NULL; struct rvt_swqe *wqe; - u32 hwords; - u32 len; - u32 bth0 = 0, bth2; + struct hfi1_swqe_priv *wpriv; + struct tid_rdma_request *req = NULL; + /* header size in 32-bit words LRH+BTH = (8+12)/4. */ + u32 hwords = 5; + u32 len = 0; + u32 bth0 = 0, bth2 = 0; u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT); u32 pmtu = qp->pmtu; char newreq; int middle = 0; int delta; + struct tid_rdma_flow *flow = NULL; lockdep_assert_held(&qp->s_lock); ps->s_txreq = get_txreq(ps->dev, qp); @@ -334,8 +389,8 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) } clear_ahg(qp); wqe = rvt_get_swqe_ptr(qp, qp->s_last); - rvt_send_complete(qp, wqe, qp->s_last != qp->s_acked ? - IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR); + hfi1_trdma_send_complete(qp, wqe, qp->s_last != qp->s_acked ? + IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR); /* will get called again */ goto done_free_tx; } @@ -354,6 +409,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) /* Send a request. */ wqe = rvt_get_swqe_ptr(qp, qp->s_cur); +check_s_state: switch (qp->s_state) { default: if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_NEXT_SEND_OK)) @@ -375,9 +431,13 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) /* * If a fence is requested, wait for previous * RDMA read and atomic operations to finish. + * However, there is no need to guard against + * TID RDMA READ after TID RDMA READ. */ if ((wqe->wr.send_flags & IB_SEND_FENCE) && - qp->s_num_rd_atomic) { + qp->s_num_rd_atomic && + (wqe->wr.opcode != IB_WR_TID_RDMA_READ || + priv->pending_tid_r_segs < qp->s_num_rd_atomic)) { qp->s_flags |= RVT_S_WAIT_FENCE; goto bail; } @@ -526,6 +586,75 @@ no_flow_control: qp->s_cur = 0; break; + case IB_WR_TID_RDMA_READ: + wpriv = wqe->priv; + req = wqe_to_tid_req(wqe); + delta = cmp_psn(qp->s_psn, wqe->psn); + + /* + * Don't allow more operations to be started + * than the QP limits allow. We could get here under + * three conditions; (1) It's a new request; (2) We are + * sending the second or later segment of a request, + * but the qp->s_state is set to OP(RDMA_READ_REQUEST) + * when the last segment of a previous request is + * received just before this; (3) We are re-sending a + * request. + */ + if (qp->s_num_rd_atomic >= qp->s_max_rd_atomic) { + qp->s_flags |= RVT_S_WAIT_RDMAR; + goto bail; + } + if (newreq) { + struct tid_rdma_flow *flow = + &req->flows[req->setup_head]; + + /* + * Set up s_sge as it is needed for TID + * allocation. However, if the pages have been + * walked and mapped, skip it. An earlier try + * has failed to allocate the TID entries. + */ + if (!flow->npagesets) { + qp->s_sge.sge = wqe->sg_list[0]; + qp->s_sge.sg_list = wqe->sg_list + 1; + qp->s_sge.num_sge = wqe->wr.num_sge; + qp->s_sge.total_len = wqe->length; + qp->s_len = wqe->length; + req->isge = 0; + req->clear_tail = req->setup_head; + req->flow_idx = req->setup_head; + req->state = TID_REQUEST_ACTIVE; + } + } else if (delta == 0) { + /* Re-send a request */ + req->cur_seg = 0; + req->comp_seg = 0; + req->ack_pending = 0; + req->flow_idx = req->clear_tail; + req->state = TID_REQUEST_RESEND; + } + req->s_next_psn = qp->s_psn; + /* Read one segment at a time */ + len = min_t(u32, req->seg_len, + wqe->length - req->seg_len * req->cur_seg); + delta = hfi1_build_tid_rdma_read_req(qp, wqe, ohdr, + &bth1, &bth2, + &len); + if (delta <= 0) { + /* Wait for TID space */ + goto bail; + } + if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) + qp->s_lsn++; + hwords += delta; + ss = &wpriv->ss; + /* Check if this is the last segment */ + if (req->cur_seg >= req->total_segs && + ++qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: /* @@ -571,11 +700,13 @@ no_flow_control: default: goto bail; } - qp->s_sge.sge = wqe->sg_list[0]; - qp->s_sge.sg_list = wqe->sg_list + 1; - qp->s_sge.num_sge = wqe->wr.num_sge; - qp->s_sge.total_len = wqe->length; - qp->s_len = wqe->length; + if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) { + qp->s_sge.sge = wqe->sg_list[0]; + qp->s_sge.sg_list = wqe->sg_list + 1; + qp->s_sge.num_sge = wqe->wr.num_sge; + qp->s_sge.total_len = wqe->length; + qp->s_len = wqe->length; + } if (newreq) { qp->s_tail++; if (qp->s_tail >= qp->s_size) @@ -583,6 +714,8 @@ no_flow_control: } if (wqe->wr.opcode == IB_WR_RDMA_READ) qp->s_psn = wqe->lpsn + 1; + else if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) + qp->s_psn = req->s_next_psn; else qp->s_psn++; break; @@ -699,6 +832,99 @@ no_flow_control: if (qp->s_cur == qp->s_size) qp->s_cur = 0; break; + case TID_OP(READ_RESP): + if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) + goto bail; + /* This is used to restart a TID read request */ + req = wqe_to_tid_req(wqe); + wpriv = wqe->priv; + /* + * Back down. The field qp->s_psn has been set to the psn with + * which the request should be restart. It's OK to use division + * as this is on the retry path. + */ + req->cur_seg = delta_psn(qp->s_psn, wqe->psn) / priv->pkts_ps; + + /* + * The following function need to be redefined to return the + * status to make sure that we find the flow. At the same + * time, we can use the req->state change to check if the + * call succeeds or not. + */ + req->state = TID_REQUEST_RESEND; + hfi1_tid_rdma_restart_req(qp, wqe, &bth2); + if (req->state != TID_REQUEST_ACTIVE) { + /* + * Failed to find the flow. Release all allocated tid + * resources. + */ + hfi1_kern_exp_rcv_clear_all(req); + hfi1_kern_clear_hw_flow(priv->rcd, qp); + + hfi1_trdma_send_complete(qp, wqe, IB_WC_LOC_QP_OP_ERR); + goto bail; + } + req->state = TID_REQUEST_RESEND; + len = min_t(u32, req->seg_len, + wqe->length - req->seg_len * req->cur_seg); + flow = &req->flows[req->flow_idx]; + len -= flow->sent; + req->s_next_psn = flow->flow_state.ib_lpsn + 1; + delta = hfi1_build_tid_rdma_read_packet(wqe, ohdr, &bth1, + &bth2, &len); + if (delta <= 0) { + /* Wait for TID space */ + goto bail; + } + hwords += delta; + ss = &wpriv->ss; + /* Check if this is the last segment */ + if (req->cur_seg >= req->total_segs && + ++qp->s_cur == qp->s_size) + qp->s_cur = 0; + qp->s_psn = req->s_next_psn; + break; + case TID_OP(READ_REQ): + req = wqe_to_tid_req(wqe); + delta = cmp_psn(qp->s_psn, wqe->psn); + /* + * If the current WR is not TID RDMA READ, or this is the start + * of a new request, we need to change the qp->s_state so that + * the request can be set up properly. + */ + if (wqe->wr.opcode != IB_WR_TID_RDMA_READ || delta == 0 || + qp->s_cur == qp->s_tail) { + qp->s_state = OP(RDMA_READ_REQUEST); + if (delta == 0 || qp->s_cur == qp->s_tail) + goto check_s_state; + else + goto bail; + } + + /* Rate limiting */ + if (qp->s_num_rd_atomic >= qp->s_max_rd_atomic) { + qp->s_flags |= RVT_S_WAIT_RDMAR; + goto bail; + } + + wpriv = wqe->priv; + /* Read one segment at a time */ + len = min_t(u32, req->seg_len, + wqe->length - req->seg_len * req->cur_seg); + delta = hfi1_build_tid_rdma_read_req(qp, wqe, ohdr, &bth1, + &bth2, &len); + if (delta <= 0) { + /* Wait for TID space */ + goto bail; + } + hwords += delta; + ss = &wpriv->ss; + /* Check if this is the last segment */ + if (req->cur_seg >= req->total_segs && + ++qp->s_cur == qp->s_size) + qp->s_cur = 0; + qp->s_psn = req->s_next_psn; + break; } qp->s_sending_hpsn = bth2; delta = delta_psn(bth2, wqe->psn); @@ -1148,8 +1374,8 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) hfi1_kern_clear_hw_flow(priv->rcd, qp); } - rvt_send_complete(qp, wqe, - IB_WC_RETRY_EXC_ERR); + hfi1_trdma_send_complete(qp, wqe, + IB_WC_RETRY_EXC_ERR); rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); } return; @@ -1189,7 +1415,8 @@ static void reset_sending_psn(struct rvt_qp *qp, u32 psn) for (;;) { wqe = rvt_get_swqe_ptr(qp, n); if (cmp_psn(psn, wqe->lpsn) <= 0) { - if (wqe->wr.opcode == IB_WR_RDMA_READ) + if (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_READ) qp->s_sending_psn = wqe->lpsn + 1; else qp->s_sending_psn = psn + 1; @@ -1238,8 +1465,9 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah) } opcode = ib_bth_get_opcode(ohdr); - if (opcode >= OP(RDMA_READ_RESPONSE_FIRST) && - opcode <= OP(ATOMIC_ACKNOWLEDGE)) { + if ((opcode >= OP(RDMA_READ_RESPONSE_FIRST) && + opcode <= OP(ATOMIC_ACKNOWLEDGE)) || + opcode == TID_OP(READ_RESP)) { WARN_ON(!qp->s_rdma_ack_cnt); qp->s_rdma_ack_cnt--; return; @@ -1255,8 +1483,12 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah) if ((psn & IB_BTH_REQ_ACK) && qp->s_acked != qp->s_tail && !(qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) && - (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) - rvt_add_retry_timer(qp); + (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) { + if (opcode == TID_OP(READ_REQ)) + rvt_add_retry_timer_ext(qp, priv->timeout_shift); + else + rvt_add_retry_timer(qp); + } while (qp->s_last != qp->s_acked) { u32 s_last; @@ -1265,6 +1497,7 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah) if (cmp_psn(wqe->lpsn, qp->s_sending_psn) >= 0 && cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) <= 0) break; + trdma_clean_swqe(qp, wqe); rvt_qp_wqe_unreserve(qp, wqe); s_last = qp->s_last; trace_hfi1_qp_send_completion(qp, wqe, s_last); @@ -1317,6 +1550,7 @@ struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) > 0) { u32 s_last; + trdma_clean_swqe(qp, wqe); rvt_put_swqe(wqe); rvt_qp_wqe_unreserve(qp, wqe); s_last = qp->s_last; @@ -1393,6 +1627,7 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, { struct hfi1_ibport *ibp; enum ib_wc_status status; + struct hfi1_qp_priv *qpriv = qp->priv; struct rvt_swqe *wqe; int ret = 0; u32 ack_psn; @@ -1439,6 +1674,8 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, */ if ((wqe->wr.opcode == IB_WR_RDMA_READ && (opcode != OP(RDMA_READ_RESPONSE_LAST) || diff != 0)) || + (wqe->wr.opcode == IB_WR_TID_RDMA_READ && + (opcode != TID_OP(READ_RESP) || diff != 0)) || ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) && (opcode != OP(ATOMIC_ACKNOWLEDGE) || diff != 0))) { @@ -1492,7 +1729,13 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, switch (aeth >> IB_AETH_NAK_SHIFT) { case 0: /* ACK */ this_cpu_inc(*ibp->rvp.rc_acks); - if (qp->s_acked != qp->s_tail) { + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { + if (wqe_to_tid_req(wqe)->ack_pending) + rvt_mod_retry_timer_ext(qp, + qpriv->timeout_shift); + else + rvt_stop_rc_timers(qp); + } else if (qp->s_acked != qp->s_tail) { /* * We are expecting more ACKs so * mod the retry timer. @@ -1581,7 +1824,10 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, ibp->rvp.n_other_naks++; class_b: if (qp->s_last == qp->s_acked) { - rvt_send_complete(qp, wqe, status); + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) + hfi1_kern_read_tid_flow_free(qp); + + hfi1_trdma_send_complete(qp, wqe, status); rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); } break; @@ -1622,6 +1868,7 @@ static void rdma_seq_err(struct rvt_qp *qp, struct hfi1_ibport *ibp, u32 psn, while (cmp_psn(psn, wqe->lpsn) > 0) { if (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_READ || wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) break; diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index f767c5c20566..f6d9e2717106 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2796,3 +2796,36 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, req->state = TID_REQUEST_ACTIVE; } + +void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp) +{ + int i, ret; + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_flow_state *fs; + + if (qp->ibqp.qp_type != IB_QPT_RC || !HFI1_CAP_IS_KSET(TID_RDMA)) + return; + + /* + * First, clear the flow to help prevent any delayed packets from + * being delivered. + */ + fs = &qpriv->flow_state; + if (fs->index != RXE_NUM_TID_FLOWS) + hfi1_kern_clear_hw_flow(qpriv->rcd, qp); + + for (i = qp->s_acked; i != qp->s_head;) { + struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, i); + + if (++i == qp->s_size) + i = 0; + /* Free only locally allocated TID entries */ + if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) + continue; + do { + struct hfi1_swqe_priv *priv = wqe->priv; + + ret = hfi1_kern_exp_rcv_clear(&priv->tid_req); + } while (!ret); + } +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index beb5982ce6ad..4f85b7ea5cf3 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -209,5 +209,6 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, struct hfi1_packet *packet); void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, u32 *bth2); +void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp); #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 2d59fcde4db6..88676ca79fda 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -165,6 +165,7 @@ const enum ib_wc_opcode ib_hfi1_wc_opcode[] = { [IB_WR_SEND] = IB_WC_SEND, [IB_WR_SEND_WITH_IMM] = IB_WC_SEND, [IB_WR_RDMA_READ] = IB_WC_RDMA_READ, + [IB_WR_TID_RDMA_READ] = IB_WC_RDMA_READ, [IB_WR_ATOMIC_CMP_AND_SWP] = IB_WC_COMP_SWAP, [IB_WR_ATOMIC_FETCH_AND_ADD] = IB_WC_FETCH_ADD, [IB_WR_SEND_WITH_INV] = IB_WC_SEND, From a0b34f75ec209e40f06912380533ec525691544f Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Thu, 24 Jan 2019 06:36:48 -0800 Subject: [PATCH 1146/4277] IB/hfi1: Add interlock between a TID RDMA request and other requests This locking mechanism is designed to provent vavious memory corruption scenarios from occurring when requests are pipelined, especially when RDMA READ/WRITE requests are interleaved with TID RDMA READ/WRITE requests: 1. READ-AFTER-READ; 2. READ-AFTER-WRITE; 3. WRITE-AFTER-READ; When memory corruption is likely, a request will be held back until previous requests have been completed. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 16 ++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.c | 37 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 11 ++++++++ drivers/infiniband/hw/hfi1/verbs.h | 3 +++ 4 files changed, 67 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index a5aacf8e5b93..349751cb8b47 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -482,6 +482,15 @@ check_s_state: len = wqe->length; ss = &qp->s_sge; bth2 = mask_psn(qp->s_psn); + + /* + * Interlock between various IB requests and TID RDMA + * if necessary. + */ + if ((priv->s_flags & HFI1_S_TID_WAIT_INTERLCK) || + hfi1_tid_rdma_wqe_interlock(qp, wqe)) + goto bail; + switch (wqe->wr.opcode) { case IB_WR_SEND: case IB_WR_SEND_WITH_IMM: @@ -1321,6 +1330,7 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) qp->s_state = OP(SEND_LAST); } done: + priv->s_flags &= ~HFI1_S_TID_WAIT_INTERLCK; qp->s_psn = psn; /* * Set RVT_S_WAIT_PSN as rc_complete() may start the timer @@ -1540,6 +1550,8 @@ struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, struct rvt_swqe *wqe, struct hfi1_ibport *ibp) { + struct hfi1_qp_priv *priv = qp->priv; + lockdep_assert_held(&qp->s_lock); /* * Don't decrement refcount and don't generate a @@ -1608,6 +1620,10 @@ struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, qp->s_draining = 0; wqe = rvt_get_swqe_ptr(qp, qp->s_acked); } + if (priv->s_flags & HFI1_S_TID_WAIT_INTERLCK) { + priv->s_flags &= ~HFI1_S_TID_WAIT_INTERLCK; + hfi1_schedule_send(qp); + } return wqe; } diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index f6d9e2717106..ccf15c9e76f8 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2829,3 +2829,40 @@ void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp) } while (!ret); } } + +bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe) +{ + struct rvt_swqe *prev; + struct hfi1_qp_priv *priv = qp->priv; + u32 s_prev; + + s_prev = (qp->s_cur == 0 ? qp->s_size : qp->s_cur) - 1; + prev = rvt_get_swqe_ptr(qp, s_prev); + + switch (wqe->wr.opcode) { + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + case IB_WR_SEND_WITH_INV: + case IB_WR_ATOMIC_CMP_AND_SWP: + case IB_WR_ATOMIC_FETCH_AND_ADD: + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_READ: + break; + case IB_WR_TID_RDMA_READ: + switch (prev->wr.opcode) { + case IB_WR_RDMA_READ: + if (qp->s_acked != qp->s_cur) + goto interlock; + break; + default: + break; + } + default: + break; + } + return false; + +interlock: + priv->s_flags |= HFI1_S_TID_WAIT_INTERLCK; + return true; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 4f85b7ea5cf3..689a5490432f 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -17,6 +17,16 @@ #define TID_RDMA_MAX_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */ #define TID_RDMA_MAX_PAGES (BIT(18) >> PAGE_SHIFT) +/* + * Bit definitions for priv->s_flags. + * These bit flags overload the bit flags defined for the QP's s_flags. + * Due to the fact that these bit fields are used only for the QP priv + * s_flags, there are no collisions. + * + * HFI1_S_TID_WAIT_INTERLCK - QP is waiting for requester interlock + */ +#define HFI1_S_TID_WAIT_INTERLCK BIT(5) + struct tid_rdma_params { struct rcu_head rcu_head; u32 qp; @@ -210,5 +220,6 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, u32 *bth2); void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp); +bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe); #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 7642b59ad5d2..841727a684d5 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -171,6 +171,9 @@ struct hfi1_qp_priv { u8 hdr_type; /* 9B or 16B */ unsigned long tid_timer_timeout_jiffies; + /* variables for the TID RDMA SE state machine */ + u32 s_flags; + /* For TID RDMA READ */ u32 tid_r_reqs; /* Num of tid reads requested */ u32 tid_r_comp; /* Num of tid reads completed */ From f1ab4efa6d32e98f9e604c9dde57cfe7b89a6c07 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:32:30 -0800 Subject: [PATCH 1147/4277] IB/hfi1: Enable TID RDMA READ protocol This patch enables TID RDMA READ protocol by converting a qualified RDMA READ request into a TID RDMA READ request internally: (1) The TID RDMA capability must be enabled; (2) The request must start on a 4K page boundary and all receiving buffers must start on 4K page boundaries; (3) The request length must be a multiple of 4K and must be larger or equal to 256K. Each receiving buffer length must be a multiple of 4K. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/qp.c | 1 + drivers/infiniband/hw/hfi1/tid_rdma.c | 68 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 11 +++++ 3 files changed, 80 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index 5fea7319167e..acdd9eba189b 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -319,6 +319,7 @@ int hfi1_setup_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe, bool *call_send) switch (qp->ibqp.qp_type) { case IB_QPT_RC: + hfi1_setup_tid_rdma_wqe(qp, wqe); case IB_QPT_UC: if (wqe->length > 0x80000000U) return -EINVAL; diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index ccf15c9e76f8..d65c030911c9 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2866,3 +2866,71 @@ interlock: priv->s_flags |= HFI1_S_TID_WAIT_INTERLCK; return true; } + +/* Does @sge meet the alignment requirements for tid rdma? */ +static inline bool hfi1_check_sge_align(struct rvt_sge *sge, int num_sge) +{ + int i; + + for (i = 0; i < num_sge; i++, sge++) + if ((u64)sge->vaddr & ~PAGE_MASK || + sge->sge_length & ~PAGE_MASK) + return false; + return true; +} + +void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) +{ + struct hfi1_qp_priv *qpriv = (struct hfi1_qp_priv *)qp->priv; + struct hfi1_swqe_priv *priv = wqe->priv; + struct tid_rdma_params *remote; + enum ib_wr_opcode new_opcode; + bool do_tid_rdma = false; + struct hfi1_pportdata *ppd = qpriv->rcd->ppd; + + if ((rdma_ah_get_dlid(&qp->remote_ah_attr) & ~((1 << ppd->lmc) - 1)) == + ppd->lid) + return; + if (qpriv->hdr_type != HFI1_PKT_TYPE_9B) + return; + + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + /* + * If TID RDMA is disabled by the negotiation, don't + * use it. + */ + if (!remote) + goto exit; + + if (wqe->wr.opcode == IB_WR_RDMA_READ) { + if (hfi1_check_sge_align(&wqe->sg_list[0], wqe->wr.num_sge)) { + new_opcode = IB_WR_TID_RDMA_READ; + do_tid_rdma = true; + } + } + + if (do_tid_rdma) { + if (hfi1_kern_exp_rcv_alloc_flows(&priv->tid_req, GFP_ATOMIC)) + goto exit; + wqe->wr.opcode = new_opcode; + priv->tid_req.seg_len = + min_t(u32, remote->max_len, wqe->length); + priv->tid_req.total_segs = + DIV_ROUND_UP(wqe->length, priv->tid_req.seg_len); + /* Compute the last PSN of the request */ + wqe->lpsn = wqe->psn; + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { + priv->tid_req.n_flows = remote->max_read; + qpriv->tid_r_reqs++; + wqe->lpsn += rvt_div_round_up_mtu(qp, wqe->length) - 1; + } + + priv->tid_req.cur_seg = 0; + priv->tid_req.comp_seg = 0; + priv->tid_req.ack_seg = 0; + priv->tid_req.state = TID_REQUEST_INACTIVE; + } +exit: + rcu_read_unlock(); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 689a5490432f..a53598ce45b2 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -14,6 +14,7 @@ #define CIRC_NEXT(val, size) CIRC_ADD(val, 1, size) #define CIRC_PREV(val, size) CIRC_ADD(val, -1, size) +#define TID_RDMA_MIN_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */ #define TID_RDMA_MAX_SEGMENT_SIZE BIT(18) /* 256 KiB (for now) */ #define TID_RDMA_MAX_PAGES (BIT(18) >> PAGE_SHIFT) @@ -222,4 +223,14 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp); bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe); +void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe); +static inline void hfi1_setup_tid_rdma_wqe(struct rvt_qp *qp, + struct rvt_swqe *wqe) +{ + if (wqe->priv && + wqe->wr.opcode == IB_WR_RDMA_READ && + wqe->length >= TID_RDMA_MIN_SEGMENT_SIZE) + setup_tid_rdma_wqe(qp, wqe); +} + #endif /* HFI1_TID_RDMA_H */ From 3ce5daa2c1798a530db9a01cd35122e0958538ad Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 19:32:44 -0800 Subject: [PATCH 1148/4277] IB/hfi1: Add static trace for TID RDMA READ protocol This patch makes the following changes to the static trace: 1. Adds the decoding of TID RDMA READ packets in IB header trace; 2. Tracks qpriv->s_flags and iow_flags in qpsleepwakeup trace; 3. Adds a new event to track RC ACK receiving; 4. Adds trace events for various stages of the TID RDMA READ protocol. These events provide a fine-grained control for monitoring and debugging the hfi1 driver in the filed. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 16 + drivers/infiniband/hw/hfi1/tid_rdma.c | 51 ++- drivers/infiniband/hw/hfi1/trace.c | 36 ++ drivers/infiniband/hw/hfi1/trace_ibhdrs.h | 2 + drivers/infiniband/hw/hfi1/trace_rc.h | 48 ++ drivers/infiniband/hw/hfi1/trace_tid.h | 528 +++++++++++++++++++++- drivers/infiniband/hw/hfi1/trace_tx.h | 12 +- 7 files changed, 684 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 349751cb8b47..6c9ef572fc69 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -121,6 +121,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, bool last_pkt; u32 delta; + trace_hfi1_rsp_make_rc_ack(qp, 0); lockdep_assert_held(&qp->s_lock); /* Don't send an ACK if we aren't supposed to. */ if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) @@ -349,6 +350,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) int delta; struct tid_rdma_flow *flow = NULL; + trace_hfi1_sender_make_rc_req(qp); lockdep_assert_held(&qp->s_lock); ps->s_txreq = get_txreq(ps->dev, qp); if (!ps->s_txreq) @@ -596,8 +598,13 @@ no_flow_control: break; case IB_WR_TID_RDMA_READ: + trace_hfi1_tid_read_sender_make_req(qp, newreq); wpriv = wqe->priv; req = wqe_to_tid_req(wqe); + trace_hfi1_tid_req_make_req_read(qp, newreq, + wqe->wr.opcode, + wqe->psn, wqe->lpsn, + req); delta = cmp_psn(qp->s_psn, wqe->psn); /* @@ -892,6 +899,8 @@ no_flow_control: ++qp->s_cur == qp->s_size) qp->s_cur = 0; qp->s_psn = req->s_next_psn; + trace_hfi1_tid_req_make_req_read(qp, 0, wqe->wr.opcode, + wqe->psn, wqe->lpsn, req); break; case TID_OP(READ_REQ): req = wqe_to_tid_req(wqe); @@ -933,6 +942,8 @@ no_flow_control: ++qp->s_cur == qp->s_size) qp->s_cur = 0; qp->s_psn = req->s_next_psn; + trace_hfi1_tid_req_make_req_read(qp, 0, wqe->wr.opcode, + wqe->psn, wqe->lpsn, req); break; } qp->s_sending_hpsn = bth2; @@ -1341,6 +1352,7 @@ done: (cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) <= 0)) qp->s_flags |= RVT_S_WAIT_PSN; qp->s_flags &= ~HFI1_S_AHG_VALID; + trace_hfi1_sender_reset_psn(qp); } /* @@ -1355,6 +1367,7 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) lockdep_assert_held(&qp->r_lock); lockdep_assert_held(&qp->s_lock); + trace_hfi1_sender_restart_rc(qp); if (qp->s_retry == 0) { if (qp->s_mig_state == IB_MIG_ARMED) { hfi1_migrate_qp(qp); @@ -1558,6 +1571,7 @@ struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, * completion if the SWQE is being resent until the send * is finished. */ + trace_hfi1_rc_completion(qp, wqe->lpsn); if (cmp_psn(wqe->lpsn, qp->s_sending_psn) < 0 || cmp_psn(qp->s_sending_psn, qp->s_sending_hpsn) > 0) { u32 s_last; @@ -1742,6 +1756,8 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, break; } + trace_hfi1_rc_ack_do(qp, aeth, psn, wqe); + trace_hfi1_sender_do_rc_ack(qp); switch (aeth >> IB_AETH_NAK_SHIFT) { case 0: /* ACK */ this_cpu_inc(*ibp->rvp.rc_acks); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index d65c030911c9..0ee79403acaf 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -1688,6 +1688,7 @@ u32 hfi1_build_tid_rdma_read_packet(struct rvt_swqe *wqe, /* This is the IB psn used to send the request */ *bth2 = mask_psn(flow->flow_state.ib_spsn + flow->pkt); + trace_hfi1_tid_flow_build_read_pkt(qp, req->flow_idx, flow); /* TID Entries for TID RDMA READ payload */ req_addr = &flow->tid_entry[flow->tid_idx]; @@ -1768,6 +1769,8 @@ u32 hfi1_build_tid_rdma_read_req(struct rvt_qp *qp, struct rvt_swqe *wqe, bool retry = true; u32 npkts = rvt_div_round_up_mtu(qp, *len); + trace_hfi1_tid_req_build_read_req(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); /* * Check sync conditions. Make sure that there are no pending * segments before freeing the flow. @@ -1883,6 +1886,8 @@ static int tid_rdma_rcv_read_request(struct rvt_qp *qp, */ flow->npkts = rvt_div_round_up_mtu(qp, len); for (i = 0; i < flow->tidcnt; i++) { + trace_hfi1_tid_entry_rcv_read_req(qp, i, + flow->tid_entry[i]); tlen = EXP_TID_GET(flow->tid_entry[i], LEN); if (!tlen) return 1; @@ -1917,6 +1922,7 @@ static int tid_rdma_rcv_read_request(struct rvt_qp *qp, flow->flow_state.ib_spsn = psn; flow->flow_state.ib_lpsn = flow->flow_state.ib_spsn + flow->npkts - 1; + trace_hfi1_tid_flow_rcv_read_req(qp, req->setup_head, flow); /* Set the initial flow index to the current flow. */ req->flow_idx = req->setup_head; @@ -1942,6 +1948,8 @@ static int tid_rdma_rcv_read_request(struct rvt_qp *qp, req->total_segs = 1; req->r_flow_psn = e->psn; + trace_hfi1_tid_req_rcv_read_req(qp, 0, e->opcode, e->psn, e->lpsn, + req); return 0; } @@ -1957,6 +1965,8 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, u8 prev; bool old_req; + trace_hfi1_rsp_tid_rcv_error(qp, psn); + trace_hfi1_tid_rdma_rcv_err(qp, 0, psn, diff); if (diff > 0) { /* sequence error */ if (!qp->r_nak_state) { @@ -1977,7 +1987,7 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, req = ack_to_tid_req(e); req->r_flow_psn = psn; - + trace_hfi1_tid_req_rcv_err(qp, 0, e->opcode, e->psn, e->lpsn, req); if (e->opcode == TID_OP(READ_REQ)) { struct ib_reth *reth; u32 offset; @@ -2088,6 +2098,7 @@ void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet) is_fecn = process_ecn(qp, packet); psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + trace_hfi1_rsp_rcv_tid_read_req(qp, psn); if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) rvt_comm_est(qp); @@ -2199,6 +2210,9 @@ u32 hfi1_build_tid_rdma_read_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, next_offset = flow->tid_offset + *len; last_pkt = (flow->sent >= flow->length); + trace_hfi1_tid_entry_build_read_resp(qp, flow->tid_idx, tidentry); + trace_hfi1_tid_flow_build_read_resp(qp, req->clear_tail, flow); + rcu_read_lock(); remote = rcu_dereference(qpriv->tid_rdma.remote); if (!remote) { @@ -2293,6 +2307,7 @@ void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet) unsigned long flags; u32 kpsn, ipsn; + trace_hfi1_sender_rcv_tid_read_resp(qp); is_fecn = process_ecn(qp, packet); kpsn = mask_psn(be32_to_cpu(ohdr->bth[2])); aeth = be32_to_cpu(ohdr->u.tid_rdma.r_rsp.aeth); @@ -2322,6 +2337,12 @@ void hfi1_rc_rcv_tid_rdma_read_resp(struct hfi1_packet *packet) hfi1_schedule_send(qp); } + trace_hfi1_ack(qp, ipsn); + trace_hfi1_tid_req_rcv_read_resp(qp, 0, req->e.swqe->wr.opcode, + req->e.swqe->psn, req->e.swqe->lpsn, + req); + trace_hfi1_tid_flow_rcv_read_resp(qp, req->clear_tail, flow); + /* Release the tid resources */ hfi1_kern_exp_rcv_clear(req); @@ -2671,6 +2692,8 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, unsigned long flags; bool ret = true; + trace_hfi1_msg_handle_kdeth_eflags(NULL, "Kdeth error: rhf ", + packet->rhf); if (packet->rhf & (RHF_VCRC_ERR | RHF_ICRC_ERR)) return ret; @@ -2754,12 +2777,20 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { *bth2 = mask_psn(qp->s_psn); flow = find_flow_ib(req, *bth2, &fidx); - if (!flow) + if (!flow) { + trace_hfi1_msg_tid_restart_req(/* msg */ + qp, "!!!!!! Could not find flow to restart: bth2 ", + (u64)*bth2); + trace_hfi1_tid_req_restart_req(qp, 0, wqe->wr.opcode, + wqe->psn, wqe->lpsn, + req); return; + } } else { return; } + trace_hfi1_tid_flow_restart_req(qp, fidx, flow); diff = delta_psn(*bth2, flow->flow_state.ib_spsn); flow->sent = 0; @@ -2794,6 +2825,9 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, /* Move flow_idx to correct index */ req->flow_idx = fidx; + trace_hfi1_tid_flow_restart_req(qp, fidx, flow); + trace_hfi1_tid_req_restart_req(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); req->state = TID_REQUEST_ACTIVE; } @@ -2868,14 +2902,17 @@ interlock: } /* Does @sge meet the alignment requirements for tid rdma? */ -static inline bool hfi1_check_sge_align(struct rvt_sge *sge, int num_sge) +static inline bool hfi1_check_sge_align(struct rvt_qp *qp, + struct rvt_sge *sge, int num_sge) { int i; - for (i = 0; i < num_sge; i++, sge++) + for (i = 0; i < num_sge; i++, sge++) { + trace_hfi1_sge_check_align(qp, i, sge); if ((u64)sge->vaddr & ~PAGE_MASK || sge->sge_length & ~PAGE_MASK) return false; + } return true; } @@ -2904,7 +2941,8 @@ void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) goto exit; if (wqe->wr.opcode == IB_WR_RDMA_READ) { - if (hfi1_check_sge_align(&wqe->sg_list[0], wqe->wr.num_sge)) { + if (hfi1_check_sge_align(qp, &wqe->sg_list[0], + wqe->wr.num_sge)) { new_opcode = IB_WR_TID_RDMA_READ; do_tid_rdma = true; } @@ -2930,6 +2968,9 @@ void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) priv->tid_req.comp_seg = 0; priv->tid_req.ack_seg = 0; priv->tid_req.state = TID_REQUEST_INACTIVE; + trace_hfi1_tid_req_setup_tid_wqe(qp, 1, wqe->wr.opcode, + wqe->psn, wqe->lpsn, + &priv->tid_req); } exit: rcu_read_unlock(); diff --git a/drivers/infiniband/hw/hfi1/trace.c b/drivers/infiniband/hw/hfi1/trace.c index f1154c3c294e..28181d711fed 100644 --- a/drivers/infiniband/hw/hfi1/trace.c +++ b/drivers/infiniband/hw/hfi1/trace.c @@ -129,6 +129,10 @@ const char *hfi1_trace_get_packet_l2_str(u8 l2) #define IETH_PRN "ieth rkey:0x%.8x" #define ATOMICACKETH_PRN "origdata:%llx" #define ATOMICETH_PRN "vaddr:0x%llx rkey:0x%.8x sdata:%llx cdata:%llx" +#define TID_RDMA_KDETH "kdeth0 0x%x kdeth1 0x%x" +#define TID_RDMA_KDETH_DATA "kdeth0 0x%x: kver %u sh %u intr %u tidctrl %u tid %x offset %x kdeth1 0x%x: jkey %x" +#define TID_READ_REQ_PRN "tid_flow_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x" +#define TID_READ_RSP_PRN "verbs_qp 0x%x" #define OP(transport, op) IB_OPCODE_## transport ## _ ## op @@ -323,6 +327,38 @@ const char *parse_everbs_hdrs( parse_syndrome(be32_to_cpu(eh->aeth) >> 24), be32_to_cpu(eh->aeth) & IB_MSN_MASK); break; + case OP(TID_RDMA, READ_REQ): + trace_seq_printf(p, TID_RDMA_KDETH " " RETH_PRN " " + TID_READ_REQ_PRN, + le32_to_cpu(eh->tid_rdma.r_req.kdeth0), + le32_to_cpu(eh->tid_rdma.r_req.kdeth1), + ib_u64_get(&eh->tid_rdma.r_req.reth.vaddr), + be32_to_cpu(eh->tid_rdma.r_req.reth.rkey), + be32_to_cpu(eh->tid_rdma.r_req.reth.length), + be32_to_cpu(eh->tid_rdma.r_req.tid_flow_psn), + be32_to_cpu(eh->tid_rdma.r_req.tid_flow_qp), + be32_to_cpu(eh->tid_rdma.r_req.verbs_qp)); + break; + case OP(TID_RDMA, READ_RESP): + trace_seq_printf(p, TID_RDMA_KDETH_DATA " " AETH_PRN " " + TID_READ_RSP_PRN, + le32_to_cpu(eh->tid_rdma.r_rsp.kdeth0), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, KVER), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, SH), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, INTR), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, TIDCTRL), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, TID), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth0, OFFSET), + le32_to_cpu(eh->tid_rdma.r_rsp.kdeth1), + KDETH_GET(eh->tid_rdma.r_rsp.kdeth1, JKEY), + be32_to_cpu(eh->tid_rdma.r_rsp.aeth) >> 24, + parse_syndrome(/* aeth */ + be32_to_cpu(eh->tid_rdma.r_rsp.aeth) + >> 24), + (be32_to_cpu(eh->tid_rdma.r_rsp.aeth) & + IB_MSN_MASK), + be32_to_cpu(eh->tid_rdma.r_rsp.verbs_qp)); + break; /* aeth + atomicacketh */ case OP(RC, ATOMIC_ACKNOWLEDGE): trace_seq_printf(p, AETH_PRN " " ATOMICACKETH_PRN, diff --git a/drivers/infiniband/hw/hfi1/trace_ibhdrs.h b/drivers/infiniband/hw/hfi1/trace_ibhdrs.h index 1dc2c28fc96e..1116238bf24d 100644 --- a/drivers/infiniband/hw/hfi1/trace_ibhdrs.h +++ b/drivers/infiniband/hw/hfi1/trace_ibhdrs.h @@ -79,6 +79,8 @@ __print_symbolic(opcode, \ ib_opcode_name(RC_ATOMIC_ACKNOWLEDGE), \ ib_opcode_name(RC_COMPARE_SWAP), \ ib_opcode_name(RC_FETCH_ADD), \ + ib_opcode_name(TID_RDMA_READ_REQ), \ + ib_opcode_name(TID_RDMA_READ_RESP), \ ib_opcode_name(UC_SEND_FIRST), \ ib_opcode_name(UC_SEND_MIDDLE), \ ib_opcode_name(UC_SEND_LAST), \ diff --git a/drivers/infiniband/hw/hfi1/trace_rc.h b/drivers/infiniband/hw/hfi1/trace_rc.h index 8ce476570462..1ebca37862e0 100644 --- a/drivers/infiniband/hw/hfi1/trace_rc.h +++ b/drivers/infiniband/hw/hfi1/trace_rc.h @@ -109,6 +109,54 @@ DEFINE_EVENT(hfi1_rc_template, hfi1_rcv_error, TP_ARGS(qp, psn) ); +DEFINE_EVENT(/* event */ + hfi1_rc_template, hfi1_rc_completion, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DECLARE_EVENT_CLASS(/* rc_ack */ + hfi1_rc_ack_template, + TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn, + struct rvt_swqe *wqe), + TP_ARGS(qp, aeth, psn, wqe), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u32, aeth) + __field(u32, psn) + __field(u8, opcode) + __field(u32, spsn) + __field(u32, lpsn) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)) + __entry->qpn = qp->ibqp.qp_num; + __entry->aeth = aeth; + __entry->psn = psn; + __entry->opcode = wqe->wr.opcode; + __entry->spsn = wqe->psn; + __entry->lpsn = wqe->lpsn; + ), + TP_printk(/* print */ + "[%s] qpn 0x%x aeth 0x%x psn 0x%x opcode 0x%x spsn 0x%x lpsn 0x%x", + __get_str(dev), + __entry->qpn, + __entry->aeth, + __entry->psn, + __entry->opcode, + __entry->spsn, + __entry->lpsn + ) +); + +DEFINE_EVENT(/* do_rc_ack */ + hfi1_rc_ack_template, hfi1_rc_ack_do, + TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn, + struct rvt_swqe *wqe), + TP_ARGS(qp, aeth, psn, wqe) +); + #endif /* __HFI1_TRACE_RC_H */ #undef TRACE_INCLUDE_PATH diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h index c1da744f44a5..b71638c22d4b 100644 --- a/drivers/infiniband/hw/hfi1/trace_tid.h +++ b/drivers/infiniband/hw/hfi1/trace_tid.h @@ -31,11 +31,41 @@ u16 hfi1_trace_get_tid_idx(u32 ent); #define TID_FLOW_PRN "[%s] qpn 0x%x flow %d: idx %d resp_ib_psn 0x%x " \ "generation 0x%x fpsn 0x%x-%x r_next_psn 0x%x " \ - "npagesets %u tnode_cnt %u tidcnt %u length %u" + "ib_psn 0x%x-%x npagesets %u tnode_cnt %u " \ + "tidcnt %u tid_idx %u tid_offset %u length %u sent %u" #define TID_NODE_PRN "[%s] qpn 0x%x %s idx %u grp base 0x%x map 0x%x " \ "used %u cnt %u" +#define RSP_INFO_PRN "[%s] qpn 0x%x state 0x%x s_state 0x%x psn 0x%x " \ + "r_psn 0x%x r_state 0x%x r_flags 0x%x " \ + "r_head_ack_queue %u s_tail_ack_queue %u " \ + "s_ack_state 0x%x " \ + "s_nak_state 0x%x s_flags 0x%x ps_flags 0x%x " \ + "iow_flags 0x%lx" + +#define SENDER_INFO_PRN "[%s] qpn 0x%x state 0x%x s_cur %u s_tail %u " \ + "s_head %u s_acked %u s_last %u s_psn 0x%x " \ + "s_last_psn 0x%x s_flags 0x%x ps_flags 0x%x " \ + "iow_flags 0x%lx s_state 0x%x s_num_rd %u s_retry %u" + +#define TID_READ_SENDER_PRN "[%s] qpn 0x%x newreq %u tid_r_reqs %u " \ + "tid_r_comp %u pending_tid_r_segs %u " \ + "s_flags 0x%x ps_flags 0x%x iow_flags 0x%lx " \ + "hw_flow_index %u generation 0x%x " \ + "fpsn 0x%x flow_flags 0x%x" + +#define TID_REQ_PRN "[%s] qpn 0x%x newreq %u opcode 0x%x psn 0x%x lpsn 0x%x " \ + "cur_seg %u comp_seg %u ack_seg %u " \ + "total_segs %u setup_head %u clear_tail %u flow_idx %u " \ + "state %u r_flow_psn 0x%x " \ + "s_next_psn 0x%x" + +#define RCV_ERR_PRN "[%s] qpn 0x%x s_flags 0x%x state 0x%x " \ + "s_tail_ack_queue %u " \ + "r_head_ack_queue %u opcode 0x%x psn 0x%x r_psn 0x%x " \ + " diff %d" + DECLARE_EVENT_CLASS(/* class */ hfi1_exp_tid_reg_unreg, TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, @@ -340,6 +370,18 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, msg, more) ); +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_tid_restart_req, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_handle_kdeth_eflags, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + DECLARE_EVENT_CLASS(/* tid_flow_page */ hfi1_tid_flow_page_template, TP_PROTO(struct rvt_qp *qp, struct tid_rdma_flow *flow, u32 index, @@ -429,10 +471,15 @@ DECLARE_EVENT_CLASS(/* tid_fow */ __field(u32, fspsn) __field(u32, flpsn) __field(u32, r_next_psn) + __field(u32, ib_spsn) + __field(u32, ib_lpsn) __field(u32, npagesets) __field(u32, tnode_cnt) __field(u32, tidcnt) + __field(u32, tid_idx) + __field(u32, tid_offset) __field(u32, length) + __field(u32, sent) ), TP_fast_assign(/* assign */ DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); @@ -446,10 +493,15 @@ DECLARE_EVENT_CLASS(/* tid_fow */ __entry->flpsn = full_flow_psn(flow, flow->flow_state.lpsn); __entry->r_next_psn = flow->flow_state.r_next_psn; + __entry->ib_spsn = flow->flow_state.ib_spsn; + __entry->ib_lpsn = flow->flow_state.ib_lpsn; __entry->npagesets = flow->npagesets; __entry->tnode_cnt = flow->tnode_cnt; __entry->tidcnt = flow->tidcnt; + __entry->tid_idx = flow->tid_idx; + __entry->tid_offset = flow->tid_offset; __entry->length = flow->length; + __entry->sent = flow->sent; ), TP_printk(/* print */ TID_FLOW_PRN, @@ -462,10 +514,15 @@ DECLARE_EVENT_CLASS(/* tid_fow */ __entry->fspsn, __entry->flpsn, __entry->r_next_psn, + __entry->ib_spsn, + __entry->ib_lpsn, __entry->npagesets, __entry->tnode_cnt, __entry->tidcnt, - __entry->length + __entry->tid_idx, + __entry->tid_offset, + __entry->length, + __entry->sent ) ); @@ -475,6 +532,36 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, index, flow) ); +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_build_read_pkt, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_build_read_resp, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_rcv_read_req, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_rcv_read_resp, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_restart_req, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + DECLARE_EVENT_CLASS(/* tid_node */ hfi1_tid_node_template, TP_PROTO(struct rvt_qp *qp, const char *msg, u32 index, u32 base, @@ -557,6 +644,443 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, index, entry) ); +DEFINE_EVENT(/* event */ + hfi1_tid_entry_template, hfi1_tid_entry_build_read_resp, + TP_PROTO(struct rvt_qp *qp, int index, u32 ent), + TP_ARGS(qp, index, ent) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_entry_template, hfi1_tid_entry_rcv_read_req, + TP_PROTO(struct rvt_qp *qp, int index, u32 ent), + TP_ARGS(qp, index, ent) +); + +DECLARE_EVENT_CLASS(/* rsp_info */ + hfi1_responder_info_template, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u8, state) + __field(u8, s_state) + __field(u32, psn) + __field(u32, r_psn) + __field(u8, r_state) + __field(u8, r_flags) + __field(u8, r_head_ack_queue) + __field(u8, s_tail_ack_queue) + __field(u8, s_ack_state) + __field(u8, s_nak_state) + __field(u8, r_nak_state) + __field(u32, s_flags) + __field(u32, ps_flags) + __field(unsigned long, iow_flags) + ), + TP_fast_assign(/* assign */ + struct hfi1_qp_priv *priv = qp->priv; + + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->state = qp->state; + __entry->s_state = qp->s_state; + __entry->psn = psn; + __entry->r_psn = qp->r_psn; + __entry->r_state = qp->r_state; + __entry->r_flags = qp->r_flags; + __entry->r_head_ack_queue = qp->r_head_ack_queue; + __entry->s_tail_ack_queue = qp->s_tail_ack_queue; + __entry->s_ack_state = qp->s_ack_state; + __entry->s_nak_state = qp->s_nak_state; + __entry->s_flags = qp->s_flags; + __entry->ps_flags = priv->s_flags; + __entry->iow_flags = priv->s_iowait.flags; + ), + TP_printk(/* print */ + RSP_INFO_PRN, + __get_str(dev), + __entry->qpn, + __entry->state, + __entry->s_state, + __entry->psn, + __entry->r_psn, + __entry->r_state, + __entry->r_flags, + __entry->r_head_ack_queue, + __entry->s_tail_ack_queue, + __entry->s_ack_state, + __entry->s_nak_state, + __entry->s_flags, + __entry->ps_flags, + __entry->iow_flags + ) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_make_rc_ack, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_rcv_tid_read_req, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_tid_rcv_error, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DECLARE_EVENT_CLASS(/* sender_info */ + hfi1_sender_info_template, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u8, state) + __field(u32, s_cur) + __field(u32, s_tail) + __field(u32, s_head) + __field(u32, s_acked) + __field(u32, s_last) + __field(u32, s_psn) + __field(u32, s_last_psn) + __field(u32, s_flags) + __field(u32, ps_flags) + __field(unsigned long, iow_flags) + __field(u8, s_state) + __field(u8, s_num_rd) + __field(u8, s_retry) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)) + __entry->qpn = qp->ibqp.qp_num; + __entry->state = qp->state; + __entry->s_cur = qp->s_cur; + __entry->s_tail = qp->s_tail; + __entry->s_head = qp->s_head; + __entry->s_acked = qp->s_acked; + __entry->s_last = qp->s_last; + __entry->s_psn = qp->s_psn; + __entry->s_last_psn = qp->s_last_psn; + __entry->s_flags = qp->s_flags; + __entry->ps_flags = ((struct hfi1_qp_priv *)qp->priv)->s_flags; + __entry->iow_flags = + ((struct hfi1_qp_priv *)qp->priv)->s_iowait.flags; + __entry->s_state = qp->s_state; + __entry->s_num_rd = qp->s_num_rd_atomic; + __entry->s_retry = qp->s_retry; + ), + TP_printk(/* print */ + SENDER_INFO_PRN, + __get_str(dev), + __entry->qpn, + __entry->state, + __entry->s_cur, + __entry->s_tail, + __entry->s_head, + __entry->s_acked, + __entry->s_last, + __entry->s_psn, + __entry->s_last_psn, + __entry->s_flags, + __entry->ps_flags, + __entry->iow_flags, + __entry->s_state, + __entry->s_num_rd, + __entry->s_retry + ) +); + +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_make_rc_req, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_reset_psn, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_restart_rc, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_do_rc_ack, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_rcv_tid_read_resp, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DECLARE_EVENT_CLASS(/* tid_read_sender */ + hfi1_tid_read_sender_template, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(char, newreq) + __field(u32, tid_r_reqs) + __field(u32, tid_r_comp) + __field(u32, pending_tid_r_segs) + __field(u32, s_flags) + __field(u32, ps_flags) + __field(unsigned long, iow_flags) + __field(u32, hw_flow_index) + __field(u32, generation) + __field(u32, fpsn) + __field(u32, flow_flags) + ), + TP_fast_assign(/* assign */ + struct hfi1_qp_priv *priv = qp->priv; + + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->newreq = newreq; + __entry->tid_r_reqs = priv->tid_r_reqs; + __entry->tid_r_comp = priv->tid_r_comp; + __entry->pending_tid_r_segs = priv->pending_tid_r_segs; + __entry->s_flags = qp->s_flags; + __entry->ps_flags = priv->s_flags; + __entry->iow_flags = priv->s_iowait.flags; + __entry->hw_flow_index = priv->flow_state.index; + __entry->generation = priv->flow_state.generation; + __entry->fpsn = priv->flow_state.psn; + __entry->flow_flags = priv->flow_state.flags; + ), + TP_printk(/* print */ + TID_READ_SENDER_PRN, + __get_str(dev), + __entry->qpn, + __entry->newreq, + __entry->tid_r_reqs, + __entry->tid_r_comp, + __entry->pending_tid_r_segs, + __entry->s_flags, + __entry->ps_flags, + __entry->iow_flags, + __entry->hw_flow_index, + __entry->generation, + __entry->fpsn, + __entry->flow_flags + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_read_sender_template, hfi1_tid_read_sender_make_req, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DECLARE_EVENT_CLASS(/* tid_rdma_request */ + hfi1_tid_rdma_request_template, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(char, newreq) + __field(u8, opcode) + __field(u32, psn) + __field(u32, lpsn) + __field(u32, cur_seg) + __field(u32, comp_seg) + __field(u32, ack_seg) + __field(u32, total_segs) + __field(u16, setup_head) + __field(u16, clear_tail) + __field(u16, flow_idx) + __field(u32, state) + __field(u32, r_flow_psn) + __field(u32, s_next_psn) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->newreq = newreq; + __entry->opcode = opcode; + __entry->psn = psn; + __entry->lpsn = lpsn; + __entry->cur_seg = req->cur_seg; + __entry->comp_seg = req->comp_seg; + __entry->ack_seg = req->ack_seg; + __entry->total_segs = req->total_segs; + __entry->setup_head = req->setup_head; + __entry->clear_tail = req->clear_tail; + __entry->flow_idx = req->flow_idx; + __entry->state = req->state; + __entry->r_flow_psn = req->r_flow_psn; + __entry->s_next_psn = req->s_next_psn; + ), + TP_printk(/* print */ + TID_REQ_PRN, + __get_str(dev), + __entry->qpn, + __entry->newreq, + __entry->opcode, + __entry->psn, + __entry->lpsn, + __entry->cur_seg, + __entry->comp_seg, + __entry->ack_seg, + __entry->total_segs, + __entry->setup_head, + __entry->clear_tail, + __entry->flow_idx, + __entry->state, + __entry->r_flow_psn, + __entry->s_next_psn + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_make_req_read, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_build_read_req, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_read_req, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_read_resp, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_err, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_restart_req, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_setup_tid_wqe, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DECLARE_EVENT_CLASS(/* rc_rcv_err */ + hfi1_rc_rcv_err_template, + TP_PROTO(struct rvt_qp *qp, u32 opcode, u32 psn, int diff), + TP_ARGS(qp, opcode, psn, diff), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u32, s_flags) + __field(u8, state) + __field(u8, s_tail_ack_queue) + __field(u8, r_head_ack_queue) + __field(u32, opcode) + __field(u32, psn) + __field(u32, r_psn) + __field(int, diff) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)) + __entry->qpn = qp->ibqp.qp_num; + __entry->s_flags = qp->s_flags; + __entry->state = qp->state; + __entry->s_tail_ack_queue = qp->s_tail_ack_queue; + __entry->r_head_ack_queue = qp->r_head_ack_queue; + __entry->opcode = opcode; + __entry->psn = psn; + __entry->r_psn = qp->r_psn; + __entry->diff = diff; + ), + TP_printk(/* print */ + RCV_ERR_PRN, + __get_str(dev), + __entry->qpn, + __entry->s_flags, + __entry->state, + __entry->s_tail_ack_queue, + __entry->r_head_ack_queue, + __entry->opcode, + __entry->psn, + __entry->r_psn, + __entry->diff + ) +); + +DEFINE_EVENT(/* event */ + hfi1_rc_rcv_err_template, hfi1_tid_rdma_rcv_err, + TP_PROTO(struct rvt_qp *qp, u32 opcode, u32 psn, int diff), + TP_ARGS(qp, opcode, psn, diff) +); + +DECLARE_EVENT_CLASS(/* sge */ + hfi1_sge_template, + TP_PROTO(struct rvt_qp *qp, int index, struct rvt_sge *sge), + TP_ARGS(qp, index, sge), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(int, index) + __field(u64, vaddr) + __field(u32, sge_length) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->index = index; + __entry->vaddr = (u64)sge->vaddr; + __entry->sge_length = sge->sge_length; + ), + TP_printk(/* print */ + "[%s] qpn 0x%x sge %d: vaddr 0x%llx sge_length %u", + __get_str(dev), + __entry->qpn, + __entry->index, + __entry->vaddr, + __entry->sge_length + ) +); + +DEFINE_EVENT(/* event */ + hfi1_sge_template, hfi1_sge_check_align, + TP_PROTO(struct rvt_qp *qp, int index, struct rvt_sge *sge), + TP_ARGS(qp, index, sge) +); + #endif /* __HFI1_TRACE_TID_H */ #undef TRACE_INCLUDE_PATH diff --git a/drivers/infiniband/hw/hfi1/trace_tx.h b/drivers/infiniband/hw/hfi1/trace_tx.h index c57af3b31fe1..37dbb3e599c3 100644 --- a/drivers/infiniband/hw/hfi1/trace_tx.h +++ b/drivers/infiniband/hw/hfi1/trace_tx.h @@ -114,19 +114,27 @@ DECLARE_EVENT_CLASS(hfi1_qpsleepwakeup_template, __field(u32, qpn) __field(u32, flags) __field(u32, s_flags) + __field(u32, ps_flags) + __field(unsigned long, iow_flags) ), TP_fast_assign( DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)) __entry->flags = flags; __entry->qpn = qp->ibqp.qp_num; __entry->s_flags = qp->s_flags; + __entry->ps_flags = + ((struct hfi1_qp_priv *)qp->priv)->s_flags; + __entry->iow_flags = + ((struct hfi1_qp_priv *)qp->priv)->s_iowait.flags; ), TP_printk( - "[%s] qpn 0x%x flags 0x%x s_flags 0x%x", + "[%s] qpn 0x%x flags 0x%x s_flags 0x%x ps_flags 0x%x iow_flags 0x%lx", __get_str(dev), __entry->qpn, __entry->flags, - __entry->s_flags + __entry->s_flags, + __entry->ps_flags, + __entry->iow_flags ) ); From c098bbb00cd1986cbb58ed1712643f80ed00fcc3 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:48:28 -0800 Subject: [PATCH 1149/4277] IB/hfi1: Build TID RDMA WRITE request This patch adds the functions to build TID RDMA WRITE request. The work request opcode, packet opcode, and packet formats for TID RDMA WRITE protocol are also defined in this patch. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/qp.h | 2 + drivers/infiniband/hw/hfi1/tid_rdma.c | 38 ++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 3 ++ include/rdma/ib_hdrs.h | 5 +++ include/rdma/tid_rdma_defs.h | 56 +++++++++++++++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h index ce25a27aa4a1..f74e2509e8b9 100644 --- a/drivers/infiniband/hw/hfi1/qp.h +++ b/drivers/infiniband/hw/hfi1/qp.h @@ -64,12 +64,14 @@ extern const struct rvt_operation_params hfi1_post_parms[]; * HFI1_S_AHG_CLEAR - have send engine clear ahg state * HFI1_S_WAIT_PIO_DRAIN - qp waiting for PIOs to drain * HFI1_S_WAIT_TID_SPACE - a QP is waiting for TID resource + * HFI1_S_WAIT_TID_RESP - waiting for a TID RDMA WRITE response * HFI1_S_MIN_BIT_MASK - the lowest bit that can be used by hfi1 */ #define HFI1_S_AHG_VALID 0x80000000 #define HFI1_S_AHG_CLEAR 0x40000000 #define HFI1_S_WAIT_PIO_DRAIN 0x20000000 #define HFI1_S_WAIT_TID_SPACE 0x10000000 +#define HFI1_S_WAIT_TID_RESP 0x08000000 #define HFI1_S_MIN_BIT_MASK 0x01000000 /* diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 0ee79403acaf..089e301d9bcd 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2975,3 +2975,41 @@ void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) exit: rcu_read_unlock(); } + +/* TID RDMA WRITE functions */ + +u32 hfi1_build_tid_rdma_write_req(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, + u32 *bth1, u32 *bth2, u32 *len) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct tid_rdma_params *remote; + + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + /* + * Set the number of flow to be used based on negotiated + * parameters. + */ + req->n_flows = remote->max_write; + req->state = TID_REQUEST_ACTIVE; + + KDETH_RESET(ohdr->u.tid_rdma.w_req.kdeth0, KVER, 0x1); + KDETH_RESET(ohdr->u.tid_rdma.w_req.kdeth1, JKEY, remote->jkey); + ohdr->u.tid_rdma.w_req.reth.vaddr = + cpu_to_be64(wqe->rdma_wr.remote_addr + (wqe->length - *len)); + ohdr->u.tid_rdma.w_req.reth.rkey = + cpu_to_be32(wqe->rdma_wr.rkey); + ohdr->u.tid_rdma.w_req.reth.length = cpu_to_be32(*len); + ohdr->u.tid_rdma.w_req.verbs_qp = cpu_to_be32(qp->remote_qpn); + *bth1 &= ~RVT_QPN_MASK; + *bth1 |= remote->qp; + qp->s_state = TID_OP(WRITE_REQ); + qp->s_flags |= HFI1_S_WAIT_TID_RESP; + *bth2 |= IB_BTH_REQ_ACK; + *len = 0; + + rcu_read_unlock(); + return sizeof(ohdr->u.tid_rdma.w_req) / sizeof(u32); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index a53598ce45b2..baba539b2b80 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -233,4 +233,7 @@ static inline void hfi1_setup_tid_rdma_wqe(struct rvt_qp *qp, setup_tid_rdma_wqe(qp, wqe); } +u32 hfi1_build_tid_rdma_write_req(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, + u32 *bth1, u32 *bth2, u32 *len); #endif /* HFI1_TID_RDMA_H */ diff --git a/include/rdma/ib_hdrs.h b/include/rdma/ib_hdrs.h index 58a0a0f99e7f..9a90bd031e8c 100644 --- a/include/rdma/ib_hdrs.h +++ b/include/rdma/ib_hdrs.h @@ -123,6 +123,11 @@ union ib_ehdrs { union { struct tid_rdma_read_req r_req; struct tid_rdma_read_resp r_rsp; + struct tid_rdma_write_req w_req; + struct tid_rdma_write_resp w_rsp; + struct tid_rdma_write_data w_data; + struct tid_rdma_resync resync; + struct tid_rdma_ack ack; } tid_rdma; } __packed; diff --git a/include/rdma/tid_rdma_defs.h b/include/rdma/tid_rdma_defs.h index 1c431ea32b52..08fe47c7ad2c 100644 --- a/include/rdma/tid_rdma_defs.h +++ b/include/rdma/tid_rdma_defs.h @@ -27,16 +27,71 @@ struct tid_rdma_read_resp { __be32 verbs_qp; }; +struct tid_rdma_write_req { + __le32 kdeth0; + __le32 kdeth1; + struct ib_reth reth; + __be32 reserved[2]; + __be32 verbs_qp; +}; + +struct tid_rdma_write_resp { + __le32 kdeth0; + __le32 kdeth1; + __be32 aeth; + __be32 reserved[3]; + __be32 tid_flow_psn; + __be32 tid_flow_qp; + __be32 verbs_qp; +}; + +struct tid_rdma_write_data { + __le32 kdeth0; + __le32 kdeth1; + __be32 reserved[6]; + __be32 verbs_qp; +}; + +struct tid_rdma_resync { + __le32 kdeth0; + __le32 kdeth1; + __be32 reserved[6]; + __be32 verbs_qp; +}; + +struct tid_rdma_ack { + __le32 kdeth0; + __le32 kdeth1; + __be32 aeth; + __be32 reserved[2]; + __be32 tid_flow_psn; + __be32 verbs_psn; + __be32 tid_flow_qp; + __be32 verbs_qp; +}; + /* * TID RDMA Opcodes */ #define IB_OPCODE_TID_RDMA 0xe0 enum { + IB_OPCODE_WRITE_REQ = 0x0, + IB_OPCODE_WRITE_RESP = 0x1, + IB_OPCODE_WRITE_DATA = 0x2, + IB_OPCODE_WRITE_DATA_LAST = 0x3, IB_OPCODE_READ_REQ = 0x4, IB_OPCODE_READ_RESP = 0x5, + IB_OPCODE_RESYNC = 0x6, + IB_OPCODE_ACK = 0x7, + IB_OPCODE(TID_RDMA, WRITE_REQ), + IB_OPCODE(TID_RDMA, WRITE_RESP), + IB_OPCODE(TID_RDMA, WRITE_DATA), + IB_OPCODE(TID_RDMA, WRITE_DATA_LAST), IB_OPCODE(TID_RDMA, READ_REQ), IB_OPCODE(TID_RDMA, READ_RESP), + IB_OPCODE(TID_RDMA, RESYNC), + IB_OPCODE(TID_RDMA, ACK), }; #define TID_OP(x) IB_OPCODE_TID_RDMA_##x @@ -47,6 +102,7 @@ enum { * low level drivers. Two of those are used but renamed * to be more descriptive. */ +#define IB_WR_TID_RDMA_WRITE IB_WR_RESERVED1 #define IB_WR_TID_RDMA_READ IB_WR_RESERVED2 #endif /* TID_RDMA_DEFS_H */ From f5a4a95f4dd8a09d28936c2e1e357e4c8dcca6c1 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:48:38 -0800 Subject: [PATCH 1150/4277] IB/hfi1: Allow for extra entries in QP's s_ack_queue The TID RDMA WRITE protocol differs from normal IB RDMA WRITE in that TID RDMA WRITE requests do require responses, not just ACKs. Therefore, TID RDMA WRITE requests need to be treated as RDMA READ requests from the point of view of the QPs' s_ack_queue. In other words, the QPs' need to allow for TID RDMA WRITE requests to be stored in their s_ack_queue. However, because the user does not know anything about the TID RDMA capability and/or protocols, these extra entries in the queue cannot be advertized to the user. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.h | 11 +++++++++++ drivers/infiniband/hw/hfi1/verbs.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index baba539b2b80..9b952351f072 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -28,6 +28,17 @@ */ #define HFI1_S_TID_WAIT_INTERLCK BIT(5) +/* + * Unlike regular IB RDMA VERBS, which do not require an entry + * in the s_ack_queue, TID RDMA WRITE requests do because they + * generate responses. + * Therefore, the s_ack_queue needs to be extended by a certain + * amount. The key point is that the queue needs to be extended + * without letting the "user" know so they user doesn't end up + * using these extra entries. + */ +#define HFI1_TID_RDMA_WRITE_CNT 8 + struct tid_rdma_params { struct rcu_head rcu_head; u32 qp; diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 88676ca79fda..7b87b77582bd 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -1888,7 +1888,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) dd->verbs_dev.rdi.dparms.wss_threshold = wss_threshold; dd->verbs_dev.rdi.dparms.wss_clean_period = wss_clean_period; dd->verbs_dev.rdi.dparms.reserved_operations = 1; - dd->verbs_dev.rdi.dparms.extra_rdma_atomic = 1; + dd->verbs_dev.rdi.dparms.extra_rdma_atomic = HFI1_TID_RDMA_WRITE_CNT; /* post send table */ dd->verbs_dev.rdi.post_parms = hfi1_post_parms; From 4f9264d156dc6c154a8a6cfae780730bad45c6f8 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:48:48 -0800 Subject: [PATCH 1151/4277] IB/hfi1: Add an s_acked_ack_queue pointer The s_ack_queue is managed by two pointers into the ring: r_head_ack_queue and s_tail_ack_queue. r_head_ack_queue is the index of where the next received request is going to be placed and s_tail_ack_queue is the entry of the request currently being processed. This works perfectly fine for normal Verbs as the requests are processed one at a time and the s_tail_ack_queue is not moved until the request that it points to is fully completed. In this fashion, s_tail_ack_queue constantly chases r_head_ack_queue and the two pointers can easily be used to determine "queue full" and "queue empty" conditions. The detection of these two conditions are imported in determining when an old entry can safely be overwritten with a new received request and the resources associated with the old request be safely released. When pipelined TID RDMA WRITE is introduced into this mix, things look very different. r_head_ack_queue is still the point at which a newly received request will be inserted, s_tail_ack_queue is still the currently processed request. However, with pipelined TID RDMA WRITE requests, s_tail_ack_queue moves to the next request once all TID RDMA WRITE responses for that request have been sent. The rest of the protocol for a particular request is managed by other pointers specific to TID RDMA - r_tid_tail and r_tid_ack - which point to the entries for which the next TID RDMA DATA packets are going to arrive and the request for which the next TID RDMA ACK packets are to be generated, respectively. What this means is that entries in the ring, which are "behind" s_tail_ack_queue (entries which s_tail_ack_queue has gone past) are no longer considered complete. This is where the problem is - a newly received request could potentially overwrite a still active TID RDMA WRITE request. The reason why the TID RDMA pointers trail s_tail_ack_queue is that the normal Verbs send engine uses s_tail_ack_queue as the pointer for the next response. Since TID RDMA WRITE responses are processed by the normal Verbs send engine, s_tail_ack_queue had to be moved to the next entry once all TID RDMA WRITE response packets were sent to get the desired pipelining between requests. Doing otherwise would mean that the normal Verbs send engine would not be able to send the TID RDMA WRITE responses for the next TID RDMA request until the current one is fully completed. This patch introduces the s_acked_ack_queue index to point to the next request to complete on the responder side. For requests other than TID RDMA WRITE, s_acked_ack_queue should always be kept in sync with s_tail_ack_queue. For TID RDMA WRITE request, it may fall behind s_tail_ack_queue. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 33 ++++++++++++++++++++++---- drivers/infiniband/hw/hfi1/rc.h | 1 + drivers/infiniband/hw/hfi1/tid_rdma.c | 2 ++ drivers/infiniband/hw/hfi1/trace_tid.h | 10 ++++++-- drivers/infiniband/sw/rdmavt/qp.c | 1 + include/rdma/rdmavt_qp.h | 1 + 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 6c9ef572fc69..9dc8e524510e 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -120,6 +120,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, struct hfi1_qp_priv *priv = qp->priv; bool last_pkt; u32 delta; + u8 next = qp->s_tail_ack_queue; trace_hfi1_rsp_make_rc_ack(qp, 0); lockdep_assert_held(&qp->s_lock); @@ -149,9 +150,17 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, * response has been sent instead of only being * constructed. */ - if (++qp->s_tail_ack_queue > - rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) - qp->s_tail_ack_queue = 0; + if (++next > rvt_size_atomic(&dev->rdi)) + next = 0; + /* + * Only advance the s_acked_ack_queue pointer if there + * have been no TID RDMA requests. + */ + e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + if (e->opcode != TID_OP(WRITE_REQ) && + qp->s_acked_ack_queue == qp->s_tail_ack_queue) + qp->s_acked_ack_queue = next; + qp->s_tail_ack_queue = next; /* FALLTHROUGH */ case OP(SEND_ONLY): case OP(ACKNOWLEDGE): @@ -172,6 +181,10 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, */ len = e->rdma_sge.sge_length; if (len && !e->rdma_sge.mr) { + if (qp->s_acked_ack_queue == + qp->s_tail_ack_queue) + qp->s_acked_ack_queue = + qp->r_head_ack_queue; qp->s_tail_ack_queue = qp->r_head_ack_queue; goto bail; } @@ -202,6 +215,10 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, */ len = e->rdma_sge.sge_length; if (len && !e->rdma_sge.mr) { + if (qp->s_acked_ack_queue == + qp->s_tail_ack_queue) + qp->s_acked_ack_queue = + qp->r_head_ack_queue; qp->s_tail_ack_queue = qp->r_head_ack_queue; goto bail; } @@ -2235,6 +2252,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, e->psn = psn; if (old_req) goto unlock_done; + if (qp->s_acked_ack_queue == qp->s_tail_ack_queue) + qp->s_acked_ack_queue = prev; qp->s_tail_ack_queue = prev; break; } @@ -2248,6 +2267,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, */ if (!e || e->opcode != (u8)opcode || old_req) goto unlock_done; + if (qp->s_tail_ack_queue == qp->s_acked_ack_queue) + qp->s_acked_ack_queue = prev; qp->s_tail_ack_queue = prev; break; } @@ -2274,6 +2295,8 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, * Resend the RDMA read or atomic op which * ACKs this duplicate request. */ + if (qp->s_tail_ack_queue == qp->s_acked_ack_queue) + qp->s_acked_ack_queue = mra; qp->s_tail_ack_queue = mra; break; } @@ -2646,7 +2669,7 @@ send_last: if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) next = 0; spin_lock_irqsave(&qp->s_lock, flags); - if (unlikely(next == qp->s_tail_ack_queue)) { + if (unlikely(next == qp->s_acked_ack_queue)) { if (!qp->s_ack_queue[next].sent) goto nack_inv_unlck; update_ack_queue(qp, next); @@ -2723,7 +2746,7 @@ send_last: if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) next = 0; spin_lock_irqsave(&qp->s_lock, flags); - if (unlikely(next == qp->s_tail_ack_queue)) { + if (unlikely(next == qp->s_acked_ack_queue)) { if (!qp->s_ack_queue[next].sent) goto nack_inv_unlck; update_ack_queue(qp, next); diff --git a/drivers/infiniband/hw/hfi1/rc.h b/drivers/infiniband/hw/hfi1/rc.h index 4329eadcb3df..8e0935b9bf2a 100644 --- a/drivers/infiniband/hw/hfi1/rc.h +++ b/drivers/infiniband/hw/hfi1/rc.h @@ -18,6 +18,7 @@ static inline void update_ack_queue(struct rvt_qp *qp, unsigned int n) if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) next = 0; qp->s_tail_ack_queue = next; + qp->s_acked_ack_queue = next; qp->s_ack_state = OP(ACKNOWLEDGE); } diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 089e301d9bcd..c320a99afb35 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2044,6 +2044,8 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, goto unlock; } /* Re-process old requests.*/ + if (qp->s_acked_ack_queue == qp->s_tail_ack_queue) + qp->s_acked_ack_queue = prev; qp->s_tail_ack_queue = prev; /* * Since the qp->s_tail_ack_queue is modified, the diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h index b71638c22d4b..51f5b0e8da71 100644 --- a/drivers/infiniband/hw/hfi1/trace_tid.h +++ b/drivers/infiniband/hw/hfi1/trace_tid.h @@ -40,7 +40,7 @@ u16 hfi1_trace_get_tid_idx(u32 ent); #define RSP_INFO_PRN "[%s] qpn 0x%x state 0x%x s_state 0x%x psn 0x%x " \ "r_psn 0x%x r_state 0x%x r_flags 0x%x " \ "r_head_ack_queue %u s_tail_ack_queue %u " \ - "s_ack_state 0x%x " \ + "s_acked_ack_queue %u s_ack_state 0x%x " \ "s_nak_state 0x%x s_flags 0x%x ps_flags 0x%x " \ "iow_flags 0x%lx" @@ -62,7 +62,7 @@ u16 hfi1_trace_get_tid_idx(u32 ent); "s_next_psn 0x%x" #define RCV_ERR_PRN "[%s] qpn 0x%x s_flags 0x%x state 0x%x " \ - "s_tail_ack_queue %u " \ + "s_acked_ack_queue %u s_tail_ack_queue %u " \ "r_head_ack_queue %u opcode 0x%x psn 0x%x r_psn 0x%x " \ " diff %d" @@ -671,6 +671,7 @@ DECLARE_EVENT_CLASS(/* rsp_info */ __field(u8, r_flags) __field(u8, r_head_ack_queue) __field(u8, s_tail_ack_queue) + __field(u8, s_acked_ack_queue) __field(u8, s_ack_state) __field(u8, s_nak_state) __field(u8, r_nak_state) @@ -691,6 +692,7 @@ DECLARE_EVENT_CLASS(/* rsp_info */ __entry->r_flags = qp->r_flags; __entry->r_head_ack_queue = qp->r_head_ack_queue; __entry->s_tail_ack_queue = qp->s_tail_ack_queue; + __entry->s_acked_ack_queue = qp->s_acked_ack_queue; __entry->s_ack_state = qp->s_ack_state; __entry->s_nak_state = qp->s_nak_state; __entry->s_flags = qp->s_flags; @@ -709,6 +711,7 @@ DECLARE_EVENT_CLASS(/* rsp_info */ __entry->r_flags, __entry->r_head_ack_queue, __entry->s_tail_ack_queue, + __entry->s_acked_ack_queue, __entry->s_ack_state, __entry->s_nak_state, __entry->s_flags, @@ -1007,6 +1010,7 @@ DECLARE_EVENT_CLASS(/* rc_rcv_err */ __field(u32, qpn) __field(u32, s_flags) __field(u8, state) + __field(u8, s_acked_ack_queue) __field(u8, s_tail_ack_queue) __field(u8, r_head_ack_queue) __field(u32, opcode) @@ -1019,6 +1023,7 @@ DECLARE_EVENT_CLASS(/* rc_rcv_err */ __entry->qpn = qp->ibqp.qp_num; __entry->s_flags = qp->s_flags; __entry->state = qp->state; + __entry->s_acked_ack_queue = qp->s_acked_ack_queue; __entry->s_tail_ack_queue = qp->s_tail_ack_queue; __entry->r_head_ack_queue = qp->r_head_ack_queue; __entry->opcode = opcode; @@ -1032,6 +1037,7 @@ DECLARE_EVENT_CLASS(/* rc_rcv_err */ __entry->qpn, __entry->s_flags, __entry->state, + __entry->s_acked_ack_queue, __entry->s_tail_ack_queue, __entry->r_head_ack_queue, __entry->opcode, diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 2769ebdf89fb..14ec2577bcaa 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -854,6 +854,7 @@ static void rvt_init_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, qp->s_mig_state = IB_MIG_MIGRATED; qp->r_head_ack_queue = 0; qp->s_tail_ack_queue = 0; + qp->s_acked_ack_queue = 0; qp->s_num_rd_atomic = 0; if (qp->r_rq.wq) { qp->r_rq.wq->head = 0; diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index d8d88d023092..4ee612ab6cb4 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -375,6 +375,7 @@ struct rvt_qp { u8 s_rnr_retry; /* requester RNR retry counter */ u8 s_num_rd_atomic; /* number of RDMA read/atomic pending */ u8 s_tail_ack_queue; /* index into s_ack_queue[] */ + u8 s_acked_ack_queue; /* index into s_ack_queue[] */ struct rvt_sge_state s_ack_rdma_sge; struct timer_list s_timer; From 07b923701e38f93b4725e64318e6483f890c1c1d Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:48:59 -0800 Subject: [PATCH 1152/4277] IB/hfi1: Add functions to receive TID RDMA WRITE request This patch adds the functions to receive TID RDMA WRITE request. The request will be stored in the QP's s_ack_queue. This patch also adds code to handle duplicate TID RDMA WRITE request and a function to allocate TID resources for data receiving on the responder side. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/init.c | 1 + drivers/infiniband/hw/hfi1/rc.c | 3 + drivers/infiniband/hw/hfi1/tid_rdma.c | 570 +++++++++++++++++++++++++- drivers/infiniband/hw/hfi1/tid_rdma.h | 16 + drivers/infiniband/hw/hfi1/verbs.h | 12 + 5 files changed, 601 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index d13304f7340d..7841a0ad7cb6 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1512,6 +1512,7 @@ static int __init hfi1_mod_init(void) goto bail_dev; } + hfi1_compute_tid_rdma_flow_wt(); /* * These must be called before the driver is registered with * the PCI subsystem. diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 9dc8e524510e..fcb733ea8dfb 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -2411,6 +2411,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) void *data = packet->payload; u32 tlen = packet->tlen; struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *qpriv = qp->priv; struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct ib_other_headers *ohdr = packet->ohdr; u32 opcode = packet->opcode; @@ -2716,6 +2717,7 @@ send_last: qp->r_state = opcode; qp->r_nak_state = 0; qp->r_head_ack_queue = next; + qpriv->r_tid_alloc = qp->r_head_ack_queue; /* Schedule the send engine. */ qp->s_flags |= RVT_S_RESP_PENDING; @@ -2789,6 +2791,7 @@ ack: qp->r_state = opcode; qp->r_nak_state = 0; qp->r_head_ack_queue = next; + qpriv->r_tid_alloc = qp->r_head_ack_queue; /* Schedule the send engine. */ qp->s_flags |= RVT_S_RESP_PENDING; diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index c320a99afb35..516dca9f497e 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -109,12 +109,15 @@ static u32 mask_generation(u32 a) * C - Capcode */ +static u32 tid_rdma_flow_wt; + static void tid_rdma_trigger_resume(struct work_struct *work); static void hfi1_kern_exp_rcv_free_flows(struct tid_rdma_request *req); static int hfi1_kern_exp_rcv_alloc_flows(struct tid_rdma_request *req, gfp_t gfp); static void hfi1_init_trdma_req(struct rvt_qp *qp, struct tid_rdma_request *req); +static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx); static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { @@ -313,6 +316,11 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->flow_state.index = RXE_NUM_TID_FLOWS; qpriv->flow_state.last_index = RXE_NUM_TID_FLOWS; qpriv->flow_state.generation = KERN_GENERATION_RESERVED; + qpriv->rnr_nak_state = TID_RNR_NAK_INIT; + qpriv->r_tid_head = HFI1_QP_WQE_INVALID; + qpriv->r_tid_tail = HFI1_QP_WQE_INVALID; + qpriv->r_tid_ack = HFI1_QP_WQE_INVALID; + qpriv->r_tid_alloc = HFI1_QP_WQE_INVALID; INIT_LIST_HEAD(&qpriv->tid_wait); if (init_attr->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { @@ -1959,6 +1967,8 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, { struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); struct hfi1_ctxtdata *rcd = ((struct hfi1_qp_priv *)qp->priv)->rcd; + struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); + struct hfi1_qp_priv *qpriv = qp->priv; struct rvt_ack_entry *e; struct tid_rdma_request *req; unsigned long flags; @@ -1982,7 +1992,8 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, spin_lock_irqsave(&qp->s_lock, flags); e = find_prev_entry(qp, psn, &prev, NULL, &old_req); - if (!e || e->opcode != TID_OP(READ_REQ)) + if (!e || (e->opcode != TID_OP(READ_REQ) && + e->opcode != TID_OP(WRITE_REQ))) goto unlock; req = ack_to_tid_req(e); @@ -2042,6 +2053,114 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, */ if (old_req) goto unlock; + } else { + struct flow_state *fstate; + bool schedule = false; + u8 i; + + if (req->state == TID_REQUEST_RESEND) { + req->state = TID_REQUEST_RESEND_ACTIVE; + } else if (req->state == TID_REQUEST_INIT_RESEND) { + req->state = TID_REQUEST_INIT; + schedule = true; + } + + /* + * True if the request is already scheduled (between + * qp->s_tail_ack_queue and qp->r_head_ack_queue). + * Also, don't change requests, which are at the SYNC + * point and haven't generated any responses yet. + * There is nothing to retransmit for them yet. + */ + if (old_req || req->state == TID_REQUEST_INIT || + (req->state == TID_REQUEST_SYNC && !req->cur_seg)) { + for (i = prev + 1; ; i++) { + if (i > rvt_size_atomic(&dev->rdi)) + i = 0; + if (i == qp->r_head_ack_queue) + break; + e = &qp->s_ack_queue[i]; + req = ack_to_tid_req(e); + if (e->opcode == TID_OP(WRITE_REQ) && + req->state == TID_REQUEST_INIT) + req->state = TID_REQUEST_INIT_RESEND; + } + /* + * If the state of the request has been changed, + * the first leg needs to get scheduled in order to + * pick up the change. Otherwise, normal response + * processing should take care of it. + */ + if (!schedule) + goto unlock; + } + + /* + * If there is no more allocated segment, just schedule the qp + * without changing any state. + */ + if (req->clear_tail == req->setup_head) + goto schedule; + /* + * If this request has sent responses for segments, which have + * not received data yet (flow_idx != clear_tail), the flow_idx + * pointer needs to be adjusted so the same responses can be + * re-sent. + */ + if (CIRC_CNT(req->flow_idx, req->clear_tail, MAX_FLOWS)) { + fstate = &req->flows[req->clear_tail].flow_state; + qpriv->pending_tid_w_segs -= + CIRC_CNT(req->flow_idx, req->clear_tail, + MAX_FLOWS); + req->flow_idx = + CIRC_ADD(req->clear_tail, + delta_psn(psn, fstate->resp_ib_psn), + MAX_FLOWS); + qpriv->pending_tid_w_segs += + delta_psn(psn, fstate->resp_ib_psn); + /* + * When flow_idx == setup_head, we've gotten a duplicate + * request for a segment, which has not been allocated + * yet. In that case, don't adjust this request. + * However, we still want to go through the loop below + * to adjust all subsequent requests. + */ + if (CIRC_CNT(req->setup_head, req->flow_idx, + MAX_FLOWS)) { + req->cur_seg = delta_psn(psn, e->psn); + req->state = TID_REQUEST_RESEND_ACTIVE; + } + } + + for (i = prev + 1; ; i++) { + /* + * Look at everything up to and including + * s_tail_ack_queue + */ + if (i > rvt_size_atomic(&dev->rdi)) + i = 0; + if (i == qp->r_head_ack_queue) + break; + e = &qp->s_ack_queue[i]; + req = ack_to_tid_req(e); + trace_hfi1_tid_req_rcv_err(qp, 0, e->opcode, e->psn, + e->lpsn, req); + if (e->opcode != TID_OP(WRITE_REQ) || + req->cur_seg == req->comp_seg || + req->state == TID_REQUEST_INIT || + req->state == TID_REQUEST_INIT_RESEND) { + if (req->state == TID_REQUEST_INIT) + req->state = TID_REQUEST_INIT_RESEND; + continue; + } + qpriv->pending_tid_w_segs -= + CIRC_CNT(req->flow_idx, + req->clear_tail, + MAX_FLOWS); + req->flow_idx = req->clear_tail; + req->state = TID_REQUEST_RESEND; + req->cur_seg = req->comp_seg; + } } /* Re-process old requests.*/ if (qp->s_acked_ack_queue == qp->s_tail_ack_queue) @@ -2054,6 +2173,18 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, * wrong memory region. */ qp->s_ack_state = OP(ACKNOWLEDGE); +schedule: + /* + * It's possible to receive a retry psn that is earlier than an RNRNAK + * psn. In this case, the rnrnak state should be cleared. + */ + if (qpriv->rnr_nak_state) { + qp->s_nak_state = 0; + qpriv->rnr_nak_state = TID_RNR_NAK_INIT; + qp->r_psn = e->lpsn + 1; + hfi1_tid_write_alloc_resources(qp, true); + } + qp->r_state = e->opcode; qp->r_nak_state = 0; qp->s_flags |= RVT_S_RESP_PENDING; @@ -2164,6 +2295,14 @@ void hfi1_rc_rcv_tid_rdma_read_req(struct hfi1_packet *packet) qp->r_head_ack_queue = next; + /* + * For all requests other than TID WRITE which are added to the ack + * queue, qpriv->r_tid_alloc follows qp->r_head_ack_queue. It is ok to + * do this because of interlocks between these and TID WRITE + * requests. The same change has also been made in hfi1_rc_rcv(). + */ + qpriv->r_tid_alloc = qp->r_head_ack_queue; + /* Schedule the send tasklet. */ qp->s_flags |= RVT_S_RESP_PENDING; hfi1_schedule_send(qp); @@ -3015,3 +3154,432 @@ u32 hfi1_build_tid_rdma_write_req(struct rvt_qp *qp, struct rvt_swqe *wqe, rcu_read_unlock(); return sizeof(ohdr->u.tid_rdma.w_req) / sizeof(u32); } + +void hfi1_compute_tid_rdma_flow_wt(void) +{ + /* + * Heuristic for computing the RNR timeout when waiting on the flow + * queue. Rather than a computationaly expensive exact estimate of when + * a flow will be available, we assume that if a QP is at position N in + * the flow queue it has to wait approximately (N + 1) * (number of + * segments between two sync points), assuming PMTU of 4K. The rationale + * for this is that flows are released and recycled at each sync point. + */ + tid_rdma_flow_wt = MAX_TID_FLOW_PSN * enum_to_mtu(OPA_MTU_4096) / + TID_RDMA_MAX_SEGMENT_SIZE; +} + +static u32 position_in_queue(struct hfi1_qp_priv *qpriv, + struct tid_queue *queue) +{ + return qpriv->tid_enqueue - queue->dequeue; +} + +/* + * @qp: points to rvt_qp context. + * @to_seg: desired RNR timeout in segments. + * Return: index of the next highest timeout in the ib_hfi1_rnr_table[] + */ +static u32 hfi1_compute_tid_rnr_timeout(struct rvt_qp *qp, u32 to_seg) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + u64 timeout; + u32 bytes_per_us; + u8 i; + + bytes_per_us = active_egress_rate(qpriv->rcd->ppd) / 8; + timeout = (to_seg * TID_RDMA_MAX_SEGMENT_SIZE) / bytes_per_us; + /* + * Find the next highest value in the RNR table to the required + * timeout. This gives the responder some padding. + */ + for (i = 1; i <= IB_AETH_CREDIT_MASK; i++) + if (rvt_rnr_tbl_to_usec(i) >= timeout) + return i; + return 0; +} + +/** + * Central place for resource allocation at TID write responder, + * is called from write_req and write_data interrupt handlers as + * well as the send thread when a queued QP is scheduled for + * resource allocation. + * + * Iterates over (a) segments of a request and then (b) queued requests + * themselves to allocate resources for up to local->max_write + * segments across multiple requests. Stop allocating when we + * hit a sync point, resume allocating after data packets at + * sync point have been received. + * + * Resource allocation and sending of responses is decoupled. The + * request/segment which are being allocated and sent are as follows. + * Resources are allocated for: + * [request: qpriv->r_tid_alloc, segment: req->alloc_seg] + * The send thread sends: + * [request: qp->s_tail_ack_queue, segment:req->cur_seg] + */ +static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx) +{ + struct tid_rdma_request *req; + struct hfi1_qp_priv *qpriv = qp->priv; + struct hfi1_ctxtdata *rcd = qpriv->rcd; + struct tid_rdma_params *local = &qpriv->tid_rdma.local; + struct rvt_ack_entry *e; + u32 npkts, to_seg; + bool last; + int ret = 0; + + lockdep_assert_held(&qp->s_lock); + + while (1) { + /* + * Don't allocate more segments if a RNR NAK has already been + * scheduled to avoid messing up qp->r_psn: the RNR NAK will + * be sent only when all allocated segments have been sent. + * However, if more segments are allocated before that, TID RDMA + * WRITE RESP packets will be sent out for these new segments + * before the RNR NAK packet. When the requester receives the + * RNR NAK packet, it will restart with qp->s_last_psn + 1, + * which does not match qp->r_psn and will be dropped. + * Consequently, the requester will exhaust its retries and + * put the qp into error state. + */ + if (qpriv->rnr_nak_state == TID_RNR_NAK_SEND) + break; + + /* No requests left to process */ + if (qpriv->r_tid_alloc == qpriv->r_tid_head) { + /* If all data has been received, clear the flow */ + if (qpriv->flow_state.index < RXE_NUM_TID_FLOWS && + !qpriv->alloc_w_segs) + hfi1_kern_clear_hw_flow(rcd, qp); + break; + } + + e = &qp->s_ack_queue[qpriv->r_tid_alloc]; + if (e->opcode != TID_OP(WRITE_REQ)) + goto next_req; + req = ack_to_tid_req(e); + /* Finished allocating for all segments of this request */ + if (req->alloc_seg >= req->total_segs) + goto next_req; + + /* Can allocate only a maximum of local->max_write for a QP */ + if (qpriv->alloc_w_segs >= local->max_write) + break; + + /* Don't allocate at a sync point with data packets pending */ + if (qpriv->sync_pt && qpriv->alloc_w_segs) + break; + + /* All data received at the sync point, continue */ + if (qpriv->sync_pt && !qpriv->alloc_w_segs) { + hfi1_kern_clear_hw_flow(rcd, qp); + qpriv->sync_pt = false; + if (qpriv->s_flags & HFI1_R_TID_SW_PSN) + qpriv->s_flags &= ~HFI1_R_TID_SW_PSN; + } + + /* Allocate flow if we don't have one */ + if (qpriv->flow_state.index >= RXE_NUM_TID_FLOWS) { + ret = hfi1_kern_setup_hw_flow(qpriv->rcd, qp); + if (ret) { + to_seg = tid_rdma_flow_wt * + position_in_queue(qpriv, + &rcd->flow_queue); + break; + } + } + + npkts = rvt_div_round_up_mtu(qp, req->seg_len); + + /* + * We are at a sync point if we run out of KDETH PSN space. + * Last PSN of every generation is reserved for RESYNC. + */ + if (qpriv->flow_state.psn + npkts > MAX_TID_FLOW_PSN - 1) { + qpriv->sync_pt = true; + break; + } + + /* + * If overtaking req->acked_tail, send an RNR NAK. Because the + * QP is not queued in this case, and the issue can only be + * caused due a delay in scheduling the second leg which we + * cannot estimate, we use a rather arbitrary RNR timeout of + * (MAX_FLOWS / 2) segments + */ + if (!CIRC_SPACE(req->setup_head, req->acked_tail, + MAX_FLOWS)) { + ret = -EAGAIN; + to_seg = MAX_FLOWS >> 1; + qpriv->s_flags |= RVT_S_ACK_PENDING; + break; + } + + /* Try to allocate rcv array / TID entries */ + ret = hfi1_kern_exp_rcv_setup(req, &req->ss, &last); + if (ret == -EAGAIN) + to_seg = position_in_queue(qpriv, &rcd->rarr_queue); + if (ret) + break; + + qpriv->alloc_w_segs++; + req->alloc_seg++; + continue; +next_req: + /* Begin processing the next request */ + if (++qpriv->r_tid_alloc > + rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) + qpriv->r_tid_alloc = 0; + } + + /* + * Schedule an RNR NAK to be sent if (a) flow or rcv array allocation + * has failed (b) we are called from the rcv handler interrupt context + * (c) an RNR NAK has not already been scheduled + */ + if (ret == -EAGAIN && intr_ctx && !qp->r_nak_state) + goto send_rnr_nak; + + return; + +send_rnr_nak: + lockdep_assert_held(&qp->r_lock); + + /* Set r_nak_state to prevent unrelated events from generating NAK's */ + qp->r_nak_state = hfi1_compute_tid_rnr_timeout(qp, to_seg) | IB_RNR_NAK; + + /* Pull back r_psn to the segment being RNR NAK'd */ + qp->r_psn = e->psn + req->alloc_seg; + qp->r_ack_psn = qp->r_psn; + /* + * Pull back r_head_ack_queue to the ack entry following the request + * being RNR NAK'd. This allows resources to be allocated to the request + * if the queued QP is scheduled. + */ + qp->r_head_ack_queue = qpriv->r_tid_alloc + 1; + if (qp->r_head_ack_queue > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) + qp->r_head_ack_queue = 0; + qpriv->r_tid_head = qp->r_head_ack_queue; + /* + * These send side fields are used in make_rc_ack(). They are set in + * hfi1_send_rc_ack() but must be set here before dropping qp->s_lock + * for consistency + */ + qp->s_nak_state = qp->r_nak_state; + qp->s_ack_psn = qp->r_ack_psn; + /* + * Clear the ACK PENDING flag to prevent unwanted ACK because we + * have modified qp->s_ack_psn here. + */ + qp->s_flags &= ~(RVT_S_ACK_PENDING); + + /* + * qpriv->rnr_nak_state is used to determine when the scheduled RNR NAK + * has actually been sent. qp->s_flags RVT_S_ACK_PENDING bit cannot be + * used for this because qp->s_lock is dropped before calling + * hfi1_send_rc_ack() leading to inconsistency between the receive + * interrupt handlers and the send thread in make_rc_ack() + */ + qpriv->rnr_nak_state = TID_RNR_NAK_SEND; + + /* + * Schedule RNR NAK to be sent. RNR NAK's are scheduled from the receive + * interrupt handlers but will be sent from the send engine behind any + * previous responses that may have been scheduled + */ + rc_defered_ack(rcd, qp); +} + +void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet) +{ + /* HANDLER FOR TID RDMA WRITE REQUEST packet (Responder side)*/ + + /* + * 1. Verify TID RDMA WRITE REQ as per IB_OPCODE_RC_RDMA_WRITE_FIRST + * (see hfi1_rc_rcv()) + * - Don't allow 0-length requests. + * 2. Put TID RDMA WRITE REQ into the response queueu (s_ack_queue) + * - Setup struct tid_rdma_req with request info + * - Prepare struct tid_rdma_flow array? + * 3. Set the qp->s_ack_state as state diagram in design doc. + * 4. Set RVT_S_RESP_PENDING in s_flags. + * 5. Kick the send engine (hfi1_schedule_send()) + */ + struct hfi1_ctxtdata *rcd = packet->rcd; + struct rvt_qp *qp = packet->qp; + struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_ack_entry *e; + unsigned long flags; + struct ib_reth *reth; + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_request *req; + u32 bth0, psn, len, rkey, num_segs; + bool is_fecn; + u8 next; + u64 vaddr; + int diff; + + bth0 = be32_to_cpu(ohdr->bth[0]); + if (hfi1_ruc_check_hdr(ibp, packet)) + return; + + is_fecn = process_ecn(qp, packet); + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + + if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) + rvt_comm_est(qp); + + if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_WRITE))) + goto nack_inv; + + reth = &ohdr->u.tid_rdma.w_req.reth; + vaddr = be64_to_cpu(reth->vaddr); + len = be32_to_cpu(reth->length); + + num_segs = DIV_ROUND_UP(len, qpriv->tid_rdma.local.max_len); + diff = delta_psn(psn, qp->r_psn); + if (unlikely(diff)) { + if (tid_rdma_rcv_error(packet, ohdr, qp, psn, diff)) + return; + goto send_ack; + } + + /* + * The resent request which was previously RNR NAK'd is inserted at the + * location of the original request, which is one entry behind + * r_head_ack_queue + */ + if (qpriv->rnr_nak_state) + qp->r_head_ack_queue = qp->r_head_ack_queue ? + qp->r_head_ack_queue - 1 : + rvt_size_atomic(ib_to_rvt(qp->ibqp.device)); + + /* We've verified the request, insert it into the ack queue. */ + next = qp->r_head_ack_queue + 1; + if (next > rvt_size_atomic(ib_to_rvt(qp->ibqp.device))) + next = 0; + spin_lock_irqsave(&qp->s_lock, flags); + if (unlikely(next == qp->s_acked_ack_queue)) { + if (!qp->s_ack_queue[next].sent) + goto nack_inv_unlock; + update_ack_queue(qp, next); + } + e = &qp->s_ack_queue[qp->r_head_ack_queue]; + req = ack_to_tid_req(e); + + /* Bring previously RNR NAK'd request back to life */ + if (qpriv->rnr_nak_state) { + qp->r_nak_state = 0; + qp->s_nak_state = 0; + qpriv->rnr_nak_state = TID_RNR_NAK_INIT; + qp->r_psn = e->lpsn + 1; + req->state = TID_REQUEST_INIT; + goto update_head; + } + + if (e->rdma_sge.mr) { + rvt_put_mr(e->rdma_sge.mr); + e->rdma_sge.mr = NULL; + } + + /* The length needs to be in multiples of PAGE_SIZE */ + if (!len || len & ~PAGE_MASK) + goto nack_inv_unlock; + + rkey = be32_to_cpu(reth->rkey); + qp->r_len = len; + + if (e->opcode == TID_OP(WRITE_REQ) && + (req->setup_head != req->clear_tail || + req->clear_tail != req->acked_tail)) + goto nack_inv_unlock; + + if (unlikely(!rvt_rkey_ok(qp, &e->rdma_sge, qp->r_len, vaddr, + rkey, IB_ACCESS_REMOTE_WRITE))) + goto nack_acc; + + qp->r_psn += num_segs - 1; + + e->opcode = (bth0 >> 24) & 0xff; + e->psn = psn; + e->lpsn = qp->r_psn; + e->sent = 0; + + req->n_flows = min_t(u16, num_segs, qpriv->tid_rdma.local.max_write); + req->state = TID_REQUEST_INIT; + req->cur_seg = 0; + req->comp_seg = 0; + req->ack_seg = 0; + req->alloc_seg = 0; + req->isge = 0; + req->seg_len = qpriv->tid_rdma.local.max_len; + req->total_len = len; + req->total_segs = num_segs; + req->r_flow_psn = e->psn; + req->ss.sge = e->rdma_sge; + req->ss.num_sge = 1; + + req->flow_idx = req->setup_head; + req->clear_tail = req->setup_head; + req->acked_tail = req->setup_head; + + qp->r_state = e->opcode; + qp->r_nak_state = 0; + /* + * We need to increment the MSN here instead of when we + * finish sending the result since a duplicate request would + * increment it more than once. + */ + qp->r_msn++; + qp->r_psn++; + + if (qpriv->r_tid_tail == HFI1_QP_WQE_INVALID) { + qpriv->r_tid_tail = qp->r_head_ack_queue; + } else if (qpriv->r_tid_tail == qpriv->r_tid_head) { + struct tid_rdma_request *ptr; + + e = &qp->s_ack_queue[qpriv->r_tid_tail]; + ptr = ack_to_tid_req(e); + + if (e->opcode != TID_OP(WRITE_REQ) || + ptr->comp_seg == ptr->total_segs) { + if (qpriv->r_tid_tail == qpriv->r_tid_ack) + qpriv->r_tid_ack = qp->r_head_ack_queue; + qpriv->r_tid_tail = qp->r_head_ack_queue; + } + } +update_head: + qp->r_head_ack_queue = next; + qpriv->r_tid_head = qp->r_head_ack_queue; + + hfi1_tid_write_alloc_resources(qp, true); + + /* Schedule the send tasklet. */ + qp->s_flags |= RVT_S_RESP_PENDING; + hfi1_schedule_send(qp); + + spin_unlock_irqrestore(&qp->s_lock, flags); + if (is_fecn) + goto send_ack; + return; + +nack_inv_unlock: + spin_unlock_irqrestore(&qp->s_lock, flags); +nack_inv: + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + qp->r_nak_state = IB_NAK_INVALID_REQUEST; + qp->r_ack_psn = qp->r_psn; + /* Queue NAK for later */ + rc_defered_ack(rcd, qp); + return; +nack_acc: + spin_unlock_irqrestore(&qp->s_lock, flags); + rvt_rc_error(qp, IB_WC_LOC_PROT_ERR); + qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; + qp->r_ack_psn = qp->r_psn; +send_ack: + hfi1_send_rc_ack(packet, is_fecn); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 9b952351f072..7780a28db316 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -26,7 +26,9 @@ * * HFI1_S_TID_WAIT_INTERLCK - QP is waiting for requester interlock */ +/* BIT(4) reserved for RVT_S_ACK_PENDING. */ #define HFI1_S_TID_WAIT_INTERLCK BIT(5) +#define HFI1_R_TID_SW_PSN BIT(19) /* * Unlike regular IB RDMA VERBS, which do not require an entry @@ -89,10 +91,12 @@ struct tid_rdma_request { } e; struct tid_rdma_flow *flows; /* array of tid flows */ + struct rvt_sge_state ss; /* SGE state for TID RDMA requests */ u16 n_flows; /* size of the flow buffer window */ u16 setup_head; /* flow index we are setting up */ u16 clear_tail; /* flow index we are clearing */ u16 flow_idx; /* flow index most recently set up */ + u16 acked_tail; u32 seg_len; u32 total_len; @@ -103,6 +107,7 @@ struct tid_rdma_request { u32 cur_seg; /* index of current segment */ u32 comp_seg; /* index of last completed segment */ u32 ack_seg; /* index of last ack'ed segment */ + u32 alloc_seg; /* index of next segment to be allocated */ u32 isge; /* index of "current" sge */ u32 ack_pending; /* num acks pending for this request */ @@ -174,6 +179,12 @@ struct tid_rdma_flow { u32 tid_entry[TID_RDMA_MAX_PAGES]; }; +enum tid_rnr_nak_state { + TID_RNR_NAK_INIT = 0, + TID_RNR_NAK_SEND, + TID_RNR_NAK_SENT, +}; + bool tid_rdma_conn_req(struct rvt_qp *qp, u64 *data); bool tid_rdma_conn_reply(struct rvt_qp *qp, u64 data); bool tid_rdma_conn_resp(struct rvt_qp *qp, u64 *data); @@ -247,4 +258,9 @@ static inline void hfi1_setup_tid_rdma_wqe(struct rvt_qp *qp, u32 hfi1_build_tid_rdma_write_req(struct rvt_qp *qp, struct rvt_swqe *wqe, struct ib_other_headers *ohdr, u32 *bth1, u32 *bth2, u32 *len); + +void hfi1_compute_tid_rdma_flow_wt(void); + +void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 841727a684d5..9ced8a4a7b76 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -172,7 +172,15 @@ struct hfi1_qp_priv { unsigned long tid_timer_timeout_jiffies; /* variables for the TID RDMA SE state machine */ + u8 rnr_nak_state; /* RNR NAK state */ u32 s_flags; + u32 r_tid_head; /* Most recently added TID RDMA request */ + u32 r_tid_tail; /* the last completed TID RDMA request */ + u32 r_tid_ack; /* the TID RDMA request to be ACK'ed */ + u32 r_tid_alloc; /* Request for which we are allocating resources */ + u32 pending_tid_w_segs; /* Num of pending tid write segments */ + u32 alloc_w_segs; /* Number of segments for which write */ + /* resources have been allocated for this QP */ /* For TID RDMA READ */ u32 tid_r_reqs; /* Num of tid reads requested */ @@ -180,8 +188,12 @@ struct hfi1_qp_priv { u32 pending_tid_r_segs; /* Num of pending tid read segments */ u16 pkts_ps; /* packets per segment */ u8 timeout_shift; /* account for number of packets per segment */ + + u8 sync_pt; /* Set when QP reaches sync point */ }; +#define HFI1_QP_WQE_INVALID ((u32)-1) + struct hfi1_swqe_priv { struct tid_rdma_request tid_req; struct rvt_sge_state ss; /* Used for TID RDMA READ Request */ From 38d46d3676ed6ecba284eb49e4b675ca9891801a Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:49:09 -0800 Subject: [PATCH 1153/4277] IB/hfi1: Add a function to build TID RDMA WRITE response This patch adds the function to build TID RDMA WRITE response. The main role of the TID RDMA WRITE RESP packet is to send TID entries to the requester so that they can be used to encode TID RDMA WRITE DATA packet. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 95 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 5 ++ drivers/infiniband/hw/hfi1/verbs.h | 1 + 3 files changed, 101 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 516dca9f497e..78828f9f7592 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -3583,3 +3583,98 @@ nack_acc: send_ack: hfi1_send_rc_ack(packet, is_fecn); } + +u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_other_headers *ohdr, u32 *bth1, + u32 bth2, u32 *len, + struct rvt_sge_state **ss) +{ + struct hfi1_ack_priv *epriv = e->priv; + struct tid_rdma_request *req = &epriv->tid_req; + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_flow *flow = NULL; + u32 resp_len = 0, hdwords = 0; + void *resp_addr = NULL; + struct tid_rdma_params *remote; + + flow = &req->flows[req->flow_idx]; + switch (req->state) { + default: + /* + * Try to allocate resources here in case QP was queued and was + * later scheduled when resources became available + */ + hfi1_tid_write_alloc_resources(qp, false); + + /* We've already sent everything which is ready */ + if (req->cur_seg >= req->alloc_seg) + goto done; + + /* + * Resources can be assigned but responses cannot be sent in + * rnr_nak state, till the resent request is received + */ + if (qpriv->rnr_nak_state == TID_RNR_NAK_SENT) + goto done; + + req->state = TID_REQUEST_ACTIVE; + req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS); + break; + + case TID_REQUEST_RESEND_ACTIVE: + case TID_REQUEST_RESEND: + req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS); + if (!CIRC_CNT(req->setup_head, req->flow_idx, MAX_FLOWS)) + req->state = TID_REQUEST_ACTIVE; + + break; + } + flow->flow_state.resp_ib_psn = bth2; + resp_addr = (void *)flow->tid_entry; + resp_len = sizeof(*flow->tid_entry) * flow->tidcnt; + req->cur_seg++; + + memset(&ohdr->u.tid_rdma.w_rsp, 0, sizeof(ohdr->u.tid_rdma.w_rsp)); + epriv->ss.sge.vaddr = resp_addr; + epriv->ss.sge.sge_length = resp_len; + epriv->ss.sge.length = epriv->ss.sge.sge_length; + /* + * We can safely zero these out. Since the first SGE covers the + * entire packet, nothing else should even look at the MR. + */ + epriv->ss.sge.mr = NULL; + epriv->ss.sge.m = 0; + epriv->ss.sge.n = 0; + + epriv->ss.sg_list = NULL; + epriv->ss.total_len = epriv->ss.sge.sge_length; + epriv->ss.num_sge = 1; + + *ss = &epriv->ss; + *len = epriv->ss.total_len; + + /* Construct the TID RDMA WRITE RESP packet header */ + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + + KDETH_RESET(ohdr->u.tid_rdma.w_rsp.kdeth0, KVER, 0x1); + KDETH_RESET(ohdr->u.tid_rdma.w_rsp.kdeth1, JKEY, remote->jkey); + ohdr->u.tid_rdma.w_rsp.aeth = rvt_compute_aeth(qp); + ohdr->u.tid_rdma.w_rsp.tid_flow_psn = + cpu_to_be32((flow->flow_state.generation << + HFI1_KDETH_BTH_SEQ_SHIFT) | + (flow->flow_state.spsn & + HFI1_KDETH_BTH_SEQ_MASK)); + ohdr->u.tid_rdma.w_rsp.tid_flow_qp = + cpu_to_be32(qpriv->tid_rdma.local.qp | + ((flow->idx & TID_RDMA_DESTQP_FLOW_MASK) << + TID_RDMA_DESTQP_FLOW_SHIFT) | + qpriv->rcd->ctxt); + ohdr->u.tid_rdma.w_rsp.verbs_qp = cpu_to_be32(qp->remote_qpn); + *bth1 = remote->qp; + rcu_read_unlock(); + hdwords = sizeof(ohdr->u.tid_rdma.w_rsp) / sizeof(u32); + qpriv->pending_tid_w_segs++; +done: + return hdwords; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 7780a28db316..19f4dd89680f 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -263,4 +263,9 @@ void hfi1_compute_tid_rdma_flow_wt(void); void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet); +u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_other_headers *ohdr, u32 *bth1, + u32 bth2, u32 *len, + struct rvt_sge_state **ss); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 9ced8a4a7b76..3a501b09621e 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -200,6 +200,7 @@ struct hfi1_swqe_priv { }; struct hfi1_ack_priv { + struct rvt_sge_state ss; /* used for TID WRITE RESP */ struct tid_rdma_request tid_req; }; From 3c759e003a6a4d4b8fd0472f9501e8c45d775c26 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:49:19 -0800 Subject: [PATCH 1154/4277] IB/hfi1: Add TID resource timer This patch adds the TID resource timer, which is used by the responder to free any TID resources that are allocated for TID RDMA WRITE request and not returned by the requester after a reasonable time. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/qp.c | 1 + drivers/infiniband/hw/hfi1/tid_rdma.c | 92 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 3 + drivers/infiniband/hw/hfi1/verbs.h | 1 + 4 files changed, 97 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index acdd9eba189b..31b4b60f4364 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -755,6 +755,7 @@ void quiesce_qp(struct rvt_qp *qp) { struct hfi1_qp_priv *priv = qp->priv; + hfi1_del_tid_reap_timer(qp); iowait_sdma_drain(&priv->s_iowait); qp_pio_drain(qp); flush_tx_list(qp); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 78828f9f7592..ede25ee195ff 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -118,6 +118,9 @@ static int hfi1_kern_exp_rcv_alloc_flows(struct tid_rdma_request *req, static void hfi1_init_trdma_req(struct rvt_qp *qp, struct tid_rdma_request *req); static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx); +static void hfi1_tid_timeout(struct timer_list *t); +static void hfi1_add_tid_reap_timer(struct rvt_qp *qp); +static void hfi1_mod_tid_reap_timer(struct rvt_qp *qp); static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { @@ -321,6 +324,7 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->r_tid_tail = HFI1_QP_WQE_INVALID; qpriv->r_tid_ack = HFI1_QP_WQE_INVALID; qpriv->r_tid_alloc = HFI1_QP_WQE_INVALID; + timer_setup(&qpriv->s_tid_timer, hfi1_tid_timeout, 0); INIT_LIST_HEAD(&qpriv->tid_wait); if (init_attr->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { @@ -3619,6 +3623,7 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, req->state = TID_REQUEST_ACTIVE; req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS); + hfi1_add_tid_reap_timer(qp); break; case TID_REQUEST_RESEND_ACTIVE: @@ -3627,6 +3632,7 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, if (!CIRC_CNT(req->setup_head, req->flow_idx, MAX_FLOWS)) req->state = TID_REQUEST_ACTIVE; + hfi1_mod_tid_reap_timer(qp); break; } flow->flow_state.resp_ib_psn = bth2; @@ -3678,3 +3684,89 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, done: return hdwords; } + +static void hfi1_add_tid_reap_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + + lockdep_assert_held(&qp->s_lock); + if (!(qpriv->s_flags & HFI1_R_TID_RSC_TIMER)) { + qpriv->s_flags |= HFI1_R_TID_RSC_TIMER; + qpriv->s_tid_timer.expires = jiffies + + qpriv->tid_timer_timeout_jiffies; + add_timer(&qpriv->s_tid_timer); + } +} + +static void hfi1_mod_tid_reap_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + + lockdep_assert_held(&qp->s_lock); + qpriv->s_flags |= HFI1_R_TID_RSC_TIMER; + mod_timer(&qpriv->s_tid_timer, jiffies + + qpriv->tid_timer_timeout_jiffies); +} + +static int hfi1_stop_tid_reap_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + int rval = 0; + + lockdep_assert_held(&qp->s_lock); + if (qpriv->s_flags & HFI1_R_TID_RSC_TIMER) { + rval = del_timer(&qpriv->s_tid_timer); + qpriv->s_flags &= ~HFI1_R_TID_RSC_TIMER; + } + return rval; +} + +void hfi1_del_tid_reap_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + + del_timer_sync(&qpriv->s_tid_timer); + qpriv->s_flags &= ~HFI1_R_TID_RSC_TIMER; +} + +static void hfi1_tid_timeout(struct timer_list *t) +{ + struct hfi1_qp_priv *qpriv = from_timer(qpriv, t, s_tid_timer); + struct rvt_qp *qp = qpriv->owner; + struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); + unsigned long flags; + u32 i; + + spin_lock_irqsave(&qp->r_lock, flags); + spin_lock(&qp->s_lock); + if (qpriv->s_flags & HFI1_R_TID_RSC_TIMER) { + dd_dev_warn(dd_from_ibdev(qp->ibqp.device), "[QP%u] %s %d\n", + qp->ibqp.qp_num, __func__, __LINE__); + hfi1_stop_tid_reap_timer(qp); + /* + * Go though the entire ack queue and clear any outstanding + * HW flow and RcvArray resources. + */ + hfi1_kern_clear_hw_flow(qpriv->rcd, qp); + for (i = 0; i < rvt_max_atomic(rdi); i++) { + struct tid_rdma_request *req = + ack_to_tid_req(&qp->s_ack_queue[i]); + + hfi1_kern_exp_rcv_clear_all(req); + } + spin_unlock(&qp->s_lock); + if (qp->ibqp.event_handler) { + struct ib_event ev; + + ev.device = qp->ibqp.device; + ev.element.qp = &qp->ibqp; + ev.event = IB_EVENT_QP_FATAL; + qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); + } + rvt_rc_error(qp, IB_WC_RESP_TIMEOUT_ERR); + goto unlock_r_lock; + } + spin_unlock(&qp->s_lock); +unlock_r_lock: + spin_unlock_irqrestore(&qp->r_lock, flags); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 19f4dd89680f..39137e3c79fe 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -26,6 +26,7 @@ * * HFI1_S_TID_WAIT_INTERLCK - QP is waiting for requester interlock */ +#define HFI1_R_TID_RSC_TIMER BIT(2) /* BIT(4) reserved for RVT_S_ACK_PENDING. */ #define HFI1_S_TID_WAIT_INTERLCK BIT(5) #define HFI1_R_TID_SW_PSN BIT(19) @@ -268,4 +269,6 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, u32 bth2, u32 *len, struct rvt_sge_state **ss); +void hfi1_del_tid_reap_timer(struct rvt_qp *qp); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 3a501b09621e..68a41f54bc78 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -163,6 +163,7 @@ struct hfi1_qp_priv { u32 tid_enqueue; /* saved when tid waited */ u8 s_sc; /* SC[0..4] for next packet */ struct iowait s_iowait; + struct timer_list s_tid_timer; /* for timing tid wait */ struct list_head tid_wait; /* for queueing tid space */ struct hfi1_opfn_data opfn; struct tid_flow_state flow_state; From 72a0ea99ec13bcb27784c1a48f4e8fda61586c26 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:49:31 -0800 Subject: [PATCH 1155/4277] IB/hfi1: Add a function to receive TID RDMA WRITE response This patch adds a function to receive TID RDMA WRITE response. The TID entries will be stored for encoding TID RDMA WRITE DATA packet later. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 173 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 + drivers/infiniband/hw/hfi1/verbs.h | 3 + 3 files changed, 180 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index ede25ee195ff..92b6a3d90ce5 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -319,6 +319,9 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->flow_state.index = RXE_NUM_TID_FLOWS; qpriv->flow_state.last_index = RXE_NUM_TID_FLOWS; qpriv->flow_state.generation = KERN_GENERATION_RESERVED; + qpriv->s_tid_cur = HFI1_QP_WQE_INVALID; + qpriv->s_tid_head = HFI1_QP_WQE_INVALID; + qpriv->s_tid_tail = HFI1_QP_WQE_INVALID; qpriv->rnr_nak_state = TID_RNR_NAK_INIT; qpriv->r_tid_head = HFI1_QP_WQE_INVALID; qpriv->r_tid_tail = HFI1_QP_WQE_INVALID; @@ -3770,3 +3773,173 @@ static void hfi1_tid_timeout(struct timer_list *t) unlock_r_lock: spin_unlock_irqrestore(&qp->r_lock, flags); } + +void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet) +{ + /* HANDLER FOR TID RDMA WRITE RESPONSE packet (Requestor side */ + + /* + * 1. Find matching SWQE + * 2. Check that TIDENTRY array has enough space for a complete + * segment. If not, put QP in error state. + * 3. Save response data in struct tid_rdma_req and struct tid_rdma_flow + * 4. Remove HFI1_S_WAIT_TID_RESP from s_flags. + * 5. Set qp->s_state + * 6. Kick the send engine (hfi1_schedule_send()) + */ + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *qpriv = qp->priv; + struct hfi1_ctxtdata *rcd = packet->rcd; + struct rvt_swqe *wqe; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + enum ib_wc_status status; + u32 opcode, aeth, psn, flow_psn, i, tidlen = 0, pktlen; + bool is_fecn; + unsigned long flags; + + is_fecn = process_ecn(qp, packet); + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + aeth = be32_to_cpu(ohdr->u.tid_rdma.w_rsp.aeth); + opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff; + + spin_lock_irqsave(&qp->s_lock, flags); + + /* Ignore invalid responses */ + if (cmp_psn(psn, qp->s_next_psn) >= 0) + goto ack_done; + + /* Ignore duplicate responses. */ + if (unlikely(cmp_psn(psn, qp->s_last_psn) <= 0)) + goto ack_done; + + if (unlikely(qp->s_acked == qp->s_tail)) + goto ack_done; + + /* + * If we are waiting for a particular packet sequence number + * due to a request being resent, check for it. Otherwise, + * ensure that we haven't missed anything. + */ + if (qp->r_flags & RVT_R_RDMAR_SEQ) { + if (cmp_psn(psn, qp->s_last_psn + 1) != 0) + goto ack_done; + qp->r_flags &= ~RVT_R_RDMAR_SEQ; + } + + wqe = rvt_get_swqe_ptr(qp, qpriv->s_tid_cur); + if (unlikely(wqe->wr.opcode != IB_WR_TID_RDMA_WRITE)) + goto ack_op_err; + + req = wqe_to_tid_req(wqe); + /* + * If we've lost ACKs and our acked_tail pointer is too far + * behind, don't overwrite segments. Just drop the packet and + * let the reliability protocol take care of it. + */ + if (!CIRC_SPACE(req->setup_head, req->acked_tail, MAX_FLOWS)) + goto ack_done; + + /* + * The call to do_rc_ack() should be last in the chain of + * packet checks because it will end up updating the QP state. + * Therefore, anything that would prevent the packet from + * being accepted as a successful response should be prior + * to it. + */ + if (!do_rc_ack(qp, aeth, psn, opcode, 0, rcd)) + goto ack_done; + + flow = &req->flows[req->setup_head]; + flow->pkt = 0; + flow->tid_idx = 0; + flow->tid_offset = 0; + flow->sent = 0; + flow->resync_npkts = 0; + flow->tid_qpn = be32_to_cpu(ohdr->u.tid_rdma.w_rsp.tid_flow_qp); + flow->idx = (flow->tid_qpn >> TID_RDMA_DESTQP_FLOW_SHIFT) & + TID_RDMA_DESTQP_FLOW_MASK; + flow_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.w_rsp.tid_flow_psn)); + flow->flow_state.generation = flow_psn >> HFI1_KDETH_BTH_SEQ_SHIFT; + flow->flow_state.spsn = flow_psn & HFI1_KDETH_BTH_SEQ_MASK; + flow->flow_state.resp_ib_psn = psn; + flow->length = min_t(u32, req->seg_len, + (wqe->length - (req->comp_seg * req->seg_len))); + + flow->npkts = rvt_div_round_up_mtu(qp, flow->length); + flow->flow_state.lpsn = flow->flow_state.spsn + + flow->npkts - 1; + /* payload length = packet length - (header length + ICRC length) */ + pktlen = packet->tlen - (packet->hlen + 4); + if (pktlen > sizeof(flow->tid_entry)) { + status = IB_WC_LOC_LEN_ERR; + goto ack_err; + } + memcpy(flow->tid_entry, packet->ebuf, pktlen); + flow->tidcnt = pktlen / sizeof(*flow->tid_entry); + + req->comp_seg++; + /* + * Walk the TID_ENTRY list to make sure we have enough space for a + * complete segment. + */ + for (i = 0; i < flow->tidcnt; i++) { + if (!EXP_TID_GET(flow->tid_entry[i], LEN)) { + status = IB_WC_LOC_LEN_ERR; + goto ack_err; + } + tidlen += EXP_TID_GET(flow->tid_entry[i], LEN); + } + if (tidlen * PAGE_SIZE < flow->length) { + status = IB_WC_LOC_LEN_ERR; + goto ack_err; + } + + /* + * If this is the first response for this request, set the initial + * flow index to the current flow. + */ + if (!cmp_psn(psn, wqe->psn)) { + req->r_last_acked = mask_psn(wqe->psn - 1); + /* Set acked flow index to head index */ + req->acked_tail = req->setup_head; + } + + /* advance circular buffer head */ + req->setup_head = CIRC_NEXT(req->setup_head, MAX_FLOWS); + req->state = TID_REQUEST_ACTIVE; + + /* + * If all responses for this TID RDMA WRITE request have been received + * advance the pointer to the next one. + * Since TID RDMA requests could be mixed in with regular IB requests, + * they might not appear sequentially in the queue. Therefore, the + * next request needs to be "found". + */ + if (qpriv->s_tid_cur != qpriv->s_tid_head && + req->comp_seg == req->total_segs) { + for (i = qpriv->s_tid_cur + 1; ; i++) { + if (i == qp->s_size) + i = 0; + wqe = rvt_get_swqe_ptr(qp, i); + if (i == qpriv->s_tid_head) + break; + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) + break; + } + qpriv->s_tid_cur = i; + } + qp->s_flags &= ~HFI1_S_WAIT_TID_RESP; + + goto ack_done; + +ack_op_err: + status = IB_WC_LOC_QP_OP_ERR; +ack_err: + rvt_error_qp(qp, status); +ack_done: + spin_unlock_irqrestore(&qp->s_lock, flags); + if (is_fecn) + hfi1_send_rc_ack(packet, is_fecn); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 39137e3c79fe..6f11fd5ca4c0 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -102,6 +102,7 @@ struct tid_rdma_request { u32 seg_len; u32 total_len; u32 r_flow_psn; /* IB PSN of next segment start */ + u32 r_last_acked; /* IB PSN of last ACK'ed packet */ u32 s_next_psn; /* IB PSN of next segment start for read */ u32 total_segs; /* segments required to complete a request */ @@ -175,6 +176,7 @@ struct tid_rdma_flow { u8 npagesets; u8 npkts; u8 pkt; + u8 resync_npkts; struct kern_tid_node tnode[TID_RDMA_MAX_PAGES]; struct tid_rdma_pageset pagesets[TID_RDMA_MAX_PAGES]; u32 tid_entry[TID_RDMA_MAX_PAGES]; @@ -271,4 +273,6 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, void hfi1_del_tid_reap_timer(struct rvt_qp *qp); +void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 68a41f54bc78..b2096c7c1132 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -175,6 +175,9 @@ struct hfi1_qp_priv { /* variables for the TID RDMA SE state machine */ u8 rnr_nak_state; /* RNR NAK state */ u32 s_flags; + u32 s_tid_cur; + u32 s_tid_head; + u32 s_tid_tail; u32 r_tid_head; /* Most recently added TID RDMA request */ u32 r_tid_tail; /* the last completed TID RDMA request */ u32 r_tid_ack; /* the TID RDMA request to be ACK'ed */ From 539e1908e45b5cdcc72bded272f8adb52ad2c913 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:49:41 -0800 Subject: [PATCH 1156/4277] IB/hfi1: Add a function to build TID RDMA WRITE DATA packet This patch adds a function to build TID RDMA WRITE DATA packet. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 62 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 ++ 2 files changed, 66 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 92b6a3d90ce5..243feaddb811 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -3943,3 +3943,65 @@ ack_done: if (is_fecn) hfi1_send_rc_ack(packet, is_fecn); } + +bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, + u32 *bth1, u32 *bth2, u32 *len) +{ + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct tid_rdma_flow *flow = &req->flows[req->clear_tail]; + struct tid_rdma_params *remote; + struct rvt_qp *qp = req->qp; + struct hfi1_qp_priv *qpriv = qp->priv; + u32 tidentry = flow->tid_entry[flow->tid_idx]; + u32 tidlen = EXP_TID_GET(tidentry, LEN) << PAGE_SHIFT; + struct tid_rdma_write_data *wd = &ohdr->u.tid_rdma.w_data; + u32 next_offset, om = KDETH_OM_LARGE; + bool last_pkt; + + if (!tidlen) { + hfi1_trdma_send_complete(qp, wqe, IB_WC_REM_INV_RD_REQ_ERR); + rvt_error_qp(qp, IB_WC_REM_INV_RD_REQ_ERR); + } + + *len = min_t(u32, qp->pmtu, tidlen - flow->tid_offset); + flow->sent += *len; + next_offset = flow->tid_offset + *len; + last_pkt = (flow->tid_idx == (flow->tidcnt - 1) && + next_offset >= tidlen) || (flow->sent >= flow->length); + + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + KDETH_RESET(wd->kdeth0, KVER, 0x1); + KDETH_SET(wd->kdeth0, SH, !last_pkt); + KDETH_SET(wd->kdeth0, INTR, !!(!last_pkt && remote->urg)); + KDETH_SET(wd->kdeth0, TIDCTRL, EXP_TID_GET(tidentry, CTRL)); + KDETH_SET(wd->kdeth0, TID, EXP_TID_GET(tidentry, IDX)); + KDETH_SET(wd->kdeth0, OM, om == KDETH_OM_LARGE); + KDETH_SET(wd->kdeth0, OFFSET, flow->tid_offset / om); + KDETH_RESET(wd->kdeth1, JKEY, remote->jkey); + wd->verbs_qp = cpu_to_be32(qp->remote_qpn); + rcu_read_unlock(); + + *bth1 = flow->tid_qpn; + *bth2 = mask_psn(((flow->flow_state.spsn + flow->pkt++) & + HFI1_KDETH_BTH_SEQ_MASK) | + (flow->flow_state.generation << + HFI1_KDETH_BTH_SEQ_SHIFT)); + if (last_pkt) { + /* PSNs are zero-based, so +1 to count number of packets */ + if (flow->flow_state.lpsn + 1 + + rvt_div_round_up_mtu(qp, req->seg_len) > + MAX_TID_FLOW_PSN) + req->state = TID_REQUEST_SYNC; + *bth2 |= IB_BTH_REQ_ACK; + } + + if (next_offset >= tidlen) { + flow->tid_offset = 0; + flow->tid_idx++; + } else { + flow->tid_offset = next_offset; + } + return last_pkt; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 6f11fd5ca4c0..f28c7ab752b2 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -275,4 +275,8 @@ void hfi1_del_tid_reap_timer(struct rvt_qp *qp); void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet); +bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, + u32 *bth1, u32 *bth2, u32 *len); + #endif /* HFI1_TID_RDMA_H */ From d72fe7d5008b5600a11f03a0dcb743fd7acb0085 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:49:51 -0800 Subject: [PATCH 1157/4277] IB/hfi1: Add a function to receive TID RDMA WRITE DATA packet This patch adds a function to receive TID RDMA WRITE DATA packet, which is in the KDETH PSN space in packet ordering. Due to the use of header suppression, software is generally only notified when the last data packet for a segment is received. This patch also adds code to handle KDETH EFLAGS errors for ingress TID RDMA WRITE DATA packets. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 236 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 2 + drivers/infiniband/hw/hfi1/verbs.h | 3 + 3 files changed, 241 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 243feaddb811..166a34c8449d 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2566,13 +2566,32 @@ static bool tid_rdma_tid_err(struct hfi1_ctxtdata *rcd, u8 opcode) { struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *qpriv = qp->priv; u32 ipsn; struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_ack_entry *e; + struct tid_rdma_request *req; + struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); + u32 i; if (rcv_type >= RHF_RCV_TYPE_IB) goto done; spin_lock(&qp->s_lock); + + /* + * We've ran out of space in the eager buffer. + * Eagerly received KDETH packets which require space in the + * Eager buffer (packet that have payload) are TID RDMA WRITE + * response packets. In this case, we have to re-transmit the + * TID RDMA WRITE request. + */ + if (rcv_type == RHF_RCV_TYPE_EAGER) { + hfi1_restart_rc(qp, qp->s_last_psn + 1, 1); + hfi1_schedule_send(qp); + goto done_unlock; + } + /* * For TID READ response, error out QP after freeing the tid * resources. @@ -2586,8 +2605,25 @@ static bool tid_rdma_tid_err(struct hfi1_ctxtdata *rcd, rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); goto done; } + goto done_unlock; } + /* + * Error out the qp for TID RDMA WRITE + */ + hfi1_kern_clear_hw_flow(qpriv->rcd, qp); + for (i = 0; i < rvt_max_atomic(rdi); i++) { + e = &qp->s_ack_queue[i]; + if (e->opcode == TID_OP(WRITE_REQ)) { + req = ack_to_tid_req(e); + hfi1_kern_exp_rcv_clear_all(req); + } + } + spin_unlock(&qp->s_lock); + rvt_rc_error(qp, IB_WC_LOC_LEN_ERR); + goto done; + +done_unlock: spin_unlock(&qp->s_lock); done: return true; @@ -2837,8 +2873,12 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, u8 opcode; u32 qp_num, psn, ibpsn; struct rvt_qp *qp; + struct hfi1_qp_priv *qpriv; unsigned long flags; bool ret = true; + struct rvt_ack_entry *e; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; trace_hfi1_msg_handle_kdeth_eflags(NULL, "Kdeth error: rhf ", packet->rhf); @@ -2897,14 +2937,109 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, ibpsn = mask_psn(ibpsn); ret = handle_read_kdeth_eflags(rcd, packet, rcv_type, rte, psn, ibpsn); + goto r_unlock; } + /* + * qp->s_tail_ack_queue points to the rvt_ack_entry currently being + * processed. These a completed sequentially so we can be sure that + * the pointer will not change until the entire request has completed. + */ + spin_lock(&qp->s_lock); + qpriv = qp->priv; + e = &qp->s_ack_queue[qpriv->r_tid_tail]; + req = ack_to_tid_req(e); + flow = &req->flows[req->clear_tail]; + + switch (rcv_type) { + case RHF_RCV_TYPE_EXPECTED: + switch (rte) { + case RHF_RTE_EXPECTED_FLOW_SEQ_ERR: + if (!(qpriv->s_flags & HFI1_R_TID_SW_PSN)) { + u64 reg; + + qpriv->s_flags |= HFI1_R_TID_SW_PSN; + /* + * The only sane way to get the amount of + * progress is to read the HW flow state. + */ + reg = read_uctxt_csr(dd, rcd->ctxt, + RCV_TID_FLOW_TABLE + + (8 * flow->idx)); + flow->flow_state.r_next_psn = mask_psn(reg); + qpriv->r_next_psn_kdeth = + flow->flow_state.r_next_psn; + goto nak_psn; + } else { + /* + * If the received PSN does not match the next + * expected PSN, NAK the packet. + * However, only do that if we know that the a + * NAK has already been sent. Otherwise, this + * mismatch could be due to packets that were + * already in flight. + */ + if (psn != flow->flow_state.r_next_psn) { + psn = flow->flow_state.r_next_psn; + goto nak_psn; + } + + qpriv->s_nak_state = 0; + /* + * If SW PSN verification is successful and this + * is the last packet in the segment, tell the + * caller to process it as a normal packet. + */ + if (psn == full_flow_psn(flow, + flow->flow_state.lpsn)) + ret = false; + qpriv->r_next_psn_kdeth = + ++flow->flow_state.r_next_psn; + } + break; + + case RHF_RTE_EXPECTED_FLOW_GEN_ERR: + goto nak_psn; + + default: + break; + } + break; + + case RHF_RCV_TYPE_ERROR: + switch (rte) { + case RHF_RTE_ERROR_OP_CODE_ERR: + case RHF_RTE_ERROR_KHDR_MIN_LEN_ERR: + case RHF_RTE_ERROR_KHDR_HCRC_ERR: + case RHF_RTE_ERROR_KHDR_KVER_ERR: + case RHF_RTE_ERROR_CONTEXT_ERR: + case RHF_RTE_ERROR_KHDR_TID_ERR: + default: + break; + } + default: + break; + } + +unlock: + spin_unlock(&qp->s_lock); r_unlock: spin_unlock_irqrestore(&qp->r_lock, flags); rcu_unlock: rcu_read_unlock(); drop: return ret; +nak_psn: + ibp->rvp.n_rc_seqnak++; + if (!qpriv->s_nak_state) { + qpriv->s_nak_state = IB_NAK_PSN_ERROR; + /* We are NAK'ing the next expected PSN */ + qpriv->s_nak_psn = mask_psn(flow->flow_state.r_next_psn); + qpriv->s_flags |= RVT_S_ACK_PENDING; + if (qpriv->r_tid_ack == HFI1_QP_WQE_INVALID) + qpriv->r_tid_ack = qpriv->r_tid_tail; + } + goto unlock; } /* @@ -4005,3 +4140,104 @@ bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe, } return last_pkt; } + +void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet) +{ + struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_ctxtdata *rcd = priv->rcd; + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_ack_entry *e; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); + unsigned long flags; + u32 psn, next; + u8 opcode; + + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0xff; + + /* + * All error handling should be done by now. If we are here, the packet + * is either good or been accepted by the error handler. + */ + spin_lock_irqsave(&qp->s_lock, flags); + e = &qp->s_ack_queue[priv->r_tid_tail]; + req = ack_to_tid_req(e); + flow = &req->flows[req->clear_tail]; + if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.lpsn))) { + if (cmp_psn(psn, flow->flow_state.r_next_psn)) + goto send_nak; + flow->flow_state.r_next_psn++; + goto exit; + } + flow->flow_state.r_next_psn = mask_psn(psn + 1); + hfi1_kern_exp_rcv_clear(req); + priv->alloc_w_segs--; + rcd->flows[flow->idx].psn = psn & HFI1_KDETH_BTH_SEQ_MASK; + req->comp_seg++; + priv->s_nak_state = 0; + + /* + * Release the flow if one of the following conditions has been met: + * - The request has reached a sync point AND all outstanding + * segments have been completed, or + * - The entire request is complete and there are no more requests + * (of any kind) in the queue. + */ + if (priv->r_tid_ack == HFI1_QP_WQE_INVALID) + priv->r_tid_ack = priv->r_tid_tail; + + if (opcode == TID_OP(WRITE_DATA_LAST)) { + for (next = priv->r_tid_tail + 1; ; next++) { + if (next > rvt_size_atomic(&dev->rdi)) + next = 0; + if (next == priv->r_tid_head) + break; + e = &qp->s_ack_queue[next]; + if (e->opcode == TID_OP(WRITE_REQ)) + break; + } + priv->r_tid_tail = next; + if (++qp->s_acked_ack_queue > rvt_size_atomic(&dev->rdi)) + qp->s_acked_ack_queue = 0; + } + + hfi1_tid_write_alloc_resources(qp, true); + + /* + * If we need to generate more responses, schedule the + * send engine. + */ + if (req->cur_seg < req->total_segs || + qp->s_tail_ack_queue != qp->r_head_ack_queue) { + qp->s_flags |= RVT_S_RESP_PENDING; + hfi1_schedule_send(qp); + } + + priv->pending_tid_w_segs--; + if (priv->s_flags & HFI1_R_TID_RSC_TIMER) { + if (priv->pending_tid_w_segs) + hfi1_mod_tid_reap_timer(req->qp); + else + hfi1_stop_tid_reap_timer(req->qp); + } + +done: + priv->s_flags |= RVT_S_ACK_PENDING; +exit: + priv->r_next_psn_kdeth = flow->flow_state.r_next_psn; + spin_unlock_irqrestore(&qp->s_lock, flags); + return; + +send_nak: + if (!priv->s_nak_state) { + priv->s_nak_state = IB_NAK_PSN_ERROR; + priv->s_nak_psn = flow->flow_state.r_next_psn; + priv->s_flags |= RVT_S_ACK_PENDING; + if (priv->r_tid_ack == HFI1_QP_WQE_INVALID) + priv->r_tid_ack = priv->r_tid_tail; + } + goto done; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index f28c7ab752b2..647a6f0cba31 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -279,4 +279,6 @@ bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe, struct ib_other_headers *ohdr, u32 *bth1, u32 *bth2, u32 *len); +void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index b2096c7c1132..eec6e822635b 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -174,6 +174,8 @@ struct hfi1_qp_priv { /* variables for the TID RDMA SE state machine */ u8 rnr_nak_state; /* RNR NAK state */ + u8 s_nak_state; + u32 s_nak_psn; u32 s_flags; u32 s_tid_cur; u32 s_tid_head; @@ -193,6 +195,7 @@ struct hfi1_qp_priv { u16 pkts_ps; /* packets per segment */ u8 timeout_shift; /* account for number of packets per segment */ + u32 r_next_psn_kdeth; u8 sync_pt; /* Set when QP reaches sync point */ }; From 0f75e325aa11552599a18d7558970be16fc15c1a Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:50:03 -0800 Subject: [PATCH 1158/4277] IB/hfi1: Add a function to build TID RDMA ACK packet This patch adds a function to build TID RDMA ACJ packet, which is also in the KDETH PSN space for packet ordering. This packet is used to acknowledge the receiving of all the TID RDMA WRITE DATA packets before the given KDETH PSN. Similar to RC ACK packets, TID RDMA ACK packets could also be coalesced. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 77 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 ++ drivers/infiniband/hw/hfi1/verbs.h | 2 + 3 files changed, 83 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 166a34c8449d..d8a7f07b028d 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -4241,3 +4241,80 @@ send_nak: } goto done; } + +static bool hfi1_tid_rdma_is_resync_psn(u32 psn) +{ + return (bool)((psn & HFI1_KDETH_BTH_SEQ_MASK) == + HFI1_KDETH_BTH_SEQ_MASK); +} + +u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_other_headers *ohdr, u16 iflow, + u32 *bth1, u32 *bth2) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_flow_state *fs = &qpriv->flow_state; + struct tid_rdma_request *req = ack_to_tid_req(e); + struct tid_rdma_flow *flow = &req->flows[iflow]; + struct tid_rdma_params *remote; + + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth1, JKEY, remote->jkey); + ohdr->u.tid_rdma.ack.verbs_qp = cpu_to_be32(qp->remote_qpn); + *bth1 = remote->qp; + rcu_read_unlock(); + + if (qpriv->resync) { + *bth2 = mask_psn((fs->generation << + HFI1_KDETH_BTH_SEQ_SHIFT) - 1); + ohdr->u.tid_rdma.ack.aeth = rvt_compute_aeth(qp); + } else if (qpriv->s_nak_state) { + *bth2 = mask_psn(qpriv->s_nak_psn); + ohdr->u.tid_rdma.ack.aeth = + cpu_to_be32((qp->r_msn & IB_MSN_MASK) | + (qpriv->s_nak_state << + IB_AETH_CREDIT_SHIFT)); + } else { + *bth2 = full_flow_psn(flow, flow->flow_state.lpsn); + ohdr->u.tid_rdma.ack.aeth = rvt_compute_aeth(qp); + } + KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth0, KVER, 0x1); + ohdr->u.tid_rdma.ack.tid_flow_qp = + cpu_to_be32(qpriv->tid_rdma.local.qp | + ((flow->idx & TID_RDMA_DESTQP_FLOW_MASK) << + TID_RDMA_DESTQP_FLOW_SHIFT) | + qpriv->rcd->ctxt); + + ohdr->u.tid_rdma.ack.tid_flow_psn = 0; + ohdr->u.tid_rdma.ack.verbs_psn = + cpu_to_be32(flow->flow_state.resp_ib_psn); + + if (qpriv->resync) { + /* + * If the PSN before the current expect KDETH PSN is the + * RESYNC PSN, then we never received a good TID RDMA WRITE + * DATA packet after a previous RESYNC. + * In this case, the next expected KDETH PSN stays the same. + */ + if (hfi1_tid_rdma_is_resync_psn(qpriv->r_next_psn_kdeth - 1)) { + ohdr->u.tid_rdma.ack.tid_flow_psn = + cpu_to_be32(qpriv->r_next_psn_kdeth_save); + } else { + /* + * Because the KDETH PSNs jump during a RESYNC, it's + * not possible to infer (or compute) the previous value + * of r_next_psn_kdeth in the case of back-to-back + * RESYNC packets. Therefore, we save it. + */ + qpriv->r_next_psn_kdeth_save = + qpriv->r_next_psn_kdeth - 1; + ohdr->u.tid_rdma.ack.tid_flow_psn = + cpu_to_be32(qpriv->r_next_psn_kdeth_save); + qpriv->r_next_psn_kdeth = mask_psn(*bth2 + 1); + } + qpriv->resync = false; + } + + return sizeof(ohdr->u.tid_rdma.ack) / sizeof(u32); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 647a6f0cba31..89f5af627128 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -281,4 +281,8 @@ bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe, void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet); +u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e, + struct ib_other_headers *ohdr, u16 iflow, + u32 *bth1, u32 *bth2); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index eec6e822635b..3a97a39aeba4 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -196,7 +196,9 @@ struct hfi1_qp_priv { u8 timeout_shift; /* account for number of packets per segment */ u32 r_next_psn_kdeth; + u32 r_next_psn_kdeth_save; u8 sync_pt; /* Set when QP reaches sync point */ + u8 resync; }; #define HFI1_QP_WQE_INVALID ((u32)-1) From 9e93e967f7b452e6c9e4a33d0b42ff64fa7293c4 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:50:14 -0800 Subject: [PATCH 1159/4277] IB/hfi1: Add a function to receive TID RDMA ACK packet This patch adds a function to receive TID RDMA ACK packet, which could be an acknowledge to either a TID RDMA WRITE DATA packet or an TID RDMA RESYNC packet. For an ACK to TID RDMA WRITE DATA packet, the request segments are completed appropriately. For an ACK to a TID RDMA RESYNC packet, any pending segment flow information is updated accordingly. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/opfn.c | 3 + drivers/infiniband/hw/hfi1/qp.h | 2 + drivers/infiniband/hw/hfi1/tid_rdma.c | 212 +++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 3 + drivers/infiniband/hw/hfi1/trace_tid.h | 5 +- drivers/infiniband/hw/hfi1/verbs.h | 4 + 6 files changed, 228 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hfi1/opfn.c b/drivers/infiniband/hw/hfi1/opfn.c index 2ca070690b2f..82e1889ca969 100644 --- a/drivers/infiniband/hw/hfi1/opfn.c +++ b/drivers/infiniband/hw/hfi1/opfn.c @@ -245,6 +245,9 @@ void opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask) struct hfi1_qp_priv *priv = qp->priv; unsigned long flags; + if (attr_mask & IB_QP_RETRY_CNT) + priv->s_retry = attr->retry_cnt; + spin_lock_irqsave(&priv->opfn.lock, flags); if (ibqp->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { struct tid_rdma_params *local = &priv->tid_rdma.local; diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h index f74e2509e8b9..d531b760ea93 100644 --- a/drivers/infiniband/hw/hfi1/qp.h +++ b/drivers/infiniband/hw/hfi1/qp.h @@ -65,6 +65,7 @@ extern const struct rvt_operation_params hfi1_post_parms[]; * HFI1_S_WAIT_PIO_DRAIN - qp waiting for PIOs to drain * HFI1_S_WAIT_TID_SPACE - a QP is waiting for TID resource * HFI1_S_WAIT_TID_RESP - waiting for a TID RDMA WRITE response + * HFI1_S_WAIT_HALT - halt the first leg send engine * HFI1_S_MIN_BIT_MASK - the lowest bit that can be used by hfi1 */ #define HFI1_S_AHG_VALID 0x80000000 @@ -72,6 +73,7 @@ extern const struct rvt_operation_params hfi1_post_parms[]; #define HFI1_S_WAIT_PIO_DRAIN 0x20000000 #define HFI1_S_WAIT_TID_SPACE 0x10000000 #define HFI1_S_WAIT_TID_RESP 0x08000000 +#define HFI1_S_WAIT_HALT 0x04000000 #define HFI1_S_MIN_BIT_MASK 0x01000000 /* diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index d8a7f07b028d..5eb8453a719e 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -319,6 +319,7 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->flow_state.index = RXE_NUM_TID_FLOWS; qpriv->flow_state.last_index = RXE_NUM_TID_FLOWS; qpriv->flow_state.generation = KERN_GENERATION_RESERVED; + qpriv->s_state = TID_OP(WRITE_RESP); qpriv->s_tid_cur = HFI1_QP_WQE_INVALID; qpriv->s_tid_head = HFI1_QP_WQE_INVALID; qpriv->s_tid_tail = HFI1_QP_WQE_INVALID; @@ -327,6 +328,7 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->r_tid_tail = HFI1_QP_WQE_INVALID; qpriv->r_tid_ack = HFI1_QP_WQE_INVALID; qpriv->r_tid_alloc = HFI1_QP_WQE_INVALID; + atomic_set(&qpriv->n_tid_requests, 0); timer_setup(&qpriv->s_tid_timer, hfi1_tid_timeout, 0); INIT_LIST_HEAD(&qpriv->tid_wait); @@ -4318,3 +4320,213 @@ u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e, return sizeof(ohdr->u.tid_rdma.ack) / sizeof(u32); } + +void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) +{ + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *qpriv = qp->priv; + struct rvt_swqe *wqe; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + u32 aeth, psn, req_psn, ack_psn, fspsn, resync_psn, ack_kpsn; + bool is_fecn; + unsigned long flags; + u16 fidx; + + is_fecn = process_ecn(qp, packet); + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + aeth = be32_to_cpu(ohdr->u.tid_rdma.ack.aeth); + req_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.ack.verbs_psn)); + resync_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.ack.tid_flow_psn)); + + spin_lock_irqsave(&qp->s_lock, flags); + + /* If we are waiting for an ACK to RESYNC, drop any other packets */ + if ((qp->s_flags & HFI1_S_WAIT_HALT) && + cmp_psn(psn, qpriv->s_resync_psn)) + goto ack_op_err; + + ack_psn = req_psn; + if (hfi1_tid_rdma_is_resync_psn(psn)) + ack_kpsn = resync_psn; + else + ack_kpsn = psn; + if (aeth >> 29) { + ack_psn--; + ack_kpsn--; + } + + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); + + if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE) + goto ack_op_err; + + req = wqe_to_tid_req(wqe); + flow = &req->flows[req->acked_tail]; + + /* Drop stale ACK/NAK */ + if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.spsn)) < 0) + goto ack_op_err; + + while (cmp_psn(ack_kpsn, + full_flow_psn(flow, flow->flow_state.lpsn)) >= 0 && + req->ack_seg < req->cur_seg) { + req->ack_seg++; + /* advance acked segment pointer */ + req->acked_tail = CIRC_NEXT(req->acked_tail, MAX_FLOWS); + req->r_last_acked = flow->flow_state.resp_ib_psn; + if (req->ack_seg == req->total_segs) { + req->state = TID_REQUEST_COMPLETE; + wqe = do_rc_completion(qp, wqe, + to_iport(qp->ibqp.device, + qp->port_num)); + atomic_dec(&qpriv->n_tid_requests); + if (qp->s_acked == qp->s_tail) + break; + if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE) + break; + req = wqe_to_tid_req(wqe); + } + flow = &req->flows[req->acked_tail]; + } + + switch (aeth >> 29) { + case 0: /* ACK */ + if (qpriv->s_flags & RVT_S_WAIT_ACK) + qpriv->s_flags &= ~RVT_S_WAIT_ACK; + if (!hfi1_tid_rdma_is_resync_psn(psn)) { + hfi1_schedule_send(qp); + } else { + u32 spsn, fpsn, last_acked, generation; + struct tid_rdma_request *rptr; + + /* Allow new requests (see hfi1_make_tid_rdma_pkt) */ + qp->s_flags &= ~HFI1_S_WAIT_HALT; + /* + * Clear RVT_S_SEND_ONE flag in case that the TID RDMA + * ACK is received after the TID retry timer is fired + * again. In this case, do not send any more TID + * RESYNC request or wait for any more TID ACK packet. + */ + qpriv->s_flags &= ~RVT_S_SEND_ONE; + hfi1_schedule_send(qp); + + if ((qp->s_acked == qpriv->s_tid_tail && + req->ack_seg == req->total_segs) || + qp->s_acked == qp->s_tail) { + qpriv->s_state = TID_OP(WRITE_DATA_LAST); + goto done; + } + + if (req->ack_seg == req->comp_seg) { + qpriv->s_state = TID_OP(WRITE_DATA); + goto done; + } + + /* + * The PSN to start with is the next PSN after the + * RESYNC PSN. + */ + psn = mask_psn(psn + 1); + generation = psn >> HFI1_KDETH_BTH_SEQ_SHIFT; + spsn = 0; + + /* + * Update to the correct WQE when we get an ACK(RESYNC) + * in the middle of a request. + */ + if (delta_psn(ack_psn, wqe->lpsn)) + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); + req = wqe_to_tid_req(wqe); + flow = &req->flows[req->acked_tail]; + /* + * RESYNC re-numbers the PSN ranges of all remaining + * segments. Also, PSN's start from 0 in the middle of a + * segment and the first segment size is less than the + * default number of packets. flow->resync_npkts is used + * to track the number of packets from the start of the + * real segment to the point of 0 PSN after the RESYNC + * in order to later correctly rewind the SGE. + */ + fpsn = full_flow_psn(flow, flow->flow_state.spsn); + req->r_ack_psn = psn; + flow->resync_npkts += + delta_psn(mask_psn(resync_psn + 1), fpsn); + /* + * Renumber all packet sequence number ranges + * based on the new generation. + */ + last_acked = qp->s_acked; + rptr = req; + while (1) { + /* start from last acked segment */ + for (fidx = rptr->acked_tail; + CIRC_CNT(rptr->setup_head, fidx, + MAX_FLOWS); + fidx = CIRC_NEXT(fidx, MAX_FLOWS)) { + u32 lpsn; + u32 gen; + + flow = &rptr->flows[fidx]; + gen = flow->flow_state.generation; + if (WARN_ON(gen == generation && + flow->flow_state.spsn != + spsn)) + continue; + lpsn = flow->flow_state.lpsn; + lpsn = full_flow_psn(flow, lpsn); + flow->npkts = + delta_psn(lpsn, + mask_psn(resync_psn) + ); + flow->flow_state.generation = + generation; + flow->flow_state.spsn = spsn; + flow->flow_state.lpsn = + flow->flow_state.spsn + + flow->npkts - 1; + flow->pkt = 0; + spsn += flow->npkts; + resync_psn += flow->npkts; + } + if (++last_acked == qpriv->s_tid_cur + 1) + break; + if (last_acked == qp->s_size) + last_acked = 0; + wqe = rvt_get_swqe_ptr(qp, last_acked); + rptr = wqe_to_tid_req(wqe); + } + req->cur_seg = req->ack_seg; + qpriv->s_tid_tail = qp->s_acked; + qpriv->s_state = TID_OP(WRITE_REQ); + } +done: + qpriv->s_retry = qp->s_retry_cnt; + break; + + case 3: /* NAK */ + switch ((aeth >> IB_AETH_CREDIT_SHIFT) & + IB_AETH_CREDIT_MASK) { + case 0: /* PSN sequence error */ + flow = &req->flows[req->acked_tail]; + fspsn = full_flow_psn(flow, flow->flow_state.spsn); + req->r_ack_psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + req->cur_seg = req->ack_seg; + qpriv->s_tid_tail = qp->s_acked; + qpriv->s_state = TID_OP(WRITE_REQ); + qpriv->s_retry = qp->s_retry_cnt; + break; + + default: + break; + } + break; + + default: + break; + } + +ack_op_err: + spin_unlock_irqrestore(&qp->s_lock, flags); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 89f5af627128..499036e7a3e8 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -101,6 +101,7 @@ struct tid_rdma_request { u32 seg_len; u32 total_len; + u32 r_ack_psn; /* next expected ack PSN */ u32 r_flow_psn; /* IB PSN of next segment start */ u32 r_last_acked; /* IB PSN of last ACK'ed packet */ u32 s_next_psn; /* IB PSN of next segment start for read */ @@ -285,4 +286,6 @@ u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e, struct ib_other_headers *ohdr, u16 iflow, u32 *bth1, u32 *bth2); +void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h index 51f5b0e8da71..a45b5257d6c4 100644 --- a/drivers/infiniband/hw/hfi1/trace_tid.h +++ b/drivers/infiniband/hw/hfi1/trace_tid.h @@ -52,7 +52,7 @@ u16 hfi1_trace_get_tid_idx(u32 ent); #define TID_READ_SENDER_PRN "[%s] qpn 0x%x newreq %u tid_r_reqs %u " \ "tid_r_comp %u pending_tid_r_segs %u " \ "s_flags 0x%x ps_flags 0x%x iow_flags 0x%lx " \ - "hw_flow_index %u generation 0x%x " \ + "s_state 0x%x hw_flow_index %u generation 0x%x " \ "fpsn 0x%x flow_flags 0x%x" #define TID_REQ_PRN "[%s] qpn 0x%x newreq %u opcode 0x%x psn 0x%x lpsn 0x%x " \ @@ -844,6 +844,7 @@ DECLARE_EVENT_CLASS(/* tid_read_sender */ __field(u32, s_flags) __field(u32, ps_flags) __field(unsigned long, iow_flags) + __field(u8, s_state) __field(u32, hw_flow_index) __field(u32, generation) __field(u32, fpsn) @@ -861,6 +862,7 @@ DECLARE_EVENT_CLASS(/* tid_read_sender */ __entry->s_flags = qp->s_flags; __entry->ps_flags = priv->s_flags; __entry->iow_flags = priv->s_iowait.flags; + __entry->s_state = priv->s_state; __entry->hw_flow_index = priv->flow_state.index; __entry->generation = priv->flow_state.generation; __entry->fpsn = priv->flow_state.psn; @@ -877,6 +879,7 @@ DECLARE_EVENT_CLASS(/* tid_read_sender */ __entry->s_flags, __entry->ps_flags, __entry->iow_flags, + __entry->s_state, __entry->hw_flow_index, __entry->generation, __entry->fpsn, diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 3a97a39aeba4..30e3f5af5cf1 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -170,9 +170,12 @@ struct hfi1_qp_priv { struct tid_rdma_qp_params tid_rdma; struct rvt_qp *owner; u8 hdr_type; /* 9B or 16B */ + atomic_t n_tid_requests; /* # of sent TID RDMA requests */ unsigned long tid_timer_timeout_jiffies; /* variables for the TID RDMA SE state machine */ + u8 s_state; + u8 s_retry; u8 rnr_nak_state; /* RNR NAK state */ u8 s_nak_state; u32 s_nak_psn; @@ -197,6 +200,7 @@ struct hfi1_qp_priv { u32 r_next_psn_kdeth; u32 r_next_psn_kdeth_save; + u32 s_resync_psn; u8 sync_pt; /* Set when QP reaches sync point */ u8 resync; }; From 829eaee5d09a7500bdce9ed0bc6ec6861f8ae45b Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:50:24 -0800 Subject: [PATCH 1160/4277] IB/hfi1: Add TID RDMA retry timer This patch adds the TID RDMA retry timer to make sure that TID RDMA WRITE DATA packets for a segment are received successfully by the responder. This timer is generally armed when the last TID RDMA WRITE DATA packet for a segment is sent out and stopped when all TID RDMA DATA packets are acknowledged. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/opfn.c | 2 + drivers/infiniband/hw/hfi1/tid_rdma.c | 93 +++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 ++ drivers/infiniband/hw/hfi1/verbs.h | 2 + 4 files changed, 101 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/opfn.c b/drivers/infiniband/hw/hfi1/opfn.c index 82e1889ca969..370a5a8eaa71 100644 --- a/drivers/infiniband/hw/hfi1/opfn.c +++ b/drivers/infiniband/hw/hfi1/opfn.c @@ -252,6 +252,8 @@ void opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask) if (ibqp->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { struct tid_rdma_params *local = &priv->tid_rdma.local; + if (attr_mask & IB_QP_TIMEOUT) + priv->tid_retry_timeout_jiffies = qp->timeout_jiffies; if (qp->pmtu == enum_to_mtu(OPA_MTU_4096) || qp->pmtu == enum_to_mtu(OPA_MTU_8192)) { tid_rdma_opfn_init(qp, local); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 5eb8453a719e..a4faf7d6c224 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -121,6 +121,9 @@ static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx); static void hfi1_tid_timeout(struct timer_list *t); static void hfi1_add_tid_reap_timer(struct rvt_qp *qp); static void hfi1_mod_tid_reap_timer(struct rvt_qp *qp); +static void hfi1_mod_tid_retry_timer(struct rvt_qp *qp); +static int hfi1_stop_tid_retry_timer(struct rvt_qp *qp); +static void hfi1_tid_retry_timeout(struct timer_list *t); static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { @@ -330,6 +333,7 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->r_tid_alloc = HFI1_QP_WQE_INVALID; atomic_set(&qpriv->n_tid_requests, 0); timer_setup(&qpriv->s_tid_timer, hfi1_tid_timeout, 0); + timer_setup(&qpriv->s_tid_retry_timer, hfi1_tid_retry_timeout, 0); INIT_LIST_HEAD(&qpriv->tid_wait); if (init_attr->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { @@ -4396,11 +4400,19 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) if (qpriv->s_flags & RVT_S_WAIT_ACK) qpriv->s_flags &= ~RVT_S_WAIT_ACK; if (!hfi1_tid_rdma_is_resync_psn(psn)) { + /* Check if there is any pending TID ACK */ + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE && + req->ack_seg < req->cur_seg) + hfi1_mod_tid_retry_timer(qp); + else + hfi1_stop_tid_retry_timer(qp); hfi1_schedule_send(qp); } else { u32 spsn, fpsn, last_acked, generation; struct tid_rdma_request *rptr; + /* ACK(RESYNC) */ + hfi1_stop_tid_retry_timer(qp); /* Allow new requests (see hfi1_make_tid_rdma_pkt) */ qp->s_flags &= ~HFI1_S_WAIT_HALT; /* @@ -4506,6 +4518,7 @@ done: break; case 3: /* NAK */ + hfi1_stop_tid_retry_timer(qp); switch ((aeth >> IB_AETH_CREDIT_SHIFT) & IB_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ @@ -4530,3 +4543,83 @@ done: ack_op_err: spin_unlock_irqrestore(&qp->s_lock, flags); } + +void hfi1_add_tid_retry_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct ib_qp *ibqp = &qp->ibqp; + struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); + + lockdep_assert_held(&qp->s_lock); + if (!(priv->s_flags & HFI1_S_TID_RETRY_TIMER)) { + priv->s_flags |= HFI1_S_TID_RETRY_TIMER; + priv->s_tid_retry_timer.expires = jiffies + + priv->tid_retry_timeout_jiffies + rdi->busy_jiffies; + add_timer(&priv->s_tid_retry_timer); + } +} + +static void hfi1_mod_tid_retry_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct ib_qp *ibqp = &qp->ibqp; + struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); + + lockdep_assert_held(&qp->s_lock); + priv->s_flags |= HFI1_S_TID_RETRY_TIMER; + mod_timer(&priv->s_tid_retry_timer, jiffies + + priv->tid_retry_timeout_jiffies + rdi->busy_jiffies); +} + +static int hfi1_stop_tid_retry_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + int rval = 0; + + lockdep_assert_held(&qp->s_lock); + if (priv->s_flags & HFI1_S_TID_RETRY_TIMER) { + rval = del_timer(&priv->s_tid_retry_timer); + priv->s_flags &= ~HFI1_S_TID_RETRY_TIMER; + } + return rval; +} + +void hfi1_del_tid_retry_timer(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + + del_timer_sync(&priv->s_tid_retry_timer); + priv->s_flags &= ~HFI1_S_TID_RETRY_TIMER; +} + +static void hfi1_tid_retry_timeout(struct timer_list *t) +{ + struct hfi1_qp_priv *priv = from_timer(priv, t, s_tid_retry_timer); + struct rvt_qp *qp = priv->owner; + struct rvt_swqe *wqe; + unsigned long flags; + + spin_lock_irqsave(&qp->r_lock, flags); + spin_lock(&qp->s_lock); + if (priv->s_flags & HFI1_S_TID_RETRY_TIMER) { + hfi1_stop_tid_retry_timer(qp); + if (!priv->s_retry) { + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); + hfi1_trdma_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR); + rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); + } else { + priv->s_flags &= ~RVT_S_WAIT_ACK; + /* Only send one packet (the RESYNC) */ + priv->s_flags |= RVT_S_SEND_ONE; + /* + * No additional request shall be made by this QP until + * the RESYNC has been complete. + */ + qp->s_flags |= HFI1_S_WAIT_HALT; + priv->s_state = TID_OP(RESYNC); + priv->s_retry--; + } + } + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_lock, flags); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 499036e7a3e8..3be5f79ed1fb 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -29,6 +29,7 @@ #define HFI1_R_TID_RSC_TIMER BIT(2) /* BIT(4) reserved for RVT_S_ACK_PENDING. */ #define HFI1_S_TID_WAIT_INTERLCK BIT(5) +#define HFI1_S_TID_RETRY_TIMER BIT(17) #define HFI1_R_TID_SW_PSN BIT(19) /* @@ -288,4 +289,7 @@ u32 hfi1_build_tid_rdma_write_ack(struct rvt_qp *qp, struct rvt_ack_entry *e, void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet); +void hfi1_add_tid_retry_timer(struct rvt_qp *qp); +void hfi1_del_tid_retry_timer(struct rvt_qp *qp); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 30e3f5af5cf1..bfd642e831f7 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -164,6 +164,7 @@ struct hfi1_qp_priv { u8 s_sc; /* SC[0..4] for next packet */ struct iowait s_iowait; struct timer_list s_tid_timer; /* for timing tid wait */ + struct timer_list s_tid_retry_timer; /* for timing tid ack */ struct list_head tid_wait; /* for queueing tid space */ struct hfi1_opfn_data opfn; struct tid_flow_state flow_state; @@ -172,6 +173,7 @@ struct hfi1_qp_priv { u8 hdr_type; /* 9B or 16B */ atomic_t n_tid_requests; /* # of sent TID RDMA requests */ unsigned long tid_timer_timeout_jiffies; + unsigned long tid_retry_timeout_jiffies; /* variables for the TID RDMA SE state machine */ u8 s_state; From 6e391c6a4a8f97d34fa859c906387c05e91adbe9 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:50:36 -0800 Subject: [PATCH 1161/4277] IB/hfi1: Add a function to build TID RDMA RESYNC packet This patch adds a function to build TID RDMA RESYNC packet, which is sent by the requester to notify the responder that no TID RDMA ACK packet has been received for a given KDETH PSN. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 26 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index a4faf7d6c224..e10f6d714dff 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -4623,3 +4623,29 @@ static void hfi1_tid_retry_timeout(struct timer_list *t) spin_unlock(&qp->s_lock); spin_unlock_irqrestore(&qp->r_lock, flags); } + +u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, u32 *bth1, + u32 *bth2, u16 fidx) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + struct tid_rdma_params *remote; + struct tid_rdma_request *req = wqe_to_tid_req(wqe); + struct tid_rdma_flow *flow = &req->flows[fidx]; + u32 generation; + + rcu_read_lock(); + remote = rcu_dereference(qpriv->tid_rdma.remote); + KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth1, JKEY, remote->jkey); + ohdr->u.tid_rdma.ack.verbs_qp = cpu_to_be32(qp->remote_qpn); + *bth1 = remote->qp; + rcu_read_unlock(); + + generation = kern_flow_generation_next(flow->flow_state.generation); + *bth2 = mask_psn((generation << HFI1_KDETH_BTH_SEQ_SHIFT) - 1); + qpriv->s_resync_psn = *bth2; + *bth2 |= IB_BTH_REQ_ACK; + KDETH_RESET(ohdr->u.tid_rdma.ack.kdeth0, KVER, 0x1); + + return sizeof(ohdr->u.tid_rdma.resync) / sizeof(u32); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 3be5f79ed1fb..d876b0efeac2 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -292,4 +292,8 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet); void hfi1_add_tid_retry_timer(struct rvt_qp *qp); void hfi1_del_tid_retry_timer(struct rvt_qp *qp); +u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe, + struct ib_other_headers *ohdr, u32 *bth1, + u32 *bth2, u16 fidx); + #endif /* HFI1_TID_RDMA_H */ From 7cf0ad679de46c61739238c3f4542f14cc7bbc69 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:50:46 -0800 Subject: [PATCH 1162/4277] IB/hfi1: Add a function to receive TID RDMA RESYNC packet This patch adds a function to receive TID RDMA RESYNC packet on the responder side. The QP's hardware flow will be updated and all allocated software flows will be updated accordingly in order to drop all stale packets. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Ashutosh Dixit Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 103 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 2 + 2 files changed, 105 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index e10f6d714dff..1901d5b6bbb9 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -4649,3 +4649,106 @@ u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe, return sizeof(ohdr->u.tid_rdma.resync) / sizeof(u32); } + +void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet) +{ + struct ib_other_headers *ohdr = packet->ohdr; + struct rvt_qp *qp = packet->qp; + struct hfi1_qp_priv *qpriv = qp->priv; + struct hfi1_ctxtdata *rcd = qpriv->rcd; + struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); + struct rvt_ack_entry *e; + struct tid_rdma_request *req; + struct tid_rdma_flow *flow; + struct tid_flow_state *fs = &qpriv->flow_state; + u32 psn, generation, idx, gen_next; + bool is_fecn; + unsigned long flags; + + is_fecn = process_ecn(qp, packet); + psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + + generation = mask_psn(psn + 1) >> HFI1_KDETH_BTH_SEQ_SHIFT; + spin_lock_irqsave(&qp->s_lock, flags); + + gen_next = (fs->generation == KERN_GENERATION_RESERVED) ? + generation : kern_flow_generation_next(fs->generation); + /* + * RESYNC packet contains the "next" generation and can only be + * from the current or previous generations + */ + if (generation != mask_generation(gen_next - 1) && + generation != gen_next) + goto bail; + /* Already processing a resync */ + if (qpriv->resync) + goto bail; + + spin_lock(&rcd->exp_lock); + if (fs->index >= RXE_NUM_TID_FLOWS) { + /* + * If we don't have a flow, save the generation so it can be + * applied when a new flow is allocated + */ + fs->generation = generation; + } else { + /* Reprogram the QP flow with new generation */ + rcd->flows[fs->index].generation = generation; + fs->generation = kern_setup_hw_flow(rcd, fs->index); + } + fs->psn = 0; + /* + * Disable SW PSN checking since a RESYNC is equivalent to a + * sync point and the flow has/will be reprogrammed + */ + qpriv->s_flags &= ~HFI1_R_TID_SW_PSN; + + /* + * Reset all TID flow information with the new generation. + * This is done for all requests and segments after the + * last received segment + */ + for (idx = qpriv->r_tid_tail; ; idx++) { + u16 flow_idx; + + if (idx > rvt_size_atomic(&dev->rdi)) + idx = 0; + e = &qp->s_ack_queue[idx]; + if (e->opcode == TID_OP(WRITE_REQ)) { + req = ack_to_tid_req(e); + + /* start from last unacked segment */ + for (flow_idx = req->clear_tail; + CIRC_CNT(req->setup_head, flow_idx, + MAX_FLOWS); + flow_idx = CIRC_NEXT(flow_idx, MAX_FLOWS)) { + u32 lpsn; + u32 next; + + flow = &req->flows[flow_idx]; + lpsn = full_flow_psn(flow, + flow->flow_state.lpsn); + next = flow->flow_state.r_next_psn; + flow->npkts = delta_psn(lpsn, next - 1); + flow->flow_state.generation = fs->generation; + flow->flow_state.spsn = fs->psn; + flow->flow_state.lpsn = + flow->flow_state.spsn + flow->npkts - 1; + flow->flow_state.r_next_psn = + full_flow_psn(flow, + flow->flow_state.spsn); + fs->psn += flow->npkts; + } + } + if (idx == qp->s_tail_ack_queue) + break; + } + + spin_unlock(&rcd->exp_lock); + qpriv->resync = true; + /* RESYNC request always gets a TID RDMA ACK. */ + qpriv->s_nak_state = 0; + qpriv->s_flags |= RVT_S_ACK_PENDING; +bail: + spin_unlock_irqrestore(&qp->s_lock, flags); +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index d876b0efeac2..bdcf18455d9d 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -296,4 +296,6 @@ u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe, struct ib_other_headers *ohdr, u32 *bth1, u32 *bth2, u16 fidx); +void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet); + #endif /* HFI1_TID_RDMA_H */ From 6e38fca6b1524e9a9aa0d2a10d99975eef1791c1 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:50:56 -0800 Subject: [PATCH 1163/4277] IB/hfi1: Resend the TID RDMA WRITE DATA packets This patch adds the logic to resend TID RDMA WRITE DATA packets. The tracking indices will be reset properly so that the correct TID entries will be used. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 63 ++++++++++++++++++++++++--- drivers/infiniband/hw/hfi1/verbs.h | 1 + 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 1901d5b6bbb9..cb6321b0d2c9 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -3059,8 +3059,9 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, { struct tid_rdma_request *req = wqe_to_tid_req(wqe); struct tid_rdma_flow *flow; - int diff; - u32 tididx = 0; + struct hfi1_qp_priv *qpriv = qp->priv; + int diff, delta_pkts; + u32 tididx = 0, i; u16 fidx; if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { @@ -3076,11 +3077,20 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, return; } } else { - return; + fidx = req->acked_tail; + flow = &req->flows[fidx]; + *bth2 = mask_psn(req->r_ack_psn); } + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) + delta_pkts = delta_psn(*bth2, flow->flow_state.ib_spsn); + else + delta_pkts = delta_psn(*bth2, + full_flow_psn(flow, + flow->flow_state.spsn)); + trace_hfi1_tid_flow_restart_req(qp, fidx, flow); - diff = delta_psn(*bth2, flow->flow_state.ib_spsn); + diff = delta_pkts + flow->resync_npkts; flow->sent = 0; flow->pkt = 0; @@ -3104,6 +3114,18 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, break; } } + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) { + rvt_skip_sge(&qpriv->tid_ss, (req->cur_seg * req->seg_len) + + flow->sent, 0); + /* + * Packet PSN is based on flow_state.spsn + flow->pkt. However, + * during a RESYNC, the generation is incremented and the + * sequence is reset to 0. Since we've adjusted the npkts in the + * flow and the SGE has been sufficiently advanced, we have to + * adjust flow->pkt in order to calculate the correct PSN. + */ + flow->pkt -= flow->resync_npkts; + } if (flow->tid_offset == EXP_TID_GET(flow->tid_entry[tididx], LEN) * PAGE_SIZE) { @@ -3111,13 +3133,42 @@ void hfi1_tid_rdma_restart_req(struct rvt_qp *qp, struct rvt_swqe *wqe, flow->tid_offset = 0; } flow->tid_idx = tididx; - /* Move flow_idx to correct index */ - req->flow_idx = fidx; + if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) + /* Move flow_idx to correct index */ + req->flow_idx = fidx; + else + req->clear_tail = fidx; trace_hfi1_tid_flow_restart_req(qp, fidx, flow); trace_hfi1_tid_req_restart_req(qp, 0, wqe->wr.opcode, wqe->psn, wqe->lpsn, req); req->state = TID_REQUEST_ACTIVE; + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) { + /* Reset all the flows that we are going to resend */ + fidx = CIRC_NEXT(fidx, MAX_FLOWS); + i = qpriv->s_tid_tail; + do { + for (; CIRC_CNT(req->setup_head, fidx, MAX_FLOWS); + fidx = CIRC_NEXT(fidx, MAX_FLOWS)) { + req->flows[fidx].sent = 0; + req->flows[fidx].pkt = 0; + req->flows[fidx].tid_idx = 0; + req->flows[fidx].tid_offset = 0; + req->flows[fidx].resync_npkts = 0; + } + if (i == qpriv->s_tid_cur) + break; + do { + i = (++i == qp->s_size ? 0 : i); + wqe = rvt_get_swqe_ptr(qp, i); + } while (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE); + req = wqe_to_tid_req(wqe); + req->cur_seg = req->ack_seg; + fidx = req->acked_tail; + /* Pull req->clear_tail back */ + req->clear_tail = fidx; + } while (1); + } } void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp) diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index bfd642e831f7..ce40ea9f43c3 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -171,6 +171,7 @@ struct hfi1_qp_priv { struct tid_rdma_qp_params tid_rdma; struct rvt_qp *owner; u8 hdr_type; /* 9B or 16B */ + struct rvt_sge_state tid_ss; /* SGE state pointer for 2nd leg */ atomic_t n_tid_requests; /* # of sent TID RDMA requests */ unsigned long tid_timer_timeout_jiffies; unsigned long tid_retry_timeout_jiffies; From 70dcb2e3dc6aa827d74e09c830ea06c660274880 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:51:07 -0800 Subject: [PATCH 1164/4277] IB/hfi1: Add the TID second leg send packet builder To improve performance, the TID RDMA WRITE protocol is designed to own a second leg to send data and ack packets in the KDETH PSN space. This patch adds the packet builder for the requester side, which contains the state machine to build TID RDMA WRITE DATA and TID RDMA RESYNC packet. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 211 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 7 + drivers/infiniband/hw/hfi1/verbs.h | 2 + 3 files changed, 220 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index cb6321b0d2c9..44c5c0010888 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -331,6 +331,7 @@ int hfi1_qp_priv_init(struct rvt_dev_info *rdi, struct rvt_qp *qp, qpriv->r_tid_tail = HFI1_QP_WQE_INVALID; qpriv->r_tid_ack = HFI1_QP_WQE_INVALID; qpriv->r_tid_alloc = HFI1_QP_WQE_INVALID; + atomic_set(&qpriv->n_requests, 0); atomic_set(&qpriv->n_tid_requests, 0); timer_setup(&qpriv->s_tid_timer, hfi1_tid_timeout, 0); timer_setup(&qpriv->s_tid_retry_timer, hfi1_tid_retry_timeout, 0); @@ -4803,3 +4804,213 @@ void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet) bail: spin_unlock_irqrestore(&qp->s_lock, flags); } + +/* + * Call this function when the last TID RDMA WRITE DATA packet for a request + * is built. + */ +static void update_tid_tail(struct rvt_qp *qp) + __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *priv = qp->priv; + u32 i; + struct rvt_swqe *wqe; + + lockdep_assert_held(&qp->s_lock); + /* Can't move beyond s_tid_cur */ + if (priv->s_tid_tail == priv->s_tid_cur) + return; + for (i = priv->s_tid_tail + 1; ; i++) { + if (i == qp->s_size) + i = 0; + + if (i == priv->s_tid_cur) + break; + wqe = rvt_get_swqe_ptr(qp, i); + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) + break; + } + priv->s_tid_tail = i; + priv->s_state = TID_OP(WRITE_RESP); +} + +int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) + __must_hold(&qp->s_lock) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct rvt_swqe *wqe; + u32 bth1 = 0, bth2 = 0, hwords = 5, len, middle = 0; + struct ib_other_headers *ohdr; + struct rvt_sge_state *ss = &qp->s_sge; + struct rvt_ack_entry *e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + struct tid_rdma_request *req = ack_to_tid_req(e); + bool last = false; + u8 opcode = TID_OP(WRITE_DATA); + + lockdep_assert_held(&qp->s_lock); + /* + * Prioritize the sending of the requests and responses over the + * sending of the TID RDMA data packets. + */ + if (((atomic_read(&priv->n_tid_requests) < HFI1_TID_RDMA_WRITE_CNT) && + atomic_read(&priv->n_requests) && + !(qp->s_flags & (RVT_S_BUSY | RVT_S_WAIT_ACK | + HFI1_S_ANY_WAIT_IO))) || + (e->opcode == TID_OP(WRITE_REQ) && req->cur_seg < req->alloc_seg && + !(qp->s_flags & (RVT_S_BUSY | HFI1_S_ANY_WAIT_IO)))) { + struct iowait_work *iowork; + + iowork = iowait_get_ib_work(&priv->s_iowait); + ps->s_txreq = get_waiting_verbs_txreq(iowork); + if (ps->s_txreq || hfi1_make_rc_req(qp, ps)) { + priv->s_flags |= HFI1_S_TID_BUSY_SET; + return 1; + } + } + + ps->s_txreq = get_txreq(ps->dev, qp); + if (!ps->s_txreq) + goto bail_no_tx; + + ohdr = &ps->s_txreq->phdr.hdr.ibh.u.oth; + + if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_SEND_OK)) { + if (!(ib_rvt_state_ops[qp->state] & RVT_FLUSH_SEND)) + goto bail; + /* We are in the error state, flush the work request. */ + if (qp->s_last == READ_ONCE(qp->s_head)) + goto bail; + /* If DMAs are in progress, we can't flush immediately. */ + if (iowait_sdma_pending(&priv->s_iowait)) { + qp->s_flags |= RVT_S_WAIT_DMA; + goto bail; + } + clear_ahg(qp); + wqe = rvt_get_swqe_ptr(qp, qp->s_last); + hfi1_trdma_send_complete(qp, wqe, qp->s_last != qp->s_acked ? + IB_WC_SUCCESS : IB_WC_WR_FLUSH_ERR); + /* will get called again */ + goto done_free_tx; + } + + if (priv->s_flags & RVT_S_WAIT_ACK) + goto bail; + + /* Check whether there is anything to do. */ + if (priv->s_tid_tail == HFI1_QP_WQE_INVALID) + goto bail; + wqe = rvt_get_swqe_ptr(qp, priv->s_tid_tail); + req = wqe_to_tid_req(wqe); + switch (priv->s_state) { + case TID_OP(WRITE_REQ): + case TID_OP(WRITE_RESP): + priv->tid_ss.sge = wqe->sg_list[0]; + priv->tid_ss.sg_list = wqe->sg_list + 1; + priv->tid_ss.num_sge = wqe->wr.num_sge; + priv->tid_ss.total_len = wqe->length; + + if (priv->s_state == TID_OP(WRITE_REQ)) + hfi1_tid_rdma_restart_req(qp, wqe, &bth2); + priv->s_state = TID_OP(WRITE_DATA); + /* fall through */ + + case TID_OP(WRITE_DATA): + /* + * 1. Check whether TID RDMA WRITE RESP available. + * 2. If no: + * 2.1 If have more segments and no TID RDMA WRITE RESP, + * set HFI1_S_WAIT_TID_RESP + * 2.2 Return indicating no progress made. + * 3. If yes: + * 3.1 Build TID RDMA WRITE DATA packet. + * 3.2 If last packet in segment: + * 3.2.1 Change KDETH header bits + * 3.2.2 Advance RESP pointers. + * 3.3 Return indicating progress made. + */ + wqe = rvt_get_swqe_ptr(qp, priv->s_tid_tail); + req = wqe_to_tid_req(wqe); + len = wqe->length; + + if (!req->comp_seg || req->cur_seg == req->comp_seg) + goto bail; + + last = hfi1_build_tid_rdma_packet(wqe, ohdr, &bth1, &bth2, + &len); + + if (last) { + /* move pointer to next flow */ + req->clear_tail = CIRC_NEXT(req->clear_tail, + MAX_FLOWS); + if (++req->cur_seg < req->total_segs) { + if (!CIRC_CNT(req->setup_head, req->clear_tail, + MAX_FLOWS)) + qp->s_flags |= HFI1_S_WAIT_TID_RESP; + } else { + priv->s_state = TID_OP(WRITE_DATA_LAST); + opcode = TID_OP(WRITE_DATA_LAST); + + /* Advance the s_tid_tail now */ + update_tid_tail(qp); + } + } + hwords += sizeof(ohdr->u.tid_rdma.w_data) / sizeof(u32); + ss = &priv->tid_ss; + break; + + case TID_OP(RESYNC): + /* Use generation from the most recently received response */ + wqe = rvt_get_swqe_ptr(qp, priv->s_tid_cur); + req = wqe_to_tid_req(wqe); + /* If no responses for this WQE look at the previous one */ + if (!req->comp_seg) { + wqe = rvt_get_swqe_ptr(qp, + (!priv->s_tid_cur ? qp->s_size : + priv->s_tid_cur) - 1); + req = wqe_to_tid_req(wqe); + } + hwords += hfi1_build_tid_rdma_resync(qp, wqe, ohdr, &bth1, + &bth2, + CIRC_PREV(req->setup_head, + MAX_FLOWS)); + ss = NULL; + len = 0; + opcode = TID_OP(RESYNC); + break; + + default: + goto bail; + } + if (priv->s_flags & RVT_S_SEND_ONE) { + priv->s_flags &= ~RVT_S_SEND_ONE; + priv->s_flags |= RVT_S_WAIT_ACK; + bth2 |= IB_BTH_REQ_ACK; + } + qp->s_len -= len; + ps->s_txreq->hdr_dwords = hwords; + ps->s_txreq->sde = priv->s_sde; + ps->s_txreq->ss = ss; + ps->s_txreq->s_cur_size = len; + hfi1_make_ruc_header(qp, ohdr, (opcode << 24), bth1, bth2, + middle, ps); + return 1; +done_free_tx: + hfi1_put_txreq(ps->s_txreq); + ps->s_txreq = NULL; + return 1; + +bail: + hfi1_put_txreq(ps->s_txreq); +bail_no_tx: + ps->s_txreq = NULL; + priv->s_flags &= ~RVT_S_BUSY; + /* + * If we didn't get a txreq, the QP will be woken up later to try + * again, set the flags to the the wake up which work item to wake + * up. + * (A better algorithm should be found to do this and generalize the + * sleep/wakeup flags.) + */ + iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID); + return 0; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index bdcf18455d9d..0ce0ef6d60f2 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -26,9 +26,13 @@ * * HFI1_S_TID_WAIT_INTERLCK - QP is waiting for requester interlock */ +#define HFI1_S_TID_BUSY_SET BIT(0) +/* BIT(1) reserved for RVT_S_BUSY. */ #define HFI1_R_TID_RSC_TIMER BIT(2) +/* BIT(3) reserved for RVT_S_RESP_PENDING. */ /* BIT(4) reserved for RVT_S_ACK_PENDING. */ #define HFI1_S_TID_WAIT_INTERLCK BIT(5) +/* BIT(7) - BIT(15) reserved for RVT_S_WAIT_*. */ #define HFI1_S_TID_RETRY_TIMER BIT(17) #define HFI1_R_TID_SW_PSN BIT(19) @@ -298,4 +302,7 @@ u32 hfi1_build_tid_rdma_resync(struct rvt_qp *qp, struct rvt_swqe *wqe, void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet); +struct hfi1_pkt_state; +int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index ce40ea9f43c3..3e45149d22c4 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -172,6 +172,8 @@ struct hfi1_qp_priv { struct rvt_qp *owner; u8 hdr_type; /* 9B or 16B */ struct rvt_sge_state tid_ss; /* SGE state pointer for 2nd leg */ + atomic_t n_requests; /* # of TID RDMA requests in the */ + /* queue */ atomic_t n_tid_requests; /* # of sent TID RDMA requests */ unsigned long tid_timer_timeout_jiffies; unsigned long tid_retry_timeout_jiffies; From 24c5bfeaf1e66efbc15cd9a6f5565c38d8cdb630 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:51:17 -0800 Subject: [PATCH 1165/4277] IB/hfi1: Add the TID second leg ACK packet builder This patch adds the TID packet builder for the responder side, which contains the state machine to build TID RDMA ACK packet for either TID RDMA WRITE DATA or TID RDMA RESYNC packets. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 141 ++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 44c5c0010888..15c12243c166 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -124,6 +124,9 @@ static void hfi1_mod_tid_reap_timer(struct rvt_qp *qp); static void hfi1_mod_tid_retry_timer(struct rvt_qp *qp); static int hfi1_stop_tid_retry_timer(struct rvt_qp *qp); static void hfi1_tid_retry_timeout(struct timer_list *t); +static int make_tid_rdma_ack(struct rvt_qp *qp, + struct ib_other_headers *ohdr, + struct hfi1_pkt_state *ps); static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { @@ -4874,6 +4877,10 @@ int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) ohdr = &ps->s_txreq->phdr.hdr.ibh.u.oth; + if ((priv->s_flags & RVT_S_ACK_PENDING) && + make_tid_rdma_ack(qp, ohdr, ps)) + return 1; + if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_SEND_OK)) { if (!(ib_rvt_state_ops[qp->state] & RVT_FLUSH_SEND)) goto bail; @@ -5014,3 +5021,137 @@ bail_no_tx: iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID); return 0; } + +static int make_tid_rdma_ack(struct rvt_qp *qp, + struct ib_other_headers *ohdr, + struct hfi1_pkt_state *ps) +{ + struct rvt_ack_entry *e; + struct hfi1_qp_priv *qpriv = qp->priv; + struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); + u32 hwords, next; + u32 len = 0; + u32 bth1 = 0, bth2 = 0; + int middle = 0; + u16 flow; + struct tid_rdma_request *req, *nreq; + + /* Don't send an ACK if we aren't supposed to. */ + if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) + goto bail; + + /* header size in 32-bit words LRH+BTH = (8+12)/4. */ + hwords = 5; + + e = &qp->s_ack_queue[qpriv->r_tid_ack]; + req = ack_to_tid_req(e); + /* + * In the RESYNC case, we are exactly one segment past the + * previously sent ack or at the previously sent NAK. So to send + * the resync ack, we go back one segment (which might be part of + * the previous request) and let the do-while loop execute again. + * The advantage of executing the do-while loop is that any data + * received after the previous ack is automatically acked in the + * RESYNC ack. It turns out that for the do-while loop we only need + * to pull back qpriv->r_tid_ack, not the segment + * indices/counters. The scheme works even if the previous request + * was not a TID WRITE request. + */ + if (qpriv->resync) { + if (!req->ack_seg || req->ack_seg == req->total_segs) + qpriv->r_tid_ack = !qpriv->r_tid_ack ? + rvt_size_atomic(&dev->rdi) : + qpriv->r_tid_ack - 1; + e = &qp->s_ack_queue[qpriv->r_tid_ack]; + req = ack_to_tid_req(e); + } + + /* + * If we've sent all the ACKs that we can, we are done + * until we get more segments... + */ + if (!qpriv->s_nak_state && !qpriv->resync && + req->ack_seg == req->comp_seg) + goto bail; + + do { + /* + * To deal with coalesced ACKs, the acked_tail pointer + * into the flow array is used. The distance between it + * and the clear_tail is the number of flows that are + * being ACK'ed. + */ + req->ack_seg += + /* Get up-to-date value */ + CIRC_CNT(req->clear_tail, req->acked_tail, + MAX_FLOWS); + /* Advance acked index */ + req->acked_tail = req->clear_tail; + + /* + * req->clear_tail points to the segment currently being + * received. So, when sending an ACK, the previous + * segment is being ACK'ed. + */ + flow = CIRC_PREV(req->acked_tail, MAX_FLOWS); + if (req->ack_seg != req->total_segs) + break; + req->state = TID_REQUEST_COMPLETE; + + next = qpriv->r_tid_ack + 1; + if (next > rvt_size_atomic(&dev->rdi)) + next = 0; + qpriv->r_tid_ack = next; + if (qp->s_ack_queue[next].opcode != TID_OP(WRITE_REQ)) + break; + nreq = ack_to_tid_req(&qp->s_ack_queue[next]); + if (!nreq->comp_seg || nreq->ack_seg == nreq->comp_seg) + break; + + /* Move to the next ack entry now */ + e = &qp->s_ack_queue[qpriv->r_tid_ack]; + req = ack_to_tid_req(e); + } while (1); + + /* + * At this point qpriv->r_tid_ack == qpriv->r_tid_tail but e and + * req could be pointing at the previous ack queue entry + */ + if (qpriv->s_nak_state || + (qpriv->resync && + !hfi1_tid_rdma_is_resync_psn(qpriv->r_next_psn_kdeth - 1) && + (cmp_psn(qpriv->r_next_psn_kdeth - 1, + full_flow_psn(&req->flows[flow], + req->flows[flow].flow_state.lpsn)) > 0))) { + /* + * A NAK will implicitly acknowledge all previous TID RDMA + * requests. Therefore, we NAK with the req->acked_tail + * segment for the request at qpriv->r_tid_ack (same at + * this point as the req->clear_tail segment for the + * qpriv->r_tid_tail request) + */ + e = &qp->s_ack_queue[qpriv->r_tid_ack]; + req = ack_to_tid_req(e); + flow = req->acked_tail; + } + + hwords += hfi1_build_tid_rdma_write_ack(qp, e, ohdr, flow, &bth1, + &bth2); + len = 0; + qpriv->s_flags &= ~RVT_S_ACK_PENDING; + ps->s_txreq->hdr_dwords = hwords; + ps->s_txreq->sde = qpriv->s_sde; + ps->s_txreq->s_cur_size = len; + ps->s_txreq->ss = NULL; + hfi1_make_ruc_header(qp, ohdr, (TID_OP(ACK) << 24), bth1, bth2, middle, + ps); + return 1; +bail: + /* + * Ensure s_rdma_ack_cnt changes are committed prior to resetting + * RVT_S_RESP_PENDING + */ + smp_wmb(); + qpriv->s_flags &= ~RVT_S_ACK_PENDING; + return 0; +} From 572f0c3301138961a596c522729afb5801135d6e Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:51:27 -0800 Subject: [PATCH 1166/4277] IB/hfi1: Add the dual leg code The "Second Leg" of the TID RDMA WRITE protocol deals with the transfer of data and ack packets, which are in the KDETH PSN space, as opposed to the IB PSN space. Therefore, the Second Leg could be considered as a separate state machine. As such, it is handled by a different work queue item which is scheduled along with the normal IB state machine work item. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/iowait.h | 12 +++ drivers/infiniband/hw/hfi1/qp.c | 34 ++++++- drivers/infiniband/hw/hfi1/qp.h | 1 + drivers/infiniband/hw/hfi1/ruc.c | 32 ++++-- drivers/infiniband/hw/hfi1/tid_rdma.c | 141 ++++++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 4 + drivers/infiniband/hw/hfi1/verbs.h | 3 + 7 files changed, 217 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/iowait.h b/drivers/infiniband/hw/hfi1/iowait.h index 23a58ac0d47c..bd913701761d 100644 --- a/drivers/infiniband/hw/hfi1/iowait.h +++ b/drivers/infiniband/hw/hfi1/iowait.h @@ -185,6 +185,18 @@ static inline bool iowait_schedule(struct iowait *wait, return !!queue_work_on(cpu, wq, &wait->wait[IOWAIT_IB_SE].iowork); } +/** + * iowait_tid_schedule - schedule the tid SE + * @wait: the iowait structure + * @wq: the work queue + * @cpu: the cpu + */ +static inline bool iowait_tid_schedule(struct iowait *wait, + struct workqueue_struct *wq, int cpu) +{ + return !!queue_work_on(cpu, wq, &wait->wait[IOWAIT_TID_SE].iowork); +} + /** * iowait_sdma_drain() - wait for DMAs to drain * diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index 31b4b60f4364..96632c77f36f 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -431,6 +431,11 @@ static void hfi1_qp_schedule(struct rvt_qp *qp) if (ret) iowait_clear_flag(&priv->s_iowait, IOWAIT_PENDING_IB); } + if (iowait_flag_set(&priv->s_iowait, IOWAIT_PENDING_TID)) { + ret = hfi1_schedule_tid_send(qp); + if (ret) + iowait_clear_flag(&priv->s_iowait, IOWAIT_PENDING_TID); + } } void hfi1_qp_wakeup(struct rvt_qp *qp, u32 flag) @@ -450,8 +455,27 @@ void hfi1_qp_wakeup(struct rvt_qp *qp, u32 flag) void hfi1_qp_unbusy(struct rvt_qp *qp, struct iowait_work *wait) { - if (iowait_set_work_flag(wait) == IOWAIT_IB_SE) + struct hfi1_qp_priv *priv = qp->priv; + + if (iowait_set_work_flag(wait) == IOWAIT_IB_SE) { qp->s_flags &= ~RVT_S_BUSY; + /* + * If we are sending a first-leg packet from the second leg, + * we need to clear the busy flag from priv->s_flags to + * avoid a race condition when the qp wakes up before + * the call to hfi1_verbs_send() returns to the second + * leg. In that case, the second leg will terminate without + * being re-scheduled, resulting in failure to send TID RDMA + * WRITE DATA and TID RDMA ACK packets. + */ + if (priv->s_flags & HFI1_S_TID_BUSY_SET) { + priv->s_flags &= ~(HFI1_S_TID_BUSY_SET | + RVT_S_BUSY); + iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID); + } + } else { + priv->s_flags &= ~RVT_S_BUSY; + } } static int iowait_sleep( @@ -694,7 +718,7 @@ void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp) &priv->s_iowait, 1, _hfi1_do_send, - NULL, + _hfi1_do_tid_send, iowait_sleep, iowait_wakeup, iowait_sdma_drained); @@ -851,7 +875,8 @@ void notify_error_qp(struct rvt_qp *qp) if (lock) { write_seqlock(lock); if (!list_empty(&priv->s_iowait.list) && - !(qp->s_flags & RVT_S_BUSY)) { + !(qp->s_flags & RVT_S_BUSY) && + !(priv->s_flags & RVT_S_BUSY)) { qp->s_flags &= ~RVT_S_ANY_WAIT_IO; list_del_init(&priv->s_iowait.list); priv->s_iowait.lock = NULL; @@ -860,7 +885,8 @@ void notify_error_qp(struct rvt_qp *qp) write_sequnlock(lock); } - if (!(qp->s_flags & RVT_S_BUSY)) { + if (!(qp->s_flags & RVT_S_BUSY) && !(priv->s_flags & RVT_S_BUSY)) { + qp->s_hdrwords = 0; if (qp->s_rdma_mr) { rvt_put_mr(qp->s_rdma_mr); qp->s_rdma_mr = NULL; diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h index d531b760ea93..b670321365d3 100644 --- a/drivers/infiniband/hw/hfi1/qp.h +++ b/drivers/infiniband/hw/hfi1/qp.h @@ -82,6 +82,7 @@ extern const struct rvt_operation_params hfi1_post_parms[]; #define HFI1_S_ANY_WAIT_IO (RVT_S_ANY_WAIT_IO | HFI1_S_WAIT_PIO_DRAIN) #define HFI1_S_ANY_WAIT (HFI1_S_ANY_WAIT_IO | RVT_S_ANY_WAIT_SEND) +#define HFI1_S_ANY_TID_WAIT_SEND (RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_DMA) /* * Send if not busy or waiting for I/O and either diff --git a/drivers/infiniband/hw/hfi1/ruc.c b/drivers/infiniband/hw/hfi1/ruc.c index f96c0f544cb0..124a3ec1e15c 100644 --- a/drivers/infiniband/hw/hfi1/ruc.c +++ b/drivers/infiniband/hw/hfi1/ruc.c @@ -453,11 +453,13 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, #define SEND_RESCHED_TIMEOUT (5 * HZ) /* 5s in jiffies */ /** - * schedule_send_yield - test for a yield required for QP send engine + * hfi1_schedule_send_yield - test for a yield required for QP + * send engine * @timeout: Final time for timeout slice for jiffies * @qp: a pointer to QP * @ps: a pointer to a structure with commonly lookup values for * the the send engine progress + * @tid - true if it is the tid leg * * This routine checks if the time slice for the QP has expired * for RC QPs, if so an additional work entry is queued. At this @@ -465,8 +467,8 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, * returns true if a yield is required, otherwise, false * is returned. */ -static bool schedule_send_yield(struct rvt_qp *qp, - struct hfi1_pkt_state *ps) +bool hfi1_schedule_send_yield(struct rvt_qp *qp, struct hfi1_pkt_state *ps, + bool tid) { ps->pkts_sent = true; @@ -474,8 +476,24 @@ static bool schedule_send_yield(struct rvt_qp *qp, if (!ps->in_thread || workqueue_congested(ps->cpu, ps->ppd->hfi1_wq)) { spin_lock_irqsave(&qp->s_lock, ps->flags); - qp->s_flags &= ~RVT_S_BUSY; - hfi1_schedule_send(qp); + if (!tid) { + qp->s_flags &= ~RVT_S_BUSY; + hfi1_schedule_send(qp); + } else { + struct hfi1_qp_priv *priv = qp->priv; + + if (priv->s_flags & + HFI1_S_TID_BUSY_SET) { + qp->s_flags &= ~RVT_S_BUSY; + priv->s_flags &= + ~(HFI1_S_TID_BUSY_SET | + RVT_S_BUSY); + } else { + priv->s_flags &= ~RVT_S_BUSY; + } + hfi1_schedule_tid_send(qp); + } + spin_unlock_irqrestore(&qp->s_lock, ps->flags); this_cpu_inc(*ps->ppd->dd->send_schedule); trace_hfi1_rc_expired_time_slice(qp, true); @@ -576,6 +594,8 @@ void hfi1_do_send(struct rvt_qp *qp, bool in_thread) do { /* Check for a constructed packet to be sent. */ if (ps.s_txreq) { + if (priv->s_flags & HFI1_S_TID_BUSY_SET) + qp->s_flags |= RVT_S_BUSY; spin_unlock_irqrestore(&qp->s_lock, ps.flags); /* * If the packet cannot be sent now, return and @@ -585,7 +605,7 @@ void hfi1_do_send(struct rvt_qp *qp, bool in_thread) return; /* allow other tasks to run */ - if (schedule_send_yield(qp, &ps)) + if (hfi1_schedule_send_yield(qp, &ps, false)) return; spin_lock_irqsave(&qp->s_lock, ps.flags); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 15c12243c166..80111dd1d876 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -127,6 +127,7 @@ static void hfi1_tid_retry_timeout(struct timer_list *t); static int make_tid_rdma_ack(struct rvt_qp *qp, struct ib_other_headers *ohdr, struct hfi1_pkt_state *ps); +static void hfi1_do_tid_send(struct rvt_qp *qp); static u64 tid_rdma_opfn_encode(struct tid_rdma_params *p) { @@ -3048,6 +3049,7 @@ nak_psn: qpriv->s_flags |= RVT_S_ACK_PENDING; if (qpriv->r_tid_ack == HFI1_QP_WQE_INVALID) qpriv->r_tid_ack = qpriv->r_tid_tail; + hfi1_schedule_tid_send(qp); } goto unlock; } @@ -3517,6 +3519,7 @@ static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx) ret = -EAGAIN; to_seg = MAX_FLOWS >> 1; qpriv->s_flags |= RVT_S_ACK_PENDING; + hfi1_schedule_tid_send(qp); break; } @@ -4128,6 +4131,7 @@ void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet) } qp->s_flags &= ~HFI1_S_WAIT_TID_RESP; + hfi1_schedule_tid_send(qp); goto ack_done; ack_op_err: @@ -4287,6 +4291,7 @@ void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet) done: priv->s_flags |= RVT_S_ACK_PENDING; + hfi1_schedule_tid_send(qp); exit: priv->r_next_psn_kdeth = flow->flow_state.r_next_psn; spin_unlock_irqrestore(&qp->s_lock, flags); @@ -4299,6 +4304,7 @@ send_nak: priv->s_flags |= RVT_S_ACK_PENDING; if (priv->r_tid_ack == HFI1_QP_WQE_INVALID) priv->r_tid_ack = priv->r_tid_tail; + hfi1_schedule_tid_send(qp); } goto done; } @@ -4567,6 +4573,7 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) req->cur_seg = req->ack_seg; qpriv->s_tid_tail = qp->s_acked; qpriv->s_state = TID_OP(WRITE_REQ); + hfi1_schedule_tid_send(qp); } done: qpriv->s_retry = qp->s_retry_cnt; @@ -4584,6 +4591,7 @@ done: qpriv->s_tid_tail = qp->s_acked; qpriv->s_state = TID_OP(WRITE_REQ); qpriv->s_retry = qp->s_retry_cnt; + hfi1_schedule_tid_send(qp); break; default: @@ -4673,6 +4681,7 @@ static void hfi1_tid_retry_timeout(struct timer_list *t) qp->s_flags |= HFI1_S_WAIT_HALT; priv->s_state = TID_OP(RESYNC); priv->s_retry--; + hfi1_schedule_tid_send(qp); } } spin_unlock(&qp->s_lock); @@ -4804,6 +4813,7 @@ void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet) /* RESYNC request always gets a TID RDMA ACK. */ qpriv->s_nak_state = 0; qpriv->s_flags |= RVT_S_ACK_PENDING; + hfi1_schedule_tid_send(qp); bail: spin_unlock_irqrestore(&qp->s_lock, flags); } @@ -5155,3 +5165,134 @@ bail: qpriv->s_flags &= ~RVT_S_ACK_PENDING; return 0; } + +static int hfi1_send_tid_ok(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + + return !(priv->s_flags & RVT_S_BUSY || + qp->s_flags & HFI1_S_ANY_WAIT_IO) && + (verbs_txreq_queued(iowait_get_tid_work(&priv->s_iowait)) || + (priv->s_flags & RVT_S_RESP_PENDING) || + !(qp->s_flags & HFI1_S_ANY_TID_WAIT_SEND)); +} + +void _hfi1_do_tid_send(struct work_struct *work) +{ + struct iowait_work *w = container_of(work, struct iowait_work, iowork); + struct rvt_qp *qp = iowait_to_qp(w->iow); + + hfi1_do_tid_send(qp); +} + +static void hfi1_do_tid_send(struct rvt_qp *qp) +{ + struct hfi1_pkt_state ps; + struct hfi1_qp_priv *priv = qp->priv; + + ps.dev = to_idev(qp->ibqp.device); + ps.ibp = to_iport(qp->ibqp.device, qp->port_num); + ps.ppd = ppd_from_ibp(ps.ibp); + ps.wait = iowait_get_tid_work(&priv->s_iowait); + ps.in_thread = false; + ps.timeout_int = qp->timeout_jiffies / 8; + + spin_lock_irqsave(&qp->s_lock, ps.flags); + + /* Return if we are already busy processing a work request. */ + if (!hfi1_send_tid_ok(qp)) { + if (qp->s_flags & HFI1_S_ANY_WAIT_IO) + iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_TID); + spin_unlock_irqrestore(&qp->s_lock, ps.flags); + return; + } + + priv->s_flags |= RVT_S_BUSY; + + ps.timeout = jiffies + ps.timeout_int; + ps.cpu = priv->s_sde ? priv->s_sde->cpu : + cpumask_first(cpumask_of_node(ps.ppd->dd->node)); + ps.pkts_sent = false; + + /* insure a pre-built packet is handled */ + ps.s_txreq = get_waiting_verbs_txreq(ps.wait); + do { + /* Check for a constructed packet to be sent. */ + if (ps.s_txreq) { + if (priv->s_flags & HFI1_S_TID_BUSY_SET) { + qp->s_flags |= RVT_S_BUSY; + ps.wait = iowait_get_ib_work(&priv->s_iowait); + } + spin_unlock_irqrestore(&qp->s_lock, ps.flags); + + /* + * If the packet cannot be sent now, return and + * the send tasklet will be woken up later. + */ + if (hfi1_verbs_send(qp, &ps)) + return; + + /* allow other tasks to run */ + if (hfi1_schedule_send_yield(qp, &ps, true)) + return; + + spin_lock_irqsave(&qp->s_lock, ps.flags); + if (priv->s_flags & HFI1_S_TID_BUSY_SET) { + qp->s_flags &= ~RVT_S_BUSY; + priv->s_flags &= ~HFI1_S_TID_BUSY_SET; + ps.wait = iowait_get_tid_work(&priv->s_iowait); + if (iowait_flag_set(&priv->s_iowait, + IOWAIT_PENDING_IB)) + hfi1_schedule_send(qp); + } + } + } while (hfi1_make_tid_rdma_pkt(qp, &ps)); + iowait_starve_clear(ps.pkts_sent, &priv->s_iowait); + spin_unlock_irqrestore(&qp->s_lock, ps.flags); +} + +static bool _hfi1_schedule_tid_send(struct rvt_qp *qp) +{ + struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_ibport *ibp = + to_iport(qp->ibqp.device, qp->port_num); + struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); + struct hfi1_devdata *dd = dd_from_ibdev(qp->ibqp.device); + + return iowait_tid_schedule(&priv->s_iowait, ppd->hfi1_wq, + priv->s_sde ? + priv->s_sde->cpu : + cpumask_first(cpumask_of_node(dd->node))); +} + +/** + * hfi1_schedule_tid_send - schedule progress on TID RDMA state machine + * @qp: the QP + * + * This schedules qp progress on the TID RDMA state machine. Caller + * should hold the s_lock. + * Unlike hfi1_schedule_send(), this cannot use hfi1_send_ok() because + * the two state machines can step on each other with respect to the + * RVT_S_BUSY flag. + * Therefore, a modified test is used. + * @return true if the second leg is scheduled; + * false if the second leg is not scheduled. + */ +bool hfi1_schedule_tid_send(struct rvt_qp *qp) +{ + lockdep_assert_held(&qp->s_lock); + if (hfi1_send_tid_ok(qp)) { + /* + * The following call returns true if the qp is not on the + * queue and false if the qp is already on the queue before + * this call. Either way, the qp will be on the queue when the + * call returns. + */ + _hfi1_schedule_tid_send(qp); + return true; + } + if (qp->s_flags & HFI1_S_ANY_WAIT_IO) + iowait_set_flag(&((struct hfi1_qp_priv *)qp->priv)->s_iowait, + IOWAIT_PENDING_TID); + return false; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 0ce0ef6d60f2..7f8f17ba6c14 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -305,4 +305,8 @@ void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet); struct hfi1_pkt_state; int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps); +void _hfi1_do_tid_send(struct work_struct *work); + +bool hfi1_schedule_tid_send(struct rvt_qp *qp); + #endif /* HFI1_TID_RDMA_H */ diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 3e45149d22c4..bee3d21a548e 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -443,6 +443,9 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, u32 bth0, u32 bth1, u32 bth2, int middle, struct hfi1_pkt_state *ps); +bool hfi1_schedule_send_yield(struct rvt_qp *qp, struct hfi1_pkt_state *ps, + bool tid); + void _hfi1_do_send(struct work_struct *work); void hfi1_do_send_from_rvt(struct rvt_qp *qp); From 3c6cb20a0d17d7a75778fb0935d6fa427c8177af Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:51:39 -0800 Subject: [PATCH 1167/4277] IB/hfi1: Add TID RDMA WRITE functionality into RDMA verbs This patch integrates TID RDMA WRITE protocol into normal RDMA verbs framework. The TID RDMA WRITE protocol is an end-to-end protocol between the hfi1 drivers on two OPA nodes that converts a qualified RDMA WRITE request into a TID RDMA WRITE request to avoid data copying on the responder side. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/qp.c | 7 + drivers/infiniband/hw/hfi1/rc.c | 487 ++++++++++++++++++++++--- drivers/infiniband/hw/hfi1/tid_rdma.c | 14 + drivers/infiniband/hw/hfi1/user_sdma.c | 3 +- drivers/infiniband/hw/hfi1/verbs.c | 17 +- drivers/infiniband/hw/hfi1/verbs.h | 1 + include/rdma/rdmavt_qp.h | 1 + 7 files changed, 481 insertions(+), 49 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index 96632c77f36f..cfd598e4b303 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -138,6 +138,12 @@ const struct rvt_operation_params hfi1_post_parms[RVT_OPERATION_MAX] = { .flags = RVT_OPERATION_USE_RESERVE, }, +[IB_WR_TID_RDMA_WRITE] = { + .length = sizeof(struct ib_rdma_wr), + .qpt_support = BIT(IB_QPT_RC), + .flags = RVT_OPERATION_IGN_RNR_CNT, +}, + }; static void flush_list_head(struct list_head *l) @@ -780,6 +786,7 @@ void quiesce_qp(struct rvt_qp *qp) struct hfi1_qp_priv *priv = qp->priv; hfi1_del_tid_reap_timer(qp); + hfi1_del_tid_retry_timer(qp); iowait_sdma_drain(&priv->s_iowait); qp_pio_drain(qp); flush_tx_list(qp); diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index fcb733ea8dfb..6d2abea896e5 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -111,16 +111,17 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, struct hfi1_pkt_state *ps) { struct rvt_ack_entry *e; - u32 hwords; + u32 hwords, hdrlen; u32 len = 0; u32 bth0 = 0, bth2 = 0; u32 bth1 = qp->remote_qpn | (HFI1_CAP_IS_KSET(OPFN) << IB_BTHE_E_SHIFT); int middle = 0; u32 pmtu = qp->pmtu; - struct hfi1_qp_priv *priv = qp->priv; + struct hfi1_qp_priv *qpriv = qp->priv; bool last_pkt; u32 delta; u8 next = qp->s_tail_ack_queue; + struct tid_rdma_request *req; trace_hfi1_rsp_make_rc_ack(qp, 0); lockdep_assert_held(&qp->s_lock); @@ -128,7 +129,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) goto bail; - if (priv->hdr_type == HFI1_PKT_TYPE_9B) + if (qpriv->hdr_type == HFI1_PKT_TYPE_9B) /* header size in 32-bit words LRH+BTH = (8+12)/4. */ hwords = 5; else @@ -206,6 +207,21 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, hwords++; qp->s_ack_rdma_psn = e->psn; bth2 = mask_psn(qp->s_ack_rdma_psn++); + } else if (e->opcode == TID_OP(WRITE_REQ)) { + /* + * If a TID RDMA WRITE RESP is being resent, we have to + * wait for the actual request. All requests that are to + * be resent will have their state set to + * TID_REQUEST_RESEND. When the new request arrives, the + * state will be changed to TID_REQUEST_RESEND_ACTIVE. + */ + req = ack_to_tid_req(e); + if (req->state == TID_REQUEST_RESEND || + req->state == TID_REQUEST_INIT_RESEND) + goto bail; + qp->s_ack_state = TID_OP(WRITE_RESP); + qp->s_ack_rdma_psn = mask_psn(e->psn + req->cur_seg); + goto write_resp; } else if (e->opcode == TID_OP(READ_REQ)) { /* * If a TID RDMA read response is being resent and @@ -267,6 +283,59 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, bth2 = mask_psn(qp->s_ack_rdma_psn++); break; + case TID_OP(WRITE_RESP): +write_resp: + /* + * 1. Check if RVT_S_ACK_PENDING is set. If yes, + * goto normal. + * 2. Attempt to allocate TID resources. + * 3. Remove RVT_S_RESP_PENDING flags from s_flags + * 4. If resources not available: + * 4.1 Set RVT_S_WAIT_TID_SPACE + * 4.2 Queue QP on RCD TID queue + * 4.3 Put QP on iowait list. + * 4.4 Build IB RNR NAK with appropriate timeout value + * 4.5 Return indication progress made. + * 5. If resources are available: + * 5.1 Program HW flow CSRs + * 5.2 Build TID RDMA WRITE RESP packet + * 5.3 If more resources needed, do 2.1 - 2.3. + * 5.4 Wake up next QP on RCD TID queue. + * 5.5 Return indication progress made. + */ + + e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + req = ack_to_tid_req(e); + + /* + * Send scheduled RNR NAK's. RNR NAK's need to be sent at + * segment boundaries, not at request boundaries. Don't change + * s_ack_state because we are still in the middle of a request + */ + if (qpriv->rnr_nak_state == TID_RNR_NAK_SEND && + qp->s_tail_ack_queue == qpriv->r_tid_alloc && + req->cur_seg == req->alloc_seg) { + qpriv->rnr_nak_state = TID_RNR_NAK_SENT; + goto normal_no_state; + } + + bth2 = mask_psn(qp->s_ack_rdma_psn); + hdrlen = hfi1_build_tid_rdma_write_resp(qp, e, ohdr, &bth1, + bth2, &len, + &ps->s_txreq->ss); + if (!hdrlen) + return 0; + + hwords += hdrlen; + bth0 = qp->s_ack_state << 24; + qp->s_ack_rdma_psn++; + if (req->cur_seg != req->total_segs) + break; + + e->sent = 1; + qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); + break; + case TID_OP(READ_RESP): read_resp: e = &qp->s_ack_queue[qp->s_tail_ack_queue]; @@ -298,8 +367,7 @@ normal: * (see above). */ qp->s_ack_state = OP(SEND_ONLY); - qp->s_flags &= ~RVT_S_ACK_PENDING; - ps->s_txreq->ss = NULL; +normal_no_state: if (qp->s_nak_state) ohdr->u.aeth = cpu_to_be32((qp->r_msn & IB_MSN_MASK) | @@ -311,9 +379,11 @@ normal: len = 0; bth0 = OP(ACKNOWLEDGE) << 24; bth2 = mask_psn(qp->s_ack_psn); + qp->s_flags &= ~RVT_S_ACK_PENDING; + ps->s_txreq->ss = NULL; } qp->s_rdma_ack_cnt++; - ps->s_txreq->sde = priv->s_sde; + ps->s_txreq->sde = qpriv->s_sde; ps->s_txreq->s_cur_size = len; ps->s_txreq->hdr_dwords = hwords; hfi1_make_ruc_header(qp, ohdr, bth0, bth1, bth2, middle, ps); @@ -366,6 +436,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) int middle = 0; int delta; struct tid_rdma_flow *flow = NULL; + struct tid_rdma_params *remote; trace_hfi1_sender_make_rc_req(qp); lockdep_assert_held(&qp->s_lock); @@ -414,7 +485,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) goto done_free_tx; } - if (qp->s_flags & (RVT_S_WAIT_RNR | RVT_S_WAIT_ACK)) + if (qp->s_flags & (RVT_S_WAIT_RNR | RVT_S_WAIT_ACK | HFI1_S_WAIT_HALT)) goto bail; if (cmp_psn(qp->s_psn, qp->s_sending_hpsn) <= 0) { @@ -586,6 +657,108 @@ no_flow_control: qp->s_cur = 0; break; + case IB_WR_TID_RDMA_WRITE: + if (newreq) { + /* + * Limit the number of TID RDMA WRITE requests. + */ + if (atomic_read(&priv->n_tid_requests) >= + HFI1_TID_RDMA_WRITE_CNT) + goto bail; + + if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) + qp->s_lsn++; + } + + hwords += hfi1_build_tid_rdma_write_req(qp, wqe, ohdr, + &bth1, &bth2, + &len); + ss = NULL; + if (priv->s_tid_cur == HFI1_QP_WQE_INVALID) { + priv->s_tid_cur = qp->s_cur; + if (priv->s_tid_tail == HFI1_QP_WQE_INVALID) { + priv->s_tid_tail = qp->s_cur; + priv->s_state = TID_OP(WRITE_RESP); + } + } else if (priv->s_tid_cur == priv->s_tid_head) { + struct rvt_swqe *__w; + struct tid_rdma_request *__r; + + __w = rvt_get_swqe_ptr(qp, priv->s_tid_cur); + __r = wqe_to_tid_req(__w); + + /* + * The s_tid_cur pointer is advanced to s_cur if + * any of the following conditions about the WQE + * to which s_ti_cur currently points to are + * satisfied: + * 1. The request is not a TID RDMA WRITE + * request, + * 2. The request is in the INACTIVE or + * COMPLETE states (TID RDMA READ requests + * stay at INACTIVE and TID RDMA WRITE + * transition to COMPLETE when done), + * 3. The request is in the ACTIVE or SYNC + * state and the number of completed + * segments is equal to the total segment + * count. + * (If ACTIVE, the request is waiting for + * ACKs. If SYNC, the request has not + * received any responses because it's + * waiting on a sync point.) + */ + if (__w->wr.opcode != IB_WR_TID_RDMA_WRITE || + __r->state == TID_REQUEST_INACTIVE || + __r->state == TID_REQUEST_COMPLETE || + ((__r->state == TID_REQUEST_ACTIVE || + __r->state == TID_REQUEST_SYNC) && + __r->comp_seg == __r->total_segs)) { + if (priv->s_tid_tail == + priv->s_tid_cur && + priv->s_state == + TID_OP(WRITE_DATA_LAST)) { + priv->s_tid_tail = qp->s_cur; + priv->s_state = + TID_OP(WRITE_RESP); + } + priv->s_tid_cur = qp->s_cur; + } + /* + * A corner case: when the last TID RDMA WRITE + * request was completed, s_tid_head, + * s_tid_cur, and s_tid_tail all point to the + * same location. Other requests are posted and + * s_cur wraps around to the same location, + * where a new TID RDMA WRITE is posted. In + * this case, none of the indices need to be + * updated. However, the priv->s_state should. + */ + if (priv->s_tid_tail == qp->s_cur && + priv->s_state == TID_OP(WRITE_DATA_LAST)) + priv->s_state = TID_OP(WRITE_RESP); + } + req = wqe_to_tid_req(wqe); + if (newreq) { + priv->s_tid_head = qp->s_cur; + priv->pending_tid_w_resp += req->total_segs; + atomic_inc(&priv->n_tid_requests); + atomic_dec(&priv->n_requests); + } else { + req->state = TID_REQUEST_RESEND; + req->comp_seg = delta_psn(bth2, wqe->psn); + /* + * Pull back any segments since we are going + * to re-receive them. + */ + req->setup_head = req->clear_tail; + priv->pending_tid_w_resp += + delta_psn(wqe->lpsn, bth2) + 1; + } + + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + case IB_WR_RDMA_READ: /* * Don't allow more operations to be started @@ -745,7 +918,8 @@ no_flow_control: if (qp->s_tail >= qp->s_size) qp->s_tail = 0; } - if (wqe->wr.opcode == IB_WR_RDMA_READ) + if (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) qp->s_psn = wqe->lpsn + 1; else if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) qp->s_psn = req->s_next_psn; @@ -865,6 +1039,33 @@ no_flow_control: if (qp->s_cur == qp->s_size) qp->s_cur = 0; break; + + case TID_OP(WRITE_RESP): + /* + * This value for s_state is used for restarting a TID RDMA + * WRITE request. See comment in OP(RDMA_READ_RESPONSE_MIDDLE + * for more). + */ + req = wqe_to_tid_req(wqe); + req->state = TID_REQUEST_RESEND; + rcu_read_lock(); + remote = rcu_dereference(priv->tid_rdma.remote); + req->comp_seg = delta_psn(qp->s_psn, wqe->psn); + len = wqe->length - (req->comp_seg * remote->max_len); + rcu_read_unlock(); + + bth2 = mask_psn(qp->s_psn); + hwords += hfi1_build_tid_rdma_write_req(qp, wqe, ohdr, &bth1, + &bth2, &len); + qp->s_psn = wqe->lpsn + 1; + ss = NULL; + qp->s_state = TID_OP(WRITE_REQ); + priv->pending_tid_w_resp += delta_psn(wqe->lpsn, bth2) + 1; + priv->s_tid_cur = qp->s_cur; + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + case TID_OP(READ_RESP): if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) goto bail; @@ -965,7 +1166,8 @@ no_flow_control: } qp->s_sending_hpsn = bth2; delta = delta_psn(bth2, wqe->psn); - if (delta && delta % HFI1_PSN_CREDIT == 0) + if (delta && delta % HFI1_PSN_CREDIT == 0 && + wqe->wr.opcode != IB_WR_TID_RDMA_WRITE) bth2 |= IB_BTH_REQ_ACK; if (qp->s_flags & RVT_S_SEND_ONE) { qp->s_flags &= ~RVT_S_SEND_ONE; @@ -998,6 +1200,12 @@ bail: bail_no_tx: ps->s_txreq = NULL; qp->s_flags &= ~RVT_S_BUSY; + /* + * If we didn't get a txreq, the QP will be woken up later to try + * again. Set the flags to indicate which work item to wake + * up. + */ + iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_IB); return 0; } @@ -1285,6 +1493,7 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) lockdep_assert_held(&qp->s_lock); qp->s_cur = n; priv->pending_tid_r_segs = 0; + priv->pending_tid_w_resp = 0; qp->s_num_rd_atomic = 0; /* @@ -1342,6 +1551,10 @@ static void reset_psn(struct rvt_qp *qp, u32 psn) qp->s_state = OP(RDMA_READ_RESPONSE_LAST); break; + case IB_WR_TID_RDMA_WRITE: + qp->s_state = TID_OP(WRITE_RESP); + break; + case IB_WR_RDMA_READ: qp->s_state = OP(RDMA_READ_RESPONSE_MIDDLE); break; @@ -1435,7 +1648,7 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) qp->s_flags &= ~(RVT_S_WAIT_FENCE | RVT_S_WAIT_RDMAR | RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_PSN | - RVT_S_WAIT_ACK); + RVT_S_WAIT_ACK | HFI1_S_WAIT_TID_RESP); if (wait) qp->s_flags |= RVT_S_SEND_ONE; reset_psn(qp, psn); @@ -1443,7 +1656,8 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) /* * Set qp->s_sending_psn to the next PSN after the given one. - * This would be psn+1 except when RDMA reads are present. + * This would be psn+1 except when RDMA reads or TID RDMA ops + * are present. */ static void reset_sending_psn(struct rvt_qp *qp, u32 psn) { @@ -1456,7 +1670,8 @@ static void reset_sending_psn(struct rvt_qp *qp, u32 psn) wqe = rvt_get_swqe_ptr(qp, n); if (cmp_psn(psn, wqe->lpsn) <= 0) { if (wqe->wr.opcode == IB_WR_RDMA_READ || - wqe->wr.opcode == IB_WR_TID_RDMA_READ) + wqe->wr.opcode == IB_WR_TID_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) qp->s_sending_psn = wqe->lpsn + 1; else qp->s_sending_psn = psn + 1; @@ -1479,8 +1694,9 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah) struct rvt_swqe *wqe; struct ib_header *hdr = NULL; struct hfi1_16b_header *hdr_16b = NULL; - u32 opcode; + u32 opcode, head, tail; u32 psn; + struct tid_rdma_request *req; lockdep_assert_held(&qp->s_lock); if (!(ib_rvt_state_ops[qp->state] & RVT_SEND_OR_FLUSH_OR_RECV_OK)) @@ -1507,29 +1723,84 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct hfi1_opa_header *opah) opcode = ib_bth_get_opcode(ohdr); if ((opcode >= OP(RDMA_READ_RESPONSE_FIRST) && opcode <= OP(ATOMIC_ACKNOWLEDGE)) || - opcode == TID_OP(READ_RESP)) { + opcode == TID_OP(READ_RESP) || + opcode == TID_OP(WRITE_RESP)) { WARN_ON(!qp->s_rdma_ack_cnt); qp->s_rdma_ack_cnt--; return; } psn = ib_bth_get_psn(ohdr); - reset_sending_psn(qp, psn); + /* + * Don't attempt to reset the sending PSN for packets in the + * KDETH PSN space since the PSN does not match anything. + */ + if (opcode != TID_OP(WRITE_DATA) && + opcode != TID_OP(WRITE_DATA_LAST) && + opcode != TID_OP(ACK) && opcode != TID_OP(RESYNC)) + reset_sending_psn(qp, psn); + + /* Handle TID RDMA WRITE packets differently */ + if (opcode >= TID_OP(WRITE_REQ) && + opcode <= TID_OP(WRITE_DATA_LAST)) { + head = priv->s_tid_head; + tail = priv->s_tid_cur; + /* + * s_tid_cur is set to s_tid_head in the case, where + * a new TID RDMA request is being started and all + * previous ones have been completed. + * Therefore, we need to do a secondary check in order + * to properly determine whether we should start the + * RC timer. + */ + wqe = rvt_get_swqe_ptr(qp, tail); + req = wqe_to_tid_req(wqe); + if (head == tail && req->comp_seg < req->total_segs) { + if (tail == 0) + tail = qp->s_size - 1; + else + tail -= 1; + } + } else { + head = qp->s_tail; + tail = qp->s_acked; + } /* * Start timer after a packet requesting an ACK has been sent and * there are still requests that haven't been acked. */ - if ((psn & IB_BTH_REQ_ACK) && qp->s_acked != qp->s_tail && + if ((psn & IB_BTH_REQ_ACK) && tail != head && + opcode != TID_OP(WRITE_DATA) && opcode != TID_OP(WRITE_DATA_LAST) && + opcode != TID_OP(RESYNC) && !(qp->s_flags & - (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) && - (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) { + (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) && + (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) { if (opcode == TID_OP(READ_REQ)) rvt_add_retry_timer_ext(qp, priv->timeout_shift); else rvt_add_retry_timer(qp); } + /* Start TID RDMA ACK timer */ + if ((opcode == TID_OP(WRITE_DATA) || + opcode == TID_OP(WRITE_DATA_LAST) || + opcode == TID_OP(RESYNC)) && + (psn & IB_BTH_REQ_ACK) && + !(priv->s_flags & HFI1_S_TID_RETRY_TIMER) && + (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) { + /* + * The TID RDMA ACK packet could be received before this + * function is called. Therefore, add the timer only if TID + * RDMA ACK packets are actually pending. + */ + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); + req = wqe_to_tid_req(wqe); + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE && + req->ack_seg < req->cur_seg) + hfi1_add_tid_retry_timer(qp); + } + while (qp->s_last != qp->s_acked) { u32 s_last; @@ -1628,7 +1899,16 @@ struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, } qp->s_retry = qp->s_retry_cnt; - update_last_psn(qp, wqe->lpsn); + /* + * Don't update the last PSN if the request being completed is + * a TID RDMA WRITE request. + * Completion of the TID RDMA WRITE requests are done by the + * TID RDMA ACKs and as such could be for a request that has + * already been ACKed as far as the IB state machine is + * concerned. + */ + if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE) + update_last_psn(qp, wqe->lpsn); /* * If we are completing a request which is in the process of @@ -1658,6 +1938,54 @@ struct rvt_swqe *do_rc_completion(struct rvt_qp *qp, return wqe; } +static void set_restart_qp(struct rvt_qp *qp, struct hfi1_ctxtdata *rcd) +{ + /* Retry this request. */ + if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) { + qp->r_flags |= RVT_R_RDMAR_SEQ; + hfi1_restart_rc(qp, qp->s_last_psn + 1, 0); + if (list_empty(&qp->rspwait)) { + qp->r_flags |= RVT_R_RSP_SEND; + rvt_get_qp(qp); + list_add_tail(&qp->rspwait, &rcd->qp_wait_list); + } + } +} + +/** + * update_qp_retry_state - Update qp retry state. + * @qp: the QP + * @psn: the packet sequence number of the TID RDMA WRITE RESP. + * @spsn: The start psn for the given TID RDMA WRITE swqe. + * @lpsn: The last psn for the given TID RDMA WRITE swqe. + * + * This function is called to update the qp retry state upon + * receiving a TID WRITE RESP after the qp is scheduled to retry + * a request. + */ +static void update_qp_retry_state(struct rvt_qp *qp, u32 psn, u32 spsn, + u32 lpsn) +{ + struct hfi1_qp_priv *qpriv = qp->priv; + + qp->s_psn = psn + 1; + /* + * If this is the first TID RDMA WRITE RESP packet for the current + * request, change the s_state so that the retry will be processed + * correctly. Similarly, if this is the last TID RDMA WRITE RESP + * packet, change the s_state and advance the s_cur. + */ + if (cmp_psn(psn, lpsn) >= 0) { + qp->s_cur = qpriv->s_tid_cur + 1; + if (qp->s_cur >= qp->s_size) + qp->s_cur = 0; + qp->s_state = TID_OP(WRITE_REQ); + } else if (!cmp_psn(psn, spsn)) { + qp->s_cur = qpriv->s_tid_cur; + qp->s_state = TID_OP(WRITE_RESP); + } +} + /** * do_rc_ack - process an incoming RC ACK * @qp: the QP the ACK came in on @@ -1679,6 +2007,7 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, int ret = 0; u32 ack_psn; int diff; + struct rvt_dev_info *rdi; lockdep_assert_held(&qp->s_lock); /* @@ -1725,18 +2054,10 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, (opcode != TID_OP(READ_RESP) || diff != 0)) || ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) && - (opcode != OP(ATOMIC_ACKNOWLEDGE) || diff != 0))) { - /* Retry this request. */ - if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) { - qp->r_flags |= RVT_R_RDMAR_SEQ; - hfi1_restart_rc(qp, qp->s_last_psn + 1, 0); - if (list_empty(&qp->rspwait)) { - qp->r_flags |= RVT_R_RSP_SEND; - rvt_get_qp(qp); - list_add_tail(&qp->rspwait, - &rcd->qp_wait_list); - } - } + (opcode != OP(ATOMIC_ACKNOWLEDGE) || diff != 0)) || + (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE && + (delta_psn(psn, qp->s_last_psn) != 1))) { + set_restart_qp(qp, rcd); /* * No need to process the ACK/NAK since we are * restarting an earlier request. @@ -1768,6 +2089,14 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, hfi1_schedule_send(qp); } } + + /* + * TID RDMA WRITE requests will be completed by the TID RDMA + * ACK packet handler (see tid_rdma.c). + */ + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) + break; + wqe = do_rc_completion(qp, wqe, ibp); if (qp->s_acked == qp->s_tail) break; @@ -1785,17 +2114,60 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, else rvt_stop_rc_timers(qp); } else if (qp->s_acked != qp->s_tail) { + struct rvt_swqe *__w = NULL; + + if (qpriv->s_tid_cur != HFI1_QP_WQE_INVALID) + __w = rvt_get_swqe_ptr(qp, qpriv->s_tid_cur); + /* - * We are expecting more ACKs so - * mod the retry timer. + * Stop timers if we've received all of the TID RDMA + * WRITE * responses. */ - rvt_mod_retry_timer(qp); - /* - * We can stop re-sending the earlier packets and - * continue with the next packet the receiver wants. - */ - if (cmp_psn(qp->s_psn, psn) <= 0) - reset_psn(qp, psn + 1); + if (__w && __w->wr.opcode == IB_WR_TID_RDMA_WRITE && + opcode == TID_OP(WRITE_RESP)) { + /* + * Normally, the loop above would correctly + * process all WQEs from s_acked onward and + * either complete them or check for correct + * PSN sequencing. + * However, for TID RDMA, due to pipelining, + * the response may not be for the request at + * s_acked so the above look would just be + * skipped. This does not allow for checking + * the PSN sequencing. It has to be done + * separately. + */ + if (cmp_psn(psn, qp->s_last_psn + 1)) { + set_restart_qp(qp, rcd); + goto bail_stop; + } + /* + * If the psn is being resent, stop the + * resending. + */ + if (qp->s_cur != qp->s_tail && + cmp_psn(qp->s_psn, psn) <= 0) + update_qp_retry_state(qp, psn, + __w->psn, + __w->lpsn); + else if (--qpriv->pending_tid_w_resp) + rvt_mod_retry_timer(qp); + else + rvt_stop_rc_timers(qp); + } else { + /* + * We are expecting more ACKs so + * mod the retry timer. + */ + rvt_mod_retry_timer(qp); + /* + * We can stop re-sending the earlier packets + * and continue with the next packet the + * receiver wants. + */ + if (cmp_psn(qp->s_psn, psn) <= 0) + reset_psn(qp, psn + 1); + } } else { /* No more acks - kill all timers */ rvt_stop_rc_timers(qp); @@ -1811,6 +2183,15 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, rvt_get_credit(qp, aeth); qp->s_rnr_retry = qp->s_rnr_retry_cnt; qp->s_retry = qp->s_retry_cnt; + /* + * If the current request is a TID RDMA WRITE request and the + * response is not a TID RDMA WRITE RESP packet, s_last_psn + * can't be advanced. + */ + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE && + opcode != TID_OP(WRITE_RESP) && + cmp_psn(psn, wqe->psn) >= 0) + return 1; update_last_psn(qp, psn); return 1; @@ -1820,20 +2201,31 @@ int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, goto bail_stop; if (qp->s_flags & RVT_S_WAIT_RNR) goto bail_stop; - if (qp->s_rnr_retry == 0) { + rdi = ib_to_rvt(qp->ibqp.device); + if (qp->s_rnr_retry == 0 && + !((rdi->post_parms[wqe->wr.opcode].flags & + RVT_OPERATION_IGN_RNR_CNT) && + qp->s_rnr_retry_cnt == 0)) { status = IB_WC_RNR_RETRY_EXC_ERR; goto class_b; } - if (qp->s_rnr_retry_cnt < 7) + if (qp->s_rnr_retry_cnt < 7 && qp->s_rnr_retry_cnt > 0) qp->s_rnr_retry--; - /* The last valid PSN is the previous PSN. */ - update_last_psn(qp, psn - 1); + /* + * The last valid PSN is the previous PSN. For TID RDMA WRITE + * request, s_last_psn should be incremented only when a TID + * RDMA WRITE RESP is received to avoid skipping lost TID RDMA + * WRITE RESP packets. + */ + if (wqe->wr.opcode == IB_WR_TID_RDMA_WRITE) { + reset_psn(qp, qp->s_last_psn + 1); + } else { + update_last_psn(qp, psn - 1); + reset_psn(qp, psn); + } ibp->rvp.n_rc_resends += delta_psn(qp->s_psn, psn); - - reset_psn(qp, psn); - qp->s_flags &= ~(RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_ACK); rvt_stop_rc_timers(qp); rvt_add_rnr_timer(qp, aeth); @@ -1918,6 +2310,7 @@ static void rdma_seq_err(struct rvt_qp *qp, struct hfi1_ibport *ibp, u32 psn, while (cmp_psn(psn, wqe->lpsn) > 0) { if (wqe->wr.opcode == IB_WR_RDMA_READ || wqe->wr.opcode == IB_WR_TID_RDMA_READ || + wqe->wr.opcode == IB_WR_TID_RDMA_WRITE || wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) break; diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 80111dd1d876..490e47a0f68b 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -3205,6 +3205,20 @@ void hfi1_qp_kern_exp_rcv_clear_all(struct rvt_qp *qp) do { struct hfi1_swqe_priv *priv = wqe->priv; + ret = hfi1_kern_exp_rcv_clear(&priv->tid_req); + } while (!ret); + } + for (i = qp->s_acked_ack_queue; i != qp->r_head_ack_queue;) { + struct rvt_ack_entry *e = &qp->s_ack_queue[i]; + + if (++i == rvt_max_atomic(ib_to_rvt(qp->ibqp.device))) + i = 0; + /* Free only locally allocated TID entries */ + if (e->opcode != TID_OP(WRITE_REQ)) + continue; + do { + struct hfi1_ack_priv *priv = e->priv; + ret = hfi1_kern_exp_rcv_clear(&priv->tid_req); } while (!ret); } diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index e5e7fad09f32..6764114b886c 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -1126,7 +1126,8 @@ static inline u32 set_pkt_bth_psn(__be32 bthpsn, u8 expct, u32 frags) 0xffffffull), psn = val & mask; if (expct) - psn = (psn & ~BTH_SEQ_MASK) | ((psn + frags) & BTH_SEQ_MASK); + psn = (psn & ~HFI1_KDETH_BTH_SEQ_MASK) | + ((psn + frags) & HFI1_KDETH_BTH_SEQ_MASK); else psn = psn + frags; return psn & mask; diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 7b87b77582bd..ab97d71cdd92 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -161,6 +161,7 @@ MODULE_PARM_DESC(wss_clean_period, "Count of verbs copies before an entry in the */ const enum ib_wc_opcode ib_hfi1_wc_opcode[] = { [IB_WR_RDMA_WRITE] = IB_WC_RDMA_WRITE, + [IB_WR_TID_RDMA_WRITE] = IB_WC_RDMA_WRITE, [IB_WR_RDMA_WRITE_WITH_IMM] = IB_WC_RDMA_WRITE, [IB_WR_SEND] = IB_WC_SEND, [IB_WR_SEND_WITH_IMM] = IB_WC_SEND, @@ -203,6 +204,12 @@ const u8 hdr_len_by_opcode[256] = { [IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = 12 + 8 + 4, [IB_OPCODE_TID_RDMA_READ_REQ] = 12 + 8 + 36, [IB_OPCODE_TID_RDMA_READ_RESP] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_WRITE_REQ] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_WRITE_RESP] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_WRITE_DATA] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_WRITE_DATA_LAST] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_ACK] = 12 + 8 + 36, + [IB_OPCODE_TID_RDMA_RESYNC] = 12 + 8 + 36, /* UC */ [IB_OPCODE_UC_SEND_FIRST] = 12 + 8, [IB_OPCODE_UC_SEND_MIDDLE] = 12 + 8, @@ -248,8 +255,14 @@ static const opcode_handler opcode_handler_tbl[256] = { [IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = &hfi1_rc_rcv, /* TID RDMA has separate handlers for different opcodes.*/ + [IB_OPCODE_TID_RDMA_WRITE_REQ] = &hfi1_rc_rcv_tid_rdma_write_req, + [IB_OPCODE_TID_RDMA_WRITE_RESP] = &hfi1_rc_rcv_tid_rdma_write_resp, + [IB_OPCODE_TID_RDMA_WRITE_DATA] = &hfi1_rc_rcv_tid_rdma_write_data, + [IB_OPCODE_TID_RDMA_WRITE_DATA_LAST] = &hfi1_rc_rcv_tid_rdma_write_data, [IB_OPCODE_TID_RDMA_READ_REQ] = &hfi1_rc_rcv_tid_rdma_read_req, [IB_OPCODE_TID_RDMA_READ_RESP] = &hfi1_rc_rcv_tid_rdma_read_resp, + [IB_OPCODE_TID_RDMA_RESYNC] = &hfi1_rc_rcv_tid_rdma_resync, + [IB_OPCODE_TID_RDMA_ACK] = &hfi1_rc_rcv_tid_rdma_ack, /* UC */ [IB_OPCODE_UC_SEND_FIRST] = &hfi1_uc_rcv, @@ -1332,7 +1345,9 @@ static void hfi1_fill_device_attr(struct hfi1_devdata *dd) rdi->dparms.props.max_mr_size = U64_MAX; rdi->dparms.props.max_fast_reg_page_list_len = UINT_MAX; rdi->dparms.props.max_qp = hfi1_max_qps; - rdi->dparms.props.max_qp_wr = hfi1_max_qp_wrs; + rdi->dparms.props.max_qp_wr = + (hfi1_max_qp_wrs >= HFI1_QP_WQE_INVALID ? + HFI1_QP_WQE_INVALID - 1 : hfi1_max_qp_wrs); rdi->dparms.props.max_send_sge = hfi1_max_sges; rdi->dparms.props.max_recv_sge = hfi1_max_sges; rdi->dparms.props.max_sge_rd = hfi1_max_sges; diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index bee3d21a548e..62ace0b2d17a 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -193,6 +193,7 @@ struct hfi1_qp_priv { u32 r_tid_ack; /* the TID RDMA request to be ACK'ed */ u32 r_tid_alloc; /* Request for which we are allocating resources */ u32 pending_tid_w_segs; /* Num of pending tid write segments */ + u32 pending_tid_w_resp; /* Num of pending tid write responses */ u32 alloc_w_segs; /* Number of segments for which write */ /* resources have been allocated for this QP */ diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index 4ee612ab6cb4..f0fbd4063fef 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -246,6 +246,7 @@ struct rvt_ack_entry { #define RVT_OPERATION_ATOMIC_SGE 0x00000004 #define RVT_OPERATION_LOCAL 0x00000008 #define RVT_OPERATION_USE_RESERVE 0x00000010 +#define RVT_OPERATION_IGN_RNR_CNT 0x00000020 #define RVT_OPERATION_MAX (IB_WR_RESERVED10 + 1) From c6c231175ccdf188d443c27e5456b9e2f65e44d4 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:51:49 -0800 Subject: [PATCH 1168/4277] IB/hfi1: Add interlock between TID RDMA WRITE and other requests This locking mechanism is designed to provent vavious memory corruption scenarios from occurring when requests are pipelined, especially when RDMA WRITE requests are interleaved with TID RDMA READ requests: 1. READ-AFTER-READ; 2. READ-AFTER-WRITE; 3. WRITE-AFTER-READ; 4. WRITE-AFTER-WRITE. When memory corruption is likely, a request will be held back until previous requests have been completed. Reviewed-by: Mike Marciniszyn Signed-off-by: Mitko Haralanov Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 6 ++++ drivers/infiniband/hw/hfi1/tid_rdma.c | 46 +++++++++++++++++++++++++-- drivers/infiniband/hw/hfi1/tid_rdma.h | 9 ++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 6d2abea896e5..cfb863364f50 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -173,6 +173,12 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, } e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + /* Check for tid write fence */ + if ((qpriv->s_flags & HFI1_R_TID_WAIT_INTERLCK) || + hfi1_tid_rdma_ack_interlock(qp, e)) { + iowait_set_flag(&qpriv->s_iowait, IOWAIT_PENDING_IB); + goto bail; + } if (e->opcode == OP(RDMA_READ_REQUEST)) { /* * If a RDMA read response is being resent and diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 490e47a0f68b..286752011f25 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2179,6 +2179,7 @@ static int tid_rdma_rcv_error(struct hfi1_packet *packet, req->state = TID_REQUEST_RESEND; req->cur_seg = req->comp_seg; } + qpriv->s_flags &= ~HFI1_R_TID_WAIT_INTERLCK; } /* Re-process old requests.*/ if (qp->s_acked_ack_queue == qp->s_tail_ack_queue) @@ -3229,6 +3230,7 @@ bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe) struct rvt_swqe *prev; struct hfi1_qp_priv *priv = qp->priv; u32 s_prev; + struct tid_rdma_request *req; s_prev = (qp->s_cur == 0 ? qp->s_size : qp->s_cur) - 1; prev = rvt_get_swqe_ptr(qp, s_prev); @@ -3240,14 +3242,28 @@ bool hfi1_tid_rdma_wqe_interlock(struct rvt_qp *qp, struct rvt_swqe *wqe) case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: case IB_WR_RDMA_WRITE: + switch (prev->wr.opcode) { + case IB_WR_TID_RDMA_WRITE: + req = wqe_to_tid_req(prev); + if (req->ack_seg != req->total_segs) + goto interlock; + default: + break; + } case IB_WR_RDMA_READ: - break; + if (prev->wr.opcode != IB_WR_TID_RDMA_WRITE) + break; + /* fall through */ case IB_WR_TID_RDMA_READ: switch (prev->wr.opcode) { case IB_WR_RDMA_READ: if (qp->s_acked != qp->s_cur) goto interlock; break; + case IB_WR_TID_RDMA_WRITE: + req = wqe_to_tid_req(prev); + if (req->ack_seg != req->total_segs) + goto interlock; default: break; } @@ -5157,7 +5173,9 @@ static int make_tid_rdma_ack(struct rvt_qp *qp, e = &qp->s_ack_queue[qpriv->r_tid_ack]; req = ack_to_tid_req(e); flow = req->acked_tail; - } + } else if (req->ack_seg == req->total_segs && + qpriv->s_flags & HFI1_R_TID_WAIT_INTERLCK) + qpriv->s_flags &= ~HFI1_R_TID_WAIT_INTERLCK; hwords += hfi1_build_tid_rdma_write_ack(qp, e, ohdr, flow, &bth1, &bth2); @@ -5310,3 +5328,27 @@ bool hfi1_schedule_tid_send(struct rvt_qp *qp) IOWAIT_PENDING_TID); return false; } + +bool hfi1_tid_rdma_ack_interlock(struct rvt_qp *qp, struct rvt_ack_entry *e) +{ + struct rvt_ack_entry *prev; + struct tid_rdma_request *req; + struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); + struct hfi1_qp_priv *priv = qp->priv; + u32 s_prev; + + s_prev = qp->s_tail_ack_queue == 0 ? rvt_size_atomic(&dev->rdi) : + (qp->s_tail_ack_queue - 1); + prev = &qp->s_ack_queue[s_prev]; + + if ((e->opcode == TID_OP(READ_REQ) || + e->opcode == OP(RDMA_READ_REQUEST)) && + prev->opcode == TID_OP(WRITE_REQ)) { + req = ack_to_tid_req(prev); + if (req->ack_seg != req->total_segs) { + priv->s_flags |= HFI1_R_TID_WAIT_INTERLCK; + return true; + } + } + return false; +} diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 7f8f17ba6c14..44468188a374 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -25,6 +25,7 @@ * s_flags, there are no collisions. * * HFI1_S_TID_WAIT_INTERLCK - QP is waiting for requester interlock + * HFI1_R_TID_WAIT_INTERLCK - QP is waiting for responder interlock */ #define HFI1_S_TID_BUSY_SET BIT(0) /* BIT(1) reserved for RVT_S_BUSY. */ @@ -32,9 +33,15 @@ /* BIT(3) reserved for RVT_S_RESP_PENDING. */ /* BIT(4) reserved for RVT_S_ACK_PENDING. */ #define HFI1_S_TID_WAIT_INTERLCK BIT(5) +#define HFI1_R_TID_WAIT_INTERLCK BIT(6) /* BIT(7) - BIT(15) reserved for RVT_S_WAIT_*. */ +/* BIT(16) reserved for RVT_S_SEND_ONE */ #define HFI1_S_TID_RETRY_TIMER BIT(17) +/* BIT(18) reserved for RVT_S_ECN. */ #define HFI1_R_TID_SW_PSN BIT(19) +/* BIT(26) reserved for HFI1_S_WAIT_HALT */ +/* BIT(27) reserved for HFI1_S_WAIT_TID_RESP */ +/* BIT(28) reserved for HFI1_S_WAIT_TID_SPACE */ /* * Unlike regular IB RDMA VERBS, which do not require an entry @@ -309,4 +316,6 @@ void _hfi1_do_tid_send(struct work_struct *work); bool hfi1_schedule_tid_send(struct rvt_qp *qp); +bool hfi1_tid_rdma_ack_interlock(struct rvt_qp *qp, struct rvt_ack_entry *e); + #endif /* HFI1_TID_RDMA_H */ From ad00889e7ca226a2bed2b210f17c93b7be1b1542 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:51:59 -0800 Subject: [PATCH 1169/4277] IB/hfi1: Enable TID RDMA WRITE protocol This patch enables TID RDMA WRITE protocol by converting a qualified RDMA WRITE request into a TID RDMA WRITE request internally: (1) The TID RDMA cability must be enabled; (2) The request must start on a 4K page boundary; (3) The request length must be a multiple of 4K and must be larger or equal to 256K. Signed-off-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/tid_rdma.c | 22 ++++++++++++++++++++++ drivers/infiniband/hw/hfi1/tid_rdma.h | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 286752011f25..db3188f66dba 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -3322,6 +3322,18 @@ void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) new_opcode = IB_WR_TID_RDMA_READ; do_tid_rdma = true; } + } else if (wqe->wr.opcode == IB_WR_RDMA_WRITE) { + /* + * TID RDMA is enabled for this RDMA WRITE request iff: + * 1. The remote address is page-aligned, + * 2. The length is larger than the minimum segment size, + * 3. The length is page-multiple. + */ + if (!(wqe->rdma_wr.remote_addr & ~PAGE_MASK) && + !(wqe->length & ~PAGE_MASK)) { + new_opcode = IB_WR_TID_RDMA_WRITE; + do_tid_rdma = true; + } } if (do_tid_rdma) { @@ -3338,12 +3350,22 @@ void setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) priv->tid_req.n_flows = remote->max_read; qpriv->tid_r_reqs++; wqe->lpsn += rvt_div_round_up_mtu(qp, wqe->length) - 1; + } else { + wqe->lpsn += priv->tid_req.total_segs - 1; + atomic_inc(&qpriv->n_requests); } priv->tid_req.cur_seg = 0; priv->tid_req.comp_seg = 0; priv->tid_req.ack_seg = 0; priv->tid_req.state = TID_REQUEST_INACTIVE; + /* + * Reset acked_tail. + * TID RDMA READ does not have ACKs so it does not + * update the pointer. We have to reset it so TID RDMA + * WRITE does not get confused. + */ + priv->tid_req.acked_tail = priv->tid_req.setup_head; trace_hfi1_tid_req_setup_tid_wqe(qp, 1, wqe->wr.opcode, wqe->psn, wqe->lpsn, &priv->tid_req); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.h b/drivers/infiniband/hw/hfi1/tid_rdma.h index 44468188a374..53ab24ef4f02 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.h +++ b/drivers/infiniband/hw/hfi1/tid_rdma.h @@ -266,7 +266,8 @@ static inline void hfi1_setup_tid_rdma_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe) { if (wqe->priv && - wqe->wr.opcode == IB_WR_RDMA_READ && + (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_RDMA_WRITE) && wqe->length >= TID_RDMA_MIN_SEGMENT_SIZE) setup_tid_rdma_wqe(qp, wqe); } From a05c9bdcfd16cec3a004cca339ab45de4cdf4799 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:52:09 -0800 Subject: [PATCH 1170/4277] IB/hfi1: Add static trace for TID RDMA WRITE protocol This patch makes the following changes to the static trace: 1. Adds the decoding of TID RDMA WRITE packets in IB header trace; 2. Adds trace events for various stages of the TID RDMA WRITE protocol. These events provide a fine-grained control for monitoring and debugging the hfi1 driver in the filed. Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/rc.c | 12 + drivers/infiniband/hw/hfi1/tid_rdma.c | 88 ++++ drivers/infiniband/hw/hfi1/trace.c | 66 +++ drivers/infiniband/hw/hfi1/trace_ibhdrs.h | 6 + drivers/infiniband/hw/hfi1/trace_tid.h | 517 +++++++++++++++++++++- drivers/infiniband/hw/hfi1/trace_tx.h | 6 + 6 files changed, 692 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index cfb863364f50..82afa7736be7 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -162,6 +162,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, qp->s_acked_ack_queue == qp->s_tail_ack_queue) qp->s_acked_ack_queue = next; qp->s_tail_ack_queue = next; + trace_hfi1_rsp_make_rc_ack(qp, e->psn); /* FALLTHROUGH */ case OP(SEND_ONLY): case OP(ACKNOWLEDGE): @@ -263,6 +264,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, bth2 = mask_psn(e->psn); e->sent = 1; } + trace_hfi1_tid_write_rsp_make_rc_ack(qp); bth0 = qp->s_ack_state << 24; break; @@ -335,6 +337,8 @@ write_resp: hwords += hdrlen; bth0 = qp->s_ack_state << 24; qp->s_ack_rdma_psn++; + trace_hfi1_tid_req_make_rc_ack_write(qp, 0, e->opcode, e->psn, + e->lpsn, req); if (req->cur_seg != req->total_segs) break; @@ -761,6 +765,11 @@ no_flow_control: delta_psn(wqe->lpsn, bth2) + 1; } + trace_hfi1_tid_write_sender_make_req(qp, newreq); + trace_hfi1_tid_req_make_req_write(qp, newreq, + wqe->wr.opcode, + wqe->psn, wqe->lpsn, + req); if (++qp->s_cur == qp->s_size) qp->s_cur = 0; break; @@ -1070,6 +1079,8 @@ no_flow_control: priv->s_tid_cur = qp->s_cur; if (++qp->s_cur == qp->s_size) qp->s_cur = 0; + trace_hfi1_tid_req_make_req_write(qp, 0, wqe->wr.opcode, + wqe->psn, wqe->lpsn, req); break; case TID_OP(READ_RESP): @@ -1625,6 +1636,7 @@ void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) wqe = do_rc_completion(qp, wqe, ibp); qp->s_flags &= ~RVT_S_WAIT_ACK; } else { + trace_hfi1_tid_write_sender_restart_rc(qp, 0); if (wqe->wr.opcode == IB_WR_TID_RDMA_READ) { struct tid_rdma_request *req; diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index db3188f66dba..a49eb3d9b5b9 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2962,6 +2962,12 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, e = &qp->s_ack_queue[qpriv->r_tid_tail]; req = ack_to_tid_req(e); flow = &req->flows[req->clear_tail]; + trace_hfi1_eflags_err_write(qp, rcv_type, rte, psn); + trace_hfi1_rsp_handle_kdeth_eflags(qp, psn); + trace_hfi1_tid_write_rsp_handle_kdeth_eflags(qp); + trace_hfi1_tid_req_handle_kdeth_eflags(qp, 0, e->opcode, e->psn, + e->lpsn, req); + trace_hfi1_tid_flow_handle_kdeth_eflags(qp, req->clear_tail, flow); switch (rcv_type) { case RHF_RCV_TYPE_EXPECTED: @@ -3489,6 +3495,8 @@ static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx) lockdep_assert_held(&qp->s_lock); while (1) { + trace_hfi1_rsp_tid_write_alloc_res(qp, 0); + trace_hfi1_tid_write_rsp_alloc_res(qp); /* * Don't allocate more segments if a RNR NAK has already been * scheduled to avoid messing up qp->r_psn: the RNR NAK will @@ -3517,6 +3525,8 @@ static void hfi1_tid_write_alloc_resources(struct rvt_qp *qp, bool intr_ctx) if (e->opcode != TID_OP(WRITE_REQ)) goto next_req; req = ack_to_tid_req(e); + trace_hfi1_tid_req_write_alloc_res(qp, 0, e->opcode, e->psn, + e->lpsn, req); /* Finished allocating for all segments of this request */ if (req->alloc_seg >= req->total_segs) goto next_req; @@ -3633,6 +3643,7 @@ send_rnr_nak: */ qp->s_flags &= ~(RVT_S_ACK_PENDING); + trace_hfi1_rsp_tid_write_alloc_res(qp, qp->r_psn); /* * qpriv->rnr_nak_state is used to determine when the scheduled RNR NAK * has actually been sent. qp->s_flags RVT_S_ACK_PENDING bit cannot be @@ -3686,6 +3697,7 @@ void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet) is_fecn = process_ecn(qp, packet); psn = mask_psn(be32_to_cpu(ohdr->bth[2])); + trace_hfi1_rsp_rcv_tid_write_req(qp, psn); if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) rvt_comm_est(qp); @@ -3794,6 +3806,9 @@ void hfi1_rc_rcv_tid_rdma_write_req(struct hfi1_packet *packet) qp->r_msn++; qp->r_psn++; + trace_hfi1_tid_req_rcv_write_req(qp, 0, e->opcode, e->psn, e->lpsn, + req); + if (qpriv->r_tid_tail == HFI1_QP_WQE_INVALID) { qpriv->r_tid_tail = qp->r_head_ack_queue; } else if (qpriv->r_tid_tail == qpriv->r_tid_head) { @@ -3814,6 +3829,7 @@ update_head: qpriv->r_tid_head = qp->r_head_ack_queue; hfi1_tid_write_alloc_resources(qp, true); + trace_hfi1_tid_write_rsp_rcv_req(qp); /* Schedule the send tasklet. */ qp->s_flags |= RVT_S_RESP_PENDING; @@ -3855,6 +3871,10 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, void *resp_addr = NULL; struct tid_rdma_params *remote; + trace_hfi1_tid_req_build_write_resp(qp, 0, e->opcode, e->psn, e->lpsn, + req); + trace_hfi1_tid_write_rsp_build_resp(qp); + trace_hfi1_rsp_build_tid_write_resp(qp, bth2); flow = &req->flows[req->flow_idx]; switch (req->state) { default: @@ -3876,12 +3896,14 @@ u32 hfi1_build_tid_rdma_write_resp(struct rvt_qp *qp, struct rvt_ack_entry *e, goto done; req->state = TID_REQUEST_ACTIVE; + trace_hfi1_tid_flow_build_write_resp(qp, req->flow_idx, flow); req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS); hfi1_add_tid_reap_timer(qp); break; case TID_REQUEST_RESEND_ACTIVE: case TID_REQUEST_RESEND: + trace_hfi1_tid_flow_build_write_resp(qp, req->flow_idx, flow); req->flow_idx = CIRC_NEXT(req->flow_idx, MAX_FLOWS); if (!CIRC_CNT(req->setup_head, req->flow_idx, MAX_FLOWS)) req->state = TID_REQUEST_ACTIVE; @@ -3996,6 +4018,9 @@ static void hfi1_tid_timeout(struct timer_list *t) if (qpriv->s_flags & HFI1_R_TID_RSC_TIMER) { dd_dev_warn(dd_from_ibdev(qp->ibqp.device), "[QP%u] %s %d\n", qp->ibqp.qp_num, __func__, __LINE__); + trace_hfi1_msg_tid_timeout(/* msg */ + qp, "resource timeout = ", + (u64)qpriv->tid_timer_timeout_jiffies); hfi1_stop_tid_reap_timer(qp); /* * Go though the entire ack queue and clear any outstanding @@ -4102,6 +4127,8 @@ void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet) if (!do_rc_ack(qp, aeth, psn, opcode, 0, rcd)) goto ack_done; + trace_hfi1_ack(qp, psn); + flow = &req->flows[req->setup_head]; flow->pkt = 0; flow->tid_idx = 0; @@ -4129,13 +4156,17 @@ void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet) } memcpy(flow->tid_entry, packet->ebuf, pktlen); flow->tidcnt = pktlen / sizeof(*flow->tid_entry); + trace_hfi1_tid_flow_rcv_write_resp(qp, req->setup_head, flow); req->comp_seg++; + trace_hfi1_tid_write_sender_rcv_resp(qp, 0); /* * Walk the TID_ENTRY list to make sure we have enough space for a * complete segment. */ for (i = 0; i < flow->tidcnt; i++) { + trace_hfi1_tid_entry_rcv_write_resp(/* entry */ + qp, i, flow->tid_entry[i]); if (!EXP_TID_GET(flow->tid_entry[i], LEN)) { status = IB_WC_LOC_LEN_ERR; goto ack_err; @@ -4147,6 +4178,8 @@ void hfi1_rc_rcv_tid_rdma_write_resp(struct hfi1_packet *packet) goto ack_err; } + trace_hfi1_tid_req_rcv_write_resp(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); /* * If this is the first response for this request, set the initial * flow index to the current flow. @@ -4221,6 +4254,8 @@ bool hfi1_build_tid_rdma_packet(struct rvt_swqe *wqe, next_offset = flow->tid_offset + *len; last_pkt = (flow->tid_idx == (flow->tidcnt - 1) && next_offset >= tidlen) || (flow->sent >= flow->length); + trace_hfi1_tid_entry_build_write_data(qp, flow->tid_idx, tidentry); + trace_hfi1_tid_flow_build_write_data(qp, req->clear_tail, flow); rcu_read_lock(); remote = rcu_dereference(qpriv->tid_rdma.remote); @@ -4303,6 +4338,10 @@ void hfi1_rc_rcv_tid_rdma_write_data(struct hfi1_packet *packet) * - The entire request is complete and there are no more requests * (of any kind) in the queue. */ + trace_hfi1_rsp_rcv_tid_write_data(qp, psn); + trace_hfi1_tid_req_rcv_write_data(qp, 0, e->opcode, e->psn, e->lpsn, + req); + trace_hfi1_tid_write_rsp_rcv_data(qp); if (priv->r_tid_ack == HFI1_QP_WQE_INVALID) priv->r_tid_ack = priv->r_tid_tail; @@ -4451,6 +4490,7 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) unsigned long flags; u16 fidx; + trace_hfi1_tid_write_sender_rcv_tid_ack(qp, 0); is_fecn = process_ecn(qp, packet); psn = mask_psn(be32_to_cpu(ohdr->bth[2])); aeth = be32_to_cpu(ohdr->u.tid_rdma.ack.aeth); @@ -4458,6 +4498,7 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) resync_psn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.ack.tid_flow_psn)); spin_lock_irqsave(&qp->s_lock, flags); + trace_hfi1_rcv_tid_ack(qp, aeth, psn, req_psn, resync_psn); /* If we are waiting for an ACK to RESYNC, drop any other packets */ if ((qp->s_flags & HFI1_S_WAIT_HALT) && @@ -4480,7 +4521,10 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) goto ack_op_err; req = wqe_to_tid_req(wqe); + trace_hfi1_tid_req_rcv_tid_ack(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); flow = &req->flows[req->acked_tail]; + trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, flow); /* Drop stale ACK/NAK */ if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.spsn)) < 0) @@ -4493,11 +4537,14 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) /* advance acked segment pointer */ req->acked_tail = CIRC_NEXT(req->acked_tail, MAX_FLOWS); req->r_last_acked = flow->flow_state.resp_ib_psn; + trace_hfi1_tid_req_rcv_tid_ack(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); if (req->ack_seg == req->total_segs) { req->state = TID_REQUEST_COMPLETE; wqe = do_rc_completion(qp, wqe, to_iport(qp->ibqp.device, qp->port_num)); + trace_hfi1_sender_rcv_tid_ack(qp); atomic_dec(&qpriv->n_tid_requests); if (qp->s_acked == qp->s_tail) break; @@ -4506,8 +4553,11 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) req = wqe_to_tid_req(wqe); } flow = &req->flows[req->acked_tail]; + trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, flow); } + trace_hfi1_tid_req_rcv_tid_ack(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); switch (aeth >> 29) { case 0: /* ACK */ if (qpriv->s_flags & RVT_S_WAIT_ACK) @@ -4614,6 +4664,9 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) flow->pkt = 0; spsn += flow->npkts; resync_psn += flow->npkts; + trace_hfi1_tid_flow_rcv_tid_ack(qp, + fidx, + flow); } if (++last_acked == qpriv->s_tid_cur + 1) break; @@ -4638,6 +4691,8 @@ done: case 0: /* PSN sequence error */ flow = &req->flows[req->acked_tail]; fspsn = full_flow_psn(flow, flow->flow_state.spsn); + trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, + flow); req->r_ack_psn = mask_psn(be32_to_cpu(ohdr->bth[2])); req->cur_seg = req->ack_seg; qpriv->s_tid_tail = qp->s_acked; @@ -4713,16 +4768,28 @@ static void hfi1_tid_retry_timeout(struct timer_list *t) struct rvt_qp *qp = priv->owner; struct rvt_swqe *wqe; unsigned long flags; + struct tid_rdma_request *req; spin_lock_irqsave(&qp->r_lock, flags); spin_lock(&qp->s_lock); + trace_hfi1_tid_write_sender_retry_timeout(qp, 0); if (priv->s_flags & HFI1_S_TID_RETRY_TIMER) { hfi1_stop_tid_retry_timer(qp); if (!priv->s_retry) { + trace_hfi1_msg_tid_retry_timeout(/* msg */ + qp, + "Exhausted retries. Tid retry timeout = ", + (u64)priv->tid_retry_timeout_jiffies); + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); hfi1_trdma_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR); rvt_error_qp(qp, IB_WC_WR_FLUSH_ERR); } else { + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); + req = wqe_to_tid_req(wqe); + trace_hfi1_tid_req_tid_retry_timeout(/* req */ + qp, 0, wqe->wr.opcode, wqe->psn, wqe->lpsn, req); + priv->s_flags &= ~RVT_S_WAIT_ACK; /* Only send one packet (the RESYNC) */ priv->s_flags |= RVT_S_SEND_ONE; @@ -4818,6 +4885,7 @@ void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet) * sync point and the flow has/will be reprogrammed */ qpriv->s_flags &= ~HFI1_R_TID_SW_PSN; + trace_hfi1_tid_write_rsp_rcv_resync(qp); /* * Reset all TID flow information with the new generation. @@ -4832,6 +4900,8 @@ void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet) e = &qp->s_ack_queue[idx]; if (e->opcode == TID_OP(WRITE_REQ)) { req = ack_to_tid_req(e); + trace_hfi1_tid_req_rcv_resync(qp, 0, e->opcode, e->psn, + e->lpsn, req); /* start from last unacked segment */ for (flow_idx = req->clear_tail; @@ -4854,6 +4924,8 @@ void hfi1_rc_rcv_tid_rdma_resync(struct hfi1_packet *packet) full_flow_psn(flow, flow->flow_state.spsn); fs->psn += flow->npkts; + trace_hfi1_tid_flow_rcv_resync(qp, flow_idx, + flow); } } if (idx == qp->s_tail_ack_queue) @@ -4913,6 +4985,7 @@ int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) u8 opcode = TID_OP(WRITE_DATA); lockdep_assert_held(&qp->s_lock); + trace_hfi1_tid_write_sender_make_tid_pkt(qp, 0); /* * Prioritize the sending of the requests and responses over the * sending of the TID RDMA data packets. @@ -4970,6 +5043,8 @@ int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) goto bail; wqe = rvt_get_swqe_ptr(qp, priv->s_tid_tail); req = wqe_to_tid_req(wqe); + trace_hfi1_tid_req_make_tid_pkt(qp, 0, wqe->wr.opcode, wqe->psn, + wqe->lpsn, req); switch (priv->s_state) { case TID_OP(WRITE_REQ): case TID_OP(WRITE_RESP): @@ -4997,6 +5072,8 @@ int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) * 3.2.2 Advance RESP pointers. * 3.3 Return indicating progress made. */ + trace_hfi1_sender_make_tid_pkt(qp); + trace_hfi1_tid_write_sender_make_tid_pkt(qp, 0); wqe = rvt_get_swqe_ptr(qp, priv->s_tid_tail); req = wqe_to_tid_req(wqe); len = wqe->length; @@ -5004,6 +5081,8 @@ int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) if (!req->comp_seg || req->cur_seg == req->comp_seg) goto bail; + trace_hfi1_tid_req_make_tid_pkt(qp, 0, wqe->wr.opcode, + wqe->psn, wqe->lpsn, req); last = hfi1_build_tid_rdma_packet(wqe, ohdr, &bth1, &bth2, &len); @@ -5028,6 +5107,7 @@ int hfi1_make_tid_rdma_pkt(struct rvt_qp *qp, struct hfi1_pkt_state *ps) break; case TID_OP(RESYNC): + trace_hfi1_sender_make_tid_pkt(qp); /* Use generation from the most recently received response */ wqe = rvt_get_swqe_ptr(qp, priv->s_tid_cur); req = wqe_to_tid_req(wqe); @@ -5098,6 +5178,7 @@ static int make_tid_rdma_ack(struct rvt_qp *qp, u16 flow; struct tid_rdma_request *req, *nreq; + trace_hfi1_tid_write_rsp_make_tid_ack(qp); /* Don't send an ACK if we aren't supposed to. */ if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) goto bail; @@ -5128,6 +5209,9 @@ static int make_tid_rdma_ack(struct rvt_qp *qp, req = ack_to_tid_req(e); } + trace_hfi1_rsp_make_tid_ack(qp, e->psn); + trace_hfi1_tid_req_make_tid_ack(qp, 0, e->opcode, e->psn, e->lpsn, + req); /* * If we've sent all the ACKs that we can, we are done * until we get more segments... @@ -5199,6 +5283,9 @@ static int make_tid_rdma_ack(struct rvt_qp *qp, qpriv->s_flags & HFI1_R_TID_WAIT_INTERLCK) qpriv->s_flags &= ~HFI1_R_TID_WAIT_INTERLCK; + trace_hfi1_tid_write_rsp_make_tid_ack(qp); + trace_hfi1_tid_req_make_tid_ack(qp, 0, e->opcode, e->psn, e->lpsn, + req); hwords += hfi1_build_tid_rdma_write_ack(qp, e, ohdr, flow, &bth1, &bth2); len = 0; @@ -5251,6 +5338,7 @@ static void hfi1_do_tid_send(struct rvt_qp *qp) ps.in_thread = false; ps.timeout_int = qp->timeout_jiffies / 8; + trace_hfi1_rc_do_tid_send(qp, false); spin_lock_irqsave(&qp->s_lock, ps.flags); /* Return if we are already busy processing a work request. */ diff --git a/drivers/infiniband/hw/hfi1/trace.c b/drivers/infiniband/hw/hfi1/trace.c index 28181d711fed..9a3d236bcc88 100644 --- a/drivers/infiniband/hw/hfi1/trace.c +++ b/drivers/infiniband/hw/hfi1/trace.c @@ -133,6 +133,11 @@ const char *hfi1_trace_get_packet_l2_str(u8 l2) #define TID_RDMA_KDETH_DATA "kdeth0 0x%x: kver %u sh %u intr %u tidctrl %u tid %x offset %x kdeth1 0x%x: jkey %x" #define TID_READ_REQ_PRN "tid_flow_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x" #define TID_READ_RSP_PRN "verbs_qp 0x%x" +#define TID_WRITE_REQ_PRN "original_qp 0x%x" +#define TID_WRITE_RSP_PRN "tid_flow_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x" +#define TID_WRITE_DATA_PRN "verbs_qp 0x%x" +#define TID_ACK_PRN "tid_flow_psn 0x%x verbs_psn 0x%x tid_flow_qp 0x%x verbs_qp 0x%x" +#define TID_RESYNC_PRN "verbs_qp 0x%x" #define OP(transport, op) IB_OPCODE_## transport ## _ ## op @@ -327,6 +332,45 @@ const char *parse_everbs_hdrs( parse_syndrome(be32_to_cpu(eh->aeth) >> 24), be32_to_cpu(eh->aeth) & IB_MSN_MASK); break; + case OP(TID_RDMA, WRITE_REQ): + trace_seq_printf(p, TID_RDMA_KDETH " " RETH_PRN " " + TID_WRITE_REQ_PRN, + le32_to_cpu(eh->tid_rdma.w_req.kdeth0), + le32_to_cpu(eh->tid_rdma.w_req.kdeth1), + ib_u64_get(&eh->tid_rdma.w_req.reth.vaddr), + be32_to_cpu(eh->tid_rdma.w_req.reth.rkey), + be32_to_cpu(eh->tid_rdma.w_req.reth.length), + be32_to_cpu(eh->tid_rdma.w_req.verbs_qp)); + break; + case OP(TID_RDMA, WRITE_RESP): + trace_seq_printf(p, TID_RDMA_KDETH " " AETH_PRN " " + TID_WRITE_RSP_PRN, + le32_to_cpu(eh->tid_rdma.w_rsp.kdeth0), + le32_to_cpu(eh->tid_rdma.w_rsp.kdeth1), + be32_to_cpu(eh->tid_rdma.w_rsp.aeth) >> 24, + parse_syndrome(/* aeth */ + be32_to_cpu(eh->tid_rdma.w_rsp.aeth) + >> 24), + (be32_to_cpu(eh->tid_rdma.w_rsp.aeth) & + IB_MSN_MASK), + be32_to_cpu(eh->tid_rdma.w_rsp.tid_flow_psn), + be32_to_cpu(eh->tid_rdma.w_rsp.tid_flow_qp), + be32_to_cpu(eh->tid_rdma.w_rsp.verbs_qp)); + break; + case OP(TID_RDMA, WRITE_DATA_LAST): + case OP(TID_RDMA, WRITE_DATA): + trace_seq_printf(p, TID_RDMA_KDETH_DATA " " TID_WRITE_DATA_PRN, + le32_to_cpu(eh->tid_rdma.w_data.kdeth0), + KDETH_GET(eh->tid_rdma.w_data.kdeth0, KVER), + KDETH_GET(eh->tid_rdma.w_data.kdeth0, SH), + KDETH_GET(eh->tid_rdma.w_data.kdeth0, INTR), + KDETH_GET(eh->tid_rdma.w_data.kdeth0, TIDCTRL), + KDETH_GET(eh->tid_rdma.w_data.kdeth0, TID), + KDETH_GET(eh->tid_rdma.w_data.kdeth0, OFFSET), + le32_to_cpu(eh->tid_rdma.w_data.kdeth1), + KDETH_GET(eh->tid_rdma.w_data.kdeth1, JKEY), + be32_to_cpu(eh->tid_rdma.w_data.verbs_qp)); + break; case OP(TID_RDMA, READ_REQ): trace_seq_printf(p, TID_RDMA_KDETH " " RETH_PRN " " TID_READ_REQ_PRN, @@ -359,6 +403,28 @@ const char *parse_everbs_hdrs( IB_MSN_MASK), be32_to_cpu(eh->tid_rdma.r_rsp.verbs_qp)); break; + case OP(TID_RDMA, ACK): + trace_seq_printf(p, TID_RDMA_KDETH " " AETH_PRN " " + TID_ACK_PRN, + le32_to_cpu(eh->tid_rdma.ack.kdeth0), + le32_to_cpu(eh->tid_rdma.ack.kdeth1), + be32_to_cpu(eh->tid_rdma.ack.aeth) >> 24, + parse_syndrome(/* aeth */ + be32_to_cpu(eh->tid_rdma.ack.aeth) + >> 24), + (be32_to_cpu(eh->tid_rdma.ack.aeth) & + IB_MSN_MASK), + be32_to_cpu(eh->tid_rdma.ack.tid_flow_psn), + be32_to_cpu(eh->tid_rdma.ack.verbs_psn), + be32_to_cpu(eh->tid_rdma.ack.tid_flow_qp), + be32_to_cpu(eh->tid_rdma.ack.verbs_qp)); + break; + case OP(TID_RDMA, RESYNC): + trace_seq_printf(p, TID_RDMA_KDETH " " TID_RESYNC_PRN, + le32_to_cpu(eh->tid_rdma.resync.kdeth0), + le32_to_cpu(eh->tid_rdma.resync.kdeth1), + be32_to_cpu(eh->tid_rdma.resync.verbs_qp)); + break; /* aeth + atomicacketh */ case OP(RC, ATOMIC_ACKNOWLEDGE): trace_seq_printf(p, AETH_PRN " " ATOMICACKETH_PRN, diff --git a/drivers/infiniband/hw/hfi1/trace_ibhdrs.h b/drivers/infiniband/hw/hfi1/trace_ibhdrs.h index 1116238bf24d..d1372cc66de6 100644 --- a/drivers/infiniband/hw/hfi1/trace_ibhdrs.h +++ b/drivers/infiniband/hw/hfi1/trace_ibhdrs.h @@ -79,8 +79,14 @@ __print_symbolic(opcode, \ ib_opcode_name(RC_ATOMIC_ACKNOWLEDGE), \ ib_opcode_name(RC_COMPARE_SWAP), \ ib_opcode_name(RC_FETCH_ADD), \ + ib_opcode_name(TID_RDMA_WRITE_REQ), \ + ib_opcode_name(TID_RDMA_WRITE_RESP), \ + ib_opcode_name(TID_RDMA_WRITE_DATA), \ + ib_opcode_name(TID_RDMA_WRITE_DATA_LAST), \ ib_opcode_name(TID_RDMA_READ_REQ), \ ib_opcode_name(TID_RDMA_READ_RESP), \ + ib_opcode_name(TID_RDMA_RESYNC), \ + ib_opcode_name(TID_RDMA_ACK), \ ib_opcode_name(UC_SEND_FIRST), \ ib_opcode_name(UC_SEND_MIDDLE), \ ib_opcode_name(UC_SEND_LAST), \ diff --git a/drivers/infiniband/hw/hfi1/trace_tid.h b/drivers/infiniband/hw/hfi1/trace_tid.h index a45b5257d6c4..548dfc45a407 100644 --- a/drivers/infiniband/hw/hfi1/trace_tid.h +++ b/drivers/infiniband/hw/hfi1/trace_tid.h @@ -56,16 +56,33 @@ u16 hfi1_trace_get_tid_idx(u32 ent); "fpsn 0x%x flow_flags 0x%x" #define TID_REQ_PRN "[%s] qpn 0x%x newreq %u opcode 0x%x psn 0x%x lpsn 0x%x " \ - "cur_seg %u comp_seg %u ack_seg %u " \ + "cur_seg %u comp_seg %u ack_seg %u alloc_seg %u " \ "total_segs %u setup_head %u clear_tail %u flow_idx %u " \ - "state %u r_flow_psn 0x%x " \ - "s_next_psn 0x%x" + "acked_tail %u state %u r_ack_psn 0x%x r_flow_psn 0x%x " \ + "r_last_ackd 0x%x s_next_psn 0x%x" #define RCV_ERR_PRN "[%s] qpn 0x%x s_flags 0x%x state 0x%x " \ "s_acked_ack_queue %u s_tail_ack_queue %u " \ "r_head_ack_queue %u opcode 0x%x psn 0x%x r_psn 0x%x " \ " diff %d" +#define TID_WRITE_RSPDR_PRN "[%s] qpn 0x%x r_tid_head %u r_tid_tail %u " \ + "r_tid_ack %u r_tid_alloc %u alloc_w_segs %u " \ + "pending_tid_w_segs %u sync_pt %s " \ + "ps_nak_psn 0x%x ps_nak_state 0x%x " \ + "prnr_nak_state 0x%x hw_flow_index %u generation "\ + "0x%x fpsn 0x%x flow_flags 0x%x resync %s" \ + "r_next_psn_kdeth 0x%x" + +#define TID_WRITE_SENDER_PRN "[%s] qpn 0x%x newreq %u s_tid_cur %u " \ + "s_tid_tail %u s_tid_head %u " \ + "pending_tid_w_resp %u n_requests %u " \ + "n_tid_requests %u s_flags 0x%x ps_flags 0x%x "\ + "iow_flags 0x%lx s_state 0x%x s_retry %u" + +#define KDETH_EFLAGS_ERR_PRN "[%s] qpn 0x%x TID ERR: RcvType 0x%x " \ + "RcvTypeError 0x%x PSN 0x%x" + DECLARE_EVENT_CLASS(/* class */ hfi1_exp_tid_reg_unreg, TP_PROTO(unsigned int ctxt, u16 subctxt, u32 rarr, u32 npages, @@ -382,6 +399,18 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, msg, more) ); +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_tid_timeout, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + +DEFINE_EVENT(/* event */ + hfi1_msg_template, hfi1_msg_tid_retry_timeout, + TP_PROTO(struct rvt_qp *qp, const char *msg, u64 more), + TP_ARGS(qp, msg, more) +); + DECLARE_EVENT_CLASS(/* tid_flow_page */ hfi1_tid_flow_page_template, TP_PROTO(struct rvt_qp *qp, struct tid_rdma_flow *flow, u32 index, @@ -562,6 +591,42 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, index, flow) ); +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_build_write_resp, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_rcv_write_resp, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_build_write_data, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_rcv_tid_ack, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_rcv_resync, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_flow_template, hfi1_tid_flow_handle_kdeth_eflags, + TP_PROTO(struct rvt_qp *qp, int index, struct tid_rdma_flow *flow), + TP_ARGS(qp, index, flow) +); + DECLARE_EVENT_CLASS(/* tid_node */ hfi1_tid_node_template, TP_PROTO(struct rvt_qp *qp, const char *msg, u32 index, u32 base, @@ -656,6 +721,18 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, index, ent) ); +DEFINE_EVENT(/* event */ + hfi1_tid_entry_template, hfi1_tid_entry_rcv_write_resp, + TP_PROTO(struct rvt_qp *qp, int index, u32 entry), + TP_ARGS(qp, index, entry) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_entry_template, hfi1_tid_entry_build_write_data, + TP_PROTO(struct rvt_qp *qp, int index, u32 entry), + TP_ARGS(qp, index, entry) +); + DECLARE_EVENT_CLASS(/* rsp_info */ hfi1_responder_info_template, TP_PROTO(struct rvt_qp *qp, u32 psn), @@ -738,6 +815,42 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, psn) ); +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_tid_write_alloc_res, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_rcv_tid_write_req, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_build_tid_write_resp, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_rcv_tid_write_data, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_make_tid_ack, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + +DEFINE_EVENT(/* event */ + hfi1_responder_info_template, hfi1_rsp_handle_kdeth_eflags, + TP_PROTO(struct rvt_qp *qp, u32 psn), + TP_ARGS(qp, psn) +); + DECLARE_EVENT_CLASS(/* sender_info */ hfi1_sender_info_template, TP_PROTO(struct rvt_qp *qp), @@ -830,6 +943,18 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp) ); +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_rcv_tid_ack, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_sender_info_template, hfi1_sender_make_tid_pkt, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + DECLARE_EVENT_CLASS(/* tid_read_sender */ hfi1_tid_read_sender_template, TP_PROTO(struct rvt_qp *qp, char newreq), @@ -908,12 +1033,16 @@ DECLARE_EVENT_CLASS(/* tid_rdma_request */ __field(u32, cur_seg) __field(u32, comp_seg) __field(u32, ack_seg) + __field(u32, alloc_seg) __field(u32, total_segs) __field(u16, setup_head) __field(u16, clear_tail) __field(u16, flow_idx) + __field(u16, acked_tail) __field(u32, state) + __field(u32, r_ack_psn) __field(u32, r_flow_psn) + __field(u32, r_last_acked) __field(u32, s_next_psn) ), TP_fast_assign(/* assign */ @@ -926,12 +1055,16 @@ DECLARE_EVENT_CLASS(/* tid_rdma_request */ __entry->cur_seg = req->cur_seg; __entry->comp_seg = req->comp_seg; __entry->ack_seg = req->ack_seg; + __entry->alloc_seg = req->alloc_seg; __entry->total_segs = req->total_segs; __entry->setup_head = req->setup_head; __entry->clear_tail = req->clear_tail; __entry->flow_idx = req->flow_idx; + __entry->acked_tail = req->acked_tail; __entry->state = req->state; + __entry->r_ack_psn = req->r_ack_psn; __entry->r_flow_psn = req->r_flow_psn; + __entry->r_last_acked = req->r_last_acked; __entry->s_next_psn = req->s_next_psn; ), TP_printk(/* print */ @@ -945,12 +1078,16 @@ DECLARE_EVENT_CLASS(/* tid_rdma_request */ __entry->cur_seg, __entry->comp_seg, __entry->ack_seg, + __entry->alloc_seg, __entry->total_segs, __entry->setup_head, __entry->clear_tail, __entry->flow_idx, + __entry->acked_tail, __entry->state, + __entry->r_ack_psn, __entry->r_flow_psn, + __entry->r_last_acked, __entry->s_next_psn ) ); @@ -1004,6 +1141,97 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, newreq, opcode, psn, lpsn, req) ); +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_write_alloc_res, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_write_req, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_build_write_resp, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_write_resp, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_write_data, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_tid_ack, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_tid_retry_timeout, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_rcv_resync, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_make_tid_pkt, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_make_tid_ack, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_handle_kdeth_eflags, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_make_rc_ack_write, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_rdma_request_template, hfi1_tid_req_make_req_write, + TP_PROTO(struct rvt_qp *qp, char newreq, u8 opcode, u32 psn, u32 lpsn, + struct tid_rdma_request *req), + TP_ARGS(qp, newreq, opcode, psn, lpsn, req) +); + DECLARE_EVENT_CLASS(/* rc_rcv_err */ hfi1_rc_rcv_err_template, TP_PROTO(struct rvt_qp *qp, u32 opcode, u32 psn, int diff), @@ -1090,6 +1318,289 @@ DEFINE_EVENT(/* event */ TP_ARGS(qp, index, sge) ); +DECLARE_EVENT_CLASS(/* tid_write_sp */ + hfi1_tid_write_rsp_template, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u32, r_tid_head) + __field(u32, r_tid_tail) + __field(u32, r_tid_ack) + __field(u32, r_tid_alloc) + __field(u32, alloc_w_segs) + __field(u32, pending_tid_w_segs) + __field(bool, sync_pt) + __field(u32, ps_nak_psn) + __field(u8, ps_nak_state) + __field(u8, prnr_nak_state) + __field(u32, hw_flow_index) + __field(u32, generation) + __field(u32, fpsn) + __field(u32, flow_flags) + __field(bool, resync) + __field(u32, r_next_psn_kdeth) + ), + TP_fast_assign(/* assign */ + struct hfi1_qp_priv *priv = qp->priv; + + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->r_tid_head = priv->r_tid_head; + __entry->r_tid_tail = priv->r_tid_tail; + __entry->r_tid_ack = priv->r_tid_ack; + __entry->r_tid_alloc = priv->r_tid_alloc; + __entry->alloc_w_segs = priv->alloc_w_segs; + __entry->pending_tid_w_segs = priv->pending_tid_w_segs; + __entry->sync_pt = priv->sync_pt; + __entry->ps_nak_psn = priv->s_nak_psn; + __entry->ps_nak_state = priv->s_nak_state; + __entry->prnr_nak_state = priv->rnr_nak_state; + __entry->hw_flow_index = priv->flow_state.index; + __entry->generation = priv->flow_state.generation; + __entry->fpsn = priv->flow_state.psn; + __entry->flow_flags = priv->flow_state.flags; + __entry->resync = priv->resync; + __entry->r_next_psn_kdeth = priv->r_next_psn_kdeth; + ), + TP_printk(/* print */ + TID_WRITE_RSPDR_PRN, + __get_str(dev), + __entry->qpn, + __entry->r_tid_head, + __entry->r_tid_tail, + __entry->r_tid_ack, + __entry->r_tid_alloc, + __entry->alloc_w_segs, + __entry->pending_tid_w_segs, + __entry->sync_pt ? "yes" : "no", + __entry->ps_nak_psn, + __entry->ps_nak_state, + __entry->prnr_nak_state, + __entry->hw_flow_index, + __entry->generation, + __entry->fpsn, + __entry->flow_flags, + __entry->resync ? "yes" : "no", + __entry->r_next_psn_kdeth + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_alloc_res, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_rcv_req, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_build_resp, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_rcv_data, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_rcv_resync, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_make_tid_ack, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_handle_kdeth_eflags, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_rsp_template, hfi1_tid_write_rsp_make_rc_ack, + TP_PROTO(struct rvt_qp *qp), + TP_ARGS(qp) +); + +DECLARE_EVENT_CLASS(/* tid_write_sender */ + hfi1_tid_write_sender_template, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(char, newreq) + __field(u32, s_tid_cur) + __field(u32, s_tid_tail) + __field(u32, s_tid_head) + __field(u32, pending_tid_w_resp) + __field(u32, n_requests) + __field(u32, n_tid_requests) + __field(u32, s_flags) + __field(u32, ps_flags) + __field(unsigned long, iow_flags) + __field(u8, s_state) + __field(u8, s_retry) + ), + TP_fast_assign(/* assign */ + struct hfi1_qp_priv *priv = qp->priv; + + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->newreq = newreq; + __entry->s_tid_cur = priv->s_tid_cur; + __entry->s_tid_tail = priv->s_tid_tail; + __entry->s_tid_head = priv->s_tid_head; + __entry->pending_tid_w_resp = priv->pending_tid_w_resp; + __entry->n_requests = atomic_read(&priv->n_requests); + __entry->n_tid_requests = atomic_read(&priv->n_tid_requests); + __entry->s_flags = qp->s_flags; + __entry->ps_flags = priv->s_flags; + __entry->iow_flags = priv->s_iowait.flags; + __entry->s_state = priv->s_state; + __entry->s_retry = priv->s_retry; + ), + TP_printk(/* print */ + TID_WRITE_SENDER_PRN, + __get_str(dev), + __entry->qpn, + __entry->newreq, + __entry->s_tid_cur, + __entry->s_tid_tail, + __entry->s_tid_head, + __entry->pending_tid_w_resp, + __entry->n_requests, + __entry->n_tid_requests, + __entry->s_flags, + __entry->ps_flags, + __entry->iow_flags, + __entry->s_state, + __entry->s_retry + ) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_sender_template, hfi1_tid_write_sender_rcv_resp, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_sender_template, hfi1_tid_write_sender_rcv_tid_ack, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_sender_template, hfi1_tid_write_sender_retry_timeout, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_sender_template, hfi1_tid_write_sender_make_tid_pkt, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_sender_template, hfi1_tid_write_sender_make_req, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DEFINE_EVENT(/* event */ + hfi1_tid_write_sender_template, hfi1_tid_write_sender_restart_rc, + TP_PROTO(struct rvt_qp *qp, char newreq), + TP_ARGS(qp, newreq) +); + +DECLARE_EVENT_CLASS(/* tid_ack */ + hfi1_tid_ack_template, + TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn, + u32 req_psn, u32 resync_psn), + TP_ARGS(qp, aeth, psn, req_psn, resync_psn), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u32, aeth) + __field(u32, psn) + __field(u32, req_psn) + __field(u32, resync_psn) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)) + __entry->qpn = qp->ibqp.qp_num; + __entry->aeth = aeth; + __entry->psn = psn; + __entry->req_psn = req_psn; + __entry->resync_psn = resync_psn; + ), + TP_printk(/* print */ + "[%s] qpn 0x%x aeth 0x%x psn 0x%x req_psn 0x%x resync_psn 0x%x", + __get_str(dev), + __entry->qpn, + __entry->aeth, + __entry->psn, + __entry->req_psn, + __entry->resync_psn + ) +); + +DEFINE_EVENT(/* rcv_tid_ack */ + hfi1_tid_ack_template, hfi1_rcv_tid_ack, + TP_PROTO(struct rvt_qp *qp, u32 aeth, u32 psn, + u32 req_psn, u32 resync_psn), + TP_ARGS(qp, aeth, psn, req_psn, resync_psn) +); + +DECLARE_EVENT_CLASS(/* kdeth_eflags_error */ + hfi1_kdeth_eflags_error_template, + TP_PROTO(struct rvt_qp *qp, u8 rcv_type, u8 rte, u32 psn), + TP_ARGS(qp, rcv_type, rte, psn), + TP_STRUCT__entry(/* entry */ + DD_DEV_ENTRY(dd_from_ibdev(qp->ibqp.device)) + __field(u32, qpn) + __field(u8, rcv_type) + __field(u8, rte) + __field(u32, psn) + ), + TP_fast_assign(/* assign */ + DD_DEV_ASSIGN(dd_from_ibdev(qp->ibqp.device)); + __entry->qpn = qp->ibqp.qp_num; + __entry->rcv_type = rcv_type; + __entry->rte = rte; + __entry->psn = psn; + ), + TP_printk(/* print */ + KDETH_EFLAGS_ERR_PRN, + __get_str(dev), + __entry->qpn, + __entry->rcv_type, + __entry->rte, + __entry->psn + ) +); + +DEFINE_EVENT(/* event */ + hfi1_kdeth_eflags_error_template, hfi1_eflags_err_write, + TP_PROTO(struct rvt_qp *qp, u8 rcv_type, u8 rte, u32 psn), + TP_ARGS(qp, rcv_type, rte, psn) +); + #endif /* __HFI1_TRACE_TID_H */ #undef TRACE_INCLUDE_PATH diff --git a/drivers/infiniband/hw/hfi1/trace_tx.h b/drivers/infiniband/hw/hfi1/trace_tx.h index 37dbb3e599c3..09eb0c9ada00 100644 --- a/drivers/infiniband/hw/hfi1/trace_tx.h +++ b/drivers/infiniband/hw/hfi1/trace_tx.h @@ -846,6 +846,12 @@ DEFINE_EVENT( TP_ARGS(qp, flag) ); +DEFINE_EVENT(/* event */ + hfi1_do_send_template, hfi1_rc_do_tid_send, + TP_PROTO(struct rvt_qp *qp, bool flag), + TP_ARGS(qp, flag) +); + DEFINE_EVENT( hfi1_do_send_template, hfi1_rc_expired_time_slice, TP_PROTO(struct rvt_qp *qp, bool flag), From 34025fb0c4c9d6b2e294f8f8f0a82491a13c83a2 Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Wed, 23 Jan 2019 21:52:19 -0800 Subject: [PATCH 1171/4277] IB/hfi1: Prioritize the sending of ACK packets ACK packets are generally associated with request completion and resource release and therefore should be sent first. This patch optimizes the send engine by using the following policies: (1) QPs with RVT_S_ACK_PENDING bit set in qp->s_flags or qpriv->s_flags should have their priority incremented; (2) QPs with ACK or TID-ACK packet queued should have their priority incremented; (3) When a QP is queued to the wait list due to resource constraints, it will be queued to the head if it has ACK packet to send; (4) When selecting qps to run from the wait list, the one with the highest priority and starve_cnt will be selected; each priority will be equivalent to a fixed number of starve_cnt (16). Reviewed-by: Mitko Haralanov Signed-off-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/iowait.c | 34 ++++++++- drivers/infiniband/hw/hfi1/iowait.h | 87 ++++++++++++++++-------- drivers/infiniband/hw/hfi1/pio.c | 18 +++-- drivers/infiniband/hw/hfi1/qp.c | 15 +++- drivers/infiniband/hw/hfi1/rc.c | 1 + drivers/infiniband/hw/hfi1/sdma.c | 24 ++++--- drivers/infiniband/hw/hfi1/sdma_txreq.h | 1 + drivers/infiniband/hw/hfi1/tid_rdma.c | 1 + drivers/infiniband/hw/hfi1/user_sdma.c | 6 +- drivers/infiniband/hw/hfi1/verbs.c | 1 + drivers/infiniband/hw/hfi1/verbs_txreq.h | 1 + drivers/infiniband/hw/hfi1/vnic_sdma.c | 6 +- 12 files changed, 144 insertions(+), 51 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/iowait.c b/drivers/infiniband/hw/hfi1/iowait.c index 582f1ba136ff..adb4a1ba921b 100644 --- a/drivers/infiniband/hw/hfi1/iowait.c +++ b/drivers/infiniband/hw/hfi1/iowait.c @@ -6,6 +6,9 @@ #include "iowait.h" #include "trace_iowait.h" +/* 1 priority == 16 starve_cnt */ +#define IOWAIT_PRIORITY_STARVE_SHIFT 4 + void iowait_set_flag(struct iowait *wait, u32 flag) { trace_hfi1_iowait_set(wait, flag); @@ -44,7 +47,8 @@ void iowait_init(struct iowait *wait, u32 tx_limit, uint seq, bool pkts_sent), void (*wakeup)(struct iowait *wait, int reason), - void (*sdma_drained)(struct iowait *wait)) + void (*sdma_drained)(struct iowait *wait), + void (*init_priority)(struct iowait *wait)) { int i; @@ -58,6 +62,7 @@ void iowait_init(struct iowait *wait, u32 tx_limit, wait->sleep = sleep; wait->wakeup = wakeup; wait->sdma_drained = sdma_drained; + wait->init_priority = init_priority; wait->flags = 0; for (i = 0; i < IOWAIT_SES; i++) { wait->wait[i].iow = wait; @@ -92,3 +97,30 @@ int iowait_set_work_flag(struct iowait_work *w) iowait_set_flag(w->iow, IOWAIT_PENDING_TID); return IOWAIT_TID_SE; } + +/** + * iowait_priority_update_top - update the top priority entry + * @w: the iowait struct + * @top: a pointer to the top priority entry + * @idx: the index of the current iowait in an array + * @top_idx: the array index for the iowait entry that has the top priority + * + * This function is called to compare the priority of a given + * iowait with the given top priority entry. The top index will + * be returned. + */ +uint iowait_priority_update_top(struct iowait *w, + struct iowait *top, + uint idx, uint top_idx) +{ + u8 cnt, tcnt; + + /* Convert priority into starve_cnt and compare the total.*/ + cnt = (w->priority << IOWAIT_PRIORITY_STARVE_SHIFT) + w->starved_cnt; + tcnt = (top->priority << IOWAIT_PRIORITY_STARVE_SHIFT) + + top->starved_cnt; + if (cnt > tcnt) + return idx; + else + return top_idx; +} diff --git a/drivers/infiniband/hw/hfi1/iowait.h b/drivers/infiniband/hw/hfi1/iowait.h index bd913701761d..07847cb72169 100644 --- a/drivers/infiniband/hw/hfi1/iowait.h +++ b/drivers/infiniband/hw/hfi1/iowait.h @@ -100,6 +100,7 @@ struct iowait_work { * @sleep: no space callback * @wakeup: space callback wakeup * @sdma_drained: sdma count drained + * @init_priority: callback to manipulate priority * @lock: lock protected head of wait queue * @iowork: workqueue overhead * @wait_dma: wait for sdma_busy == 0 @@ -109,7 +110,7 @@ struct iowait_work { * @tx_limit: limit for overflow queuing * @tx_count: number of tx entry's in tx_head'ed list * @flags: wait flags (one per QP) - * @wait: SE array + * @wait: SE array for multiple legs * * This is to be embedded in user's state structure * (QP or PQ). @@ -120,10 +121,13 @@ struct iowait_work { * are callbacks for the ULP to implement * what ever queuing/dequeuing of * the embedded iowait and its containing struct - * when a resource shortage like SDMA ring space is seen. + * when a resource shortage like SDMA ring space + * or PIO credit space is seen. * * Both potentially have locks help - * so sleeping is not allowed. + * so sleeping is not allowed and it is not + * supported to submit txreqs from the wakeup + * call directly because of lock conflicts. * * The wait_dma member along with the iow * @@ -143,6 +147,7 @@ struct iowait { ); void (*wakeup)(struct iowait *wait, int reason); void (*sdma_drained)(struct iowait *wait); + void (*init_priority)(struct iowait *wait); seqlock_t *lock; wait_queue_head_t wait_dma; wait_queue_head_t wait_pio; @@ -152,6 +157,7 @@ struct iowait { u32 tx_limit; u32 tx_count; u8 starved_cnt; + u8 priority; unsigned long flags; struct iowait_work wait[IOWAIT_SES]; }; @@ -171,7 +177,8 @@ void iowait_init(struct iowait *wait, u32 tx_limit, uint seq, bool pkts_sent), void (*wakeup)(struct iowait *wait, int reason), - void (*sdma_drained)(struct iowait *wait)); + void (*sdma_drained)(struct iowait *wait), + void (*init_priority)(struct iowait *wait)); /** * iowait_schedule() - schedule the default send engine work @@ -339,6 +346,8 @@ static inline u16 iowait_get_desc(struct iowait_work *w) tx = list_first_entry(&w->tx_head, struct sdma_txreq, list); num_desc = tx->num_desc; + if (tx->flags & SDMA_TXREQ_F_VIP) + w->iow->priority++; } return num_desc; } @@ -352,6 +361,37 @@ static inline u32 iowait_get_all_desc(struct iowait *w) return num_desc; } +static inline void iowait_update_priority(struct iowait_work *w) +{ + struct sdma_txreq *tx = NULL; + + if (!list_empty(&w->tx_head)) { + tx = list_first_entry(&w->tx_head, struct sdma_txreq, + list); + if (tx->flags & SDMA_TXREQ_F_VIP) + w->iow->priority++; + } +} + +static inline void iowait_update_all_priority(struct iowait *w) +{ + iowait_update_priority(&w->wait[IOWAIT_IB_SE]); + iowait_update_priority(&w->wait[IOWAIT_TID_SE]); +} + +static inline void iowait_init_priority(struct iowait *w) +{ + w->priority = 0; + if (w->init_priority) + w->init_priority(w); +} + +static inline void iowait_get_priority(struct iowait *w) +{ + iowait_init_priority(w); + iowait_update_all_priority(w); +} + /** * iowait_queue - Put the iowait on a wait queue * @pkts_sent: have some packets been sent before queuing? @@ -368,14 +408,18 @@ static inline void iowait_queue(bool pkts_sent, struct iowait *w, /* * To play fair, insert the iowait at the tail of the wait queue if it * has already sent some packets; Otherwise, put it at the head. + * However, if it has priority packets to send, also put it at the + * head. */ - if (pkts_sent) { - list_add_tail(&w->list, wait_head); + if (pkts_sent) w->starved_cnt = 0; - } else { - list_add(&w->list, wait_head); + else w->starved_cnt++; - } + + if (w->priority > 0 || !pkts_sent) + list_add(&w->list, wait_head); + else + list_add_tail(&w->list, wait_head); } /** @@ -392,27 +436,10 @@ static inline void iowait_starve_clear(bool pkts_sent, struct iowait *w) w->starved_cnt = 0; } -/** - * iowait_starve_find_max - Find the maximum of the starve count - * @w: the iowait struct - * @max: a variable containing the max starve count - * @idx: the index of the current iowait in an array - * @max_idx: a variable containing the array index for the - * iowait entry that has the max starve count - * - * This function is called to compare the starve count of a - * given iowait with the given max starve count. The max starve - * count and the index will be updated if the iowait's start - * count is larger. - */ -static inline void iowait_starve_find_max(struct iowait *w, u8 *max, - uint idx, uint *max_idx) -{ - if (w->starved_cnt > *max) { - *max = w->starved_cnt; - *max_idx = idx; - } -} +/* Update the top priority index */ +uint iowait_priority_update_top(struct iowait *w, + struct iowait *top, + uint idx, uint top_idx); /** * iowait_packet_queued() - determine if a packet is queued diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c index 04126d7e318d..a1de566fe95e 100644 --- a/drivers/infiniband/hw/hfi1/pio.c +++ b/drivers/infiniband/hw/hfi1/pio.c @@ -1599,8 +1599,7 @@ static void sc_piobufavail(struct send_context *sc) struct rvt_qp *qp; struct hfi1_qp_priv *priv; unsigned long flags; - uint i, n = 0, max_idx = 0; - u8 max_starved_cnt = 0; + uint i, n = 0, top_idx = 0; if (dd->send_contexts[sc->sw_index].type != SC_KERNEL && dd->send_contexts[sc->sw_index].type != SC_VL15) @@ -1619,11 +1618,18 @@ static void sc_piobufavail(struct send_context *sc) if (n == ARRAY_SIZE(qps)) break; wait = list_first_entry(list, struct iowait, list); + iowait_get_priority(wait); qp = iowait_to_qp(wait); priv = qp->priv; list_del_init(&priv->s_iowait.list); priv->s_iowait.lock = NULL; - iowait_starve_find_max(wait, &max_starved_cnt, n, &max_idx); + if (n) { + priv = qps[top_idx]->priv; + top_idx = iowait_priority_update_top(wait, + &priv->s_iowait, + n, top_idx); + } + /* refcount held until actual wake up */ qps[n++] = qp; } @@ -1638,12 +1644,12 @@ static void sc_piobufavail(struct send_context *sc) } write_sequnlock_irqrestore(&sc->waitlock, flags); - /* Wake up the most starved one first */ + /* Wake up the top-priority one first */ if (n) - hfi1_qp_wakeup(qps[max_idx], + hfi1_qp_wakeup(qps[top_idx], RVT_S_WAIT_PIO | HFI1_S_WAIT_PIO_DRAIN); for (i = 0; i < n; i++) - if (i != max_idx) + if (i != top_idx) hfi1_qp_wakeup(qps[i], RVT_S_WAIT_PIO | HFI1_S_WAIT_PIO_DRAIN); } diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index cfd598e4b303..d8f7add935df 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -518,6 +518,7 @@ static int iowait_sleep( ibp->rvp.n_dmawait++; qp->s_flags |= RVT_S_WAIT_DMA_DESC; + iowait_get_priority(&priv->s_iowait); iowait_queue(pkts_sent, &priv->s_iowait, &sde->dmawait); priv->s_iowait.lock = &sde->waitlock; @@ -567,6 +568,17 @@ static void iowait_sdma_drained(struct iowait *wait) spin_unlock_irqrestore(&qp->s_lock, flags); } +static void hfi1_init_priority(struct iowait *w) +{ + struct rvt_qp *qp = iowait_to_qp(w); + struct hfi1_qp_priv *priv = qp->priv; + + if (qp->s_flags & RVT_S_ACK_PENDING) + w->priority++; + if (priv->s_flags & RVT_S_ACK_PENDING) + w->priority++; +} + /** * qp_to_sdma_engine - map a qp to a send engine * @qp: the QP @@ -727,7 +739,8 @@ void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp) _hfi1_do_tid_send, iowait_sleep, iowait_wakeup, - iowait_sdma_drained); + iowait_sdma_drained, + hfi1_init_priority); return priv; } diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 82afa7736be7..e6726c1ab866 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -390,6 +390,7 @@ normal_no_state: bth0 = OP(ACKNOWLEDGE) << 24; bth2 = mask_psn(qp->s_ack_psn); qp->s_flags &= ~RVT_S_ACK_PENDING; + ps->s_txreq->txreq.flags |= SDMA_TXREQ_F_VIP; ps->s_txreq->ss = NULL; } qp->s_rdma_ack_cnt++; diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index 96897a91fb0a..b0110728f541 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -1747,10 +1747,9 @@ retry: */ static void sdma_desc_avail(struct sdma_engine *sde, uint avail) { - struct iowait *wait, *nw; + struct iowait *wait, *nw, *twait; struct iowait *waits[SDMA_WAIT_BATCH_SIZE]; - uint i, n = 0, seq, max_idx = 0; - u8 max_starved_cnt = 0; + uint i, n = 0, seq, tidx = 0; #ifdef CONFIG_SDMA_VERBOSITY dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", sde->this_idx, @@ -1775,13 +1774,20 @@ static void sdma_desc_avail(struct sdma_engine *sde, uint avail) continue; if (n == ARRAY_SIZE(waits)) break; + iowait_init_priority(wait); num_desc = iowait_get_all_desc(wait); if (num_desc > avail) break; avail -= num_desc; - /* Find the most starved wait memeber */ - iowait_starve_find_max(wait, &max_starved_cnt, - n, &max_idx); + /* Find the top-priority wait memeber */ + if (n) { + twait = waits[tidx]; + tidx = + iowait_priority_update_top(wait, + twait, + n, + tidx); + } list_del_init(&wait->list); waits[n++] = wait; } @@ -1790,12 +1796,12 @@ static void sdma_desc_avail(struct sdma_engine *sde, uint avail) } } while (read_seqretry(&sde->waitlock, seq)); - /* Schedule the most starved one first */ + /* Schedule the top-priority entry first */ if (n) - waits[max_idx]->wakeup(waits[max_idx], SDMA_AVAIL_REASON); + waits[tidx]->wakeup(waits[tidx], SDMA_AVAIL_REASON); for (i = 0; i < n; i++) - if (i != max_idx) + if (i != tidx) waits[i]->wakeup(waits[i], SDMA_AVAIL_REASON); } diff --git a/drivers/infiniband/hw/hfi1/sdma_txreq.h b/drivers/infiniband/hw/hfi1/sdma_txreq.h index bf7d777d756e..514a4784566b 100644 --- a/drivers/infiniband/hw/hfi1/sdma_txreq.h +++ b/drivers/infiniband/hw/hfi1/sdma_txreq.h @@ -91,6 +91,7 @@ struct sdma_desc { #define SDMA_TXREQ_F_URGENT 0x0001 #define SDMA_TXREQ_F_AHG_COPY 0x0002 #define SDMA_TXREQ_F_USE_AHG 0x0004 +#define SDMA_TXREQ_F_VIP 0x0010 struct sdma_txreq; typedef void (*callback_t)(struct sdma_txreq *, int); diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index a49eb3d9b5b9..bc2ff83026f7 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -5296,6 +5296,7 @@ static int make_tid_rdma_ack(struct rvt_qp *qp, ps->s_txreq->ss = NULL; hfi1_make_ruc_header(qp, ohdr, (TID_OP(ACK) << 24), bth1, bth2, middle, ps); + ps->s_txreq->txreq.flags |= SDMA_TXREQ_F_VIP; return 1; bail: /* diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index 6764114b886c..8bfbc6d7ea34 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -144,8 +144,10 @@ static int defer_packet_queue( */ xchg(&pq->state, SDMA_PKT_Q_DEFERRED); write_seqlock(&sde->waitlock); - if (list_empty(&pq->busy.list)) + if (list_empty(&pq->busy.list)) { + iowait_get_priority(&pq->busy); iowait_queue(pkts_sent, &pq->busy, &sde->dmawait); + } write_sequnlock(&sde->waitlock); return -EBUSY; eagain: @@ -191,7 +193,7 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, pq->mm = fd->mm; iowait_init(&pq->busy, 0, NULL, NULL, defer_packet_queue, - activate_packet_queue, NULL); + activate_packet_queue, NULL, NULL); pq->reqidx = 0; pq->reqs = kcalloc(hfi1_sdma_comp_ring_size, diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index ab97d71cdd92..55a56b3d7f83 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -945,6 +945,7 @@ static int pio_wait(struct rvt_qp *qp, dev->n_piodrain += !!(flag & HFI1_S_WAIT_PIO_DRAIN); qp->s_flags |= flag; was_empty = list_empty(&sc->piowait); + iowait_get_priority(&priv->s_iowait); iowait_queue(ps->pkts_sent, &priv->s_iowait, &sc->piowait); priv->s_iowait.lock = &sc->waitlock; diff --git a/drivers/infiniband/hw/hfi1/verbs_txreq.h b/drivers/infiniband/hw/hfi1/verbs_txreq.h index 2a77af26a231..b002e96eb335 100644 --- a/drivers/infiniband/hw/hfi1/verbs_txreq.h +++ b/drivers/infiniband/hw/hfi1/verbs_txreq.h @@ -94,6 +94,7 @@ static inline struct verbs_txreq *get_txreq(struct hfi1_ibdev *dev, tx->txreq.num_desc = 0; /* Set the header type */ tx->phdr.hdr.hdr_type = priv->hdr_type; + tx->txreq.flags = 0; return tx; } diff --git a/drivers/infiniband/hw/hfi1/vnic_sdma.c b/drivers/infiniband/hw/hfi1/vnic_sdma.c index 1f81c480e028..af1b1ffcb38e 100644 --- a/drivers/infiniband/hw/hfi1/vnic_sdma.c +++ b/drivers/infiniband/hw/hfi1/vnic_sdma.c @@ -240,8 +240,10 @@ static int hfi1_vnic_sdma_sleep(struct sdma_engine *sde, } vnic_sdma->state = HFI1_VNIC_SDMA_Q_DEFERRED; - if (list_empty(&vnic_sdma->wait.list)) + if (list_empty(&vnic_sdma->wait.list)) { + iowait_get_priority(wait->iow); iowait_queue(pkts_sent, wait->iow, &sde->dmawait); + } write_sequnlock(&sde->waitlock); return -EBUSY; } @@ -281,7 +283,7 @@ void hfi1_vnic_sdma_init(struct hfi1_vnic_vport_info *vinfo) iowait_init(&vnic_sdma->wait, 0, NULL, NULL, hfi1_vnic_sdma_sleep, - hfi1_vnic_sdma_wakeup, NULL); + hfi1_vnic_sdma_wakeup, NULL, NULL); vnic_sdma->sde = &vinfo->dd->per_sdma[i]; vnic_sdma->dd = vinfo->dd; vnic_sdma->vinfo = vinfo; From b8bdf0925902b07e45d4d9c8796f7c531cfe9320 Mon Sep 17 00:00:00 2001 From: Pramod Kumar Date: Thu, 3 Jan 2019 14:25:32 +0530 Subject: [PATCH 1172/4277] dt-bindings: thermal: Add binding document for SR thermal Add binding document for supported thermal implementation in Stingray. Reviewed-by: Ray Jui Reviewed-by: Scott Branden Reviewed-by: Rob Herring Signed-off-by: Pramod Kumar Signed-off-by: Srinath Mannam Signed-off-by: Eduardo Valentin --- .../bindings/thermal/brcm,sr-thermal.txt | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt diff --git a/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt b/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt new file mode 100644 index 000000000000..3ab330219d45 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt @@ -0,0 +1,105 @@ +* Broadcom Stingray Thermal + +This binding describes thermal sensors that is part of Stingray SoCs. + +Required properties: +- compatible : Must be "brcm,sr-thermal" +- reg : Memory where tmon data will be available. +- brcm,tmon-mask: A one cell bit mask of valid TMON sources. + Each bit represents single TMON source. +- #thermal-sensor-cells : Thermal sensor phandler +- polling-delay: Max number of milliseconds to wait between polls. +- thermal-sensors: A list of thermal sensor phandles and specifier. + specifier value is tmon ID and it should be + in correspond with brcm,tmon-mask. +- temperature: trip temperature threshold in millicelsius. + +Example: + tmons { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x8f100000 0x100>; + + tmon: tmon@0 { + compatible = "brcm,sr-thermal"; + reg = <0x0 0x40>; + brcm,tmon-mask = <0x3f>; + #thermal-sensor-cells = <1>; + }; + }; + + thermal-zones { + ihost0_thermal: ihost0-thermal { + polling-delay-passive = <0>; + polling-delay = <1000>; + thermal-sensors = <&tmon 0>; + trips { + cpu-crit { + temperature = <105000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + ihost1_thermal: ihost1-thermal { + polling-delay-passive = <0>; + polling-delay = <1000>; + thermal-sensors = <&tmon 1>; + trips { + cpu-crit { + temperature = <105000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + ihost2_thermal: ihost2-thermal { + polling-delay-passive = <0>; + polling-delay = <1000>; + thermal-sensors = <&tmon 2>; + trips { + cpu-crit { + temperature = <105000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + ihost3_thermal: ihost3-thermal { + polling-delay-passive = <0>; + polling-delay = <1000>; + thermal-sensors = <&tmon 3>; + trips { + cpu-crit { + temperature = <105000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + crmu_thermal: crmu-thermal { + polling-delay-passive = <0>; + polling-delay = <1000>; + thermal-sensors = <&tmon 4>; + trips { + cpu-crit { + temperature = <105000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + nitro_thermal: nitro-thermal { + polling-delay-passive = <0>; + polling-delay = <1000>; + thermal-sensors = <&tmon 5>; + trips { + cpu-crit { + temperature = <105000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; From 250e211057c7237dc75634b1372a1a3bd58dcd96 Mon Sep 17 00:00:00 2001 From: Pramod Kumar Date: Thu, 3 Jan 2019 14:25:33 +0530 Subject: [PATCH 1173/4277] thermal: broadcom: Add Stingray thermal driver Stingray SoC has six temperature sensor and those are configured, controlled and accessed to read temperature and update in DDR memory using m0 firmware. All six sensors has been given 4 bytes of memory in DDR to write temperature in millivolts. This thermal driver read temperature values from DDR because no direct access to sensors. Like this all temparature sensors are monitored and trips at critical temperature. If driver can't handle thermal runaways because of any unknown reason, then firmware in m0 Processor will handle. Reviewed-by: Ray Jui Reviewed-by: Scott Branden Reviewed-by: Vikram Prakash Reviewed-by: Rob Herring Signed-off-by: Pramod Kumar Signed-off-by: Srinath Mannam Signed-off-by: Eduardo Valentin --- drivers/thermal/Kconfig | 3 +- drivers/thermal/broadcom/Kconfig | 9 ++ drivers/thermal/broadcom/Makefile | 1 + drivers/thermal/broadcom/sr-thermal.c | 121 ++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 drivers/thermal/broadcom/sr-thermal.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 30323426902e..b71a774367f6 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -343,7 +343,8 @@ source "drivers/thermal/intel/Kconfig" endmenu menu "Broadcom thermal drivers" -depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST +depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCM_IPROC || \ + COMPILE_TEST source "drivers/thermal/broadcom/Kconfig" endmenu diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig index c106a15bf7f9..dc9a9bdde3ed 100644 --- a/drivers/thermal/broadcom/Kconfig +++ b/drivers/thermal/broadcom/Kconfig @@ -22,3 +22,12 @@ config BCM_NS_THERMAL BCM4708, BCM4709, BCM5301x, BCM95852X, etc). It contains DMU (Device Management Unit) block with a thermal sensor that allows checking CPU temperature. + +config BCM_SR_THERMAL + tristate "Stingray thermal driver" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + Support for the Stingray family of SoCs. Its different blocks like + iHost, CRMU and NITRO has thermal sensor that allows checking its + temperature. diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile index fae10ecafaef..79df69eb2b8c 100644 --- a/drivers/thermal/broadcom/Makefile +++ b/drivers/thermal/broadcom/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o obj-$(CONFIG_BRCMSTB_THERMAL) += brcmstb_thermal.o obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o +obj-$(CONFIG_BCM_SR_THERMAL) += sr-thermal.o diff --git a/drivers/thermal/broadcom/sr-thermal.c b/drivers/thermal/broadcom/sr-thermal.c new file mode 100644 index 000000000000..2284cbecedf3 --- /dev/null +++ b/drivers/thermal/broadcom/sr-thermal.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Broadcom + */ + +#include +#include +#include +#include +#include + +/* + * In stingray thermal IO memory, + * Total Number of available TMONs MASK is at offset 0 + * temperature registers BASE is at 4 byte offset. + * Each TMON temperature register size is 4. + */ +#define SR_TMON_TEMP_BASE(id) ((id) * 0x4) + +#define SR_TMON_MAX_LIST 6 + +struct sr_tmon { + struct thermal_zone_device *tz; + unsigned int crit_temp; + unsigned int tmon_id; + struct sr_thermal *priv; +}; + +struct sr_thermal { + void __iomem *regs; + unsigned int max_crit_temp; + struct sr_tmon tmon[SR_TMON_MAX_LIST]; +}; + +static int sr_get_temp(void *data, int *temp) +{ + struct sr_tmon *tmon = data; + struct sr_thermal *sr_thermal = tmon->priv; + + *temp = readl(sr_thermal->regs + SR_TMON_TEMP_BASE(tmon->tmon_id)); + + return 0; +} + +static const struct thermal_zone_of_device_ops sr_tz_ops = { + .get_temp = sr_get_temp, +}; + +static int sr_thermal_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sr_thermal *sr_thermal; + struct sr_tmon *tmon; + struct resource *res; + u32 sr_tmon_list = 0; + unsigned int i; + int ret; + + sr_thermal = devm_kzalloc(dev, sizeof(*sr_thermal), GFP_KERNEL); + if (!sr_thermal) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sr_thermal->regs = (void __iomem *)devm_memremap(&pdev->dev, res->start, + resource_size(res), + MEMREMAP_WB); + if (IS_ERR(sr_thermal->regs)) { + dev_err(dev, "failed to get io address\n"); + return PTR_ERR(sr_thermal->regs); + } + + ret = device_property_read_u32(dev, "brcm,tmon-mask", &sr_tmon_list); + if (ret) + return ret; + + tmon = sr_thermal->tmon; + for (i = 0; i < SR_TMON_MAX_LIST; i++, tmon++) { + if (!(sr_tmon_list & BIT(i))) + continue; + + /* Flush temperature registers */ + writel(0, sr_thermal->regs + SR_TMON_TEMP_BASE(i)); + tmon->tmon_id = i; + tmon->priv = sr_thermal; + tmon->tz = devm_thermal_zone_of_sensor_register(dev, i, tmon, + &sr_tz_ops); + if (IS_ERR(tmon->tz)) + return PTR_ERR(tmon->tz); + + dev_dbg(dev, "thermal sensor %d registered\n", i); + } + platform_set_drvdata(pdev, sr_thermal); + + return 0; +} + +static const struct of_device_id sr_thermal_of_match[] = { + { .compatible = "brcm,sr-thermal", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sr_thermal_of_match); + +static const struct acpi_device_id sr_thermal_acpi_ids[] = { + { .id = "BRCM0500" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, sr_thermal_acpi_ids); + +static struct platform_driver sr_thermal_driver = { + .probe = sr_thermal_probe, + .driver = { + .name = "sr-thermal", + .of_match_table = sr_thermal_of_match, + .acpi_match_table = ACPI_PTR(sr_thermal_acpi_ids), + }, +}; +module_platform_driver(sr_thermal_driver); + +MODULE_AUTHOR("Pramod Kumar "); +MODULE_DESCRIPTION("Stingray thermal driver"); +MODULE_LICENSE("GPL v2"); From 7797ff424818c975b59c35880dbc90fe08350204 Mon Sep 17 00:00:00 2001 From: Yuantian Tang Date: Fri, 18 Jan 2019 13:39:40 +0800 Subject: [PATCH 1174/4277] thermal: qoriq: add multiple sensors support The QorIQ Layerscape SoC has several thermal sensors but the current driver only supports one. Massage the code to be sensor oriented and allow the support for multiple sensors. Signed-off-by: Yuantian Tang Reviewed-by: Daniel Lezcano Signed-off-by: Eduardo Valentin --- drivers/thermal/qoriq_thermal.c | 100 ++++++++++++++------------------ 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 18c711b19514..3b5f5b3fb1bc 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -59,14 +59,21 @@ struct qoriq_tmu_regs { u32 ttr3cr; /* Temperature Range 3 Control Register */ }; +struct qoriq_tmu_data; + /* * Thermal zone data */ +struct qoriq_sensor { + struct thermal_zone_device *tzd; + struct qoriq_tmu_data *qdata; + int id; +}; + struct qoriq_tmu_data { - struct thermal_zone_device *tz; struct qoriq_tmu_regs __iomem *regs; - int sensor_id; bool little_endian; + struct qoriq_sensor *sensor[SITES_MAX]; }; static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) @@ -87,48 +94,50 @@ static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) static int tmu_get_temp(void *p, int *temp) { + struct qoriq_sensor *qsensor = p; + struct qoriq_tmu_data *qdata = qsensor->qdata; u32 val; - struct qoriq_tmu_data *data = p; - val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr); + val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); *temp = (val & 0xff) * 1000; return 0; } -static int qoriq_tmu_get_sensor_id(void) +static const struct thermal_zone_of_device_ops tmu_tz_ops = { + .get_temp = tmu_get_temp, +}; + +static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) { - int ret, id; - struct of_phandle_args sensor_specs; - struct device_node *np, *sensor_np; + struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev); + int id, sites = 0; - np = of_find_node_by_name(NULL, "thermal-zones"); - if (!np) - return -ENODEV; + for (id = 0; id < SITES_MAX; id++) { + qdata->sensor[id] = devm_kzalloc(&pdev->dev, + sizeof(struct qoriq_sensor), GFP_KERNEL); + if (!qdata->sensor[id]) + return -ENOMEM; - sensor_np = of_get_next_child(np, NULL); - ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors", - "#thermal-sensor-cells", - 0, &sensor_specs); - if (ret) { - of_node_put(np); - of_node_put(sensor_np); - return ret; + qdata->sensor[id]->id = id; + qdata->sensor[id]->qdata = qdata; + qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register( + &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops); + if (IS_ERR(qdata->sensor[id]->tzd)) { + if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV) + continue; + else + return PTR_ERR(qdata->sensor[id]->tzd); + } + + sites |= 0x1 << (15 - id); } - if (sensor_specs.args_count >= 1) { - id = sensor_specs.args[0]; - WARN(sensor_specs.args_count > 1, - "%pOFn: too many cells in sensor specifier %d\n", - sensor_specs.np, sensor_specs.args_count); - } else { - id = 0; - } + /* Enable monitoring */ + if (sites != 0) + tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr); - of_node_put(np); - of_node_put(sensor_np); - - return id; + return 0; } static int qoriq_tmu_calibration(struct platform_device *pdev) @@ -178,16 +187,11 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) tmu_write(data, TMR_DISABLE, &data->regs->tmr); } -static const struct thermal_zone_of_device_ops tmu_tz_ops = { - .get_temp = tmu_get_temp, -}; - static int qoriq_tmu_probe(struct platform_device *pdev) { int ret; struct qoriq_tmu_data *data; struct device_node *np = pdev->dev.of_node; - u32 site; if (!np) { dev_err(&pdev->dev, "Device OF-Node is NULL"); @@ -203,13 +207,6 @@ static int qoriq_tmu_probe(struct platform_device *pdev) data->little_endian = of_property_read_bool(np, "little-endian"); - data->sensor_id = qoriq_tmu_get_sensor_id(); - if (data->sensor_id < 0) { - dev_err(&pdev->dev, "Failed to get sensor id\n"); - ret = -ENODEV; - goto err_iomap; - } - data->regs = of_iomap(np, 0); if (!data->regs) { dev_err(&pdev->dev, "Failed to get memory region\n"); @@ -223,20 +220,13 @@ static int qoriq_tmu_probe(struct platform_device *pdev) if (ret < 0) goto err_tmu; - data->tz = devm_thermal_zone_of_sensor_register(&pdev->dev, - data->sensor_id, - data, &tmu_tz_ops); - if (IS_ERR(data->tz)) { - ret = PTR_ERR(data->tz); - dev_err(&pdev->dev, - "Failed to register thermal zone device %d\n", ret); - goto err_tmu; + ret = qoriq_tmu_register_tmu_zone(pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register sensors\n"); + ret = -ENODEV; + goto err_iomap; } - /* Enable monitoring */ - site = 0x1 << (15 - data->sensor_id); - tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr); - return 0; err_tmu: From eb9aecd90d1a39601e91cd08b90d5fee51d321a6 Mon Sep 17 00:00:00 2001 From: Michael Kao Date: Fri, 1 Feb 2019 15:38:07 +0800 Subject: [PATCH 1175/4277] thermal: mediatek: fix register index error The index of msr and adcpnp should match the sensor which belongs to the selected bank in the for loop. Fixes: b7cf0053738c ("thermal: Add Mediatek thermal driver for mt2701.") Signed-off-by: Michael Kao Signed-off-by: Eduardo Valentin --- drivers/thermal/mtk_thermal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 0691f260f6ea..f64643629d8b 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -431,7 +431,8 @@ static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank) u32 raw; for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) { - raw = readl(mt->thermal_base + conf->msr[i]); + raw = readl(mt->thermal_base + + conf->msr[conf->bank_data[bank->id].sensors[i]]); temp = raw_to_mcelsius(mt, conf->bank_data[bank->id].sensors[i], @@ -568,7 +569,8 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, for (i = 0; i < conf->bank_data[num].num_sensors; i++) writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]], - mt->thermal_base + conf->adcpnp[i]); + mt->thermal_base + + conf->adcpnp[conf->bank_data[num].sensors[i]]); writel((1 << conf->bank_data[num].num_sensors) - 1, mt->thermal_base + TEMP_MONCTL0); From 1d0819455e4329a3cf1e257888bd252d0bd15600 Mon Sep 17 00:00:00 2001 From: Michael Kao Date: Fri, 1 Feb 2019 15:38:08 +0800 Subject: [PATCH 1176/4277] thermal: mediatek: add common index of vts settings. Each project has different number of vts settings. For the MT2701 just have to set three vts, but the original code flow add five unnecessary vts. Add common index of vts settings for scalablity, and reduce the setting of unnecessary vts. Signed-off-by: Michael Kao Signed-off-by: Eduardo Valentin --- drivers/thermal/mtk_thermal.c | 93 ++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index f64643629d8b..07f8ad78ffb0 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -112,17 +112,26 @@ * MT2701 has 3 sensors and needs 3 VTS calibration data. * MT2712 has 4 sensors and needs 4 VTS calibration data. */ -#define MT8173_CALIB_BUF0_VALID BIT(0) -#define MT8173_CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff) -#define MT8173_CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff) -#define MT8173_CALIB_BUF0_VTS_TS2(x) (((x) >> 8) & 0x1ff) -#define MT8173_CALIB_BUF1_VTS_TS3(x) (((x) >> 0) & 0x1ff) -#define MT8173_CALIB_BUF2_VTS_TS4(x) (((x) >> 23) & 0x1ff) -#define MT8173_CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff) -#define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f) -#define MT8173_CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f) -#define MT8173_CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1) -#define MT8173_CALIB_BUF1_ID(x) (((x) >> 9) & 0x1) +#define CALIB_BUF0_VALID BIT(0) +#define CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff) +#define CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff) +#define CALIB_BUF0_VTS_TS2(x) (((x) >> 8) & 0x1ff) +#define CALIB_BUF1_VTS_TS3(x) (((x) >> 0) & 0x1ff) +#define CALIB_BUF2_VTS_TS4(x) (((x) >> 23) & 0x1ff) +#define CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff) +#define CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f) +#define CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f) +#define CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1) +#define CALIB_BUF1_ID(x) (((x) >> 9) & 0x1) + +enum { + VTS1, + VTS2, + VTS3, + VTS4, + VTSABB, + MAX_NUM_VTS, +}; /* MT2701 thermal sensors */ #define MT2701_TS1 0 @@ -175,6 +184,7 @@ struct mtk_thermal_data { s32 num_banks; s32 num_sensors; s32 auxadc_channel; + const int *vts_index; const int *sensor_mux_values; const int *msr; const int *adcpnp; @@ -194,7 +204,7 @@ struct mtk_thermal { s32 adc_ge; s32 degc_cali; s32 o_slope; - s32 vts[MT8173_NUM_SENSORS]; + s32 vts[MAX_NUM_VTS]; const struct mtk_thermal_data *conf; struct mtk_thermal_bank banks[]; @@ -218,6 +228,10 @@ static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; +static const int mt8173_vts_index[MT8173_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4, VTSABB +}; + /* MT2701 thermal sensor data */ static const int mt2701_bank_data[MT2701_NUM_SENSORS] = { MT2701_TS1, MT2701_TS2, MT2701_TSABB @@ -233,6 +247,10 @@ static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; +static const int mt2701_vts_index[MT2701_NUM_SENSORS] = { + VTS1, VTS2, VTS3 +}; + /* MT2712 thermal sensor data */ static const int mt2712_bank_data[MT2712_NUM_SENSORS] = { MT2712_TS1, MT2712_TS2, MT2712_TS3, MT2712_TS4 @@ -248,11 +266,16 @@ static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = { static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 }; +static const int mt2712_vts_index[MT2712_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4 +}; + /* MT7622 thermal sensor data */ static const int mt7622_bank_data[MT7622_NUM_SENSORS] = { MT7622_TS1, }; static const int mt7622_msr[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; static const int mt7622_adcpnp[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, }; +static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 }; /** * The MT8173 thermal controller has four banks. Each bank can read up to @@ -271,6 +294,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = { .auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL, .num_banks = MT8173_NUM_ZONES, .num_sensors = MT8173_NUM_SENSORS, + .vts_index = mt8173_vts_index, .bank_data = { { .num_sensors = 2, @@ -305,6 +329,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = { .auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL, .num_banks = 1, .num_sensors = MT2701_NUM_SENSORS, + .vts_index = mt2701_vts_index, .bank_data = { { .num_sensors = 3, @@ -330,6 +355,7 @@ static const struct mtk_thermal_data mt2712_thermal_data = { .auxadc_channel = MT2712_TEMP_AUXADC_CHANNEL, .num_banks = 1, .num_sensors = MT2712_NUM_SENSORS, + .vts_index = mt2712_vts_index, .bank_data = { { .num_sensors = 4, @@ -349,6 +375,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = { .auxadc_channel = MT7622_TEMP_AUXADC_CHANNEL, .num_banks = MT7622_NUM_ZONES, .num_sensors = MT7622_NUM_SENSORS, + .vts_index = mt7622_vts_index, .bank_data = { { .num_sensors = 1, @@ -629,19 +656,37 @@ static int mtk_thermal_get_calibration_data(struct device *dev, goto out; } - if (buf[0] & MT8173_CALIB_BUF0_VALID) { - mt->adc_ge = MT8173_CALIB_BUF1_ADC_GE(buf[1]); - mt->vts[MT8173_TS1] = MT8173_CALIB_BUF0_VTS_TS1(buf[0]); - mt->vts[MT8173_TS2] = MT8173_CALIB_BUF0_VTS_TS2(buf[0]); - mt->vts[MT8173_TS3] = MT8173_CALIB_BUF1_VTS_TS3(buf[1]); - mt->vts[MT8173_TS4] = MT8173_CALIB_BUF2_VTS_TS4(buf[2]); - mt->vts[MT8173_TSABB] = MT8173_CALIB_BUF2_VTS_TSABB(buf[2]); - mt->degc_cali = MT8173_CALIB_BUF0_DEGC_CALI(buf[0]); - if (MT8173_CALIB_BUF1_ID(buf[1]) & - MT8173_CALIB_BUF0_O_SLOPE_SIGN(buf[0])) - mt->o_slope = -MT8173_CALIB_BUF0_O_SLOPE(buf[0]); + if (buf[0] & CALIB_BUF0_VALID) { + mt->adc_ge = CALIB_BUF1_ADC_GE(buf[1]); + + for (i = 0; i < mt->conf->num_sensors; i++) { + switch (mt->conf->vts_index[i]) { + case VTS1: + mt->vts[VTS1] = CALIB_BUF0_VTS_TS1(buf[0]); + break; + case VTS2: + mt->vts[VTS2] = CALIB_BUF0_VTS_TS2(buf[0]); + break; + case VTS3: + mt->vts[VTS3] = CALIB_BUF1_VTS_TS3(buf[1]); + break; + case VTS4: + mt->vts[VTS4] = CALIB_BUF2_VTS_TS4(buf[2]); + break; + case VTSABB: + mt->vts[VTSABB] = CALIB_BUF2_VTS_TSABB(buf[2]); + break; + default: + break; + } + } + + mt->degc_cali = CALIB_BUF0_DEGC_CALI(buf[0]); + if (CALIB_BUF1_ID(buf[1]) & + CALIB_BUF0_O_SLOPE_SIGN(buf[0])) + mt->o_slope = -CALIB_BUF0_O_SLOPE(buf[0]); else - mt->o_slope = MT8173_CALIB_BUF0_O_SLOPE(buf[0]); + mt->o_slope = CALIB_BUF0_O_SLOPE(buf[0]); } else { dev_info(dev, "Device not calibrated, using default calibration values\n"); } From f84514766985d39ff546097eb887f896a8cfb855 Mon Sep 17 00:00:00 2001 From: Michael Kao Date: Fri, 1 Feb 2019 15:38:09 +0800 Subject: [PATCH 1177/4277] thermal: mediatek: add calibration item Add calibration item in thermal_data to support the project with different calibration coefficient. Signed-off-by: Michael Kao Signed-off-by: Eduardo Valentin --- drivers/thermal/mtk_thermal.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 07f8ad78ffb0..45c658711e0e 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -105,6 +105,9 @@ /* The number of sensing points per bank */ #define MT8173_NUM_SENSORS_PER_ZONE 4 +/* The calibration coefficient of sensor */ +#define MT8173_CALIBRATION 165 + /* * Layout of the fuses providing the calibration data * These macros could be used for MT8173, MT2701, and MT2712. @@ -147,6 +150,9 @@ enum { /* The number of sensing points per bank */ #define MT2701_NUM_SENSORS_PER_ZONE 3 +/* The calibration coefficient of sensor */ +#define MT2701_CALIBRATION 165 + /* MT2712 thermal sensors */ #define MT2712_TS1 0 #define MT2712_TS2 1 @@ -162,12 +168,18 @@ enum { /* The number of sensing points per bank */ #define MT2712_NUM_SENSORS_PER_ZONE 4 +/* The calibration coefficient of sensor */ +#define MT2712_CALIBRATION 165 + #define MT7622_TEMP_AUXADC_CHANNEL 11 #define MT7622_NUM_SENSORS 1 #define MT7622_NUM_ZONES 1 #define MT7622_NUM_SENSORS_PER_ZONE 1 #define MT7622_TS1 0 +/* The calibration coefficient of sensor */ +#define MT7622_CALIBRATION 165 + struct mtk_thermal; struct thermal_bank_cfg { @@ -188,6 +200,7 @@ struct mtk_thermal_data { const int *sensor_mux_values; const int *msr; const int *adcpnp; + const int cali_val; struct thermal_bank_cfg bank_data[]; }; @@ -295,6 +308,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = { .num_banks = MT8173_NUM_ZONES, .num_sensors = MT8173_NUM_SENSORS, .vts_index = mt8173_vts_index, + .cali_val = MT8173_CALIBRATION, .bank_data = { { .num_sensors = 2, @@ -330,6 +344,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = { .num_banks = 1, .num_sensors = MT2701_NUM_SENSORS, .vts_index = mt2701_vts_index, + .cali_val = MT2701_CALIBRATION, .bank_data = { { .num_sensors = 3, @@ -356,6 +371,7 @@ static const struct mtk_thermal_data mt2712_thermal_data = { .num_banks = 1, .num_sensors = MT2712_NUM_SENSORS, .vts_index = mt2712_vts_index, + .cali_val = MT2712_CALIBRATION, .bank_data = { { .num_sensors = 4, @@ -376,6 +392,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = { .num_banks = MT7622_NUM_ZONES, .num_sensors = MT7622_NUM_SENSORS, .vts_index = mt7622_vts_index, + .cali_val = MT7622_CALIBRATION, .bank_data = { { .num_sensors = 1, @@ -402,7 +419,7 @@ static int raw_to_mcelsius(struct mtk_thermal *mt, int sensno, s32 raw) raw &= 0xfff; tmp = 203450520 << 3; - tmp /= 165 + mt->o_slope; + tmp /= mt->conf->cali_val + mt->o_slope; tmp /= 10000 + mt->adc_ge; tmp *= raw - mt->vts[sensno] - 3350; tmp >>= 3; From bd9403943d7d19ce54ae462762beecb54a751c44 Mon Sep 17 00:00:00 2001 From: Michael Kao Date: Fri, 1 Feb 2019 15:38:10 +0800 Subject: [PATCH 1178/4277] thermal: mediatek: add thermal controller offset One thermal controller can read four sensors at most, so we need to add controller_offset for the project with more than four sensors to reuse the same register settings. Signed-off-by: Michael Kao Signed-off-by: Eduardo Valentin --- drivers/thermal/mtk_thermal.c | 79 ++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 45c658711e0e..c96a746c42ea 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -105,6 +105,9 @@ /* The number of sensing points per bank */ #define MT8173_NUM_SENSORS_PER_ZONE 4 +/* The number of controller in the MT8173 */ +#define MT8173_NUM_CONTROLLER 1 + /* The calibration coefficient of sensor */ #define MT8173_CALIBRATION 165 @@ -150,6 +153,9 @@ enum { /* The number of sensing points per bank */ #define MT2701_NUM_SENSORS_PER_ZONE 3 +/* The number of controller in the MT2701 */ +#define MT2701_NUM_CONTROLLER 1 + /* The calibration coefficient of sensor */ #define MT2701_CALIBRATION 165 @@ -168,6 +174,9 @@ enum { /* The number of sensing points per bank */ #define MT2712_NUM_SENSORS_PER_ZONE 4 +/* The number of controller in the MT2712 */ +#define MT2712_NUM_CONTROLLER 1 + /* The calibration coefficient of sensor */ #define MT2712_CALIBRATION 165 @@ -176,6 +185,7 @@ enum { #define MT7622_NUM_ZONES 1 #define MT7622_NUM_SENSORS_PER_ZONE 1 #define MT7622_TS1 0 +#define MT7622_NUM_CONTROLLER 1 /* The calibration coefficient of sensor */ #define MT7622_CALIBRATION 165 @@ -201,6 +211,8 @@ struct mtk_thermal_data { const int *msr; const int *adcpnp; const int cali_val; + const int num_controller; + const int *controller_offset; struct thermal_bank_cfg bank_data[]; }; @@ -240,6 +252,7 @@ static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { }; static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; +static const int mt8173_tc_offset[MT8173_NUM_CONTROLLER] = { 0x0, }; static const int mt8173_vts_index[MT8173_NUM_SENSORS] = { VTS1, VTS2, VTS3, VTS4, VTSABB @@ -259,6 +272,7 @@ static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { }; static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; +static const int mt2701_tc_offset[MT2701_NUM_CONTROLLER] = { 0x0, }; static const int mt2701_vts_index[MT2701_NUM_SENSORS] = { VTS1, VTS2, VTS3 @@ -278,6 +292,7 @@ static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = { }; static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 }; +static const int mt2712_tc_offset[MT2712_NUM_CONTROLLER] = { 0x0, }; static const int mt2712_vts_index[MT2712_NUM_SENSORS] = { VTS1, VTS2, VTS3, VTS4 @@ -289,6 +304,7 @@ static const int mt7622_msr[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; static const int mt7622_adcpnp[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, }; static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 }; +static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, }; /** * The MT8173 thermal controller has four banks. Each bank can read up to @@ -309,6 +325,8 @@ static const struct mtk_thermal_data mt8173_thermal_data = { .num_sensors = MT8173_NUM_SENSORS, .vts_index = mt8173_vts_index, .cali_val = MT8173_CALIBRATION, + .num_controller = MT8173_NUM_CONTROLLER, + .controller_offset = mt8173_tc_offset, .bank_data = { { .num_sensors = 2, @@ -345,6 +363,8 @@ static const struct mtk_thermal_data mt2701_thermal_data = { .num_sensors = MT2701_NUM_SENSORS, .vts_index = mt2701_vts_index, .cali_val = MT2701_CALIBRATION, + .num_controller = MT2701_NUM_CONTROLLER, + .controller_offset = mt2701_tc_offset, .bank_data = { { .num_sensors = 3, @@ -372,6 +392,8 @@ static const struct mtk_thermal_data mt2712_thermal_data = { .num_sensors = MT2712_NUM_SENSORS, .vts_index = mt2712_vts_index, .cali_val = MT2712_CALIBRATION, + .num_controller = MT2712_NUM_CONTROLLER, + .controller_offset = mt2712_tc_offset, .bank_data = { { .num_sensors = 4, @@ -393,6 +415,8 @@ static const struct mtk_thermal_data mt7622_thermal_data = { .num_sensors = MT7622_NUM_SENSORS, .vts_index = mt7622_vts_index, .cali_val = MT7622_CALIBRATION, + .num_controller = MT7622_NUM_CONTROLLER, + .controller_offset = mt7622_tc_offset, .bank_data = { { .num_sensors = 1, @@ -523,19 +547,23 @@ static const struct thermal_zone_of_device_ops mtk_thermal_ops = { }; static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, - u32 apmixed_phys_base, u32 auxadc_phys_base) + u32 apmixed_phys_base, u32 auxadc_phys_base, + int ctrl_id) { struct mtk_thermal_bank *bank = &mt->banks[num]; const struct mtk_thermal_data *conf = mt->conf; int i; + int offset = mt->conf->controller_offset[ctrl_id]; + void __iomem *controller_base = mt->thermal_base + offset; + bank->id = num; bank->mt = mt; mtk_thermal_get_bank(bank); /* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us */ - writel(TEMP_MONCTL1_PERIOD_UNIT(12), mt->thermal_base + TEMP_MONCTL1); + writel(TEMP_MONCTL1_PERIOD_UNIT(12), controller_base + TEMP_MONCTL1); /* * filt interval is 1 * 46.540us = 46.54us, @@ -543,21 +571,21 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, */ writel(TEMP_MONCTL2_FILTER_INTERVAL(1) | TEMP_MONCTL2_SENSOR_INTERVAL(429), - mt->thermal_base + TEMP_MONCTL2); + controller_base + TEMP_MONCTL2); /* poll is set to 10u */ writel(TEMP_AHBPOLL_ADC_POLL_INTERVAL(768), - mt->thermal_base + TEMP_AHBPOLL); + controller_base + TEMP_AHBPOLL); /* temperature sampling control, 1 sample */ - writel(0x0, mt->thermal_base + TEMP_MSRCTL0); + writel(0x0, controller_base + TEMP_MSRCTL0); /* exceed this polling time, IRQ would be inserted */ - writel(0xffffffff, mt->thermal_base + TEMP_AHBTO); + writel(0xffffffff, controller_base + TEMP_AHBTO); /* number of interrupts per event, 1 is enough */ - writel(0x0, mt->thermal_base + TEMP_MONIDET0); - writel(0x0, mt->thermal_base + TEMP_MONIDET1); + writel(0x0, controller_base + TEMP_MONIDET0); + writel(0x0, controller_base + TEMP_MONIDET1); /* * The MT8173 thermal controller does not have its own ADC. Instead it @@ -572,44 +600,44 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, * this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0) * automatically by hw */ - writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCMUX); + writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCMUX); /* AHB address for auxadc mux selection */ writel(auxadc_phys_base + AUXADC_CON1_CLR_V, - mt->thermal_base + TEMP_ADCMUXADDR); + controller_base + TEMP_ADCMUXADDR); /* AHB address for pnp sensor mux selection */ writel(apmixed_phys_base + APMIXED_SYS_TS_CON1, - mt->thermal_base + TEMP_PNPMUXADDR); + controller_base + TEMP_PNPMUXADDR); /* AHB value for auxadc enable */ - writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCEN); + writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCEN); /* AHB address for auxadc enable (channel 0 immediate mode selected) */ writel(auxadc_phys_base + AUXADC_CON1_SET_V, - mt->thermal_base + TEMP_ADCENADDR); + controller_base + TEMP_ADCENADDR); /* AHB address for auxadc valid bit */ writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), - mt->thermal_base + TEMP_ADCVALIDADDR); + controller_base + TEMP_ADCVALIDADDR); /* AHB address for auxadc voltage output */ writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), - mt->thermal_base + TEMP_ADCVOLTADDR); + controller_base + TEMP_ADCVOLTADDR); /* read valid & voltage are at the same register */ - writel(0x0, mt->thermal_base + TEMP_RDCTRL); + writel(0x0, controller_base + TEMP_RDCTRL); /* indicate where the valid bit is */ writel(TEMP_ADCVALIDMASK_VALID_HIGH | TEMP_ADCVALIDMASK_VALID_POS(12), - mt->thermal_base + TEMP_ADCVALIDMASK); + controller_base + TEMP_ADCVALIDMASK); /* no shift */ - writel(0x0, mt->thermal_base + TEMP_ADCVOLTAGESHIFT); + writel(0x0, controller_base + TEMP_ADCVOLTAGESHIFT); /* enable auxadc mux write transaction */ writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE, - mt->thermal_base + TEMP_ADCWRITECTRL); + controller_base + TEMP_ADCWRITECTRL); for (i = 0; i < conf->bank_data[num].num_sensors; i++) writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]], @@ -617,11 +645,11 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, conf->adcpnp[conf->bank_data[num].sensors[i]]); writel((1 << conf->bank_data[num].num_sensors) - 1, - mt->thermal_base + TEMP_MONCTL0); + controller_base + TEMP_MONCTL0); writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE | TEMP_ADCWRITECTRL_ADC_MUX_WRITE, - mt->thermal_base + TEMP_ADCWRITECTRL); + controller_base + TEMP_ADCWRITECTRL); mtk_thermal_put_bank(bank); } @@ -737,7 +765,7 @@ MODULE_DEVICE_TABLE(of, mtk_thermal_of_match); static int mtk_thermal_probe(struct platform_device *pdev) { - int ret, i; + int ret, i, ctrl_id; struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node; struct mtk_thermal *mt; struct resource *res; @@ -817,9 +845,10 @@ static int mtk_thermal_probe(struct platform_device *pdev) goto err_disable_clk_auxadc; } - for (i = 0; i < mt->conf->num_banks; i++) - mtk_thermal_init_bank(mt, i, apmixed_phys_base, - auxadc_phys_base); + for (ctrl_id = 0; ctrl_id < mt->conf->num_controller ; ctrl_id++) + for (i = 0; i < mt->conf->num_banks; i++) + mtk_thermal_init_bank(mt, i, apmixed_phys_base, + auxadc_phys_base, ctrl_id); platform_set_drvdata(pdev, mt); From cb82aaade9d1bf87dadd95534c31304fb4b2ffe8 Mon Sep 17 00:00:00 2001 From: Michael Kao Date: Fri, 1 Feb 2019 15:38:11 +0800 Subject: [PATCH 1179/4277] thermal: mediatek: add flag for bank selection For past ic designs, the thermal controller should select banks before reading the thermal sensor. And the new ic design architecture removes this mechanism. Signed-off-by: Michael Kao Signed-off-by: Eduardo Valentin --- drivers/thermal/mtk_thermal.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index c96a746c42ea..0a3944e4a62a 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -213,6 +213,7 @@ struct mtk_thermal_data { const int cali_val; const int num_controller; const int *controller_offset; + bool need_switch_bank; struct thermal_bank_cfg bank_data[]; }; @@ -327,6 +328,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = { .cali_val = MT8173_CALIBRATION, .num_controller = MT8173_NUM_CONTROLLER, .controller_offset = mt8173_tc_offset, + .need_switch_bank = true, .bank_data = { { .num_sensors = 2, @@ -365,6 +367,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = { .cali_val = MT2701_CALIBRATION, .num_controller = MT2701_NUM_CONTROLLER, .controller_offset = mt2701_tc_offset, + .need_switch_bank = true, .bank_data = { { .num_sensors = 3, @@ -394,6 +397,7 @@ static const struct mtk_thermal_data mt2712_thermal_data = { .cali_val = MT2712_CALIBRATION, .num_controller = MT2712_NUM_CONTROLLER, .controller_offset = mt2712_tc_offset, + .need_switch_bank = true, .bank_data = { { .num_sensors = 4, @@ -417,6 +421,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = { .cali_val = MT7622_CALIBRATION, .num_controller = MT7622_NUM_CONTROLLER, .controller_offset = mt7622_tc_offset, + .need_switch_bank = true, .bank_data = { { .num_sensors = 1, @@ -463,12 +468,14 @@ static void mtk_thermal_get_bank(struct mtk_thermal_bank *bank) struct mtk_thermal *mt = bank->mt; u32 val; - mutex_lock(&mt->lock); + if (mt->conf->need_switch_bank) { + mutex_lock(&mt->lock); - val = readl(mt->thermal_base + PTPCORESEL); - val &= ~0xf; - val |= bank->id; - writel(val, mt->thermal_base + PTPCORESEL); + val = readl(mt->thermal_base + PTPCORESEL); + val &= ~0xf; + val |= bank->id; + writel(val, mt->thermal_base + PTPCORESEL); + } } /** @@ -481,7 +488,8 @@ static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank) { struct mtk_thermal *mt = bank->mt; - mutex_unlock(&mt->lock); + if (mt->conf->need_switch_bank) + mutex_unlock(&mt->lock); } /** From c0d7c861bd5792bef9de7192359b05099611bb27 Mon Sep 17 00:00:00 2001 From: Michael Kao Date: Fri, 1 Feb 2019 15:38:12 +0800 Subject: [PATCH 1180/4277] dt-bindings: thermal: add binding document for mt8183 thermal controller This patch adds binding document for mt8183 thermal controller. Signed-off-by: Michael Kao Signed-off-by: Eduardo Valentin --- Documentation/devicetree/bindings/thermal/mediatek-thermal.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/mediatek-thermal.txt b/Documentation/devicetree/bindings/thermal/mediatek-thermal.txt index 41d6a443ad66..f8d7831f3974 100644 --- a/Documentation/devicetree/bindings/thermal/mediatek-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/mediatek-thermal.txt @@ -13,6 +13,7 @@ Required properties: - "mediatek,mt2701-thermal" : For MT2701 family of SoCs - "mediatek,mt2712-thermal" : For MT2712 family of SoCs - "mediatek,mt7622-thermal" : For MT7622 SoC + - "mediatek,mt8183-thermal" : For MT8183 family of SoCs - reg: Address range of the thermal controller - interrupts: IRQ for the thermal controller - clocks, clock-names: Clocks needed for the thermal controller. required From ccf3209f0044e46236e238494181ae1a73275fad Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 9 Nov 2018 20:12:25 +0100 Subject: [PATCH 1181/4277] scsi: bsg: refactor bsg_ioctl Move all actual functionality into helpers, just leaving the dispatch in this function. Signed-off-by: Christoph Hellwig Reviewed-by: Benjamin Block Tested-by: Benjamin Block Tested-by: Avri Altman Acked-by: Jens Axboe Signed-off-by: Martin K. Petersen --- block/bsg.c | 162 ++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 88 deletions(-) diff --git a/block/bsg.c b/block/bsg.c index 50e5f8f666f2..a799b0ace55c 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -138,32 +138,35 @@ static const struct bsg_ops bsg_scsi_ops = { .free_rq = bsg_scsi_free_rq, }; -static struct request * -bsg_map_hdr(struct request_queue *q, struct sg_io_v4 *hdr, fmode_t mode) +static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) { struct request *rq, *next_rq = NULL; + struct bio *bio, *bidi_bio = NULL; + struct sg_io_v4 hdr; int ret; + if (copy_from_user(&hdr, uarg, sizeof(hdr))) + return -EFAULT; + if (!q->bsg_dev.class_dev) - return ERR_PTR(-ENXIO); + return -ENXIO; - if (hdr->guard != 'Q') - return ERR_PTR(-EINVAL); - - ret = q->bsg_dev.ops->check_proto(hdr); + if (hdr.guard != 'Q') + return -EINVAL; + ret = q->bsg_dev.ops->check_proto(&hdr); if (ret) - return ERR_PTR(ret); + return ret; - rq = blk_get_request(q, hdr->dout_xfer_len ? + rq = blk_get_request(q, hdr.dout_xfer_len ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0); if (IS_ERR(rq)) - return rq; + return PTR_ERR(rq); - ret = q->bsg_dev.ops->fill_hdr(rq, hdr, mode); + ret = q->bsg_dev.ops->fill_hdr(rq, &hdr, mode); if (ret) goto out; - rq->timeout = msecs_to_jiffies(hdr->timeout); + rq->timeout = msecs_to_jiffies(hdr.timeout); if (!rq->timeout) rq->timeout = q->sg_timeout; if (!rq->timeout) @@ -171,7 +174,7 @@ bsg_map_hdr(struct request_queue *q, struct sg_io_v4 *hdr, fmode_t mode) if (rq->timeout < BLK_MIN_SG_TIMEOUT) rq->timeout = BLK_MIN_SG_TIMEOUT; - if (hdr->dout_xfer_len && hdr->din_xfer_len) { + if (hdr.dout_xfer_len && hdr.din_xfer_len) { if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) { ret = -EOPNOTSUPP; goto out; @@ -188,23 +191,42 @@ bsg_map_hdr(struct request_queue *q, struct sg_io_v4 *hdr, fmode_t mode) } rq->next_rq = next_rq; - ret = blk_rq_map_user(q, next_rq, NULL, uptr64(hdr->din_xferp), - hdr->din_xfer_len, GFP_KERNEL); + ret = blk_rq_map_user(q, next_rq, NULL, uptr64(hdr.din_xferp), + hdr.din_xfer_len, GFP_KERNEL); if (ret) goto out_free_nextrq; } - if (hdr->dout_xfer_len) { - ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr->dout_xferp), - hdr->dout_xfer_len, GFP_KERNEL); - } else if (hdr->din_xfer_len) { - ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr->din_xferp), - hdr->din_xfer_len, GFP_KERNEL); + if (hdr.dout_xfer_len) { + ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.dout_xferp), + hdr.dout_xfer_len, GFP_KERNEL); + } else if (hdr.din_xfer_len) { + ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.din_xferp), + hdr.din_xfer_len, GFP_KERNEL); } if (ret) goto out_unmap_nextrq; - return rq; + + bio = rq->bio; + if (rq->next_rq) + bidi_bio = rq->next_rq->bio; + + blk_execute_rq(q, NULL, rq, !(hdr.flags & BSG_FLAG_Q_AT_TAIL)); + ret = rq->q->bsg_dev.ops->complete_rq(rq, &hdr); + + if (rq->next_rq) { + blk_rq_unmap_user(bidi_bio); + blk_put_request(rq->next_rq); + } + + blk_rq_unmap_user(bio); + rq->q->bsg_dev.ops->free_rq(rq); + blk_put_request(rq); + + if (copy_to_user(uarg, &hdr, sizeof(hdr))) + return -EFAULT; + return ret; out_unmap_nextrq: if (rq->next_rq) @@ -215,24 +237,6 @@ out_free_nextrq: out: q->bsg_dev.ops->free_rq(rq); blk_put_request(rq); - return ERR_PTR(ret); -} - -static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr, - struct bio *bio, struct bio *bidi_bio) -{ - int ret; - - ret = rq->q->bsg_dev.ops->complete_rq(rq, hdr); - - if (rq->next_rq) { - blk_rq_unmap_user(bidi_bio); - blk_put_request(rq->next_rq); - } - - blk_rq_unmap_user(bio); - rq->q->bsg_dev.ops->free_rq(rq); - blk_put_request(rq); return ret; } @@ -367,31 +371,39 @@ static int bsg_release(struct inode *inode, struct file *file) return bsg_put_device(bd); } +static int bsg_get_command_q(struct bsg_device *bd, int __user *uarg) +{ + return put_user(bd->max_queue, uarg); +} + +static int bsg_set_command_q(struct bsg_device *bd, int __user *uarg) +{ + int queue; + + if (get_user(queue, uarg)) + return -EFAULT; + if (queue < 1) + return -EINVAL; + + spin_lock_irq(&bd->lock); + bd->max_queue = queue; + spin_unlock_irq(&bd->lock); + return 0; +} + static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct bsg_device *bd = file->private_data; - int __user *uarg = (int __user *) arg; - int ret; + void __user *uarg = (void __user *) arg; switch (cmd) { - /* - * our own ioctls - */ + /* + * Our own ioctls + */ case SG_GET_COMMAND_Q: - return put_user(bd->max_queue, uarg); - case SG_SET_COMMAND_Q: { - int queue; - - if (get_user(queue, uarg)) - return -EFAULT; - if (queue < 1) - return -EINVAL; - - spin_lock_irq(&bd->lock); - bd->max_queue = queue; - spin_unlock_irq(&bd->lock); - return 0; - } + return bsg_get_command_q(bd, uarg); + case SG_SET_COMMAND_Q: + return bsg_set_command_q(bd, uarg); /* * SCSI/sg ioctls @@ -404,36 +416,10 @@ static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case SG_GET_RESERVED_SIZE: case SG_SET_RESERVED_SIZE: case SG_EMULATED_HOST: - case SCSI_IOCTL_SEND_COMMAND: { - void __user *uarg = (void __user *) arg; + case SCSI_IOCTL_SEND_COMMAND: return scsi_cmd_ioctl(bd->queue, NULL, file->f_mode, cmd, uarg); - } - case SG_IO: { - struct request *rq; - struct bio *bio, *bidi_bio = NULL; - struct sg_io_v4 hdr; - int at_head; - - if (copy_from_user(&hdr, uarg, sizeof(hdr))) - return -EFAULT; - - rq = bsg_map_hdr(bd->queue, &hdr, file->f_mode); - if (IS_ERR(rq)) - return PTR_ERR(rq); - - bio = rq->bio; - if (rq->next_rq) - bidi_bio = rq->next_rq->bio; - - at_head = (0 == (hdr.flags & BSG_FLAG_Q_AT_TAIL)); - blk_execute_rq(bd->queue, NULL, rq, at_head); - ret = blk_complete_sgv4_hdr_rq(rq, &hdr, bio, bidi_bio); - - if (copy_to_user(uarg, &hdr, sizeof(hdr))) - return -EFAULT; - - return ret; - } + case SG_IO: + return bsg_sg_io(bd->queue, file->f_mode, uarg); default: return -ENOTTY; } From 972248e9111ee6fe9fb56c24ecfd7434f3d713ac Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 29 Jan 2019 09:32:03 +0100 Subject: [PATCH 1182/4277] scsi: bsg-lib: handle bidi requests without block layer help We can just stash away the second request in struct bsg_job instead of using the block layer req->next_rq field, allowing for the eventual removal of the latter. Signed-off-by: Christoph Hellwig Acked-by: Jens Axboe Signed-off-by: Martin K. Petersen --- block/bsg-lib.c | 44 +++++++++++++++++--- block/bsg.c | 68 +++++++------------------------ drivers/scsi/scsi_transport_sas.c | 1 - include/linux/bsg-lib.h | 4 ++ 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/block/bsg-lib.c b/block/bsg-lib.c index 192129856342..005e2b75d775 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -51,11 +51,40 @@ static int bsg_transport_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, fmode_t mode) { struct bsg_job *job = blk_mq_rq_to_pdu(rq); + int ret; job->request_len = hdr->request_len; job->request = memdup_user(uptr64(hdr->request), hdr->request_len); + if (IS_ERR(job->request)) + return PTR_ERR(job->request); - return PTR_ERR_OR_ZERO(job->request); + if (hdr->dout_xfer_len && hdr->din_xfer_len) { + job->bidi_rq = blk_get_request(rq->q, REQ_OP_SCSI_IN, 0); + if (IS_ERR(job->bidi_rq)) { + ret = PTR_ERR(job->bidi_rq); + goto out; + } + + ret = blk_rq_map_user(rq->q, job->bidi_rq, NULL, + uptr64(hdr->din_xferp), hdr->din_xfer_len, + GFP_KERNEL); + if (ret) + goto out_free_bidi_rq; + + job->bidi_bio = job->bidi_rq->bio; + } else { + job->bidi_rq = NULL; + job->bidi_bio = NULL; + } + + return 0; + +out_free_bidi_rq: + if (job->bidi_rq) + blk_put_request(job->bidi_rq); +out: + kfree(job->request); + return ret; } static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr) @@ -93,7 +122,7 @@ static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr) /* we assume all request payload was transferred, residual == 0 */ hdr->dout_resid = 0; - if (rq->next_rq) { + if (job->bidi_rq) { unsigned int rsp_len = job->reply_payload.payload_len; if (WARN_ON(job->reply_payload_rcv_len > rsp_len)) @@ -111,6 +140,11 @@ static void bsg_transport_free_rq(struct request *rq) { struct bsg_job *job = blk_mq_rq_to_pdu(rq); + if (job->bidi_rq) { + blk_rq_unmap_user(job->bidi_bio); + blk_put_request(job->bidi_rq); + } + kfree(job->request); } @@ -200,7 +234,6 @@ static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req) */ static bool bsg_prepare_job(struct device *dev, struct request *req) { - struct request *rsp = req->next_rq; struct bsg_job *job = blk_mq_rq_to_pdu(req); int ret; @@ -211,8 +244,8 @@ static bool bsg_prepare_job(struct device *dev, struct request *req) if (ret) goto failjob_rls_job; } - if (rsp && rsp->bio) { - ret = bsg_map_buffer(&job->reply_payload, rsp); + if (job->bidi_rq) { + ret = bsg_map_buffer(&job->reply_payload, job->bidi_rq); if (ret) goto failjob_rls_rqst_payload; } @@ -369,7 +402,6 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, } q->queuedata = dev; - blk_queue_flag_set(QUEUE_FLAG_BIDI, q); blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); ret = bsg_register_queue(q, dev, name, &bsg_transport_ops); diff --git a/block/bsg.c b/block/bsg.c index a799b0ace55c..f306853c6b08 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -74,6 +74,11 @@ static int bsg_scsi_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, { struct scsi_request *sreq = scsi_req(rq); + if (hdr->dout_xfer_len && hdr->din_xfer_len) { + pr_warn_once("BIDI support in bsg has been removed.\n"); + return -EOPNOTSUPP; + } + sreq->cmd_len = hdr->request_len; if (sreq->cmd_len > BLK_MAX_CDB) { sreq->cmd = kzalloc(sreq->cmd_len, GFP_KERNEL); @@ -114,14 +119,10 @@ static int bsg_scsi_complete_rq(struct request *rq, struct sg_io_v4 *hdr) hdr->response_len = len; } - if (rq->next_rq) { - hdr->dout_resid = sreq->resid_len; - hdr->din_resid = scsi_req(rq->next_rq)->resid_len; - } else if (rq_data_dir(rq) == READ) { + if (rq_data_dir(rq) == READ) hdr->din_resid = sreq->resid_len; - } else { + else hdr->dout_resid = sreq->resid_len; - } return ret; } @@ -140,8 +141,8 @@ static const struct bsg_ops bsg_scsi_ops = { static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) { - struct request *rq, *next_rq = NULL; - struct bio *bio, *bidi_bio = NULL; + struct request *rq; + struct bio *bio; struct sg_io_v4 hdr; int ret; @@ -164,7 +165,7 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) ret = q->bsg_dev.ops->fill_hdr(rq, &hdr, mode); if (ret) - goto out; + return ret; rq->timeout = msecs_to_jiffies(hdr.timeout); if (!rq->timeout) @@ -174,29 +175,6 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) if (rq->timeout < BLK_MIN_SG_TIMEOUT) rq->timeout = BLK_MIN_SG_TIMEOUT; - if (hdr.dout_xfer_len && hdr.din_xfer_len) { - if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) { - ret = -EOPNOTSUPP; - goto out; - } - - pr_warn_once( - "BIDI support in bsg has been deprecated and might be removed. " - "Please report your use case to linux-scsi@vger.kernel.org\n"); - - next_rq = blk_get_request(q, REQ_OP_SCSI_IN, 0); - if (IS_ERR(next_rq)) { - ret = PTR_ERR(next_rq); - goto out; - } - - rq->next_rq = next_rq; - ret = blk_rq_map_user(q, next_rq, NULL, uptr64(hdr.din_xferp), - hdr.din_xfer_len, GFP_KERNEL); - if (ret) - goto out_free_nextrq; - } - if (hdr.dout_xfer_len) { ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.dout_xferp), hdr.dout_xfer_len, GFP_KERNEL); @@ -206,38 +184,20 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) } if (ret) - goto out_unmap_nextrq; + goto out_free_rq; bio = rq->bio; - if (rq->next_rq) - bidi_bio = rq->next_rq->bio; blk_execute_rq(q, NULL, rq, !(hdr.flags & BSG_FLAG_Q_AT_TAIL)); ret = rq->q->bsg_dev.ops->complete_rq(rq, &hdr); - - if (rq->next_rq) { - blk_rq_unmap_user(bidi_bio); - blk_put_request(rq->next_rq); - } - blk_rq_unmap_user(bio); + +out_free_rq: rq->q->bsg_dev.ops->free_rq(rq); blk_put_request(rq); - - if (copy_to_user(uarg, &hdr, sizeof(hdr))) + if (!ret && copy_to_user(uarg, &hdr, sizeof(hdr))) return -EFAULT; return ret; - -out_unmap_nextrq: - if (rq->next_rq) - blk_rq_unmap_user(rq->next_rq->bio); -out_free_nextrq: - if (rq->next_rq) - blk_put_request(rq->next_rq); -out: - q->bsg_dev.ops->free_rq(rq); - blk_put_request(rq); - return ret; } static struct bsg_device *bsg_alloc_device(void) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 692b46937e52..60f1a81d2034 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -213,7 +213,6 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) to_sas_host_attrs(shost)->q = q; } - blk_queue_flag_set(QUEUE_FLAG_BIDI, q); return 0; } diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h index b356e0006731..7f14517a559b 100644 --- a/include/linux/bsg-lib.h +++ b/include/linux/bsg-lib.h @@ -69,6 +69,10 @@ struct bsg_job { int result; unsigned int reply_payload_rcv_len; + /* BIDI support */ + struct request *bidi_rq; + struct bio *bidi_bio; + void *dd_data; /* Used for driver-specific storage */ }; From 80f2121380caa14895638b24b81800158c0844f2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 29 Jan 2019 09:32:30 +0100 Subject: [PATCH 1183/4277] scsi: fs: remove exofs This was an example for using the SCSI OSD protocol, which we're trying to remove. Signed-off-by: Christoph Hellwig Reviewed-by: Jens Axboe Signed-off-by: Martin K. Petersen --- Documentation/filesystems/exofs.txt | 185 ---- Documentation/scsi/osd.txt | 5 - MAINTAINERS | 1 - fs/Kconfig | 3 - fs/Makefile | 1 - fs/exofs/BUGS | 3 - fs/exofs/Kbuild | 20 - fs/exofs/Kconfig | 13 - fs/exofs/Kconfig.ore | 14 - fs/exofs/common.h | 262 ----- fs/exofs/dir.c | 661 ------------ fs/exofs/exofs.h | 240 ----- fs/exofs/file.c | 83 -- fs/exofs/inode.c | 1514 --------------------------- fs/exofs/namei.c | 323 ------ fs/exofs/ore.c | 1178 --------------------- fs/exofs/ore_raid.c | 756 ------------- fs/exofs/ore_raid.h | 62 -- fs/exofs/super.c | 1071 ------------------- fs/exofs/sys.c | 205 ---- 20 files changed, 6600 deletions(-) delete mode 100644 Documentation/filesystems/exofs.txt delete mode 100644 fs/exofs/BUGS delete mode 100644 fs/exofs/Kbuild delete mode 100644 fs/exofs/Kconfig delete mode 100644 fs/exofs/Kconfig.ore delete mode 100644 fs/exofs/common.h delete mode 100644 fs/exofs/dir.c delete mode 100644 fs/exofs/exofs.h delete mode 100644 fs/exofs/file.c delete mode 100644 fs/exofs/inode.c delete mode 100644 fs/exofs/namei.c delete mode 100644 fs/exofs/ore.c delete mode 100644 fs/exofs/ore_raid.c delete mode 100644 fs/exofs/ore_raid.h delete mode 100644 fs/exofs/super.c delete mode 100644 fs/exofs/sys.c diff --git a/Documentation/filesystems/exofs.txt b/Documentation/filesystems/exofs.txt deleted file mode 100644 index 23583a136975..000000000000 --- a/Documentation/filesystems/exofs.txt +++ /dev/null @@ -1,185 +0,0 @@ -=============================================================================== -WHAT IS EXOFS? -=============================================================================== - -exofs is a file system that uses an OSD and exports the API of a normal Linux -file system. Users access exofs like any other local file system, and exofs -will in turn issue commands to the local OSD initiator. - -OSD is a new T10 command set that views storage devices not as a large/flat -array of sectors but as a container of objects, each having a length, quota, -time attributes and more. Each object is addressed by a 64bit ID, and is -contained in a 64bit ID partition. Each object has associated attributes -attached to it, which are integral part of the object and provide metadata about -the object. The standard defines some common obligatory attributes, but user -attributes can be added as needed. - -=============================================================================== -ENVIRONMENT -=============================================================================== - -To use this file system, you need to have an object store to run it on. You -may download a target from: -http://open-osd.org - -See Documentation/scsi/osd.txt for how to setup a working osd environment. - -=============================================================================== -USAGE -=============================================================================== - -1. Download and compile exofs and open-osd initiator: - You need an external Kernel source tree or kernel headers from your - distribution. (anything based on 2.6.26 or later). - - a. download open-osd including exofs source using: - [parent-directory]$ git clone git://git.open-osd.org/open-osd.git - - b. Build the library module like this: - [parent-directory]$ make -C KSRC=$(KER_DIR) open-osd - - This will build both the open-osd initiator as well as the exofs kernel - module. Use whatever parameters you compiled your Kernel with and - $(KER_DIR) above pointing to the Kernel you compile against. See the file - open-osd/top-level-Makefile for an example. - -2. Get the OSD initiator and target set up properly, and login to the target. - See Documentation/scsi/osd.txt for farther instructions. Also see ./do-osd - for example script that does all these steps. - -3. Insmod the exofs.ko module: - [exofs]$ insmod exofs.ko - -4. Make sure the directory where you want to mount exists. If not, create it. - (For example, mkdir /mnt/exofs) - -5. At first run you will need to invoke the mkfs.exofs application - - As an example, this will create the file system on: - /dev/osd0 partition ID 65536 - - mkfs.exofs --pid=65536 --format /dev/osd0 - - The --format is optional. If not specified, no OSD_FORMAT will be - performed and a clean file system will be created in the specified pid, - in the available space of the target. (Use --format=size_in_meg to limit - the total LUN space available) - - If pid already exists, it will be deleted and a new one will be created in - its place. Be careful. - - An exofs lives inside a single OSD partition. You can create multiple exofs - filesystems on the same device using multiple pids. - - (run mkfs.exofs without any parameters for usage help message) - -6. Mount the file system. - - For example, to mount /dev/osd0, partition ID 0x10000 on /mnt/exofs: - - mount -t exofs -o pid=65536 /dev/osd0 /mnt/exofs/ - -7. For reference (See do-exofs example script): - do-exofs start - an example of how to perform the above steps. - do-exofs stop - an example of how to unmount the file system. - do-exofs format - an example of how to format and mkfs a new exofs. - -8. Extra compilation flags (uncomment in fs/exofs/Kbuild): - CONFIG_EXOFS_DEBUG - for debug messages and extra checks. - -=============================================================================== -exofs mount options -=============================================================================== -Similar to any mount command: - mount -t exofs -o exofs_options /dev/osdX mount_exofs_directory - -Where: - -t exofs: specifies the exofs file system - - /dev/osdX: X is a decimal number. /dev/osdX was created after a successful - login into an OSD target. - - mount_exofs_directory: The directory to mount the file system on - - exofs specific options: Options are separated by commas (,) - pid= - The partition number to mount/create as - container of the filesystem. - This option is mandatory. integer can be - Hex by pre-pending an 0x to the number. - osdname= - Mount by a device's osdname. - osdname is usually a 36 character uuid of the - form "d2683732-c906-4ee1-9dbd-c10c27bb40df". - It is one of the device's uuid specified in the - mkfs.exofs format command. - If this option is specified then the /dev/osdX - above can be empty and is ignored. - to= - Timeout in ticks for a single command. - default is (60 * HZ) [for debugging only] - -=============================================================================== -DESIGN -=============================================================================== - -* The file system control block (AKA on-disk superblock) resides in an object - with a special ID (defined in common.h). - Information included in the file system control block is used to fill the - in-memory superblock structure at mount time. This object is created before - the file system is used by mkexofs.c. It contains information such as: - - The file system's magic number - - The next inode number to be allocated - -* Each file resides in its own object and contains the data (and it will be - possible to extend the file over multiple objects, though this has not been - implemented yet). - -* A directory is treated as a file, and essentially contains a list of pairs for files that are found in that directory. The object - IDs correspond to the files' inode numbers and will be allocated according to - a bitmap (stored in a separate object). Now they are allocated using a - counter. - -* Each file's control block (AKA on-disk inode) is stored in its object's - attributes. This applies to both regular files and other types (directories, - device files, symlinks, etc.). - -* Credentials are generated per object (inode and superblock) when they are - created in memory (read from disk or created). The credential works for all - operations and is used as long as the object remains in memory. - -* Async OSD operations are used whenever possible, but the target may execute - them out of order. The operations that concern us are create, delete, - readpage, writepage, update_inode, and truncate. The following pairs of - operations should execute in the order written, and we need to prevent them - from executing in reverse order: - - The following are handled with the OBJ_CREATED and OBJ_2BCREATED - flags. OBJ_CREATED is set when we know the object exists on the OSD - - in create's callback function, and when we successfully do a - read_inode. - OBJ_2BCREATED is set in the beginning of the create function, so we - know that we should wait. - - create/delete: delete should wait until the object is created - on the OSD. - - create/readpage: readpage should be able to return a page - full of zeroes in this case. If there was a write already - en-route (i.e. create, writepage, readpage) then the page - would be locked, and so it would really be the same as - create/writepage. - - create/writepage: if writepage is called for a sync write, it - should wait until the object is created on the OSD. - Otherwise, it should just return. - - create/truncate: truncate should wait until the object is - created on the OSD. - - create/update_inode: update_inode should wait until the - object is created on the OSD. - - Handled by VFS locks: - - readpage/delete: shouldn't happen because of page lock. - - writepage/delete: shouldn't happen because of page lock. - - readpage/writepage: shouldn't happen because of page lock. - -=============================================================================== -LICENSE/COPYRIGHT -=============================================================================== -The exofs file system is based on ext2 v0.5b (distributed with the Linux kernel -version 2.6.10). All files include the original copyrights, and the license -is GPL version 2 (only version 2, as is true for the Linux kernel). The -Linux kernel can be downloaded from www.kernel.org. diff --git a/Documentation/scsi/osd.txt b/Documentation/scsi/osd.txt index 5a9879bad073..2bc2ab06b0c0 100644 --- a/Documentation/scsi/osd.txt +++ b/Documentation/scsi/osd.txt @@ -24,11 +24,6 @@ osd-uld: platform, both for the in-kernel initiator as well as connected targets. It currently has no useful user-mode API, though it could have if need be. -exofs: - Is an OSD based Linux file system. It uses the osd-initiator and osd-uld, -to export a usable file system for users. -See Documentation/filesystems/exofs.txt for more details - osd target: There are no current plans for an OSD target implementation in kernel. For all needs, a user-mode target that is based on the scsi tgt target framework is diff --git a/MAINTAINERS b/MAINTAINERS index 6c445a485804..108b340ab625 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11390,7 +11390,6 @@ M: Boaz Harrosh S: Maintained F: drivers/scsi/osd/ F: include/scsi/osd_* -F: fs/exofs/ OV2659 OMNIVISION SENSOR DRIVER M: "Lad, Prabhakar" diff --git a/fs/Kconfig b/fs/Kconfig index ac474a61be37..2557506051a3 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -254,12 +254,9 @@ source "fs/romfs/Kconfig" source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" -source "fs/exofs/Kconfig" endif # MISC_FILESYSTEMS -source "fs/exofs/Kconfig.ore" - menuconfig NETWORK_FILESYSTEMS bool "Network File Systems" default y diff --git a/fs/Makefile b/fs/Makefile index 293733f61594..4a930ee78d68 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -124,7 +124,6 @@ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ obj-$(CONFIG_F2FS_FS) += f2fs/ -obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ diff --git a/fs/exofs/BUGS b/fs/exofs/BUGS deleted file mode 100644 index 1b2d4c63a579..000000000000 --- a/fs/exofs/BUGS +++ /dev/null @@ -1,3 +0,0 @@ -- Out-of-space may cause a severe problem if the object (and directory entry) - were written, but the inode attributes failed. Then if the filesystem was - unmounted and mounted the kernel can get into an endless loop doing a readdir. diff --git a/fs/exofs/Kbuild b/fs/exofs/Kbuild deleted file mode 100644 index a364fd0965ec..000000000000 --- a/fs/exofs/Kbuild +++ /dev/null @@ -1,20 +0,0 @@ -# -# Kbuild for the EXOFS module -# -# Copyright (C) 2008 Panasas Inc. All rights reserved. -# -# Authors: -# Boaz Harrosh -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# -# Kbuild - Gets included from the Kernels Makefile and build system -# - -# ore module library -libore-y := ore.o ore_raid.o -obj-$(CONFIG_ORE) += libore.o - -exofs-y := inode.o file.o namei.o dir.o super.o sys.o -obj-$(CONFIG_EXOFS_FS) += exofs.o diff --git a/fs/exofs/Kconfig b/fs/exofs/Kconfig deleted file mode 100644 index 86194b2f799d..000000000000 --- a/fs/exofs/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config EXOFS_FS - tristate "exofs: OSD based file system support" - depends on SCSI_OSD_ULD - help - EXOFS is a file system that uses an OSD storage device, - as its backing storage. - -# Debugging-related stuff -config EXOFS_DEBUG - bool "Enable debugging" - depends on EXOFS_FS - help - This option enables EXOFS debug prints. diff --git a/fs/exofs/Kconfig.ore b/fs/exofs/Kconfig.ore deleted file mode 100644 index 2daf2329c28d..000000000000 --- a/fs/exofs/Kconfig.ore +++ /dev/null @@ -1,14 +0,0 @@ -# ORE - Objects Raid Engine (libore.ko) -# -# Note ORE needs to "select ASYNC_XOR". So Not to force multiple selects -# for every ORE user we do it like this. Any user should add itself here -# at the "depends on EXOFS_FS || ..." with an ||. The dependencies are -# selected here, and we default to "ON". So in effect it is like been -# selected by any of the users. -config ORE - tristate - depends on EXOFS_FS || PNFS_OBJLAYOUT - select ASYNC_XOR - select RAID6_PQ - select ASYNC_PQ - default SCSI_OSD_ULD diff --git a/fs/exofs/common.h b/fs/exofs/common.h deleted file mode 100644 index 7d88ef566213..000000000000 --- a/fs/exofs/common.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * common.h - Common definitions for both Kernel and user-mode utilities - * - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __EXOFS_COM_H__ -#define __EXOFS_COM_H__ - -#include - -#include -#include -#include - -/**************************************************************************** - * Object ID related defines - * NOTE: inode# = object ID - EXOFS_OBJ_OFF - ****************************************************************************/ -#define EXOFS_MIN_PID 0x10000 /* Smallest partition ID */ -#define EXOFS_OBJ_OFF 0x10000 /* offset for objects */ -#define EXOFS_SUPER_ID 0x10000 /* object ID for on-disk superblock */ -#define EXOFS_DEVTABLE_ID 0x10001 /* object ID for on-disk device table */ -#define EXOFS_ROOT_ID 0x10002 /* object ID for root directory */ - -/* exofs Application specific page/attribute */ -/* Inode attrs */ -# define EXOFS_APAGE_FS_DATA (OSD_APAGE_APP_DEFINED_FIRST + 3) -# define EXOFS_ATTR_INODE_DATA 1 -# define EXOFS_ATTR_INODE_FILE_LAYOUT 2 -# define EXOFS_ATTR_INODE_DIR_LAYOUT 3 -/* Partition attrs */ -# define EXOFS_APAGE_SB_DATA (0xF0000000U + 3) -# define EXOFS_ATTR_SB_STATS 1 - -/* - * The maximum number of files we can have is limited by the size of the - * inode number. This is the largest object ID that the file system supports. - * Object IDs 0, 1, and 2 are always in use (see above defines). - */ -enum { - EXOFS_MAX_INO_ID = (sizeof(ino_t) * 8 == 64) ? ULLONG_MAX : - (1ULL << (sizeof(ino_t) * 8ULL - 1ULL)), - EXOFS_MAX_ID = (EXOFS_MAX_INO_ID - 1 - EXOFS_OBJ_OFF), -}; - -/**************************************************************************** - * Misc. - ****************************************************************************/ -#define EXOFS_BLKSHIFT 12 -#define EXOFS_BLKSIZE (1UL << EXOFS_BLKSHIFT) - -/**************************************************************************** - * superblock-related things - ****************************************************************************/ -#define EXOFS_SUPER_MAGIC 0x5DF5 - -/* - * The file system control block - stored in object EXOFS_SUPER_ID's data. - * This is where the in-memory superblock is stored on disk. - */ -enum {EXOFS_FSCB_VER = 1, EXOFS_DT_VER = 1}; -struct exofs_fscb { - __le64 s_nextid; /* Only used after mkfs */ - __le64 s_numfiles; /* Only used after mkfs */ - __le32 s_version; /* == EXOFS_FSCB_VER */ - __le16 s_magic; /* Magic signature */ - __le16 s_newfs; /* Non-zero if this is a new fs */ - - /* From here on it's a static part, only written by mkexofs */ - __le64 s_dev_table_oid; /* Resurved, not used */ - __le64 s_dev_table_count; /* == 0 means no dev_table */ -} __packed; - -/* - * This struct is set on the FS partition's attributes. - * [EXOFS_APAGE_SB_DATA, EXOFS_ATTR_SB_STATS] and is written together - * with the create command, to atomically persist the sb writeable information. - */ -struct exofs_sb_stats { - __le64 s_nextid; /* Highest object ID used */ - __le64 s_numfiles; /* Number of files on fs */ -} __packed; - -/* - * Describes the raid used in the FS. It is part of the device table. - * This here is taken from the pNFS-objects definition. In exofs we - * use one raid policy through-out the filesystem. (NOTE: the funny - * alignment at beginning. We take care of it at exofs_device_table. - */ -struct exofs_dt_data_map { - __le32 cb_num_comps; - __le64 cb_stripe_unit; - __le32 cb_group_width; - __le32 cb_group_depth; - __le32 cb_mirror_cnt; - __le32 cb_raid_algorithm; -} __packed; - -/* - * This is an osd device information descriptor. It is a single entry in - * the exofs device table. It describes an osd target lun which - * contains data belonging to this FS. (Same partition_id on all devices) - */ -struct exofs_dt_device_info { - __le32 systemid_len; - u8 systemid[OSD_SYSTEMID_LEN]; - __le64 long_name_offset; /* If !0 then offset-in-file */ - __le32 osdname_len; /* */ - u8 osdname[44]; /* Embbeded, Usually an asci uuid */ -} __packed; - -/* - * The EXOFS device table - stored in object EXOFS_DEVTABLE_ID's data. - * It contains the raid used for this multy-device FS and an array of - * participating devices. - */ -struct exofs_device_table { - __le32 dt_version; /* == EXOFS_DT_VER */ - struct exofs_dt_data_map dt_data_map; /* Raid policy to use */ - - /* Resurved space For future use. Total includeing this: - * (8 * sizeof(le64)) - */ - __le64 __Resurved[4]; - - __le64 dt_num_devices; /* Array size */ - struct exofs_dt_device_info dt_dev_table[]; /* Array of devices */ -} __packed; - -/**************************************************************************** - * inode-related things - ****************************************************************************/ -#define EXOFS_IDATA 5 - -/* - * The file control block - stored in an object's attributes. This is where - * the in-memory inode is stored on disk. - */ -struct exofs_fcb { - __le64 i_size; /* Size of the file */ - __le16 i_mode; /* File mode */ - __le16 i_links_count; /* Links count */ - __le32 i_uid; /* Owner Uid */ - __le32 i_gid; /* Group Id */ - __le32 i_atime; /* Access time */ - __le32 i_ctime; /* Creation time */ - __le32 i_mtime; /* Modification time */ - __le32 i_flags; /* File flags (unused for now)*/ - __le32 i_generation; /* File version (for NFS) */ - __le32 i_data[EXOFS_IDATA]; /* Short symlink names and device #s */ -}; - -#define EXOFS_INO_ATTR_SIZE sizeof(struct exofs_fcb) - -/* This is the Attribute the fcb is stored in */ -static const struct __weak osd_attr g_attr_inode_data = ATTR_DEF( - EXOFS_APAGE_FS_DATA, - EXOFS_ATTR_INODE_DATA, - EXOFS_INO_ATTR_SIZE); - -/**************************************************************************** - * dentry-related things - ****************************************************************************/ -#define EXOFS_NAME_LEN 255 - -/* - * The on-disk directory entry - */ -struct exofs_dir_entry { - __le64 inode_no; /* inode number */ - __le16 rec_len; /* directory entry length */ - u8 name_len; /* name length */ - u8 file_type; /* umm...file type */ - char name[EXOFS_NAME_LEN]; /* file name */ -}; - -enum { - EXOFS_FT_UNKNOWN, - EXOFS_FT_REG_FILE, - EXOFS_FT_DIR, - EXOFS_FT_CHRDEV, - EXOFS_FT_BLKDEV, - EXOFS_FT_FIFO, - EXOFS_FT_SOCK, - EXOFS_FT_SYMLINK, - EXOFS_FT_MAX -}; - -#define EXOFS_DIR_PAD 4 -#define EXOFS_DIR_ROUND (EXOFS_DIR_PAD - 1) -#define EXOFS_DIR_REC_LEN(name_len) \ - (((name_len) + offsetof(struct exofs_dir_entry, name) + \ - EXOFS_DIR_ROUND) & ~EXOFS_DIR_ROUND) - -/* - * The on-disk (optional) layout structure. - * sits in an EXOFS_ATTR_INODE_FILE_LAYOUT or EXOFS_ATTR_INODE_DIR_LAYOUT - * attribute, attached to any inode, usually to a directory. - */ - -enum exofs_inode_layout_gen_functions { - LAYOUT_MOVING_WINDOW = 0, - LAYOUT_IMPLICT = 1, -}; - -struct exofs_on_disk_inode_layout { - __le16 gen_func; /* One of enum exofs_inode_layout_gen_functions */ - __le16 pad; - union { - /* gen_func == LAYOUT_MOVING_WINDOW (default) */ - struct exofs_layout_sliding_window { - __le32 num_devices; /* first n devices in global-table*/ - } sliding_window __packed; - - /* gen_func == LAYOUT_IMPLICT */ - struct exofs_layout_implict_list { - struct exofs_dt_data_map data_map; - /* Variable array of size data_map.cb_num_comps. These - * are device indexes of the devices in the global table - */ - __le32 dev_indexes[]; - } implict __packed; - }; -} __packed; - -static inline size_t exofs_on_disk_inode_layout_size(unsigned max_devs) -{ - return sizeof(struct exofs_on_disk_inode_layout) + - max_devs * sizeof(__le32); -} - -#endif /*ifndef __EXOFS_COM_H__*/ diff --git a/fs/exofs/dir.c b/fs/exofs/dir.c deleted file mode 100644 index f0138674c1ed..000000000000 --- a/fs/exofs/dir.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "exofs.h" - -static inline unsigned exofs_chunk_size(struct inode *inode) -{ - return inode->i_sb->s_blocksize; -} - -static inline void exofs_put_page(struct page *page) -{ - kunmap(page); - put_page(page); -} - -static unsigned exofs_last_byte(struct inode *inode, unsigned long page_nr) -{ - loff_t last_byte = inode->i_size; - - last_byte -= page_nr << PAGE_SHIFT; - if (last_byte > PAGE_SIZE) - last_byte = PAGE_SIZE; - return last_byte; -} - -static int exofs_commit_chunk(struct page *page, loff_t pos, unsigned len) -{ - struct address_space *mapping = page->mapping; - struct inode *dir = mapping->host; - int err = 0; - - inode_inc_iversion(dir); - - if (!PageUptodate(page)) - SetPageUptodate(page); - - if (pos+len > dir->i_size) { - i_size_write(dir, pos+len); - mark_inode_dirty(dir); - } - set_page_dirty(page); - - if (IS_DIRSYNC(dir)) - err = write_one_page(page); - else - unlock_page(page); - - return err; -} - -static bool exofs_check_page(struct page *page) -{ - struct inode *dir = page->mapping->host; - unsigned chunk_size = exofs_chunk_size(dir); - char *kaddr = page_address(page); - unsigned offs, rec_len; - unsigned limit = PAGE_SIZE; - struct exofs_dir_entry *p; - char *error; - - /* if the page is the last one in the directory */ - if ((dir->i_size >> PAGE_SHIFT) == page->index) { - limit = dir->i_size & ~PAGE_MASK; - if (limit & (chunk_size - 1)) - goto Ebadsize; - if (!limit) - goto out; - } - for (offs = 0; offs <= limit - EXOFS_DIR_REC_LEN(1); offs += rec_len) { - p = (struct exofs_dir_entry *)(kaddr + offs); - rec_len = le16_to_cpu(p->rec_len); - - if (rec_len < EXOFS_DIR_REC_LEN(1)) - goto Eshort; - if (rec_len & 3) - goto Ealign; - if (rec_len < EXOFS_DIR_REC_LEN(p->name_len)) - goto Enamelen; - if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) - goto Espan; - } - if (offs != limit) - goto Eend; -out: - SetPageChecked(page); - return true; - -Ebadsize: - EXOFS_ERR("ERROR [exofs_check_page]: " - "size of directory(0x%lx) is not a multiple of chunk size\n", - dir->i_ino - ); - goto fail; -Eshort: - error = "rec_len is smaller than minimal"; - goto bad_entry; -Ealign: - error = "unaligned directory entry"; - goto bad_entry; -Enamelen: - error = "rec_len is too small for name_len"; - goto bad_entry; -Espan: - error = "directory entry across blocks"; - goto bad_entry; -bad_entry: - EXOFS_ERR( - "ERROR [exofs_check_page]: bad entry in directory(0x%lx): %s - " - "offset=%lu, inode=0x%llx, rec_len=%d, name_len=%d\n", - dir->i_ino, error, (page->index<inode_no)), - rec_len, p->name_len); - goto fail; -Eend: - p = (struct exofs_dir_entry *)(kaddr + offs); - EXOFS_ERR("ERROR [exofs_check_page]: " - "entry in directory(0x%lx) spans the page boundary" - "offset=%lu, inode=0x%llx\n", - dir->i_ino, (page->index<inode_no))); -fail: - SetPageError(page); - return false; -} - -static struct page *exofs_get_page(struct inode *dir, unsigned long n) -{ - struct address_space *mapping = dir->i_mapping; - struct page *page = read_mapping_page(mapping, n, NULL); - - if (!IS_ERR(page)) { - kmap(page); - if (unlikely(!PageChecked(page))) { - if (PageError(page) || !exofs_check_page(page)) - goto fail; - } - } - return page; - -fail: - exofs_put_page(page); - return ERR_PTR(-EIO); -} - -static inline int exofs_match(int len, const unsigned char *name, - struct exofs_dir_entry *de) -{ - if (len != de->name_len) - return 0; - if (!de->inode_no) - return 0; - return !memcmp(name, de->name, len); -} - -static inline -struct exofs_dir_entry *exofs_next_entry(struct exofs_dir_entry *p) -{ - return (struct exofs_dir_entry *)((char *)p + le16_to_cpu(p->rec_len)); -} - -static inline unsigned -exofs_validate_entry(char *base, unsigned offset, unsigned mask) -{ - struct exofs_dir_entry *de = (struct exofs_dir_entry *)(base + offset); - struct exofs_dir_entry *p = - (struct exofs_dir_entry *)(base + (offset&mask)); - while ((char *)p < (char *)de) { - if (p->rec_len == 0) - break; - p = exofs_next_entry(p); - } - return (char *)p - base; -} - -static unsigned char exofs_filetype_table[EXOFS_FT_MAX] = { - [EXOFS_FT_UNKNOWN] = DT_UNKNOWN, - [EXOFS_FT_REG_FILE] = DT_REG, - [EXOFS_FT_DIR] = DT_DIR, - [EXOFS_FT_CHRDEV] = DT_CHR, - [EXOFS_FT_BLKDEV] = DT_BLK, - [EXOFS_FT_FIFO] = DT_FIFO, - [EXOFS_FT_SOCK] = DT_SOCK, - [EXOFS_FT_SYMLINK] = DT_LNK, -}; - -#define S_SHIFT 12 -static unsigned char exofs_type_by_mode[S_IFMT >> S_SHIFT] = { - [S_IFREG >> S_SHIFT] = EXOFS_FT_REG_FILE, - [S_IFDIR >> S_SHIFT] = EXOFS_FT_DIR, - [S_IFCHR >> S_SHIFT] = EXOFS_FT_CHRDEV, - [S_IFBLK >> S_SHIFT] = EXOFS_FT_BLKDEV, - [S_IFIFO >> S_SHIFT] = EXOFS_FT_FIFO, - [S_IFSOCK >> S_SHIFT] = EXOFS_FT_SOCK, - [S_IFLNK >> S_SHIFT] = EXOFS_FT_SYMLINK, -}; - -static inline -void exofs_set_de_type(struct exofs_dir_entry *de, struct inode *inode) -{ - umode_t mode = inode->i_mode; - de->file_type = exofs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; -} - -static int -exofs_readdir(struct file *file, struct dir_context *ctx) -{ - loff_t pos = ctx->pos; - struct inode *inode = file_inode(file); - unsigned int offset = pos & ~PAGE_MASK; - unsigned long n = pos >> PAGE_SHIFT; - unsigned long npages = dir_pages(inode); - unsigned chunk_mask = ~(exofs_chunk_size(inode)-1); - bool need_revalidate = !inode_eq_iversion(inode, file->f_version); - - if (pos > inode->i_size - EXOFS_DIR_REC_LEN(1)) - return 0; - - for ( ; n < npages; n++, offset = 0) { - char *kaddr, *limit; - struct exofs_dir_entry *de; - struct page *page = exofs_get_page(inode, n); - - if (IS_ERR(page)) { - EXOFS_ERR("ERROR: bad page in directory(0x%lx)\n", - inode->i_ino); - ctx->pos += PAGE_SIZE - offset; - return PTR_ERR(page); - } - kaddr = page_address(page); - if (unlikely(need_revalidate)) { - if (offset) { - offset = exofs_validate_entry(kaddr, offset, - chunk_mask); - ctx->pos = (n<f_version = inode_query_iversion(inode); - need_revalidate = false; - } - de = (struct exofs_dir_entry *)(kaddr + offset); - limit = kaddr + exofs_last_byte(inode, n) - - EXOFS_DIR_REC_LEN(1); - for (; (char *)de <= limit; de = exofs_next_entry(de)) { - if (de->rec_len == 0) { - EXOFS_ERR("ERROR: " - "zero-length entry in directory(0x%lx)\n", - inode->i_ino); - exofs_put_page(page); - return -EIO; - } - if (de->inode_no) { - unsigned char t; - - if (de->file_type < EXOFS_FT_MAX) - t = exofs_filetype_table[de->file_type]; - else - t = DT_UNKNOWN; - - if (!dir_emit(ctx, de->name, de->name_len, - le64_to_cpu(de->inode_no), - t)) { - exofs_put_page(page); - return 0; - } - } - ctx->pos += le16_to_cpu(de->rec_len); - } - exofs_put_page(page); - } - return 0; -} - -struct exofs_dir_entry *exofs_find_entry(struct inode *dir, - struct dentry *dentry, struct page **res_page) -{ - const unsigned char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; - unsigned reclen = EXOFS_DIR_REC_LEN(namelen); - unsigned long start, n; - unsigned long npages = dir_pages(dir); - struct page *page = NULL; - struct exofs_i_info *oi = exofs_i(dir); - struct exofs_dir_entry *de; - - if (npages == 0) - goto out; - - *res_page = NULL; - - start = oi->i_dir_start_lookup; - if (start >= npages) - start = 0; - n = start; - do { - char *kaddr; - page = exofs_get_page(dir, n); - if (!IS_ERR(page)) { - kaddr = page_address(page); - de = (struct exofs_dir_entry *) kaddr; - kaddr += exofs_last_byte(dir, n) - reclen; - while ((char *) de <= kaddr) { - if (de->rec_len == 0) { - EXOFS_ERR("ERROR: zero-length entry in " - "directory(0x%lx)\n", - dir->i_ino); - exofs_put_page(page); - goto out; - } - if (exofs_match(namelen, name, de)) - goto found; - de = exofs_next_entry(de); - } - exofs_put_page(page); - } - if (++n >= npages) - n = 0; - } while (n != start); -out: - return NULL; - -found: - *res_page = page; - oi->i_dir_start_lookup = n; - return de; -} - -struct exofs_dir_entry *exofs_dotdot(struct inode *dir, struct page **p) -{ - struct page *page = exofs_get_page(dir, 0); - struct exofs_dir_entry *de = NULL; - - if (!IS_ERR(page)) { - de = exofs_next_entry( - (struct exofs_dir_entry *)page_address(page)); - *p = page; - } - return de; -} - -ino_t exofs_parent_ino(struct dentry *child) -{ - struct page *page; - struct exofs_dir_entry *de; - ino_t ino; - - de = exofs_dotdot(d_inode(child), &page); - if (!de) - return 0; - - ino = le64_to_cpu(de->inode_no); - exofs_put_page(page); - return ino; -} - -ino_t exofs_inode_by_name(struct inode *dir, struct dentry *dentry) -{ - ino_t res = 0; - struct exofs_dir_entry *de; - struct page *page; - - de = exofs_find_entry(dir, dentry, &page); - if (de) { - res = le64_to_cpu(de->inode_no); - exofs_put_page(page); - } - return res; -} - -int exofs_set_link(struct inode *dir, struct exofs_dir_entry *de, - struct page *page, struct inode *inode) -{ - loff_t pos = page_offset(page) + - (char *) de - (char *) page_address(page); - unsigned len = le16_to_cpu(de->rec_len); - int err; - - lock_page(page); - err = exofs_write_begin(NULL, page->mapping, pos, len, 0, &page, NULL); - if (err) - EXOFS_ERR("exofs_set_link: exofs_write_begin FAILED => %d\n", - err); - - de->inode_no = cpu_to_le64(inode->i_ino); - exofs_set_de_type(de, inode); - if (likely(!err)) - err = exofs_commit_chunk(page, pos, len); - exofs_put_page(page); - dir->i_mtime = dir->i_ctime = current_time(dir); - mark_inode_dirty(dir); - return err; -} - -int exofs_add_link(struct dentry *dentry, struct inode *inode) -{ - struct inode *dir = d_inode(dentry->d_parent); - const unsigned char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; - unsigned chunk_size = exofs_chunk_size(dir); - unsigned reclen = EXOFS_DIR_REC_LEN(namelen); - unsigned short rec_len, name_len; - struct page *page = NULL; - struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; - struct exofs_dir_entry *de; - unsigned long npages = dir_pages(dir); - unsigned long n; - char *kaddr; - loff_t pos; - int err; - - for (n = 0; n <= npages; n++) { - char *dir_end; - - page = exofs_get_page(dir, n); - err = PTR_ERR(page); - if (IS_ERR(page)) - goto out; - lock_page(page); - kaddr = page_address(page); - dir_end = kaddr + exofs_last_byte(dir, n); - de = (struct exofs_dir_entry *)kaddr; - kaddr += PAGE_SIZE - reclen; - while ((char *)de <= kaddr) { - if ((char *)de == dir_end) { - name_len = 0; - rec_len = chunk_size; - de->rec_len = cpu_to_le16(chunk_size); - de->inode_no = 0; - goto got_it; - } - if (de->rec_len == 0) { - EXOFS_ERR("ERROR: exofs_add_link: " - "zero-length entry in directory(0x%lx)\n", - inode->i_ino); - err = -EIO; - goto out_unlock; - } - err = -EEXIST; - if (exofs_match(namelen, name, de)) - goto out_unlock; - name_len = EXOFS_DIR_REC_LEN(de->name_len); - rec_len = le16_to_cpu(de->rec_len); - if (!de->inode_no && rec_len >= reclen) - goto got_it; - if (rec_len >= name_len + reclen) - goto got_it; - de = (struct exofs_dir_entry *) ((char *) de + rec_len); - } - unlock_page(page); - exofs_put_page(page); - } - - EXOFS_ERR("exofs_add_link: BAD dentry=%p or inode=0x%lx\n", - dentry, inode->i_ino); - return -EINVAL; - -got_it: - pos = page_offset(page) + - (char *)de - (char *)page_address(page); - err = exofs_write_begin(NULL, page->mapping, pos, rec_len, 0, - &page, NULL); - if (err) - goto out_unlock; - if (de->inode_no) { - struct exofs_dir_entry *de1 = - (struct exofs_dir_entry *)((char *)de + name_len); - de1->rec_len = cpu_to_le16(rec_len - name_len); - de->rec_len = cpu_to_le16(name_len); - de = de1; - } - de->name_len = namelen; - memcpy(de->name, name, namelen); - de->inode_no = cpu_to_le64(inode->i_ino); - exofs_set_de_type(de, inode); - err = exofs_commit_chunk(page, pos, rec_len); - dir->i_mtime = dir->i_ctime = current_time(dir); - mark_inode_dirty(dir); - sbi->s_numfiles++; - -out_put: - exofs_put_page(page); -out: - return err; -out_unlock: - unlock_page(page); - goto out_put; -} - -int exofs_delete_entry(struct exofs_dir_entry *dir, struct page *page) -{ - struct address_space *mapping = page->mapping; - struct inode *inode = mapping->host; - struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; - char *kaddr = page_address(page); - unsigned from = ((char *)dir - kaddr) & ~(exofs_chunk_size(inode)-1); - unsigned to = ((char *)dir - kaddr) + le16_to_cpu(dir->rec_len); - loff_t pos; - struct exofs_dir_entry *pde = NULL; - struct exofs_dir_entry *de = (struct exofs_dir_entry *) (kaddr + from); - int err; - - while (de < dir) { - if (de->rec_len == 0) { - EXOFS_ERR("ERROR: exofs_delete_entry:" - "zero-length entry in directory(0x%lx)\n", - inode->i_ino); - err = -EIO; - goto out; - } - pde = de; - de = exofs_next_entry(de); - } - if (pde) - from = (char *)pde - (char *)page_address(page); - pos = page_offset(page) + from; - lock_page(page); - err = exofs_write_begin(NULL, page->mapping, pos, to - from, 0, - &page, NULL); - if (err) - EXOFS_ERR("exofs_delete_entry: exofs_write_begin FAILED => %d\n", - err); - if (pde) - pde->rec_len = cpu_to_le16(to - from); - dir->inode_no = 0; - if (likely(!err)) - err = exofs_commit_chunk(page, pos, to - from); - inode->i_ctime = inode->i_mtime = current_time(inode); - mark_inode_dirty(inode); - sbi->s_numfiles--; -out: - exofs_put_page(page); - return err; -} - -/* kept aligned on 4 bytes */ -#define THIS_DIR ".\0\0" -#define PARENT_DIR "..\0" - -int exofs_make_empty(struct inode *inode, struct inode *parent) -{ - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); - unsigned chunk_size = exofs_chunk_size(inode); - struct exofs_dir_entry *de; - int err; - void *kaddr; - - if (!page) - return -ENOMEM; - - err = exofs_write_begin(NULL, page->mapping, 0, chunk_size, 0, - &page, NULL); - if (err) { - unlock_page(page); - goto fail; - } - - kaddr = kmap_atomic(page); - de = (struct exofs_dir_entry *)kaddr; - de->name_len = 1; - de->rec_len = cpu_to_le16(EXOFS_DIR_REC_LEN(1)); - memcpy(de->name, THIS_DIR, sizeof(THIS_DIR)); - de->inode_no = cpu_to_le64(inode->i_ino); - exofs_set_de_type(de, inode); - - de = (struct exofs_dir_entry *)(kaddr + EXOFS_DIR_REC_LEN(1)); - de->name_len = 2; - de->rec_len = cpu_to_le16(chunk_size - EXOFS_DIR_REC_LEN(1)); - de->inode_no = cpu_to_le64(parent->i_ino); - memcpy(de->name, PARENT_DIR, sizeof(PARENT_DIR)); - exofs_set_de_type(de, inode); - kunmap_atomic(kaddr); - err = exofs_commit_chunk(page, 0, chunk_size); -fail: - put_page(page); - return err; -} - -int exofs_empty_dir(struct inode *inode) -{ - struct page *page = NULL; - unsigned long i, npages = dir_pages(inode); - - for (i = 0; i < npages; i++) { - char *kaddr; - struct exofs_dir_entry *de; - page = exofs_get_page(inode, i); - - if (IS_ERR(page)) - continue; - - kaddr = page_address(page); - de = (struct exofs_dir_entry *)kaddr; - kaddr += exofs_last_byte(inode, i) - EXOFS_DIR_REC_LEN(1); - - while ((char *)de <= kaddr) { - if (de->rec_len == 0) { - EXOFS_ERR("ERROR: exofs_empty_dir: " - "zero-length directory entry" - "kaddr=%p, de=%p\n", kaddr, de); - goto not_empty; - } - if (de->inode_no != 0) { - /* check for . and .. */ - if (de->name[0] != '.') - goto not_empty; - if (de->name_len > 2) - goto not_empty; - if (de->name_len < 2) { - if (le64_to_cpu(de->inode_no) != - inode->i_ino) - goto not_empty; - } else if (de->name[1] != '.') - goto not_empty; - } - de = exofs_next_entry(de); - } - exofs_put_page(page); - } - return 1; - -not_empty: - exofs_put_page(page); - return 0; -} - -const struct file_operations exofs_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .iterate_shared = exofs_readdir, -}; diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h deleted file mode 100644 index 5dc392404559..000000000000 --- a/fs/exofs/exofs.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __EXOFS_H__ -#define __EXOFS_H__ - -#include -#include -#include -#include - -#include "common.h" - -#define EXOFS_ERR(fmt, a...) printk(KERN_ERR "exofs: " fmt, ##a) - -#ifdef CONFIG_EXOFS_DEBUG -#define EXOFS_DBGMSG(fmt, a...) \ - printk(KERN_NOTICE "exofs @%s:%d: " fmt, __func__, __LINE__, ##a) -#else -#define EXOFS_DBGMSG(fmt, a...) \ - do { if (0) printk(fmt, ##a); } while (0) -#endif - -/* u64 has problems with printk this will cast it to unsigned long long */ -#define _LLU(x) (unsigned long long)(x) - -struct exofs_dev { - struct ore_dev ored; - unsigned did; - unsigned urilen; - uint8_t *uri; - struct kobject ed_kobj; -}; -/* - * our extension to the in-memory superblock - */ -struct exofs_sb_info { - struct exofs_sb_stats s_ess; /* Written often, pre-allocate*/ - int s_timeout; /* timeout for OSD operations */ - uint64_t s_nextid; /* highest object ID used */ - uint32_t s_numfiles; /* number of files on fs */ - spinlock_t s_next_gen_lock; /* spinlock for gen # update */ - u32 s_next_generation; /* next gen # to use */ - atomic_t s_curr_pending; /* number of pending commands */ - - struct ore_layout layout; /* Default files layout */ - struct ore_comp one_comp; /* id & cred of partition id=0*/ - struct ore_components oc; /* comps for the partition */ - struct kobject s_kobj; /* holds per-sbi kobject */ -}; - -/* - * our extension to the in-memory inode - */ -struct exofs_i_info { - struct inode vfs_inode; /* normal in-memory inode */ - wait_queue_head_t i_wq; /* wait queue for inode */ - unsigned long i_flags; /* various atomic flags */ - uint32_t i_data[EXOFS_IDATA];/*short symlink names and device #s*/ - uint32_t i_dir_start_lookup; /* which page to start lookup */ - uint64_t i_commit_size; /* the object's written length */ - struct ore_comp one_comp; /* same component for all devices */ - struct ore_components oc; /* inode view of the device table */ -}; - -static inline osd_id exofs_oi_objno(struct exofs_i_info *oi) -{ - return oi->vfs_inode.i_ino + EXOFS_OBJ_OFF; -} - -/* - * our inode flags - */ -#define OBJ_2BCREATED 0 /* object will be created soon*/ -#define OBJ_CREATED 1 /* object has been created on the osd*/ - -static inline int obj_2bcreated(struct exofs_i_info *oi) -{ - return test_bit(OBJ_2BCREATED, &oi->i_flags); -} - -static inline void set_obj_2bcreated(struct exofs_i_info *oi) -{ - set_bit(OBJ_2BCREATED, &oi->i_flags); -} - -static inline int obj_created(struct exofs_i_info *oi) -{ - return test_bit(OBJ_CREATED, &oi->i_flags); -} - -static inline void set_obj_created(struct exofs_i_info *oi) -{ - set_bit(OBJ_CREATED, &oi->i_flags); -} - -int __exofs_wait_obj_created(struct exofs_i_info *oi); -static inline int wait_obj_created(struct exofs_i_info *oi) -{ - if (likely(obj_created(oi))) - return 0; - - return __exofs_wait_obj_created(oi); -} - -/* - * get to our inode from the vfs inode - */ -static inline struct exofs_i_info *exofs_i(struct inode *inode) -{ - return container_of(inode, struct exofs_i_info, vfs_inode); -} - -/* - * Maximum count of links to a file - */ -#define EXOFS_LINK_MAX 32000 - -/************************* - * function declarations * - *************************/ - -/* inode.c */ -unsigned exofs_max_io_pages(struct ore_layout *layout, - unsigned expected_pages); -int exofs_setattr(struct dentry *, struct iattr *); -int exofs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata); -extern struct inode *exofs_iget(struct super_block *, unsigned long); -struct inode *exofs_new_inode(struct inode *, umode_t); -extern int exofs_write_inode(struct inode *, struct writeback_control *wbc); -extern void exofs_evict_inode(struct inode *); - -/* dir.c: */ -int exofs_add_link(struct dentry *, struct inode *); -ino_t exofs_inode_by_name(struct inode *, struct dentry *); -int exofs_delete_entry(struct exofs_dir_entry *, struct page *); -int exofs_make_empty(struct inode *, struct inode *); -struct exofs_dir_entry *exofs_find_entry(struct inode *, struct dentry *, - struct page **); -int exofs_empty_dir(struct inode *); -struct exofs_dir_entry *exofs_dotdot(struct inode *, struct page **); -ino_t exofs_parent_ino(struct dentry *child); -int exofs_set_link(struct inode *, struct exofs_dir_entry *, struct page *, - struct inode *); - -/* super.c */ -void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], - const struct osd_obj_id *obj); -int exofs_sbi_write_stats(struct exofs_sb_info *sbi); - -/* sys.c */ -int exofs_sysfs_init(void); -void exofs_sysfs_uninit(void); -int exofs_sysfs_sb_add(struct exofs_sb_info *sbi, - struct exofs_dt_device_info *dt_dev); -void exofs_sysfs_sb_del(struct exofs_sb_info *sbi); -int exofs_sysfs_odev_add(struct exofs_dev *edev, - struct exofs_sb_info *sbi); -void exofs_sysfs_dbg_print(void); - -/********************* - * operation vectors * - *********************/ -/* dir.c: */ -extern const struct file_operations exofs_dir_operations; - -/* file.c */ -extern const struct inode_operations exofs_file_inode_operations; -extern const struct file_operations exofs_file_operations; - -/* inode.c */ -extern const struct address_space_operations exofs_aops; - -/* namei.c */ -extern const struct inode_operations exofs_dir_inode_operations; -extern const struct inode_operations exofs_special_inode_operations; - -/* exofs_init_comps will initialize an ore_components device array - * pointing to a single ore_comp struct, and a round-robin view - * of the device table. - * The first device of each inode is the [inode->ino % num_devices] - * and the rest of the devices sequentially following where the - * first device is after the last device. - * It is assumed that the global device array at @sbi is twice - * bigger and that the device table repeats twice. - * See: exofs_read_lookup_dev_table() - */ -static inline void exofs_init_comps(struct ore_components *oc, - struct ore_comp *one_comp, - struct exofs_sb_info *sbi, osd_id oid) -{ - unsigned dev_mod = (unsigned)oid, first_dev; - - one_comp->obj.partition = sbi->one_comp.obj.partition; - one_comp->obj.id = oid; - exofs_make_credential(one_comp->cred, &one_comp->obj); - - oc->first_dev = 0; - oc->numdevs = sbi->layout.group_width * sbi->layout.mirrors_p1 * - sbi->layout.group_count; - oc->single_comp = EC_SINGLE_COMP; - oc->comps = one_comp; - - /* Round robin device view of the table */ - first_dev = (dev_mod * sbi->layout.mirrors_p1) % sbi->oc.numdevs; - oc->ods = &sbi->oc.ods[first_dev]; -} - -#endif diff --git a/fs/exofs/file.c b/fs/exofs/file.c deleted file mode 100644 index a94594ea2aa3..000000000000 --- a/fs/exofs/file.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "exofs.h" - -static int exofs_release_file(struct inode *inode, struct file *filp) -{ - return 0; -} - -/* exofs_file_fsync - flush the inode to disk - * - * Note, in exofs all metadata is written as part of inode, regardless. - * The writeout is synchronous - */ -static int exofs_file_fsync(struct file *filp, loff_t start, loff_t end, - int datasync) -{ - struct inode *inode = filp->f_mapping->host; - int ret; - - ret = file_write_and_wait_range(filp, start, end); - if (ret) - return ret; - - inode_lock(inode); - ret = sync_inode_metadata(filp->f_mapping->host, 1); - inode_unlock(inode); - return ret; -} - -static int exofs_flush(struct file *file, fl_owner_t id) -{ - int ret = vfs_fsync(file, 0); - /* TODO: Flush the OSD target */ - return ret; -} - -const struct file_operations exofs_file_operations = { - .llseek = generic_file_llseek, - .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, - .mmap = generic_file_mmap, - .open = generic_file_open, - .release = exofs_release_file, - .fsync = exofs_file_fsync, - .flush = exofs_flush, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, -}; - -const struct inode_operations exofs_file_inode_operations = { - .setattr = exofs_setattr, -}; diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c deleted file mode 100644 index 5f81fcd383a4..000000000000 --- a/fs/exofs/inode.c +++ /dev/null @@ -1,1514 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "exofs.h" - -#define EXOFS_DBGMSG2(M...) do {} while (0) - -unsigned exofs_max_io_pages(struct ore_layout *layout, - unsigned expected_pages) -{ - unsigned pages = min_t(unsigned, expected_pages, - layout->max_io_length / PAGE_SIZE); - - return pages; -} - -struct page_collect { - struct exofs_sb_info *sbi; - struct inode *inode; - unsigned expected_pages; - struct ore_io_state *ios; - - struct page **pages; - unsigned alloc_pages; - unsigned nr_pages; - unsigned long length; - loff_t pg_first; /* keep 64bit also in 32-arches */ - bool read_4_write; /* This means two things: that the read is sync - * And the pages should not be unlocked. - */ - struct page *that_locked_page; -}; - -static void _pcol_init(struct page_collect *pcol, unsigned expected_pages, - struct inode *inode) -{ - struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; - - pcol->sbi = sbi; - pcol->inode = inode; - pcol->expected_pages = expected_pages; - - pcol->ios = NULL; - pcol->pages = NULL; - pcol->alloc_pages = 0; - pcol->nr_pages = 0; - pcol->length = 0; - pcol->pg_first = -1; - pcol->read_4_write = false; - pcol->that_locked_page = NULL; -} - -static void _pcol_reset(struct page_collect *pcol) -{ - pcol->expected_pages -= min(pcol->nr_pages, pcol->expected_pages); - - pcol->pages = NULL; - pcol->alloc_pages = 0; - pcol->nr_pages = 0; - pcol->length = 0; - pcol->pg_first = -1; - pcol->ios = NULL; - pcol->that_locked_page = NULL; - - /* this is probably the end of the loop but in writes - * it might not end here. don't be left with nothing - */ - if (!pcol->expected_pages) - pcol->expected_pages = - exofs_max_io_pages(&pcol->sbi->layout, ~0); -} - -static int pcol_try_alloc(struct page_collect *pcol) -{ - unsigned pages; - - /* TODO: easily support bio chaining */ - pages = exofs_max_io_pages(&pcol->sbi->layout, pcol->expected_pages); - - for (; pages; pages >>= 1) { - pcol->pages = kmalloc_array(pages, sizeof(struct page *), - GFP_KERNEL); - if (likely(pcol->pages)) { - pcol->alloc_pages = pages; - return 0; - } - } - - EXOFS_ERR("Failed to kmalloc expected_pages=%u\n", - pcol->expected_pages); - return -ENOMEM; -} - -static void pcol_free(struct page_collect *pcol) -{ - kfree(pcol->pages); - pcol->pages = NULL; - - if (pcol->ios) { - ore_put_io_state(pcol->ios); - pcol->ios = NULL; - } -} - -static int pcol_add_page(struct page_collect *pcol, struct page *page, - unsigned len) -{ - if (unlikely(pcol->nr_pages >= pcol->alloc_pages)) - return -ENOMEM; - - pcol->pages[pcol->nr_pages++] = page; - pcol->length += len; - return 0; -} - -enum {PAGE_WAS_NOT_IN_IO = 17}; -static int update_read_page(struct page *page, int ret) -{ - switch (ret) { - case 0: - /* Everything is OK */ - SetPageUptodate(page); - if (PageError(page)) - ClearPageError(page); - break; - case -EFAULT: - /* In this case we were trying to read something that wasn't on - * disk yet - return a page full of zeroes. This should be OK, - * because the object should be empty (if there was a write - * before this read, the read would be waiting with the page - * locked */ - clear_highpage(page); - - SetPageUptodate(page); - if (PageError(page)) - ClearPageError(page); - EXOFS_DBGMSG("recovered read error\n"); - /* fall through */ - case PAGE_WAS_NOT_IN_IO: - ret = 0; /* recovered error */ - break; - default: - SetPageError(page); - } - return ret; -} - -static void update_write_page(struct page *page, int ret) -{ - if (unlikely(ret == PAGE_WAS_NOT_IN_IO)) - return; /* don't pass start don't collect $200 */ - - if (ret) { - mapping_set_error(page->mapping, ret); - SetPageError(page); - } - end_page_writeback(page); -} - -/* Called at the end of reads, to optionally unlock pages and update their - * status. - */ -static int __readpages_done(struct page_collect *pcol) -{ - int i; - u64 good_bytes; - u64 length = 0; - int ret = ore_check_io(pcol->ios, NULL); - - if (likely(!ret)) { - good_bytes = pcol->length; - ret = PAGE_WAS_NOT_IN_IO; - } else { - good_bytes = 0; - } - - EXOFS_DBGMSG2("readpages_done(0x%lx) good_bytes=0x%llx" - " length=0x%lx nr_pages=%u\n", - pcol->inode->i_ino, _LLU(good_bytes), pcol->length, - pcol->nr_pages); - - for (i = 0; i < pcol->nr_pages; i++) { - struct page *page = pcol->pages[i]; - struct inode *inode = page->mapping->host; - int page_stat; - - if (inode != pcol->inode) - continue; /* osd might add more pages at end */ - - if (likely(length < good_bytes)) - page_stat = 0; - else - page_stat = ret; - - EXOFS_DBGMSG2(" readpages_done(0x%lx, 0x%lx) %s\n", - inode->i_ino, page->index, - page_stat ? "bad_bytes" : "good_bytes"); - - ret = update_read_page(page, page_stat); - if (!pcol->read_4_write) - unlock_page(page); - length += PAGE_SIZE; - } - - pcol_free(pcol); - EXOFS_DBGMSG2("readpages_done END\n"); - return ret; -} - -/* callback of async reads */ -static void readpages_done(struct ore_io_state *ios, void *p) -{ - struct page_collect *pcol = p; - - __readpages_done(pcol); - atomic_dec(&pcol->sbi->s_curr_pending); - kfree(pcol); -} - -static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw) -{ - int i; - - for (i = 0; i < pcol->nr_pages; i++) { - struct page *page = pcol->pages[i]; - - if (rw == READ) - update_read_page(page, ret); - else - update_write_page(page, ret); - - unlock_page(page); - } -} - -static int _maybe_not_all_in_one_io(struct ore_io_state *ios, - struct page_collect *pcol_src, struct page_collect *pcol) -{ - /* length was wrong or offset was not page aligned */ - BUG_ON(pcol_src->nr_pages < ios->nr_pages); - - if (pcol_src->nr_pages > ios->nr_pages) { - struct page **src_page; - unsigned pages_less = pcol_src->nr_pages - ios->nr_pages; - unsigned long len_less = pcol_src->length - ios->length; - unsigned i; - int ret; - - /* This IO was trimmed */ - pcol_src->nr_pages = ios->nr_pages; - pcol_src->length = ios->length; - - /* Left over pages are passed to the next io */ - pcol->expected_pages += pages_less; - pcol->nr_pages = pages_less; - pcol->length = len_less; - src_page = pcol_src->pages + pcol_src->nr_pages; - pcol->pg_first = (*src_page)->index; - - ret = pcol_try_alloc(pcol); - if (unlikely(ret)) - return ret; - - for (i = 0; i < pages_less; ++i) - pcol->pages[i] = *src_page++; - - EXOFS_DBGMSG("Length was adjusted nr_pages=0x%x " - "pages_less=0x%x expected_pages=0x%x " - "next_offset=0x%llx next_len=0x%lx\n", - pcol_src->nr_pages, pages_less, pcol->expected_pages, - pcol->pg_first * PAGE_SIZE, pcol->length); - } - return 0; -} - -static int read_exec(struct page_collect *pcol) -{ - struct exofs_i_info *oi = exofs_i(pcol->inode); - struct ore_io_state *ios; - struct page_collect *pcol_copy = NULL; - int ret; - - if (!pcol->pages) - return 0; - - if (!pcol->ios) { - int ret = ore_get_rw_state(&pcol->sbi->layout, &oi->oc, true, - pcol->pg_first << PAGE_SHIFT, - pcol->length, &pcol->ios); - - if (ret) - return ret; - } - - ios = pcol->ios; - ios->pages = pcol->pages; - - if (pcol->read_4_write) { - ore_read(pcol->ios); - return __readpages_done(pcol); - } - - pcol_copy = kmalloc(sizeof(*pcol_copy), GFP_KERNEL); - if (!pcol_copy) { - ret = -ENOMEM; - goto err; - } - - *pcol_copy = *pcol; - ios->done = readpages_done; - ios->private = pcol_copy; - - /* pages ownership was passed to pcol_copy */ - _pcol_reset(pcol); - - ret = _maybe_not_all_in_one_io(ios, pcol_copy, pcol); - if (unlikely(ret)) - goto err; - - EXOFS_DBGMSG2("read_exec(0x%lx) offset=0x%llx length=0x%llx\n", - pcol->inode->i_ino, _LLU(ios->offset), _LLU(ios->length)); - - ret = ore_read(ios); - if (unlikely(ret)) - goto err; - - atomic_inc(&pcol->sbi->s_curr_pending); - - return 0; - -err: - if (!pcol_copy) /* Failed before ownership transfer */ - pcol_copy = pcol; - _unlock_pcol_pages(pcol_copy, ret, READ); - pcol_free(pcol_copy); - kfree(pcol_copy); - - return ret; -} - -/* readpage_strip is called either directly from readpage() or by the VFS from - * within read_cache_pages(), to add one more page to be read. It will try to - * collect as many contiguous pages as posible. If a discontinuity is - * encountered, or it runs out of resources, it will submit the previous segment - * and will start a new collection. Eventually caller must submit the last - * segment if present. - */ -static int readpage_strip(void *data, struct page *page) -{ - struct page_collect *pcol = data; - struct inode *inode = pcol->inode; - struct exofs_i_info *oi = exofs_i(inode); - loff_t i_size = i_size_read(inode); - pgoff_t end_index = i_size >> PAGE_SHIFT; - size_t len; - int ret; - - BUG_ON(!PageLocked(page)); - - /* FIXME: Just for debugging, will be removed */ - if (PageUptodate(page)) - EXOFS_ERR("PageUptodate(0x%lx, 0x%lx)\n", pcol->inode->i_ino, - page->index); - - pcol->that_locked_page = page; - - if (page->index < end_index) - len = PAGE_SIZE; - else if (page->index == end_index) - len = i_size & ~PAGE_MASK; - else - len = 0; - - if (!len || !obj_created(oi)) { - /* this will be out of bounds, or doesn't exist yet. - * Current page is cleared and the request is split - */ - clear_highpage(page); - - SetPageUptodate(page); - if (PageError(page)) - ClearPageError(page); - - if (!pcol->read_4_write) - unlock_page(page); - EXOFS_DBGMSG("readpage_strip(0x%lx) empty page len=%zx " - "read_4_write=%d index=0x%lx end_index=0x%lx " - "splitting\n", inode->i_ino, len, - pcol->read_4_write, page->index, end_index); - - return read_exec(pcol); - } - -try_again: - - if (unlikely(pcol->pg_first == -1)) { - pcol->pg_first = page->index; - } else if (unlikely((pcol->pg_first + pcol->nr_pages) != - page->index)) { - /* Discontinuity detected, split the request */ - ret = read_exec(pcol); - if (unlikely(ret)) - goto fail; - goto try_again; - } - - if (!pcol->pages) { - ret = pcol_try_alloc(pcol); - if (unlikely(ret)) - goto fail; - } - - if (len != PAGE_SIZE) - zero_user(page, len, PAGE_SIZE - len); - - EXOFS_DBGMSG2(" readpage_strip(0x%lx, 0x%lx) len=0x%zx\n", - inode->i_ino, page->index, len); - - ret = pcol_add_page(pcol, page, len); - if (ret) { - EXOFS_DBGMSG2("Failed pcol_add_page pages[i]=%p " - "this_len=0x%zx nr_pages=%u length=0x%lx\n", - page, len, pcol->nr_pages, pcol->length); - - /* split the request, and start again with current page */ - ret = read_exec(pcol); - if (unlikely(ret)) - goto fail; - - goto try_again; - } - - return 0; - -fail: - /* SetPageError(page); ??? */ - unlock_page(page); - return ret; -} - -static int exofs_readpages(struct file *file, struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - struct page_collect pcol; - int ret; - - _pcol_init(&pcol, nr_pages, mapping->host); - - ret = read_cache_pages(mapping, pages, readpage_strip, &pcol); - if (ret) { - EXOFS_ERR("read_cache_pages => %d\n", ret); - return ret; - } - - ret = read_exec(&pcol); - if (unlikely(ret)) - return ret; - - return read_exec(&pcol); -} - -static int _readpage(struct page *page, bool read_4_write) -{ - struct page_collect pcol; - int ret; - - _pcol_init(&pcol, 1, page->mapping->host); - - pcol.read_4_write = read_4_write; - ret = readpage_strip(&pcol, page); - if (ret) { - EXOFS_ERR("_readpage => %d\n", ret); - return ret; - } - - return read_exec(&pcol); -} - -/* - * We don't need the file - */ -static int exofs_readpage(struct file *file, struct page *page) -{ - return _readpage(page, false); -} - -/* Callback for osd_write. All writes are asynchronous */ -static void writepages_done(struct ore_io_state *ios, void *p) -{ - struct page_collect *pcol = p; - int i; - u64 good_bytes; - u64 length = 0; - int ret = ore_check_io(ios, NULL); - - atomic_dec(&pcol->sbi->s_curr_pending); - - if (likely(!ret)) { - good_bytes = pcol->length; - ret = PAGE_WAS_NOT_IN_IO; - } else { - good_bytes = 0; - } - - EXOFS_DBGMSG2("writepages_done(0x%lx) good_bytes=0x%llx" - " length=0x%lx nr_pages=%u\n", - pcol->inode->i_ino, _LLU(good_bytes), pcol->length, - pcol->nr_pages); - - for (i = 0; i < pcol->nr_pages; i++) { - struct page *page = pcol->pages[i]; - struct inode *inode = page->mapping->host; - int page_stat; - - if (inode != pcol->inode) - continue; /* osd might add more pages to a bio */ - - if (likely(length < good_bytes)) - page_stat = 0; - else - page_stat = ret; - - update_write_page(page, page_stat); - unlock_page(page); - EXOFS_DBGMSG2(" writepages_done(0x%lx, 0x%lx) status=%d\n", - inode->i_ino, page->index, page_stat); - - length += PAGE_SIZE; - } - - pcol_free(pcol); - kfree(pcol); - EXOFS_DBGMSG2("writepages_done END\n"); -} - -static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate) -{ - struct page_collect *pcol = priv; - pgoff_t index = offset / PAGE_SIZE; - - if (!pcol->that_locked_page || - (pcol->that_locked_page->index != index)) { - struct page *page; - loff_t i_size = i_size_read(pcol->inode); - - if (offset >= i_size) { - *uptodate = true; - EXOFS_DBGMSG2("offset >= i_size index=0x%lx\n", index); - return ZERO_PAGE(0); - } - - page = find_get_page(pcol->inode->i_mapping, index); - if (!page) { - page = find_or_create_page(pcol->inode->i_mapping, - index, GFP_NOFS); - if (unlikely(!page)) { - EXOFS_DBGMSG("grab_cache_page Failed " - "index=0x%llx\n", _LLU(index)); - return NULL; - } - unlock_page(page); - } - *uptodate = PageUptodate(page); - EXOFS_DBGMSG2("index=0x%lx uptodate=%d\n", index, *uptodate); - return page; - } else { - EXOFS_DBGMSG2("YES that_locked_page index=0x%lx\n", - pcol->that_locked_page->index); - *uptodate = true; - return pcol->that_locked_page; - } -} - -static void __r4w_put_page(void *priv, struct page *page) -{ - struct page_collect *pcol = priv; - - if ((pcol->that_locked_page != page) && (ZERO_PAGE(0) != page)) { - EXOFS_DBGMSG2("index=0x%lx\n", page->index); - put_page(page); - return; - } - EXOFS_DBGMSG2("that_locked_page index=0x%lx\n", - ZERO_PAGE(0) == page ? -1 : page->index); -} - -static const struct _ore_r4w_op _r4w_op = { - .get_page = &__r4w_get_page, - .put_page = &__r4w_put_page, -}; - -static int write_exec(struct page_collect *pcol) -{ - struct exofs_i_info *oi = exofs_i(pcol->inode); - struct ore_io_state *ios; - struct page_collect *pcol_copy = NULL; - int ret; - - if (!pcol->pages) - return 0; - - BUG_ON(pcol->ios); - ret = ore_get_rw_state(&pcol->sbi->layout, &oi->oc, false, - pcol->pg_first << PAGE_SHIFT, - pcol->length, &pcol->ios); - if (unlikely(ret)) - goto err; - - pcol_copy = kmalloc(sizeof(*pcol_copy), GFP_KERNEL); - if (!pcol_copy) { - EXOFS_ERR("write_exec: Failed to kmalloc(pcol)\n"); - ret = -ENOMEM; - goto err; - } - - *pcol_copy = *pcol; - - ios = pcol->ios; - ios->pages = pcol_copy->pages; - ios->done = writepages_done; - ios->r4w = &_r4w_op; - ios->private = pcol_copy; - - /* pages ownership was passed to pcol_copy */ - _pcol_reset(pcol); - - ret = _maybe_not_all_in_one_io(ios, pcol_copy, pcol); - if (unlikely(ret)) - goto err; - - EXOFS_DBGMSG2("write_exec(0x%lx) offset=0x%llx length=0x%llx\n", - pcol->inode->i_ino, _LLU(ios->offset), _LLU(ios->length)); - - ret = ore_write(ios); - if (unlikely(ret)) { - EXOFS_ERR("write_exec: ore_write() Failed\n"); - goto err; - } - - atomic_inc(&pcol->sbi->s_curr_pending); - return 0; - -err: - if (!pcol_copy) /* Failed before ownership transfer */ - pcol_copy = pcol; - _unlock_pcol_pages(pcol_copy, ret, WRITE); - pcol_free(pcol_copy); - kfree(pcol_copy); - - return ret; -} - -/* writepage_strip is called either directly from writepage() or by the VFS from - * within write_cache_pages(), to add one more page to be written to storage. - * It will try to collect as many contiguous pages as possible. If a - * discontinuity is encountered or it runs out of resources it will submit the - * previous segment and will start a new collection. - * Eventually caller must submit the last segment if present. - */ -static int writepage_strip(struct page *page, - struct writeback_control *wbc_unused, void *data) -{ - struct page_collect *pcol = data; - struct inode *inode = pcol->inode; - struct exofs_i_info *oi = exofs_i(inode); - loff_t i_size = i_size_read(inode); - pgoff_t end_index = i_size >> PAGE_SHIFT; - size_t len; - int ret; - - BUG_ON(!PageLocked(page)); - - ret = wait_obj_created(oi); - if (unlikely(ret)) - goto fail; - - if (page->index < end_index) - /* in this case, the page is within the limits of the file */ - len = PAGE_SIZE; - else { - len = i_size & ~PAGE_MASK; - - if (page->index > end_index || !len) { - /* in this case, the page is outside the limits - * (truncate in progress) - */ - ret = write_exec(pcol); - if (unlikely(ret)) - goto fail; - if (PageError(page)) - ClearPageError(page); - unlock_page(page); - EXOFS_DBGMSG("writepage_strip(0x%lx, 0x%lx) " - "outside the limits\n", - inode->i_ino, page->index); - return 0; - } - } - -try_again: - - if (unlikely(pcol->pg_first == -1)) { - pcol->pg_first = page->index; - } else if (unlikely((pcol->pg_first + pcol->nr_pages) != - page->index)) { - /* Discontinuity detected, split the request */ - ret = write_exec(pcol); - if (unlikely(ret)) - goto fail; - - EXOFS_DBGMSG("writepage_strip(0x%lx, 0x%lx) Discontinuity\n", - inode->i_ino, page->index); - goto try_again; - } - - if (!pcol->pages) { - ret = pcol_try_alloc(pcol); - if (unlikely(ret)) - goto fail; - } - - EXOFS_DBGMSG2(" writepage_strip(0x%lx, 0x%lx) len=0x%zx\n", - inode->i_ino, page->index, len); - - ret = pcol_add_page(pcol, page, len); - if (unlikely(ret)) { - EXOFS_DBGMSG2("Failed pcol_add_page " - "nr_pages=%u total_length=0x%lx\n", - pcol->nr_pages, pcol->length); - - /* split the request, next loop will start again */ - ret = write_exec(pcol); - if (unlikely(ret)) { - EXOFS_DBGMSG("write_exec failed => %d", ret); - goto fail; - } - - goto try_again; - } - - BUG_ON(PageWriteback(page)); - set_page_writeback(page); - - return 0; - -fail: - EXOFS_DBGMSG("Error: writepage_strip(0x%lx, 0x%lx)=>%d\n", - inode->i_ino, page->index, ret); - mapping_set_error(page->mapping, -EIO); - unlock_page(page); - return ret; -} - -static int exofs_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct page_collect pcol; - long start, end, expected_pages; - int ret; - - start = wbc->range_start >> PAGE_SHIFT; - end = (wbc->range_end == LLONG_MAX) ? - start + mapping->nrpages : - wbc->range_end >> PAGE_SHIFT; - - if (start || end) - expected_pages = end - start + 1; - else - expected_pages = mapping->nrpages; - - if (expected_pages < 32L) - expected_pages = 32L; - - EXOFS_DBGMSG2("inode(0x%lx) wbc->start=0x%llx wbc->end=0x%llx " - "nrpages=%lu start=0x%lx end=0x%lx expected_pages=%ld\n", - mapping->host->i_ino, wbc->range_start, wbc->range_end, - mapping->nrpages, start, end, expected_pages); - - _pcol_init(&pcol, expected_pages, mapping->host); - - ret = write_cache_pages(mapping, wbc, writepage_strip, &pcol); - if (unlikely(ret)) { - EXOFS_ERR("write_cache_pages => %d\n", ret); - return ret; - } - - ret = write_exec(&pcol); - if (unlikely(ret)) - return ret; - - if (wbc->sync_mode == WB_SYNC_ALL) { - return write_exec(&pcol); /* pump the last reminder */ - } else if (pcol.nr_pages) { - /* not SYNC let the reminder join the next writeout */ - unsigned i; - - for (i = 0; i < pcol.nr_pages; i++) { - struct page *page = pcol.pages[i]; - - end_page_writeback(page); - set_page_dirty(page); - unlock_page(page); - } - } - return 0; -} - -/* -static int exofs_writepage(struct page *page, struct writeback_control *wbc) -{ - struct page_collect pcol; - int ret; - - _pcol_init(&pcol, 1, page->mapping->host); - - ret = writepage_strip(page, NULL, &pcol); - if (ret) { - EXOFS_ERR("exofs_writepage => %d\n", ret); - return ret; - } - - return write_exec(&pcol); -} -*/ -/* i_mutex held using inode->i_size directly */ -static void _write_failed(struct inode *inode, loff_t to) -{ - if (to > inode->i_size) - truncate_pagecache(inode, inode->i_size); -} - -int exofs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - int ret = 0; - struct page *page; - - page = *pagep; - if (page == NULL) { - page = grab_cache_page_write_begin(mapping, pos >> PAGE_SHIFT, - flags); - if (!page) { - EXOFS_DBGMSG("grab_cache_page_write_begin failed\n"); - return -ENOMEM; - } - *pagep = page; - } - - /* read modify write */ - if (!PageUptodate(page) && (len != PAGE_SIZE)) { - loff_t i_size = i_size_read(mapping->host); - pgoff_t end_index = i_size >> PAGE_SHIFT; - - if (page->index > end_index) { - clear_highpage(page); - SetPageUptodate(page); - } else { - ret = _readpage(page, true); - if (ret) { - unlock_page(page); - EXOFS_DBGMSG("__readpage failed\n"); - } - } - } - return ret; -} - -static int exofs_write_begin_export(struct file *file, - struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - *pagep = NULL; - - return exofs_write_begin(file, mapping, pos, len, flags, pagep, - fsdata); -} - -static int exofs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - struct inode *inode = mapping->host; - loff_t last_pos = pos + copied; - - if (!PageUptodate(page)) { - if (copied < len) { - _write_failed(inode, pos + len); - copied = 0; - goto out; - } - SetPageUptodate(page); - } - if (last_pos > inode->i_size) { - i_size_write(inode, last_pos); - mark_inode_dirty(inode); - } - set_page_dirty(page); -out: - unlock_page(page); - put_page(page); - return copied; -} - -static int exofs_releasepage(struct page *page, gfp_t gfp) -{ - EXOFS_DBGMSG("page 0x%lx\n", page->index); - WARN_ON(1); - return 0; -} - -static void exofs_invalidatepage(struct page *page, unsigned int offset, - unsigned int length) -{ - EXOFS_DBGMSG("page 0x%lx offset 0x%x length 0x%x\n", - page->index, offset, length); - WARN_ON(1); -} - - - /* TODO: Should be easy enough to do proprly */ -static ssize_t exofs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) -{ - return 0; -} - -const struct address_space_operations exofs_aops = { - .readpage = exofs_readpage, - .readpages = exofs_readpages, - .writepage = NULL, - .writepages = exofs_writepages, - .write_begin = exofs_write_begin_export, - .write_end = exofs_write_end, - .releasepage = exofs_releasepage, - .set_page_dirty = __set_page_dirty_nobuffers, - .invalidatepage = exofs_invalidatepage, - - /* Not implemented Yet */ - .bmap = NULL, /* TODO: use osd's OSD_ACT_READ_MAP */ - .direct_IO = exofs_direct_IO, - - /* With these NULL has special meaning or default is not exported */ - .migratepage = NULL, - .launder_page = NULL, - .is_partially_uptodate = NULL, - .error_remove_page = NULL, -}; - -/****************************************************************************** - * INODE OPERATIONS - *****************************************************************************/ - -/* - * Test whether an inode is a fast symlink. - */ -static inline int exofs_inode_is_fast_symlink(struct inode *inode) -{ - struct exofs_i_info *oi = exofs_i(inode); - - return S_ISLNK(inode->i_mode) && (oi->i_data[0] != 0); -} - -static int _do_truncate(struct inode *inode, loff_t newsize) -{ - struct exofs_i_info *oi = exofs_i(inode); - struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; - int ret; - - inode->i_mtime = inode->i_ctime = current_time(inode); - - ret = ore_truncate(&sbi->layout, &oi->oc, (u64)newsize); - if (likely(!ret)) - truncate_setsize(inode, newsize); - - EXOFS_DBGMSG2("(0x%lx) size=0x%llx ret=>%d\n", - inode->i_ino, newsize, ret); - return ret; -} - -/* - * Set inode attributes - update size attribute on OSD if needed, - * otherwise just call generic functions. - */ -int exofs_setattr(struct dentry *dentry, struct iattr *iattr) -{ - struct inode *inode = d_inode(dentry); - int error; - - /* if we are about to modify an object, and it hasn't been - * created yet, wait - */ - error = wait_obj_created(exofs_i(inode)); - if (unlikely(error)) - return error; - - error = setattr_prepare(dentry, iattr); - if (unlikely(error)) - return error; - - if ((iattr->ia_valid & ATTR_SIZE) && - iattr->ia_size != i_size_read(inode)) { - error = _do_truncate(inode, iattr->ia_size); - if (unlikely(error)) - return error; - } - - setattr_copy(inode, iattr); - mark_inode_dirty(inode); - return 0; -} - -static const struct osd_attr g_attr_inode_file_layout = ATTR_DEF( - EXOFS_APAGE_FS_DATA, - EXOFS_ATTR_INODE_FILE_LAYOUT, - 0); -static const struct osd_attr g_attr_inode_dir_layout = ATTR_DEF( - EXOFS_APAGE_FS_DATA, - EXOFS_ATTR_INODE_DIR_LAYOUT, - 0); - -/* - * Read the Linux inode info from the OSD, and return it as is. In exofs the - * inode info is in an application specific page/attribute of the osd-object. - */ -static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi, - struct exofs_fcb *inode) -{ - struct exofs_sb_info *sbi = sb->s_fs_info; - struct osd_attr attrs[] = { - [0] = g_attr_inode_data, - [1] = g_attr_inode_file_layout, - [2] = g_attr_inode_dir_layout, - }; - struct ore_io_state *ios; - struct exofs_on_disk_inode_layout *layout; - int ret; - - ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); - return ret; - } - - attrs[1].len = exofs_on_disk_inode_layout_size(sbi->oc.numdevs); - attrs[2].len = exofs_on_disk_inode_layout_size(sbi->oc.numdevs); - - ios->in_attr = attrs; - ios->in_attr_len = ARRAY_SIZE(attrs); - - ret = ore_read(ios); - if (unlikely(ret)) { - EXOFS_ERR("object(0x%llx) corrupted, return empty file=>%d\n", - _LLU(oi->one_comp.obj.id), ret); - memset(inode, 0, sizeof(*inode)); - inode->i_mode = 0040000 | (0777 & ~022); - /* If object is lost on target we might as well enable it's - * delete. - */ - ret = 0; - goto out; - } - - ret = extract_attr_from_ios(ios, &attrs[0]); - if (ret) { - EXOFS_ERR("%s: extract_attr 0 of inode failed\n", __func__); - goto out; - } - WARN_ON(attrs[0].len != EXOFS_INO_ATTR_SIZE); - memcpy(inode, attrs[0].val_ptr, EXOFS_INO_ATTR_SIZE); - - ret = extract_attr_from_ios(ios, &attrs[1]); - if (ret) { - EXOFS_ERR("%s: extract_attr 1 of inode failed\n", __func__); - goto out; - } - if (attrs[1].len) { - layout = attrs[1].val_ptr; - if (layout->gen_func != cpu_to_le16(LAYOUT_MOVING_WINDOW)) { - EXOFS_ERR("%s: unsupported files layout %d\n", - __func__, layout->gen_func); - ret = -ENOTSUPP; - goto out; - } - } - - ret = extract_attr_from_ios(ios, &attrs[2]); - if (ret) { - EXOFS_ERR("%s: extract_attr 2 of inode failed\n", __func__); - goto out; - } - if (attrs[2].len) { - layout = attrs[2].val_ptr; - if (layout->gen_func != cpu_to_le16(LAYOUT_MOVING_WINDOW)) { - EXOFS_ERR("%s: unsupported meta-data layout %d\n", - __func__, layout->gen_func); - ret = -ENOTSUPP; - goto out; - } - } - -out: - ore_put_io_state(ios); - return ret; -} - -static void __oi_init(struct exofs_i_info *oi) -{ - init_waitqueue_head(&oi->i_wq); - oi->i_flags = 0; -} -/* - * Fill in an inode read from the OSD and set it up for use - */ -struct inode *exofs_iget(struct super_block *sb, unsigned long ino) -{ - struct exofs_i_info *oi; - struct exofs_fcb fcb; - struct inode *inode; - int ret; - - inode = iget_locked(sb, ino); - if (!inode) - return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - oi = exofs_i(inode); - __oi_init(oi); - exofs_init_comps(&oi->oc, &oi->one_comp, sb->s_fs_info, - exofs_oi_objno(oi)); - - /* read the inode from the osd */ - ret = exofs_get_inode(sb, oi, &fcb); - if (ret) - goto bad_inode; - - set_obj_created(oi); - - /* copy stuff from on-disk struct to in-memory struct */ - inode->i_mode = le16_to_cpu(fcb.i_mode); - i_uid_write(inode, le32_to_cpu(fcb.i_uid)); - i_gid_write(inode, le32_to_cpu(fcb.i_gid)); - set_nlink(inode, le16_to_cpu(fcb.i_links_count)); - inode->i_ctime.tv_sec = (signed)le32_to_cpu(fcb.i_ctime); - inode->i_atime.tv_sec = (signed)le32_to_cpu(fcb.i_atime); - inode->i_mtime.tv_sec = (signed)le32_to_cpu(fcb.i_mtime); - inode->i_ctime.tv_nsec = - inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = 0; - oi->i_commit_size = le64_to_cpu(fcb.i_size); - i_size_write(inode, oi->i_commit_size); - inode->i_blkbits = EXOFS_BLKSHIFT; - inode->i_generation = le32_to_cpu(fcb.i_generation); - - oi->i_dir_start_lookup = 0; - - if ((inode->i_nlink == 0) && (inode->i_mode == 0)) { - ret = -ESTALE; - goto bad_inode; - } - - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { - if (fcb.i_data[0]) - inode->i_rdev = - old_decode_dev(le32_to_cpu(fcb.i_data[0])); - else - inode->i_rdev = - new_decode_dev(le32_to_cpu(fcb.i_data[1])); - } else { - memcpy(oi->i_data, fcb.i_data, sizeof(fcb.i_data)); - } - - if (S_ISREG(inode->i_mode)) { - inode->i_op = &exofs_file_inode_operations; - inode->i_fop = &exofs_file_operations; - inode->i_mapping->a_ops = &exofs_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &exofs_dir_inode_operations; - inode->i_fop = &exofs_dir_operations; - inode->i_mapping->a_ops = &exofs_aops; - } else if (S_ISLNK(inode->i_mode)) { - if (exofs_inode_is_fast_symlink(inode)) { - inode->i_op = &simple_symlink_inode_operations; - inode->i_link = (char *)oi->i_data; - } else { - inode->i_op = &page_symlink_inode_operations; - inode_nohighmem(inode); - inode->i_mapping->a_ops = &exofs_aops; - } - } else { - inode->i_op = &exofs_special_inode_operations; - if (fcb.i_data[0]) - init_special_inode(inode, inode->i_mode, - old_decode_dev(le32_to_cpu(fcb.i_data[0]))); - else - init_special_inode(inode, inode->i_mode, - new_decode_dev(le32_to_cpu(fcb.i_data[1]))); - } - - unlock_new_inode(inode); - return inode; - -bad_inode: - iget_failed(inode); - return ERR_PTR(ret); -} - -int __exofs_wait_obj_created(struct exofs_i_info *oi) -{ - if (!obj_created(oi)) { - EXOFS_DBGMSG("!obj_created\n"); - BUG_ON(!obj_2bcreated(oi)); - wait_event(oi->i_wq, obj_created(oi)); - EXOFS_DBGMSG("wait_event done\n"); - } - return unlikely(is_bad_inode(&oi->vfs_inode)) ? -EIO : 0; -} - -/* - * Callback function from exofs_new_inode(). The important thing is that we - * set the obj_created flag so that other methods know that the object exists on - * the OSD. - */ -static void create_done(struct ore_io_state *ios, void *p) -{ - struct inode *inode = p; - struct exofs_i_info *oi = exofs_i(inode); - struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; - int ret; - - ret = ore_check_io(ios, NULL); - ore_put_io_state(ios); - - atomic_dec(&sbi->s_curr_pending); - - if (unlikely(ret)) { - EXOFS_ERR("object=0x%llx creation failed in pid=0x%llx", - _LLU(exofs_oi_objno(oi)), - _LLU(oi->one_comp.obj.partition)); - /*TODO: When FS is corrupted creation can fail, object already - * exist. Get rid of this asynchronous creation, if exist - * increment the obj counter and try the next object. Until we - * succeed. All these dangling objects will be made into lost - * files by chkfs.exofs - */ - } - - set_obj_created(oi); - - wake_up(&oi->i_wq); -} - -/* - * Set up a new inode and create an object for it on the OSD - */ -struct inode *exofs_new_inode(struct inode *dir, umode_t mode) -{ - struct super_block *sb = dir->i_sb; - struct exofs_sb_info *sbi = sb->s_fs_info; - struct inode *inode; - struct exofs_i_info *oi; - struct ore_io_state *ios; - int ret; - - inode = new_inode(sb); - if (!inode) - return ERR_PTR(-ENOMEM); - - oi = exofs_i(inode); - __oi_init(oi); - - set_obj_2bcreated(oi); - - inode_init_owner(inode, dir, mode); - inode->i_ino = sbi->s_nextid++; - inode->i_blkbits = EXOFS_BLKSHIFT; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); - oi->i_commit_size = inode->i_size = 0; - spin_lock(&sbi->s_next_gen_lock); - inode->i_generation = sbi->s_next_generation++; - spin_unlock(&sbi->s_next_gen_lock); - insert_inode_hash(inode); - - exofs_init_comps(&oi->oc, &oi->one_comp, sb->s_fs_info, - exofs_oi_objno(oi)); - exofs_sbi_write_stats(sbi); /* Make sure new sbi->s_nextid is on disk */ - - mark_inode_dirty(inode); - - ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios); - if (unlikely(ret)) { - EXOFS_ERR("exofs_new_inode: ore_get_io_state failed\n"); - return ERR_PTR(ret); - } - - ios->done = create_done; - ios->private = inode; - - ret = ore_create(ios); - if (ret) { - ore_put_io_state(ios); - return ERR_PTR(ret); - } - atomic_inc(&sbi->s_curr_pending); - - return inode; -} - -/* - * struct to pass two arguments to update_inode's callback - */ -struct updatei_args { - struct exofs_sb_info *sbi; - struct exofs_fcb fcb; -}; - -/* - * Callback function from exofs_update_inode(). - */ -static void updatei_done(struct ore_io_state *ios, void *p) -{ - struct updatei_args *args = p; - - ore_put_io_state(ios); - - atomic_dec(&args->sbi->s_curr_pending); - - kfree(args); -} - -/* - * Write the inode to the OSD. Just fill up the struct, and set the attribute - * synchronously or asynchronously depending on the do_sync flag. - */ -static int exofs_update_inode(struct inode *inode, int do_sync) -{ - struct exofs_i_info *oi = exofs_i(inode); - struct super_block *sb = inode->i_sb; - struct exofs_sb_info *sbi = sb->s_fs_info; - struct ore_io_state *ios; - struct osd_attr attr; - struct exofs_fcb *fcb; - struct updatei_args *args; - int ret; - - args = kzalloc(sizeof(*args), GFP_KERNEL); - if (!args) { - EXOFS_DBGMSG("Failed kzalloc of args\n"); - return -ENOMEM; - } - - fcb = &args->fcb; - - fcb->i_mode = cpu_to_le16(inode->i_mode); - fcb->i_uid = cpu_to_le32(i_uid_read(inode)); - fcb->i_gid = cpu_to_le32(i_gid_read(inode)); - fcb->i_links_count = cpu_to_le16(inode->i_nlink); - fcb->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); - fcb->i_atime = cpu_to_le32(inode->i_atime.tv_sec); - fcb->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec); - oi->i_commit_size = i_size_read(inode); - fcb->i_size = cpu_to_le64(oi->i_commit_size); - fcb->i_generation = cpu_to_le32(inode->i_generation); - - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { - if (old_valid_dev(inode->i_rdev)) { - fcb->i_data[0] = - cpu_to_le32(old_encode_dev(inode->i_rdev)); - fcb->i_data[1] = 0; - } else { - fcb->i_data[0] = 0; - fcb->i_data[1] = - cpu_to_le32(new_encode_dev(inode->i_rdev)); - fcb->i_data[2] = 0; - } - } else - memcpy(fcb->i_data, oi->i_data, sizeof(fcb->i_data)); - - ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); - goto free_args; - } - - attr = g_attr_inode_data; - attr.val_ptr = fcb; - ios->out_attr_len = 1; - ios->out_attr = &attr; - - wait_obj_created(oi); - - if (!do_sync) { - args->sbi = sbi; - ios->done = updatei_done; - ios->private = args; - } - - ret = ore_write(ios); - if (!do_sync && !ret) { - atomic_inc(&sbi->s_curr_pending); - goto out; /* deallocation in updatei_done */ - } - - ore_put_io_state(ios); -free_args: - kfree(args); -out: - EXOFS_DBGMSG("(0x%lx) do_sync=%d ret=>%d\n", - inode->i_ino, do_sync, ret); - return ret; -} - -int exofs_write_inode(struct inode *inode, struct writeback_control *wbc) -{ - /* FIXME: fix fsync and use wbc->sync_mode == WB_SYNC_ALL */ - return exofs_update_inode(inode, 1); -} - -/* - * Callback function from exofs_delete_inode() - don't have much cleaning up to - * do. - */ -static void delete_done(struct ore_io_state *ios, void *p) -{ - struct exofs_sb_info *sbi = p; - - ore_put_io_state(ios); - - atomic_dec(&sbi->s_curr_pending); -} - -/* - * Called when the refcount of an inode reaches zero. We remove the object - * from the OSD here. We make sure the object was created before we try and - * delete it. - */ -void exofs_evict_inode(struct inode *inode) -{ - struct exofs_i_info *oi = exofs_i(inode); - struct super_block *sb = inode->i_sb; - struct exofs_sb_info *sbi = sb->s_fs_info; - struct ore_io_state *ios; - int ret; - - truncate_inode_pages_final(&inode->i_data); - - /* TODO: should do better here */ - if (inode->i_nlink || is_bad_inode(inode)) - goto no_delete; - - inode->i_size = 0; - clear_inode(inode); - - /* if we are deleting an obj that hasn't been created yet, wait. - * This also makes sure that create_done cannot be called with an - * already evicted inode. - */ - wait_obj_created(oi); - /* ignore the error, attempt a remove anyway */ - - /* Now Remove the OSD objects */ - ret = ore_get_io_state(&sbi->layout, &oi->oc, &ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: ore_get_io_state failed\n", __func__); - return; - } - - ios->done = delete_done; - ios->private = sbi; - - ret = ore_remove(ios); - if (ret) { - EXOFS_ERR("%s: ore_remove failed\n", __func__); - ore_put_io_state(ios); - return; - } - atomic_inc(&sbi->s_curr_pending); - - return; - -no_delete: - clear_inode(inode); -} diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c deleted file mode 100644 index 7295cd722770..000000000000 --- a/fs/exofs/namei.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "exofs.h" - -static inline int exofs_add_nondir(struct dentry *dentry, struct inode *inode) -{ - int err = exofs_add_link(dentry, inode); - if (!err) { - d_instantiate(dentry, inode); - return 0; - } - inode_dec_link_count(inode); - iput(inode); - return err; -} - -static struct dentry *exofs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct inode *inode; - ino_t ino; - - if (dentry->d_name.len > EXOFS_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); - - ino = exofs_inode_by_name(dir, dentry); - inode = ino ? exofs_iget(dir->i_sb, ino) : NULL; - return d_splice_alias(inode, dentry); -} - -static int exofs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool excl) -{ - struct inode *inode = exofs_new_inode(dir, mode); - int err = PTR_ERR(inode); - if (!IS_ERR(inode)) { - inode->i_op = &exofs_file_inode_operations; - inode->i_fop = &exofs_file_operations; - inode->i_mapping->a_ops = &exofs_aops; - mark_inode_dirty(inode); - err = exofs_add_nondir(dentry, inode); - } - return err; -} - -static int exofs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t rdev) -{ - struct inode *inode; - int err; - - inode = exofs_new_inode(dir, mode); - err = PTR_ERR(inode); - if (!IS_ERR(inode)) { - init_special_inode(inode, inode->i_mode, rdev); - mark_inode_dirty(inode); - err = exofs_add_nondir(dentry, inode); - } - return err; -} - -static int exofs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - struct super_block *sb = dir->i_sb; - int err = -ENAMETOOLONG; - unsigned l = strlen(symname)+1; - struct inode *inode; - struct exofs_i_info *oi; - - if (l > sb->s_blocksize) - goto out; - - inode = exofs_new_inode(dir, S_IFLNK | S_IRWXUGO); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out; - - oi = exofs_i(inode); - if (l > sizeof(oi->i_data)) { - /* slow symlink */ - inode->i_op = &page_symlink_inode_operations; - inode_nohighmem(inode); - inode->i_mapping->a_ops = &exofs_aops; - memset(oi->i_data, 0, sizeof(oi->i_data)); - - err = page_symlink(inode, symname, l); - if (err) - goto out_fail; - } else { - /* fast symlink */ - inode->i_op = &simple_symlink_inode_operations; - inode->i_link = (char *)oi->i_data; - memcpy(oi->i_data, symname, l); - inode->i_size = l-1; - } - mark_inode_dirty(inode); - - err = exofs_add_nondir(dentry, inode); -out: - return err; - -out_fail: - inode_dec_link_count(inode); - iput(inode); - goto out; -} - -static int exofs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct inode *inode = d_inode(old_dentry); - - inode->i_ctime = current_time(inode); - inode_inc_link_count(inode); - ihold(inode); - - return exofs_add_nondir(dentry, inode); -} - -static int exofs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - struct inode *inode; - int err; - - inode_inc_link_count(dir); - - inode = exofs_new_inode(dir, S_IFDIR | mode); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out_dir; - - inode->i_op = &exofs_dir_inode_operations; - inode->i_fop = &exofs_dir_operations; - inode->i_mapping->a_ops = &exofs_aops; - - inode_inc_link_count(inode); - - err = exofs_make_empty(inode, dir); - if (err) - goto out_fail; - - err = exofs_add_link(dentry, inode); - if (err) - goto out_fail; - - d_instantiate(dentry, inode); -out: - return err; - -out_fail: - inode_dec_link_count(inode); - inode_dec_link_count(inode); - iput(inode); -out_dir: - inode_dec_link_count(dir); - goto out; -} - -static int exofs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = d_inode(dentry); - struct exofs_dir_entry *de; - struct page *page; - int err = -ENOENT; - - de = exofs_find_entry(dir, dentry, &page); - if (!de) - goto out; - - err = exofs_delete_entry(de, page); - if (err) - goto out; - - inode->i_ctime = dir->i_ctime; - inode_dec_link_count(inode); - err = 0; -out: - return err; -} - -static int exofs_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = d_inode(dentry); - int err = -ENOTEMPTY; - - if (exofs_empty_dir(inode)) { - err = exofs_unlink(dir, dentry); - if (!err) { - inode->i_size = 0; - inode_dec_link_count(inode); - inode_dec_link_count(dir); - } - } - return err; -} - -static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) -{ - struct inode *old_inode = d_inode(old_dentry); - struct inode *new_inode = d_inode(new_dentry); - struct page *dir_page = NULL; - struct exofs_dir_entry *dir_de = NULL; - struct page *old_page; - struct exofs_dir_entry *old_de; - int err = -ENOENT; - - if (flags & ~RENAME_NOREPLACE) - return -EINVAL; - - old_de = exofs_find_entry(old_dir, old_dentry, &old_page); - if (!old_de) - goto out; - - if (S_ISDIR(old_inode->i_mode)) { - err = -EIO; - dir_de = exofs_dotdot(old_inode, &dir_page); - if (!dir_de) - goto out_old; - } - - if (new_inode) { - struct page *new_page; - struct exofs_dir_entry *new_de; - - err = -ENOTEMPTY; - if (dir_de && !exofs_empty_dir(new_inode)) - goto out_dir; - - err = -ENOENT; - new_de = exofs_find_entry(new_dir, new_dentry, &new_page); - if (!new_de) - goto out_dir; - err = exofs_set_link(new_dir, new_de, new_page, old_inode); - new_inode->i_ctime = current_time(new_inode); - if (dir_de) - drop_nlink(new_inode); - inode_dec_link_count(new_inode); - if (err) - goto out_dir; - } else { - err = exofs_add_link(new_dentry, old_inode); - if (err) - goto out_dir; - if (dir_de) - inode_inc_link_count(new_dir); - } - - old_inode->i_ctime = current_time(old_inode); - - exofs_delete_entry(old_de, old_page); - mark_inode_dirty(old_inode); - - if (dir_de) { - err = exofs_set_link(old_inode, dir_de, dir_page, new_dir); - inode_dec_link_count(old_dir); - if (err) - goto out_dir; - } - return 0; - - -out_dir: - if (dir_de) { - kunmap(dir_page); - put_page(dir_page); - } -out_old: - kunmap(old_page); - put_page(old_page); -out: - return err; -} - -const struct inode_operations exofs_dir_inode_operations = { - .create = exofs_create, - .lookup = exofs_lookup, - .link = exofs_link, - .unlink = exofs_unlink, - .symlink = exofs_symlink, - .mkdir = exofs_mkdir, - .rmdir = exofs_rmdir, - .mknod = exofs_mknod, - .rename = exofs_rename, - .setattr = exofs_setattr, -}; - -const struct inode_operations exofs_special_inode_operations = { - .setattr = exofs_setattr, -}; diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c deleted file mode 100644 index 5331a15a61f1..000000000000 --- a/fs/exofs/ore.c +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include - -#include "ore_raid.h" - -MODULE_AUTHOR("Boaz Harrosh "); -MODULE_DESCRIPTION("Objects Raid Engine ore.ko"); -MODULE_LICENSE("GPL"); - -/* ore_verify_layout does a couple of things: - * 1. Given a minimum number of needed parameters fixes up the rest of the - * members to be operatonals for the ore. The needed parameters are those - * that are defined by the pnfs-objects layout STD. - * 2. Check to see if the current ore code actually supports these parameters - * for example stripe_unit must be a multple of the system PAGE_SIZE, - * and etc... - * 3. Cache some havily used calculations that will be needed by users. - */ - -enum { BIO_MAX_PAGES_KMALLOC = - (PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),}; - -int ore_verify_layout(unsigned total_comps, struct ore_layout *layout) -{ - u64 stripe_length; - - switch (layout->raid_algorithm) { - case PNFS_OSD_RAID_0: - layout->parity = 0; - break; - case PNFS_OSD_RAID_5: - layout->parity = 1; - break; - case PNFS_OSD_RAID_PQ: - layout->parity = 2; - break; - case PNFS_OSD_RAID_4: - default: - ORE_ERR("Only RAID_0/5/6 for now received-enum=%d\n", - layout->raid_algorithm); - return -EINVAL; - } - if (0 != (layout->stripe_unit & ~PAGE_MASK)) { - ORE_ERR("Stripe Unit(0x%llx)" - " must be Multples of PAGE_SIZE(0x%lx)\n", - _LLU(layout->stripe_unit), PAGE_SIZE); - return -EINVAL; - } - if (layout->group_width) { - if (!layout->group_depth) { - ORE_ERR("group_depth == 0 && group_width != 0\n"); - return -EINVAL; - } - if (total_comps < (layout->group_width * layout->mirrors_p1)) { - ORE_ERR("Data Map wrong, " - "numdevs=%d < group_width=%d * mirrors=%d\n", - total_comps, layout->group_width, - layout->mirrors_p1); - return -EINVAL; - } - layout->group_count = total_comps / layout->mirrors_p1 / - layout->group_width; - } else { - if (layout->group_depth) { - printk(KERN_NOTICE "Warning: group_depth ignored " - "group_width == 0 && group_depth == %lld\n", - _LLU(layout->group_depth)); - } - layout->group_width = total_comps / layout->mirrors_p1; - layout->group_depth = -1; - layout->group_count = 1; - } - - stripe_length = (u64)layout->group_width * layout->stripe_unit; - if (stripe_length >= (1ULL << 32)) { - ORE_ERR("Stripe_length(0x%llx) >= 32bit is not supported\n", - _LLU(stripe_length)); - return -EINVAL; - } - - layout->max_io_length = - (BIO_MAX_PAGES_KMALLOC * PAGE_SIZE - layout->stripe_unit) * - (layout->group_width - layout->parity); - if (layout->parity) { - unsigned stripe_length = - (layout->group_width - layout->parity) * - layout->stripe_unit; - - layout->max_io_length /= stripe_length; - layout->max_io_length *= stripe_length; - } - ORE_DBGMSG("max_io_length=0x%lx\n", layout->max_io_length); - - return 0; -} -EXPORT_SYMBOL(ore_verify_layout); - -static u8 *_ios_cred(struct ore_io_state *ios, unsigned index) -{ - return ios->oc->comps[index & ios->oc->single_comp].cred; -} - -static struct osd_obj_id *_ios_obj(struct ore_io_state *ios, unsigned index) -{ - return &ios->oc->comps[index & ios->oc->single_comp].obj; -} - -static struct osd_dev *_ios_od(struct ore_io_state *ios, unsigned index) -{ - ORE_DBGMSG2("oc->first_dev=%d oc->numdevs=%d i=%d oc->ods=%p\n", - ios->oc->first_dev, ios->oc->numdevs, index, - ios->oc->ods); - - return ore_comp_dev(ios->oc, index); -} - -int _ore_get_io_state(struct ore_layout *layout, - struct ore_components *oc, unsigned numdevs, - unsigned sgs_per_dev, unsigned num_par_pages, - struct ore_io_state **pios) -{ - struct ore_io_state *ios; - size_t size_ios, size_extra, size_total; - void *ios_extra; - - /* - * The desired layout looks like this, with the extra_allocation - * items pointed at from fields within ios or per_dev: - - struct __alloc_all_io_state { - struct ore_io_state ios; - struct ore_per_dev_state per_dev[numdevs]; - union { - struct osd_sg_entry sglist[sgs_per_dev * numdevs]; - struct page *pages[num_par_pages]; - } extra_allocation; - } whole_allocation; - - */ - - /* This should never happen, so abort early if it ever does. */ - if (sgs_per_dev && num_par_pages) { - ORE_DBGMSG("Tried to use both pages and sglist\n"); - *pios = NULL; - return -EINVAL; - } - - if (numdevs > (INT_MAX - sizeof(*ios)) / - sizeof(struct ore_per_dev_state)) - return -ENOMEM; - size_ios = sizeof(*ios) + sizeof(struct ore_per_dev_state) * numdevs; - - if (sgs_per_dev * numdevs > INT_MAX / sizeof(struct osd_sg_entry)) - return -ENOMEM; - if (num_par_pages > INT_MAX / sizeof(struct page *)) - return -ENOMEM; - size_extra = max(sizeof(struct osd_sg_entry) * (sgs_per_dev * numdevs), - sizeof(struct page *) * num_par_pages); - - size_total = size_ios + size_extra; - - if (likely(size_total <= PAGE_SIZE)) { - ios = kzalloc(size_total, GFP_KERNEL); - if (unlikely(!ios)) { - ORE_DBGMSG("Failed kzalloc bytes=%zd\n", size_total); - *pios = NULL; - return -ENOMEM; - } - ios_extra = (char *)ios + size_ios; - } else { - ios = kzalloc(size_ios, GFP_KERNEL); - if (unlikely(!ios)) { - ORE_DBGMSG("Failed alloc first part bytes=%zd\n", - size_ios); - *pios = NULL; - return -ENOMEM; - } - ios_extra = kzalloc(size_extra, GFP_KERNEL); - if (unlikely(!ios_extra)) { - ORE_DBGMSG("Failed alloc second part bytes=%zd\n", - size_extra); - kfree(ios); - *pios = NULL; - return -ENOMEM; - } - - /* In this case the per_dev[0].sgilist holds the pointer to - * be freed - */ - ios->extra_part_alloc = true; - } - - if (num_par_pages) { - ios->parity_pages = ios_extra; - ios->max_par_pages = num_par_pages; - } - if (sgs_per_dev) { - struct osd_sg_entry *sgilist = ios_extra; - unsigned d; - - for (d = 0; d < numdevs; ++d) { - ios->per_dev[d].sglist = sgilist; - sgilist += sgs_per_dev; - } - ios->sgs_per_dev = sgs_per_dev; - } - - ios->layout = layout; - ios->oc = oc; - *pios = ios; - return 0; -} - -/* Allocate an io_state for only a single group of devices - * - * If a user needs to call ore_read/write() this version must be used becase it - * allocates extra stuff for striping and raid. - * The ore might decide to only IO less then @length bytes do to alignmets - * and constrains as follows: - * - The IO cannot cross group boundary. - * - In raid5/6 The end of the IO must align at end of a stripe eg. - * (@offset + @length) % strip_size == 0. Or the complete range is within a - * single stripe. - * - Memory condition only permitted a shorter IO. (A user can use @length=~0 - * And check the returned ios->length for max_io_size.) - * - * The caller must check returned ios->length (and/or ios->nr_pages) and - * re-issue these pages that fall outside of ios->length - */ -int ore_get_rw_state(struct ore_layout *layout, struct ore_components *oc, - bool is_reading, u64 offset, u64 length, - struct ore_io_state **pios) -{ - struct ore_io_state *ios; - unsigned numdevs = layout->group_width * layout->mirrors_p1; - unsigned sgs_per_dev = 0, max_par_pages = 0; - int ret; - - if (layout->parity && length) { - unsigned data_devs = layout->group_width - layout->parity; - unsigned stripe_size = layout->stripe_unit * data_devs; - unsigned pages_in_unit = layout->stripe_unit / PAGE_SIZE; - u32 remainder; - u64 num_stripes; - u64 num_raid_units; - - num_stripes = div_u64_rem(length, stripe_size, &remainder); - if (remainder) - ++num_stripes; - - num_raid_units = num_stripes * layout->parity; - - if (is_reading) { - /* For reads add per_dev sglist array */ - /* TODO: Raid 6 we need twice more. Actually: - * num_stripes / LCMdP(W,P); - * if (W%P != 0) num_stripes *= parity; - */ - - /* first/last seg is split */ - num_raid_units += layout->group_width; - sgs_per_dev = div_u64(num_raid_units, data_devs) + 2; - } else { - /* For Writes add parity pages array. */ - max_par_pages = num_raid_units * pages_in_unit * - sizeof(struct page *); - } - } - - ret = _ore_get_io_state(layout, oc, numdevs, sgs_per_dev, max_par_pages, - pios); - if (unlikely(ret)) - return ret; - - ios = *pios; - ios->reading = is_reading; - ios->offset = offset; - - if (length) { - ore_calc_stripe_info(layout, offset, length, &ios->si); - ios->length = ios->si.length; - ios->nr_pages = ((ios->offset & (PAGE_SIZE - 1)) + - ios->length + PAGE_SIZE - 1) / PAGE_SIZE; - if (layout->parity) - _ore_post_alloc_raid_stuff(ios); - } - - return 0; -} -EXPORT_SYMBOL(ore_get_rw_state); - -/* Allocate an io_state for all the devices in the comps array - * - * This version of io_state allocation is used mostly by create/remove - * and trunc where we currently need all the devices. The only wastful - * bit is the read/write_attributes with no IO. Those sites should - * be converted to use ore_get_rw_state() with length=0 - */ -int ore_get_io_state(struct ore_layout *layout, struct ore_components *oc, - struct ore_io_state **pios) -{ - return _ore_get_io_state(layout, oc, oc->numdevs, 0, 0, pios); -} -EXPORT_SYMBOL(ore_get_io_state); - -void ore_put_io_state(struct ore_io_state *ios) -{ - if (ios) { - unsigned i; - - for (i = 0; i < ios->numdevs; i++) { - struct ore_per_dev_state *per_dev = &ios->per_dev[i]; - - if (per_dev->or) - osd_end_request(per_dev->or); - if (per_dev->bio) - bio_put(per_dev->bio); - } - - _ore_free_raid_stuff(ios); - kfree(ios); - } -} -EXPORT_SYMBOL(ore_put_io_state); - -static void _sync_done(struct ore_io_state *ios, void *p) -{ - struct completion *waiting = p; - - complete(waiting); -} - -static void _last_io(struct kref *kref) -{ - struct ore_io_state *ios = container_of( - kref, struct ore_io_state, kref); - - ios->done(ios, ios->private); -} - -static void _done_io(struct osd_request *or, void *p) -{ - struct ore_io_state *ios = p; - - kref_put(&ios->kref, _last_io); -} - -int ore_io_execute(struct ore_io_state *ios) -{ - DECLARE_COMPLETION_ONSTACK(wait); - bool sync = (ios->done == NULL); - int i, ret; - - if (sync) { - ios->done = _sync_done; - ios->private = &wait; - } - - for (i = 0; i < ios->numdevs; i++) { - struct osd_request *or = ios->per_dev[i].or; - if (unlikely(!or)) - continue; - - ret = osd_finalize_request(or, 0, _ios_cred(ios, i), NULL); - if (unlikely(ret)) { - ORE_DBGMSG("Failed to osd_finalize_request() => %d\n", - ret); - return ret; - } - } - - kref_init(&ios->kref); - - for (i = 0; i < ios->numdevs; i++) { - struct osd_request *or = ios->per_dev[i].or; - if (unlikely(!or)) - continue; - - kref_get(&ios->kref); - osd_execute_request_async(or, _done_io, ios); - } - - kref_put(&ios->kref, _last_io); - ret = 0; - - if (sync) { - wait_for_completion(&wait); - ret = ore_check_io(ios, NULL); - } - return ret; -} - -static void _clear_bio(struct bio *bio) -{ - struct bio_vec *bv; - unsigned i; - - bio_for_each_segment_all(bv, bio, i) { - unsigned this_count = bv->bv_len; - - if (likely(PAGE_SIZE == this_count)) - clear_highpage(bv->bv_page); - else - zero_user(bv->bv_page, bv->bv_offset, this_count); - } -} - -int ore_check_io(struct ore_io_state *ios, ore_on_dev_error on_dev_error) -{ - enum osd_err_priority acumulated_osd_err = 0; - int acumulated_lin_err = 0; - int i; - - for (i = 0; i < ios->numdevs; i++) { - struct osd_sense_info osi; - struct ore_per_dev_state *per_dev = &ios->per_dev[i]; - struct osd_request *or = per_dev->or; - int ret; - - if (unlikely(!or)) - continue; - - ret = osd_req_decode_sense(or, &osi); - if (likely(!ret)) - continue; - - if ((OSD_ERR_PRI_CLEAR_PAGES == osi.osd_err_pri) && - per_dev->bio) { - /* start read offset passed endof file. - * Note: if we do not have bio it means read-attributes - * In this case we should return error to caller. - */ - _clear_bio(per_dev->bio); - ORE_DBGMSG("start read offset passed end of file " - "offset=0x%llx, length=0x%llx\n", - _LLU(per_dev->offset), - _LLU(per_dev->length)); - - continue; /* we recovered */ - } - - if (on_dev_error) { - u64 residual = ios->reading ? - or->in.residual : or->out.residual; - u64 offset = (ios->offset + ios->length) - residual; - unsigned dev = per_dev->dev - ios->oc->first_dev; - struct ore_dev *od = ios->oc->ods[dev]; - - on_dev_error(ios, od, dev, osi.osd_err_pri, - offset, residual); - } - if (osi.osd_err_pri >= acumulated_osd_err) { - acumulated_osd_err = osi.osd_err_pri; - acumulated_lin_err = ret; - } - } - - return acumulated_lin_err; -} -EXPORT_SYMBOL(ore_check_io); - -/* - * L - logical offset into the file - * - * D - number of Data devices - * D = group_width - parity - * - * U - The number of bytes in a stripe within a group - * U = stripe_unit * D - * - * T - The number of bytes striped within a group of component objects - * (before advancing to the next group) - * T = U * group_depth - * - * S - The number of bytes striped across all component objects - * before the pattern repeats - * S = T * group_count - * - * M - The "major" (i.e., across all components) cycle number - * M = L / S - * - * G - Counts the groups from the beginning of the major cycle - * G = (L - (M * S)) / T [or (L % S) / T] - * - * H - The byte offset within the group - * H = (L - (M * S)) % T [or (L % S) % T] - * - * N - The "minor" (i.e., across the group) stripe number - * N = H / U - * - * C - The component index coresponding to L - * - * C = (H - (N * U)) / stripe_unit + G * D - * [or (L % U) / stripe_unit + G * D] - * - * O - The component offset coresponding to L - * O = L % stripe_unit + N * stripe_unit + M * group_depth * stripe_unit - * - * LCMdP – Parity cycle: Lowest Common Multiple of group_width, parity - * divide by parity - * LCMdP = lcm(group_width, parity) / parity - * - * R - The parity Rotation stripe - * (Note parity cycle always starts at a group's boundary) - * R = N % LCMdP - * - * I = the first parity device index - * I = (group_width + group_width - R*parity - parity) % group_width - * - * Craid - The component index Rotated - * Craid = (group_width + C - R*parity) % group_width - * (We add the group_width to avoid negative numbers modulo math) - */ -void ore_calc_stripe_info(struct ore_layout *layout, u64 file_offset, - u64 length, struct ore_striping_info *si) -{ - u32 stripe_unit = layout->stripe_unit; - u32 group_width = layout->group_width; - u64 group_depth = layout->group_depth; - u32 parity = layout->parity; - - u32 D = group_width - parity; - u32 U = D * stripe_unit; - u64 T = U * group_depth; - u64 S = T * layout->group_count; - u64 M = div64_u64(file_offset, S); - - /* - G = (L - (M * S)) / T - H = (L - (M * S)) % T - */ - u64 LmodS = file_offset - M * S; - u32 G = div64_u64(LmodS, T); - u64 H = LmodS - G * T; - - u32 N = div_u64(H, U); - u32 Nlast; - - /* "H - (N * U)" is just "H % U" so it's bound to u32 */ - u32 C = (u32)(H - (N * U)) / stripe_unit + G * group_width; - u32 first_dev = C - C % group_width; - - div_u64_rem(file_offset, stripe_unit, &si->unit_off); - - si->obj_offset = si->unit_off + (N * stripe_unit) + - (M * group_depth * stripe_unit); - si->cur_comp = C - first_dev; - si->cur_pg = si->unit_off / PAGE_SIZE; - - if (parity) { - u32 LCMdP = lcm(group_width, parity) / parity; - /* R = N % LCMdP; */ - u32 RxP = (N % LCMdP) * parity; - - si->par_dev = (group_width + group_width - parity - RxP) % - group_width + first_dev; - si->dev = (group_width + group_width + C - RxP) % - group_width + first_dev; - si->bytes_in_stripe = U; - si->first_stripe_start = M * S + G * T + N * U; - } else { - /* Make the math correct see _prepare_one_group */ - si->par_dev = group_width; - si->dev = C; - } - - si->dev *= layout->mirrors_p1; - si->par_dev *= layout->mirrors_p1; - si->offset = file_offset; - si->length = T - H; - if (si->length > length) - si->length = length; - - Nlast = div_u64(H + si->length + U - 1, U); - si->maxdevUnits = Nlast - N; - - si->M = M; -} -EXPORT_SYMBOL(ore_calc_stripe_info); - -int _ore_add_stripe_unit(struct ore_io_state *ios, unsigned *cur_pg, - unsigned pgbase, struct page **pages, - struct ore_per_dev_state *per_dev, int cur_len) -{ - unsigned pg = *cur_pg; - struct request_queue *q = - osd_request_queue(_ios_od(ios, per_dev->dev)); - unsigned len = cur_len; - int ret; - - if (per_dev->bio == NULL) { - unsigned bio_size; - - if (!ios->reading) { - bio_size = ios->si.maxdevUnits; - } else { - bio_size = (ios->si.maxdevUnits + 1) * - (ios->layout->group_width - ios->layout->parity) / - ios->layout->group_width; - } - bio_size *= (ios->layout->stripe_unit / PAGE_SIZE); - - per_dev->bio = bio_kmalloc(GFP_KERNEL, bio_size); - if (unlikely(!per_dev->bio)) { - ORE_DBGMSG("Failed to allocate BIO size=%u\n", - bio_size); - ret = -ENOMEM; - goto out; - } - } - - while (cur_len > 0) { - unsigned pglen = min_t(unsigned, PAGE_SIZE - pgbase, cur_len); - unsigned added_len; - - cur_len -= pglen; - - added_len = bio_add_pc_page(q, per_dev->bio, pages[pg], - pglen, pgbase); - if (unlikely(pglen != added_len)) { - /* If bi_vcnt == bi_max then this is a SW BUG */ - ORE_DBGMSG("Failed bio_add_pc_page bi_vcnt=0x%x " - "bi_max=0x%x BIO_MAX=0x%x cur_len=0x%x\n", - per_dev->bio->bi_vcnt, - per_dev->bio->bi_max_vecs, - BIO_MAX_PAGES_KMALLOC, cur_len); - ret = -ENOMEM; - goto out; - } - _add_stripe_page(ios->sp2d, &ios->si, pages[pg]); - - pgbase = 0; - ++pg; - } - BUG_ON(cur_len); - - per_dev->length += len; - *cur_pg = pg; - ret = 0; -out: /* we fail the complete unit on an error eg don't advance - * per_dev->length and cur_pg. This means that we might have a bigger - * bio than the CDB requested length (per_dev->length). That's fine - * only the oposite is fatal. - */ - return ret; -} - -static int _add_parity_units(struct ore_io_state *ios, - struct ore_striping_info *si, - unsigned dev, unsigned first_dev, - unsigned mirrors_p1, unsigned devs_in_group, - unsigned cur_len) -{ - unsigned do_parity; - int ret = 0; - - for (do_parity = ios->layout->parity; do_parity; --do_parity) { - struct ore_per_dev_state *per_dev; - - per_dev = &ios->per_dev[dev - first_dev]; - if (!per_dev->length && !per_dev->offset) { - /* Only/always the parity unit of the first - * stripe will be empty. So this is a chance to - * initialize the per_dev info. - */ - per_dev->dev = dev; - per_dev->offset = si->obj_offset - si->unit_off; - } - - ret = _ore_add_parity_unit(ios, si, per_dev, cur_len, - do_parity == 1); - if (unlikely(ret)) - break; - - if (do_parity != 1) { - dev = ((dev + mirrors_p1) % devs_in_group) + first_dev; - si->cur_comp = (si->cur_comp + 1) % - ios->layout->group_width; - } - } - - return ret; -} - -static int _prepare_for_striping(struct ore_io_state *ios) -{ - struct ore_striping_info *si = &ios->si; - unsigned stripe_unit = ios->layout->stripe_unit; - unsigned mirrors_p1 = ios->layout->mirrors_p1; - unsigned group_width = ios->layout->group_width; - unsigned devs_in_group = group_width * mirrors_p1; - unsigned dev = si->dev; - unsigned first_dev = dev - (dev % devs_in_group); - unsigned cur_pg = ios->pages_consumed; - u64 length = ios->length; - int ret = 0; - - if (!ios->pages) { - ios->numdevs = ios->layout->mirrors_p1; - return 0; - } - - BUG_ON(length > si->length); - - while (length) { - struct ore_per_dev_state *per_dev = - &ios->per_dev[dev - first_dev]; - unsigned cur_len, page_off = 0; - - if (!per_dev->length && !per_dev->offset) { - /* First time initialize the per_dev info. */ - per_dev->dev = dev; - if (dev == si->dev) { - WARN_ON(dev == si->par_dev); - per_dev->offset = si->obj_offset; - cur_len = stripe_unit - si->unit_off; - page_off = si->unit_off & ~PAGE_MASK; - BUG_ON(page_off && (page_off != ios->pgbase)); - } else { - per_dev->offset = si->obj_offset - si->unit_off; - cur_len = stripe_unit; - } - } else { - cur_len = stripe_unit; - } - if (cur_len >= length) - cur_len = length; - - ret = _ore_add_stripe_unit(ios, &cur_pg, page_off, ios->pages, - per_dev, cur_len); - if (unlikely(ret)) - goto out; - - length -= cur_len; - - dev = ((dev + mirrors_p1) % devs_in_group) + first_dev; - si->cur_comp = (si->cur_comp + 1) % group_width; - if (unlikely((dev == si->par_dev) || (!length && ios->sp2d))) { - if (!length && ios->sp2d) { - /* If we are writing and this is the very last - * stripe. then operate on parity dev. - */ - dev = si->par_dev; - /* If last stripe operate on parity comp */ - si->cur_comp = group_width - ios->layout->parity; - } - - /* In writes cur_len just means if it's the - * last one. See _ore_add_parity_unit. - */ - ret = _add_parity_units(ios, si, dev, first_dev, - mirrors_p1, devs_in_group, - ios->sp2d ? length : cur_len); - if (unlikely(ret)) - goto out; - - /* Rotate next par_dev backwards with wraping */ - si->par_dev = (devs_in_group + si->par_dev - - ios->layout->parity * mirrors_p1) % - devs_in_group + first_dev; - /* Next stripe, start fresh */ - si->cur_comp = 0; - si->cur_pg = 0; - si->obj_offset += cur_len; - si->unit_off = 0; - } - } -out: - ios->numdevs = devs_in_group; - ios->pages_consumed = cur_pg; - return ret; -} - -int ore_create(struct ore_io_state *ios) -{ - int i, ret; - - for (i = 0; i < ios->oc->numdevs; i++) { - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, i)); - if (unlikely(!or)) { - ORE_ERR("%s: osd_start_request failed\n", __func__); - ret = -ENOMEM; - goto out; - } - ios->per_dev[i].or = or; - ios->numdevs++; - - osd_req_create_object(or, _ios_obj(ios, i)); - } - ret = ore_io_execute(ios); - -out: - return ret; -} -EXPORT_SYMBOL(ore_create); - -int ore_remove(struct ore_io_state *ios) -{ - int i, ret; - - for (i = 0; i < ios->oc->numdevs; i++) { - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, i)); - if (unlikely(!or)) { - ORE_ERR("%s: osd_start_request failed\n", __func__); - ret = -ENOMEM; - goto out; - } - ios->per_dev[i].or = or; - ios->numdevs++; - - osd_req_remove_object(or, _ios_obj(ios, i)); - } - ret = ore_io_execute(ios); - -out: - return ret; -} -EXPORT_SYMBOL(ore_remove); - -static int _write_mirror(struct ore_io_state *ios, int cur_comp) -{ - struct ore_per_dev_state *master_dev = &ios->per_dev[cur_comp]; - unsigned dev = ios->per_dev[cur_comp].dev; - unsigned last_comp = cur_comp + ios->layout->mirrors_p1; - int ret = 0; - - if (ios->pages && !master_dev->length) - return 0; /* Just an empty slot */ - - for (; cur_comp < last_comp; ++cur_comp, ++dev) { - struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp]; - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, dev)); - if (unlikely(!or)) { - ORE_ERR("%s: osd_start_request failed\n", __func__); - ret = -ENOMEM; - goto out; - } - per_dev->or = or; - - if (ios->pages) { - struct bio *bio; - - if (per_dev != master_dev) { - bio = bio_clone_fast(master_dev->bio, - GFP_KERNEL, NULL); - if (unlikely(!bio)) { - ORE_DBGMSG( - "Failed to allocate BIO size=%u\n", - master_dev->bio->bi_max_vecs); - ret = -ENOMEM; - goto out; - } - - bio->bi_disk = NULL; - bio->bi_next = NULL; - per_dev->offset = master_dev->offset; - per_dev->length = master_dev->length; - per_dev->bio = bio; - per_dev->dev = dev; - } else { - bio = master_dev->bio; - /* FIXME: bio_set_dir() */ - bio_set_op_attrs(bio, REQ_OP_WRITE, 0); - } - - osd_req_write(or, _ios_obj(ios, cur_comp), - per_dev->offset, bio, per_dev->length); - ORE_DBGMSG("write(0x%llx) offset=0x%llx " - "length=0x%llx dev=%d\n", - _LLU(_ios_obj(ios, cur_comp)->id), - _LLU(per_dev->offset), - _LLU(per_dev->length), dev); - } else if (ios->kern_buff) { - per_dev->offset = ios->si.obj_offset; - per_dev->dev = ios->si.dev + dev; - - /* no cross device without page array */ - BUG_ON((ios->layout->group_width > 1) && - (ios->si.unit_off + ios->length > - ios->layout->stripe_unit)); - - ret = osd_req_write_kern(or, _ios_obj(ios, cur_comp), - per_dev->offset, - ios->kern_buff, ios->length); - if (unlikely(ret)) - goto out; - ORE_DBGMSG2("write_kern(0x%llx) offset=0x%llx " - "length=0x%llx dev=%d\n", - _LLU(_ios_obj(ios, cur_comp)->id), - _LLU(per_dev->offset), - _LLU(ios->length), per_dev->dev); - } else { - osd_req_set_attributes(or, _ios_obj(ios, cur_comp)); - ORE_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n", - _LLU(_ios_obj(ios, cur_comp)->id), - ios->out_attr_len, dev); - } - - if (ios->out_attr) - osd_req_add_set_attr_list(or, ios->out_attr, - ios->out_attr_len); - - if (ios->in_attr) - osd_req_add_get_attr_list(or, ios->in_attr, - ios->in_attr_len); - } - -out: - return ret; -} - -int ore_write(struct ore_io_state *ios) -{ - int i; - int ret; - - if (unlikely(ios->sp2d && !ios->r4w)) { - /* A library is attempting a RAID-write without providing - * a pages lock interface. - */ - WARN_ON_ONCE(1); - return -ENOTSUPP; - } - - ret = _prepare_for_striping(ios); - if (unlikely(ret)) - return ret; - - for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) { - ret = _write_mirror(ios, i); - if (unlikely(ret)) - return ret; - } - - ret = ore_io_execute(ios); - return ret; -} -EXPORT_SYMBOL(ore_write); - -int _ore_read_mirror(struct ore_io_state *ios, unsigned cur_comp) -{ - struct osd_request *or; - struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp]; - struct osd_obj_id *obj = _ios_obj(ios, cur_comp); - unsigned first_dev = (unsigned)obj->id; - - if (ios->pages && !per_dev->length) - return 0; /* Just an empty slot */ - - first_dev = per_dev->dev + first_dev % ios->layout->mirrors_p1; - or = osd_start_request(_ios_od(ios, first_dev)); - if (unlikely(!or)) { - ORE_ERR("%s: osd_start_request failed\n", __func__); - return -ENOMEM; - } - per_dev->or = or; - - if (ios->pages) { - if (per_dev->cur_sg) { - /* finalize the last sg_entry */ - _ore_add_sg_seg(per_dev, 0, false); - if (unlikely(!per_dev->cur_sg)) - return 0; /* Skip parity only device */ - - osd_req_read_sg(or, obj, per_dev->bio, - per_dev->sglist, per_dev->cur_sg); - } else { - /* The no raid case */ - osd_req_read(or, obj, per_dev->offset, - per_dev->bio, per_dev->length); - } - - ORE_DBGMSG("read(0x%llx) offset=0x%llx length=0x%llx" - " dev=%d sg_len=%d\n", _LLU(obj->id), - _LLU(per_dev->offset), _LLU(per_dev->length), - first_dev, per_dev->cur_sg); - } else { - BUG_ON(ios->kern_buff); - - osd_req_get_attributes(or, obj); - ORE_DBGMSG2("obj(0x%llx) get_attributes=%d dev=%d\n", - _LLU(obj->id), - ios->in_attr_len, first_dev); - } - if (ios->out_attr) - osd_req_add_set_attr_list(or, ios->out_attr, ios->out_attr_len); - - if (ios->in_attr) - osd_req_add_get_attr_list(or, ios->in_attr, ios->in_attr_len); - - return 0; -} - -int ore_read(struct ore_io_state *ios) -{ - int i; - int ret; - - ret = _prepare_for_striping(ios); - if (unlikely(ret)) - return ret; - - for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) { - ret = _ore_read_mirror(ios, i); - if (unlikely(ret)) - return ret; - } - - ret = ore_io_execute(ios); - return ret; -} -EXPORT_SYMBOL(ore_read); - -int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr) -{ - struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */ - void *iter = NULL; - int nelem; - - do { - nelem = 1; - osd_req_decode_get_attr_list(ios->per_dev[0].or, - &cur_attr, &nelem, &iter); - if ((cur_attr.attr_page == attr->attr_page) && - (cur_attr.attr_id == attr->attr_id)) { - attr->len = cur_attr.len; - attr->val_ptr = cur_attr.val_ptr; - return 0; - } - } while (iter); - - return -EIO; -} -EXPORT_SYMBOL(extract_attr_from_ios); - -static int _truncate_mirrors(struct ore_io_state *ios, unsigned cur_comp, - struct osd_attr *attr) -{ - int last_comp = cur_comp + ios->layout->mirrors_p1; - - for (; cur_comp < last_comp; ++cur_comp) { - struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp]; - struct osd_request *or; - - or = osd_start_request(_ios_od(ios, cur_comp)); - if (unlikely(!or)) { - ORE_ERR("%s: osd_start_request failed\n", __func__); - return -ENOMEM; - } - per_dev->or = or; - - osd_req_set_attributes(or, _ios_obj(ios, cur_comp)); - osd_req_add_set_attr_list(or, attr, 1); - } - - return 0; -} - -struct _trunc_info { - struct ore_striping_info si; - u64 prev_group_obj_off; - u64 next_group_obj_off; - - unsigned first_group_dev; - unsigned nex_group_dev; -}; - -static void _calc_trunk_info(struct ore_layout *layout, u64 file_offset, - struct _trunc_info *ti) -{ - unsigned stripe_unit = layout->stripe_unit; - - ore_calc_stripe_info(layout, file_offset, 0, &ti->si); - - ti->prev_group_obj_off = ti->si.M * stripe_unit; - ti->next_group_obj_off = ti->si.M ? (ti->si.M - 1) * stripe_unit : 0; - - ti->first_group_dev = ti->si.dev - (ti->si.dev % layout->group_width); - ti->nex_group_dev = ti->first_group_dev + layout->group_width; -} - -int ore_truncate(struct ore_layout *layout, struct ore_components *oc, - u64 size) -{ - struct ore_io_state *ios; - struct exofs_trunc_attr { - struct osd_attr attr; - __be64 newsize; - } *size_attrs; - struct _trunc_info ti; - int i, ret; - - ret = ore_get_io_state(layout, oc, &ios); - if (unlikely(ret)) - return ret; - - _calc_trunk_info(ios->layout, size, &ti); - - size_attrs = kcalloc(ios->oc->numdevs, sizeof(*size_attrs), - GFP_KERNEL); - if (unlikely(!size_attrs)) { - ret = -ENOMEM; - goto out; - } - - ios->numdevs = ios->oc->numdevs; - - for (i = 0; i < ios->numdevs; ++i) { - struct exofs_trunc_attr *size_attr = &size_attrs[i]; - u64 obj_size; - - if (i < ti.first_group_dev) - obj_size = ti.prev_group_obj_off; - else if (i >= ti.nex_group_dev) - obj_size = ti.next_group_obj_off; - else if (i < ti.si.dev) /* dev within this group */ - obj_size = ti.si.obj_offset + - ios->layout->stripe_unit - ti.si.unit_off; - else if (i == ti.si.dev) - obj_size = ti.si.obj_offset; - else /* i > ti.dev */ - obj_size = ti.si.obj_offset - ti.si.unit_off; - - size_attr->newsize = cpu_to_be64(obj_size); - size_attr->attr = g_attr_logical_length; - size_attr->attr.val_ptr = &size_attr->newsize; - - ORE_DBGMSG2("trunc(0x%llx) obj_offset=0x%llx dev=%d\n", - _LLU(oc->comps->obj.id), _LLU(obj_size), i); - ret = _truncate_mirrors(ios, i * ios->layout->mirrors_p1, - &size_attr->attr); - if (unlikely(ret)) - goto out; - } - ret = ore_io_execute(ios); - -out: - kfree(size_attrs); - ore_put_io_state(ios); - return ret; -} -EXPORT_SYMBOL(ore_truncate); - -const struct osd_attr g_attr_logical_length = ATTR_DEF( - OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8); -EXPORT_SYMBOL(g_attr_logical_length); diff --git a/fs/exofs/ore_raid.c b/fs/exofs/ore_raid.c deleted file mode 100644 index 199590f36203..000000000000 --- a/fs/exofs/ore_raid.c +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Copyright (C) 2011 - * Boaz Harrosh - * - * This file is part of the objects raid engine (ore). - * - * It is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License - * along with "ore". If not, write to the Free Software Foundation, Inc: - * "Free Software Foundation " - */ - -#include -#include - -#include "ore_raid.h" - -#undef ORE_DBGMSG2 -#define ORE_DBGMSG2 ORE_DBGMSG - -static struct page *_raid_page_alloc(void) -{ - return alloc_page(GFP_KERNEL); -} - -static void _raid_page_free(struct page *p) -{ - __free_page(p); -} - -/* This struct is forward declare in ore_io_state, but is private to here. - * It is put on ios->sp2d for RAID5/6 writes only. See _gen_xor_unit. - * - * __stripe_pages_2d is a 2d array of pages, and it is also a corner turn. - * Ascending page index access is sp2d(p-minor, c-major). But storage is - * sp2d[p-minor][c-major], so it can be properlly presented to the async-xor - * API. - */ -struct __stripe_pages_2d { - /* Cache some hot path repeated calculations */ - unsigned parity; - unsigned data_devs; - unsigned pages_in_unit; - - bool needed ; - - /* Array size is pages_in_unit (layout->stripe_unit / PAGE_SIZE) */ - struct __1_page_stripe { - bool alloc; - unsigned write_count; - struct async_submit_ctl submit; - struct dma_async_tx_descriptor *tx; - - /* The size of this array is data_devs + parity */ - struct page **pages; - struct page **scribble; - /* bool array, size of this array is data_devs */ - char *page_is_read; - } _1p_stripes[]; -}; - -/* This can get bigger then a page. So support multiple page allocations - * _sp2d_free should be called even if _sp2d_alloc fails (by returning - * none-zero). - */ -static int _sp2d_alloc(unsigned pages_in_unit, unsigned group_width, - unsigned parity, struct __stripe_pages_2d **psp2d) -{ - struct __stripe_pages_2d *sp2d; - unsigned data_devs = group_width - parity; - - /* - * Desired allocation layout is, though when larger than PAGE_SIZE, - * each struct __alloc_1p_arrays is separately allocated: - - struct _alloc_all_bytes { - struct __alloc_stripe_pages_2d { - struct __stripe_pages_2d sp2d; - struct __1_page_stripe _1p_stripes[pages_in_unit]; - } __asp2d; - struct __alloc_1p_arrays { - struct page *pages[group_width]; - struct page *scribble[group_width]; - char page_is_read[data_devs]; - } __a1pa[pages_in_unit]; - } *_aab; - - struct __alloc_1p_arrays *__a1pa; - struct __alloc_1p_arrays *__a1pa_end; - - */ - - char *__a1pa; - char *__a1pa_end; - - const size_t sizeof_stripe_pages_2d = - sizeof(struct __stripe_pages_2d) + - sizeof(struct __1_page_stripe) * pages_in_unit; - const size_t sizeof__a1pa = - ALIGN(sizeof(struct page *) * (2 * group_width) + data_devs, - sizeof(void *)); - const size_t sizeof__a1pa_arrays = sizeof__a1pa * pages_in_unit; - const size_t alloc_total = sizeof_stripe_pages_2d + - sizeof__a1pa_arrays; - - unsigned num_a1pa, alloc_size, i; - - /* FIXME: check these numbers in ore_verify_layout */ - BUG_ON(sizeof_stripe_pages_2d > PAGE_SIZE); - BUG_ON(sizeof__a1pa > PAGE_SIZE); - - /* - * If alloc_total would be larger than PAGE_SIZE, only allocate - * as many a1pa items as would fill the rest of the page, instead - * of the full pages_in_unit count. - */ - if (alloc_total > PAGE_SIZE) { - num_a1pa = (PAGE_SIZE - sizeof_stripe_pages_2d) / sizeof__a1pa; - alloc_size = sizeof_stripe_pages_2d + sizeof__a1pa * num_a1pa; - } else { - num_a1pa = pages_in_unit; - alloc_size = alloc_total; - } - - *psp2d = sp2d = kzalloc(alloc_size, GFP_KERNEL); - if (unlikely(!sp2d)) { - ORE_DBGMSG("!! Failed to alloc sp2d size=%d\n", alloc_size); - return -ENOMEM; - } - /* From here Just call _sp2d_free */ - - /* Find start of a1pa area. */ - __a1pa = (char *)sp2d + sizeof_stripe_pages_2d; - /* Find end of the _allocated_ a1pa area. */ - __a1pa_end = __a1pa + alloc_size; - - /* Allocate additionally needed a1pa items in PAGE_SIZE chunks. */ - for (i = 0; i < pages_in_unit; ++i) { - struct __1_page_stripe *stripe = &sp2d->_1p_stripes[i]; - - if (unlikely(__a1pa >= __a1pa_end)) { - num_a1pa = min_t(unsigned, PAGE_SIZE / sizeof__a1pa, - pages_in_unit - i); - alloc_size = sizeof__a1pa * num_a1pa; - - __a1pa = kzalloc(alloc_size, GFP_KERNEL); - if (unlikely(!__a1pa)) { - ORE_DBGMSG("!! Failed to _alloc_1p_arrays=%d\n", - num_a1pa); - return -ENOMEM; - } - __a1pa_end = __a1pa + alloc_size; - /* First *pages is marked for kfree of the buffer */ - stripe->alloc = true; - } - - /* - * Attach all _lp_stripes pointers to the allocation for - * it which was either part of the original PAGE_SIZE - * allocation or the subsequent allocation in this loop. - */ - stripe->pages = (void *)__a1pa; - stripe->scribble = stripe->pages + group_width; - stripe->page_is_read = (char *)stripe->scribble + group_width; - __a1pa += sizeof__a1pa; - } - - sp2d->parity = parity; - sp2d->data_devs = data_devs; - sp2d->pages_in_unit = pages_in_unit; - return 0; -} - -static void _sp2d_reset(struct __stripe_pages_2d *sp2d, - const struct _ore_r4w_op *r4w, void *priv) -{ - unsigned data_devs = sp2d->data_devs; - unsigned group_width = data_devs + sp2d->parity; - int p, c; - - if (!sp2d->needed) - return; - - for (c = data_devs - 1; c >= 0; --c) - for (p = sp2d->pages_in_unit - 1; p >= 0; --p) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - - if (_1ps->page_is_read[c]) { - struct page *page = _1ps->pages[c]; - - r4w->put_page(priv, page); - _1ps->page_is_read[c] = false; - } - } - - for (p = 0; p < sp2d->pages_in_unit; p++) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - - memset(_1ps->pages, 0, group_width * sizeof(*_1ps->pages)); - _1ps->write_count = 0; - _1ps->tx = NULL; - } - - sp2d->needed = false; -} - -static void _sp2d_free(struct __stripe_pages_2d *sp2d) -{ - unsigned i; - - if (!sp2d) - return; - - for (i = 0; i < sp2d->pages_in_unit; ++i) { - if (sp2d->_1p_stripes[i].alloc) - kfree(sp2d->_1p_stripes[i].pages); - } - - kfree(sp2d); -} - -static unsigned _sp2d_min_pg(struct __stripe_pages_2d *sp2d) -{ - unsigned p; - - for (p = 0; p < sp2d->pages_in_unit; p++) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - - if (_1ps->write_count) - return p; - } - - return ~0; -} - -static unsigned _sp2d_max_pg(struct __stripe_pages_2d *sp2d) -{ - int p; - - for (p = sp2d->pages_in_unit - 1; p >= 0; --p) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - - if (_1ps->write_count) - return p; - } - - return ~0; -} - -static void _gen_xor_unit(struct __stripe_pages_2d *sp2d) -{ - unsigned p; - unsigned tx_flags = ASYNC_TX_ACK; - - if (sp2d->parity == 1) - tx_flags |= ASYNC_TX_XOR_ZERO_DST; - - for (p = 0; p < sp2d->pages_in_unit; p++) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - - if (!_1ps->write_count) - continue; - - init_async_submit(&_1ps->submit, tx_flags, - NULL, NULL, NULL, (addr_conv_t *)_1ps->scribble); - - if (sp2d->parity == 1) - _1ps->tx = async_xor(_1ps->pages[sp2d->data_devs], - _1ps->pages, 0, sp2d->data_devs, - PAGE_SIZE, &_1ps->submit); - else /* parity == 2 */ - _1ps->tx = async_gen_syndrome(_1ps->pages, 0, - sp2d->data_devs + sp2d->parity, - PAGE_SIZE, &_1ps->submit); - } - - for (p = 0; p < sp2d->pages_in_unit; p++) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - /* NOTE: We wait for HW synchronously (I don't have such HW - * to test with.) Is parallelism needed with today's multi - * cores? - */ - async_tx_issue_pending(_1ps->tx); - } -} - -void _ore_add_stripe_page(struct __stripe_pages_2d *sp2d, - struct ore_striping_info *si, struct page *page) -{ - struct __1_page_stripe *_1ps; - - sp2d->needed = true; - - _1ps = &sp2d->_1p_stripes[si->cur_pg]; - _1ps->pages[si->cur_comp] = page; - ++_1ps->write_count; - - si->cur_pg = (si->cur_pg + 1) % sp2d->pages_in_unit; - /* si->cur_comp is advanced outside at main loop */ -} - -void _ore_add_sg_seg(struct ore_per_dev_state *per_dev, unsigned cur_len, - bool not_last) -{ - struct osd_sg_entry *sge; - - ORE_DBGMSG("dev=%d cur_len=0x%x not_last=%d cur_sg=%d " - "offset=0x%llx length=0x%x last_sgs_total=0x%x\n", - per_dev->dev, cur_len, not_last, per_dev->cur_sg, - _LLU(per_dev->offset), per_dev->length, - per_dev->last_sgs_total); - - if (!per_dev->cur_sg) { - sge = per_dev->sglist; - - /* First time we prepare two entries */ - if (per_dev->length) { - ++per_dev->cur_sg; - sge->offset = per_dev->offset; - sge->len = per_dev->length; - } else { - /* Here the parity is the first unit of this object. - * This happens every time we reach a parity device on - * the same stripe as the per_dev->offset. We need to - * just skip this unit. - */ - per_dev->offset += cur_len; - return; - } - } else { - /* finalize the last one */ - sge = &per_dev->sglist[per_dev->cur_sg - 1]; - sge->len = per_dev->length - per_dev->last_sgs_total; - } - - if (not_last) { - /* Partly prepare the next one */ - struct osd_sg_entry *next_sge = sge + 1; - - ++per_dev->cur_sg; - next_sge->offset = sge->offset + sge->len + cur_len; - /* Save cur len so we know how mutch was added next time */ - per_dev->last_sgs_total = per_dev->length; - next_sge->len = 0; - } else if (!sge->len) { - /* Optimize for when the last unit is a parity */ - --per_dev->cur_sg; - } -} - -static int _alloc_read_4_write(struct ore_io_state *ios) -{ - struct ore_layout *layout = ios->layout; - int ret; - /* We want to only read those pages not in cache so worst case - * is a stripe populated with every other page - */ - unsigned sgs_per_dev = ios->sp2d->pages_in_unit + 2; - - ret = _ore_get_io_state(layout, ios->oc, - layout->group_width * layout->mirrors_p1, - sgs_per_dev, 0, &ios->ios_read_4_write); - return ret; -} - -/* @si contains info of the to-be-inserted page. Update of @si should be - * maintained by caller. Specificaly si->dev, si->obj_offset, ... - */ -static int _add_to_r4w(struct ore_io_state *ios, struct ore_striping_info *si, - struct page *page, unsigned pg_len) -{ - struct request_queue *q; - struct ore_per_dev_state *per_dev; - struct ore_io_state *read_ios; - unsigned first_dev = si->dev - (si->dev % - (ios->layout->group_width * ios->layout->mirrors_p1)); - unsigned comp = si->dev - first_dev; - unsigned added_len; - - if (!ios->ios_read_4_write) { - int ret = _alloc_read_4_write(ios); - - if (unlikely(ret)) - return ret; - } - - read_ios = ios->ios_read_4_write; - read_ios->numdevs = ios->layout->group_width * ios->layout->mirrors_p1; - - per_dev = &read_ios->per_dev[comp]; - if (!per_dev->length) { - per_dev->bio = bio_kmalloc(GFP_KERNEL, - ios->sp2d->pages_in_unit); - if (unlikely(!per_dev->bio)) { - ORE_DBGMSG("Failed to allocate BIO size=%u\n", - ios->sp2d->pages_in_unit); - return -ENOMEM; - } - per_dev->offset = si->obj_offset; - per_dev->dev = si->dev; - } else if (si->obj_offset != (per_dev->offset + per_dev->length)) { - u64 gap = si->obj_offset - (per_dev->offset + per_dev->length); - - _ore_add_sg_seg(per_dev, gap, true); - } - q = osd_request_queue(ore_comp_dev(read_ios->oc, per_dev->dev)); - added_len = bio_add_pc_page(q, per_dev->bio, page, pg_len, - si->obj_offset % PAGE_SIZE); - if (unlikely(added_len != pg_len)) { - ORE_DBGMSG("Failed to bio_add_pc_page bi_vcnt=%d\n", - per_dev->bio->bi_vcnt); - return -ENOMEM; - } - - per_dev->length += pg_len; - return 0; -} - -/* read the beginning of an unaligned first page */ -static int _add_to_r4w_first_page(struct ore_io_state *ios, struct page *page) -{ - struct ore_striping_info si; - unsigned pg_len; - - ore_calc_stripe_info(ios->layout, ios->offset, 0, &si); - - pg_len = si.obj_offset % PAGE_SIZE; - si.obj_offset -= pg_len; - - ORE_DBGMSG("offset=0x%llx len=0x%x index=0x%lx dev=%x\n", - _LLU(si.obj_offset), pg_len, page->index, si.dev); - - return _add_to_r4w(ios, &si, page, pg_len); -} - -/* read the end of an incomplete last page */ -static int _add_to_r4w_last_page(struct ore_io_state *ios, u64 *offset) -{ - struct ore_striping_info si; - struct page *page; - unsigned pg_len, p, c; - - ore_calc_stripe_info(ios->layout, *offset, 0, &si); - - p = si.cur_pg; - c = si.cur_comp; - page = ios->sp2d->_1p_stripes[p].pages[c]; - - pg_len = PAGE_SIZE - (si.unit_off % PAGE_SIZE); - *offset += pg_len; - - ORE_DBGMSG("p=%d, c=%d next-offset=0x%llx len=0x%x dev=%x par_dev=%d\n", - p, c, _LLU(*offset), pg_len, si.dev, si.par_dev); - - BUG_ON(!page); - - return _add_to_r4w(ios, &si, page, pg_len); -} - -static void _mark_read4write_pages_uptodate(struct ore_io_state *ios, int ret) -{ - struct bio_vec *bv; - unsigned i, d; - - /* loop on all devices all pages */ - for (d = 0; d < ios->numdevs; d++) { - struct bio *bio = ios->per_dev[d].bio; - - if (!bio) - continue; - - bio_for_each_segment_all(bv, bio, i) { - struct page *page = bv->bv_page; - - SetPageUptodate(page); - if (PageError(page)) - ClearPageError(page); - } - } -} - -/* read_4_write is hacked to read the start of the first stripe and/or - * the end of the last stripe. If needed, with an sg-gap at each device/page. - * It is assumed to be called after the to_be_written pages of the first stripe - * are populating ios->sp2d[][] - * - * NOTE: We call ios->r4w->lock_fn for all pages needed for parity calculations - * These pages are held at sp2d[p].pages[c] but with - * sp2d[p].page_is_read[c] = true. At _sp2d_reset these pages are - * ios->r4w->lock_fn(). The ios->r4w->lock_fn might signal that the page is - * @uptodate=true, so we don't need to read it, only unlock, after IO. - * - * TODO: The read_4_write should calc a need_to_read_pages_count, if bigger then - * to-be-written count, we should consider the xor-in-place mode. - * need_to_read_pages_count is the actual number of pages not present in cache. - * maybe "devs_in_group - ios->sp2d[p].write_count" is a good enough - * approximation? In this mode the read pages are put in the empty places of - * ios->sp2d[p][*], xor is calculated the same way. These pages are - * allocated/freed and don't go through cache - */ -static int _read_4_write_first_stripe(struct ore_io_state *ios) -{ - struct ore_striping_info read_si; - struct __stripe_pages_2d *sp2d = ios->sp2d; - u64 offset = ios->si.first_stripe_start; - unsigned c, p, min_p = sp2d->pages_in_unit, max_p = -1; - - if (offset == ios->offset) /* Go to start collect $200 */ - goto read_last_stripe; - - min_p = _sp2d_min_pg(sp2d); - max_p = _sp2d_max_pg(sp2d); - - ORE_DBGMSG("stripe_start=0x%llx ios->offset=0x%llx min_p=%d max_p=%d\n", - offset, ios->offset, min_p, max_p); - - for (c = 0; ; c++) { - ore_calc_stripe_info(ios->layout, offset, 0, &read_si); - read_si.obj_offset += min_p * PAGE_SIZE; - offset += min_p * PAGE_SIZE; - for (p = min_p; p <= max_p; p++) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - struct page **pp = &_1ps->pages[c]; - bool uptodate; - - if (*pp) { - if (ios->offset % PAGE_SIZE) - /* Read the remainder of the page */ - _add_to_r4w_first_page(ios, *pp); - /* to-be-written pages start here */ - goto read_last_stripe; - } - - *pp = ios->r4w->get_page(ios->private, offset, - &uptodate); - if (unlikely(!*pp)) - return -ENOMEM; - - if (!uptodate) - _add_to_r4w(ios, &read_si, *pp, PAGE_SIZE); - - /* Mark read-pages to be cache_released */ - _1ps->page_is_read[c] = true; - read_si.obj_offset += PAGE_SIZE; - offset += PAGE_SIZE; - } - offset += (sp2d->pages_in_unit - p) * PAGE_SIZE; - } - -read_last_stripe: - return 0; -} - -static int _read_4_write_last_stripe(struct ore_io_state *ios) -{ - struct ore_striping_info read_si; - struct __stripe_pages_2d *sp2d = ios->sp2d; - u64 offset; - u64 last_stripe_end; - unsigned bytes_in_stripe = ios->si.bytes_in_stripe; - unsigned c, p, min_p = sp2d->pages_in_unit, max_p = -1; - - offset = ios->offset + ios->length; - if (offset % PAGE_SIZE) - _add_to_r4w_last_page(ios, &offset); - /* offset will be aligned to next page */ - - last_stripe_end = div_u64(offset + bytes_in_stripe - 1, bytes_in_stripe) - * bytes_in_stripe; - if (offset == last_stripe_end) /* Optimize for the aligned case */ - goto read_it; - - ore_calc_stripe_info(ios->layout, offset, 0, &read_si); - p = read_si.cur_pg; - c = read_si.cur_comp; - - if (min_p == sp2d->pages_in_unit) { - /* Didn't do it yet */ - min_p = _sp2d_min_pg(sp2d); - max_p = _sp2d_max_pg(sp2d); - } - - ORE_DBGMSG("offset=0x%llx stripe_end=0x%llx min_p=%d max_p=%d\n", - offset, last_stripe_end, min_p, max_p); - - while (offset < last_stripe_end) { - struct __1_page_stripe *_1ps = &sp2d->_1p_stripes[p]; - - if ((min_p <= p) && (p <= max_p)) { - struct page *page; - bool uptodate; - - BUG_ON(_1ps->pages[c]); - page = ios->r4w->get_page(ios->private, offset, - &uptodate); - if (unlikely(!page)) - return -ENOMEM; - - _1ps->pages[c] = page; - /* Mark read-pages to be cache_released */ - _1ps->page_is_read[c] = true; - if (!uptodate) - _add_to_r4w(ios, &read_si, page, PAGE_SIZE); - } - - offset += PAGE_SIZE; - if (p == (sp2d->pages_in_unit - 1)) { - ++c; - p = 0; - ore_calc_stripe_info(ios->layout, offset, 0, &read_si); - } else { - read_si.obj_offset += PAGE_SIZE; - ++p; - } - } - -read_it: - return 0; -} - -static int _read_4_write_execute(struct ore_io_state *ios) -{ - struct ore_io_state *ios_read; - unsigned i; - int ret; - - ios_read = ios->ios_read_4_write; - if (!ios_read) - return 0; - - /* FIXME: Ugly to signal _sbi_read_mirror that we have bio(s). Change - * to check for per_dev->bio - */ - ios_read->pages = ios->pages; - - /* Now read these devices */ - for (i = 0; i < ios_read->numdevs; i += ios_read->layout->mirrors_p1) { - ret = _ore_read_mirror(ios_read, i); - if (unlikely(ret)) - return ret; - } - - ret = ore_io_execute(ios_read); /* Synchronus execution */ - if (unlikely(ret)) { - ORE_DBGMSG("!! ore_io_execute => %d\n", ret); - return ret; - } - - _mark_read4write_pages_uptodate(ios_read, ret); - ore_put_io_state(ios_read); - ios->ios_read_4_write = NULL; /* Might need a reuse at last stripe */ - return 0; -} - -/* In writes @cur_len means length left. .i.e cur_len==0 is the last parity U */ -int _ore_add_parity_unit(struct ore_io_state *ios, - struct ore_striping_info *si, - struct ore_per_dev_state *per_dev, - unsigned cur_len, bool do_xor) -{ - if (ios->reading) { - if (per_dev->cur_sg >= ios->sgs_per_dev) { - ORE_DBGMSG("cur_sg(%d) >= sgs_per_dev(%d)\n" , - per_dev->cur_sg, ios->sgs_per_dev); - return -ENOMEM; - } - _ore_add_sg_seg(per_dev, cur_len, true); - } else { - struct __stripe_pages_2d *sp2d = ios->sp2d; - struct page **pages = ios->parity_pages + ios->cur_par_page; - unsigned num_pages; - unsigned array_start = 0; - unsigned i; - int ret; - - si->cur_pg = _sp2d_min_pg(sp2d); - num_pages = _sp2d_max_pg(sp2d) + 1 - si->cur_pg; - - if (!per_dev->length) { - per_dev->offset += si->cur_pg * PAGE_SIZE; - /* If first stripe, Read in all read4write pages - * (if needed) before we calculate the first parity. - */ - if (do_xor) - _read_4_write_first_stripe(ios); - } - if (!cur_len && do_xor) - /* If last stripe r4w pages of last stripe */ - _read_4_write_last_stripe(ios); - _read_4_write_execute(ios); - - for (i = 0; i < num_pages; i++) { - pages[i] = _raid_page_alloc(); - if (unlikely(!pages[i])) - return -ENOMEM; - - ++(ios->cur_par_page); - } - - BUG_ON(si->cur_comp < sp2d->data_devs); - BUG_ON(si->cur_pg + num_pages > sp2d->pages_in_unit); - - ret = _ore_add_stripe_unit(ios, &array_start, 0, pages, - per_dev, num_pages * PAGE_SIZE); - if (unlikely(ret)) - return ret; - - if (do_xor) { - _gen_xor_unit(sp2d); - _sp2d_reset(sp2d, ios->r4w, ios->private); - } - } - return 0; -} - -int _ore_post_alloc_raid_stuff(struct ore_io_state *ios) -{ - if (ios->parity_pages) { - struct ore_layout *layout = ios->layout; - unsigned pages_in_unit = layout->stripe_unit / PAGE_SIZE; - - if (_sp2d_alloc(pages_in_unit, layout->group_width, - layout->parity, &ios->sp2d)) { - return -ENOMEM; - } - } - return 0; -} - -void _ore_free_raid_stuff(struct ore_io_state *ios) -{ - if (ios->sp2d) { /* writing and raid */ - unsigned i; - - for (i = 0; i < ios->cur_par_page; i++) { - struct page *page = ios->parity_pages[i]; - - if (page) - _raid_page_free(page); - } - if (ios->extra_part_alloc) - kfree(ios->parity_pages); - /* If IO returned an error pages might need unlocking */ - _sp2d_reset(ios->sp2d, ios->r4w, ios->private); - _sp2d_free(ios->sp2d); - } else { - /* Will only be set if raid reading && sglist is big */ - if (ios->extra_part_alloc) - kfree(ios->per_dev[0].sglist); - } - if (ios->ios_read_4_write) - ore_put_io_state(ios->ios_read_4_write); -} diff --git a/fs/exofs/ore_raid.h b/fs/exofs/ore_raid.h deleted file mode 100644 index a6e746775570..000000000000 --- a/fs/exofs/ore_raid.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) from 2011 - * Boaz Harrosh - * - * This file is part of the objects raid engine (ore). - * - * It is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License - * along with "ore". If not, write to the Free Software Foundation, Inc: - * "Free Software Foundation " - */ - -#include - -#define ORE_ERR(fmt, a...) printk(KERN_ERR "ore: " fmt, ##a) - -#ifdef CONFIG_EXOFS_DEBUG -#define ORE_DBGMSG(fmt, a...) \ - printk(KERN_NOTICE "ore @%s:%d: " fmt, __func__, __LINE__, ##a) -#else -#define ORE_DBGMSG(fmt, a...) \ - do { if (0) printk(fmt, ##a); } while (0) -#endif - -/* u64 has problems with printk this will cast it to unsigned long long */ -#define _LLU(x) (unsigned long long)(x) - -#define ORE_DBGMSG2(M...) do {} while (0) -/* #define ORE_DBGMSG2 ORE_DBGMSG */ - -/* ios_raid.c stuff needed by ios.c */ -int _ore_post_alloc_raid_stuff(struct ore_io_state *ios); -void _ore_free_raid_stuff(struct ore_io_state *ios); - -void _ore_add_sg_seg(struct ore_per_dev_state *per_dev, unsigned cur_len, - bool not_last); -int _ore_add_parity_unit(struct ore_io_state *ios, struct ore_striping_info *si, - struct ore_per_dev_state *per_dev, unsigned cur_len, - bool do_xor); -void _ore_add_stripe_page(struct __stripe_pages_2d *sp2d, - struct ore_striping_info *si, struct page *page); -static inline void _add_stripe_page(struct __stripe_pages_2d *sp2d, - struct ore_striping_info *si, struct page *page) -{ - if (!sp2d) /* Inline the fast path */ - return; /* Hay no raid stuff */ - _ore_add_stripe_page(sp2d, si, page); -} - -/* ios.c stuff needed by ios_raid.c */ -int _ore_get_io_state(struct ore_layout *layout, - struct ore_components *oc, unsigned numdevs, - unsigned sgs_per_dev, unsigned num_par_pages, - struct ore_io_state **pios); -int _ore_add_stripe_unit(struct ore_io_state *ios, unsigned *cur_pg, - unsigned pgbase, struct page **pages, - struct ore_per_dev_state *per_dev, int cur_len); -int _ore_read_mirror(struct ore_io_state *ios, unsigned cur_comp); -int ore_io_execute(struct ore_io_state *ios); diff --git a/fs/exofs/super.c b/fs/exofs/super.c deleted file mode 100644 index fc80c7233fa5..000000000000 --- a/fs/exofs/super.c +++ /dev/null @@ -1,1071 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs 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. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs 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 exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "exofs.h" - -#define EXOFS_DBGMSG2(M...) do {} while (0) - -/****************************************************************************** - * MOUNT OPTIONS - *****************************************************************************/ - -/* - * struct to hold what we get from mount options - */ -struct exofs_mountopt { - bool is_osdname; - const char *dev_name; - uint64_t pid; - int timeout; -}; - -/* - * exofs-specific mount-time options. - */ -enum { Opt_name, Opt_pid, Opt_to, Opt_err }; - -/* - * Our mount-time options. These should ideally be 64-bit unsigned, but the - * kernel's parsing functions do not currently support that. 32-bit should be - * sufficient for most applications now. - */ -static match_table_t tokens = { - {Opt_name, "osdname=%s"}, - {Opt_pid, "pid=%u"}, - {Opt_to, "to=%u"}, - {Opt_err, NULL} -}; - -/* - * The main option parsing method. Also makes sure that all of the mandatory - * mount options were set. - */ -static int parse_options(char *options, struct exofs_mountopt *opts) -{ - char *p; - substring_t args[MAX_OPT_ARGS]; - int option; - bool s_pid = false; - - EXOFS_DBGMSG("parse_options %s\n", options); - /* defaults */ - memset(opts, 0, sizeof(*opts)); - opts->timeout = BLK_DEFAULT_SG_TIMEOUT; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - char str[32]; - - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_name: - kfree(opts->dev_name); - opts->dev_name = match_strdup(&args[0]); - if (unlikely(!opts->dev_name)) { - EXOFS_ERR("Error allocating dev_name"); - return -ENOMEM; - } - opts->is_osdname = true; - break; - case Opt_pid: - if (0 == match_strlcpy(str, &args[0], sizeof(str))) - return -EINVAL; - opts->pid = simple_strtoull(str, NULL, 0); - if (opts->pid < EXOFS_MIN_PID) { - EXOFS_ERR("Partition ID must be >= %u", - EXOFS_MIN_PID); - return -EINVAL; - } - s_pid = true; - break; - case Opt_to: - if (match_int(&args[0], &option)) - return -EINVAL; - if (option <= 0) { - EXOFS_ERR("Timeout must be > 0"); - return -EINVAL; - } - opts->timeout = option * HZ; - break; - } - } - - if (!s_pid) { - EXOFS_ERR("Need to specify the following options:\n"); - EXOFS_ERR(" -o pid=pid_no_to_use\n"); - return -EINVAL; - } - - return 0; -} - -/****************************************************************************** - * INODE CACHE - *****************************************************************************/ - -/* - * Our inode cache. Isn't it pretty? - */ -static struct kmem_cache *exofs_inode_cachep; - -/* - * Allocate an inode in the cache - */ -static struct inode *exofs_alloc_inode(struct super_block *sb) -{ - struct exofs_i_info *oi; - - oi = kmem_cache_alloc(exofs_inode_cachep, GFP_KERNEL); - if (!oi) - return NULL; - - inode_set_iversion(&oi->vfs_inode, 1); - return &oi->vfs_inode; -} - -static void exofs_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(exofs_inode_cachep, exofs_i(inode)); -} - -/* - * Remove an inode from the cache - */ -static void exofs_destroy_inode(struct inode *inode) -{ - call_rcu(&inode->i_rcu, exofs_i_callback); -} - -/* - * Initialize the inode - */ -static void exofs_init_once(void *foo) -{ - struct exofs_i_info *oi = foo; - - inode_init_once(&oi->vfs_inode); -} - -/* - * Create and initialize the inode cache - */ -static int init_inodecache(void) -{ - exofs_inode_cachep = kmem_cache_create_usercopy("exofs_inode_cache", - sizeof(struct exofs_i_info), 0, - SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | - SLAB_ACCOUNT, - offsetof(struct exofs_i_info, i_data), - sizeof_field(struct exofs_i_info, i_data), - exofs_init_once); - if (exofs_inode_cachep == NULL) - return -ENOMEM; - return 0; -} - -/* - * Destroy the inode cache - */ -static void destroy_inodecache(void) -{ - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - kmem_cache_destroy(exofs_inode_cachep); -} - -/****************************************************************************** - * Some osd helpers - *****************************************************************************/ -void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], const struct osd_obj_id *obj) -{ - osd_sec_init_nosec_doall_caps(cred_a, obj, false, true); -} - -static int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj, - u64 offset, void *p, unsigned length) -{ - struct osd_request *or = osd_start_request(od); -/* struct osd_sense_info osi = {.key = 0};*/ - int ret; - - if (unlikely(!or)) { - EXOFS_DBGMSG("%s: osd_start_request failed.\n", __func__); - return -ENOMEM; - } - ret = osd_req_read_kern(or, obj, offset, p, length); - if (unlikely(ret)) { - EXOFS_DBGMSG("%s: osd_req_read_kern failed.\n", __func__); - goto out; - } - - ret = osd_finalize_request(or, 0, cred, NULL); - if (unlikely(ret)) { - EXOFS_DBGMSG("Failed to osd_finalize_request() => %d\n", ret); - goto out; - } - - ret = osd_execute_request(or); - if (unlikely(ret)) - EXOFS_DBGMSG("osd_execute_request() => %d\n", ret); - /* osd_req_decode_sense(or, ret); */ - -out: - osd_end_request(or); - EXOFS_DBGMSG2("read_kern(0x%llx) offset=0x%llx " - "length=0x%llx dev=%p ret=>%d\n", - _LLU(obj->id), _LLU(offset), _LLU(length), od, ret); - return ret; -} - -static const struct osd_attr g_attr_sb_stats = ATTR_DEF( - EXOFS_APAGE_SB_DATA, - EXOFS_ATTR_SB_STATS, - sizeof(struct exofs_sb_stats)); - -static int __sbi_read_stats(struct exofs_sb_info *sbi) -{ - struct osd_attr attrs[] = { - [0] = g_attr_sb_stats, - }; - struct ore_io_state *ios; - int ret; - - ret = ore_get_io_state(&sbi->layout, &sbi->oc, &ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); - return ret; - } - - ios->in_attr = attrs; - ios->in_attr_len = ARRAY_SIZE(attrs); - - ret = ore_read(ios); - if (unlikely(ret)) { - EXOFS_ERR("Error reading super_block stats => %d\n", ret); - goto out; - } - - ret = extract_attr_from_ios(ios, &attrs[0]); - if (ret) { - EXOFS_ERR("%s: extract_attr of sb_stats failed\n", __func__); - goto out; - } - if (attrs[0].len) { - struct exofs_sb_stats *ess; - - if (unlikely(attrs[0].len != sizeof(*ess))) { - EXOFS_ERR("%s: Wrong version of exofs_sb_stats " - "size(%d) != expected(%zd)\n", - __func__, attrs[0].len, sizeof(*ess)); - goto out; - } - - ess = attrs[0].val_ptr; - sbi->s_nextid = le64_to_cpu(ess->s_nextid); - sbi->s_numfiles = le32_to_cpu(ess->s_numfiles); - } - -out: - ore_put_io_state(ios); - return ret; -} - -static void stats_done(struct ore_io_state *ios, void *p) -{ - ore_put_io_state(ios); - /* Good thanks nothing to do anymore */ -} - -/* Asynchronously write the stats attribute */ -int exofs_sbi_write_stats(struct exofs_sb_info *sbi) -{ - struct osd_attr attrs[] = { - [0] = g_attr_sb_stats, - }; - struct ore_io_state *ios; - int ret; - - ret = ore_get_io_state(&sbi->layout, &sbi->oc, &ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__); - return ret; - } - - sbi->s_ess.s_nextid = cpu_to_le64(sbi->s_nextid); - sbi->s_ess.s_numfiles = cpu_to_le64(sbi->s_numfiles); - attrs[0].val_ptr = &sbi->s_ess; - - - ios->done = stats_done; - ios->private = sbi; - ios->out_attr = attrs; - ios->out_attr_len = ARRAY_SIZE(attrs); - - ret = ore_write(ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: ore_write failed.\n", __func__); - ore_put_io_state(ios); - } - - return ret; -} - -/****************************************************************************** - * SUPERBLOCK FUNCTIONS - *****************************************************************************/ -static const struct super_operations exofs_sops; -static const struct export_operations exofs_export_ops; - -/* - * Write the superblock to the OSD - */ -static int exofs_sync_fs(struct super_block *sb, int wait) -{ - struct exofs_sb_info *sbi; - struct exofs_fscb *fscb; - struct ore_comp one_comp; - struct ore_components oc; - struct ore_io_state *ios; - int ret = -ENOMEM; - - fscb = kmalloc(sizeof(*fscb), GFP_KERNEL); - if (unlikely(!fscb)) - return -ENOMEM; - - sbi = sb->s_fs_info; - - /* NOTE: We no longer dirty the super_block anywhere in exofs. The - * reason we write the fscb here on unmount is so we can stay backwards - * compatible with fscb->s_version == 1. (What we are not compatible - * with is if a new version FS crashed and then we try to mount an old - * version). Otherwise the exofs_fscb is read-only from mkfs time. All - * the writeable info is set in exofs_sbi_write_stats() above. - */ - - exofs_init_comps(&oc, &one_comp, sbi, EXOFS_SUPER_ID); - - ret = ore_get_io_state(&sbi->layout, &oc, &ios); - if (unlikely(ret)) - goto out; - - ios->length = offsetof(struct exofs_fscb, s_dev_table_oid); - memset(fscb, 0, ios->length); - fscb->s_nextid = cpu_to_le64(sbi->s_nextid); - fscb->s_numfiles = cpu_to_le64(sbi->s_numfiles); - fscb->s_magic = cpu_to_le16(sb->s_magic); - fscb->s_newfs = 0; - fscb->s_version = EXOFS_FSCB_VER; - - ios->offset = 0; - ios->kern_buff = fscb; - - ret = ore_write(ios); - if (unlikely(ret)) - EXOFS_ERR("%s: ore_write failed.\n", __func__); - -out: - EXOFS_DBGMSG("s_nextid=0x%llx ret=%d\n", _LLU(sbi->s_nextid), ret); - ore_put_io_state(ios); - kfree(fscb); - return ret; -} - -static void _exofs_print_device(const char *msg, const char *dev_path, - struct osd_dev *od, u64 pid) -{ - const struct osd_dev_info *odi = osduld_device_info(od); - - printk(KERN_NOTICE "exofs: %s %s osd_name-%s pid-0x%llx\n", - msg, dev_path ?: "", odi->osdname, _LLU(pid)); -} - -static void exofs_free_sbi(struct exofs_sb_info *sbi) -{ - unsigned numdevs = sbi->oc.numdevs; - - while (numdevs) { - unsigned i = --numdevs; - struct osd_dev *od = ore_comp_dev(&sbi->oc, i); - - if (od) { - ore_comp_set_dev(&sbi->oc, i, NULL); - osduld_put_device(od); - } - } - kfree(sbi->oc.ods); - kfree(sbi); -} - -/* - * This function is called when the vfs is freeing the superblock. We just - * need to free our own part. - */ -static void exofs_put_super(struct super_block *sb) -{ - int num_pend; - struct exofs_sb_info *sbi = sb->s_fs_info; - - /* make sure there are no pending commands */ - for (num_pend = atomic_read(&sbi->s_curr_pending); num_pend > 0; - num_pend = atomic_read(&sbi->s_curr_pending)) { - wait_queue_head_t wq; - - printk(KERN_NOTICE "%s: !!Pending operations in flight. " - "This is a BUG. please report to osd-dev@open-osd.org\n", - __func__); - init_waitqueue_head(&wq); - wait_event_timeout(wq, - (atomic_read(&sbi->s_curr_pending) == 0), - msecs_to_jiffies(100)); - } - - _exofs_print_device("Unmounting", NULL, ore_comp_dev(&sbi->oc, 0), - sbi->one_comp.obj.partition); - - exofs_sysfs_sb_del(sbi); - exofs_free_sbi(sbi); - sb->s_fs_info = NULL; -} - -static int _read_and_match_data_map(struct exofs_sb_info *sbi, unsigned numdevs, - struct exofs_device_table *dt) -{ - int ret; - - sbi->layout.stripe_unit = - le64_to_cpu(dt->dt_data_map.cb_stripe_unit); - sbi->layout.group_width = - le32_to_cpu(dt->dt_data_map.cb_group_width); - sbi->layout.group_depth = - le32_to_cpu(dt->dt_data_map.cb_group_depth); - sbi->layout.mirrors_p1 = - le32_to_cpu(dt->dt_data_map.cb_mirror_cnt) + 1; - sbi->layout.raid_algorithm = - le32_to_cpu(dt->dt_data_map.cb_raid_algorithm); - - ret = ore_verify_layout(numdevs, &sbi->layout); - - EXOFS_DBGMSG("exofs: layout: " - "num_comps=%u stripe_unit=0x%x group_width=%u " - "group_depth=0x%llx mirrors_p1=%u raid_algorithm=%u\n", - numdevs, - sbi->layout.stripe_unit, - sbi->layout.group_width, - _LLU(sbi->layout.group_depth), - sbi->layout.mirrors_p1, - sbi->layout.raid_algorithm); - return ret; -} - -static unsigned __ra_pages(struct ore_layout *layout) -{ - const unsigned _MIN_RA = 32; /* min 128K read-ahead */ - unsigned ra_pages = layout->group_width * layout->stripe_unit / - PAGE_SIZE; - unsigned max_io_pages = exofs_max_io_pages(layout, ~0); - - ra_pages *= 2; /* two stripes */ - if (ra_pages < _MIN_RA) - ra_pages = roundup(_MIN_RA, ra_pages / 2); - - if (ra_pages > max_io_pages) - ra_pages = max_io_pages; - - return ra_pages; -} - -/* @odi is valid only as long as @fscb_dev is valid */ -static int exofs_devs_2_odi(struct exofs_dt_device_info *dt_dev, - struct osd_dev_info *odi) -{ - odi->systemid_len = le32_to_cpu(dt_dev->systemid_len); - if (likely(odi->systemid_len)) - memcpy(odi->systemid, dt_dev->systemid, OSD_SYSTEMID_LEN); - - odi->osdname_len = le32_to_cpu(dt_dev->osdname_len); - odi->osdname = dt_dev->osdname; - - /* FIXME support long names. Will need a _put function */ - if (dt_dev->long_name_offset) - return -EINVAL; - - /* Make sure osdname is printable! - * mkexofs should give us space for a null-terminator else the - * device-table is invalid. - */ - if (unlikely(odi->osdname_len >= sizeof(dt_dev->osdname))) - odi->osdname_len = sizeof(dt_dev->osdname) - 1; - dt_dev->osdname[odi->osdname_len] = 0; - - /* If it's all zeros something is bad we read past end-of-obj */ - return !(odi->systemid_len || odi->osdname_len); -} - -static int __alloc_dev_table(struct exofs_sb_info *sbi, unsigned numdevs, - struct exofs_dev **peds) -{ - /* Twice bigger table: See exofs_init_comps() and comment at - * exofs_read_lookup_dev_table() - */ - const size_t numores = numdevs * 2 - 1; - struct exofs_dev *eds; - unsigned i; - - sbi->oc.ods = kzalloc(numores * sizeof(struct ore_dev *) + - numdevs * sizeof(struct exofs_dev), GFP_KERNEL); - if (unlikely(!sbi->oc.ods)) { - EXOFS_ERR("ERROR: failed allocating Device array[%d]\n", - numdevs); - return -ENOMEM; - } - - /* Start of allocated struct exofs_dev entries */ - *peds = eds = (void *)sbi->oc.ods[numores]; - /* Initialize pointers into struct exofs_dev */ - for (i = 0; i < numdevs; ++i) - sbi->oc.ods[i] = &eds[i].ored; - return 0; -} - -static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi, - struct osd_dev *fscb_od, - unsigned table_count) -{ - struct ore_comp comp; - struct exofs_device_table *dt; - struct exofs_dev *eds; - unsigned table_bytes = table_count * sizeof(dt->dt_dev_table[0]) + - sizeof(*dt); - unsigned numdevs, i; - int ret; - - dt = kmalloc(table_bytes, GFP_KERNEL); - if (unlikely(!dt)) { - EXOFS_ERR("ERROR: allocating %x bytes for device table\n", - table_bytes); - return -ENOMEM; - } - - sbi->oc.numdevs = 0; - - comp.obj.partition = sbi->one_comp.obj.partition; - comp.obj.id = EXOFS_DEVTABLE_ID; - exofs_make_credential(comp.cred, &comp.obj); - - ret = exofs_read_kern(fscb_od, comp.cred, &comp.obj, 0, dt, - table_bytes); - if (unlikely(ret)) { - EXOFS_ERR("ERROR: reading device table\n"); - goto out; - } - - numdevs = le64_to_cpu(dt->dt_num_devices); - if (unlikely(!numdevs)) { - ret = -EINVAL; - goto out; - } - WARN_ON(table_count != numdevs); - - ret = _read_and_match_data_map(sbi, numdevs, dt); - if (unlikely(ret)) - goto out; - - ret = __alloc_dev_table(sbi, numdevs, &eds); - if (unlikely(ret)) - goto out; - /* exofs round-robins the device table view according to inode - * number. We hold a: twice bigger table hence inodes can point - * to any device and have a sequential view of the table - * starting at this device. See exofs_init_comps() - */ - memcpy(&sbi->oc.ods[numdevs], &sbi->oc.ods[0], - (numdevs - 1) * sizeof(sbi->oc.ods[0])); - - /* create sysfs subdir under which we put the device table - * And cluster layout. A Superblock is identified by the string: - * "dev[0].osdname"_"pid" - */ - exofs_sysfs_sb_add(sbi, &dt->dt_dev_table[0]); - - for (i = 0; i < numdevs; i++) { - struct exofs_fscb fscb; - struct osd_dev_info odi; - struct osd_dev *od; - - if (exofs_devs_2_odi(&dt->dt_dev_table[i], &odi)) { - EXOFS_ERR("ERROR: Read all-zeros device entry\n"); - ret = -EINVAL; - goto out; - } - - printk(KERN_NOTICE "Add device[%d]: osd_name-%s\n", - i, odi.osdname); - - /* the exofs id is currently the table index */ - eds[i].did = i; - - /* On all devices the device table is identical. The user can - * specify any one of the participating devices on the command - * line. We always keep them in device-table order. - */ - if (fscb_od && osduld_device_same(fscb_od, &odi)) { - eds[i].ored.od = fscb_od; - ++sbi->oc.numdevs; - fscb_od = NULL; - exofs_sysfs_odev_add(&eds[i], sbi); - continue; - } - - od = osduld_info_lookup(&odi); - if (IS_ERR(od)) { - ret = PTR_ERR(od); - EXOFS_ERR("ERROR: device requested is not found " - "osd_name-%s =>%d\n", odi.osdname, ret); - goto out; - } - - eds[i].ored.od = od; - ++sbi->oc.numdevs; - - /* Read the fscb of the other devices to make sure the FS - * partition is there. - */ - ret = exofs_read_kern(od, comp.cred, &comp.obj, 0, &fscb, - sizeof(fscb)); - if (unlikely(ret)) { - EXOFS_ERR("ERROR: Malformed participating device " - "error reading fscb osd_name-%s\n", - odi.osdname); - goto out; - } - exofs_sysfs_odev_add(&eds[i], sbi); - - /* TODO: verify other information is correct and FS-uuid - * matches. Benny what did you say about device table - * generation and old devices? - */ - } - -out: - kfree(dt); - if (unlikely(fscb_od && !ret)) { - EXOFS_ERR("ERROR: Bad device-table container device not present\n"); - osduld_put_device(fscb_od); - return -EINVAL; - } - return ret; -} - -/* - * Read the superblock from the OSD and fill in the fields - */ -static int exofs_fill_super(struct super_block *sb, - struct exofs_mountopt *opts, - struct exofs_sb_info *sbi, - int silent) -{ - struct inode *root; - struct osd_dev *od; /* Master device */ - struct exofs_fscb fscb; /*on-disk superblock info */ - struct ore_comp comp; - unsigned table_count; - int ret; - - /* use mount options to fill superblock */ - if (opts->is_osdname) { - struct osd_dev_info odi = {.systemid_len = 0}; - - odi.osdname_len = strlen(opts->dev_name); - odi.osdname = (u8 *)opts->dev_name; - od = osduld_info_lookup(&odi); - kfree(opts->dev_name); - opts->dev_name = NULL; - } else { - od = osduld_path_lookup(opts->dev_name); - } - if (IS_ERR(od)) { - ret = -EINVAL; - goto free_sbi; - } - - /* Default layout in case we do not have a device-table */ - sbi->layout.stripe_unit = PAGE_SIZE; - sbi->layout.mirrors_p1 = 1; - sbi->layout.group_width = 1; - sbi->layout.group_depth = -1; - sbi->layout.group_count = 1; - sbi->s_timeout = opts->timeout; - - sbi->one_comp.obj.partition = opts->pid; - sbi->one_comp.obj.id = 0; - exofs_make_credential(sbi->one_comp.cred, &sbi->one_comp.obj); - sbi->oc.single_comp = EC_SINGLE_COMP; - sbi->oc.comps = &sbi->one_comp; - - /* fill in some other data by hand */ - memset(sb->s_id, 0, sizeof(sb->s_id)); - strcpy(sb->s_id, "exofs"); - sb->s_blocksize = EXOFS_BLKSIZE; - sb->s_blocksize_bits = EXOFS_BLKSHIFT; - sb->s_maxbytes = MAX_LFS_FILESIZE; - sb->s_max_links = EXOFS_LINK_MAX; - atomic_set(&sbi->s_curr_pending, 0); - sb->s_bdev = NULL; - sb->s_dev = 0; - - comp.obj.partition = sbi->one_comp.obj.partition; - comp.obj.id = EXOFS_SUPER_ID; - exofs_make_credential(comp.cred, &comp.obj); - - ret = exofs_read_kern(od, comp.cred, &comp.obj, 0, &fscb, sizeof(fscb)); - if (unlikely(ret)) - goto free_sbi; - - sb->s_magic = le16_to_cpu(fscb.s_magic); - /* NOTE: we read below to be backward compatible with old versions */ - sbi->s_nextid = le64_to_cpu(fscb.s_nextid); - sbi->s_numfiles = le32_to_cpu(fscb.s_numfiles); - - /* make sure what we read from the object store is correct */ - if (sb->s_magic != EXOFS_SUPER_MAGIC) { - if (!silent) - EXOFS_ERR("ERROR: Bad magic value\n"); - ret = -EINVAL; - goto free_sbi; - } - if (le32_to_cpu(fscb.s_version) > EXOFS_FSCB_VER) { - EXOFS_ERR("ERROR: Bad FSCB version expected-%d got-%d\n", - EXOFS_FSCB_VER, le32_to_cpu(fscb.s_version)); - ret = -EINVAL; - goto free_sbi; - } - - /* start generation numbers from a random point */ - get_random_bytes(&sbi->s_next_generation, sizeof(u32)); - spin_lock_init(&sbi->s_next_gen_lock); - - table_count = le64_to_cpu(fscb.s_dev_table_count); - if (table_count) { - ret = exofs_read_lookup_dev_table(sbi, od, table_count); - if (unlikely(ret)) - goto free_sbi; - } else { - struct exofs_dev *eds; - - ret = __alloc_dev_table(sbi, 1, &eds); - if (unlikely(ret)) - goto free_sbi; - - ore_comp_set_dev(&sbi->oc, 0, od); - sbi->oc.numdevs = 1; - } - - __sbi_read_stats(sbi); - - /* set up operation vectors */ - ret = super_setup_bdi(sb); - if (ret) { - EXOFS_DBGMSG("Failed to super_setup_bdi\n"); - goto free_sbi; - } - sb->s_bdi->ra_pages = __ra_pages(&sbi->layout); - sb->s_fs_info = sbi; - sb->s_op = &exofs_sops; - sb->s_export_op = &exofs_export_ops; - root = exofs_iget(sb, EXOFS_ROOT_ID - EXOFS_OBJ_OFF); - if (IS_ERR(root)) { - EXOFS_ERR("ERROR: exofs_iget failed\n"); - ret = PTR_ERR(root); - goto free_sbi; - } - sb->s_root = d_make_root(root); - if (!sb->s_root) { - EXOFS_ERR("ERROR: get root inode failed\n"); - ret = -ENOMEM; - goto free_sbi; - } - - if (!S_ISDIR(root->i_mode)) { - dput(sb->s_root); - sb->s_root = NULL; - EXOFS_ERR("ERROR: corrupt root inode (mode = %hd)\n", - root->i_mode); - ret = -EINVAL; - goto free_sbi; - } - - exofs_sysfs_dbg_print(); - _exofs_print_device("Mounting", opts->dev_name, - ore_comp_dev(&sbi->oc, 0), - sbi->one_comp.obj.partition); - return 0; - -free_sbi: - EXOFS_ERR("Unable to mount exofs on %s pid=0x%llx err=%d\n", - opts->dev_name, sbi->one_comp.obj.partition, ret); - exofs_free_sbi(sbi); - return ret; -} - -/* - * Set up the superblock (calls exofs_fill_super eventually) - */ -static struct dentry *exofs_mount(struct file_system_type *type, - int flags, const char *dev_name, - void *data) -{ - struct super_block *s; - struct exofs_mountopt opts; - struct exofs_sb_info *sbi; - int ret; - - ret = parse_options(data, &opts); - if (ret) { - kfree(opts.dev_name); - return ERR_PTR(ret); - } - - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) { - kfree(opts.dev_name); - return ERR_PTR(-ENOMEM); - } - - s = sget(type, NULL, set_anon_super, flags, NULL); - - if (IS_ERR(s)) { - kfree(opts.dev_name); - kfree(sbi); - return ERR_CAST(s); - } - - if (!opts.dev_name) - opts.dev_name = dev_name; - - - ret = exofs_fill_super(s, &opts, sbi, flags & SB_SILENT ? 1 : 0); - if (ret) { - deactivate_locked_super(s); - return ERR_PTR(ret); - } - s->s_flags |= SB_ACTIVE; - return dget(s->s_root); -} - -/* - * Return information about the file system state in the buffer. This is used - * by the 'df' command, for example. - */ -static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct exofs_sb_info *sbi = sb->s_fs_info; - struct ore_io_state *ios; - struct osd_attr attrs[] = { - ATTR_DEF(OSD_APAGE_PARTITION_QUOTAS, - OSD_ATTR_PQ_CAPACITY_QUOTA, sizeof(__be64)), - ATTR_DEF(OSD_APAGE_PARTITION_INFORMATION, - OSD_ATTR_PI_USED_CAPACITY, sizeof(__be64)), - }; - uint64_t capacity = ULLONG_MAX; - uint64_t used = ULLONG_MAX; - int ret; - - ret = ore_get_io_state(&sbi->layout, &sbi->oc, &ios); - if (ret) { - EXOFS_DBGMSG("ore_get_io_state failed.\n"); - return ret; - } - - ios->in_attr = attrs; - ios->in_attr_len = ARRAY_SIZE(attrs); - - ret = ore_read(ios); - if (unlikely(ret)) - goto out; - - ret = extract_attr_from_ios(ios, &attrs[0]); - if (likely(!ret)) { - capacity = get_unaligned_be64(attrs[0].val_ptr); - if (unlikely(!capacity)) - capacity = ULLONG_MAX; - } else - EXOFS_DBGMSG("exofs_statfs: get capacity failed.\n"); - - ret = extract_attr_from_ios(ios, &attrs[1]); - if (likely(!ret)) - used = get_unaligned_be64(attrs[1].val_ptr); - else - EXOFS_DBGMSG("exofs_statfs: get used-space failed.\n"); - - /* fill in the stats buffer */ - buf->f_type = EXOFS_SUPER_MAGIC; - buf->f_bsize = EXOFS_BLKSIZE; - buf->f_blocks = capacity >> 9; - buf->f_bfree = (capacity - used) >> 9; - buf->f_bavail = buf->f_bfree; - buf->f_files = sbi->s_numfiles; - buf->f_ffree = EXOFS_MAX_ID - sbi->s_numfiles; - buf->f_namelen = EXOFS_NAME_LEN; - -out: - ore_put_io_state(ios); - return ret; -} - -static const struct super_operations exofs_sops = { - .alloc_inode = exofs_alloc_inode, - .destroy_inode = exofs_destroy_inode, - .write_inode = exofs_write_inode, - .evict_inode = exofs_evict_inode, - .put_super = exofs_put_super, - .sync_fs = exofs_sync_fs, - .statfs = exofs_statfs, -}; - -/****************************************************************************** - * EXPORT OPERATIONS - *****************************************************************************/ - -static struct dentry *exofs_get_parent(struct dentry *child) -{ - unsigned long ino = exofs_parent_ino(child); - - if (!ino) - return ERR_PTR(-ESTALE); - - return d_obtain_alias(exofs_iget(child->d_sb, ino)); -} - -static struct inode *exofs_nfs_get_inode(struct super_block *sb, - u64 ino, u32 generation) -{ - struct inode *inode; - - inode = exofs_iget(sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - if (generation && inode->i_generation != generation) { - /* we didn't find the right inode.. */ - iput(inode); - return ERR_PTR(-ESTALE); - } - return inode; -} - -static struct dentry *exofs_fh_to_dentry(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) -{ - return generic_fh_to_dentry(sb, fid, fh_len, fh_type, - exofs_nfs_get_inode); -} - -static struct dentry *exofs_fh_to_parent(struct super_block *sb, - struct fid *fid, int fh_len, int fh_type) -{ - return generic_fh_to_parent(sb, fid, fh_len, fh_type, - exofs_nfs_get_inode); -} - -static const struct export_operations exofs_export_ops = { - .fh_to_dentry = exofs_fh_to_dentry, - .fh_to_parent = exofs_fh_to_parent, - .get_parent = exofs_get_parent, -}; - -/****************************************************************************** - * INSMOD/RMMOD - *****************************************************************************/ - -/* - * struct that describes this file system - */ -static struct file_system_type exofs_type = { - .owner = THIS_MODULE, - .name = "exofs", - .mount = exofs_mount, - .kill_sb = generic_shutdown_super, -}; -MODULE_ALIAS_FS("exofs"); - -static int __init init_exofs(void) -{ - int err; - - err = init_inodecache(); - if (err) - goto out; - - err = register_filesystem(&exofs_type); - if (err) - goto out_d; - - /* We don't fail if sysfs creation failed */ - exofs_sysfs_init(); - - return 0; -out_d: - destroy_inodecache(); -out: - return err; -} - -static void __exit exit_exofs(void) -{ - exofs_sysfs_uninit(); - unregister_filesystem(&exofs_type); - destroy_inodecache(); -} - -MODULE_AUTHOR("Avishay Traeger "); -MODULE_DESCRIPTION("exofs"); -MODULE_LICENSE("GPL"); - -module_init(init_exofs) -module_exit(exit_exofs) diff --git a/fs/exofs/sys.c b/fs/exofs/sys.c deleted file mode 100644 index 1f7d5e46cdda..000000000000 --- a/fs/exofs/sys.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2012 - * Sachin Bhamare - * Boaz Harrosh - * - * This file is part of exofs. - * - * exofs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License 2 as published by - * the Free Software Foundation. - * - * exofs 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 exofs; if not, write to the: - * Free Software Foundation - */ - -#include -#include - -#include "exofs.h" - -struct odev_attr { - struct attribute attr; - ssize_t (*show)(struct exofs_dev *, char *); - ssize_t (*store)(struct exofs_dev *, const char *, size_t); -}; - -static ssize_t odev_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct exofs_dev *edp = container_of(kobj, struct exofs_dev, ed_kobj); - struct odev_attr *a = container_of(attr, struct odev_attr, attr); - - return a->show ? a->show(edp, buf) : 0; -} - -static ssize_t odev_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t len) -{ - struct exofs_dev *edp = container_of(kobj, struct exofs_dev, ed_kobj); - struct odev_attr *a = container_of(attr, struct odev_attr, attr); - - return a->store ? a->store(edp, buf, len) : len; -} - -static const struct sysfs_ops odev_attr_ops = { - .show = odev_attr_show, - .store = odev_attr_store, -}; - - -static struct kset *exofs_kset; - -static ssize_t osdname_show(struct exofs_dev *edp, char *buf) -{ - struct osd_dev *odev = edp->ored.od; - const struct osd_dev_info *odi = osduld_device_info(odev); - - return snprintf(buf, odi->osdname_len + 1, "%s", odi->osdname); -} - -static ssize_t systemid_show(struct exofs_dev *edp, char *buf) -{ - struct osd_dev *odev = edp->ored.od; - const struct osd_dev_info *odi = osduld_device_info(odev); - - memcpy(buf, odi->systemid, odi->systemid_len); - return odi->systemid_len; -} - -static ssize_t uri_show(struct exofs_dev *edp, char *buf) -{ - return snprintf(buf, edp->urilen, "%s", edp->uri); -} - -static ssize_t uri_store(struct exofs_dev *edp, const char *buf, size_t len) -{ - uint8_t *new_uri; - - edp->urilen = strlen(buf) + 1; - new_uri = krealloc(edp->uri, edp->urilen, GFP_KERNEL); - if (new_uri == NULL) - return -ENOMEM; - edp->uri = new_uri; - strncpy(edp->uri, buf, edp->urilen); - return edp->urilen; -} - -#define OSD_ATTR(name, mode, show, store) \ - static struct odev_attr odev_attr_##name = \ - __ATTR(name, mode, show, store) - -OSD_ATTR(osdname, S_IRUGO, osdname_show, NULL); -OSD_ATTR(systemid, S_IRUGO, systemid_show, NULL); -OSD_ATTR(uri, S_IRWXU, uri_show, uri_store); - -static struct attribute *odev_attrs[] = { - &odev_attr_osdname.attr, - &odev_attr_systemid.attr, - &odev_attr_uri.attr, - NULL, -}; - -static struct kobj_type odev_ktype = { - .default_attrs = odev_attrs, - .sysfs_ops = &odev_attr_ops, -}; - -static struct kobj_type uuid_ktype = { -}; - -void exofs_sysfs_dbg_print(void) -{ -#ifdef CONFIG_EXOFS_DEBUG - struct kobject *k_name, *k_tmp; - - list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) { - printk(KERN_INFO "%s: name %s ref %d\n", - __func__, kobject_name(k_name), - (int)kref_read(&k_name->kref)); - } -#endif -} -/* - * This function removes all kobjects under exofs_kset - * At the end of it, exofs_kset kobject will have a refcount - * of 1 which gets decremented only on exofs module unload - */ -void exofs_sysfs_sb_del(struct exofs_sb_info *sbi) -{ - struct kobject *k_name, *k_tmp; - struct kobject *s_kobj = &sbi->s_kobj; - - list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) { - /* Remove all that are children of this SBI */ - if (k_name->parent == s_kobj) - kobject_put(k_name); - } - kobject_put(s_kobj); -} - -/* - * This function creates sysfs entries to hold the current exofs cluster - * instance (uniquely identified by osdname,pid tuple). - * This function gets called once per exofs mount instance. - */ -int exofs_sysfs_sb_add(struct exofs_sb_info *sbi, - struct exofs_dt_device_info *dt_dev) -{ - struct kobject *s_kobj; - int retval = 0; - uint64_t pid = sbi->one_comp.obj.partition; - - /* allocate new uuid dirent */ - s_kobj = &sbi->s_kobj; - s_kobj->kset = exofs_kset; - retval = kobject_init_and_add(s_kobj, &uuid_ktype, - &exofs_kset->kobj, "%s_%llx", dt_dev->osdname, pid); - if (retval) { - EXOFS_ERR("ERROR: Failed to create sysfs entry for " - "uuid-%s_%llx => %d\n", dt_dev->osdname, pid, retval); - return -ENOMEM; - } - return 0; -} - -int exofs_sysfs_odev_add(struct exofs_dev *edev, struct exofs_sb_info *sbi) -{ - struct kobject *d_kobj; - int retval = 0; - - /* create osd device group which contains following attributes - * osdname, systemid & uri - */ - d_kobj = &edev->ed_kobj; - d_kobj->kset = exofs_kset; - retval = kobject_init_and_add(d_kobj, &odev_ktype, - &sbi->s_kobj, "dev%u", edev->did); - if (retval) { - EXOFS_ERR("ERROR: Failed to create sysfs entry for " - "device dev%u\n", edev->did); - return retval; - } - return 0; -} - -int exofs_sysfs_init(void) -{ - exofs_kset = kset_create_and_add("exofs", NULL, fs_kobj); - if (!exofs_kset) { - EXOFS_ERR("ERROR: kset_create_and_add exofs failed\n"); - return -ENOMEM; - } - return 0; -} - -void exofs_sysfs_uninit(void) -{ - kset_unregister(exofs_kset); -} From 19fcae3d4f2dd513d472055fb694d802e9a21b91 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 8 Nov 2018 09:28:20 +0100 Subject: [PATCH 1184/4277] scsi: remove the SCSI OSD library Now that all the users are gone the SCSI OSD library can be removed as well. Signed-off-by: Christoph Hellwig Reviewed-by: Jens Axboe Signed-off-by: Martin K. Petersen --- Documentation/scsi/osd.txt | 192 --- MAINTAINERS | 6 - drivers/scsi/Kconfig | 2 - drivers/scsi/Makefile | 1 - drivers/scsi/osd/Kbuild | 20 - drivers/scsi/osd/Kconfig | 49 - drivers/scsi/osd/osd_debug.h | 30 - drivers/scsi/osd/osd_initiator.c | 2076 ------------------------------ drivers/scsi/osd/osd_uld.c | 571 -------- include/scsi/osd_initiator.h | 511 -------- include/scsi/osd_ore.h | 201 --- 11 files changed, 3659 deletions(-) delete mode 100644 Documentation/scsi/osd.txt delete mode 100644 drivers/scsi/osd/Kbuild delete mode 100644 drivers/scsi/osd/Kconfig delete mode 100644 drivers/scsi/osd/osd_debug.h delete mode 100644 drivers/scsi/osd/osd_initiator.c delete mode 100644 drivers/scsi/osd/osd_uld.c delete mode 100644 include/scsi/osd_initiator.h delete mode 100644 include/scsi/osd_ore.h diff --git a/Documentation/scsi/osd.txt b/Documentation/scsi/osd.txt deleted file mode 100644 index 2bc2ab06b0c0..000000000000 --- a/Documentation/scsi/osd.txt +++ /dev/null @@ -1,192 +0,0 @@ -The OSD Standard -================ -OSD (Object-Based Storage Device) is a T10 SCSI command set that is designed -to provide efficient operation of input/output logical units that manage the -allocation, placement, and accessing of variable-size data-storage containers, -called objects. Objects are intended to contain operating system and application -constructs. Each object has associated attributes attached to it, which are -integral part of the object and provide metadata about the object. The standard -defines some common obligatory attributes, but user attributes can be added as -needed. - -See: http://www.t10.org/ftp/t10/drafts/osd2/ for the latest draft for OSD 2 -or search the web for "OSD SCSI" - -OSD in the Linux Kernel -======================= -osd-initiator: - The main component of OSD in Kernel is the osd-initiator library. Its main -user is intended to be the pNFS-over-objects layout driver, which uses objects -as its back-end data storage. Other clients are the other osd parts listed below. - -osd-uld: - This is a SCSI ULD that registers for OSD type devices and provides a testing -platform, both for the in-kernel initiator as well as connected targets. It -currently has no useful user-mode API, though it could have if need be. - -osd target: - There are no current plans for an OSD target implementation in kernel. For all -needs, a user-mode target that is based on the scsi tgt target framework is -available from Ohio Supercomputer Center (OSC) at: -http://www.open-osd.org/bin/view/Main/OscOsdProject -There are several other target implementations. See http://open-osd.org for more -links. - -Files and Folders -================= -This is the complete list of files included in this work: -include/scsi/ - osd_initiator.h Main API for the initiator library - osd_types.h Common OSD types - osd_sec.h Security Manager API - osd_protocol.h Wire definitions of the OSD standard protocol - osd_attributes.h Wire definitions of OSD attributes - -drivers/scsi/osd/ - osd_initiator.c OSD-Initiator library implementation - osd_uld.c The OSD scsi ULD - osd_ktest.{h,c} In-kernel test suite (called by osd_uld) - osd_debug.h Some printk macros - Makefile For both in-tree and out-of-tree compilation - Kconfig Enables inclusion of the different pieces - osd_test.c User-mode application to call the kernel tests - -The OSD-Initiator Library -========================= -osd_initiator is a low level implementation of an osd initiator encoder. -But even though, it should be intuitive and easy to use. Perhaps over time an -higher lever will form that automates some of the more common recipes. - -init/fini: -- osd_dev_init() associates a scsi_device with an osd_dev structure - and initializes some global pools. This should be done once per scsi_device - (OSD LUN). The osd_dev structure is needed for calling osd_start_request(). - -- osd_dev_fini() cleans up before a osd_dev/scsi_device destruction. - -OSD commands encoding, execution, and decoding of results: - -struct osd_request's is used to iteratively encode an OSD command and carry -its state throughout execution. Each request goes through these stages: - -a. osd_start_request() allocates the request. - -b. Any of the osd_req_* methods is used to encode a request of the specified - type. - -c. osd_req_add_{get,set}_attr_* may be called to add get/set attributes to the - CDB. "List" or "Page" mode can be used exclusively. The attribute-list API - can be called multiple times on the same request. However, only one - attribute-page can be read, as mandated by the OSD standard. - -d. osd_finalize_request() computes offsets into the data-in and data-out buffers - and signs the request using the provided capability key and integrity- - check parameters. - -e. osd_execute_request() may be called to execute the request via the block - layer and wait for its completion. The request can be executed - asynchronously by calling the block layer API directly. - -f. After execution, osd_req_decode_sense() can be called to decode the request's - sense information. - -g. osd_req_decode_get_attr() may be called to retrieve osd_add_get_attr_list() - values. - -h. osd_end_request() must be called to deallocate the request and any resource - associated with it. Note that osd_end_request cleans up the request at any - stage and it must always be called after a successful osd_start_request(). - -osd_request's structure: - -The OSD standard defines a complex structure of IO segments pointed to by -members in the CDB. Up to 3 segments can be deployed in the IN-Buffer and up to -4 in the OUT-Buffer. The ASCII illustration below depicts a secure-read with -associated get+set of attributes-lists. Other combinations very on the same -basic theme. From no-segments-used up to all-segments-used. - -|________OSD-CDB__________| -| | -|read_len (offset=0) -|---------\ -| | | -|get_attrs_list_length | | -|get_attrs_list_offset -|----\ | -| | | | -|retrieved_attrs_alloc_len| | | -|retrieved_attrs_offset -|----|----|-\ -| | | | | -|set_attrs_list_length | | | | -|set_attrs_list_offset -|-\ | | | -| | | | | | -|in_data_integ_offset -|-|--|----|-|-\ -|out_data_integ_offset -|-|--|--\ | | | -\_________________________/ | | | | | | - | | | | | | -|_______OUT-BUFFER________| | | | | | | -| Set attr list | - -More up-to-date information can be found on: -http://open-osd.org - -Boaz Harrosh - -References -========== -Weber, R., "SCSI Object-Based Storage Device Commands", -T10/1355-D ANSI/INCITS 400-2004, -http://www.t10.org/ftp/t10/drafts/osd/osd-r10.pdf - -Weber, R., "SCSI Object-Based Storage Device Commands -2 (OSD-2)" -T10/1729-D, Working Draft, rev. 3 -http://www.t10.org/ftp/t10/drafts/osd2/osd2r03.pdf diff --git a/MAINTAINERS b/MAINTAINERS index 108b340ab625..0cd037950bc3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11385,12 +11385,6 @@ W: http://www.nongnu.org/orinoco/ S: Orphan F: drivers/net/wireless/intersil/orinoco/ -OSD LIBRARY and FILESYSTEM -M: Boaz Harrosh -S: Maintained -F: drivers/scsi/osd/ -F: include/scsi/osd_* - OV2659 OMNIVISION SENSOR DRIVER M: "Lad, Prabhakar" L: linux-media@vger.kernel.org diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 85c5c89f2fa5..237c6294738d 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1515,6 +1515,4 @@ source "drivers/scsi/pcmcia/Kconfig" source "drivers/scsi/device_handler/Kconfig" -source "drivers/scsi/osd/Kconfig" - endmenu diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index fcb41ae329c4..8826111fdf4a 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -150,7 +150,6 @@ obj-$(CONFIG_CHR_DEV_SG) += sg.o obj-$(CONFIG_CHR_DEV_SCH) += ch.o obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o -obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/ obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/ # This goes last, so that "real" scsi devices probe earlier diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild deleted file mode 100644 index 58cecd45b0f5..000000000000 --- a/drivers/scsi/osd/Kbuild +++ /dev/null @@ -1,20 +0,0 @@ -# -# Kbuild for the OSD modules -# -# Copyright (C) 2008 Panasas Inc. All rights reserved. -# -# Authors: -# Boaz Harrosh -# Benny Halevy -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 -# - -# libosd.ko - osd-initiator library -libosd-y := osd_initiator.o -obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o - -# osd.ko - SCSI ULD and char-device -osd-y := osd_uld.o -obj-$(CONFIG_SCSI_OSD_ULD) += osd.o diff --git a/drivers/scsi/osd/Kconfig b/drivers/scsi/osd/Kconfig deleted file mode 100644 index 347cc5e33749..000000000000 --- a/drivers/scsi/osd/Kconfig +++ /dev/null @@ -1,49 +0,0 @@ -# -# Kernel configuration file for the OSD scsi protocol -# -# Copyright (C) 2008 Panasas Inc. All rights reserved. -# -# Authors: -# Boaz Harrosh -# Benny Halevy -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public version 2 License as -# published by the Free Software Foundation -# -config SCSI_OSD_INITIATOR - tristate "OSD-Initiator library" - depends on SCSI - help - Enable the OSD-Initiator library (libosd.ko). - NOTE: You must also select CRYPTO_SHA1 + CRYPTO_HMAC and their - dependencies - -config SCSI_OSD_ULD - tristate "OSD Upper Level driver" - depends on SCSI_OSD_INITIATOR - help - Build a SCSI upper layer driver that exports /dev/osdX devices - to user-mode for testing and controlling OSD devices. It is also - needed by exofs, for mounting an OSD based file system. - -config SCSI_OSD_DPRINT_SENSE - int "(0-2) When sense is returned, DEBUG print all sense descriptors" - default 1 - depends on SCSI_OSD_INITIATOR - help - When a CHECK_CONDITION status is returned from a target, and a - sense-buffer is retrieved, turning this on will dump a full - sense-decoding message. Setting to 2 will also print recoverable - errors that might be regularly returned for some filesystem - operations. - -config SCSI_OSD_DEBUG - bool "Compile All OSD modules with lots of DEBUG prints" - default n - depends on SCSI_OSD_INITIATOR - help - OSD Code is populated with lots of OSD_DEBUG(..) printouts to - dmesg. Enable this if you found a bug and you want to help us - track the problem (see also MAINTAINERS). Setting this will also - force SCSI_OSD_DPRINT_SENSE=2. diff --git a/drivers/scsi/osd/osd_debug.h b/drivers/scsi/osd/osd_debug.h deleted file mode 100644 index 26341261bb5c..000000000000 --- a/drivers/scsi/osd/osd_debug.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * osd_debug.h - Some kprintf macros - * - * Copyright (C) 2008 Panasas Inc. All rights reserved. - * - * Authors: - * Boaz Harrosh - * Benny Halevy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * - */ -#ifndef __OSD_DEBUG_H__ -#define __OSD_DEBUG_H__ - -#define OSD_ERR(fmt, a...) printk(KERN_ERR "osd: " fmt, ##a) -#define OSD_INFO(fmt, a...) printk(KERN_NOTICE "osd: " fmt, ##a) - -#ifdef CONFIG_SCSI_OSD_DEBUG -#define OSD_DEBUG(fmt, a...) \ - printk(KERN_NOTICE "osd @%s:%d: " fmt, __func__, __LINE__, ##a) -#else -#define OSD_DEBUG(fmt, a...) do {} while (0) -#endif - -/* u64 has problems with printk this will cast it to unsigned long long */ -#define _LLU(x) (unsigned long long)(x) - -#endif /* ndef __OSD_DEBUG_H__ */ diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c deleted file mode 100644 index 60cf7c5eb880..000000000000 --- a/drivers/scsi/osd/osd_initiator.c +++ /dev/null @@ -1,2076 +0,0 @@ -/* - * osd_initiator - Main body of the osd initiator library. - * - * Note: The file does not contain the advanced security functionality which - * is only needed by the security_manager's initiators. - * - * Copyright (C) 2008 Panasas Inc. All rights reserved. - * - * Authors: - * Boaz Harrosh - * Benny Halevy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Panasas company nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "osd_debug.h" - -#ifndef __unused -# define __unused __attribute__((unused)) -#endif - -enum { OSD_REQ_RETRIES = 1 }; - -MODULE_AUTHOR("Boaz Harrosh "); -MODULE_DESCRIPTION("open-osd initiator library libosd.ko"); -MODULE_LICENSE("GPL"); - -static inline void build_test(void) -{ - /* structures were not packed */ - BUILD_BUG_ON(sizeof(struct osd_capability) != OSD_CAP_LEN); - BUILD_BUG_ON(sizeof(struct osdv2_cdb) != OSD_TOTAL_CDB_LEN); - BUILD_BUG_ON(sizeof(struct osdv1_cdb) != OSDv1_TOTAL_CDB_LEN); -} - -static const char *_osd_ver_desc(struct osd_request *or) -{ - return osd_req_is_ver1(or) ? "OSD1" : "OSD2"; -} - -#define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len) - -static int _osd_get_print_system_info(struct osd_dev *od, - void *caps, struct osd_dev_info *odi) -{ - struct osd_request *or; - struct osd_attr get_attrs[] = { - ATTR_DEF_RI(OSD_ATTR_RI_VENDOR_IDENTIFICATION, 8), - ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_IDENTIFICATION, 16), - ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_MODEL, 32), - ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_REVISION_LEVEL, 4), - ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER, 64 /*variable*/), - ATTR_DEF_RI(OSD_ATTR_RI_OSD_NAME, 64 /*variable*/), - ATTR_DEF_RI(OSD_ATTR_RI_TOTAL_CAPACITY, 8), - ATTR_DEF_RI(OSD_ATTR_RI_USED_CAPACITY, 8), - ATTR_DEF_RI(OSD_ATTR_RI_NUMBER_OF_PARTITIONS, 8), - ATTR_DEF_RI(OSD_ATTR_RI_CLOCK, 6), - /* IBM-OSD-SIM Has a bug with this one put it last */ - ATTR_DEF_RI(OSD_ATTR_RI_OSD_SYSTEM_ID, 20), - }; - void *iter = NULL, *pFirst; - int nelem = ARRAY_SIZE(get_attrs), a = 0; - int ret; - - or = osd_start_request(od); - if (!or) - return -ENOMEM; - - /* get attrs */ - osd_req_get_attributes(or, &osd_root_object); - osd_req_add_get_attr_list(or, get_attrs, ARRAY_SIZE(get_attrs)); - - ret = osd_finalize_request(or, 0, caps, NULL); - if (ret) - goto out; - - ret = osd_execute_request(or); - if (ret) { - OSD_ERR("Failed to detect %s => %d\n", _osd_ver_desc(or), ret); - goto out; - } - - osd_req_decode_get_attr_list(or, get_attrs, &nelem, &iter); - - OSD_INFO("Detected %s device\n", - _osd_ver_desc(or)); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("VENDOR_IDENTIFICATION [%s]\n", - (char *)pFirst); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("PRODUCT_IDENTIFICATION [%s]\n", - (char *)pFirst); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("PRODUCT_MODEL [%s]\n", - (char *)pFirst); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("PRODUCT_REVISION_LEVEL [%u]\n", - pFirst ? get_unaligned_be32(pFirst) : ~0U); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n", - (char *)pFirst); - - odi->osdname_len = get_attrs[a].len; - /* Avoid NULL for memcmp optimization 0-length is good enough */ - odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL); - if (!odi->osdname) { - ret = -ENOMEM; - goto out; - } - if (odi->osdname_len) - memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len); - OSD_INFO("OSD_NAME [%s]\n", odi->osdname); - a++; - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("TOTAL_CAPACITY [0x%llx]\n", - pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("USED_CAPACITY [0x%llx]\n", - pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL); - - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("NUMBER_OF_PARTITIONS [%llu]\n", - pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL); - - if (a >= nelem) - goto out; - - /* FIXME: Where are the time utilities */ - pFirst = get_attrs[a++].val_ptr; - OSD_INFO("CLOCK [0x%6phN]\n", pFirst); - - if (a < nelem) { /* IBM-OSD-SIM bug, Might not have it */ - unsigned len = get_attrs[a].len; - char sid_dump[32*4 + 2]; /* 2nibbles+space+ASCII */ - - hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1, - sid_dump, sizeof(sid_dump), true); - OSD_INFO("OSD_SYSTEM_ID(%d)\n" - " [%s]\n", len, sid_dump); - - if (unlikely(len > sizeof(odi->systemid))) { - OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). " - "device identification might not work\n", len); - len = sizeof(odi->systemid); - } - odi->systemid_len = len; - memcpy(odi->systemid, get_attrs[a].val_ptr, len); - a++; - } -out: - osd_end_request(or); - return ret; -} - -int osd_auto_detect_ver(struct osd_dev *od, - void *caps, struct osd_dev_info *odi) -{ - int ret; - - /* Auto-detect the osd version */ - ret = _osd_get_print_system_info(od, caps, odi); - if (ret) { - osd_dev_set_ver(od, OSD_VER1); - OSD_DEBUG("converting to OSD1\n"); - ret = _osd_get_print_system_info(od, caps, odi); - } - - return ret; -} -EXPORT_SYMBOL(osd_auto_detect_ver); - -static unsigned _osd_req_cdb_len(struct osd_request *or) -{ - return osd_req_is_ver1(or) ? OSDv1_TOTAL_CDB_LEN : OSD_TOTAL_CDB_LEN; -} - -static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len) -{ - return osd_req_is_ver1(or) ? - osdv1_attr_list_elem_size(len) : - osdv2_attr_list_elem_size(len); -} - -static void _osd_req_alist_elem_encode(struct osd_request *or, - void *attr_last, const struct osd_attr *oa) -{ - if (osd_req_is_ver1(or)) { - struct osdv1_attributes_list_element *attr = attr_last; - - attr->attr_page = cpu_to_be32(oa->attr_page); - attr->attr_id = cpu_to_be32(oa->attr_id); - attr->attr_bytes = cpu_to_be16(oa->len); - memcpy(attr->attr_val, oa->val_ptr, oa->len); - } else { - struct osdv2_attributes_list_element *attr = attr_last; - - attr->attr_page = cpu_to_be32(oa->attr_page); - attr->attr_id = cpu_to_be32(oa->attr_id); - attr->attr_bytes = cpu_to_be16(oa->len); - memcpy(attr->attr_val, oa->val_ptr, oa->len); - } -} - -static int _osd_req_alist_elem_decode(struct osd_request *or, - void *cur_p, struct osd_attr *oa, unsigned max_bytes) -{ - unsigned inc; - if (osd_req_is_ver1(or)) { - struct osdv1_attributes_list_element *attr = cur_p; - - if (max_bytes < sizeof(*attr)) - return -1; - - oa->len = be16_to_cpu(attr->attr_bytes); - inc = _osd_req_alist_elem_size(or, oa->len); - if (inc > max_bytes) - return -1; - - oa->attr_page = be32_to_cpu(attr->attr_page); - oa->attr_id = be32_to_cpu(attr->attr_id); - - /* OSD1: On empty attributes we return a pointer to 2 bytes - * of zeros. This keeps similar behaviour with OSD2. - * (See below) - */ - oa->val_ptr = likely(oa->len) ? attr->attr_val : - (u8 *)&attr->attr_bytes; - } else { - struct osdv2_attributes_list_element *attr = cur_p; - - if (max_bytes < sizeof(*attr)) - return -1; - - oa->len = be16_to_cpu(attr->attr_bytes); - inc = _osd_req_alist_elem_size(or, oa->len); - if (inc > max_bytes) - return -1; - - oa->attr_page = be32_to_cpu(attr->attr_page); - oa->attr_id = be32_to_cpu(attr->attr_id); - - /* OSD2: For convenience, on empty attributes, we return 8 bytes - * of zeros here. This keeps the same behaviour with OSD2r04, - * and is nice with null terminating ASCII fields. - * oa->val_ptr == NULL marks the end-of-list, or error. - */ - oa->val_ptr = likely(oa->len) ? attr->attr_val : attr->reserved; - } - return inc; -} - -static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head) -{ - return osd_req_is_ver1(or) ? - osdv1_list_size(list_head) : - osdv2_list_size(list_head); -} - -static unsigned _osd_req_sizeof_alist_header(struct osd_request *or) -{ - return osd_req_is_ver1(or) ? - sizeof(struct osdv1_attributes_list_header) : - sizeof(struct osdv2_attributes_list_header); -} - -static void _osd_req_set_alist_type(struct osd_request *or, - void *list, int list_type) -{ - if (osd_req_is_ver1(or)) { - struct osdv1_attributes_list_header *attr_list = list; - - memset(attr_list, 0, sizeof(*attr_list)); - attr_list->type = list_type; - } else { - struct osdv2_attributes_list_header *attr_list = list; - - memset(attr_list, 0, sizeof(*attr_list)); - attr_list->type = list_type; - } -} - -static bool _osd_req_is_alist_type(struct osd_request *or, - void *list, int list_type) -{ - if (!list) - return false; - - if (osd_req_is_ver1(or)) { - struct osdv1_attributes_list_header *attr_list = list; - - return attr_list->type == list_type; - } else { - struct osdv2_attributes_list_header *attr_list = list; - - return attr_list->type == list_type; - } -} - -/* This is for List-objects not Attributes-Lists */ -static void _osd_req_encode_olist(struct osd_request *or, - struct osd_obj_id_list *list) -{ - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - - if (osd_req_is_ver1(or)) { - cdbh->v1.list_identifier = list->list_identifier; - cdbh->v1.start_address = list->continuation_id; - } else { - cdbh->v2.list_identifier = list->list_identifier; - cdbh->v2.start_address = list->continuation_id; - } -} - -static osd_cdb_offset osd_req_encode_offset(struct osd_request *or, - u64 offset, unsigned *padding) -{ - return __osd_encode_offset(offset, padding, - osd_req_is_ver1(or) ? - OSDv1_OFFSET_MIN_SHIFT : OSD_OFFSET_MIN_SHIFT, - OSD_OFFSET_MAX_SHIFT); -} - -static struct osd_security_parameters * -_osd_req_sec_params(struct osd_request *or) -{ - struct osd_cdb *ocdb = &or->cdb; - - if (osd_req_is_ver1(or)) - return (struct osd_security_parameters *)&ocdb->v1.sec_params; - else - return (struct osd_security_parameters *)&ocdb->v2.sec_params; -} - -void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device) -{ - memset(osdd, 0, sizeof(*osdd)); - osdd->scsi_device = scsi_device; - osdd->def_timeout = BLK_DEFAULT_SG_TIMEOUT; -#ifdef OSD_VER1_SUPPORT - osdd->version = OSD_VER2; -#endif - /* TODO: Allocate pools for osd_request attributes ... */ -} -EXPORT_SYMBOL(osd_dev_init); - -void osd_dev_fini(struct osd_dev *osdd) -{ - /* TODO: De-allocate pools */ - - osdd->scsi_device = NULL; -} -EXPORT_SYMBOL(osd_dev_fini); - -static struct osd_request *_osd_request_alloc(gfp_t gfp) -{ - struct osd_request *or; - - /* TODO: Use mempool with one saved request */ - or = kzalloc(sizeof(*or), gfp); - return or; -} - -static void _osd_request_free(struct osd_request *or) -{ - kfree(or); -} - -struct osd_request *osd_start_request(struct osd_dev *dev) -{ - struct osd_request *or; - - or = _osd_request_alloc(GFP_KERNEL); - if (!or) - return NULL; - - or->osd_dev = dev; - or->timeout = dev->def_timeout; - or->retries = OSD_REQ_RETRIES; - - return or; -} -EXPORT_SYMBOL(osd_start_request); - -static void _osd_free_seg(struct osd_request *or __unused, - struct _osd_req_data_segment *seg) -{ - if (!seg->buff || !seg->alloc_size) - return; - - kfree(seg->buff); - seg->buff = NULL; - seg->alloc_size = 0; -} - -static void _put_request(struct request *rq) -{ - /* - * If osd_finalize_request() was called but the request was not - * executed through the block layer, then we must release BIOs. - * TODO: Keep error code in or->async_error. Need to audit all - * code paths. - */ - if (unlikely(rq->bio)) - blk_mq_end_request(rq, BLK_STS_IOERR); - else - blk_put_request(rq); -} - -void osd_end_request(struct osd_request *or) -{ - struct request *rq = or->request; - - if (rq) { - if (rq->next_rq) { - _put_request(rq->next_rq); - rq->next_rq = NULL; - } - - _put_request(rq); - } - - _osd_free_seg(or, &or->get_attr); - _osd_free_seg(or, &or->enc_get_attr); - _osd_free_seg(or, &or->set_attr); - _osd_free_seg(or, &or->cdb_cont); - - _osd_request_free(or); -} -EXPORT_SYMBOL(osd_end_request); - -static void _set_error_resid(struct osd_request *or, struct request *req, - blk_status_t error) -{ - or->async_error = error; - or->req_errors = scsi_req(req)->result; - or->sense_len = scsi_req(req)->sense_len; - if (or->sense_len) - memcpy(or->sense, scsi_req(req)->sense, or->sense_len); - if (or->out.req) - or->out.residual = scsi_req(or->out.req)->resid_len; - if (or->in.req) - or->in.residual = scsi_req(or->in.req)->resid_len; -} - -int osd_execute_request(struct osd_request *or) -{ - blk_execute_rq(or->request->q, NULL, or->request, 0); - - if (scsi_req(or->request)->result) { - _set_error_resid(or, or->request, BLK_STS_IOERR); - return -EIO; - } - - _set_error_resid(or, or->request, BLK_STS_OK); - return 0; -} -EXPORT_SYMBOL(osd_execute_request); - -static void osd_request_async_done(struct request *req, blk_status_t error) -{ - struct osd_request *or = req->end_io_data; - - _set_error_resid(or, req, error); - if (req->next_rq) { - blk_put_request(req->next_rq); - req->next_rq = NULL; - } - - blk_put_request(req); - or->request = NULL; - or->in.req = NULL; - or->out.req = NULL; - - if (or->async_done) - or->async_done(or, or->async_private); - else - osd_end_request(or); -} - -int osd_execute_request_async(struct osd_request *or, - osd_req_done_fn *done, void *private) -{ - or->request->end_io_data = or; - or->async_private = private; - or->async_done = done; - - blk_execute_rq_nowait(or->request->q, NULL, or->request, 0, - osd_request_async_done); - return 0; -} -EXPORT_SYMBOL(osd_execute_request_async); - -u8 sg_out_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT]; -u8 sg_in_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT]; - -static int _osd_realloc_seg(struct osd_request *or, - struct _osd_req_data_segment *seg, unsigned max_bytes) -{ - void *buff; - - if (seg->alloc_size >= max_bytes) - return 0; - - buff = krealloc(seg->buff, max_bytes, GFP_KERNEL); - if (!buff) { - OSD_ERR("Failed to Realloc %d-bytes was-%d\n", max_bytes, - seg->alloc_size); - return -ENOMEM; - } - - memset(buff + seg->alloc_size, 0, max_bytes - seg->alloc_size); - seg->buff = buff; - seg->alloc_size = max_bytes; - return 0; -} - -static int _alloc_cdb_cont(struct osd_request *or, unsigned total_bytes) -{ - OSD_DEBUG("total_bytes=%d\n", total_bytes); - return _osd_realloc_seg(or, &or->cdb_cont, total_bytes); -} - -static int _alloc_set_attr_list(struct osd_request *or, - const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) -{ - unsigned total_bytes = add_bytes; - - for (; nelem; --nelem, ++oa) - total_bytes += _osd_req_alist_elem_size(or, oa->len); - - OSD_DEBUG("total_bytes=%d\n", total_bytes); - return _osd_realloc_seg(or, &or->set_attr, total_bytes); -} - -static int _alloc_get_attr_desc(struct osd_request *or, unsigned max_bytes) -{ - OSD_DEBUG("total_bytes=%d\n", max_bytes); - return _osd_realloc_seg(or, &or->enc_get_attr, max_bytes); -} - -static int _alloc_get_attr_list(struct osd_request *or) -{ - OSD_DEBUG("total_bytes=%d\n", or->get_attr.total_bytes); - return _osd_realloc_seg(or, &or->get_attr, or->get_attr.total_bytes); -} - -/* - * Common to all OSD commands - */ - -static void _osdv1_req_encode_common(struct osd_request *or, - __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) -{ - struct osdv1_cdb *ocdb = &or->cdb.v1; - - /* - * For speed, the commands - * OSD_ACT_PERFORM_SCSI_COMMAND , V1 0x8F7E, V2 0x8F7C - * OSD_ACT_SCSI_TASK_MANAGEMENT , V1 0x8F7F, V2 0x8F7D - * are not supported here. Should pass zero and set after the call - */ - act &= cpu_to_be16(~0x0080); /* V1 action code */ - - OSD_DEBUG("OSDv1 execute opcode 0x%x\n", be16_to_cpu(act)); - - ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD; - ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH; - ocdb->h.varlen_cdb.service_action = act; - - ocdb->h.partition = cpu_to_be64(obj->partition); - ocdb->h.object = cpu_to_be64(obj->id); - ocdb->h.v1.length = cpu_to_be64(len); - ocdb->h.v1.start_address = cpu_to_be64(offset); -} - -static void _osdv2_req_encode_common(struct osd_request *or, - __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) -{ - struct osdv2_cdb *ocdb = &or->cdb.v2; - - OSD_DEBUG("OSDv2 execute opcode 0x%x\n", be16_to_cpu(act)); - - ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD; - ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH; - ocdb->h.varlen_cdb.service_action = act; - - ocdb->h.partition = cpu_to_be64(obj->partition); - ocdb->h.object = cpu_to_be64(obj->id); - ocdb->h.v2.length = cpu_to_be64(len); - ocdb->h.v2.start_address = cpu_to_be64(offset); -} - -static void _osd_req_encode_common(struct osd_request *or, - __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) -{ - if (osd_req_is_ver1(or)) - _osdv1_req_encode_common(or, act, obj, offset, len); - else - _osdv2_req_encode_common(or, act, obj, offset, len); -} - -/* - * Device commands - */ -/*TODO: void osd_req_set_master_seed_xchg(struct osd_request *, ...); */ -/*TODO: void osd_req_set_master_key(struct osd_request *, ...); */ - -void osd_req_format(struct osd_request *or, u64 tot_capacity) -{ - _osd_req_encode_common(or, OSD_ACT_FORMAT_OSD, &osd_root_object, 0, - tot_capacity); -} -EXPORT_SYMBOL(osd_req_format); - -int osd_req_list_dev_partitions(struct osd_request *or, - osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem) -{ - return osd_req_list_partition_objects(or, 0, initial_id, list, nelem); -} -EXPORT_SYMBOL(osd_req_list_dev_partitions); - -static void _osd_req_encode_flush(struct osd_request *or, - enum osd_options_flush_scope_values op) -{ - struct osd_cdb_head *ocdb = osd_cdb_head(&or->cdb); - - ocdb->command_specific_options = op; -} - -void osd_req_flush_obsd(struct osd_request *or, - enum osd_options_flush_scope_values op) -{ - _osd_req_encode_common(or, OSD_ACT_FLUSH_OSD, &osd_root_object, 0, 0); - _osd_req_encode_flush(or, op); -} -EXPORT_SYMBOL(osd_req_flush_obsd); - -/*TODO: void osd_req_perform_scsi_command(struct osd_request *, - const u8 *cdb, ...); */ -/*TODO: void osd_req_task_management(struct osd_request *, ...); */ - -/* - * Partition commands - */ -static void _osd_req_encode_partition(struct osd_request *or, - __be16 act, osd_id partition) -{ - struct osd_obj_id par = { - .partition = partition, - .id = 0, - }; - - _osd_req_encode_common(or, act, &par, 0, 0); -} - -void osd_req_create_partition(struct osd_request *or, osd_id partition) -{ - _osd_req_encode_partition(or, OSD_ACT_CREATE_PARTITION, partition); -} -EXPORT_SYMBOL(osd_req_create_partition); - -void osd_req_remove_partition(struct osd_request *or, osd_id partition) -{ - _osd_req_encode_partition(or, OSD_ACT_REMOVE_PARTITION, partition); -} -EXPORT_SYMBOL(osd_req_remove_partition); - -/*TODO: void osd_req_set_partition_key(struct osd_request *, - osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE], - u8 seed[OSD_CRYPTO_SEED_SIZE]); */ - -static int _osd_req_list_objects(struct osd_request *or, - __be16 action, const struct osd_obj_id *obj, osd_id initial_id, - struct osd_obj_id_list *list, unsigned nelem) -{ - struct request_queue *q = osd_request_queue(or->osd_dev); - u64 len = nelem * sizeof(osd_id) + sizeof(*list); - struct bio *bio; - - _osd_req_encode_common(or, action, obj, (u64)initial_id, len); - - if (list->list_identifier) - _osd_req_encode_olist(or, list); - - WARN_ON(or->in.bio); - bio = bio_map_kern(q, list, len, GFP_KERNEL); - if (IS_ERR(bio)) { - OSD_ERR("!!! Failed to allocate list_objects BIO\n"); - return PTR_ERR(bio); - } - - bio_set_op_attrs(bio, REQ_OP_READ, 0); - or->in.bio = bio; - or->in.total_bytes = bio->bi_iter.bi_size; - return 0; -} - -int osd_req_list_partition_collections(struct osd_request *or, - osd_id partition, osd_id initial_id, struct osd_obj_id_list *list, - unsigned nelem) -{ - struct osd_obj_id par = { - .partition = partition, - .id = 0, - }; - - return osd_req_list_collection_objects(or, &par, initial_id, list, - nelem); -} -EXPORT_SYMBOL(osd_req_list_partition_collections); - -int osd_req_list_partition_objects(struct osd_request *or, - osd_id partition, osd_id initial_id, struct osd_obj_id_list *list, - unsigned nelem) -{ - struct osd_obj_id par = { - .partition = partition, - .id = 0, - }; - - return _osd_req_list_objects(or, OSD_ACT_LIST, &par, initial_id, list, - nelem); -} -EXPORT_SYMBOL(osd_req_list_partition_objects); - -void osd_req_flush_partition(struct osd_request *or, - osd_id partition, enum osd_options_flush_scope_values op) -{ - _osd_req_encode_partition(or, OSD_ACT_FLUSH_PARTITION, partition); - _osd_req_encode_flush(or, op); -} -EXPORT_SYMBOL(osd_req_flush_partition); - -/* - * Collection commands - */ -/*TODO: void osd_req_create_collection(struct osd_request *, - const struct osd_obj_id *); */ -/*TODO: void osd_req_remove_collection(struct osd_request *, - const struct osd_obj_id *); */ - -int osd_req_list_collection_objects(struct osd_request *or, - const struct osd_obj_id *obj, osd_id initial_id, - struct osd_obj_id_list *list, unsigned nelem) -{ - return _osd_req_list_objects(or, OSD_ACT_LIST_COLLECTION, obj, - initial_id, list, nelem); -} -EXPORT_SYMBOL(osd_req_list_collection_objects); - -/*TODO: void query(struct osd_request *, ...); V2 */ - -void osd_req_flush_collection(struct osd_request *or, - const struct osd_obj_id *obj, enum osd_options_flush_scope_values op) -{ - _osd_req_encode_common(or, OSD_ACT_FLUSH_PARTITION, obj, 0, 0); - _osd_req_encode_flush(or, op); -} -EXPORT_SYMBOL(osd_req_flush_collection); - -/*TODO: void get_member_attrs(struct osd_request *, ...); V2 */ -/*TODO: void set_member_attrs(struct osd_request *, ...); V2 */ - -/* - * Object commands - */ -void osd_req_create_object(struct osd_request *or, struct osd_obj_id *obj) -{ - _osd_req_encode_common(or, OSD_ACT_CREATE, obj, 0, 0); -} -EXPORT_SYMBOL(osd_req_create_object); - -void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *obj) -{ - _osd_req_encode_common(or, OSD_ACT_REMOVE, obj, 0, 0); -} -EXPORT_SYMBOL(osd_req_remove_object); - - -/*TODO: void osd_req_create_multi(struct osd_request *or, - struct osd_obj_id *first, struct osd_obj_id_list *list, unsigned nelem); -*/ - -void osd_req_write(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, - struct bio *bio, u64 len) -{ - _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len); - WARN_ON(or->out.bio || or->out.total_bytes); - WARN_ON(!op_is_write(bio_op(bio))); - or->out.bio = bio; - or->out.total_bytes = len; -} -EXPORT_SYMBOL(osd_req_write); - -int osd_req_write_kern(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, void* buff, u64 len) -{ - struct request_queue *req_q = osd_request_queue(or->osd_dev); - struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL); - - if (IS_ERR(bio)) - return PTR_ERR(bio); - - bio_set_op_attrs(bio, REQ_OP_WRITE, 0); - osd_req_write(or, obj, offset, bio, len); - return 0; -} -EXPORT_SYMBOL(osd_req_write_kern); - -/*TODO: void osd_req_append(struct osd_request *, - const struct osd_obj_id *, struct bio *data_out); */ -/*TODO: void osd_req_create_write(struct osd_request *, - const struct osd_obj_id *, struct bio *data_out, u64 offset); */ -/*TODO: void osd_req_clear(struct osd_request *, - const struct osd_obj_id *, u64 offset, u64 len); */ -/*TODO: void osd_req_punch(struct osd_request *, - const struct osd_obj_id *, u64 offset, u64 len); V2 */ - -void osd_req_flush_object(struct osd_request *or, - const struct osd_obj_id *obj, enum osd_options_flush_scope_values op, - /*V2*/ u64 offset, /*V2*/ u64 len) -{ - if (unlikely(osd_req_is_ver1(or) && (offset || len))) { - OSD_DEBUG("OSD Ver1 flush on specific range ignored\n"); - offset = 0; - len = 0; - } - - _osd_req_encode_common(or, OSD_ACT_FLUSH, obj, offset, len); - _osd_req_encode_flush(or, op); -} -EXPORT_SYMBOL(osd_req_flush_object); - -void osd_req_read(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, - struct bio *bio, u64 len) -{ - _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len); - WARN_ON(or->in.bio || or->in.total_bytes); - WARN_ON(op_is_write(bio_op(bio))); - or->in.bio = bio; - or->in.total_bytes = len; -} -EXPORT_SYMBOL(osd_req_read); - -int osd_req_read_kern(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, void* buff, u64 len) -{ - struct request_queue *req_q = osd_request_queue(or->osd_dev); - struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL); - - if (IS_ERR(bio)) - return PTR_ERR(bio); - - osd_req_read(or, obj, offset, bio, len); - return 0; -} -EXPORT_SYMBOL(osd_req_read_kern); - -static int _add_sg_continuation_descriptor(struct osd_request *or, - const struct osd_sg_entry *sglist, unsigned numentries, u64 *len) -{ - struct osd_sg_continuation_descriptor *oscd; - u32 oscd_size; - unsigned i; - int ret; - - oscd_size = sizeof(*oscd) + numentries * sizeof(oscd->entries[0]); - - if (!or->cdb_cont.total_bytes) { - /* First time, jump over the header, we will write to: - * cdb_cont.buff + cdb_cont.total_bytes - */ - or->cdb_cont.total_bytes = - sizeof(struct osd_continuation_segment_header); - } - - ret = _alloc_cdb_cont(or, or->cdb_cont.total_bytes + oscd_size); - if (unlikely(ret)) - return ret; - - oscd = or->cdb_cont.buff + or->cdb_cont.total_bytes; - oscd->hdr.type = cpu_to_be16(SCATTER_GATHER_LIST); - oscd->hdr.pad_length = 0; - oscd->hdr.length = cpu_to_be32(oscd_size - sizeof(*oscd)); - - *len = 0; - /* copy the sg entries and convert to network byte order */ - for (i = 0; i < numentries; i++) { - oscd->entries[i].offset = cpu_to_be64(sglist[i].offset); - oscd->entries[i].len = cpu_to_be64(sglist[i].len); - *len += sglist[i].len; - } - - or->cdb_cont.total_bytes += oscd_size; - OSD_DEBUG("total_bytes=%d oscd_size=%d numentries=%d\n", - or->cdb_cont.total_bytes, oscd_size, numentries); - return 0; -} - -static int _osd_req_finalize_cdb_cont(struct osd_request *or, const u8 *cap_key) -{ - struct request_queue *req_q = osd_request_queue(or->osd_dev); - struct bio *bio; - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - struct osd_continuation_segment_header *cont_seg_hdr; - - if (!or->cdb_cont.total_bytes) - return 0; - - cont_seg_hdr = or->cdb_cont.buff; - cont_seg_hdr->format = CDB_CONTINUATION_FORMAT_V2; - cont_seg_hdr->service_action = cdbh->varlen_cdb.service_action; - - /* create a bio for continuation segment */ - bio = bio_map_kern(req_q, or->cdb_cont.buff, or->cdb_cont.total_bytes, - GFP_KERNEL); - if (IS_ERR(bio)) - return PTR_ERR(bio); - - bio_set_op_attrs(bio, REQ_OP_WRITE, 0); - - /* integrity check the continuation before the bio is linked - * with the other data segments since the continuation - * integrity is separate from the other data segments. - */ - osd_sec_sign_data(cont_seg_hdr->integrity_check, bio, cap_key); - - cdbh->v2.cdb_continuation_length = cpu_to_be32(or->cdb_cont.total_bytes); - - /* we can't use _req_append_segment, because we need to link in the - * continuation bio to the head of the bio list - the - * continuation segment (if it exists) is always the first segment in - * the out data buffer. - */ - bio->bi_next = or->out.bio; - or->out.bio = bio; - or->out.total_bytes += or->cdb_cont.total_bytes; - - return 0; -} - -/* osd_req_write_sg: Takes a @bio that points to the data out buffer and an - * @sglist that has the scatter gather entries. Scatter-gather enables a write - * of multiple none-contiguous areas of an object, in a single call. The extents - * may overlap and/or be in any order. The only constrain is that: - * total_bytes(sglist) >= total_bytes(bio) - */ -int osd_req_write_sg(struct osd_request *or, - const struct osd_obj_id *obj, struct bio *bio, - const struct osd_sg_entry *sglist, unsigned numentries) -{ - u64 len; - int ret = _add_sg_continuation_descriptor(or, sglist, numentries, &len); - - if (ret) - return ret; - osd_req_write(or, obj, 0, bio, len); - - return 0; -} -EXPORT_SYMBOL(osd_req_write_sg); - -/* osd_req_read_sg: Read multiple extents of an object into @bio - * See osd_req_write_sg - */ -int osd_req_read_sg(struct osd_request *or, - const struct osd_obj_id *obj, struct bio *bio, - const struct osd_sg_entry *sglist, unsigned numentries) -{ - u64 len; - u64 off; - int ret; - - if (numentries > 1) { - off = 0; - ret = _add_sg_continuation_descriptor(or, sglist, numentries, - &len); - if (ret) - return ret; - } else { - /* Optimize the case of single segment, read_sg is a - * bidi operation. - */ - len = sglist->len; - off = sglist->offset; - } - osd_req_read(or, obj, off, bio, len); - - return 0; -} -EXPORT_SYMBOL(osd_req_read_sg); - -/* SG-list write/read Kern API - * - * osd_req_{write,read}_sg_kern takes an array of @buff pointers and an array - * of sg_entries. @numentries indicates how many pointers and sg_entries there - * are. By requiring an array of buff pointers. This allows a caller to do a - * single write/read and scatter into multiple buffers. - * NOTE: Each buffer + len should not cross a page boundary. - */ -static struct bio *_create_sg_bios(struct osd_request *or, - void **buff, const struct osd_sg_entry *sglist, unsigned numentries) -{ - struct request_queue *q = osd_request_queue(or->osd_dev); - struct bio *bio; - unsigned i; - - bio = bio_kmalloc(GFP_KERNEL, numentries); - if (unlikely(!bio)) { - OSD_DEBUG("Failed to allocate BIO size=%u\n", numentries); - return ERR_PTR(-ENOMEM); - } - - for (i = 0; i < numentries; i++) { - unsigned offset = offset_in_page(buff[i]); - struct page *page = virt_to_page(buff[i]); - unsigned len = sglist[i].len; - unsigned added_len; - - BUG_ON(offset + len > PAGE_SIZE); - added_len = bio_add_pc_page(q, bio, page, len, offset); - if (unlikely(len != added_len)) { - OSD_DEBUG("bio_add_pc_page len(%d) != added_len(%d)\n", - len, added_len); - bio_put(bio); - return ERR_PTR(-ENOMEM); - } - } - - return bio; -} - -int osd_req_write_sg_kern(struct osd_request *or, - const struct osd_obj_id *obj, void **buff, - const struct osd_sg_entry *sglist, unsigned numentries) -{ - struct bio *bio = _create_sg_bios(or, buff, sglist, numentries); - if (IS_ERR(bio)) - return PTR_ERR(bio); - - bio_set_op_attrs(bio, REQ_OP_WRITE, 0); - osd_req_write_sg(or, obj, bio, sglist, numentries); - - return 0; -} -EXPORT_SYMBOL(osd_req_write_sg_kern); - -int osd_req_read_sg_kern(struct osd_request *or, - const struct osd_obj_id *obj, void **buff, - const struct osd_sg_entry *sglist, unsigned numentries) -{ - struct bio *bio = _create_sg_bios(or, buff, sglist, numentries); - if (IS_ERR(bio)) - return PTR_ERR(bio); - - osd_req_read_sg(or, obj, bio, sglist, numentries); - - return 0; -} -EXPORT_SYMBOL(osd_req_read_sg_kern); - - - -void osd_req_get_attributes(struct osd_request *or, - const struct osd_obj_id *obj) -{ - _osd_req_encode_common(or, OSD_ACT_GET_ATTRIBUTES, obj, 0, 0); -} -EXPORT_SYMBOL(osd_req_get_attributes); - -void osd_req_set_attributes(struct osd_request *or, - const struct osd_obj_id *obj) -{ - _osd_req_encode_common(or, OSD_ACT_SET_ATTRIBUTES, obj, 0, 0); -} -EXPORT_SYMBOL(osd_req_set_attributes); - -/* - * Attributes List-mode - */ - -int osd_req_add_set_attr_list(struct osd_request *or, - const struct osd_attr *oa, unsigned nelem) -{ - unsigned total_bytes = or->set_attr.total_bytes; - void *attr_last; - int ret; - - if (or->attributes_mode && - or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) { - WARN_ON(1); - return -EINVAL; - } - or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; - - if (!total_bytes) { /* first-time: allocate and put list header */ - total_bytes = _osd_req_sizeof_alist_header(or); - ret = _alloc_set_attr_list(or, oa, nelem, total_bytes); - if (ret) - return ret; - _osd_req_set_alist_type(or, or->set_attr.buff, - OSD_ATTR_LIST_SET_RETRIEVE); - } - attr_last = or->set_attr.buff + total_bytes; - - for (; nelem; --nelem) { - unsigned elem_size = _osd_req_alist_elem_size(or, oa->len); - - total_bytes += elem_size; - if (unlikely(or->set_attr.alloc_size < total_bytes)) { - or->set_attr.total_bytes = total_bytes - elem_size; - ret = _alloc_set_attr_list(or, oa, nelem, total_bytes); - if (ret) - return ret; - attr_last = - or->set_attr.buff + or->set_attr.total_bytes; - } - - _osd_req_alist_elem_encode(or, attr_last, oa); - - attr_last += elem_size; - ++oa; - } - - or->set_attr.total_bytes = total_bytes; - return 0; -} -EXPORT_SYMBOL(osd_req_add_set_attr_list); - -static int _req_append_segment(struct osd_request *or, - unsigned padding, struct _osd_req_data_segment *seg, - struct _osd_req_data_segment *last_seg, struct _osd_io_info *io) -{ - void *pad_buff; - int ret; - - if (padding) { - /* check if we can just add it to last buffer */ - if (last_seg && - (padding <= last_seg->alloc_size - last_seg->total_bytes)) - pad_buff = last_seg->buff + last_seg->total_bytes; - else - pad_buff = io->pad_buff; - - ret = blk_rq_map_kern(io->req->q, io->req, pad_buff, padding, - GFP_KERNEL); - if (ret) - return ret; - io->total_bytes += padding; - } - - ret = blk_rq_map_kern(io->req->q, io->req, seg->buff, seg->total_bytes, - GFP_KERNEL); - if (ret) - return ret; - - io->total_bytes += seg->total_bytes; - OSD_DEBUG("padding=%d buff=%p total_bytes=%d\n", padding, seg->buff, - seg->total_bytes); - return 0; -} - -static int _osd_req_finalize_set_attr_list(struct osd_request *or) -{ - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - unsigned padding; - int ret; - - if (!or->set_attr.total_bytes) { - cdbh->attrs_list.set_attr_offset = OSD_OFFSET_UNUSED; - return 0; - } - - cdbh->attrs_list.set_attr_bytes = cpu_to_be32(or->set_attr.total_bytes); - cdbh->attrs_list.set_attr_offset = - osd_req_encode_offset(or, or->out.total_bytes, &padding); - - ret = _req_append_segment(or, padding, &or->set_attr, - or->out.last_seg, &or->out); - if (ret) - return ret; - - or->out.last_seg = &or->set_attr; - return 0; -} - -int osd_req_add_get_attr_list(struct osd_request *or, - const struct osd_attr *oa, unsigned nelem) -{ - unsigned total_bytes = or->enc_get_attr.total_bytes; - void *attr_last; - int ret; - - if (or->attributes_mode && - or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) { - WARN_ON(1); - return -EINVAL; - } - or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; - - /* first time calc data-in list header size */ - if (!or->get_attr.total_bytes) - or->get_attr.total_bytes = _osd_req_sizeof_alist_header(or); - - /* calc data-out info */ - if (!total_bytes) { /* first-time: allocate and put list header */ - unsigned max_bytes; - - total_bytes = _osd_req_sizeof_alist_header(or); - max_bytes = total_bytes + - nelem * sizeof(struct osd_attributes_list_attrid); - ret = _alloc_get_attr_desc(or, max_bytes); - if (ret) - return ret; - - _osd_req_set_alist_type(or, or->enc_get_attr.buff, - OSD_ATTR_LIST_GET); - } - attr_last = or->enc_get_attr.buff + total_bytes; - - for (; nelem; --nelem) { - struct osd_attributes_list_attrid *attrid; - const unsigned cur_size = sizeof(*attrid); - - total_bytes += cur_size; - if (unlikely(or->enc_get_attr.alloc_size < total_bytes)) { - or->enc_get_attr.total_bytes = total_bytes - cur_size; - ret = _alloc_get_attr_desc(or, - total_bytes + nelem * sizeof(*attrid)); - if (ret) - return ret; - attr_last = or->enc_get_attr.buff + - or->enc_get_attr.total_bytes; - } - - attrid = attr_last; - attrid->attr_page = cpu_to_be32(oa->attr_page); - attrid->attr_id = cpu_to_be32(oa->attr_id); - - attr_last += cur_size; - - /* calc data-in size */ - or->get_attr.total_bytes += - _osd_req_alist_elem_size(or, oa->len); - ++oa; - } - - or->enc_get_attr.total_bytes = total_bytes; - - OSD_DEBUG( - "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%zu)\n", - or->get_attr.total_bytes, - or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or), - or->enc_get_attr.total_bytes, - (or->enc_get_attr.total_bytes - _osd_req_sizeof_alist_header(or)) - / sizeof(struct osd_attributes_list_attrid)); - - return 0; -} -EXPORT_SYMBOL(osd_req_add_get_attr_list); - -static int _osd_req_finalize_get_attr_list(struct osd_request *or) -{ - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - unsigned out_padding; - unsigned in_padding; - int ret; - - if (!or->enc_get_attr.total_bytes) { - cdbh->attrs_list.get_attr_desc_offset = OSD_OFFSET_UNUSED; - cdbh->attrs_list.get_attr_offset = OSD_OFFSET_UNUSED; - return 0; - } - - ret = _alloc_get_attr_list(or); - if (ret) - return ret; - - /* The out-going buffer info update */ - OSD_DEBUG("out-going\n"); - cdbh->attrs_list.get_attr_desc_bytes = - cpu_to_be32(or->enc_get_attr.total_bytes); - - cdbh->attrs_list.get_attr_desc_offset = - osd_req_encode_offset(or, or->out.total_bytes, &out_padding); - - ret = _req_append_segment(or, out_padding, &or->enc_get_attr, - or->out.last_seg, &or->out); - if (ret) - return ret; - or->out.last_seg = &or->enc_get_attr; - - /* The incoming buffer info update */ - OSD_DEBUG("in-coming\n"); - cdbh->attrs_list.get_attr_alloc_length = - cpu_to_be32(or->get_attr.total_bytes); - - cdbh->attrs_list.get_attr_offset = - osd_req_encode_offset(or, or->in.total_bytes, &in_padding); - - ret = _req_append_segment(or, in_padding, &or->get_attr, NULL, - &or->in); - if (ret) - return ret; - or->in.last_seg = &or->get_attr; - - return 0; -} - -int osd_req_decode_get_attr_list(struct osd_request *or, - struct osd_attr *oa, int *nelem, void **iterator) -{ - unsigned cur_bytes, returned_bytes; - int n; - const unsigned sizeof_attr_list = _osd_req_sizeof_alist_header(or); - void *cur_p; - - if (!_osd_req_is_alist_type(or, or->get_attr.buff, - OSD_ATTR_LIST_SET_RETRIEVE)) { - oa->attr_page = 0; - oa->attr_id = 0; - oa->val_ptr = NULL; - oa->len = 0; - *iterator = NULL; - return 0; - } - - if (*iterator) { - BUG_ON((*iterator < or->get_attr.buff) || - (or->get_attr.buff + or->get_attr.alloc_size < *iterator)); - cur_p = *iterator; - cur_bytes = (*iterator - or->get_attr.buff) - sizeof_attr_list; - returned_bytes = or->get_attr.total_bytes; - } else { /* first time decode the list header */ - cur_bytes = sizeof_attr_list; - returned_bytes = _osd_req_alist_size(or, or->get_attr.buff) + - sizeof_attr_list; - - cur_p = or->get_attr.buff + sizeof_attr_list; - - if (returned_bytes > or->get_attr.alloc_size) { - OSD_DEBUG("target report: space was not big enough! " - "Allocate=%u Needed=%u\n", - or->get_attr.alloc_size, - returned_bytes + sizeof_attr_list); - - returned_bytes = - or->get_attr.alloc_size - sizeof_attr_list; - } - or->get_attr.total_bytes = returned_bytes; - } - - for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) { - int inc = _osd_req_alist_elem_decode(or, cur_p, oa, - returned_bytes - cur_bytes); - - if (inc < 0) { - OSD_ERR("BAD FOOD from target. list not valid!" - "c=%d r=%d n=%d\n", - cur_bytes, returned_bytes, n); - oa->val_ptr = NULL; - cur_bytes = returned_bytes; /* break the caller loop */ - break; - } - - cur_bytes += inc; - cur_p += inc; - ++oa; - } - - *iterator = (returned_bytes - cur_bytes) ? cur_p : NULL; - *nelem = n; - return returned_bytes - cur_bytes; -} -EXPORT_SYMBOL(osd_req_decode_get_attr_list); - -/* - * Attributes Page-mode - */ - -int osd_req_add_get_attr_page(struct osd_request *or, - u32 page_id, void *attar_page, unsigned max_page_len, - const struct osd_attr *set_one_attr) -{ - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - - if (or->attributes_mode && - or->attributes_mode != OSD_CDB_GET_ATTR_PAGE_SET_ONE) { - WARN_ON(1); - return -EINVAL; - } - or->attributes_mode = OSD_CDB_GET_ATTR_PAGE_SET_ONE; - - or->get_attr.buff = attar_page; - or->get_attr.total_bytes = max_page_len; - - cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id); - cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len); - - if (!set_one_attr || !set_one_attr->attr_page) - return 0; /* The set is optional */ - - or->set_attr.buff = set_one_attr->val_ptr; - or->set_attr.total_bytes = set_one_attr->len; - - cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page); - cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id); - cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len); - return 0; -} -EXPORT_SYMBOL(osd_req_add_get_attr_page); - -static int _osd_req_finalize_attr_page(struct osd_request *or) -{ - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - unsigned in_padding, out_padding; - int ret; - - /* returned page */ - cdbh->attrs_page.get_attr_offset = - osd_req_encode_offset(or, or->in.total_bytes, &in_padding); - - ret = _req_append_segment(or, in_padding, &or->get_attr, NULL, - &or->in); - if (ret) - return ret; - - if (or->set_attr.total_bytes == 0) - return 0; - - /* set one value */ - cdbh->attrs_page.set_attr_offset = - osd_req_encode_offset(or, or->out.total_bytes, &out_padding); - - ret = _req_append_segment(or, out_padding, &or->set_attr, NULL, - &or->out); - return ret; -} - -static inline void osd_sec_parms_set_out_offset(bool is_v1, - struct osd_security_parameters *sec_parms, osd_cdb_offset offset) -{ - if (is_v1) - sec_parms->v1.data_out_integrity_check_offset = offset; - else - sec_parms->v2.data_out_integrity_check_offset = offset; -} - -static inline void osd_sec_parms_set_in_offset(bool is_v1, - struct osd_security_parameters *sec_parms, osd_cdb_offset offset) -{ - if (is_v1) - sec_parms->v1.data_in_integrity_check_offset = offset; - else - sec_parms->v2.data_in_integrity_check_offset = offset; -} - -static int _osd_req_finalize_data_integrity(struct osd_request *or, - bool has_in, bool has_out, struct bio *out_data_bio, u64 out_data_bytes, - const u8 *cap_key) -{ - struct osd_security_parameters *sec_parms = _osd_req_sec_params(or); - int ret; - - if (!osd_is_sec_alldata(sec_parms)) - return 0; - - if (has_out) { - struct _osd_req_data_segment seg = { - .buff = &or->out_data_integ, - .total_bytes = sizeof(or->out_data_integ), - }; - unsigned pad; - - or->out_data_integ.data_bytes = cpu_to_be64(out_data_bytes); - or->out_data_integ.set_attributes_bytes = cpu_to_be64( - or->set_attr.total_bytes); - or->out_data_integ.get_attributes_bytes = cpu_to_be64( - or->enc_get_attr.total_bytes); - - osd_sec_parms_set_out_offset(osd_req_is_ver1(or), sec_parms, - osd_req_encode_offset(or, or->out.total_bytes, &pad)); - - ret = _req_append_segment(or, pad, &seg, or->out.last_seg, - &or->out); - if (ret) - return ret; - or->out.last_seg = NULL; - - /* they are now all chained to request sign them all together */ - osd_sec_sign_data(&or->out_data_integ, out_data_bio, - cap_key); - } - - if (has_in) { - struct _osd_req_data_segment seg = { - .buff = &or->in_data_integ, - .total_bytes = sizeof(or->in_data_integ), - }; - unsigned pad; - - osd_sec_parms_set_in_offset(osd_req_is_ver1(or), sec_parms, - osd_req_encode_offset(or, or->in.total_bytes, &pad)); - - ret = _req_append_segment(or, pad, &seg, or->in.last_seg, - &or->in); - if (ret) - return ret; - - or->in.last_seg = NULL; - } - - return 0; -} - -/* - * osd_finalize_request and helpers - */ -static struct request *_make_request(struct request_queue *q, bool has_write, - struct _osd_io_info *oii) -{ - struct request *req; - struct bio *bio = oii->bio; - int ret; - - req = blk_get_request(q, has_write ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, - 0); - if (IS_ERR(req)) - return req; - - for_each_bio(bio) { - struct bio *bounce_bio = bio; - - ret = blk_rq_append_bio(req, &bounce_bio); - if (ret) - return ERR_PTR(ret); - } - - return req; -} - -static int _init_blk_request(struct osd_request *or, - bool has_in, bool has_out) -{ - struct scsi_device *scsi_device = or->osd_dev->scsi_device; - struct request_queue *q = scsi_device->request_queue; - struct request *req; - int ret; - - req = _make_request(q, has_out, has_out ? &or->out : &or->in); - if (IS_ERR(req)) { - ret = PTR_ERR(req); - goto out; - } - - or->request = req; - req->rq_flags |= RQF_QUIET; - - req->timeout = or->timeout; - scsi_req(req)->retries = or->retries; - - if (has_out) { - or->out.req = req; - if (has_in) { - /* allocate bidi request */ - req = _make_request(q, false, &or->in); - if (IS_ERR(req)) { - OSD_DEBUG("blk_get_request for bidi failed\n"); - ret = PTR_ERR(req); - goto out; - } - or->in.req = or->request->next_rq = req; - } - } else if (has_in) - or->in.req = req; - - ret = 0; -out: - OSD_DEBUG("or=%p has_in=%d has_out=%d => %d, %p\n", - or, has_in, has_out, ret, or->request); - return ret; -} - -int osd_finalize_request(struct osd_request *or, - u8 options, const void *cap, const u8 *cap_key) -{ - struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); - bool has_in, has_out; - /* Save for data_integrity without the cdb_continuation */ - struct bio *out_data_bio = or->out.bio; - u64 out_data_bytes = or->out.total_bytes; - int ret; - - if (options & OSD_REQ_FUA) - cdbh->options |= OSD_CDB_FUA; - - if (options & OSD_REQ_DPO) - cdbh->options |= OSD_CDB_DPO; - - if (options & OSD_REQ_BYPASS_TIMESTAMPS) - cdbh->timestamp_control = OSD_CDB_BYPASS_TIMESTAMPS; - - osd_set_caps(&or->cdb, cap); - - has_in = or->in.bio || or->get_attr.total_bytes; - has_out = or->out.bio || or->cdb_cont.total_bytes || - or->set_attr.total_bytes || or->enc_get_attr.total_bytes; - - ret = _osd_req_finalize_cdb_cont(or, cap_key); - if (ret) { - OSD_DEBUG("_osd_req_finalize_cdb_cont failed\n"); - return ret; - } - ret = _init_blk_request(or, has_in, has_out); - if (ret) { - OSD_DEBUG("_init_blk_request failed\n"); - return ret; - } - - or->out.pad_buff = sg_out_pad_buffer; - or->in.pad_buff = sg_in_pad_buffer; - - if (!or->attributes_mode) - or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; - cdbh->command_specific_options |= or->attributes_mode; - if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) { - ret = _osd_req_finalize_attr_page(or); - if (ret) { - OSD_DEBUG("_osd_req_finalize_attr_page failed\n"); - return ret; - } - } else { - /* TODO: I think that for the GET_ATTR command these 2 should - * be reversed to keep them in execution order (for embedded - * targets with low memory footprint) - */ - ret = _osd_req_finalize_set_attr_list(or); - if (ret) { - OSD_DEBUG("_osd_req_finalize_set_attr_list failed\n"); - return ret; - } - - ret = _osd_req_finalize_get_attr_list(or); - if (ret) { - OSD_DEBUG("_osd_req_finalize_get_attr_list failed\n"); - return ret; - } - } - - ret = _osd_req_finalize_data_integrity(or, has_in, has_out, - out_data_bio, out_data_bytes, - cap_key); - if (ret) - return ret; - - osd_sec_sign_cdb(&or->cdb, cap_key); - - scsi_req(or->request)->cmd = or->cdb.buff; - scsi_req(or->request)->cmd_len = _osd_req_cdb_len(or); - - return 0; -} -EXPORT_SYMBOL(osd_finalize_request); - -static bool _is_osd_security_code(int code) -{ - return (code == osd_security_audit_value_frozen) || - (code == osd_security_working_key_frozen) || - (code == osd_nonce_not_unique) || - (code == osd_nonce_timestamp_out_of_range) || - (code == osd_invalid_dataout_buffer_integrity_check_value); -} - -#define OSD_SENSE_PRINT1(fmt, a...) \ - do { \ - if (__cur_sense_need_output) \ - OSD_ERR(fmt, ##a); \ - } while (0) - -#define OSD_SENSE_PRINT2(fmt, a...) OSD_SENSE_PRINT1(" " fmt, ##a) - -int osd_req_decode_sense_full(struct osd_request *or, - struct osd_sense_info *osi, bool silent, - struct osd_obj_id *bad_obj_list __unused, int max_obj __unused, - struct osd_attr *bad_attr_list, int max_attr) -{ - int sense_len, original_sense_len; - struct osd_sense_info local_osi; - struct scsi_sense_descriptor_based *ssdb; - void *cur_descriptor; -#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 0) - const bool __cur_sense_need_output = false; -#else - bool __cur_sense_need_output = !silent; -#endif - int ret; - - if (likely(!or->req_errors)) - return 0; - - osi = osi ? : &local_osi; - memset(osi, 0, sizeof(*osi)); - - ssdb = (typeof(ssdb))or->sense; - sense_len = or->sense_len; - if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) { - OSD_ERR("Block-layer returned error(0x%x) but " - "sense_len(%u) || key(%d) is empty\n", - or->req_errors, sense_len, ssdb->sense_key); - goto analyze; - } - - if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { - OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", - ssdb->response_code, sense_len); - goto analyze; - } - - osi->key = ssdb->sense_key; - osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); - original_sense_len = ssdb->additional_sense_length + 8; - -#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 1) - if (__cur_sense_need_output) - __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); -#endif - OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " - "additional_code=0x%x async_error=%d errors=0x%x\n", - osi->key, original_sense_len, sense_len, - osi->additional_code, or->async_error, - or->req_errors); - - if (original_sense_len < sense_len) - sense_len = original_sense_len; - - cur_descriptor = ssdb->ssd; - sense_len -= sizeof(*ssdb); - while (sense_len > 0) { - struct scsi_sense_descriptor *ssd = cur_descriptor; - int cur_len = ssd->additional_length + 2; - - sense_len -= cur_len; - - if (sense_len < 0) - break; /* sense was truncated */ - - switch (ssd->descriptor_type) { - case scsi_sense_information: - case scsi_sense_command_specific_information: - { - struct scsi_sense_command_specific_data_descriptor - *sscd = cur_descriptor; - - osi->command_info = - get_unaligned_be64(&sscd->information) ; - OSD_SENSE_PRINT2( - "command_specific_information 0x%llx \n", - _LLU(osi->command_info)); - break; - } - case scsi_sense_key_specific: - { - struct scsi_sense_key_specific_data_descriptor - *ssks = cur_descriptor; - - osi->sense_info = get_unaligned_be16(&ssks->value); - OSD_SENSE_PRINT2( - "sense_key_specific_information %u" - "sksv_cd_bpv_bp (0x%x)\n", - osi->sense_info, ssks->sksv_cd_bpv_bp); - break; - } - case osd_sense_object_identification: - { /*FIXME: Keep first not last, Store in array*/ - struct osd_sense_identification_data_descriptor - *osidd = cur_descriptor; - - osi->not_initiated_command_functions = - le32_to_cpu(osidd->not_initiated_functions); - osi->completed_command_functions = - le32_to_cpu(osidd->completed_functions); - osi->obj.partition = be64_to_cpu(osidd->partition_id); - osi->obj.id = be64_to_cpu(osidd->object_id); - OSD_SENSE_PRINT2( - "object_identification pid=0x%llx oid=0x%llx\n", - _LLU(osi->obj.partition), _LLU(osi->obj.id)); - OSD_SENSE_PRINT2( - "not_initiated_bits(%x) " - "completed_command_bits(%x)\n", - osi->not_initiated_command_functions, - osi->completed_command_functions); - break; - } - case osd_sense_response_integrity_check: - { - struct osd_sense_response_integrity_check_descriptor - *d = cur_descriptor; - /* 2nibbles+space+ASCII */ - char dump[sizeof(d->integrity_check_value) * 4 + 2]; - - hex_dump_to_buffer(d->integrity_check_value, - sizeof(d->integrity_check_value), - 32, 1, dump, sizeof(dump), true); - OSD_SENSE_PRINT2("response_integrity [%s]\n", dump); - } - case osd_sense_attribute_identification: - { - struct osd_sense_attributes_data_descriptor - *osadd = cur_descriptor; - unsigned len = min(cur_len, sense_len); - struct osd_sense_attr *pattr = osadd->sense_attrs; - - while (len >= sizeof(*pattr)) { - u32 attr_page = be32_to_cpu(pattr->attr_page); - u32 attr_id = be32_to_cpu(pattr->attr_id); - - if (!osi->attr.attr_page) { - osi->attr.attr_page = attr_page; - osi->attr.attr_id = attr_id; - } - - if (bad_attr_list && max_attr) { - bad_attr_list->attr_page = attr_page; - bad_attr_list->attr_id = attr_id; - bad_attr_list++; - max_attr--; - } - - len -= sizeof(*pattr); - OSD_SENSE_PRINT2( - "osd_sense_attribute_identification" - "attr_page=0x%x attr_id=0x%x\n", - attr_page, attr_id); - } - } - /*These are not legal for OSD*/ - case scsi_sense_field_replaceable_unit: - OSD_SENSE_PRINT2("scsi_sense_field_replaceable_unit\n"); - break; - case scsi_sense_stream_commands: - OSD_SENSE_PRINT2("scsi_sense_stream_commands\n"); - break; - case scsi_sense_block_commands: - OSD_SENSE_PRINT2("scsi_sense_block_commands\n"); - break; - case scsi_sense_ata_return: - OSD_SENSE_PRINT2("scsi_sense_ata_return\n"); - break; - default: - if (ssd->descriptor_type <= scsi_sense_Reserved_last) - OSD_SENSE_PRINT2( - "scsi_sense Reserved descriptor (0x%x)", - ssd->descriptor_type); - else - OSD_SENSE_PRINT2( - "scsi_sense Vendor descriptor (0x%x)", - ssd->descriptor_type); - } - - cur_descriptor += cur_len; - } - -analyze: - if (!osi->key) { - /* scsi sense is Empty, the request was never issued to target - * linux return code might tell us what happened. - */ - if (or->async_error == BLK_STS_RESOURCE) - osi->osd_err_pri = OSD_ERR_PRI_RESOURCE; - else - osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE; - ret = or->async_error; - } else if (osi->key <= scsi_sk_recovered_error) { - osi->osd_err_pri = 0; - ret = 0; - } else if (osi->additional_code == scsi_invalid_field_in_cdb) { - if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) { - osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES; - ret = -EFAULT; /* caller should recover from this */ - } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) { - osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND; - ret = -ENOENT; - } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) { - osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS; - ret = -EACCES; - } else { - osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; - ret = -EINVAL; - } - } else if (osi->additional_code == osd_quota_error) { - osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE; - ret = -ENOSPC; - } else if (_is_osd_security_code(osi->additional_code)) { - osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED; - ret = -EINVAL; - } else { - osi->osd_err_pri = OSD_ERR_PRI_EIO; - ret = -EIO; - } - - if (!or->out.residual) - or->out.residual = or->out.total_bytes; - if (!or->in.residual) - or->in.residual = or->in.total_bytes; - - return ret; -} -EXPORT_SYMBOL(osd_req_decode_sense_full); - -/* - * Implementation of osd_sec.h API - * TODO: Move to a separate osd_sec.c file at a later stage. - */ - -enum { OSD_SEC_CAP_V1_ALL_CAPS = - OSD_SEC_CAP_APPEND | OSD_SEC_CAP_OBJ_MGMT | OSD_SEC_CAP_REMOVE | - OSD_SEC_CAP_CREATE | OSD_SEC_CAP_SET_ATTR | OSD_SEC_CAP_GET_ATTR | - OSD_SEC_CAP_WRITE | OSD_SEC_CAP_READ | OSD_SEC_CAP_POL_SEC | - OSD_SEC_CAP_GLOBAL | OSD_SEC_CAP_DEV_MGMT -}; - -enum { OSD_SEC_CAP_V2_ALL_CAPS = - OSD_SEC_CAP_V1_ALL_CAPS | OSD_SEC_CAP_QUERY | OSD_SEC_CAP_M_OBJECT -}; - -void osd_sec_init_nosec_doall_caps(void *caps, - const struct osd_obj_id *obj, bool is_collection, const bool is_v1) -{ - struct osd_capability *cap = caps; - u8 type; - u8 descriptor_type; - - if (likely(obj->id)) { - if (unlikely(is_collection)) { - type = OSD_SEC_OBJ_COLLECTION; - descriptor_type = is_v1 ? OSD_SEC_OBJ_DESC_OBJ : - OSD_SEC_OBJ_DESC_COL; - } else { - type = OSD_SEC_OBJ_USER; - descriptor_type = OSD_SEC_OBJ_DESC_OBJ; - } - WARN_ON(!obj->partition); - } else { - type = obj->partition ? OSD_SEC_OBJ_PARTITION : - OSD_SEC_OBJ_ROOT; - descriptor_type = OSD_SEC_OBJ_DESC_PAR; - } - - memset(cap, 0, sizeof(*cap)); - - cap->h.format = OSD_SEC_CAP_FORMAT_VER1; - cap->h.integrity_algorithm__key_version = 0; /* MAKE_BYTE(0, 0); */ - cap->h.security_method = OSD_SEC_NOSEC; -/* cap->expiration_time; - cap->AUDIT[30-10]; - cap->discriminator[42-30]; - cap->object_created_time; */ - cap->h.object_type = type; - osd_sec_set_caps(&cap->h, OSD_SEC_CAP_V1_ALL_CAPS); - cap->h.object_descriptor_type = descriptor_type; - cap->od.obj_desc.policy_access_tag = 0; - cap->od.obj_desc.allowed_partition_id = cpu_to_be64(obj->partition); - cap->od.obj_desc.allowed_object_id = cpu_to_be64(obj->id); -} -EXPORT_SYMBOL(osd_sec_init_nosec_doall_caps); - -/* FIXME: Extract version from caps pointer. - * Also Pete's target only supports caps from OSDv1 for now - */ -void osd_set_caps(struct osd_cdb *cdb, const void *caps) -{ - /* NOTE: They start at same address */ - memcpy(&cdb->v1.caps, caps, OSDv1_CAP_LEN); -} - -bool osd_is_sec_alldata(struct osd_security_parameters *sec_parms __unused) -{ - return false; -} - -void osd_sec_sign_cdb(struct osd_cdb *ocdb __unused, const u8 *cap_key __unused) -{ -} - -void osd_sec_sign_data(void *data_integ __unused, - struct bio *bio __unused, const u8 *cap_key __unused) -{ -} - -/* - * Declared in osd_protocol.h - * 4.12.5 Data-In and Data-Out buffer offsets - * byte offset = mantissa * (2^(exponent+8)) - * Returns the smallest allowed encoded offset that contains given @offset - * The actual encoded offset returned is @offset + *@padding. - */ -osd_cdb_offset __osd_encode_offset( - u64 offset, unsigned *padding, int min_shift, int max_shift) -{ - u64 try_offset = -1, mod, align; - osd_cdb_offset be32_offset; - int shift; - - *padding = 0; - if (!offset) - return 0; - - for (shift = min_shift; shift < max_shift; ++shift) { - try_offset = offset >> shift; - if (try_offset < (1 << OSD_OFFSET_MAX_BITS)) - break; - } - - BUG_ON(shift == max_shift); - - align = 1 << shift; - mod = offset & (align - 1); - if (mod) { - *padding = align - mod; - try_offset += 1; - } - - try_offset |= ((shift - 8) & 0xf) << 28; - be32_offset = cpu_to_be32((u32)try_offset); - - OSD_DEBUG("offset=%llu mantissa=%llu exp=%d encoded=%x pad=%d\n", - _LLU(offset), _LLU(try_offset & 0x0FFFFFFF), shift, - be32_offset, *padding); - return be32_offset; -} diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c deleted file mode 100644 index eaf36ccf58db..000000000000 --- a/drivers/scsi/osd/osd_uld.c +++ /dev/null @@ -1,571 +0,0 @@ -/* - * osd_uld.c - OSD Upper Layer Driver - * - * A Linux driver module that registers as a SCSI ULD and probes - * for OSD type SCSI devices. - * It's main function is to export osd devices to in-kernel users like - * osdfs and pNFS-objects-LD. It also provides one ioctl for running - * in Kernel tests. - * - * Copyright (C) 2008 Panasas Inc. All rights reserved. - * - * Authors: - * Boaz Harrosh - * Benny Halevy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Panasas company nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "osd_debug.h" - -#ifndef TYPE_OSD -# define TYPE_OSD 0x11 -#endif - -#ifndef SCSI_OSD_MAJOR -# define SCSI_OSD_MAJOR 260 -#endif -#define SCSI_OSD_MAX_MINOR MINORMASK - -static const char osd_name[] = "osd"; -static const char *osd_version_string = "open-osd 0.2.1"; - -MODULE_AUTHOR("Boaz Harrosh "); -MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR); -MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD); - -struct osd_uld_device { - int minor; - struct device class_dev; - struct cdev cdev; - struct osd_dev od; - struct osd_dev_info odi; - struct gendisk *disk; -}; - -struct osd_dev_handle { - struct osd_dev od; - struct file *file; - struct osd_uld_device *oud; -} ; - -static DEFINE_IDA(osd_minor_ida); - -/* - * scsi sysfs attribute operations - */ -static ssize_t osdname_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct osd_uld_device *ould = container_of(dev, struct osd_uld_device, - class_dev); - return sprintf(buf, "%s\n", ould->odi.osdname); -} -static DEVICE_ATTR_RO(osdname); - -static ssize_t systemid_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct osd_uld_device *ould = container_of(dev, struct osd_uld_device, - class_dev); - - memcpy(buf, ould->odi.systemid, ould->odi.systemid_len); - return ould->odi.systemid_len; -} -static DEVICE_ATTR_RO(systemid); - -static struct attribute *osd_uld_attrs[] = { - &dev_attr_osdname.attr, - &dev_attr_systemid.attr, - NULL, -}; -ATTRIBUTE_GROUPS(osd_uld); - -static struct class osd_uld_class = { - .owner = THIS_MODULE, - .name = "scsi_osd", - .dev_groups = osd_uld_groups, -}; - -/* - * Char Device operations - */ - -static int osd_uld_open(struct inode *inode, struct file *file) -{ - struct osd_uld_device *oud = container_of(inode->i_cdev, - struct osd_uld_device, cdev); - - get_device(&oud->class_dev); - /* cache osd_uld_device on file handle */ - file->private_data = oud; - OSD_DEBUG("osd_uld_open %p\n", oud); - return 0; -} - -static int osd_uld_release(struct inode *inode, struct file *file) -{ - struct osd_uld_device *oud = file->private_data; - - OSD_DEBUG("osd_uld_release %p\n", file->private_data); - file->private_data = NULL; - put_device(&oud->class_dev); - return 0; -} - -/* FIXME: Only one vector for now */ -unsigned g_test_ioctl; -do_test_fn *g_do_test; - -int osduld_register_test(unsigned ioctl, do_test_fn *do_test) -{ - if (g_test_ioctl) - return -EINVAL; - - g_test_ioctl = ioctl; - g_do_test = do_test; - return 0; -} -EXPORT_SYMBOL(osduld_register_test); - -void osduld_unregister_test(unsigned ioctl) -{ - if (ioctl == g_test_ioctl) { - g_test_ioctl = 0; - g_do_test = NULL; - } -} -EXPORT_SYMBOL(osduld_unregister_test); - -static do_test_fn *_find_ioctl(unsigned cmd) -{ - if (g_test_ioctl == cmd) - return g_do_test; - else - return NULL; -} - -static long osd_uld_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct osd_uld_device *oud = file->private_data; - int ret; - do_test_fn *do_test; - - do_test = _find_ioctl(cmd); - if (do_test) - ret = do_test(&oud->od, cmd, arg); - else { - OSD_ERR("Unknown ioctl %d: osd_uld_device=%p\n", cmd, oud); - ret = -ENOIOCTLCMD; - } - return ret; -} - -static const struct file_operations osd_fops = { - .owner = THIS_MODULE, - .open = osd_uld_open, - .release = osd_uld_release, - .unlocked_ioctl = osd_uld_ioctl, - .llseek = noop_llseek, -}; - -struct osd_dev *osduld_path_lookup(const char *name) -{ - struct osd_uld_device *oud; - struct osd_dev_handle *odh; - struct file *file; - int error; - - if (!name || !*name) { - OSD_ERR("Mount with !path || !*path\n"); - return ERR_PTR(-EINVAL); - } - - odh = kzalloc(sizeof(*odh), GFP_KERNEL); - if (unlikely(!odh)) - return ERR_PTR(-ENOMEM); - - file = filp_open(name, O_RDWR, 0); - if (IS_ERR(file)) { - error = PTR_ERR(file); - goto free_od; - } - - if (file->f_op != &osd_fops){ - error = -EINVAL; - goto close_file; - } - - oud = file->private_data; - - odh->od = oud->od; - odh->file = file; - odh->oud = oud; - - return &odh->od; - -close_file: - fput(file); -free_od: - kfree(odh); - return ERR_PTR(error); -} -EXPORT_SYMBOL(osduld_path_lookup); - -static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len, - const u8 *a2, unsigned a2_len) -{ - if (!a2_len) /* User string is Empty means don't care */ - return true; - - if (a1_len != a2_len) - return false; - - return 0 == memcmp(a1, a2, a1_len); -} - -static int _match_odi(struct device *dev, const void *find_data) -{ - struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, - class_dev); - const struct osd_dev_info *odi = find_data; - - if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, - odi->systemid, odi->systemid_len) && - _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, - odi->osdname, odi->osdname_len)) { - OSD_DEBUG("found device sysid_len=%d osdname=%d\n", - odi->systemid_len, odi->osdname_len); - return 1; - } else { - return 0; - } -} - -/* osduld_info_lookup - Loop through all devices, return the requested osd_dev. - * - * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't - * care. .e.g if they're both zero /dev/osd0 is returned. - */ -struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi) -{ - struct device *dev = class_find_device(&osd_uld_class, NULL, odi, _match_odi); - if (likely(dev)) { - struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL); - struct osd_uld_device *oud = container_of(dev, - struct osd_uld_device, class_dev); - - if (unlikely(!odh)) { - put_device(dev); - return ERR_PTR(-ENOMEM); - } - - odh->od = oud->od; - odh->oud = oud; - - return &odh->od; - } - - return ERR_PTR(-ENODEV); -} -EXPORT_SYMBOL(osduld_info_lookup); - -void osduld_put_device(struct osd_dev *od) -{ - if (od && !IS_ERR(od)) { - struct osd_dev_handle *odh = - container_of(od, struct osd_dev_handle, od); - struct osd_uld_device *oud = odh->oud; - - BUG_ON(od->scsi_device != oud->od.scsi_device); - - /* If scsi has released the device (logout), and exofs has last - * reference on oud it will be freed by above osd_uld_release - * within fput below. But this will oops in cdev_release which - * is called after the fops->release. A get_/put_ pair makes - * sure we have a cdev for the duration of fput - */ - if (odh->file) { - get_device(&oud->class_dev); - fput(odh->file); - } - put_device(&oud->class_dev); - kfree(odh); - } -} -EXPORT_SYMBOL(osduld_put_device); - -const struct osd_dev_info *osduld_device_info(struct osd_dev *od) -{ - struct osd_dev_handle *odh = - container_of(od, struct osd_dev_handle, od); - return &odh->oud->odi; -} -EXPORT_SYMBOL(osduld_device_info); - -bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi) -{ - struct osd_dev_handle *odh = - container_of(od, struct osd_dev_handle, od); - struct osd_uld_device *oud = odh->oud; - - return (oud->odi.systemid_len == odi->systemid_len) && - _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len, - odi->systemid, odi->systemid_len) && - (oud->odi.osdname_len == odi->osdname_len) && - _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len, - odi->osdname, odi->osdname_len); -} -EXPORT_SYMBOL(osduld_device_same); - -/* - * Scsi Device operations - */ - -static int __detect_osd(struct osd_uld_device *oud) -{ - struct scsi_device *scsi_device = oud->od.scsi_device; - struct scsi_sense_hdr sense_hdr; - char caps[OSD_CAP_LEN]; - int error; - - /* sending a test_unit_ready as first command seems to be needed - * by some targets - */ - OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n", - oud, scsi_device, scsi_device->request_queue); - error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, &sense_hdr); - if (error) - OSD_ERR("warning: scsi_test_unit_ready failed\n"); - - osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); - if (osd_auto_detect_ver(&oud->od, caps, &oud->odi)) - return -ENODEV; - - return 0; -} - -static void __remove(struct device *dev) -{ - struct osd_uld_device *oud = container_of(dev, struct osd_uld_device, - class_dev); - struct scsi_device *scsi_device = oud->od.scsi_device; - - kfree(oud->odi.osdname); - - osd_dev_fini(&oud->od); - scsi_device_put(scsi_device); - - OSD_INFO("osd_remove %s\n", - oud->disk ? oud->disk->disk_name : NULL); - - if (oud->disk) - put_disk(oud->disk); - - kfree(oud); -} - -static int osd_probe(struct device *dev) -{ - struct scsi_device *scsi_device = to_scsi_device(dev); - struct gendisk *disk; - struct osd_uld_device *oud; - int minor; - int error; - - if (scsi_device->type != TYPE_OSD) - return -ENODEV; - - minor = ida_alloc_max(&osd_minor_ida, SCSI_OSD_MAX_MINOR, GFP_KERNEL); - if (minor == -ENOSPC) - return -EBUSY; - if (minor < 0) - return -ENODEV; - - error = -ENOMEM; - oud = kzalloc(sizeof(*oud), GFP_KERNEL); - if (NULL == oud) - goto err_retract_minor; - - /* class device member */ - device_initialize(&oud->class_dev); - dev_set_drvdata(dev, oud); - oud->minor = minor; - oud->class_dev.devt = MKDEV(SCSI_OSD_MAJOR, oud->minor); - oud->class_dev.class = &osd_uld_class; - oud->class_dev.parent = dev; - oud->class_dev.release = __remove; - - /* hold one more reference to the scsi_device that will get released - * in __release, in case a logout is happening while fs is mounted - */ - if (scsi_device_get(scsi_device)) - goto err_retract_minor; - osd_dev_init(&oud->od, scsi_device); - - /* allocate a disk and set it up */ - /* FIXME: do we need this since sg has already done that */ - disk = alloc_disk(1); - if (!disk) { - OSD_ERR("alloc_disk failed\n"); - goto err_free_osd; - } - disk->major = SCSI_OSD_MAJOR; - disk->first_minor = oud->minor; - sprintf(disk->disk_name, "osd%d", oud->minor); - oud->disk = disk; - - /* Detect the OSD Version */ - error = __detect_osd(oud); - if (error) { - OSD_ERR("osd detection failed, non-compatible OSD device\n"); - goto err_free_osd; - } - - /* init the char-device for communication with user-mode */ - cdev_init(&oud->cdev, &osd_fops); - oud->cdev.owner = THIS_MODULE; - - error = dev_set_name(&oud->class_dev, "%s", disk->disk_name); - if (error) { - OSD_ERR("dev_set_name failed => %d\n", error); - goto err_free_osd; - } - - error = cdev_device_add(&oud->cdev, &oud->class_dev); - if (error) { - OSD_ERR("device_register failed => %d\n", error); - goto err_free_osd; - } - - OSD_INFO("osd_probe %s\n", disk->disk_name); - return 0; - -err_free_osd: - put_device(&oud->class_dev); -err_retract_minor: - ida_free(&osd_minor_ida, minor); - return error; -} - -static int osd_remove(struct device *dev) -{ - struct scsi_device *scsi_device = to_scsi_device(dev); - struct osd_uld_device *oud = dev_get_drvdata(dev); - - if (oud->od.scsi_device != scsi_device) { - OSD_ERR("Half cooked osd-device %p, || %p!=%p", - dev, oud->od.scsi_device, scsi_device); - } - - cdev_device_del(&oud->cdev, &oud->class_dev); - ida_free(&osd_minor_ida, oud->minor); - put_device(&oud->class_dev); - - return 0; -} - -/* - * Global driver and scsi registration - */ - -static struct scsi_driver osd_driver = { - .gendrv = { - .name = osd_name, - .owner = THIS_MODULE, - .probe = osd_probe, - .remove = osd_remove, - } -}; - -static int __init osd_uld_init(void) -{ - int err; - - err = class_register(&osd_uld_class); - if (err) { - OSD_ERR("Unable to register sysfs class => %d\n", err); - return err; - } - - err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), - SCSI_OSD_MAX_MINOR, osd_name); - if (err) { - OSD_ERR("Unable to register major %d for osd ULD => %d\n", - SCSI_OSD_MAJOR, err); - goto err_out; - } - - err = scsi_register_driver(&osd_driver.gendrv); - if (err) { - OSD_ERR("scsi_register_driver failed => %d\n", err); - goto err_out_chrdev; - } - - OSD_INFO("LOADED %s\n", osd_version_string); - return 0; - -err_out_chrdev: - unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); -err_out: - class_unregister(&osd_uld_class); - return err; -} - -static void __exit osd_uld_exit(void) -{ - scsi_unregister_driver(&osd_driver.gendrv); - unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); - class_unregister(&osd_uld_class); - OSD_INFO("UNLOADED %s\n", osd_version_string); -} - -module_init(osd_uld_init); -module_exit(osd_uld_exit); diff --git a/include/scsi/osd_initiator.h b/include/scsi/osd_initiator.h deleted file mode 100644 index 86a569d008b2..000000000000 --- a/include/scsi/osd_initiator.h +++ /dev/null @@ -1,511 +0,0 @@ -/* - * osd_initiator.h - OSD initiator API definition - * - * Copyright (C) 2008 Panasas Inc. All rights reserved. - * - * Authors: - * Boaz Harrosh - * Benny Halevy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * - */ -#ifndef __OSD_INITIATOR_H__ -#define __OSD_INITIATOR_H__ - -#include -#include - -#include -#include - -/* Note: "NI" in comments below means "Not Implemented yet" */ - -/* Configure of code: - * #undef if you *don't* want OSD v1 support in runtime. - * If #defined the initiator will dynamically configure to encode OSD v1 - * CDB's if the target is detected to be OSD v1 only. - * OSD v2 only commands, options, and attributes will be ignored if target - * is v1 only. - * If #defined will result in bigger/slower code (OK Slower maybe not) - * Q: Should this be CONFIG_SCSI_OSD_VER1_SUPPORT and set from Kconfig? - */ -#define OSD_VER1_SUPPORT y - -enum osd_std_version { - OSD_VER_NONE = 0, - OSD_VER1 = 1, - OSD_VER2 = 2, -}; - -/* - * Object-based Storage Device. - * This object represents an OSD device. - * It is not a full linux device in any way. It is only - * a place to hang resources associated with a Linux - * request Q and some default properties. - */ -struct osd_dev { - struct scsi_device *scsi_device; - unsigned def_timeout; - -#ifdef OSD_VER1_SUPPORT - enum osd_std_version version; -#endif -}; - -/* Unique Identification of an OSD device */ -struct osd_dev_info { - unsigned systemid_len; - u8 systemid[OSD_SYSTEMID_LEN]; - unsigned osdname_len; - u8 *osdname; -}; - -/* Retrieve/return osd_dev(s) for use by Kernel clients - * Use IS_ERR/ERR_PTR on returned "osd_dev *". - */ -struct osd_dev *osduld_path_lookup(const char *dev_name); -struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi); -void osduld_put_device(struct osd_dev *od); - -const struct osd_dev_info *osduld_device_info(struct osd_dev *od); -bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi); - -/* Add/remove test ioctls from external modules */ -typedef int (do_test_fn)(struct osd_dev *od, unsigned cmd, unsigned long arg); -int osduld_register_test(unsigned ioctl, do_test_fn *do_test); -void osduld_unregister_test(unsigned ioctl); - -/* These are called by uld at probe time */ -void osd_dev_init(struct osd_dev *od, struct scsi_device *scsi_device); -void osd_dev_fini(struct osd_dev *od); - -/** - * osd_auto_detect_ver - Detect the OSD version, return Unique Identification - * - * @od: OSD target lun handle - * @caps: Capabilities authorizing OSD root read attributes access - * @odi: Retrieved information uniquely identifying the osd target lun - * Note: odi->osdname must be kfreed by caller. - * - * Auto detects the OSD version of the OSD target and sets the @od - * accordingly. Meanwhile also returns the "system id" and "osd name" root - * attributes which uniquely identify the OSD target. This member is usually - * called by the ULD. ULD users should call osduld_device_info(). - * This rutine allocates osd requests and memory at GFP_KERNEL level and might - * sleep. - */ -int osd_auto_detect_ver(struct osd_dev *od, - void *caps, struct osd_dev_info *odi); - -static inline struct request_queue *osd_request_queue(struct osd_dev *od) -{ - return od->scsi_device->request_queue; -} - -/* we might want to use function vector in the future */ -static inline void osd_dev_set_ver(struct osd_dev *od, enum osd_std_version v) -{ -#ifdef OSD_VER1_SUPPORT - od->version = v; -#endif -} - -static inline bool osd_dev_is_ver1(struct osd_dev *od) -{ -#ifdef OSD_VER1_SUPPORT - return od->version == OSD_VER1; -#else - return false; -#endif -} - -struct osd_request; -typedef void (osd_req_done_fn)(struct osd_request *or, void *private); - -struct osd_request { - struct osd_cdb cdb; - struct osd_data_out_integrity_info out_data_integ; - struct osd_data_in_integrity_info in_data_integ; - - struct osd_dev *osd_dev; - struct request *request; - - struct _osd_req_data_segment { - void *buff; - unsigned alloc_size; /* 0 here means: don't call kfree */ - unsigned total_bytes; - } cdb_cont, set_attr, enc_get_attr, get_attr; - - struct _osd_io_info { - struct bio *bio; - u64 total_bytes; - u64 residual; - struct request *req; - struct _osd_req_data_segment *last_seg; - u8 *pad_buff; - } out, in; - - unsigned timeout; - unsigned retries; - unsigned sense_len; - u8 sense[OSD_MAX_SENSE_LEN]; - enum osd_attributes_mode attributes_mode; - - osd_req_done_fn *async_done; - void *async_private; - blk_status_t async_error; - int req_errors; -}; - -static inline bool osd_req_is_ver1(struct osd_request *or) -{ - return osd_dev_is_ver1(or->osd_dev); -} - -/* - * How to use the osd library: - * - * osd_start_request - * Allocates a request. - * - * osd_req_* - * Call one of, to encode the desired operation. - * - * osd_add_{get,set}_attr - * Optionally add attributes to the CDB, list or page mode. - * - * osd_finalize_request - * Computes final data out/in offsets and signs the request, - * making it ready for execution. - * - * osd_execute_request - * May be called to execute it through the block layer. Other wise submit - * the associated block request in some other way. - * - * After execution: - * osd_req_decode_sense - * Decodes sense information to verify execution results. - * - * osd_req_decode_get_attr - * Retrieve osd_add_get_attr_list() values if used. - * - * osd_end_request - * Must be called to deallocate the request. - */ - -/** - * osd_start_request - Allocate and initialize an osd_request - * - * @osd_dev: OSD device that holds the scsi-device and default values - * that the request is associated with. - * - * Allocate osd_request and initialize all members to the - * default/initial state. - */ -struct osd_request *osd_start_request(struct osd_dev *od); - -enum osd_req_options { - OSD_REQ_FUA = 0x08, /* Force Unit Access */ - OSD_REQ_DPO = 0x10, /* Disable Page Out */ - - OSD_REQ_BYPASS_TIMESTAMPS = 0x80, -}; - -/** - * osd_finalize_request - Sign request and prepare request for execution - * - * @or: osd_request to prepare - * @options: combination of osd_req_options bit flags or 0. - * @cap: A Pointer to an OSD_CAP_LEN bytes buffer that is received from - * The security manager as capabilities for this cdb. - * @cap_key: The cryptographic key used to sign the cdb/data. Can be null - * if NOSEC is used. - * - * The actual request and bios are only allocated here, so are the get_attr - * buffers that will receive the returned attributes. Copy's @cap to cdb. - * Sign the cdb/data with @cap_key. - */ -int osd_finalize_request(struct osd_request *or, - u8 options, const void *cap, const u8 *cap_key); - -/** - * osd_execute_request - Execute the request synchronously through block-layer - * - * @or: osd_request to Executed - * - * Calls blk_execute_rq to q the command and waits for completion. - */ -int osd_execute_request(struct osd_request *or); - -/** - * osd_execute_request_async - Execute the request without waitting. - * - * @or: - osd_request to Executed - * @done: (Optional) - Called at end of execution - * @private: - Will be passed to @done function - * - * Calls blk_execute_rq_nowait to queue the command. When execution is done - * optionally calls @done with @private as parameter. @or->async_error will - * have the return code - */ -int osd_execute_request_async(struct osd_request *or, - osd_req_done_fn *done, void *private); - -/** - * osd_req_decode_sense_full - Decode sense information after execution. - * - * @or: - osd_request to examine - * @osi - Receives a more detailed error report information (optional). - * @silent - Do not print to dmsg (Even if enabled) - * @bad_obj_list - Some commands act on multiple objects. Failed objects will - * be received here (optional) - * @max_obj - Size of @bad_obj_list. - * @bad_attr_list - List of failing attributes (optional) - * @max_attr - Size of @bad_attr_list. - * - * After execution, osd_request results are analyzed using this function. The - * return code is the final disposition on the error. So it is possible that a - * CHECK_CONDITION was returned from target but this will return NO_ERROR, for - * example on recovered errors. All parameters are optional if caller does - * not need any returned information. - * Note: This function will also dump the error to dmsg according to settings - * of the SCSI_OSD_DPRINT_SENSE Kconfig value. Set @silent if you know the - * command would routinely fail, to not spam the dmsg file. - */ - -/** - * osd_err_priority - osd categorized return codes in ascending severity. - * - * The categories are borrowed from the pnfs_osd_errno enum. - * See comments for translated Linux codes returned by osd_req_decode_sense. - */ -enum osd_err_priority { - OSD_ERR_PRI_NO_ERROR = 0, - /* Recoverable, caller should clear_highpage() all pages */ - OSD_ERR_PRI_CLEAR_PAGES = 1, /* -EFAULT */ - OSD_ERR_PRI_RESOURCE = 2, /* -ENOMEM */ - OSD_ERR_PRI_BAD_CRED = 3, /* -EINVAL */ - OSD_ERR_PRI_NO_ACCESS = 4, /* -EACCES */ - OSD_ERR_PRI_UNREACHABLE = 5, /* any other */ - OSD_ERR_PRI_NOT_FOUND = 6, /* -ENOENT */ - OSD_ERR_PRI_NO_SPACE = 7, /* -ENOSPC */ - OSD_ERR_PRI_EIO = 8, /* -EIO */ -}; - -struct osd_sense_info { - enum osd_err_priority osd_err_pri; - - int key; /* one of enum scsi_sense_keys */ - int additional_code ; /* enum osd_additional_sense_codes */ - union { /* Sense specific information */ - u16 sense_info; - u16 cdb_field_offset; /* scsi_invalid_field_in_cdb */ - }; - union { /* Command specific information */ - u64 command_info; - }; - - u32 not_initiated_command_functions; /* osd_command_functions_bits */ - u32 completed_command_functions; /* osd_command_functions_bits */ - struct osd_obj_id obj; - struct osd_attr attr; -}; - -int osd_req_decode_sense_full(struct osd_request *or, - struct osd_sense_info *osi, bool silent, - struct osd_obj_id *bad_obj_list, int max_obj, - struct osd_attr *bad_attr_list, int max_attr); - -static inline int osd_req_decode_sense(struct osd_request *or, - struct osd_sense_info *osi) -{ - return osd_req_decode_sense_full(or, osi, false, NULL, 0, NULL, 0); -} - -/** - * osd_end_request - return osd_request to free store - * - * @or: osd_request to free - * - * Deallocate all osd_request resources (struct req's, BIOs, buffers, etc.) - */ -void osd_end_request(struct osd_request *or); - -/* - * CDB Encoding - * - * Note: call only one of the following methods. - */ - -/* - * Device commands - */ -void osd_req_set_master_seed_xchg(struct osd_request *or, ...);/* NI */ -void osd_req_set_master_key(struct osd_request *or, ...);/* NI */ - -void osd_req_format(struct osd_request *or, u64 tot_capacity); - -/* list all partitions - * @list header must be initialized to zero on first run. - * - * Call osd_is_obj_list_done() to find if we got the complete list. - */ -int osd_req_list_dev_partitions(struct osd_request *or, - osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem); - -void osd_req_flush_obsd(struct osd_request *or, - enum osd_options_flush_scope_values); - -void osd_req_perform_scsi_command(struct osd_request *or, - const u8 *cdb, ...);/* NI */ -void osd_req_task_management(struct osd_request *or, ...);/* NI */ - -/* - * Partition commands - */ -void osd_req_create_partition(struct osd_request *or, osd_id partition); -void osd_req_remove_partition(struct osd_request *or, osd_id partition); - -void osd_req_set_partition_key(struct osd_request *or, - osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE], - u8 seed[OSD_CRYPTO_SEED_SIZE]);/* NI */ - -/* list all collections in the partition - * @list header must be init to zero on first run. - * - * Call osd_is_obj_list_done() to find if we got the complete list. - */ -int osd_req_list_partition_collections(struct osd_request *or, - osd_id partition, osd_id initial_id, struct osd_obj_id_list *list, - unsigned nelem); - -/* list all objects in the partition - * @list header must be init to zero on first run. - * - * Call osd_is_obj_list_done() to find if we got the complete list. - */ -int osd_req_list_partition_objects(struct osd_request *or, - osd_id partition, osd_id initial_id, struct osd_obj_id_list *list, - unsigned nelem); - -void osd_req_flush_partition(struct osd_request *or, - osd_id partition, enum osd_options_flush_scope_values); - -/* - * Collection commands - */ -void osd_req_create_collection(struct osd_request *or, - const struct osd_obj_id *);/* NI */ -void osd_req_remove_collection(struct osd_request *or, - const struct osd_obj_id *);/* NI */ - -/* list all objects in the collection */ -int osd_req_list_collection_objects(struct osd_request *or, - const struct osd_obj_id *, osd_id initial_id, - struct osd_obj_id_list *list, unsigned nelem); - -/* V2 only filtered list of objects in the collection */ -void osd_req_query(struct osd_request *or, ...);/* NI */ - -void osd_req_flush_collection(struct osd_request *or, - const struct osd_obj_id *, enum osd_options_flush_scope_values); - -void osd_req_get_member_attrs(struct osd_request *or, ...);/* V2-only NI */ -void osd_req_set_member_attrs(struct osd_request *or, ...);/* V2-only NI */ - -/* - * Object commands - */ -void osd_req_create_object(struct osd_request *or, struct osd_obj_id *); -void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *); - -void osd_req_write(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, struct bio *bio, u64 len); -int osd_req_write_kern(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, void *buff, u64 len); -void osd_req_append(struct osd_request *or, - const struct osd_obj_id *, struct bio *data_out);/* NI */ -void osd_req_create_write(struct osd_request *or, - const struct osd_obj_id *, struct bio *data_out, u64 offset);/* NI */ -void osd_req_clear(struct osd_request *or, - const struct osd_obj_id *, u64 offset, u64 len);/* NI */ -void osd_req_punch(struct osd_request *or, - const struct osd_obj_id *, u64 offset, u64 len);/* V2-only NI */ - -void osd_req_flush_object(struct osd_request *or, - const struct osd_obj_id *, enum osd_options_flush_scope_values, - /*V2*/ u64 offset, /*V2*/ u64 len); - -void osd_req_read(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, struct bio *bio, u64 len); -int osd_req_read_kern(struct osd_request *or, - const struct osd_obj_id *obj, u64 offset, void *buff, u64 len); - -/* Scatter/Gather write/read commands */ -int osd_req_write_sg(struct osd_request *or, - const struct osd_obj_id *obj, struct bio *bio, - const struct osd_sg_entry *sglist, unsigned numentries); -int osd_req_read_sg(struct osd_request *or, - const struct osd_obj_id *obj, struct bio *bio, - const struct osd_sg_entry *sglist, unsigned numentries); -int osd_req_write_sg_kern(struct osd_request *or, - const struct osd_obj_id *obj, void **buff, - const struct osd_sg_entry *sglist, unsigned numentries); -int osd_req_read_sg_kern(struct osd_request *or, - const struct osd_obj_id *obj, void **buff, - const struct osd_sg_entry *sglist, unsigned numentries); - -/* - * Root/Partition/Collection/Object Attributes commands - */ - -/* get before set */ -void osd_req_get_attributes(struct osd_request *or, const struct osd_obj_id *); - -/* set before get */ -void osd_req_set_attributes(struct osd_request *or, const struct osd_obj_id *); - -/* - * Attributes appended to most commands - */ - -/* Attributes List mode (or V2 CDB) */ - /* - * TODO: In ver2 if at finalize time only one attr was set and no gets, - * then the Attributes CDB mode is used automatically to save IO. - */ - -/* set a list of attributes. */ -int osd_req_add_set_attr_list(struct osd_request *or, - const struct osd_attr *, unsigned nelem); - -/* get a list of attributes */ -int osd_req_add_get_attr_list(struct osd_request *or, - const struct osd_attr *, unsigned nelem); - -/* - * Attributes list decoding - * Must be called after osd_request.request was executed - * It is called in a loop to decode the returned get_attr - * (see osd_add_get_attr) - */ -int osd_req_decode_get_attr_list(struct osd_request *or, - struct osd_attr *, int *nelem, void **iterator); - -/* Attributes Page mode */ - -/* - * Read an attribute page and optionally set one attribute - * - * Retrieves the attribute page directly to a user buffer. - * @attr_page_data shall stay valid until end of execution. - * See osd_attributes.h for common page structures - */ -int osd_req_add_get_attr_page(struct osd_request *or, - u32 page_id, void *attr_page_data, unsigned max_page_len, - const struct osd_attr *set_one); - -#endif /* __OSD_LIB_H__ */ diff --git a/include/scsi/osd_ore.h b/include/scsi/osd_ore.h deleted file mode 100644 index 7a8d2cd30328..000000000000 --- a/include/scsi/osd_ore.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2011 - * Boaz Harrosh - * - * Public Declarations of the ORE API - * - * This file is part of the ORE (Object Raid Engine) library. - * - * ORE is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. (GPL v2) - * - * ORE 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 the ORE; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __ORE_H__ -#define __ORE_H__ - -#include -#include -#include -#include -#include - -struct ore_comp { - struct osd_obj_id obj; - u8 cred[OSD_CAP_LEN]; -}; - -struct ore_layout { - /* Our way of looking at the data_map */ - enum pnfs_osd_raid_algorithm4 - raid_algorithm; - unsigned stripe_unit; - unsigned mirrors_p1; - - unsigned group_width; - unsigned parity; - u64 group_depth; - unsigned group_count; - - /* Cached often needed calculations filled in by - * ore_verify_layout - */ - unsigned long max_io_length; /* Max length that should be passed to - * ore_get_rw_state - */ -}; - -struct ore_dev { - struct osd_dev *od; -}; - -struct ore_components { - unsigned first_dev; /* First logical device no */ - unsigned numdevs; /* Num of devices in array */ - /* If @single_comp == EC_SINGLE_COMP, @comps points to a single - * component. else there are @numdevs components - */ - enum EC_COMP_USAGE { - EC_SINGLE_COMP = 0, EC_MULTPLE_COMPS = 0xffffffff - } single_comp; - struct ore_comp *comps; - - /* Array of pointers to ore_dev-* . User will usually have these pointed - * too a bigger struct which contain an "ore_dev ored" member and use - * container_of(oc->ods[i], struct foo_dev, ored) to access the bigger - * structure. - */ - struct ore_dev **ods; -}; - -/* ore_comp_dev Recievies a logical device index */ -static inline struct osd_dev *ore_comp_dev( - const struct ore_components *oc, unsigned i) -{ - BUG_ON((i < oc->first_dev) || (oc->first_dev + oc->numdevs <= i)); - return oc->ods[i - oc->first_dev]->od; -} - -static inline void ore_comp_set_dev( - struct ore_components *oc, unsigned i, struct osd_dev *od) -{ - oc->ods[i - oc->first_dev]->od = od; -} - -struct ore_striping_info { - u64 offset; - u64 obj_offset; - u64 length; - u64 first_stripe_start; /* only used in raid writes */ - u64 M; /* for truncate */ - unsigned bytes_in_stripe; - unsigned dev; - unsigned par_dev; - unsigned unit_off; - unsigned cur_pg; - unsigned cur_comp; - unsigned maxdevUnits; -}; - -struct ore_io_state; -typedef void (*ore_io_done_fn)(struct ore_io_state *ios, void *private); -struct _ore_r4w_op { - /* @Priv given here is passed ios->private */ - struct page * (*get_page)(void *priv, u64 page_index, bool *uptodate); - void (*put_page)(void *priv, struct page *page); -}; - -struct ore_io_state { - struct kref kref; - struct ore_striping_info si; - - void *private; - ore_io_done_fn done; - - struct ore_layout *layout; - struct ore_components *oc; - - /* Global read/write IO*/ - loff_t offset; - unsigned long length; - void *kern_buff; - - struct page **pages; - unsigned nr_pages; - unsigned pgbase; - unsigned pages_consumed; - - /* Attributes */ - unsigned in_attr_len; - struct osd_attr *in_attr; - unsigned out_attr_len; - struct osd_attr *out_attr; - - bool reading; - - /* House keeping of Parity pages */ - bool extra_part_alloc; - struct page **parity_pages; - unsigned max_par_pages; - unsigned cur_par_page; - unsigned sgs_per_dev; - struct __stripe_pages_2d *sp2d; - struct ore_io_state *ios_read_4_write; - const struct _ore_r4w_op *r4w; - - /* Variable array of size numdevs */ - unsigned numdevs; - struct ore_per_dev_state { - struct osd_request *or; - struct bio *bio; - loff_t offset; - unsigned length; - unsigned last_sgs_total; - unsigned dev; - struct osd_sg_entry *sglist; - unsigned cur_sg; - } per_dev[]; -}; - -static inline unsigned ore_io_state_size(unsigned numdevs) -{ - return sizeof(struct ore_io_state) + - sizeof(struct ore_per_dev_state) * numdevs; -} - -/* ore.c */ -int ore_verify_layout(unsigned total_comps, struct ore_layout *layout); -void ore_calc_stripe_info(struct ore_layout *layout, u64 file_offset, - u64 length, struct ore_striping_info *si); -int ore_get_rw_state(struct ore_layout *layout, struct ore_components *comps, - bool is_reading, u64 offset, u64 length, - struct ore_io_state **ios); -int ore_get_io_state(struct ore_layout *layout, struct ore_components *comps, - struct ore_io_state **ios); -void ore_put_io_state(struct ore_io_state *ios); - -typedef void (*ore_on_dev_error)(struct ore_io_state *ios, struct ore_dev *od, - unsigned dev_index, enum osd_err_priority oep, - u64 dev_offset, u64 dev_len); -int ore_check_io(struct ore_io_state *ios, ore_on_dev_error rep); - -int ore_create(struct ore_io_state *ios); -int ore_remove(struct ore_io_state *ios); -int ore_write(struct ore_io_state *ios); -int ore_read(struct ore_io_state *ios); -int ore_truncate(struct ore_layout *layout, struct ore_components *comps, - u64 size); - -int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr); - -extern const struct osd_attr g_attr_logical_length; - -#endif From ae3d56d81507c33024ba7c1eae2ef433aa9bc0d5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 29 Jan 2019 09:33:07 +0100 Subject: [PATCH 1185/4277] scsi: remove bidirectional command support No real need for bidi support once the OSD code is gone. Signed-off-by: Christoph Hellwig Reviewed-by: Jens Axboe Signed-off-by: Martin K. Petersen --- drivers/scsi/cxgbi/libcxgbi.c | 13 ++--- drivers/scsi/iscsi_tcp.c | 9 +--- drivers/scsi/libiscsi.c | 64 +++------------------- drivers/scsi/libiscsi_tcp.c | 8 +-- drivers/scsi/scsi_debug.c | 85 +++--------------------------- drivers/scsi/scsi_error.c | 3 -- drivers/scsi/scsi_lib.c | 60 ++------------------- drivers/scsi/virtio_scsi.c | 14 ++--- drivers/target/loopback/tcm_loop.c | 15 ------ drivers/usb/storage/uas.c | 11 +--- include/scsi/scsi_cmnd.h | 19 +------ include/scsi/scsi_eh.h | 1 - 12 files changed, 34 insertions(+), 268 deletions(-) diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index 75f876409fb9..4466ae5c9a74 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -1211,7 +1211,7 @@ scmd_get_params(struct scsi_cmnd *sc, struct scatterlist **sgl, unsigned int *sgcnt, unsigned int *dlen, unsigned int prot) { - struct scsi_data_buffer *sdb = prot ? scsi_prot(sc) : scsi_out(sc); + struct scsi_data_buffer *sdb = prot ? scsi_prot(sc) : &sc->sdb; *sgl = sdb->table.sgl; *sgcnt = sdb->table.nents; @@ -1427,8 +1427,7 @@ static void task_release_itt(struct iscsi_task *task, itt_t hdr_itt) log_debug(1 << CXGBI_DBG_DDP, "cdev 0x%p, task 0x%p, release tag 0x%x.\n", cdev, task, tag); - if (sc && - (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) && + if (sc && sc->sc_data_direction == DMA_FROM_DEVICE && cxgbi_ppm_is_ddp_tag(ppm, tag)) { struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task); struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo; @@ -1460,9 +1459,7 @@ static int task_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt) u32 tag = 0; int err = -EINVAL; - if (sc && - (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) - ) { + if (sc && sc->sc_data_direction == DMA_FROM_DEVICE) { struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task); struct cxgbi_task_tag_info *ttinfo = &tdata->ttinfo; @@ -1896,7 +1893,7 @@ int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode) if (SKB_MAX_HEAD(cdev->skb_tx_rsvd) > (512 * MAX_SKB_FRAGS) && (opcode == ISCSI_OP_SCSI_DATA_OUT || (opcode == ISCSI_OP_SCSI_CMD && - (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_TO_DEVICE)))) + sc->sc_data_direction == DMA_TO_DEVICE))) /* data could goes into skb head */ headroom += min_t(unsigned int, SKB_MAX_HEAD(cdev->skb_tx_rsvd), @@ -1971,7 +1968,7 @@ int cxgbi_conn_init_pdu(struct iscsi_task *task, unsigned int offset, return 0; if (task->sc) { - struct scsi_data_buffer *sdb = scsi_out(task->sc); + struct scsi_data_buffer *sdb = &task->sc->sdb; struct scatterlist *sg = NULL; int err; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index cae6368ebb98..ed3debce2819 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -518,7 +518,7 @@ static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task, if (!task->sc) iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count); else { - struct scsi_data_buffer *sdb = scsi_out(task->sc); + struct scsi_data_buffer *sdb = &task->sc->sdb; err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl, sdb->table.nents, offset, @@ -952,12 +952,6 @@ static umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param) return 0; } -static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev) -{ - blk_queue_flag_set(QUEUE_FLAG_BIDI, sdev->request_queue); - return 0; -} - static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev) { struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(sdev->host); @@ -985,7 +979,6 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .eh_device_reset_handler= iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, .dma_boundary = PAGE_SIZE - 1, - .slave_alloc = iscsi_sw_tcp_slave_alloc, .slave_configure = iscsi_sw_tcp_slave_configure, .target_alloc = iscsi_target_alloc, .proc_name = "iscsi_tcp", diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index b8d325ce8754..bca3a8636c27 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -228,32 +228,6 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task) return 0; } -static int iscsi_prep_bidi_ahs(struct iscsi_task *task) -{ - struct scsi_cmnd *sc = task->sc; - struct iscsi_rlength_ahdr *rlen_ahdr; - int rc; - - rlen_ahdr = iscsi_next_hdr(task); - rc = iscsi_add_hdr(task, sizeof(*rlen_ahdr)); - if (rc) - return rc; - - rlen_ahdr->ahslength = - cpu_to_be16(sizeof(rlen_ahdr->read_length) + - sizeof(rlen_ahdr->reserved)); - rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH; - rlen_ahdr->reserved = 0; - rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length); - - ISCSI_DBG_SESSION(task->conn->session, - "bidi-in rlen_ahdr->read_length(%d) " - "rlen_ahdr->ahslength(%d)\n", - be32_to_cpu(rlen_ahdr->read_length), - be16_to_cpu(rlen_ahdr->ahslength)); - return 0; -} - /** * iscsi_check_tmf_restrictions - check if a task is affected by TMF * @task: iscsi task @@ -392,13 +366,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) memcpy(hdr->cdb, sc->cmnd, cmd_len); task->imm_count = 0; - if (scsi_bidi_cmnd(sc)) { - hdr->flags |= ISCSI_FLAG_CMD_READ; - rc = iscsi_prep_bidi_ahs(task); - if (rc) - return rc; - } - if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) task->protected = true; @@ -473,12 +440,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) conn->scsicmd_pdus_cnt++; ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " - "itt 0x%x len %d bidi_len %d cmdsn %d win %d]\n", - scsi_bidi_cmnd(sc) ? "bidirectional" : + "itt 0x%x len %d cmdsn %d win %d]\n", sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", conn->id, sc, sc->cmnd[0], task->itt, transfer_length, - scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0, session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); return 0; @@ -647,12 +612,7 @@ static void fail_scsi_task(struct iscsi_task *task, int err) state = ISCSI_TASK_ABRT_TMF; sc->result = err << 16; - if (!scsi_bidi_cmnd(sc)) - scsi_set_resid(sc, scsi_bufflen(sc)); - else { - scsi_out(sc)->resid = scsi_out(sc)->length; - scsi_in(sc)->resid = scsi_in(sc)->length; - } + scsi_set_resid(sc, scsi_bufflen(sc)); /* regular RX path uses back_lock */ spin_lock_bh(&conn->session->back_lock); @@ -907,14 +867,7 @@ invalid_datalen: if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | ISCSI_FLAG_CMD_BIDI_OVERFLOW)) { - int res_count = be32_to_cpu(rhdr->bi_residual_count); - - if (scsi_bidi_cmnd(sc) && res_count > 0 && - (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW || - res_count <= scsi_in(sc)->length)) - scsi_in(sc)->resid = res_count; - else - sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; + sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; } if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | @@ -961,8 +914,8 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (res_count > 0 && (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || - res_count <= scsi_in(sc)->length)) - scsi_in(sc)->resid = res_count; + res_count <= sc->sdb.length)) + sc->sdb.resid = res_count; else sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; } @@ -1804,12 +1757,7 @@ fault: spin_unlock_bh(&session->frwd_lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); - if (!scsi_bidi_cmnd(sc)) - scsi_set_resid(sc, scsi_bufflen(sc)); - else { - scsi_out(sc)->resid = scsi_out(sc)->length; - scsi_in(sc)->resid = scsi_in(sc)->length; - } + scsi_set_resid(sc, scsi_bufflen(sc)); sc->scsi_done(sc); return 0; } diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 8a6b1b3f8277..9923e9e3b884 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -495,7 +495,7 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; int datasn = be32_to_cpu(rhdr->datasn); - unsigned total_in_length = scsi_in(task->sc)->length; + unsigned total_in_length = task->sc->sdb.length; /* * lib iscsi will update this in the completion handling if there @@ -580,11 +580,11 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) data_length, session->max_burst); data_offset = be32_to_cpu(rhdr->data_offset); - if (data_offset + data_length > scsi_out(task->sc)->length) { + if (data_offset + data_length > task->sc->sdb.length) { iscsi_conn_printk(KERN_ERR, conn, "invalid R2T with data len %u at offset %u " "and total length %d\n", data_length, - data_offset, scsi_out(task->sc)->length); + data_offset, task->sc->sdb.length); return ISCSI_ERR_DATALEN; } @@ -696,7 +696,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) if (tcp_conn->in.datalen) { struct iscsi_tcp_task *tcp_task = task->dd_data; struct ahash_request *rx_hash = NULL; - struct scsi_data_buffer *sdb = scsi_in(task->sc); + struct scsi_data_buffer *sdb = &task->sc->sdb; /* * Setup copy of Data-In into the struct scsi_cmnd diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 661512bec3ac..8044bb08455d 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -430,7 +430,6 @@ static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); -static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *); @@ -600,9 +599,6 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {0, 0x42, 0, F_D_OUT | FF_MEDIA_IO, resp_unmap, NULL, /* UNMAP */ {10, 0x1, 0, 0, 0, 0, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, /* 25 */ - {0, 0x53, 0, F_D_IN | F_D_OUT | FF_MEDIA_IO, resp_xdwriteread_10, - NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, - 0, 0, 0, 0, 0, 0} }, /* XDWRITEREAD(10) */ {0, 0x3b, 0, F_D_OUT_MAYBE, resp_write_buffer, NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, /* WRITE_BUFFER */ @@ -1010,11 +1006,11 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int act_len; - struct scsi_data_buffer *sdb = scsi_in(scp); + struct scsi_data_buffer *sdb = &scp->sdb; if (!sdb->length) return 0; - if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) + if (scp->sc_data_direction != DMA_FROM_DEVICE) return DID_ERROR << 16; act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, @@ -1033,12 +1029,12 @@ static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr, int arr_len, unsigned int off_dst) { int act_len, n; - struct scsi_data_buffer *sdb = scsi_in(scp); + struct scsi_data_buffer *sdb = &scp->sdb; off_t skip = off_dst; if (sdb->length <= off_dst) return 0; - if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) + if (scp->sc_data_direction != DMA_FROM_DEVICE) return DID_ERROR << 16; act_len = sg_pcopy_from_buffer(sdb->table.sgl, sdb->table.nents, @@ -1058,7 +1054,7 @@ static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, { if (!scsi_bufflen(scp)) return 0; - if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE)) + if (scp->sc_data_direction != DMA_TO_DEVICE) return -1; return scsi_sg_copy_to_buffer(scp, arr, arr_len); @@ -2477,21 +2473,19 @@ static int do_device_access(struct scsi_cmnd *scmd, u32 sg_skip, u64 lba, { int ret; u64 block, rest = 0; - struct scsi_data_buffer *sdb; + struct scsi_data_buffer *sdb = &scmd->sdb; enum dma_data_direction dir; if (do_write) { - sdb = scsi_out(scmd); dir = DMA_TO_DEVICE; write_since_sync = true; } else { - sdb = scsi_in(scmd); dir = DMA_FROM_DEVICE; } if (!sdb->length) return 0; - if (!(scsi_bidi_cmnd(scmd) || scmd->sc_data_direction == dir)) + if (scmd->sc_data_direction != dir) return -1; block = do_div(lba, sdebug_store_sectors); @@ -2774,7 +2768,7 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) if (unlikely(ret == -1)) return DID_ERROR << 16; - scsi_in(scp)->resid = scsi_bufflen(scp) - ret; + scp->sdb.resid = scsi_bufflen(scp) - ret; if (unlikely(sqcp)) { if (sqcp->inj_recovered) { @@ -3718,68 +3712,6 @@ static int resp_report_luns(struct scsi_cmnd *scp, return res; } -static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba, - unsigned int num, struct sdebug_dev_info *devip) -{ - int j; - unsigned char *kaddr, *buf; - unsigned int offset; - struct scsi_data_buffer *sdb = scsi_in(scp); - struct sg_mapping_iter miter; - - /* better not to use temporary buffer. */ - buf = kzalloc(scsi_bufflen(scp), GFP_ATOMIC); - if (!buf) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, - INSUFF_RES_ASCQ); - return check_condition_result; - } - - scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp)); - - offset = 0; - sg_miter_start(&miter, sdb->table.sgl, sdb->table.nents, - SG_MITER_ATOMIC | SG_MITER_TO_SG); - - while (sg_miter_next(&miter)) { - kaddr = miter.addr; - for (j = 0; j < miter.length; j++) - *(kaddr + j) ^= *(buf + offset + j); - - offset += miter.length; - } - sg_miter_stop(&miter); - kfree(buf); - - return 0; -} - -static int resp_xdwriteread_10(struct scsi_cmnd *scp, - struct sdebug_dev_info *devip) -{ - u8 *cmd = scp->cmnd; - u64 lba; - u32 num; - int errsts; - - if (!scsi_bidi_cmnd(scp)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, - INSUFF_RES_ASCQ); - return check_condition_result; - } - errsts = resp_read_dt0(scp, devip); - if (errsts) - return errsts; - if (!(cmd[1] & 0x4)) { /* DISABLE_WRITE is not set */ - errsts = resp_write_dt0(scp, devip); - if (errsts) - return errsts; - } - lba = get_unaligned_be32(cmd + 2); - num = get_unaligned_be16(cmd + 7); - return resp_xdwriteread(scp, lba, num, devip); -} - static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd) { u32 tag = blk_mq_unique_tag(cmnd->request); @@ -3953,7 +3885,6 @@ static int scsi_debug_slave_alloc(struct scsi_device *sdp) if (sdebug_verbose) pr_info("slave_alloc <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); - blk_queue_flag_set(QUEUE_FLAG_BIDI, sdp->request_queue); return 0; } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 16eef068e9e9..1b8378f36139 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -965,7 +965,6 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses, ses->cmnd = scmd->cmnd; ses->data_direction = scmd->sc_data_direction; ses->sdb = scmd->sdb; - ses->next_rq = scmd->request->next_rq; ses->result = scmd->result; ses->underflow = scmd->underflow; ses->prot_op = scmd->prot_op; @@ -976,7 +975,6 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses, scmd->cmnd = ses->eh_cmnd; memset(scmd->cmnd, 0, BLK_MAX_CDB); memset(&scmd->sdb, 0, sizeof(scmd->sdb)); - scmd->request->next_rq = NULL; scmd->result = 0; if (sense_bytes) { @@ -1029,7 +1027,6 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses) scmd->cmnd = ses->cmnd; scmd->sc_data_direction = ses->data_direction; scmd->sdb = ses->sdb; - scmd->request->next_rq = ses->next_rq; scmd->result = ses->result; scmd->underflow = ses->underflow; scmd->prot_op = ses->prot_op; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 00cd365fb7d2..2667bb95a441 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -556,15 +556,8 @@ static void scsi_uninit_cmd(struct scsi_cmnd *cmd) static void scsi_mq_free_sgtables(struct scsi_cmnd *cmd) { - struct scsi_data_buffer *sdb; - if (cmd->sdb.table.nents) sg_free_table_chained(&cmd->sdb.table, true); - if (cmd->request->next_rq) { - sdb = cmd->request->next_rq->special; - if (sdb) - sg_free_table_chained(&sdb->table, true); - } if (scsi_prot_sg_count(cmd)) sg_free_table_chained(&cmd->prot_sdb->table, true); } @@ -578,7 +571,7 @@ static void scsi_mq_uninit_cmd(struct scsi_cmnd *cmd) /* Returns false when no more bytes to process, true if there are more */ static bool scsi_end_request(struct request *req, blk_status_t error, - unsigned int bytes, unsigned int bidi_bytes) + unsigned int bytes) { struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req); struct scsi_device *sdev = cmd->device; @@ -587,11 +580,6 @@ static bool scsi_end_request(struct request *req, blk_status_t error, if (blk_update_request(req, error, bytes)) return true; - /* Bidi request must be completed as a whole */ - if (unlikely(bidi_bytes) && - blk_update_request(req->next_rq, error, bidi_bytes)) - return true; - if (blk_queue_add_random(q)) add_disk_randomness(req->rq_disk); @@ -816,7 +804,7 @@ static void scsi_io_completion_action(struct scsi_cmnd *cmd, int result) scsi_print_command(cmd); } } - if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req), 0)) + if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req))) return; /*FALLTHRU*/ case ACTION_REPREP: @@ -951,29 +939,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) */ scsi_req(req)->result = cmd->result; scsi_req(req)->resid_len = scsi_get_resid(cmd); - - if (unlikely(scsi_bidi_cmnd(cmd))) { - /* - * Bidi commands Must be complete as a whole, - * both sides at once. - */ - scsi_req(req->next_rq)->resid_len = scsi_in(cmd)->resid; - if (scsi_end_request(req, BLK_STS_OK, blk_rq_bytes(req), - blk_rq_bytes(req->next_rq))) - WARN_ONCE(true, - "Bidi command with remaining bytes"); - return; - } - } - - /* no bidi support yet, other than in pass-through */ - if (unlikely(blk_bidi_rq(req))) { - WARN_ONCE(true, "Only support bidi command in passthrough"); - scmd_printk(KERN_ERR, cmd, "Killing bidi command\n"); - if (scsi_end_request(req, BLK_STS_IOERR, blk_rq_bytes(req), - blk_rq_bytes(req->next_rq))) - WARN_ONCE(true, "Bidi command with remaining bytes"); - return; } /* @@ -990,13 +955,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) * to retry code. Fast path should return in this block. */ if (likely(blk_rq_bytes(req) > 0 || blk_stat == BLK_STS_OK)) { - if (likely(!scsi_end_request(req, blk_stat, good_bytes, 0))) + if (likely(!scsi_end_request(req, blk_stat, good_bytes))) return; /* no bytes remaining */ } /* Kill remainder if no retries. */ if (unlikely(blk_stat && scsi_noretry_cmd(cmd))) { - if (scsi_end_request(req, blk_stat, blk_rq_bytes(req), 0)) + if (scsi_end_request(req, blk_stat, blk_rq_bytes(req))) WARN_ONCE(true, "Bytes remaining after failed, no-retry command"); return; @@ -1058,12 +1023,6 @@ blk_status_t scsi_init_io(struct scsi_cmnd *cmd) if (ret) return ret; - if (blk_bidi_rq(rq)) { - ret = scsi_init_sgtable(rq->next_rq, rq->next_rq->special); - if (ret) - goto out_free_sgtables; - } - if (blk_integrity_rq(rq)) { struct scsi_data_buffer *prot_sdb = cmd->prot_sdb; int ivecs, count; @@ -1624,17 +1583,6 @@ static blk_status_t scsi_mq_prep_fn(struct request *req) (struct scatterlist *)(cmd->prot_sdb + 1); } - if (blk_bidi_rq(req)) { - struct request *next_rq = req->next_rq; - struct scsi_data_buffer *bidi_sdb = blk_mq_rq_to_pdu(next_rq); - - memset(bidi_sdb, 0, sizeof(struct scsi_data_buffer)); - bidi_sdb->table.sgl = - (struct scatterlist *)(bidi_sdb + 1); - - next_rq->special = bidi_sdb; - } - blk_mq_start_request(req); return scsi_setup_cmnd(sdev, req); diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 772b976e4ee4..1a6f150cd2d8 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -100,16 +100,8 @@ static inline struct Scsi_Host *virtio_scsi_host(struct virtio_device *vdev) static void virtscsi_compute_resid(struct scsi_cmnd *sc, u32 resid) { - if (!resid) - return; - - if (!scsi_bidi_cmnd(sc)) { + if (resid) scsi_set_resid(sc, resid); - return; - } - - scsi_in(sc)->resid = min(resid, scsi_in(sc)->length); - scsi_out(sc)->resid = resid - scsi_in(sc)->resid; } /** @@ -403,9 +395,9 @@ static int virtscsi_add_cmd(struct virtqueue *vq, if (sc && sc->sc_data_direction != DMA_NONE) { if (sc->sc_data_direction != DMA_FROM_DEVICE) - out = &scsi_out(sc)->table; + out = &sc->sdb.table; if (sc->sc_data_direction != DMA_TO_DEVICE) - in = &scsi_in(sc)->table; + in = &sc->sdb.table; } /* Request header. */ diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 89c6e3719d2b..3305b47fdf53 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -128,14 +128,6 @@ static void tcm_loop_submission_work(struct work_struct *work) set_host_byte(sc, DID_ERROR); goto out_done; } - if (scsi_bidi_cmnd(sc)) { - struct scsi_data_buffer *sdb = scsi_in(sc); - - sgl_bidi = sdb->table.sgl; - sgl_bidi_count = sdb->table.nents; - se_cmd->se_cmd_flags |= SCF_BIDI; - - } transfer_length = scsi_transfer_length(sc); if (!scsi_prot_sg_count(sc) && @@ -304,12 +296,6 @@ static int tcm_loop_target_reset(struct scsi_cmnd *sc) return FAILED; } -static int tcm_loop_slave_alloc(struct scsi_device *sd) -{ - blk_queue_flag_set(QUEUE_FLAG_BIDI, sd->request_queue); - return 0; -} - static struct scsi_host_template tcm_loop_driver_template = { .show_info = tcm_loop_show_info, .proc_name = "tcm_loopback", @@ -325,7 +311,6 @@ static struct scsi_host_template tcm_loop_driver_template = { .cmd_per_lun = 1024, .max_sectors = 0xFFFF, .dma_boundary = PAGE_SIZE - 1, - .slave_alloc = tcm_loop_slave_alloc, .module = THIS_MODULE, .track_queue_depth = 1, }; diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 36742e8e7edc..95f2142093d5 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -368,25 +368,19 @@ static void uas_data_cmplt(struct urb *urb) struct scsi_cmnd *cmnd = urb->context; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - struct scsi_data_buffer *sdb = NULL; + struct scsi_data_buffer *sdb = &cmnd->sdb; unsigned long flags; int status = urb->status; spin_lock_irqsave(&devinfo->lock, flags); if (cmdinfo->data_in_urb == urb) { - sdb = scsi_in(cmnd); cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; cmdinfo->data_in_urb = NULL; } else if (cmdinfo->data_out_urb == urb) { - sdb = scsi_out(cmnd); cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; cmdinfo->data_out_urb = NULL; } - if (sdb == NULL) { - WARN_ON_ONCE(1); - goto out; - } if (devinfo->resetting) goto out; @@ -426,8 +420,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, struct usb_device *udev = devinfo->udev; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct urb *urb = usb_alloc_urb(0, gfp); - struct scsi_data_buffer *sdb = (dir == DMA_FROM_DEVICE) - ? scsi_in(cmnd) : scsi_out(cmnd); + struct scsi_data_buffer *sdb = &cmnd->sdb; unsigned int pipe = (dir == DMA_FROM_DEVICE) ? devinfo->data_in_pipe : devinfo->data_out_pipe; diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index d85e6befa26b..af6ae3e1eba6 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -213,23 +213,6 @@ static inline int scsi_get_resid(struct scsi_cmnd *cmd) #define scsi_for_each_sg(cmd, sg, nseg, __i) \ for_each_sg(scsi_sglist(cmd), sg, nseg, __i) -static inline int scsi_bidi_cmnd(struct scsi_cmnd *cmd) -{ - return blk_bidi_rq(cmd->request) && - (cmd->request->next_rq->special != NULL); -} - -static inline struct scsi_data_buffer *scsi_in(struct scsi_cmnd *cmd) -{ - return scsi_bidi_cmnd(cmd) ? - cmd->request->next_rq->special : &cmd->sdb; -} - -static inline struct scsi_data_buffer *scsi_out(struct scsi_cmnd *cmd) -{ - return &cmd->sdb; -} - static inline int scsi_sg_copy_from_buffer(struct scsi_cmnd *cmd, void *buf, int buflen) { @@ -351,7 +334,7 @@ static inline void set_driver_byte(struct scsi_cmnd *cmd, char status) static inline unsigned scsi_transfer_length(struct scsi_cmnd *scmd) { - unsigned int xfer_len = scsi_out(scmd)->length; + unsigned int xfer_len = scmd->sdb.length; unsigned int prot_interval = scsi_prot_interval(scmd); if (scmd->prot_flags & SCSI_PROT_TRANSFER_PI) diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h index 2b7e227960e1..3810b340551c 100644 --- a/include/scsi/scsi_eh.h +++ b/include/scsi/scsi_eh.h @@ -39,7 +39,6 @@ struct scsi_eh_save { unsigned char prot_op; unsigned char *cmnd; struct scsi_data_buffer sdb; - struct request *next_rq; /* new command support */ unsigned char eh_cmnd[BLK_MAX_CDB]; struct scatterlist sense_sgl; From b9f91992991b27e72d93113095b9b92919ed0b71 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 8 Nov 2018 09:32:43 +0100 Subject: [PATCH 1186/4277] scsi: stop setting up request->special No more need in a blk-mq world where the scsi command and request are allocated together. Signed-off-by: Christoph Hellwig Reviewed-by: Jens Axboe Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 6 ------ drivers/scsi/qedi/qedi_fw.c | 7 ------- drivers/scsi/scsi_lib.c | 3 --- drivers/scsi/sr.c | 1 - 4 files changed, 17 deletions(-) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 6bbc38b1b465..6ca583bdde23 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1128,12 +1128,6 @@ void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, return; } - if (!sc_cmd->request->special) { - QEDF_WARN(&(qedf->dbg_ctx), "request->special is NULL so " - "request not valid, sc_cmd=%p.\n", sc_cmd); - return; - } - if (!sc_cmd->request->q) { QEDF_WARN(&(qedf->dbg_ctx), "request->q is NULL so request " "is not valid, sc_cmd=%p.\n", sc_cmd); diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c index 25d763ae5d5a..e2a995a6e8e7 100644 --- a/drivers/scsi/qedi/qedi_fw.c +++ b/drivers/scsi/qedi/qedi_fw.c @@ -616,13 +616,6 @@ static void qedi_scsi_completion(struct qedi_ctx *qedi, goto error; } - if (!sc_cmd->request->special) { - QEDI_WARN(&qedi->dbg_ctx, - "request->special is NULL so request not valid, sc_cmd=%p.\n", - sc_cmd); - goto error; - } - if (!sc_cmd->request->q) { QEDI_WARN(&qedi->dbg_ctx, "request->q is NULL so request is not valid, sc_cmd=%p.\n", diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 2667bb95a441..d25101c5e3b8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1566,10 +1566,7 @@ static blk_status_t scsi_mq_prep_fn(struct request *req) scsi_init_command(sdev, cmd); - req->special = cmd; - cmd->request = req; - cmd->tag = req->tag; cmd->prot_op = SCSI_PROT_NORMAL; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 38ddbbfe5f3c..039c27c2d7b3 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -394,7 +394,6 @@ static blk_status_t sr_init_command(struct scsi_cmnd *SCpnt) ret = scsi_init_io(SCpnt); if (ret != BLK_STS_OK) goto out; - WARN_ON_ONCE(SCpnt != rq->special); cd = scsi_cd(rq->rq_disk); /* from here on until we're complete, any goto out From 69ed175c195595c73901e18366cb0ebeaeb68b8a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 9 Nov 2018 19:35:11 +0100 Subject: [PATCH 1187/4277] scsi: block: remove req->special No users left. Signed-off-by: Christoph Hellwig Acked-by: Jens Axboe Signed-off-by: Martin K. Petersen --- block/blk-mq.c | 1 - drivers/scsi/sd.c | 2 -- include/linux/blkdev.h | 2 -- 3 files changed, 5 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index 3ba37b9e15e9..502cbf964a3b 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -331,7 +331,6 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data, #if defined(CONFIG_BLK_DEV_INTEGRITY) rq->nr_integrity_segments = 0; #endif - rq->special = NULL; /* tag was already set */ rq->extra_len = 0; WRITE_ONCE(rq->deadline, 0); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3db9b1fe7516..c124459041dc 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1171,8 +1171,6 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd) if (ret != BLK_STS_OK) return ret; - WARN_ON_ONCE(cmd != rq->special); - if (!scsi_device_online(sdp) || sdp->changed) { scmd_printk(KERN_ERR, cmd, "device offline or changed\n"); return BLK_STS_IOERR; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 338604dff7d0..fd1450d53f1c 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -216,8 +216,6 @@ struct request { unsigned short write_hint; unsigned short ioprio; - void *special; /* opaque pointer available for LLD use */ - unsigned int extra_len; /* length of alignment and padding */ enum mq_rq_state state; From 8b3238cabd50e2715b6544e724e74685209b190a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 6 Dec 2018 08:01:10 -0800 Subject: [PATCH 1188/4277] scsi: block: remove bidi support Unused now, and another field in struct request bites the dust. Signed-off-by: Christoph Hellwig Acked-by: Jens Axboe Signed-off-by: Martin K. Petersen --- block/blk-mq-debugfs.c | 1 - block/blk-mq.c | 3 --- include/linux/blkdev.h | 6 ------ 3 files changed, 10 deletions(-) diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 90d68760af08..ac832547160a 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -115,7 +115,6 @@ static int queue_pm_only_show(void *data, struct seq_file *m) static const char *const blk_queue_flag_name[] = { QUEUE_FLAG_NAME(STOPPED), QUEUE_FLAG_NAME(DYING), - QUEUE_FLAG_NAME(BIDI), QUEUE_FLAG_NAME(NOMERGES), QUEUE_FLAG_NAME(SAME_COMP), QUEUE_FLAG_NAME(FAIL_IO), diff --git a/block/blk-mq.c b/block/blk-mq.c index 502cbf964a3b..820d131a6893 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -339,7 +339,6 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data, rq->end_io = NULL; rq->end_io_data = NULL; - rq->next_rq = NULL; data->ctx->rq_dispatched[op_is_sync(op)]++; refcount_set(&rq->ref, 1); @@ -549,8 +548,6 @@ inline void __blk_mq_end_request(struct request *rq, blk_status_t error) rq_qos_done(rq->q, rq); rq->end_io(rq, error); } else { - if (unlikely(blk_bidi_rq(rq))) - blk_mq_free_request(rq->next_rq); blk_mq_free_request(rq); } } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index fd1450d53f1c..21beb456b97a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -234,9 +234,6 @@ struct request { */ rq_end_io_fn *end_io; void *end_io_data; - - /* for bidi */ - struct request *next_rq; }; static inline bool blk_op_is_scsi(unsigned int op) @@ -572,7 +569,6 @@ struct request_queue { #define QUEUE_FLAG_STOPPED 1 /* queue is stopped */ #define QUEUE_FLAG_DYING 2 /* queue being torn down */ -#define QUEUE_FLAG_BIDI 4 /* queue supports bidi requests */ #define QUEUE_FLAG_NOMERGES 5 /* disable merge attempts */ #define QUEUE_FLAG_SAME_COMP 6 /* complete on same CPU-group */ #define QUEUE_FLAG_FAIL_IO 7 /* fake timeout */ @@ -644,8 +640,6 @@ static inline bool blk_account_rq(struct request *rq) return (rq->rq_flags & RQF_STARTED) && !blk_rq_is_passthrough(rq); } -#define blk_bidi_rq(rq) ((rq)->next_rq != NULL) - #define list_entry_rq(ptr) list_entry((ptr), struct request, queuelist) #define rq_data_dir(rq) (op_is_write(req_op(rq)) ? WRITE : READ) From 7f147f9bfd44d048e22e8c65877d2b5590e6cf3d Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Thu, 24 Jan 2019 23:23:39 -0800 Subject: [PATCH 1189/4277] scsi: qla2xxx: Fix N2N target discovery with Local loop This patch fixes the issue where Dell-EMC Target will fail to discover LUNs if domain and area of port ID is not same as adapter's. Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 364bb52ed2a6..58b4bfe01202 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -5046,11 +5046,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) if ((domain & 0xf0) == 0xf0) continue; - /* Bypass if not same domain and area of adapter. */ - if (area && domain && - (area != vha->d_id.b.area || domain != vha->d_id.b.domain)) - continue; - /* Bypass invalid local loop ID. */ if (loop_id > LAST_LOCAL_LOOP_ID) continue; From 590f806ddd2e4d44dca9d73cbe08d2088a453a71 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:40 -0800 Subject: [PATCH 1190/4277] scsi: qla2xxx: Add pci function reset support. This patch provide call back functions to stop the chip and resume the chip if the PCI lower level driver wants to perform function level reset/FLR. Before the FLR, the chip will be stopped to turn off all DMA activity. After the FLR, the chip is reset to resume previous operation state. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_os.c | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 715b5526fb0a..ea58398bfdb1 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -7115,6 +7115,57 @@ qla2xxx_pci_resume(struct pci_dev *pdev) ha->flags.eeh_busy = 0; } +static void +qla_pci_reset_prepare(struct pci_dev *pdev) +{ + scsi_qla_host_t *base_vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = base_vha->hw; + struct qla_qpair *qpair; + + ql_log(ql_log_warn, base_vha, 0xffff, + "%s.\n", __func__); + + /* + * PCI FLR/function reset is about to reset the + * slot. Stop the chip to stop all DMA access. + * It is assumed that pci_reset_done will be called + * after FLR to resume Chip operation. + */ + ha->flags.eeh_busy = 1; + mutex_lock(&ha->mq_lock); + list_for_each_entry(qpair, &base_vha->qp_list, qp_list_elem) + qpair->online = 0; + mutex_unlock(&ha->mq_lock); + + set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); + qla2x00_abort_isp_cleanup(base_vha); + qla2x00_abort_all_cmds(base_vha, DID_RESET << 16); +} + +static void +qla_pci_reset_done(struct pci_dev *pdev) +{ + scsi_qla_host_t *base_vha = pci_get_drvdata(pdev); + struct qla_hw_data *ha = base_vha->hw; + struct qla_qpair *qpair; + + ql_log(ql_log_warn, base_vha, 0xffff, + "%s.\n", __func__); + + /* + * FLR just completed by PCI layer. Resume adapter + */ + ha->flags.eeh_busy = 0; + mutex_lock(&ha->mq_lock); + list_for_each_entry(qpair, &base_vha->qp_list, qp_list_elem) + qpair->online = 1; + mutex_unlock(&ha->mq_lock); + + base_vha->flags.online = 1; + ha->isp_ops->abort_isp(base_vha); + clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); +} + static int qla2xxx_map_queues(struct Scsi_Host *shost) { int rc; @@ -7133,6 +7184,8 @@ static const struct pci_error_handlers qla2xxx_err_handler = { .mmio_enabled = qla2xxx_pci_mmio_enabled, .slot_reset = qla2xxx_pci_slot_reset, .resume = qla2xxx_pci_resume, + .reset_prepare = qla_pci_reset_prepare, + .reset_done = qla_pci_reset_done, }; static struct pci_device_id qla2xxx_pci_tbl[] = { From 4825034afba894cc2533e91ff411bfd5b49a632c Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:41 -0800 Subject: [PATCH 1191/4277] scsi: qla2xxx: Change default ZIO threshold. Change default ZIO threshold to an optimized value. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 8858bb1ea1b2..80acf30fd8a5 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -4220,7 +4220,7 @@ struct qla_hw_data { atomic_t zio_threshold; uint16_t last_zio_threshold; -#define DEFAULT_ZIO_THRESHOLD 64 +#define DEFAULT_ZIO_THRESHOLD 5 }; #define FW_ABILITY_MAX_SPEED_MASK 0xFUL From 80676d054e5a945f8192802b68093764fbf3c5fc Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:42 -0800 Subject: [PATCH 1192/4277] scsi: qla2xxx: Fix session cleanup hang On session cleanup, either an implicit LOGO or an implicit PRLO is used to flush IOs. If the flush command hit Queue Full condition, then it is dropped. This patch adds retry code to prevent command drop. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_iocb.c | 5 ++--- drivers/scsi/qla2xxx/qla_os.c | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 65ba0e36ee60..2c27ae1924c5 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -3645,23 +3645,22 @@ qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio) int qla2x00_start_sp(srb_t *sp) { - int rval; + int rval = QLA_SUCCESS; scsi_qla_host_t *vha = sp->vha; struct qla_hw_data *ha = vha->hw; struct qla_qpair *qp = sp->qpair; void *pkt; unsigned long flags; - rval = QLA_FUNCTION_FAILED; spin_lock_irqsave(qp->qp_lock_ptr, flags); pkt = __qla2x00_alloc_iocbs(sp->qpair, sp); if (!pkt) { + rval = EAGAIN; ql_log(ql_log_warn, vha, 0x700c, "qla2x00_alloc_iocbs failed.\n"); goto done; } - rval = QLA_SUCCESS; switch (sp->type) { case SRB_LOGIN_CMD: IS_FWI2_CAPABLE(ha) ? diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index ea58398bfdb1..7bf23943c815 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -5204,14 +5204,14 @@ qla2x00_do_work(struct scsi_qla_host *vha) struct qla_work_evt *e, *tmp; unsigned long flags; LIST_HEAD(work); + int rc; spin_lock_irqsave(&vha->work_lock, flags); list_splice_init(&vha->work_list, &work); spin_unlock_irqrestore(&vha->work_lock, flags); list_for_each_entry_safe(e, tmp, &work, list) { - list_del_init(&e->list); - + rc = QLA_SUCCESS; switch (e->type) { case QLA_EVT_AEN: fc_host_post_event(vha->host, fc_get_event_number(), @@ -5225,7 +5225,7 @@ qla2x00_do_work(struct scsi_qla_host *vha) e->u.logio.data); break; case QLA_EVT_ASYNC_LOGOUT: - qla2x00_async_logout(vha, e->u.logio.fcport); + rc = qla2x00_async_logout(vha, e->u.logio.fcport); break; case QLA_EVT_ASYNC_LOGOUT_DONE: qla2x00_async_logout_done(vha, e->u.logio.fcport, @@ -5270,7 +5270,7 @@ qla2x00_do_work(struct scsi_qla_host *vha) qla24xx_do_nack_work(vha, e); break; case QLA_EVT_ASYNC_PRLO: - qla2x00_async_prlo(vha, e->u.logio.fcport); + rc = qla2x00_async_prlo(vha, e->u.logio.fcport); break; case QLA_EVT_ASYNC_PRLO_DONE: qla2x00_async_prlo_done(vha, e->u.logio.fcport, @@ -5303,6 +5303,15 @@ qla2x00_do_work(struct scsi_qla_host *vha) e->u.fcport.fcport, false); break; } + + if (rc == EAGAIN) { + /* put 'work' at head of 'vha->work_list' */ + spin_lock_irqsave(&vha->work_lock, flags); + list_splice(&work, &vha->work_list); + spin_unlock_irqrestore(&vha->work_lock, flags); + break; + } + list_del_init(&e->list); if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); From 51fd6e6351a62da87a7eb7b059d34056d73e68e5 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:43 -0800 Subject: [PATCH 1193/4277] scsi: qla2xxx: flush IO on chip reset or sess delete On Transmit respond in target mode, if the chip is already reset or the session is already deleted, then advance the command to the free step. There is no need to abort the command, because the chip has already flushed it. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 764b6ed74cfc..7f5b1156ea8c 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3257,13 +3257,10 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, unsigned long flags = 0; int res; - if (cmd->sess && cmd->sess->deleted) { + if (!qpair->fw_started || (cmd->reset_count != qpair->chip_reset) || + (cmd->sess && cmd->sess->deleted)) { cmd->state = QLA_TGT_STATE_PROCESSED; - if (cmd->sess->logout_completed) - /* no need to terminate. FW already freed exchange. */ - qlt_abort_cmd_on_host_reset(cmd->vha, cmd); - else - qlt_send_term_exchange(qpair, cmd, &cmd->atio, 0, 0); + qlt_abort_cmd_on_host_reset(cmd->vha, cmd); return 0; } From 9ecd6564d1547d64fec464fdae75c82794c94c51 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:44 -0800 Subject: [PATCH 1194/4277] scsi: qla2xxx: fix fcport null pointer access. This patch allocates DMA memory to prevent NULL pointer access for ct_sns request while sending switch commands. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 58b4bfe01202..aa72e8316533 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4755,6 +4755,16 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) if (!fcport) return NULL; + fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma, + flags); + if (!fcport->ct_desc.ct_sns) { + ql_log(ql_log_warn, vha, 0xd049, + "Failed to allocate ct_sns request.\n"); + kfree(fcport); + return NULL; + } + /* Setup fcport template structure. */ fcport->vha = vha; fcport->port_type = FCT_UNKNOWN; @@ -4763,13 +4773,11 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) fcport->supported_classes = FC_COS_UNSPECIFIED; fcport->fp_speed = PORT_SPEED_UNKNOWN; - fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev, - sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma, - flags); fcport->disc_state = DSC_DELETED; fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; fcport->deleted = QLA_SESS_DELETED; fcport->login_retry = vha->hw->login_retry_count; + fcport->chip_reset = vha->hw->base_qpair->chip_reset; fcport->logout_on_delete = 1; if (!fcport->ct_desc.ct_sns) { @@ -4778,6 +4786,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) kfree(fcport); fcport = NULL; } + INIT_WORK(&fcport->del_work, qla24xx_delete_sess_fn); INIT_WORK(&fcport->reg_work, qla_register_fcport_fn); INIT_LIST_HEAD(&fcport->gnl_entry); From 1021f0bc2f3d6ac80a33aac0059dce8fd1622235 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:45 -0800 Subject: [PATCH 1195/4277] scsi: qla2xxx: allow session delete to finish before create. This patch flushes del_work and free_work while sending NACK response for PRLI Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 7f5b1156ea8c..6b8c655d9eb4 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -684,6 +684,9 @@ void qla24xx_do_nack_work(struct scsi_qla_host *vha, struct qla_work_evt *e) switch (e->u.nack.type) { case SRB_NACK_PRLI: + t = e->u.nack.fcport; + flush_work(&t->del_work); + flush_work(&t->free_work); mutex_lock(&vha->vha_tgt.tgt_mutex); t = qlt_create_sess(vha, e->u.nack.fcport, 0); mutex_unlock(&vha->vha_tgt.tgt_mutex); From 97a93cea887376e13ea7d473ab349181850f4e39 Mon Sep 17 00:00:00 2001 From: Giridhar Malavali Date: Thu, 24 Jan 2019 23:23:46 -0800 Subject: [PATCH 1196/4277] scsi: qla2xxx: Fix SRB allocation flag to avoid sleeping in IRQ context This patch fixes SRB allocation flag from GFP_KERNEL to GFP_ATOMIC, to prevent sleeping in IRQ context Signed-off-by: Giridhar Malavali Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index aa72e8316533..3bb4fa97e40a 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1829,7 +1829,7 @@ qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait) int rval = QLA_FUNCTION_FAILED; sp = qla2xxx_get_qpair_sp(cmd_sp->vha, cmd_sp->qpair, cmd_sp->fcport, - GFP_KERNEL); + GFP_ATOMIC); if (!sp) goto done; From 5e85f6df77223f69e738b815558ca01e6891a173 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:47 -0800 Subject: [PATCH 1197/4277] scsi: qla2xxx: Prevent memory leak for CT req/rsp allocation This patch fixes memory leak by releasing DMA memory in case CT request and response allocation fails. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_gs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 90cfa394f942..4aada6b37076 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -4157,7 +4157,8 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp) spin_lock_irqsave(&vha->work_lock, flags); vha->scan.scan_flags &= ~SF_SCANNING; spin_unlock_irqrestore(&vha->work_lock, flags); - goto done_free_sp; + qla2x00_rel_sp(sp); + return rval; } sp->u.iocb_cmd.u.ctarg.req_size = GPN_FT_REQ_SIZE; @@ -4175,7 +4176,13 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp) spin_lock_irqsave(&vha->work_lock, flags); vha->scan.scan_flags &= ~SF_SCANNING; spin_unlock_irqrestore(&vha->work_lock, flags); - goto done_free_sp; + dma_free_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.req_allocated_size, + sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.req_dma); + sp->u.iocb_cmd.u.ctarg.req = NULL; + qla2x00_rel_sp(sp); + return rval; } sp->u.iocb_cmd.u.ctarg.rsp_size = rspsz; From dcbf8f8087ebc4d721fd55c4c2072f1a97f6ef6d Mon Sep 17 00:00:00 2001 From: Sawan Chandak Date: Thu, 24 Jan 2019 23:23:48 -0800 Subject: [PATCH 1198/4277] scsi: qla2xxx: Restore FAWWPN of Physical Port only for loop down When loop was made down explicitly due to cable pull, then for N2N toplogy, if FAWWPN BIT is enabled by user, then it would restore some default (garbage) value for Physical port WWPN, so this show garbage WWPN for the port. Fix is, to restore physical port WWPN, if it is fabric configuration. When loop is explicitly made down, and FAWWPN feature is enabled, then driver need to restore original flashed WWPN. Signed-off-by: Sawan Chandak Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_isr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 5c1de6ed825b..bde9940ea7d1 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -834,7 +834,8 @@ skip_rio: * Restore for Physical Port only */ if (!vha->vp_idx) { - if (ha->flags.fawwpn_enabled) { + if (ha->flags.fawwpn_enabled && + (ha->current_topology == ISP_CFG_F)) { void *wwpn = ha->init_cb->port_name; memcpy(vha->port_name, wwpn, WWN_SIZE); fc_host_port_name(vha->host) = From 87d6814a28d943acb969a9b415f66eaea95a882e Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:49 -0800 Subject: [PATCH 1199/4277] scsi: qla2xxx: Fix fw options handle eh_bus_reset() For eh_bus_reset, driver is supposed to reset the link. Current option to reset the link is applicable to Loop only. This patch updates current FW option with the one that is applicable to all topologies. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_mbx.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index f753aa495753..f4adf6baee69 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2246,10 +2246,7 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) mcp->out_mb = MBX_2|MBX_1|MBX_0; } else if (IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[0] = MBC_LIP_FULL_LOGIN; - if (N2N_TOPO(vha->hw)) - mcp->mb[1] = BIT_4; /* re-init */ - else - mcp->mb[1] = BIT_6; /* LIP */ + mcp->mb[1] = BIT_4; mcp->mb[2] = 0; mcp->mb[3] = vha->hw->loop_reset_delay; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; @@ -2759,7 +2756,7 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha) "Entered %s.\n", __func__); mcp->mb[0] = MBC_LIP_FULL_LOGIN; - mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0; + mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_4 : 0; mcp->mb[2] = 0; mcp->mb[3] = 0; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; From 1560bafdff9ed54857ac3a03c4c8d8f10d791ba6 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Thu, 24 Jan 2019 23:23:50 -0800 Subject: [PATCH 1200/4277] scsi: qla2xxx: Use complete switch scan for RSCN events This patch removes unnecessary code to handle RSCN, instead performs full scan everytime driver receives RSCN Fixes: d4f7a16aeca6f ("scsi: qla2xxx: Remove ASYNC GIDPN switch command") Cc: stable@vger.kernel.org #4.19 Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 86 --------------------------------- 1 file changed, 86 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 3bb4fa97e40a..8307eac796a2 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1471,29 +1471,6 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) return 0; } -static -void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea) -{ - fcport->rscn_gen++; - - ql_dbg(ql_dbg_disc, fcport->vha, 0x210c, - "%s %8phC DS %d LS %d\n", - __func__, fcport->port_name, fcport->disc_state, - fcport->fw_login_state); - - if (fcport->flags & FCF_ASYNC_SENT) - return; - - switch (fcport->disc_state) { - case DSC_DELETED: - case DSC_LOGIN_COMPLETE: - qla24xx_post_gpnid_work(fcport->vha, &ea->id); - break; - default: - break; - } -} - int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, u8 *port_name, u8 *node_name, void *pla, u8 fc4_type) { @@ -1560,8 +1537,6 @@ static void qla_handle_els_plogi_done(scsi_qla_host_t *vha, void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) { - fc_port_t *f, *tf; - uint32_t id = 0, mask, rid; fc_port_t *fcport; switch (ea->event) { @@ -1574,10 +1549,6 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) case FCME_RSCN: if (test_bit(UNLOADING, &vha->dpc_flags)) return; - switch (ea->id.b.rsvd_1) { - case RSCN_PORT_ADDR: -#define BIGSCAN 1 -#if defined BIGSCAN & BIGSCAN > 0 { unsigned long flags; fcport = qla2x00_find_fcport_by_nportid @@ -1596,59 +1567,6 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) } spin_unlock_irqrestore(&vha->work_lock, flags); } -#else - { - int rc; - fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1); - if (!fcport) { - /* cable moved */ - rc = qla24xx_post_gpnid_work(vha, &ea->id); - if (rc) { - ql_log(ql_log_warn, vha, 0xd044, - "RSCN GPNID work failed %06x\n", - ea->id.b24); - } - } else { - ea->fcport = fcport; - fcport->scan_needed = 1; - qla24xx_handle_rscn_event(fcport, ea); - } - } -#endif - break; - case RSCN_AREA_ADDR: - case RSCN_DOM_ADDR: - if (ea->id.b.rsvd_1 == RSCN_AREA_ADDR) { - mask = 0xffff00; - ql_dbg(ql_dbg_async, vha, 0x5044, - "RSCN: Area 0x%06x was affected\n", - ea->id.b24); - } else { - mask = 0xff0000; - ql_dbg(ql_dbg_async, vha, 0x507a, - "RSCN: Domain 0x%06x was affected\n", - ea->id.b24); - } - - rid = ea->id.b24 & mask; - list_for_each_entry_safe(f, tf, &vha->vp_fcports, - list) { - id = f->d_id.b24 & mask; - if (rid == id) { - ea->fcport = f; - qla24xx_handle_rscn_event(f, ea); - } - } - break; - case RSCN_FAB_ADDR: - default: - ql_log(ql_log_warn, vha, 0xd045, - "RSCN: Fabric was affected. Addr format %d\n", - ea->id.b.rsvd_1); - qla2x00_mark_all_devices_lost(vha, 1); - set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); - set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); - } break; case FCME_GNL_DONE: qla24xx_handle_gnl_done_event(vha, ea); @@ -1709,11 +1627,7 @@ void qla_rscn_replay(fc_port_t *fcport) ea.event = FCME_RSCN; ea.id = fcport->d_id; ea.id.b.rsvd_1 = RSCN_PORT_ADDR; -#if defined BIGSCAN & BIGSCAN > 0 qla2x00_fcport_event_handler(fcport->vha, &ea); -#else - qla24xx_post_gpnid_work(fcport->vha, &ea.id); -#endif } } From b8837a0f885184bcad347b23a3e640e5735471d7 Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Thu, 24 Jan 2019 23:23:51 -0800 Subject: [PATCH 1201/4277] scsi: qla2xxx: Update driver version to 10.00.00.13-k Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index ca7945cb959b..537098e8f1c1 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "10.00.00.12-k" +#define QLA2XXX_VERSION "10.00.00.13-k" #define QLA_DRIVER_MAJOR_VER 10 #define QLA_DRIVER_MINOR_VER 0 From 0b05e9fe1fd1d6a4f80449743f146dfe162907fc Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:17 -0800 Subject: [PATCH 1202/4277] scsi: lpfc: cleanup: remove nrport from nvme command structure An hba-wide lock is taken in the nvme io completion routine. The lock covers null'ing of the nrport pointer in the cmd structure. The nrport member isn't necessary. After extracting the pointer from the command, the pointer was dereferenced to get the fc discovery node pointer. But the fc discovery node pointer is alrady in the command structure so the dereferrence was unnecessary. Eliminated the nrport structure member and its use, which also eliminates the port-wide lock. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 30 +++++++----------------------- drivers/scsi/lpfc/lpfc_nvme.h | 1 - 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 4c66b19e6199..b59bf37af881 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -961,18 +961,16 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct nvmefc_fcp_req *nCmd; struct nvme_fc_ersp_iu *ep; struct nvme_fc_cmd_iu *cp; - struct lpfc_nvme_rport *rport; struct lpfc_nodelist *ndlp; struct lpfc_nvme_fcpreq_priv *freqpriv; struct lpfc_nvme_lport *lport; struct lpfc_nvme_ctrl_stat *cstat; - unsigned long flags; uint32_t code, status, idx; uint16_t cid, sqhd, data; uint32_t *ptr; /* Sanity check on return of outstanding command */ - if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd || !lpfc_ncmd->nrport) { + if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd) { if (!lpfc_ncmd) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, @@ -983,16 +981,14 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, "6066 Missing cmpl ptrs: lpfc_ncmd %p, " - "nvmeCmd %p nrport %p\n", - lpfc_ncmd, lpfc_ncmd->nvmeCmd, - lpfc_ncmd->nrport); + "nvmeCmd %p\n", + lpfc_ncmd, lpfc_ncmd->nvmeCmd); /* Release the lpfc_ncmd regardless of the missing elements. */ lpfc_release_nvme_buf(phba, lpfc_ncmd); return; } nCmd = lpfc_ncmd->nvmeCmd; - rport = lpfc_ncmd->nrport; status = bf_get(lpfc_wcqe_c_status, wcqe); if (vport->localport) { @@ -1016,18 +1012,11 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, * Catch race where our node has transitioned, but the * transport is still transitioning. */ - ndlp = rport->ndlp; + ndlp = lpfc_ncmd->ndlp; if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6061 rport %p, DID x%06x node not ready.\n", - rport, rport->remoteport->port_id); - - ndlp = lpfc_findnode_did(vport, rport->remoteport->port_id); - if (!ndlp) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, - "6062 Ignoring NVME cmpl. No ndlp\n"); - goto out_err; - } + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6062 Ignoring NVME cmpl. No ndlp\n"); + goto out_err; } code = bf_get(lpfc_wcqe_c_code, wcqe); @@ -1168,10 +1157,6 @@ out_err: lpfc_ncmd->nvmeCmd = NULL; } - spin_lock_irqsave(&phba->hbalock, flags); - lpfc_ncmd->nrport = NULL; - spin_unlock_irqrestore(&phba->hbalock, flags); - /* Call release with XB=1 to queue the IO into the abort list. */ lpfc_release_nvme_buf(phba, lpfc_ncmd); } @@ -1585,7 +1570,6 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, */ freqpriv->nvme_buf = lpfc_ncmd; lpfc_ncmd->nvmeCmd = pnvme_fcreq; - lpfc_ncmd->nrport = rport; lpfc_ncmd->ndlp = ndlp; lpfc_ncmd->start_time = jiffies; diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index cfd4719be25c..7a636bde326f 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -79,7 +79,6 @@ struct lpfc_nvme_rport { struct lpfc_nvme_buf { struct list_head list; struct nvmefc_fcp_req *nvmeCmd; - struct lpfc_nvme_rport *nrport; struct lpfc_nodelist *ndlp; uint32_t timeout; From e960f5ab40b6a31847c4b259aaece4a4b29556ad Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:18 -0800 Subject: [PATCH 1203/4277] scsi: lpfc: cleanup: Remove excess check on NVME io submit code path lpfc_nvme_prep_io_cmd() checks for null pnode, but caller lpfc_nvme_fcp_io_submit() has already ensured it's non-null. Remove the pnode null check. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index b59bf37af881..d3e955f70894 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1190,7 +1190,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, union lpfc_wqe128 *wqe = &pwqeq->wqe; uint32_t req_len; - if (!pnode || !NLP_CHK_NODE_ACT(pnode)) + if (!NLP_CHK_NODE_ACT(pnode)) return -EINVAL; /* From 0794d601d1748b007dc338127232e74c19fd1337 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:19 -0800 Subject: [PATCH 1204/4277] scsi: lpfc: Implement common IO buffers between NVME and SCSI Currently, both NVME and SCSI get their IO buffers from separate pools. XRI's are associated 1:1 with IO buffers, so XRI's are also split between protocols. Eliminate the independent pools and use a single pool. Each buffer structure now has a common section and a protocol section. Per protocol routines for SGL initialization are removed and replaced by common routines. Initialization of the buffers is only done on the common area. All other fields, which are protocol specific, are initialized when the buffer is allocated for use in the per-protocol allocation routine. In the past, the SCSI side allocated IO buffers as part of slave_alloc calls until the maximum XRIs for SCSI was reached. As all XRIs are now common and may be used for either protocol, allocation for everything is done as part of adapter initialization and the scsi side has no action in slave alloc. As XRI's are no longer split, the lpfc_xri_split module parameter is removed. Adapters based on SLI3 will continue to use the older scsi_buf_list_get/put routines. All SLI4 adapters utilize the new IO buffer scheme Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 17 +- drivers/scsi/lpfc/lpfc_attr.c | 23 +- drivers/scsi/lpfc/lpfc_crtn.h | 6 +- drivers/scsi/lpfc/lpfc_init.c | 515 ++++++++++++++++++---------------- drivers/scsi/lpfc/lpfc_nvme.c | 500 +++++---------------------------- drivers/scsi/lpfc/lpfc_nvme.h | 33 +-- drivers/scsi/lpfc/lpfc_scsi.c | 500 ++++++++------------------------- drivers/scsi/lpfc/lpfc_scsi.h | 27 +- drivers/scsi/lpfc/lpfc_sli.c | 285 ++++++++++++++----- drivers/scsi/lpfc/lpfc_sli4.h | 16 +- 10 files changed, 710 insertions(+), 1212 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index ebdfe5b26937..858a9a50f94d 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -617,8 +617,6 @@ struct lpfc_ras_fwlog { struct lpfc_hba { /* SCSI interface function jump table entries */ - int (*lpfc_new_scsi_buf) - (struct lpfc_vport *, int); struct lpfc_scsi_buf * (*lpfc_get_scsi_buf) (struct lpfc_hba *, struct lpfc_nodelist *); int (*lpfc_scsi_prep_dma_buf) @@ -875,7 +873,6 @@ struct lpfc_hba { uint32_t cfg_enable_fc4_type; uint32_t cfg_enable_bbcr; /* Enable BB Credit Recovery */ uint32_t cfg_enable_dpp; /* Enable Direct Packet Push */ - uint32_t cfg_xri_split; #define LPFC_ENABLE_FCP 1 #define LPFC_ENABLE_NVME 2 #define LPFC_ENABLE_BOTH 3 @@ -970,13 +967,13 @@ struct lpfc_hba { struct list_head lpfc_scsi_buf_list_get; struct list_head lpfc_scsi_buf_list_put; uint32_t total_scsi_bufs; - spinlock_t nvme_buf_list_get_lock; /* NVME buf alloc list lock */ - spinlock_t nvme_buf_list_put_lock; /* NVME buf free list lock */ - struct list_head lpfc_nvme_buf_list_get; - struct list_head lpfc_nvme_buf_list_put; - uint32_t total_nvme_bufs; - uint32_t get_nvme_bufs; - uint32_t put_nvme_bufs; + spinlock_t common_buf_list_get_lock; /* Common buf alloc list lock */ + spinlock_t common_buf_list_put_lock; /* Common buf free list lock */ + struct list_head lpfc_common_buf_list_get; + struct list_head lpfc_common_buf_list_put; + uint32_t total_common_bufs; + uint32_t get_common_bufs; + uint32_t put_common_bufs; struct list_head lpfc_iocb_list; uint32_t total_iocbq_bufs; struct list_head active_rrq_list; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 4bae72cbf3f6..0980e1b67b83 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -334,11 +334,10 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, rcu_read_lock(); scnprintf(tmp, sizeof(tmp), - "XRI Dist lpfc%d Total %d NVME %d SCSI %d ELS %d\n", + "XRI Dist lpfc%d Total %d IO %d ELS %d\n", phba->brd_no, phba->sli4_hba.max_cfg_param.max_xri, - phba->sli4_hba.nvme_xri_max, - phba->sli4_hba.scsi_xri_max, + phba->sli4_hba.common_xri_max, lpfc_sli4_get_els_iocb_cnt(phba)); if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) goto buffer_done; @@ -3730,22 +3729,6 @@ LPFC_ATTR_R(enable_fc4_type, LPFC_ENABLE_FCP, LPFC_ENABLE_FCP, LPFC_ENABLE_BOTH, "Enable FC4 Protocol support - FCP / NVME"); -/* - * lpfc_xri_split: Defines the division of XRI resources between SCSI and NVME - * This parameter is only used if: - * lpfc_enable_fc4_type is 3 - register both FCP and NVME and - * port is not configured for NVMET. - * - * ELS/CT always get 10% of XRIs, up to a maximum of 250 - * The remaining XRIs get split up based on lpfc_xri_split per port: - * - * Supported Values are in percentages - * the xri_split value is the percentage the SCSI port will get. The remaining - * percentage will go to NVME. - */ -LPFC_ATTR_R(xri_split, 50, 10, 90, - "Percentage of FCP XRI resources versus NVME"); - /* # lpfc_log_verbose: Only turn this flag on if you are willing to risk being # deluged with LOTS of information. @@ -5704,7 +5687,6 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_nodev_tmo, &dev_attr_lpfc_devloss_tmo, &dev_attr_lpfc_enable_fc4_type, - &dev_attr_lpfc_xri_split, &dev_attr_lpfc_fcp_class, &dev_attr_lpfc_use_adisc, &dev_attr_lpfc_first_burst_size, @@ -6865,7 +6847,6 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; - lpfc_xri_split_init(phba, lpfc_xri_split); lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt); lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth); lpfc_hba_log_verbose_init(phba, lpfc_log_verbose); diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 39f3fa988732..0e49004ceed1 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -520,8 +520,10 @@ int lpfc_sli4_read_config(struct lpfc_hba *); void lpfc_sli4_node_prep(struct lpfc_hba *); int lpfc_sli4_els_sgl_update(struct lpfc_hba *phba); int lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba); -int lpfc_sli4_scsi_sgl_update(struct lpfc_hba *phba); -int lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba); +int lpfc_sli4_common_sgl_update(struct lpfc_hba *phba); +int lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, + struct list_head *blist, int xricnt); +int lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc); void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *); uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *); int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index c1c36812c3d2..149f3182f41e 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1092,13 +1092,15 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) spin_unlock_irq(&phba->hbalock); - list_for_each_entry_safe(psb, psb_next, &aborts, list) { - psb->pCmd = NULL; - psb->status = IOSTAT_SUCCESS; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + list_for_each_entry_safe(psb, psb_next, &aborts, list) { + psb->pCmd = NULL; + psb->status = IOSTAT_SUCCESS; + } + spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); + list_splice(&aborts, &phba->lpfc_common_buf_list_put); + spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); } - spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag); - list_splice(&aborts, &phba->lpfc_scsi_buf_list_put); - spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag); if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { cnt = 0; @@ -1107,10 +1109,10 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) psb->status = IOSTAT_SUCCESS; cnt++; } - spin_lock_irqsave(&phba->nvme_buf_list_put_lock, iflag); - phba->put_nvme_bufs += cnt; - list_splice(&nvme_aborts, &phba->lpfc_nvme_buf_list_put); - spin_unlock_irqrestore(&phba->nvme_buf_list_put_lock, iflag); + spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); + phba->put_common_bufs += cnt; + list_splice(&nvme_aborts, &phba->lpfc_common_buf_list_put); + spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); list_for_each_entry_safe(ctxp, ctxp_next, &nvmet_aborts, list) { ctxp->flag &= ~(LPFC_NVMET_XBUSY | LPFC_NVMET_ABORT_OP); @@ -3123,6 +3125,18 @@ lpfc_online(struct lpfc_hba *phba) "6132 NVME restore reg failed " "on nvmei error x%x\n", error); } + /* Don't post more new bufs if repost already recovered + * the nvme sgls. + */ + if (phba->sli4_hba.common_xri_cnt == 0) { + i = lpfc_new_common_buf(phba, + phba->sli4_hba.common_xri_max); + if (i == 0) { + lpfc_unblock_mgmt_io(phba); + return 1; + } + phba->total_common_bufs += i; + } } else { lpfc_sli_queue_init(phba); if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */ @@ -3355,50 +3369,49 @@ lpfc_scsi_free(struct lpfc_hba *phba) spin_unlock(&phba->scsi_buf_list_get_lock); spin_unlock_irq(&phba->hbalock); } + /** - * lpfc_nvme_free - Free all the NVME buffers and IOCBs from driver lists + * lpfc_common_free - Free all the IO buffers and IOCBs from driver lists * @phba: pointer to lpfc hba data structure. * - * This routine is to free all the NVME buffers and IOCBs from the driver + * This routine is to free all the IO buffers and IOCBs from the driver * list back to kernel. It is called from lpfc_pci_remove_one to free * the internal resources before the device is removed from the system. **/ static void -lpfc_nvme_free(struct lpfc_hba *phba) +lpfc_common_free(struct lpfc_hba *phba) { struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) - return; - spin_lock_irq(&phba->hbalock); /* Release all the lpfc_nvme_bufs maintained by this host. */ - spin_lock(&phba->nvme_buf_list_put_lock); + spin_lock(&phba->common_buf_list_put_lock); list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &phba->lpfc_nvme_buf_list_put, list) { + &phba->lpfc_common_buf_list_put, list) { list_del(&lpfc_ncmd->list); - phba->put_nvme_bufs--; + phba->put_common_bufs--; dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, lpfc_ncmd->dma_handle); kfree(lpfc_ncmd); - phba->total_nvme_bufs--; + phba->total_common_bufs--; } - spin_unlock(&phba->nvme_buf_list_put_lock); + spin_unlock(&phba->common_buf_list_put_lock); - spin_lock(&phba->nvme_buf_list_get_lock); + spin_lock(&phba->common_buf_list_get_lock); list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &phba->lpfc_nvme_buf_list_get, list) { + &phba->lpfc_common_buf_list_get, list) { list_del(&lpfc_ncmd->list); - phba->get_nvme_bufs--; + phba->get_common_bufs--; dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, lpfc_ncmd->dma_handle); kfree(lpfc_ncmd); - phba->total_nvme_bufs--; + phba->total_common_bufs--; } - spin_unlock(&phba->nvme_buf_list_get_lock); + spin_unlock(&phba->common_buf_list_get_lock); spin_unlock_irq(&phba->hbalock); } + /** * lpfc_sli4_els_sgl_update - update ELS xri-sgl sizing and mapping * @phba: pointer to lpfc hba data structure. @@ -3641,7 +3654,7 @@ out_free_mem: } /** - * lpfc_sli4_scsi_sgl_update - update xri-sgl sizing and mapping + * lpfc_sli4_common_sgl_update - update xri-sgl sizing and mapping * @phba: pointer to lpfc hba data structure. * * This routine first calculates the sizes of the current els and allocated @@ -3653,94 +3666,214 @@ out_free_mem: * 0 - successful (for now, it always returns 0) **/ int -lpfc_sli4_scsi_sgl_update(struct lpfc_hba *phba) +lpfc_sli4_common_sgl_update(struct lpfc_hba *phba) { - struct lpfc_scsi_buf *psb, *psb_next; - uint16_t i, lxri, els_xri_cnt, scsi_xri_cnt; - LIST_HEAD(scsi_sgl_list); - int rc; + struct lpfc_nvme_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL; + uint16_t i, lxri, els_xri_cnt; + uint16_t common_xri_cnt, common_xri_max; + LIST_HEAD(common_sgl_list); + int rc, cnt; + + phba->total_common_bufs = 0; + phba->get_common_bufs = 0; + phba->put_common_bufs = 0; /* - * update on pci function's els xri-sgl list + * update on pci function's allocated nvme xri-sgl list */ + + /* maximum number of xris available for nvme buffers */ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); - phba->total_scsi_bufs = 0; - - /* - * update on pci function's allocated scsi xri-sgl list - */ - /* maximum number of xris available for scsi buffers */ - phba->sli4_hba.scsi_xri_max = phba->sli4_hba.max_cfg_param.max_xri - - els_xri_cnt; - - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) - return 0; - - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) - phba->sli4_hba.scsi_xri_max = /* Split them up */ - (phba->sli4_hba.scsi_xri_max * - phba->cfg_xri_split) / 100; - - spin_lock_irq(&phba->scsi_buf_list_get_lock); - spin_lock(&phba->scsi_buf_list_put_lock); - list_splice_init(&phba->lpfc_scsi_buf_list_get, &scsi_sgl_list); - list_splice(&phba->lpfc_scsi_buf_list_put, &scsi_sgl_list); - spin_unlock(&phba->scsi_buf_list_put_lock); - spin_unlock_irq(&phba->scsi_buf_list_get_lock); + common_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; + phba->sli4_hba.common_xri_max = common_xri_max; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "6060 Current allocated SCSI xri-sgl count:%d, " - "maximum SCSI xri count:%d (split:%d)\n", - phba->sli4_hba.scsi_xri_cnt, - phba->sli4_hba.scsi_xri_max, phba->cfg_xri_split); + "6074 Current allocated XRI sgl count:%d, " + "maximum XRI count:%d\n", + phba->sli4_hba.common_xri_cnt, + phba->sli4_hba.common_xri_max); - if (phba->sli4_hba.scsi_xri_cnt > phba->sli4_hba.scsi_xri_max) { - /* max scsi xri shrinked below the allocated scsi buffers */ - scsi_xri_cnt = phba->sli4_hba.scsi_xri_cnt - - phba->sli4_hba.scsi_xri_max; - /* release the extra allocated scsi buffers */ - for (i = 0; i < scsi_xri_cnt; i++) { - list_remove_head(&scsi_sgl_list, psb, - struct lpfc_scsi_buf, list); - if (psb) { + spin_lock_irq(&phba->common_buf_list_get_lock); + spin_lock(&phba->common_buf_list_put_lock); + list_splice_init(&phba->lpfc_common_buf_list_get, &common_sgl_list); + list_splice(&phba->lpfc_common_buf_list_put, &common_sgl_list); + cnt = phba->get_common_bufs + phba->put_common_bufs; + phba->get_common_bufs = 0; + phba->put_common_bufs = 0; + spin_unlock(&phba->common_buf_list_put_lock); + spin_unlock_irq(&phba->common_buf_list_get_lock); + + if (phba->sli4_hba.common_xri_cnt > phba->sli4_hba.common_xri_max) { + /* max nvme xri shrunk below the allocated nvme buffers */ + spin_lock_irq(&phba->common_buf_list_get_lock); + common_xri_cnt = phba->sli4_hba.common_xri_cnt - + phba->sli4_hba.common_xri_max; + spin_unlock_irq(&phba->common_buf_list_get_lock); + /* release the extra allocated nvme buffers */ + for (i = 0; i < common_xri_cnt; i++) { + list_remove_head(&common_sgl_list, lpfc_ncmd, + struct lpfc_nvme_buf, list); + if (lpfc_ncmd) { dma_pool_free(phba->lpfc_sg_dma_buf_pool, - psb->data, psb->dma_handle); - kfree(psb); + lpfc_ncmd->data, + lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); } } - spin_lock_irq(&phba->scsi_buf_list_get_lock); - phba->sli4_hba.scsi_xri_cnt -= scsi_xri_cnt; - spin_unlock_irq(&phba->scsi_buf_list_get_lock); + spin_lock_irq(&phba->common_buf_list_get_lock); + phba->sli4_hba.common_xri_cnt -= common_xri_cnt; + spin_unlock_irq(&phba->common_buf_list_get_lock); } - /* update xris associated to remaining allocated scsi buffers */ - psb = NULL; - psb_next = NULL; - list_for_each_entry_safe(psb, psb_next, &scsi_sgl_list, list) { + /* update xris associated to remaining allocated nvme buffers */ + lpfc_ncmd = NULL; + lpfc_ncmd_next = NULL; + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &common_sgl_list, list) { lxri = lpfc_sli4_next_xritag(phba); if (lxri == NO_XRI) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "2560 Failed to allocate xri for " - "scsi buffer\n"); + "6075 Failed to allocate xri for " + "nvme buffer\n"); rc = -ENOMEM; goto out_free_mem; } - psb->cur_iocbq.sli4_lxritag = lxri; - psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; + lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri; + lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; } - spin_lock_irq(&phba->scsi_buf_list_get_lock); - spin_lock(&phba->scsi_buf_list_put_lock); - list_splice_init(&scsi_sgl_list, &phba->lpfc_scsi_buf_list_get); - INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - spin_unlock(&phba->scsi_buf_list_put_lock); - spin_unlock_irq(&phba->scsi_buf_list_get_lock); + spin_lock_irq(&phba->common_buf_list_get_lock); + spin_lock(&phba->common_buf_list_put_lock); + list_splice_init(&common_sgl_list, &phba->lpfc_common_buf_list_get); + phba->get_common_bufs = cnt; + INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); + spin_unlock(&phba->common_buf_list_put_lock); + spin_unlock_irq(&phba->common_buf_list_get_lock); return 0; out_free_mem: - lpfc_scsi_free(phba); + lpfc_common_free(phba); return rc; } +/** + * lpfc_new_common_buf - IO buffer allocator for HBA with SLI4 IF spec + * @vport: The virtual port for which this call being executed. + * @num_to_allocate: The requested number of buffers to allocate. + * + * This routine allocates nvme buffers for device with SLI-4 interface spec, + * the nvme buffer contains all the necessary information needed to initiate + * an I/O. After allocating up to @num_to_allocate IO buffers and put + * them on a list, it post them to the port by using SGL block post. + * + * Return codes: + * int - number of nvme buffers that were allocated and posted. + * 0 = failure, less than num_to_alloc is a partial failure. + **/ +int +lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc) +{ + struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_iocbq *pwqeq; + uint16_t iotag, lxri = 0; + int bcnt, num_posted; + LIST_HEAD(prep_nblist); + LIST_HEAD(post_nblist); + LIST_HEAD(nvme_nblist); + + /* Sanity check to ensure our sizing is right for both SCSI and NVME */ + if ((sizeof(struct lpfc_scsi_buf) > LPFC_COMMON_IO_BUF_SZ) || + (sizeof(struct lpfc_nvme_buf) > LPFC_COMMON_IO_BUF_SZ)) { + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "6426 Common buffer size mismatch: %ld %ld\n", + sizeof(struct lpfc_scsi_buf), + sizeof(struct lpfc_nvme_buf)); + return 0; + } + + for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { + lpfc_ncmd = kzalloc(LPFC_COMMON_IO_BUF_SZ, GFP_KERNEL); + if (!lpfc_ncmd) + break; + /* + * Get memory from the pci pool to map the virt space to + * pci bus space for an I/O. The DMA buffer includes the + * number of SGE's necessary to support the sg_tablesize. + */ + lpfc_ncmd->data = dma_pool_alloc(phba->lpfc_sg_dma_buf_pool, + GFP_KERNEL, + &lpfc_ncmd->dma_handle); + if (!lpfc_ncmd->data) { + kfree(lpfc_ncmd); + break; + } + memset(lpfc_ncmd->data, 0, phba->cfg_sg_dma_buf_size); + + /* + * 4K Page alignment is CRITICAL to BlockGuard, double check + * to be sure. + */ + if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) && + (((unsigned long)(lpfc_ncmd->data) & + (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "3369 Memory alignment err: addr=%lx\n", + (unsigned long)lpfc_ncmd->data); + dma_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + break; + } + + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { + dma_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + break; + } + pwqeq = &lpfc_ncmd->cur_iocbq; + + /* Allocate iotag for lpfc_ncmd->cur_iocbq. */ + iotag = lpfc_sli_next_iotag(phba, pwqeq); + if (iotag == 0) { + dma_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6121 Failed to allocate IOTAG for" + " XRI:0x%x\n", lxri); + lpfc_sli4_free_xri(phba, lxri); + break; + } + pwqeq->sli4_lxritag = lxri; + pwqeq->sli4_xritag = phba->sli4_hba.xri_ids[lxri]; + pwqeq->context1 = lpfc_ncmd; + + /* Initialize local short-hand pointers. */ + lpfc_ncmd->dma_sgl = lpfc_ncmd->data; + lpfc_ncmd->dma_phys_sgl = lpfc_ncmd->dma_handle; + lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd; + + /* add the nvme buffer to a post list */ + list_add_tail(&lpfc_ncmd->list, &post_nblist); + spin_lock_irq(&phba->common_buf_list_get_lock); + phba->sli4_hba.common_xri_cnt++; + spin_unlock_irq(&phba->common_buf_list_get_lock); + } + lpfc_printf_log(phba, KERN_INFO, LOG_NVME, + "6114 Allocate %d out of %d requested new NVME " + "buffers\n", bcnt, num_to_alloc); + + /* post the list of nvme buffer sgls to port if available */ + if (!list_empty(&post_nblist)) + num_posted = lpfc_sli4_post_common_sgl_list( + phba, &post_nblist, bcnt); + else + num_posted = 0; + + return num_posted; +} + static uint64_t lpfc_get_wwpn(struct lpfc_hba *phba) { @@ -3776,111 +3909,6 @@ lpfc_get_wwpn(struct lpfc_hba *phba) return rol64(wwn, 32); } -/** - * lpfc_sli4_nvme_sgl_update - update xri-sgl sizing and mapping - * @phba: pointer to lpfc hba data structure. - * - * This routine first calculates the sizes of the current els and allocated - * scsi sgl lists, and then goes through all sgls to updates the physical - * XRIs assigned due to port function reset. During port initialization, the - * current els and allocated scsi sgl lists are 0s. - * - * Return codes - * 0 - successful (for now, it always returns 0) - **/ -int -lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba) -{ - struct lpfc_nvme_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL; - uint16_t i, lxri, els_xri_cnt; - uint16_t nvme_xri_cnt, nvme_xri_max; - LIST_HEAD(nvme_sgl_list); - int rc, cnt; - - phba->total_nvme_bufs = 0; - phba->get_nvme_bufs = 0; - phba->put_nvme_bufs = 0; - - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) - return 0; - /* - * update on pci function's allocated nvme xri-sgl list - */ - - /* maximum number of xris available for nvme buffers */ - els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); - nvme_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; - phba->sli4_hba.nvme_xri_max = nvme_xri_max; - phba->sli4_hba.nvme_xri_max -= phba->sli4_hba.scsi_xri_max; - - lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "6074 Current allocated NVME xri-sgl count:%d, " - "maximum NVME xri count:%d\n", - phba->sli4_hba.nvme_xri_cnt, - phba->sli4_hba.nvme_xri_max); - - spin_lock_irq(&phba->nvme_buf_list_get_lock); - spin_lock(&phba->nvme_buf_list_put_lock); - list_splice_init(&phba->lpfc_nvme_buf_list_get, &nvme_sgl_list); - list_splice(&phba->lpfc_nvme_buf_list_put, &nvme_sgl_list); - cnt = phba->get_nvme_bufs + phba->put_nvme_bufs; - phba->get_nvme_bufs = 0; - phba->put_nvme_bufs = 0; - spin_unlock(&phba->nvme_buf_list_put_lock); - spin_unlock_irq(&phba->nvme_buf_list_get_lock); - - if (phba->sli4_hba.nvme_xri_cnt > phba->sli4_hba.nvme_xri_max) { - /* max nvme xri shrunk below the allocated nvme buffers */ - spin_lock_irq(&phba->nvme_buf_list_get_lock); - nvme_xri_cnt = phba->sli4_hba.nvme_xri_cnt - - phba->sli4_hba.nvme_xri_max; - spin_unlock_irq(&phba->nvme_buf_list_get_lock); - /* release the extra allocated nvme buffers */ - for (i = 0; i < nvme_xri_cnt; i++) { - list_remove_head(&nvme_sgl_list, lpfc_ncmd, - struct lpfc_nvme_buf, list); - if (lpfc_ncmd) { - dma_pool_free(phba->lpfc_sg_dma_buf_pool, - lpfc_ncmd->data, - lpfc_ncmd->dma_handle); - kfree(lpfc_ncmd); - } - } - spin_lock_irq(&phba->nvme_buf_list_get_lock); - phba->sli4_hba.nvme_xri_cnt -= nvme_xri_cnt; - spin_unlock_irq(&phba->nvme_buf_list_get_lock); - } - - /* update xris associated to remaining allocated nvme buffers */ - lpfc_ncmd = NULL; - lpfc_ncmd_next = NULL; - list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &nvme_sgl_list, list) { - lxri = lpfc_sli4_next_xritag(phba); - if (lxri == NO_XRI) { - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "6075 Failed to allocate xri for " - "nvme buffer\n"); - rc = -ENOMEM; - goto out_free_mem; - } - lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri; - lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; - } - spin_lock_irq(&phba->nvme_buf_list_get_lock); - spin_lock(&phba->nvme_buf_list_put_lock); - list_splice_init(&nvme_sgl_list, &phba->lpfc_nvme_buf_list_get); - phba->get_nvme_bufs = cnt; - INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put); - spin_unlock(&phba->nvme_buf_list_put_lock); - spin_unlock_irq(&phba->nvme_buf_list_get_lock); - return 0; - -out_free_mem: - lpfc_nvme_free(phba); - return rc; -} - /** * lpfc_create_port - Create an FC port * @phba: pointer to lpfc hba data structure. @@ -5819,24 +5847,19 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) "NVME" : " "), (phba->nvmet_support ? "NVMET" : " ")); - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { - /* Initialize the scsi buffer list used by driver for scsi IO */ - spin_lock_init(&phba->scsi_buf_list_get_lock); - INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get); - spin_lock_init(&phba->scsi_buf_list_put_lock); - INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - } + /* Initialize the IO buffer list used by driver for SLI3 SCSI */ + spin_lock_init(&phba->scsi_buf_list_get_lock); + INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get); + spin_lock_init(&phba->scsi_buf_list_put_lock); + INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && - (phba->nvmet_support == 0)) { - /* Initialize the NVME buffer list used by driver for NVME IO */ - spin_lock_init(&phba->nvme_buf_list_get_lock); - INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_get); - phba->get_nvme_bufs = 0; - spin_lock_init(&phba->nvme_buf_list_put_lock); - INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put); - phba->put_nvme_bufs = 0; - } + /* Initialize the IO buffer list used by driver for SLI4 SCSI/NVME */ + spin_lock_init(&phba->common_buf_list_get_lock); + INIT_LIST_HEAD(&phba->lpfc_common_buf_list_get); + phba->get_common_bufs = 0; + spin_lock_init(&phba->common_buf_list_put_lock); + INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); + phba->put_common_bufs = 0; /* Initialize the fabric iocb list */ INIT_LIST_HEAD(&phba->fabric_iocb_list); @@ -5877,7 +5900,7 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) static int lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) { - int rc; + int rc, entry_sz; /* * Initialize timers used by driver @@ -5922,6 +5945,11 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) lpfc_template_no_hr.sg_tablesize = phba->cfg_sg_seg_cnt; lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt; + if (phba->sli_rev == LPFC_SLI_REV4) + entry_sz = sizeof(struct sli4_sge); + else + entry_sz = sizeof(struct ulp_bde64); + /* There are going to be 2 reserved BDEs: 1 FCP cmnd + 1 FCP rsp */ if (phba->cfg_enable_bg) { /* @@ -5935,7 +5963,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) */ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) + - (LPFC_MAX_SG_SEG_CNT * sizeof(struct ulp_bde64)); + (LPFC_MAX_SG_SEG_CNT * entry_sz); if (phba->cfg_sg_seg_cnt > LPFC_MAX_SG_SEG_CNT_DIF) phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT_DIF; @@ -5950,7 +5978,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) */ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) + - ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64)); + ((phba->cfg_sg_seg_cnt + 2) * entry_sz); /* Total BDEs in BPL for scsi_sg_list */ phba->cfg_total_seg_cnt = phba->cfg_sg_seg_cnt + 2; @@ -6875,11 +6903,8 @@ lpfc_init_sgl_list(struct lpfc_hba *phba) /* els xri-sgl book keeping */ phba->sli4_hba.els_xri_cnt = 0; - /* scsi xri-buffer book keeping */ - phba->sli4_hba.scsi_xri_cnt = 0; - /* nvme xri-buffer book keeping */ - phba->sli4_hba.nvme_xri_cnt = 0; + phba->sli4_hba.common_xri_cnt = 0; } /** @@ -10556,7 +10581,7 @@ static void lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) { int wait_time = 0; - int nvme_xri_cmpl = 1; + int common_xri_cmpl = 1; int nvmet_xri_cmpl = 1; int fcp_xri_cmpl = 1; int els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list); @@ -10575,13 +10600,13 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) fcp_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list); if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - nvme_xri_cmpl = + common_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_nvme_buf_list); nvmet_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list); } - while (!fcp_xri_cmpl || !els_xri_cmpl || !nvme_xri_cmpl || + while (!fcp_xri_cmpl || !els_xri_cmpl || !common_xri_cmpl || !nvmet_xri_cmpl) { if (wait_time > LPFC_XRI_EXCH_BUSY_WAIT_TMO) { if (!nvmet_xri_cmpl) @@ -10589,7 +10614,7 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) "6424 NVMET XRI exchange busy " "wait time: %d seconds.\n", wait_time/1000); - if (!nvme_xri_cmpl) + if (!common_xri_cmpl) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "6100 NVME XRI exchange busy " "wait time: %d seconds.\n", @@ -10611,7 +10636,7 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) wait_time += LPFC_XRI_EXCH_BUSY_WAIT_T1; } if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - nvme_xri_cmpl = list_empty( + common_xri_cmpl = list_empty( &phba->sli4_hba.lpfc_abts_nvme_buf_list); nvmet_xri_cmpl = list_empty( &phba->sli4_hba.lpfc_abts_nvmet_ctx_list); @@ -11190,6 +11215,8 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev) * corresponding pools here. */ lpfc_scsi_free(phba); + lpfc_free_iocb_list(phba); + lpfc_mem_free_all(phba); dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(), @@ -11767,7 +11794,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) struct lpfc_hba *phba; struct lpfc_vport *vport = NULL; struct Scsi_Host *shost = NULL; - int error; + int error, len; uint32_t cfg_mode, intr_mode; /* Allocate memory for HBA structure */ @@ -11877,19 +11904,32 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* NVME support in FW earlier in the driver load corrects the * FC4 type making a check for nvme_support unnecessary. */ - if ((phba->nvmet_support == 0) && - (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { - /* Create NVME binding with nvme_fc_transport. This - * ensures the vport is initialized. If the localport - * create fails, it should not unload the driver to - * support field issues. + if (phba->nvmet_support == 0) { + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + /* Create NVME binding with nvme_fc_transport. This + * ensures the vport is initialized. If the localport + * create fails, it should not unload the driver to + * support field issues. + */ + error = lpfc_nvme_create_localport(vport); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6004 NVME registration " + "failed, error x%x\n", + error); + } + } + /* Don't post more new bufs if repost already recovered + * the nvme sgls. */ - error = lpfc_nvme_create_localport(vport); - if (error) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6004 NVME registration failed, " - "error x%x\n", - error); + if (phba->sli4_hba.common_xri_cnt == 0) { + len = lpfc_new_common_buf( + phba, phba->sli4_hba.common_xri_max); + if (len == 0) { + error = -ENOMEM; + goto out_disable_intr; + } + phba->total_common_bufs += len; } } @@ -11989,8 +12029,7 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) /* Perform scsi free before driver resource_unset since scsi * buffers are released to their corresponding pools here. */ - lpfc_scsi_free(phba); - lpfc_nvme_free(phba); + lpfc_common_free(phba); lpfc_free_iocb_list(phba); lpfc_unset_driver_resource_phase2(phba); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index d3e955f70894..1068a5a7b126 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -783,7 +783,7 @@ lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport, * rather than the virtual memory to ease the restore * operation. */ - sgl = lpfc_ncmd->nvme_sgl; + sgl = lpfc_ncmd->dma_sgl; sgl->sge_len = cpu_to_le32(nCmd->cmdlen); if (phba->cfg_nvme_embed_cmd) { sgl->addr_hi = 0; @@ -1291,7 +1291,7 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, struct lpfc_hba *phba = vport->phba; struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; union lpfc_wqe128 *wqe = &lpfc_ncmd->cur_iocbq.wqe; - struct sli4_sge *sgl = lpfc_ncmd->nvme_sgl; + struct sli4_sge *sgl = lpfc_ncmd->dma_sgl; struct scatterlist *data_sg; struct sli4_sge *first_data_sgl; struct ulp_bde64 *bde; @@ -1380,6 +1380,8 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, } } else { + lpfc_ncmd->seg_cnt = 0; + /* For this clause to be valid, the payload_length * and sg_cnt must zero. */ @@ -1571,7 +1573,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, freqpriv->nvme_buf = lpfc_ncmd; lpfc_ncmd->nvmeCmd = pnvme_fcreq; lpfc_ncmd->ndlp = ndlp; - lpfc_ncmd->start_time = jiffies; + lpfc_ncmd->qidx = lpfc_queue_info->qidx; /* * Issue the IO on the WQ indicated by index in the hw_queue_handle. @@ -1910,422 +1912,25 @@ static struct nvme_fc_port_template lpfc_nvme_template = { .fcprqst_priv_sz = sizeof(struct lpfc_nvme_fcpreq_priv), }; -/** - * lpfc_sli4_post_nvme_sgl_block - post a block of nvme sgl list to firmware - * @phba: pointer to lpfc hba data structure. - * @nblist: pointer to nvme buffer list. - * @count: number of scsi buffers on the list. - * - * This routine is invoked to post a block of @count scsi sgl pages from a - * SCSI buffer list @nblist to the HBA using non-embedded mailbox command. - * No Lock is held. - * - **/ -static int -lpfc_sli4_post_nvme_sgl_block(struct lpfc_hba *phba, - struct list_head *nblist, - int count) -{ - struct lpfc_nvme_buf *lpfc_ncmd; - struct lpfc_mbx_post_uembed_sgl_page1 *sgl; - struct sgl_page_pairs *sgl_pg_pairs; - void *viraddr; - LPFC_MBOXQ_t *mbox; - uint32_t reqlen, alloclen, pg_pairs; - uint32_t mbox_tmo; - uint16_t xritag_start = 0; - int rc = 0; - uint32_t shdr_status, shdr_add_status; - dma_addr_t pdma_phys_bpl1; - union lpfc_sli4_cfg_shdr *shdr; - - /* Calculate the requested length of the dma memory */ - reqlen = count * sizeof(struct sgl_page_pairs) + - sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); - if (reqlen > SLI4_PAGE_SIZE) { - lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, - "6118 Block sgl registration required DMA " - "size (%d) great than a page\n", reqlen); - return -ENOMEM; - } - mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); - if (!mbox) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6119 Failed to allocate mbox cmd memory\n"); - return -ENOMEM; - } - - /* Allocate DMA memory and set up the non-embedded mailbox command */ - alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, - LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen, - LPFC_SLI4_MBX_NEMBED); - - if (alloclen < reqlen) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6120 Allocated DMA memory size (%d) is " - "less than the requested DMA memory " - "size (%d)\n", alloclen, reqlen); - lpfc_sli4_mbox_cmd_free(phba, mbox); - return -ENOMEM; - } - - /* Get the first SGE entry from the non-embedded DMA memory */ - viraddr = mbox->sge_array->addr[0]; - - /* Set up the SGL pages in the non-embedded DMA pages */ - sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr; - sgl_pg_pairs = &sgl->sgl_pg_pairs; - - pg_pairs = 0; - list_for_each_entry(lpfc_ncmd, nblist, list) { - /* Set up the sge entry */ - sgl_pg_pairs->sgl_pg0_addr_lo = - cpu_to_le32(putPaddrLow(lpfc_ncmd->dma_phys_sgl)); - sgl_pg_pairs->sgl_pg0_addr_hi = - cpu_to_le32(putPaddrHigh(lpfc_ncmd->dma_phys_sgl)); - if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE) - pdma_phys_bpl1 = lpfc_ncmd->dma_phys_sgl + - SGL_PAGE_SIZE; - else - pdma_phys_bpl1 = 0; - sgl_pg_pairs->sgl_pg1_addr_lo = - cpu_to_le32(putPaddrLow(pdma_phys_bpl1)); - sgl_pg_pairs->sgl_pg1_addr_hi = - cpu_to_le32(putPaddrHigh(pdma_phys_bpl1)); - /* Keep the first xritag on the list */ - if (pg_pairs == 0) - xritag_start = lpfc_ncmd->cur_iocbq.sli4_xritag; - sgl_pg_pairs++; - pg_pairs++; - } - bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start); - bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs); - /* Perform endian conversion if necessary */ - sgl->word0 = cpu_to_le32(sgl->word0); - - if (!phba->sli4_hba.intr_enable) - rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); - else { - mbox_tmo = lpfc_mbox_tmo_val(phba, mbox); - rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); - } - shdr = (union lpfc_sli4_cfg_shdr *)&sgl->cfg_shdr; - shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); - shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); - if (rc != MBX_TIMEOUT) - lpfc_sli4_mbox_cmd_free(phba, mbox); - if (shdr_status || shdr_add_status || rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "6125 POST_SGL_BLOCK mailbox command failed " - "status x%x add_status x%x mbx status x%x\n", - shdr_status, shdr_add_status, rc); - rc = -ENXIO; - } - return rc; -} - -/** - * lpfc_post_nvme_sgl_list - Post blocks of nvme buffer sgls from a list - * @phba: pointer to lpfc hba data structure. - * @post_nblist: pointer to the nvme buffer list. - * - * This routine walks a list of nvme buffers that was passed in. It attempts - * to construct blocks of nvme buffer sgls which contains contiguous xris and - * uses the non-embedded SGL block post mailbox commands to post to the port. - * For single NVME buffer sgl with non-contiguous xri, if any, it shall use - * embedded SGL post mailbox command for posting. The @post_nblist passed in - * must be local list, thus no lock is needed when manipulate the list. - * - * Returns: 0 = failure, non-zero number of successfully posted buffers. - **/ -static int -lpfc_post_nvme_sgl_list(struct lpfc_hba *phba, - struct list_head *post_nblist, int sb_count) -{ - struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; - int status, sgl_size; - int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0; - dma_addr_t pdma_phys_sgl1; - int last_xritag = NO_XRI; - int cur_xritag; - LIST_HEAD(prep_nblist); - LIST_HEAD(blck_nblist); - LIST_HEAD(nvme_nblist); - - /* sanity check */ - if (sb_count <= 0) - return -EINVAL; - - sgl_size = phba->cfg_sg_dma_buf_size; - - list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, post_nblist, list) { - list_del_init(&lpfc_ncmd->list); - block_cnt++; - if ((last_xritag != NO_XRI) && - (lpfc_ncmd->cur_iocbq.sli4_xritag != last_xritag + 1)) { - /* a hole in xri block, form a sgl posting block */ - list_splice_init(&prep_nblist, &blck_nblist); - post_cnt = block_cnt - 1; - /* prepare list for next posting block */ - list_add_tail(&lpfc_ncmd->list, &prep_nblist); - block_cnt = 1; - } else { - /* prepare list for next posting block */ - list_add_tail(&lpfc_ncmd->list, &prep_nblist); - /* enough sgls for non-embed sgl mbox command */ - if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) { - list_splice_init(&prep_nblist, &blck_nblist); - post_cnt = block_cnt; - block_cnt = 0; - } - } - num_posting++; - last_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag; - - /* end of repost sgl list condition for NVME buffers */ - if (num_posting == sb_count) { - if (post_cnt == 0) { - /* last sgl posting block */ - list_splice_init(&prep_nblist, &blck_nblist); - post_cnt = block_cnt; - } else if (block_cnt == 1) { - /* last single sgl with non-contiguous xri */ - if (sgl_size > SGL_PAGE_SIZE) - pdma_phys_sgl1 = - lpfc_ncmd->dma_phys_sgl + - SGL_PAGE_SIZE; - else - pdma_phys_sgl1 = 0; - cur_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag; - status = lpfc_sli4_post_sgl(phba, - lpfc_ncmd->dma_phys_sgl, - pdma_phys_sgl1, cur_xritag); - if (status) { - /* failure, put on abort nvme list */ - lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; - } else { - /* success, put on NVME buffer list */ - lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; - lpfc_ncmd->status = IOSTAT_SUCCESS; - num_posted++; - } - /* success, put on NVME buffer sgl list */ - list_add_tail(&lpfc_ncmd->list, &nvme_nblist); - } - } - - /* continue until a nembed page worth of sgls */ - if (post_cnt == 0) - continue; - - /* post block of NVME buffer list sgls */ - status = lpfc_sli4_post_nvme_sgl_block(phba, &blck_nblist, - post_cnt); - - /* don't reset xirtag due to hole in xri block */ - if (block_cnt == 0) - last_xritag = NO_XRI; - - /* reset NVME buffer post count for next round of posting */ - post_cnt = 0; - - /* put posted NVME buffer-sgl posted on NVME buffer sgl list */ - while (!list_empty(&blck_nblist)) { - list_remove_head(&blck_nblist, lpfc_ncmd, - struct lpfc_nvme_buf, list); - if (status) { - /* failure, put on abort nvme list */ - lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; - } else { - /* success, put on NVME buffer list */ - lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; - lpfc_ncmd->status = IOSTAT_SUCCESS; - num_posted++; - } - list_add_tail(&lpfc_ncmd->list, &nvme_nblist); - } - } - /* Push NVME buffers with sgl posted to the available list */ - while (!list_empty(&nvme_nblist)) { - list_remove_head(&nvme_nblist, lpfc_ncmd, - struct lpfc_nvme_buf, list); - lpfc_release_nvme_buf(phba, lpfc_ncmd); - } - return num_posted; -} - -/** - * lpfc_repost_nvme_sgl_list - Repost all the allocated nvme buffer sgls - * @phba: pointer to lpfc hba data structure. - * - * This routine walks the list of nvme buffers that have been allocated and - * repost them to the port by using SGL block post. This is needed after a - * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine - * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list - * to the lpfc_nvme_buf_list. If the repost fails, reject all nvme buffers. - * - * Returns: 0 = success, non-zero failure. - **/ -int -lpfc_repost_nvme_sgl_list(struct lpfc_hba *phba) -{ - LIST_HEAD(post_nblist); - int num_posted, rc = 0; - - /* get all NVME buffers need to repost to a local list */ - spin_lock_irq(&phba->nvme_buf_list_get_lock); - spin_lock(&phba->nvme_buf_list_put_lock); - list_splice_init(&phba->lpfc_nvme_buf_list_get, &post_nblist); - list_splice(&phba->lpfc_nvme_buf_list_put, &post_nblist); - phba->get_nvme_bufs = 0; - phba->put_nvme_bufs = 0; - spin_unlock(&phba->nvme_buf_list_put_lock); - spin_unlock_irq(&phba->nvme_buf_list_get_lock); - - /* post the list of nvme buffer sgls to port if available */ - if (!list_empty(&post_nblist)) { - num_posted = lpfc_post_nvme_sgl_list(phba, &post_nblist, - phba->sli4_hba.nvme_xri_cnt); - /* failed to post any nvme buffer, return error */ - if (num_posted == 0) - rc = -EIO; - } - return rc; -} - -/** - * lpfc_new_nvme_buf - Scsi buffer allocator for HBA with SLI4 IF spec - * @vport: The virtual port for which this call being executed. - * @num_to_allocate: The requested number of buffers to allocate. - * - * This routine allocates nvme buffers for device with SLI-4 interface spec, - * the nvme buffer contains all the necessary information needed to initiate - * a NVME I/O. After allocating up to @num_to_allocate NVME buffers and put - * them on a list, it post them to the port by using SGL block post. - * - * Return codes: - * int - number of nvme buffers that were allocated and posted. - * 0 = failure, less than num_to_alloc is a partial failure. - **/ -static int -lpfc_new_nvme_buf(struct lpfc_vport *vport, int num_to_alloc) -{ - struct lpfc_hba *phba = vport->phba; - struct lpfc_nvme_buf *lpfc_ncmd; - struct lpfc_iocbq *pwqeq; - union lpfc_wqe128 *wqe; - struct sli4_sge *sgl; - dma_addr_t pdma_phys_sgl; - uint16_t iotag, lxri = 0; - int bcnt, num_posted; - LIST_HEAD(prep_nblist); - LIST_HEAD(post_nblist); - LIST_HEAD(nvme_nblist); - - for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { - lpfc_ncmd = kzalloc(sizeof(struct lpfc_nvme_buf), GFP_KERNEL); - if (!lpfc_ncmd) - break; - /* - * Get memory from the pci pool to map the virt space to - * pci bus space for an I/O. The DMA buffer includes the - * number of SGE's necessary to support the sg_tablesize. - */ - lpfc_ncmd->data = dma_pool_zalloc(phba->lpfc_sg_dma_buf_pool, - GFP_KERNEL, - &lpfc_ncmd->dma_handle); - if (!lpfc_ncmd->data) { - kfree(lpfc_ncmd); - break; - } - - lxri = lpfc_sli4_next_xritag(phba); - if (lxri == NO_XRI) { - dma_pool_free(phba->lpfc_sg_dma_buf_pool, - lpfc_ncmd->data, lpfc_ncmd->dma_handle); - kfree(lpfc_ncmd); - break; - } - pwqeq = &(lpfc_ncmd->cur_iocbq); - wqe = &pwqeq->wqe; - - /* Allocate iotag for lpfc_ncmd->cur_iocbq. */ - iotag = lpfc_sli_next_iotag(phba, pwqeq); - if (iotag == 0) { - dma_pool_free(phba->lpfc_sg_dma_buf_pool, - lpfc_ncmd->data, lpfc_ncmd->dma_handle); - kfree(lpfc_ncmd); - lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, - "6121 Failed to allocated IOTAG for" - " XRI:0x%x\n", lxri); - lpfc_sli4_free_xri(phba, lxri); - break; - } - pwqeq->sli4_lxritag = lxri; - pwqeq->sli4_xritag = phba->sli4_hba.xri_ids[lxri]; - pwqeq->iocb_flag |= LPFC_IO_NVME; - pwqeq->context1 = lpfc_ncmd; - pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl; - - /* Initialize local short-hand pointers. */ - lpfc_ncmd->nvme_sgl = lpfc_ncmd->data; - sgl = lpfc_ncmd->nvme_sgl; - pdma_phys_sgl = lpfc_ncmd->dma_handle; - lpfc_ncmd->dma_phys_sgl = pdma_phys_sgl; - - /* Rsp SGE will be filled in when we rcv an IO - * from the NVME Layer to be sent. - * The cmd is going to be embedded so we need a SKIP SGE. - */ - bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP); - bf_set(lpfc_sli4_sge_last, sgl, 0); - sgl->word2 = cpu_to_le32(sgl->word2); - /* Fill in word 3 / sgl_len during cmd submission */ - - lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd; - - /* Initialize WQE */ - memset(wqe, 0, sizeof(union lpfc_wqe)); - - /* add the nvme buffer to a post list */ - list_add_tail(&lpfc_ncmd->list, &post_nblist); - spin_lock_irq(&phba->nvme_buf_list_get_lock); - phba->sli4_hba.nvme_xri_cnt++; - spin_unlock_irq(&phba->nvme_buf_list_get_lock); - } - lpfc_printf_log(phba, KERN_INFO, LOG_NVME, - "6114 Allocate %d out of %d requested new NVME " - "buffers\n", bcnt, num_to_alloc); - - /* post the list of nvme buffer sgls to port if available */ - if (!list_empty(&post_nblist)) - num_posted = lpfc_post_nvme_sgl_list(phba, - &post_nblist, bcnt); - else - num_posted = 0; - - return num_posted; -} - static inline struct lpfc_nvme_buf * lpfc_nvme_buf(struct lpfc_hba *phba) { struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &phba->lpfc_nvme_buf_list_get, list) { + &phba->lpfc_common_buf_list_get, list) { list_del_init(&lpfc_ncmd->list); - phba->get_nvme_bufs--; + phba->get_common_bufs--; return lpfc_ncmd; } return NULL; } /** - * lpfc_get_nvme_buf - Get a nvme buffer from lpfc_nvme_buf_list of the HBA + * lpfc_get_nvme_buf - Get a nvme buffer from lpfc_common_buf_list of the HBA * @phba: The HBA for which this call is being executed. * - * This routine removes a nvme buffer from head of @phba lpfc_nvme_buf_list list + * This routine removes a nvme buffer from head of @phba lpfc_common_buf_list * and returns to caller. * * Return codes: @@ -2337,27 +1942,57 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int expedite) { struct lpfc_nvme_buf *lpfc_ncmd = NULL; + struct sli4_sge *sgl; + struct lpfc_iocbq *pwqeq; + union lpfc_wqe128 *wqe; unsigned long iflag = 0; - spin_lock_irqsave(&phba->nvme_buf_list_get_lock, iflag); - if (phba->get_nvme_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) + spin_lock_irqsave(&phba->common_buf_list_get_lock, iflag); + if (phba->get_common_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) lpfc_ncmd = lpfc_nvme_buf(phba); if (!lpfc_ncmd) { - spin_lock(&phba->nvme_buf_list_put_lock); - list_splice(&phba->lpfc_nvme_buf_list_put, - &phba->lpfc_nvme_buf_list_get); - phba->get_nvme_bufs += phba->put_nvme_bufs; - INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put); - phba->put_nvme_bufs = 0; - spin_unlock(&phba->nvme_buf_list_put_lock); - if (phba->get_nvme_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) + spin_lock(&phba->common_buf_list_put_lock); + list_splice(&phba->lpfc_common_buf_list_put, + &phba->lpfc_common_buf_list_get); + phba->get_common_bufs += phba->put_common_bufs; + INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); + phba->put_common_bufs = 0; + spin_unlock(&phba->common_buf_list_put_lock); + if (phba->get_common_bufs > LPFC_NVME_EXPEDITE_XRICNT || + expedite) lpfc_ncmd = lpfc_nvme_buf(phba); } - spin_unlock_irqrestore(&phba->nvme_buf_list_get_lock, iflag); + spin_unlock_irqrestore(&phba->common_buf_list_get_lock, iflag); - if (lpfc_ndlp_check_qdepth(phba, ndlp) && lpfc_ncmd) { - atomic_inc(&ndlp->cmd_pending); - lpfc_ncmd->flags |= LPFC_BUMP_QDEPTH; + if (lpfc_ncmd) { + pwqeq = &(lpfc_ncmd->cur_iocbq); + wqe = &pwqeq->wqe; + + /* Setup key fields in buffer that may have been changed + * if other protocols used this buffer. + */ + pwqeq->iocb_flag = LPFC_IO_NVME; + pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl; + lpfc_ncmd->start_time = jiffies; + lpfc_ncmd->flags = 0; + + /* Rsp SGE will be filled in when we rcv an IO + * from the NVME Layer to be sent. + * The cmd is going to be embedded so we need a SKIP SGE. + */ + sgl = lpfc_ncmd->dma_sgl; + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP); + bf_set(lpfc_sli4_sge_last, sgl, 0); + sgl->word2 = cpu_to_le32(sgl->word2); + /* Fill in word 3 / sgl_len during cmd submission */ + + /* Initialize WQE */ + memset(wqe, 0, sizeof(union lpfc_wqe)); + + if (lpfc_ndlp_check_qdepth(phba, ndlp)) { + atomic_inc(&ndlp->cmd_pending); + lpfc_ncmd->flags |= LPFC_BUMP_QDEPTH; + } } return lpfc_ncmd; } @@ -2368,7 +2003,7 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * @lpfc_ncmd: The nvme buffer which is being released. * * This routine releases @lpfc_ncmd nvme buffer by adding it to tail of @phba - * lpfc_nvme_buf_list list. For SLI4 XRI's are tied to the nvme buffer + * lpfc_common_buf_list list. For SLI4 XRI's are tied to the nvme buffer * and cannot be reused for at least RA_TOV amount of time if it was * aborted. **/ @@ -2380,7 +2015,6 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) if ((lpfc_ncmd->flags & LPFC_BUMP_QDEPTH) && lpfc_ncmd->ndlp) atomic_dec(&lpfc_ncmd->ndlp->cmd_pending); - lpfc_ncmd->nonsg_phys = 0; lpfc_ncmd->ndlp = NULL; lpfc_ncmd->flags &= ~LPFC_BUMP_QDEPTH; @@ -2398,12 +2032,14 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock, iflag); } else { + /* MUST zero fields if buffer is reused by another protocol */ lpfc_ncmd->nvmeCmd = NULL; - lpfc_ncmd->cur_iocbq.iocb_flag = LPFC_IO_NVME; - spin_lock_irqsave(&phba->nvme_buf_list_put_lock, iflag); - list_add_tail(&lpfc_ncmd->list, &phba->lpfc_nvme_buf_list_put); - phba->put_nvme_bufs++; - spin_unlock_irqrestore(&phba->nvme_buf_list_put_lock, iflag); + lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL; + spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); + list_add_tail(&lpfc_ncmd->list, + &phba->lpfc_common_buf_list_put); + phba->put_common_bufs++; + spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); } } @@ -2432,7 +2068,7 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; struct lpfc_nvme_ctrl_stat *cstat; - int len, i; + int i; /* Initialize this localport instance. The vport wwn usage ensures * that NPIV is accounted for. @@ -2501,18 +2137,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) atomic_set(&cstat->fc4NvmeControlRequests, 0); atomic_set(&cstat->fc4NvmeIoCmpls, 0); } - - /* Don't post more new bufs if repost already recovered - * the nvme sgls. - */ - if (phba->sli4_hba.nvme_xri_cnt == 0) { - len = lpfc_new_nvme_buf(vport, - phba->sli4_hba.nvme_xri_max); - vport->phba->total_nvme_bufs += len; - } - } else { + } else kfree(cstat); - } return ret; } diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 7a636bde326f..e3a554417e98 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -77,7 +77,15 @@ struct lpfc_nvme_rport { }; struct lpfc_nvme_buf { + /* Common fields */ struct list_head list; + void *data; + dma_addr_t dma_handle; + dma_addr_t dma_phys_sgl; + struct sli4_sge *dma_sgl; + struct lpfc_iocbq cur_iocbq; + + /* NVME specific fields */ struct nvmefc_fcp_req *nvmeCmd; struct lpfc_nodelist *ndlp; @@ -87,36 +95,19 @@ struct lpfc_nvme_buf { #define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ #define LPFC_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ - uint16_t status; /* From IOCB Word 7- ulpStatus */ uint16_t cpu; - uint16_t qidx; - uint16_t sqid; + uint16_t status; /* From IOCB Word 7- ulpStatus */ uint32_t result; /* From IOCB Word 4. */ uint32_t seg_cnt; /* Number of scatter-gather segments returned by * dma_map_sg. The driver needs this for calls * to dma_unmap_sg. */ - dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */ - - /* - * data and dma_handle are the kernel virtual and bus address of the - * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter - * gather bde list that supports the sg_tablesize value. - */ - void *data; - dma_addr_t dma_handle; - - struct sli4_sge *nvme_sgl; - dma_addr_t dma_phys_sgl; - - /* cur_iocbq has phys of the dma-able buffer. - * Iotag is in here - */ - struct lpfc_iocbq cur_iocbq; - wait_queue_head_t *waitq; unsigned long start_time; + + uint16_t qidx; + #ifdef CONFIG_SCSI_LPFC_DEBUG_FS uint64_t ts_cmd_start; uint64_t ts_last_cmd; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index b4f1a840b3b4..2e73331cf53a 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -182,7 +182,7 @@ static void lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) { - struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl; + struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; if (sgl) { sgl += 1; sgl->word2 = le32_to_cpu(sgl->word2); @@ -394,7 +394,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) IOCB_t *iocb; dma_addr_t pdma_phys_fcp_cmd; dma_addr_t pdma_phys_fcp_rsp; - dma_addr_t pdma_phys_bpl; + dma_addr_t pdma_phys_sgl; uint16_t iotag; int bcnt, bpl_size; @@ -438,14 +438,14 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) psb->fcp_cmnd = psb->data; psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd); - psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) + + psb->dma_sgl = psb->data + sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp); /* Initialize local short-hand pointers. */ - bpl = psb->fcp_bpl; + bpl = psb->dma_sgl; pdma_phys_fcp_cmd = psb->dma_handle; pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd); - pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) + + pdma_phys_sgl = psb->dma_handle + sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp); /* @@ -496,9 +496,9 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) iocb->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64)); iocb->un.fcpi64.bdl.addrLow = - putPaddrLow(pdma_phys_bpl); + putPaddrLow(pdma_phys_sgl); iocb->un.fcpi64.bdl.addrHigh = - putPaddrHigh(pdma_phys_bpl); + putPaddrHigh(pdma_phys_sgl); iocb->ulpBdeCount = 1; iocb->ulpLe = 1; } @@ -613,359 +613,6 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, spin_unlock_irqrestore(&phba->hbalock, iflag); } -/** - * lpfc_sli4_post_scsi_sgl_list - Post blocks of scsi buffer sgls from a list - * @phba: pointer to lpfc hba data structure. - * @post_sblist: pointer to the scsi buffer list. - * - * This routine walks a list of scsi buffers that was passed in. It attempts - * to construct blocks of scsi buffer sgls which contains contiguous xris and - * uses the non-embedded SGL block post mailbox commands to post to the port. - * For single SCSI buffer sgl with non-contiguous xri, if any, it shall use - * embedded SGL post mailbox command for posting. The @post_sblist passed in - * must be local list, thus no lock is needed when manipulate the list. - * - * Returns: 0 = failure, non-zero number of successfully posted buffers. - **/ -static int -lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba, - struct list_head *post_sblist, int sb_count) -{ - struct lpfc_scsi_buf *psb, *psb_next; - int status, sgl_size; - int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0; - dma_addr_t pdma_phys_bpl1; - int last_xritag = NO_XRI; - LIST_HEAD(prep_sblist); - LIST_HEAD(blck_sblist); - LIST_HEAD(scsi_sblist); - - /* sanity check */ - if (sb_count <= 0) - return -EINVAL; - - sgl_size = phba->cfg_sg_dma_buf_size - - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp)); - - list_for_each_entry_safe(psb, psb_next, post_sblist, list) { - list_del_init(&psb->list); - block_cnt++; - if ((last_xritag != NO_XRI) && - (psb->cur_iocbq.sli4_xritag != last_xritag + 1)) { - /* a hole in xri block, form a sgl posting block */ - list_splice_init(&prep_sblist, &blck_sblist); - post_cnt = block_cnt - 1; - /* prepare list for next posting block */ - list_add_tail(&psb->list, &prep_sblist); - block_cnt = 1; - } else { - /* prepare list for next posting block */ - list_add_tail(&psb->list, &prep_sblist); - /* enough sgls for non-embed sgl mbox command */ - if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) { - list_splice_init(&prep_sblist, &blck_sblist); - post_cnt = block_cnt; - block_cnt = 0; - } - } - num_posting++; - last_xritag = psb->cur_iocbq.sli4_xritag; - - /* end of repost sgl list condition for SCSI buffers */ - if (num_posting == sb_count) { - if (post_cnt == 0) { - /* last sgl posting block */ - list_splice_init(&prep_sblist, &blck_sblist); - post_cnt = block_cnt; - } else if (block_cnt == 1) { - /* last single sgl with non-contiguous xri */ - if (sgl_size > SGL_PAGE_SIZE) - pdma_phys_bpl1 = psb->dma_phys_bpl + - SGL_PAGE_SIZE; - else - pdma_phys_bpl1 = 0; - status = lpfc_sli4_post_sgl(phba, - psb->dma_phys_bpl, - pdma_phys_bpl1, - psb->cur_iocbq.sli4_xritag); - if (status) { - /* failure, put on abort scsi list */ - psb->exch_busy = 1; - } else { - /* success, put on SCSI buffer list */ - psb->exch_busy = 0; - psb->status = IOSTAT_SUCCESS; - num_posted++; - } - /* success, put on SCSI buffer sgl list */ - list_add_tail(&psb->list, &scsi_sblist); - } - } - - /* continue until a nembed page worth of sgls */ - if (post_cnt == 0) - continue; - - /* post block of SCSI buffer list sgls */ - status = lpfc_sli4_post_scsi_sgl_block(phba, &blck_sblist, - post_cnt); - - /* don't reset xirtag due to hole in xri block */ - if (block_cnt == 0) - last_xritag = NO_XRI; - - /* reset SCSI buffer post count for next round of posting */ - post_cnt = 0; - - /* put posted SCSI buffer-sgl posted on SCSI buffer sgl list */ - while (!list_empty(&blck_sblist)) { - list_remove_head(&blck_sblist, psb, - struct lpfc_scsi_buf, list); - if (status) { - /* failure, put on abort scsi list */ - psb->exch_busy = 1; - } else { - /* success, put on SCSI buffer list */ - psb->exch_busy = 0; - psb->status = IOSTAT_SUCCESS; - num_posted++; - } - list_add_tail(&psb->list, &scsi_sblist); - } - } - /* Push SCSI buffers with sgl posted to the availble list */ - while (!list_empty(&scsi_sblist)) { - list_remove_head(&scsi_sblist, psb, - struct lpfc_scsi_buf, list); - lpfc_release_scsi_buf_s4(phba, psb); - } - return num_posted; -} - -/** - * lpfc_sli4_repost_scsi_sgl_list - Repost all the allocated scsi buffer sgls - * @phba: pointer to lpfc hba data structure. - * - * This routine walks the list of scsi buffers that have been allocated and - * repost them to the port by using SGL block post. This is needed after a - * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine - * is responsible for moving all scsi buffers on the lpfc_abts_scsi_sgl_list - * to the lpfc_scsi_buf_list. If the repost fails, reject all scsi buffers. - * - * Returns: 0 = success, non-zero failure. - **/ -int -lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba) -{ - LIST_HEAD(post_sblist); - int num_posted, rc = 0; - - /* get all SCSI buffers need to repost to a local list */ - spin_lock_irq(&phba->scsi_buf_list_get_lock); - spin_lock(&phba->scsi_buf_list_put_lock); - list_splice_init(&phba->lpfc_scsi_buf_list_get, &post_sblist); - list_splice(&phba->lpfc_scsi_buf_list_put, &post_sblist); - spin_unlock(&phba->scsi_buf_list_put_lock); - spin_unlock_irq(&phba->scsi_buf_list_get_lock); - - /* post the list of scsi buffer sgls to port if available */ - if (!list_empty(&post_sblist)) { - num_posted = lpfc_sli4_post_scsi_sgl_list(phba, &post_sblist, - phba->sli4_hba.scsi_xri_cnt); - /* failed to post any scsi buffer, return error */ - if (num_posted == 0) - rc = -EIO; - } - return rc; -} - -/** - * lpfc_new_scsi_buf_s4 - Scsi buffer allocator for HBA with SLI4 IF spec - * @vport: The virtual port for which this call being executed. - * @num_to_allocate: The requested number of buffers to allocate. - * - * This routine allocates scsi buffers for device with SLI-4 interface spec, - * the scsi buffer contains all the necessary information needed to initiate - * a SCSI I/O. After allocating up to @num_to_allocate SCSI buffers and put - * them on a list, it post them to the port by using SGL block post. - * - * Return codes: - * int - number of scsi buffers that were allocated and posted. - * 0 = failure, less than num_to_alloc is a partial failure. - **/ -static int -lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) -{ - struct lpfc_hba *phba = vport->phba; - struct lpfc_scsi_buf *psb; - struct sli4_sge *sgl; - IOCB_t *iocb; - dma_addr_t pdma_phys_fcp_cmd; - dma_addr_t pdma_phys_fcp_rsp; - dma_addr_t pdma_phys_bpl; - uint16_t iotag, lxri = 0; - int bcnt, num_posted, sgl_size; - LIST_HEAD(prep_sblist); - LIST_HEAD(post_sblist); - LIST_HEAD(scsi_sblist); - - sgl_size = phba->cfg_sg_dma_buf_size - - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp)); - - lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, - "9068 ALLOC %d scsi_bufs: %d (%d + %d + %d)\n", - num_to_alloc, phba->cfg_sg_dma_buf_size, sgl_size, - (int)sizeof(struct fcp_cmnd), - (int)sizeof(struct fcp_rsp)); - - for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { - psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL); - if (!psb) - break; - /* - * Get memory from the pci pool to map the virt space to - * pci bus space for an I/O. The DMA buffer includes space - * for the struct fcp_cmnd, struct fcp_rsp and the number - * of bde's necessary to support the sg_tablesize. - */ - psb->data = dma_pool_zalloc(phba->lpfc_sg_dma_buf_pool, - GFP_KERNEL, &psb->dma_handle); - if (!psb->data) { - kfree(psb); - break; - } - - /* - * 4K Page alignment is CRITICAL to BlockGuard, double check - * to be sure. - */ - if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) && - (((unsigned long)(psb->data) & - (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) { - lpfc_printf_log(phba, KERN_ERR, LOG_FCP, - "3369 Memory alignment error " - "addr=%lx\n", - (unsigned long)psb->data); - dma_pool_free(phba->lpfc_sg_dma_buf_pool, - psb->data, psb->dma_handle); - kfree(psb); - break; - } - - - lxri = lpfc_sli4_next_xritag(phba); - if (lxri == NO_XRI) { - dma_pool_free(phba->lpfc_sg_dma_buf_pool, - psb->data, psb->dma_handle); - kfree(psb); - break; - } - - /* Allocate iotag for psb->cur_iocbq. */ - iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); - if (iotag == 0) { - dma_pool_free(phba->lpfc_sg_dma_buf_pool, - psb->data, psb->dma_handle); - kfree(psb); - lpfc_printf_log(phba, KERN_ERR, LOG_FCP, - "3368 Failed to allocate IOTAG for" - " XRI:0x%x\n", lxri); - lpfc_sli4_free_xri(phba, lxri); - break; - } - psb->cur_iocbq.sli4_lxritag = lxri; - psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; - psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP; - psb->fcp_bpl = psb->data; - psb->fcp_cmnd = (psb->data + sgl_size); - psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd + - sizeof(struct fcp_cmnd)); - - /* Initialize local short-hand pointers. */ - sgl = (struct sli4_sge *)psb->fcp_bpl; - pdma_phys_bpl = psb->dma_handle; - pdma_phys_fcp_cmd = (psb->dma_handle + sgl_size); - pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd); - - /* - * The first two bdes are the FCP_CMD and FCP_RSP. - * The balance are sg list bdes. Initialize the - * first two and leave the rest for queuecommand. - */ - sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd)); - sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd)); - sgl->word2 = le32_to_cpu(sgl->word2); - bf_set(lpfc_sli4_sge_last, sgl, 0); - sgl->word2 = cpu_to_le32(sgl->word2); - sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd)); - sgl++; - - /* Setup the physical region for the FCP RSP */ - sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp)); - sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp)); - sgl->word2 = le32_to_cpu(sgl->word2); - bf_set(lpfc_sli4_sge_last, sgl, 1); - sgl->word2 = cpu_to_le32(sgl->word2); - sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp)); - - /* - * Since the IOCB for the FCP I/O is built into this - * lpfc_scsi_buf, initialize it with all known data now. - */ - iocb = &psb->cur_iocbq.iocb; - iocb->un.fcpi64.bdl.ulpIoTag32 = 0; - iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64; - /* setting the BLP size to 2 * sizeof BDE may not be correct. - * We are setting the bpl to point to out sgl. An sgl's - * entries are 16 bytes, a bpl entries are 12 bytes. - */ - iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd); - iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd); - iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd); - iocb->ulpBdeCount = 1; - iocb->ulpLe = 1; - iocb->ulpClass = CLASS3; - psb->cur_iocbq.context1 = psb; - psb->dma_phys_bpl = pdma_phys_bpl; - - /* add the scsi buffer to a post list */ - list_add_tail(&psb->list, &post_sblist); - spin_lock_irq(&phba->scsi_buf_list_get_lock); - phba->sli4_hba.scsi_xri_cnt++; - spin_unlock_irq(&phba->scsi_buf_list_get_lock); - } - lpfc_printf_log(phba, KERN_INFO, LOG_BG | LOG_FCP, - "3021 Allocate %d out of %d requested new SCSI " - "buffers\n", bcnt, num_to_alloc); - - /* post the list of scsi buffer sgls to port if available */ - if (!list_empty(&post_sblist)) - num_posted = lpfc_sli4_post_scsi_sgl_list(phba, - &post_sblist, bcnt); - else - num_posted = 0; - - return num_posted; -} - -/** - * lpfc_new_scsi_buf - Wrapper funciton for scsi buffer allocator - * @vport: The virtual port for which this call being executed. - * @num_to_allocate: The requested number of buffers to allocate. - * - * This routine wraps the actual SCSI buffer allocator function pointer from - * the lpfc_hba struct. - * - * Return codes: - * int - number of scsi buffers that were allocated. - * 0 = failure, less than num_to_alloc is a partial failure. - **/ -static inline int -lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc) -{ - return vport->phba->lpfc_new_scsi_buf(vport, num_to_alloc); -} - /** * lpfc_get_scsi_buf_s3 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA * @phba: The HBA for which this call is being executed. @@ -1005,10 +652,10 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) return lpfc_cmd; } /** - * lpfc_get_scsi_buf_s4 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA + * lpfc_get_scsi_buf_s4 - Get a scsi buffer from lpfc_common_buf_list of the HBA * @phba: The HBA for which this call is being executed. * - * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list + * This routine removes a scsi buffer from head of @phba lpfc_common_buf_list * and returns to caller. * * Return codes: @@ -1020,38 +667,113 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { struct lpfc_scsi_buf *lpfc_cmd, *lpfc_cmd_next; unsigned long iflag = 0; + struct sli4_sge *sgl; + IOCB_t *iocb; + dma_addr_t pdma_phys_fcp_rsp; + dma_addr_t pdma_phys_fcp_cmd; + uint32_t sgl_size; int found = 0; - spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag); + spin_lock_irqsave(&phba->common_buf_list_get_lock, iflag); list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, - &phba->lpfc_scsi_buf_list_get, list) { + &phba->lpfc_common_buf_list_get, list) { if (lpfc_test_rrq_active(phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; list_del_init(&lpfc_cmd->list); + phba->get_common_bufs--; found = 1; break; } if (!found) { - spin_lock(&phba->scsi_buf_list_put_lock); - list_splice(&phba->lpfc_scsi_buf_list_put, - &phba->lpfc_scsi_buf_list_get); - INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - spin_unlock(&phba->scsi_buf_list_put_lock); + spin_lock(&phba->common_buf_list_put_lock); + list_splice(&phba->lpfc_common_buf_list_put, + &phba->lpfc_common_buf_list_get); + phba->get_common_bufs += phba->put_common_bufs; + INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); + phba->put_common_bufs = 0; + spin_unlock(&phba->common_buf_list_put_lock); list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, - &phba->lpfc_scsi_buf_list_get, list) { + &phba->lpfc_common_buf_list_get, + list) { if (lpfc_test_rrq_active( phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; list_del_init(&lpfc_cmd->list); + phba->get_common_bufs--; found = 1; break; } } - spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag); + spin_unlock_irqrestore(&phba->common_buf_list_get_lock, iflag); if (!found) return NULL; + sgl_size = phba->cfg_sg_dma_buf_size - + (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp)); + + /* Setup key fields in buffer that may have been changed + * if other protocols used this buffer. + */ + lpfc_cmd->cur_iocbq.iocb_flag = LPFC_IO_FCP; + lpfc_cmd->prot_seg_cnt = 0; + lpfc_cmd->seg_cnt = 0; + lpfc_cmd->waitq = NULL; + lpfc_cmd->timeout = 0; + lpfc_cmd->flags = 0; + lpfc_cmd->start_time = jiffies; + lpfc_cmd->waitq = NULL; + lpfc_cmd->cpu = smp_processor_id(); +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + lpfc_cmd->prot_data_type = 0; +#endif + + lpfc_cmd->fcp_cmnd = (lpfc_cmd->data + sgl_size); + lpfc_cmd->fcp_rsp = (struct fcp_rsp *)((uint8_t *)lpfc_cmd->fcp_cmnd + + sizeof(struct fcp_cmnd)); + + /* + * The first two SGEs are the FCP_CMD and FCP_RSP. + * The balance are sg list bdes. Initialize the + * first two and leave the rest for queuecommand. + */ + sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; + pdma_phys_fcp_cmd = (lpfc_cmd->dma_handle + sgl_size); + sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd)); + sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd)); + sgl->word2 = le32_to_cpu(sgl->word2); + bf_set(lpfc_sli4_sge_last, sgl, 0); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd)); + sgl++; + + /* Setup the physical region for the FCP RSP */ + pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd); + sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp)); + sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp)); + sgl->word2 = le32_to_cpu(sgl->word2); + bf_set(lpfc_sli4_sge_last, sgl, 1); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp)); + + /* + * Since the IOCB for the FCP I/O is built into this + * lpfc_scsi_buf, initialize it with all known data now. + */ + iocb = &lpfc_cmd->cur_iocbq.iocb; + iocb->un.fcpi64.bdl.ulpIoTag32 = 0; + iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64; + /* setting the BLP size to 2 * sizeof BDE may not be correct. + * We are setting the bpl to point to out sgl. An sgl's + * entries are 16 bytes, a bpl entries are 12 bytes. + */ + iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd); + iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd); + iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd); + iocb->ulpBdeCount = 1; + iocb->ulpLe = 1; + iocb->ulpClass = CLASS3; + if (lpfc_ndlp_check_qdepth(phba, ndlp)) { atomic_inc(&ndlp->cmd_pending); lpfc_cmd->flags |= LPFC_SBUF_BUMP_QDEPTH; @@ -1089,7 +811,6 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) unsigned long iflag = 0; psb->seg_cnt = 0; - psb->nonsg_phys = 0; psb->prot_seg_cnt = 0; spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag); @@ -1105,7 +826,7 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) * @psb: The scsi buffer which is being released. * * This routine releases @psb scsi buffer by adding it to tail of @phba - * lpfc_scsi_buf_list list. For SLI4 XRI's are tied to the scsi buffer + * lpfc_common_buf_list list. For SLI4 XRI's are tied to the scsi buffer * and cannot be reused for at least RA_TOV amount of time if it was * aborted. **/ @@ -1115,7 +836,6 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) unsigned long iflag = 0; psb->seg_cnt = 0; - psb->nonsg_phys = 0; psb->prot_seg_cnt = 0; if (psb->exch_busy) { @@ -1127,11 +847,13 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock, iflag); } else { + /* MUST zero fields if buffer is reused by another protocol */ psb->pCmd = NULL; - psb->cur_iocbq.iocb_flag = LPFC_IO_FCP; - spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag); - list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list_put); - spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag); + psb->cur_iocbq.iocb_cmpl = NULL; + spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); + list_add_tail(&psb->list, &phba->lpfc_common_buf_list_put); + phba->put_common_bufs++; + spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); } } @@ -1173,7 +895,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct scatterlist *sgel = NULL; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; - struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl; + struct ulp_bde64 *bpl = lpfc_cmd->dma_sgl; struct lpfc_iocbq *iocbq = &lpfc_cmd->cur_iocbq; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; struct ulp_bde64 *data_bde = iocb_cmd->unsli3.fcp_ext.dbde; @@ -2728,7 +2450,7 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; - struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl; + struct ulp_bde64 *bpl = lpfc_cmd->dma_sgl; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; uint32_t num_bde = 0; int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction; @@ -3261,7 +2983,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct scatterlist *sgel = NULL; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; - struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl; + struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; struct sli4_sge *first_data_sgl; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; dma_addr_t physaddr; @@ -3406,7 +3128,7 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; - struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->fcp_bpl); + struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->dma_sgl); IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; uint32_t num_sge = 0; int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction; @@ -3941,7 +3663,7 @@ int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU && phba->cfg_fcp_io_channel > 1) { - cpu = smp_processor_id(); + cpu = lpfc_cmd->cpu; if (cpu < phba->sli4_hba.num_present_cpu) { cpup = phba->sli4_hba.cpu_map; cpup += cpu; @@ -4413,14 +4135,12 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) switch (dev_grp) { case LPFC_PCI_DEV_LP: - phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3; phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3; phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s3; phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3; phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s3; break; case LPFC_PCI_DEV_OC: - phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4; phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4; phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s4; phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4; @@ -4735,8 +4455,6 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) lpfc_cmd->pCmd = cmnd; lpfc_cmd->rdata = rdata; lpfc_cmd->ndlp = ndlp; - lpfc_cmd->timeout = 0; - lpfc_cmd->start_time = jiffies; cmnd->host_scribble = (unsigned char *)lpfc_cmd; if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) { @@ -5671,6 +5389,12 @@ lpfc_slave_alloc(struct scsi_device *sdev) } sdev_cnt = atomic_inc_return(&phba->sdev_cnt); + /* For SLI4, all IO buffers are pre-allocated */ + if (phba->sli_rev == LPFC_SLI_REV4) + return 0; + + /* This code path is now ONLY for SLI3 adapters */ + /* * Populate the cmds_per_lun count scsi_bufs into this host's globally * available list of scsi buffers. Don't allocate more than the @@ -5702,7 +5426,7 @@ lpfc_slave_alloc(struct scsi_device *sdev) (phba->cfg_hba_queue_depth - total)); num_to_alloc = phba->cfg_hba_queue_depth - total; } - num_allocated = lpfc_new_scsi_buf(vport, num_to_alloc); + num_allocated = lpfc_new_scsi_buf_s3(vport, num_to_alloc); if (num_to_alloc != num_allocated) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0708 Allocation request of %d " diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index b759b089432c..d784de3da0c0 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -131,7 +131,15 @@ struct lpfc_scsicmd_bkt { }; struct lpfc_scsi_buf { + /* Common fields */ struct list_head list; + void *data; + dma_addr_t dma_handle; + dma_addr_t dma_phys_sgl; + struct ulp_bde64 *dma_sgl; + struct lpfc_iocbq cur_iocbq; + + /* SCSI specific fields */ struct scsi_cmnd *pCmd; struct lpfc_rport_data *rdata; struct lpfc_nodelist *ndlp; @@ -139,9 +147,10 @@ struct lpfc_scsi_buf { uint32_t timeout; uint16_t flags; /* TBD convert exch_busy to flags */ -#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ -#define LPFC_SBUF_BUMP_QDEPTH 0x8 /* bumped queue depth counter */ +#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ +#define LPFC_SBUF_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ + uint16_t cpu; uint16_t status; /* From IOCB Word 7- ulpStatus */ uint32_t result; /* From IOCB Word 4. */ @@ -150,27 +159,13 @@ struct lpfc_scsi_buf { * to dma_unmap_sg. */ uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */ - dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */ - /* * data and dma_handle are the kernel virtual and bus address of the * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter * gather bde list that supports the sg_tablesize value. */ - void *data; - dma_addr_t dma_handle; - struct fcp_cmnd *fcp_cmnd; struct fcp_rsp *fcp_rsp; - struct ulp_bde64 *fcp_bpl; - - dma_addr_t dma_phys_bpl; - - /* cur_iocbq has phys of the dma-able buffer. - * Iotag is in here - */ - struct lpfc_iocbq cur_iocbq; - uint16_t cpu; wait_queue_head_t *waitq; unsigned long start_time; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 30734caf77e1..a450d845ec22 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -6027,11 +6027,8 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) list_add_tail(&rsrc_blks->list, ext_blk_list); rsrc_start = rsrc_id; if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) { - phba->sli4_hba.scsi_xri_start = rsrc_start + + phba->sli4_hba.common_xri_start = rsrc_start + lpfc_sli4_get_iocb_cnt(phba); - phba->sli4_hba.nvme_xri_start = - phba->sli4_hba.scsi_xri_start + - phba->sli4_hba.scsi_xri_max; } while (rsrc_id < (rsrc_start + rsrc_size)) { @@ -7057,6 +7054,45 @@ lpfc_sli4_repost_sgl_list(struct lpfc_hba *phba, return total_cnt; } +/** + * lpfc_sli4_repost_common_sgl_list - Repost all the allocated nvme buffer sgls + * @phba: pointer to lpfc hba data structure. + * + * This routine walks the list of nvme buffers that have been allocated and + * repost them to the port by using SGL block post. This is needed after a + * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine + * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list + * to the lpfc_common_buf_list. If the repost fails, reject all nvme buffers. + * + * Returns: 0 = success, non-zero failure. + **/ +int +lpfc_sli4_repost_common_sgl_list(struct lpfc_hba *phba) +{ + LIST_HEAD(post_nblist); + int num_posted, rc = 0; + + /* get all NVME buffers need to repost to a local list */ + spin_lock_irq(&phba->common_buf_list_get_lock); + spin_lock(&phba->common_buf_list_put_lock); + list_splice_init(&phba->lpfc_common_buf_list_get, &post_nblist); + list_splice(&phba->lpfc_common_buf_list_put, &post_nblist); + phba->get_common_bufs = 0; + phba->put_common_bufs = 0; + spin_unlock(&phba->common_buf_list_put_lock); + spin_unlock_irq(&phba->common_buf_list_get_lock); + + /* post the list of nvme buffer sgls to port if available */ + if (!list_empty(&post_nblist)) { + num_posted = lpfc_sli4_post_common_sgl_list( + phba, &post_nblist, phba->sli4_hba.common_xri_cnt); + /* failed to post any nvme buffer, return error */ + if (num_posted == 0) + rc = -EIO; + } + return rc; +} + void lpfc_set_host_data(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) { @@ -7518,17 +7554,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) /* We need 1 iocbq for every SGL, for IO processing */ cnt += phba->sli4_hba.nvmet_xri_cnt; } else { - /* update host scsi xri-sgl sizes and mappings */ - rc = lpfc_sli4_scsi_sgl_update(phba); - if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "6309 Failed to update scsi-sgl size " - "and mapping: %d\n", rc); - goto out_destroy_queue; - } - - /* update host nvme xri-sgl sizes and mappings */ - rc = lpfc_sli4_nvme_sgl_update(phba); + /* update host common xri-sgl sizes and mappings */ + rc = lpfc_sli4_common_sgl_update(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "6082 Failed to update nvme-sgl size " @@ -7536,6 +7563,17 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) goto out_destroy_queue; } + /* register the allocated common sgl pool to the port */ + rc = lpfc_sli4_repost_common_sgl_list(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "6116 Error %d during nvme sgl post " + "operation\n", rc); + /* Some NVME buffers were moved to abort nvme list */ + /* A pci function reset will repost them */ + rc = -ENODEV; + goto out_destroy_queue; + } cnt = phba->cfg_iocb_cnt * 1024; } @@ -7572,36 +7610,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) } } - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { - /* register the allocated scsi sgl pool to the port */ - rc = lpfc_sli4_repost_scsi_sgl_list(phba); - if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "0383 Error %d during scsi sgl post " - "operation\n", rc); - /* Some Scsi buffers were moved to abort scsi list */ - /* A pci function reset will repost them */ - rc = -ENODEV; - goto out_destroy_queue; - } - } - - if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && - (phba->nvmet_support == 0)) { - - /* register the allocated nvme sgl pool to the port */ - rc = lpfc_repost_nvme_sgl_list(phba); - if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "6116 Error %d during nvme sgl post " - "operation\n", rc); - /* Some NVME buffers were moved to abort nvme list */ - /* A pci function reset will repost them */ - rc = -ENODEV; - goto out_destroy_queue; - } - } - /* Post the rpi header region to the device. */ rc = lpfc_sli4_post_all_rpi_hdrs(phba); if (unlikely(rc)) { @@ -9484,7 +9492,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, /* 128 byte wqe support here */ lpfc_cmd = iocbq->context1; - sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl; + sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; fcp_cmnd = lpfc_cmd->fcp_cmnd; /* Word 0-2 - FCP_CMND */ @@ -9548,7 +9556,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, /* 128 byte wqe support here */ lpfc_cmd = iocbq->context1; - sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl; + sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; fcp_cmnd = lpfc_cmd->fcp_cmnd; /* Word 0-2 - FCP_CMND */ @@ -9605,7 +9613,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, /* 128 byte wqe support here */ lpfc_cmd = iocbq->context1; - sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl; + sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; fcp_cmnd = lpfc_cmd->fcp_cmnd; /* Word 0-2 - FCP_CMND */ @@ -16827,22 +16835,22 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba, } /** - * lpfc_sli4_post_scsi_sgl_block - post a block of scsi sgl list to firmware + * lpfc_sli4_post_common_sgl_block - post a block of nvme sgl list to firmware * @phba: pointer to lpfc hba data structure. - * @sblist: pointer to scsi buffer list. + * @nblist: pointer to nvme buffer list. * @count: number of scsi buffers on the list. * * This routine is invoked to post a block of @count scsi sgl pages from a - * SCSI buffer list @sblist to the HBA using non-embedded mailbox command. + * SCSI buffer list @nblist to the HBA using non-embedded mailbox command. * No Lock is held. * **/ -int -lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, - struct list_head *sblist, - int count) +static int +lpfc_sli4_post_common_sgl_block(struct lpfc_hba *phba, + struct list_head *nblist, + int count) { - struct lpfc_scsi_buf *psb; + struct lpfc_nvme_buf *lpfc_ncmd; struct lpfc_mbx_post_uembed_sgl_page1 *sgl; struct sgl_page_pairs *sgl_pg_pairs; void *viraddr; @@ -16860,25 +16868,25 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); if (reqlen > SLI4_PAGE_SIZE) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, - "0217 Block sgl registration required DMA " + "6118 Block sgl registration required DMA " "size (%d) great than a page\n", reqlen); return -ENOMEM; } mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0283 Failed to allocate mbox cmd memory\n"); + "6119 Failed to allocate mbox cmd memory\n"); return -ENOMEM; } /* Allocate DMA memory and set up the non-embedded mailbox command */ alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, - LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen, - LPFC_SLI4_MBX_NEMBED); + LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, + reqlen, LPFC_SLI4_MBX_NEMBED); if (alloclen < reqlen) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2561 Allocated DMA memory size (%d) is " + "6120 Allocated DMA memory size (%d) is " "less than the requested DMA memory " "size (%d)\n", alloclen, reqlen); lpfc_sli4_mbox_cmd_free(phba, mbox); @@ -16893,14 +16901,15 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, sgl_pg_pairs = &sgl->sgl_pg_pairs; pg_pairs = 0; - list_for_each_entry(psb, sblist, list) { + list_for_each_entry(lpfc_ncmd, nblist, list) { /* Set up the sge entry */ sgl_pg_pairs->sgl_pg0_addr_lo = - cpu_to_le32(putPaddrLow(psb->dma_phys_bpl)); + cpu_to_le32(putPaddrLow(lpfc_ncmd->dma_phys_sgl)); sgl_pg_pairs->sgl_pg0_addr_hi = - cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl)); + cpu_to_le32(putPaddrHigh(lpfc_ncmd->dma_phys_sgl)); if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE) - pdma_phys_bpl1 = psb->dma_phys_bpl + SGL_PAGE_SIZE; + pdma_phys_bpl1 = lpfc_ncmd->dma_phys_sgl + + SGL_PAGE_SIZE; else pdma_phys_bpl1 = 0; sgl_pg_pairs->sgl_pg1_addr_lo = @@ -16909,7 +16918,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, cpu_to_le32(putPaddrHigh(pdma_phys_bpl1)); /* Keep the first xritag on the list */ if (pg_pairs == 0) - xritag_start = psb->cur_iocbq.sli4_xritag; + xritag_start = lpfc_ncmd->cur_iocbq.sli4_xritag; sgl_pg_pairs++; pg_pairs++; } @@ -16918,20 +16927,20 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, /* Perform endian conversion if necessary */ sgl->word0 = cpu_to_le32(sgl->word0); - if (!phba->sli4_hba.intr_enable) + if (!phba->sli4_hba.intr_enable) { rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); - else { + } else { mbox_tmo = lpfc_mbox_tmo_val(phba, mbox); rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); } - shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr; + shdr = (union lpfc_sli4_cfg_shdr *)&sgl->cfg_shdr; shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); if (rc != MBX_TIMEOUT) lpfc_sli4_mbox_cmd_free(phba, mbox); if (shdr_status || shdr_add_status || rc) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "2564 POST_SGL_BLOCK mailbox command failed " + "6125 POST_SGL_BLOCK mailbox command failed " "status x%x add_status x%x mbx status x%x\n", shdr_status, shdr_add_status, rc); rc = -ENXIO; @@ -16939,6 +16948,142 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, return rc; } +/** + * lpfc_sli4_post_common_sgl_list - Post blocks of nvme buffer sgls from a list + * @phba: pointer to lpfc hba data structure. + * @post_nblist: pointer to the nvme buffer list. + * + * This routine walks a list of nvme buffers that was passed in. It attempts + * to construct blocks of nvme buffer sgls which contains contiguous xris and + * uses the non-embedded SGL block post mailbox commands to post to the port. + * For single NVME buffer sgl with non-contiguous xri, if any, it shall use + * embedded SGL post mailbox command for posting. The @post_nblist passed in + * must be local list, thus no lock is needed when manipulate the list. + * + * Returns: 0 = failure, non-zero number of successfully posted buffers. + **/ +int +lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, + struct list_head *post_nblist, int sb_count) +{ + struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + int status, sgl_size; + int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0; + dma_addr_t pdma_phys_sgl1; + int last_xritag = NO_XRI; + int cur_xritag; + unsigned long iflag; + LIST_HEAD(prep_nblist); + LIST_HEAD(blck_nblist); + LIST_HEAD(nvme_nblist); + + /* sanity check */ + if (sb_count <= 0) + return -EINVAL; + + sgl_size = phba->cfg_sg_dma_buf_size; + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, post_nblist, list) { + list_del_init(&lpfc_ncmd->list); + block_cnt++; + if ((last_xritag != NO_XRI) && + (lpfc_ncmd->cur_iocbq.sli4_xritag != last_xritag + 1)) { + /* a hole in xri block, form a sgl posting block */ + list_splice_init(&prep_nblist, &blck_nblist); + post_cnt = block_cnt - 1; + /* prepare list for next posting block */ + list_add_tail(&lpfc_ncmd->list, &prep_nblist); + block_cnt = 1; + } else { + /* prepare list for next posting block */ + list_add_tail(&lpfc_ncmd->list, &prep_nblist); + /* enough sgls for non-embed sgl mbox command */ + if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) { + list_splice_init(&prep_nblist, &blck_nblist); + post_cnt = block_cnt; + block_cnt = 0; + } + } + num_posting++; + last_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag; + + /* end of repost sgl list condition for NVME buffers */ + if (num_posting == sb_count) { + if (post_cnt == 0) { + /* last sgl posting block */ + list_splice_init(&prep_nblist, &blck_nblist); + post_cnt = block_cnt; + } else if (block_cnt == 1) { + /* last single sgl with non-contiguous xri */ + if (sgl_size > SGL_PAGE_SIZE) + pdma_phys_sgl1 = + lpfc_ncmd->dma_phys_sgl + + SGL_PAGE_SIZE; + else + pdma_phys_sgl1 = 0; + cur_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag; + status = lpfc_sli4_post_sgl( + phba, lpfc_ncmd->dma_phys_sgl, + pdma_phys_sgl1, cur_xritag); + if (status) { + /* failure, put on abort nvme list */ + lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; + } else { + /* success, put on NVME buffer list */ + lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; + lpfc_ncmd->status = IOSTAT_SUCCESS; + num_posted++; + } + /* success, put on NVME buffer sgl list */ + list_add_tail(&lpfc_ncmd->list, &nvme_nblist); + } + } + + /* continue until a nembed page worth of sgls */ + if (post_cnt == 0) + continue; + + /* post block of NVME buffer list sgls */ + status = lpfc_sli4_post_common_sgl_block(phba, &blck_nblist, + post_cnt); + + /* don't reset xirtag due to hole in xri block */ + if (block_cnt == 0) + last_xritag = NO_XRI; + + /* reset NVME buffer post count for next round of posting */ + post_cnt = 0; + + /* put posted NVME buffer-sgl posted on NVME buffer sgl list */ + while (!list_empty(&blck_nblist)) { + list_remove_head(&blck_nblist, lpfc_ncmd, + struct lpfc_nvme_buf, list); + if (status) { + /* failure, put on abort nvme list */ + lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; + } else { + /* success, put on NVME buffer list */ + lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; + lpfc_ncmd->status = IOSTAT_SUCCESS; + num_posted++; + } + list_add_tail(&lpfc_ncmd->list, &nvme_nblist); + } + } + /* Push NVME buffers with sgl posted to the available list */ + while (!list_empty(&nvme_nblist)) { + list_remove_head(&nvme_nblist, lpfc_ncmd, + struct lpfc_nvme_buf, list); + lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL; + lpfc_ncmd->cur_iocbq.iocb_cmpl = NULL; + spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); + list_add_tail(&lpfc_ncmd->list, + &phba->lpfc_common_buf_list_put); + phba->put_common_bufs++; + spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); + } + return num_posted; +} + /** * lpfc_fc_frame_check - Check that this frame is a valid frame to handle * @phba: pointer to lpfc_hba struct that the frame was received on diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 6b2d2350e2c6..7048df6c92aa 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -41,6 +41,9 @@ #define LPFC_FCP_IO_CHAN_DEF 4 #define LPFC_NVME_IO_CHAN_DEF 0 +/* Common buffer size to accomidate SCSI and NVME IO buffers */ +#define LPFC_COMMON_IO_BUF_SZ 768 + /* Number of channels used for Flash Optimized Fabric (FOF) operations */ #define LPFC_FOF_IO_CHAN_NUM 1 @@ -663,12 +666,9 @@ struct lpfc_sli4_hba { uint16_t rpi_hdrs_in_use; /* must post rpi hdrs if set. */ uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */ uint16_t next_rpi; - uint16_t nvme_xri_max; - uint16_t nvme_xri_cnt; - uint16_t nvme_xri_start; - uint16_t scsi_xri_max; - uint16_t scsi_xri_cnt; - uint16_t scsi_xri_start; + uint16_t common_xri_max; + uint16_t common_xri_cnt; + uint16_t common_xri_start; uint16_t els_xri_cnt; uint16_t nvmet_xri_cnt; uint16_t nvmet_io_wait_cnt; @@ -843,12 +843,10 @@ int lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *, int lpfc_sli4_queue_setup(struct lpfc_hba *); void lpfc_sli4_queue_unset(struct lpfc_hba *); int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t); -int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *); -int lpfc_repost_nvme_sgl_list(struct lpfc_hba *phba); +int lpfc_repost_common_sgl_list(struct lpfc_hba *phba); uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *); void lpfc_sli4_free_xri(struct lpfc_hba *, int); int lpfc_sli4_post_async_mbox(struct lpfc_hba *); -int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int); struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *); struct lpfc_cq_event *lpfc_sli4_cq_event_alloc(struct lpfc_hba *); void __lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *); From 7370d10ac99e8ebc5501c0fcdec482cb939ecbd4 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:20 -0800 Subject: [PATCH 1205/4277] scsi: lpfc: Remove extra vector and SLI4 queue for Expresslane There is a extra queue and msix vector for expresslane. Now that the driver will be doing queues per cpu, this oddball queue is no longer needed. Expresslane will utilize the normal per-cpu queues. Updated debugfs sli4 queue output to go along with the change Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_crtn.h | 5 - drivers/scsi/lpfc/lpfc_debugfs.c | 36 +---- drivers/scsi/lpfc/lpfc_init.c | 225 ++----------------------------- drivers/scsi/lpfc/lpfc_scsi.c | 9 +- drivers/scsi/lpfc/lpfc_sli.c | 210 ++--------------------------- drivers/scsi/lpfc/lpfc_sli4.h | 6 - 6 files changed, 24 insertions(+), 467 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 0e49004ceed1..6dc427d4228c 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -199,11 +199,6 @@ void lpfc_reset_hba(struct lpfc_hba *); int lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *hd, spinlock_t *slock); -int lpfc_fof_queue_create(struct lpfc_hba *); -int lpfc_fof_queue_setup(struct lpfc_hba *); -int lpfc_fof_queue_destroy(struct lpfc_hba *); -irqreturn_t lpfc_sli4_fof_intr_handler(int, void *); - int lpfc_sli_setup(struct lpfc_hba *); int lpfc_sli4_setup(struct lpfc_hba *phba); void lpfc_sli_queue_init(struct lpfc_hba *phba); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index a851dee09587..355857e1ef7d 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -3390,14 +3390,9 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, if (phba->sli4_hba.hba_eq && phba->io_channel_irqs) { x = phba->lpfc_idiag_last_eq; - if (phba->cfg_fof && (x >= phba->io_channel_irqs)) { - phba->lpfc_idiag_last_eq = 0; - goto fof; - } phba->lpfc_idiag_last_eq++; if (phba->lpfc_idiag_last_eq >= phba->io_channel_irqs) - if (phba->cfg_fof == 0) - phba->lpfc_idiag_last_eq = 0; + phba->lpfc_idiag_last_eq = 0; len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "EQ %d out of %d HBA EQs\n", @@ -3479,35 +3474,6 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, goto out; } -fof: - if (phba->cfg_fof) { - /* FOF EQ */ - qp = phba->sli4_hba.fof_eq; - len = __lpfc_idiag_print_eq(qp, "FOF", pbuffer, len); - - /* Reset max counter */ - if (qp) - qp->EQ_max_eqe = 0; - - if (len >= max_cnt) - goto too_big; - - /* OAS CQ */ - qp = phba->sli4_hba.oas_cq; - len = __lpfc_idiag_print_cq(qp, "OAS", pbuffer, len); - /* Reset max counter */ - if (qp) - qp->CQ_max_cqe = 0; - if (len >= max_cnt) - goto too_big; - - /* OAS WQ */ - qp = phba->sli4_hba.oas_wq; - len = __lpfc_idiag_print_wq(qp, "OAS", pbuffer, len); - if (len >= max_cnt) - goto too_big; - } - spin_unlock_irq(&phba->hbalock); return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 149f3182f41e..9d9b965f796d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -6059,7 +6059,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0}; struct lpfc_mqe *mqe; int longs; - int fof_vectors = 0; int extra; uint64_t wwn; u32 if_type; @@ -6433,8 +6432,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* Verify OAS is supported */ lpfc_sli4_oas_verify(phba); - if (phba->cfg_fof) - fof_vectors = 1; /* Verify RAS support on adapter */ lpfc_sli4_ras_init(phba); @@ -6478,7 +6475,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_remove_rpi_hdrs; } - phba->sli4_hba.hba_eq_hdl = kcalloc(fof_vectors + phba->io_channel_irqs, + phba->sli4_hba.hba_eq_hdl = kcalloc(phba->io_channel_irqs, sizeof(struct lpfc_hba_eq_hdl), GFP_KERNEL); if (!phba->sli4_hba.hba_eq_hdl) { @@ -8048,7 +8045,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) /* * Whats left after this can go toward NVME. * The minus 6 accounts for ELS, NVME LS, MBOX - * fof plus a couple extra. When configured for + * plus a couple extra. When configured for * NVMET, FCP io channel WQs are not created. */ length -= 6; @@ -8280,7 +8277,6 @@ static int lpfc_sli4_queue_verify(struct lpfc_hba *phba) { int io_channel; - int fof_vectors = phba->cfg_fof ? 1 : 0; /* * Sanity check for configured queue parameters against the run-time @@ -8299,13 +8295,13 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) io_channel = phba->sli4_hba.num_online_cpu; } - if (io_channel + fof_vectors > phba->sli4_hba.max_cfg_param.max_eq) { + if (io_channel > phba->sli4_hba.max_cfg_param.max_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2575 Reducing IO channels to match number of " "available EQs: from %d to %d\n", io_channel, phba->sli4_hba.max_cfg_param.max_eq); - io_channel = phba->sli4_hba.max_cfg_param.max_eq - fof_vectors; + io_channel = phba->sli4_hba.max_cfg_param.max_eq; } /* The actual number of FCP / NVME event queues adopted */ @@ -8769,10 +8765,6 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) phba->sli4_hba.nvmet_mrq_data[idx] = qdesc; } } - - /* Create the Queues needed for Flash Optimized Fabric operations */ - if (phba->cfg_fof) - lpfc_fof_queue_create(phba); return 0; out_error: @@ -8828,9 +8820,6 @@ lpfc_sli4_release_queue_map(uint16_t **qmap) void lpfc_sli4_queue_destroy(struct lpfc_hba *phba) { - if (phba->cfg_fof) - lpfc_fof_queue_destroy(phba); - /* Release HBA eqs */ lpfc_sli4_release_queues(&phba->sli4_hba.hba_eq, phba->io_channel_irqs); @@ -9331,16 +9320,6 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.dat_rq->queue_id, phba->sli4_hba.els_cq->queue_id); - if (phba->cfg_fof) { - rc = lpfc_fof_queue_setup(phba); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0549 Failed setup of FOF Queues: " - "rc = 0x%x\n", rc); - goto out_destroy; - } - } - for (qidx = 0; qidx < io_channel; qidx += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT, phba->cfg_fcp_imax); @@ -9370,10 +9349,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) { int qidx; - /* Unset the queues created for Flash Optimized Fabric operations */ - if (phba->cfg_fof) - lpfc_fof_queue_destroy(phba); - /* Unset mailbox command work queue */ if (phba->sli4_hba.mbx_wq) lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq); @@ -10297,8 +10272,6 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) /* Set up MSI-X multi-message vectors */ vectors = phba->io_channel_irqs; - if (phba->cfg_fof) - vectors++; rc = pci_alloc_irq_vectors(phba->pcidev, (phba->nvmet_support) ? 1 : 2, @@ -10320,16 +10293,10 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) phba->sli4_hba.hba_eq_hdl[index].idx = index; phba->sli4_hba.hba_eq_hdl[index].phba = phba; atomic_set(&phba->sli4_hba.hba_eq_hdl[index].hba_eq_in_use, 1); - if (phba->cfg_fof && (index == (vectors - 1))) - rc = request_irq(pci_irq_vector(phba->pcidev, index), - &lpfc_sli4_fof_intr_handler, 0, - name, - &phba->sli4_hba.hba_eq_hdl[index]); - else - rc = request_irq(pci_irq_vector(phba->pcidev, index), - &lpfc_sli4_hba_intr_handler, 0, - name, - &phba->sli4_hba.hba_eq_hdl[index]); + rc = request_irq(pci_irq_vector(phba->pcidev, index), + &lpfc_sli4_hba_intr_handler, 0, + name, + &phba->sli4_hba.hba_eq_hdl[index]); if (rc) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0486 MSI-X fast-path (%d) " @@ -10338,9 +10305,6 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) } } - if (phba->cfg_fof) - vectors--; - if (vectors != phba->io_channel_irqs) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3238 Reducing IO channels to match number of " @@ -10415,10 +10379,6 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba) phba->sli4_hba.hba_eq_hdl[index].phba = phba; } - if (phba->cfg_fof) { - phba->sli4_hba.hba_eq_hdl[index].idx = index; - phba->sli4_hba.hba_eq_hdl[index].phba = phba; - } return 0; } @@ -10485,12 +10445,6 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) eqhdl->phba = phba; atomic_set(&eqhdl->hba_eq_in_use, 1); } - if (phba->cfg_fof) { - eqhdl = &phba->sli4_hba.hba_eq_hdl[idx]; - eqhdl->idx = idx; - eqhdl->phba = phba; - atomic_set(&eqhdl->hba_eq_in_use, 1); - } } } return intr_mode; @@ -10516,10 +10470,6 @@ lpfc_sli4_disable_intr(struct lpfc_hba *phba) for (index = 0; index < phba->io_channel_irqs; index++) free_irq(pci_irq_vector(phba->pcidev, index), &phba->sli4_hba.hba_eq_hdl[index]); - - if (phba->cfg_fof) - free_irq(pci_irq_vector(phba->pcidev, index), - &phba->sli4_hba.hba_eq_hdl[index]); } else { free_irq(phba->pcidev->irq, phba); } @@ -12692,165 +12642,6 @@ lpfc_sli4_ras_init(struct lpfc_hba *phba) } } -/** - * lpfc_fof_queue_setup - Set up all the fof queues - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to set up all the fof queues for the FC HBA - * operation. - * - * Return codes - * 0 - successful - * -ENOMEM - No available memory - **/ -int -lpfc_fof_queue_setup(struct lpfc_hba *phba) -{ - struct lpfc_sli_ring *pring; - int rc; - - rc = lpfc_eq_create(phba, phba->sli4_hba.fof_eq, LPFC_MAX_IMAX); - if (rc) - return -ENOMEM; - - if (phba->cfg_fof) { - - rc = lpfc_cq_create(phba, phba->sli4_hba.oas_cq, - phba->sli4_hba.fof_eq, LPFC_WCQ, LPFC_FCP); - if (rc) - goto out_oas_cq; - - rc = lpfc_wq_create(phba, phba->sli4_hba.oas_wq, - phba->sli4_hba.oas_cq, LPFC_FCP); - if (rc) - goto out_oas_wq; - - /* Bind this CQ/WQ to the NVME ring */ - pring = phba->sli4_hba.oas_wq->pring; - pring->sli.sli4.wqp = - (void *)phba->sli4_hba.oas_wq; - phba->sli4_hba.oas_cq->pring = pring; - } - - return 0; - -out_oas_wq: - lpfc_cq_destroy(phba, phba->sli4_hba.oas_cq); -out_oas_cq: - lpfc_eq_destroy(phba, phba->sli4_hba.fof_eq); - return rc; - -} - -/** - * lpfc_fof_queue_create - Create all the fof queues - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to allocate all the fof queues for the FC HBA - * operation. For each SLI4 queue type, the parameters such as queue entry - * count (queue depth) shall be taken from the module parameter. For now, - * we just use some constant number as place holder. - * - * Return codes - * 0 - successful - * -ENOMEM - No availble memory - * -EIO - The mailbox failed to complete successfully. - **/ -int -lpfc_fof_queue_create(struct lpfc_hba *phba) -{ - struct lpfc_queue *qdesc; - uint32_t wqesize; - - /* Create FOF EQ */ - qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, - phba->sli4_hba.eq_esize, - phba->sli4_hba.eq_ecount); - if (!qdesc) - goto out_error; - - qdesc->qe_valid = 1; - phba->sli4_hba.fof_eq = qdesc; - - if (phba->cfg_fof) { - - /* Create OAS CQ */ - if (phba->enab_exp_wqcq_pages) - qdesc = lpfc_sli4_queue_alloc(phba, - LPFC_EXPANDED_PAGE_SIZE, - phba->sli4_hba.cq_esize, - LPFC_CQE_EXP_COUNT); - else - qdesc = lpfc_sli4_queue_alloc(phba, - LPFC_DEFAULT_PAGE_SIZE, - phba->sli4_hba.cq_esize, - phba->sli4_hba.cq_ecount); - if (!qdesc) - goto out_error; - - qdesc->qe_valid = 1; - phba->sli4_hba.oas_cq = qdesc; - - /* Create OAS WQ */ - if (phba->enab_exp_wqcq_pages) { - wqesize = (phba->fcp_embed_io) ? - LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize; - qdesc = lpfc_sli4_queue_alloc(phba, - LPFC_EXPANDED_PAGE_SIZE, - wqesize, - LPFC_WQE_EXP_COUNT); - } else - qdesc = lpfc_sli4_queue_alloc(phba, - LPFC_DEFAULT_PAGE_SIZE, - phba->sli4_hba.wq_esize, - phba->sli4_hba.wq_ecount); - - if (!qdesc) - goto out_error; - - phba->sli4_hba.oas_wq = qdesc; - list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); - - } - return 0; - -out_error: - lpfc_fof_queue_destroy(phba); - return -ENOMEM; -} - -/** - * lpfc_fof_queue_destroy - Destroy all the fof queues - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to release all the SLI4 queues with the FC HBA - * operation. - * - * Return codes - * 0 - successful - **/ -int -lpfc_fof_queue_destroy(struct lpfc_hba *phba) -{ - /* Release FOF Event queue */ - if (phba->sli4_hba.fof_eq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.fof_eq); - phba->sli4_hba.fof_eq = NULL; - } - - /* Release OAS Completion queue */ - if (phba->sli4_hba.oas_cq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.oas_cq); - phba->sli4_hba.oas_cq = NULL; - } - - /* Release OAS Work queue */ - if (phba->sli4_hba.oas_wq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.oas_wq); - phba->sli4_hba.oas_wq = NULL; - } - return 0; -} MODULE_DEVICE_TABLE(pci, lpfc_id_table); diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 2e73331cf53a..9793016046de 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -4598,14 +4598,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) iocb = &lpfc_cmd->cur_iocbq; if (phba->sli_rev == LPFC_SLI_REV4) { - if (!(phba->cfg_fof) || - (!(iocb->iocb_flag & LPFC_IO_FOF))) { - pring_s4 = - phba->sli4_hba.fcp_wq[iocb->hba_wqidx]->pring; - } else { - iocb->hba_wqidx = 0; - pring_s4 = phba->sli4_hba.oas_wq->pring; - } + pring_s4 = phba->sli4_hba.fcp_wq[iocb->hba_wqidx]->pring; if (!pring_s4) { ret = FAILED; goto out_unlock; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index a450d845ec22..30a786304848 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -5581,9 +5581,6 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) sli4_hba->sli4_cq_release(sli4_hba->nvme_cq[qidx], LPFC_QUEUE_REARM); - if (phba->cfg_fof) - sli4_hba->sli4_cq_release(sli4_hba->oas_cq, LPFC_QUEUE_REARM); - if (sli4_hba->hba_eq) for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) sli4_hba->sli4_eq_release(sli4_hba->hba_eq[qidx], @@ -5596,9 +5593,6 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) LPFC_QUEUE_REARM); } } - - if (phba->cfg_fof) - sli4_hba->sli4_eq_release(sli4_hba->fof_eq, LPFC_QUEUE_REARM); } /** @@ -9872,10 +9866,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, /* Get the WQ */ if ((piocb->iocb_flag & LPFC_IO_FCP) || (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - if (!phba->cfg_fof || (!(piocb->iocb_flag & LPFC_IO_OAS))) - wq = phba->sli4_hba.fcp_wq[piocb->hba_wqidx]; - else - wq = phba->sli4_hba.oas_wq; + wq = phba->sli4_hba.fcp_wq[piocb->hba_wqidx]; } else { wq = phba->sli4_hba.els_wq; } @@ -10010,28 +10001,20 @@ struct lpfc_sli_ring * lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) { if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { - if (!(phba->cfg_fof) || - (!(piocb->iocb_flag & LPFC_IO_FOF))) { - if (unlikely(!phba->sli4_hba.fcp_wq)) - return NULL; - /* - * for abort iocb hba_wqidx should already - * be setup based on what work queue we used. - */ - if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - piocb->hba_wqidx = - lpfc_sli4_scmd_to_wqidx_distr(phba, - piocb->context1); - piocb->hba_wqidx = piocb->hba_wqidx % - phba->cfg_fcp_io_channel; - } - return phba->sli4_hba.fcp_wq[piocb->hba_wqidx]->pring; - } else { - if (unlikely(!phba->sli4_hba.oas_wq)) - return NULL; - piocb->hba_wqidx = 0; - return phba->sli4_hba.oas_wq->pring; + if (unlikely(!phba->sli4_hba.fcp_wq)) + return NULL; + /* + * for abort iocb hba_wqidx should already + * be setup based on what work queue we used. + */ + if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { + piocb->hba_wqidx = + lpfc_sli4_scmd_to_wqidx_distr( + phba, piocb->context1); + piocb->hba_wqidx = piocb->hba_wqidx % + phba->cfg_fcp_io_channel; } + return phba->sli4_hba.fcp_wq[piocb->hba_wqidx]->pring; } else { if (unlikely(!phba->sli4_hba.els_wq)) return NULL; @@ -10550,16 +10533,6 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) spin_lock_init(&pring->ring_lock); } - if (phba->cfg_fof) { - pring = phba->sli4_hba.oas_wq->pring; - pring->flag = 0; - pring->ringno = LPFC_FCP_RING; - INIT_LIST_HEAD(&pring->txq); - INIT_LIST_HEAD(&pring->txcmplq); - INIT_LIST_HEAD(&pring->iocb_continueq); - spin_lock_init(&pring->ring_lock); - } - spin_unlock_irq(&phba->hbalock); } @@ -14219,154 +14192,6 @@ lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq) } -/** - * lpfc_sli4_fof_handle_eqe - Process a Flash Optimized Fabric event queue - * entry - * @phba: Pointer to HBA context object. - * @eqe: Pointer to fast-path event queue entry. - * - * This routine process a event queue entry from the Flash Optimized Fabric - * event queue. It will check the MajorCode and MinorCode to determine this - * is for a completion event on a completion queue, if not, an error shall be - * logged and just return. Otherwise, it will get to the corresponding - * completion queue and process all the entries on the completion queue, rearm - * the completion queue, and then return. - **/ -static void -lpfc_sli4_fof_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) -{ - struct lpfc_queue *cq; - uint16_t cqid; - - if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) { - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "9147 Not a valid completion " - "event: majorcode=x%x, minorcode=x%x\n", - bf_get_le32(lpfc_eqe_major_code, eqe), - bf_get_le32(lpfc_eqe_minor_code, eqe)); - return; - } - - /* Get the reference to the corresponding CQ */ - cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); - - /* Next check for OAS */ - cq = phba->sli4_hba.oas_cq; - if (unlikely(!cq)) { - if (phba->sli.sli_flag & LPFC_SLI_ACTIVE) - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "9148 OAS completion queue " - "does not exist\n"); - return; - } - - if (unlikely(cqid != cq->queue_id)) { - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "9149 Miss-matched fast-path compl " - "queue id: eqcqid=%d, fcpcqid=%d\n", - cqid, cq->queue_id); - return; - } - - /* Save EQ associated with this CQ */ - cq->assoc_qp = phba->sli4_hba.fof_eq; - - /* CQ work will be processed on CPU affinitized to this IRQ */ - if (!queue_work(phba->wq, &cq->irqwork)) - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0367 Cannot schedule soft IRQ " - "for CQ eqcqid=%d, cqid=%d on CPU %d\n", - cqid, cq->queue_id, smp_processor_id()); -} - -/** - * lpfc_sli4_fof_intr_handler - HBA interrupt handler to SLI-4 device - * @irq: Interrupt number. - * @dev_id: The device context pointer. - * - * This function is directly called from the PCI layer as an interrupt - * service routine when device with SLI-4 interface spec is enabled with - * MSI-X multi-message interrupt mode and there is a Flash Optimized Fabric - * IOCB ring event in the HBA. However, when the device is enabled with either - * MSI or Pin-IRQ interrupt mode, this function is called as part of the - * device-level interrupt handler. When the PCI slot is in error recovery - * or the HBA is undergoing initialization, the interrupt handler will not - * process the interrupt. The Flash Optimized Fabric ring event are handled in - * the intrrupt context. This function is called without any lock held. - * It gets the hbalock to access and update SLI data structures. Note that, - * the EQ to CQ are one-to-one map such that the EQ index is - * equal to that of CQ index. - * - * This function returns IRQ_HANDLED when interrupt is handled else it - * returns IRQ_NONE. - **/ -irqreturn_t -lpfc_sli4_fof_intr_handler(int irq, void *dev_id) -{ - struct lpfc_hba *phba; - struct lpfc_hba_eq_hdl *hba_eq_hdl; - struct lpfc_queue *eq; - struct lpfc_eqe *eqe; - unsigned long iflag; - int ecount = 0; - - /* Get the driver's phba structure from the dev_id */ - hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id; - phba = hba_eq_hdl->phba; - - if (unlikely(!phba)) - return IRQ_NONE; - - /* Get to the EQ struct associated with this vector */ - eq = phba->sli4_hba.fof_eq; - if (unlikely(!eq)) - return IRQ_NONE; - - /* Check device state for handling interrupt */ - if (unlikely(lpfc_intr_state_check(phba))) { - /* Check again for link_state with lock held */ - spin_lock_irqsave(&phba->hbalock, iflag); - if (phba->link_state < LPFC_LINK_DOWN) - /* Flush, clear interrupt, and rearm the EQ */ - lpfc_sli4_eq_flush(phba, eq); - spin_unlock_irqrestore(&phba->hbalock, iflag); - return IRQ_NONE; - } - - /* - * Process all the event on FCP fast-path EQ - */ - while ((eqe = lpfc_sli4_eq_get(eq))) { - lpfc_sli4_fof_handle_eqe(phba, eqe); - if (!(++ecount % eq->entry_repost)) - break; - eq->EQ_processed++; - } - - /* Track the max number of EQEs processed in 1 intr */ - if (ecount > eq->EQ_max_eqe) - eq->EQ_max_eqe = ecount; - - - if (unlikely(ecount == 0)) { - eq->EQ_no_entry++; - - if (phba->intr_type == MSIX) - /* MSI-X treated interrupt served as no EQ share INT */ - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "9145 MSI-X interrupt with no EQE\n"); - else { - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "9146 ISR interrupt with no EQE\n"); - /* Non MSI-X treated on interrupt as EQ share INT */ - return IRQ_NONE; - } - } - /* Always clear and re-arm the fast-path EQ */ - phba->sli4_hba.sli4_eq_release(eq, LPFC_QUEUE_REARM); - return IRQ_HANDLED; -} - /** * lpfc_sli4_hba_intr_handler - HBA interrupt handler to SLI-4 device * @irq: Interrupt number. @@ -14522,13 +14347,6 @@ lpfc_sli4_intr_handler(int irq, void *dev_id) hba_handled |= true; } - if (phba->cfg_fof) { - hba_irq_rc = lpfc_sli4_fof_intr_handler(irq, - &phba->sli4_hba.hba_eq_hdl[qidx]); - if (hba_irq_rc == IRQ_HANDLED) - hba_handled |= true; - } - return (hba_handled == true) ? IRQ_HANDLED : IRQ_NONE; } /* lpfc_sli4_intr_handler */ diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 7048df6c92aa..645620175453 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -634,13 +634,7 @@ struct lpfc_sli4_hba { uint32_t ulp0_mode; /* ULP0 protocol mode */ uint32_t ulp1_mode; /* ULP1 protocol mode */ - struct lpfc_queue *fof_eq; /* Flash Optimized Fabric Event queue */ - /* Optimized Access Storage specific queues/structures */ - - struct lpfc_queue *oas_cq; /* OAS completion queue */ - struct lpfc_queue *oas_wq; /* OAS Work queue */ - struct lpfc_sli_ring *oas_ring; uint64_t oas_next_lun; uint8_t oas_next_tgt_wwpn[8]; uint8_t oas_next_vpt_wwpn[8]; From cdb42becdd40eeb320af3f21ac9a34e9d7517516 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:21 -0800 Subject: [PATCH 1206/4277] scsi: lpfc: Replace io_channels for nvme and fcp with general hdw_queues per cpu Currently, both nvme and fcp each have their own concept of an io_channel, which is a combination wq/cq and associated msix. Different cpus would share an io_channel. The driver is now moving to per-cpu wq/cq pairs and msix vectors. The driver will still use separate wq/cq pairs per protocol on each cpu, but the protocols will share the msix vector. Given the elimination of the nvme and fcp io channels, the module parameters will be removed. A new parameter, lpfc_hdw_queue is added which allows the wq/cq pair allocation per cpu to be overridden and allocated to lesser value. If lpfc_hdw_queue is zero, the number of pairs allocated will be based on the number of cpus. If non-zero, the parameter specifies the number of queues to allocate. At this time, the maximum non-zero value is 64. To manage this new paradigm, a new hardware queue structure is created to track queue activity and relationships. As MSIX vector allocation must be known before setting up the relationships, msix allocation now occurs before queue datastructures are allocated. If the number of vectors allocated is less than the desired hardware queues, the hardware queue counts will be reduced to the number of vectors Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 4 +- drivers/scsi/lpfc/lpfc_attr.c | 84 ++---- drivers/scsi/lpfc/lpfc_debugfs.c | 152 +++++----- drivers/scsi/lpfc/lpfc_debugfs.h | 65 ++-- drivers/scsi/lpfc/lpfc_init.c | 491 +++++++++++-------------------- drivers/scsi/lpfc/lpfc_nvme.c | 16 +- drivers/scsi/lpfc/lpfc_nvmet.c | 10 +- drivers/scsi/lpfc/lpfc_scsi.c | 8 +- drivers/scsi/lpfc/lpfc_sli.c | 159 +++++----- drivers/scsi/lpfc/lpfc_sli4.h | 36 +-- 10 files changed, 418 insertions(+), 607 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 858a9a50f94d..da12476dd933 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -810,11 +810,10 @@ struct lpfc_hba { uint32_t cfg_auto_imax; uint32_t cfg_fcp_imax; uint32_t cfg_fcp_cpu_map; - uint32_t cfg_fcp_io_channel; + uint32_t cfg_hdw_queue; uint32_t cfg_suppress_rsp; uint32_t cfg_nvme_oas; uint32_t cfg_nvme_embed_cmd; - uint32_t cfg_nvme_io_channel; uint32_t cfg_nvmet_mrq_post; uint32_t cfg_nvmet_mrq; uint32_t cfg_enable_nvmet; @@ -877,7 +876,6 @@ struct lpfc_hba { #define LPFC_ENABLE_NVME 2 #define LPFC_ENABLE_BOTH 3 uint32_t cfg_enable_pbde; - uint32_t io_channel_irqs; /* number of irqs for io channels */ struct nvmet_fc_target_port *targetport; lpfc_vpd_t vpd; /* vital product data */ diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 0980e1b67b83..c6b1d432dd07 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -456,7 +456,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, totin = 0; totout = 0; - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { cstat = &lport->cstat[i]; tot = atomic_read(&cstat->fc4NvmeIoCmpls); totin += tot; @@ -4909,7 +4909,7 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, phba->cfg_fcp_imax = (uint32_t)val; phba->initial_imax = phba->cfg_fcp_imax; - for (i = 0; i < phba->io_channel_irqs; i += LPFC_MAX_EQ_DELAY_EQID_CNT) + for (i = 0; i < phba->cfg_hdw_queue; i += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, i, LPFC_MAX_EQ_DELAY_EQID_CNT, val); @@ -5398,41 +5398,23 @@ LPFC_ATTR_RW(nvme_embed_cmd, 1, 0, 2, "Embed NVME Command in WQE"); /* - * lpfc_fcp_io_channel: Set the number of FCP IO channels the driver - * will advertise it supports to the SCSI layer. This also will map to - * the number of WQs the driver will create. - * - * 0 = Configure the number of io channels to the number of active CPUs. - * 1,32 = Manually specify how many io channels to use. - * - * Value range is [0,32]. Default value is 4. - */ -LPFC_ATTR_R(fcp_io_channel, - LPFC_FCP_IO_CHAN_DEF, - LPFC_HBA_IO_CHAN_MIN, LPFC_HBA_IO_CHAN_MAX, - "Set the number of FCP I/O channels"); - -/* - * lpfc_nvme_io_channel: Set the number of IO hardware queues the driver - * will advertise it supports to the NVME layer. This also will map to - * the number of WQs the driver will create. - * - * This module parameter is valid when lpfc_enable_fc4_type is set - * to support NVME. + * lpfc_hdw_queue: Set the number of IO channels the driver + * will advertise it supports to the NVME and SCSI layers. This also + * will map to the number of EQ/CQ/WQs the driver will create. * * The NVME Layer will try to create this many, plus 1 administrative * hardware queue. The administrative queue will always map to WQ 0 * A hardware IO queue maps (qidx) to a specific driver WQ. * - * 0 = Configure the number of io channels to the number of active CPUs. - * 1,32 = Manually specify how many io channels to use. + * 0 = Configure the number of hdw queues to the number of active CPUs. + * 1,64 = Manually specify how many hdw queues to use. * - * Value range is [0,32]. Default value is 0. + * Value range is [0,64]. Default value is 0. */ -LPFC_ATTR_R(nvme_io_channel, - LPFC_NVME_IO_CHAN_DEF, - LPFC_HBA_IO_CHAN_MIN, LPFC_HBA_IO_CHAN_MAX, - "Set the number of NVME I/O channels"); +LPFC_ATTR_R(hdw_queue, + LPFC_HBA_HDWQ_DEF, + LPFC_HBA_HDWQ_MIN, LPFC_HBA_HDWQ_MAX, + "Set the number of I/O Hardware Queues"); /* # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware. @@ -5727,9 +5709,8 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_auto_imax, &dev_attr_lpfc_fcp_imax, &dev_attr_lpfc_fcp_cpu_map, - &dev_attr_lpfc_fcp_io_channel, + &dev_attr_lpfc_hdw_queue, &dev_attr_lpfc_suppress_rsp, - &dev_attr_lpfc_nvme_io_channel, &dev_attr_lpfc_nvmet_mrq, &dev_attr_lpfc_nvmet_mrq_post, &dev_attr_lpfc_nvme_enable_fb, @@ -6806,8 +6787,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) /* Initialize first burst. Target vs Initiator are different. */ lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb); lpfc_nvmet_fb_size_init(phba, lpfc_nvmet_fb_size); - lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel); - lpfc_nvme_io_channel_init(phba, lpfc_nvme_io_channel); + lpfc_hdw_queue_init(phba, lpfc_hdw_queue); lpfc_enable_bbcr_init(phba, lpfc_enable_bbcr); lpfc_enable_dpp_init(phba, lpfc_enable_dpp); @@ -6829,21 +6809,8 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) phba->cfg_enable_pbde = 0; /* A value of 0 means use the number of CPUs found in the system */ - if (phba->cfg_fcp_io_channel == 0) - phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu; - if (phba->cfg_nvme_io_channel == 0) - phba->cfg_nvme_io_channel = phba->sli4_hba.num_present_cpu; - - if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME) - phba->cfg_fcp_io_channel = 0; - - if (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP) - phba->cfg_nvme_io_channel = 0; - - if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel) - phba->io_channel_irqs = phba->cfg_fcp_io_channel; - else - phba->io_channel_irqs = phba->cfg_nvme_io_channel; + if (phba->cfg_hdw_queue == 0) + phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu; phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; @@ -6884,16 +6851,12 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba) { - if (phba->cfg_nvme_io_channel > phba->sli4_hba.num_present_cpu) - phba->cfg_nvme_io_channel = phba->sli4_hba.num_present_cpu; - - if (phba->cfg_fcp_io_channel > phba->sli4_hba.num_present_cpu) - phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu; + if (phba->cfg_hdw_queue > phba->sli4_hba.num_present_cpu) + phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu; if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME && phba->nvmet_support) { phba->cfg_enable_fc4_type &= ~LPFC_ENABLE_FCP; - phba->cfg_fcp_io_channel = 0; lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, "6013 %s x%x fb_size x%x, fb_max x%x\n", @@ -6910,11 +6873,11 @@ lpfc_nvme_mod_param_dep(struct lpfc_hba *phba) } if (!phba->cfg_nvmet_mrq) - phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel; + phba->cfg_nvmet_mrq = phba->cfg_hdw_queue; /* Adjust lpfc_nvmet_mrq to avoid running out of WQE slots */ - if (phba->cfg_nvmet_mrq > phba->cfg_nvme_io_channel) { - phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel; + if (phba->cfg_nvmet_mrq > phba->cfg_hdw_queue) { + phba->cfg_nvmet_mrq = phba->cfg_hdw_queue; lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, "6018 Adjust lpfc_nvmet_mrq to %d\n", phba->cfg_nvmet_mrq); @@ -6928,11 +6891,6 @@ lpfc_nvme_mod_param_dep(struct lpfc_hba *phba) phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_OFF; phba->cfg_nvmet_fb_size = 0; } - - if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel) - phba->io_channel_irqs = phba->cfg_fcp_io_channel; - else - phba->io_channel_irqs = phba->cfg_nvme_io_channel; } /** diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 355857e1ef7d..fc5ec99bfde2 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -919,13 +919,13 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) atomic_read(&lport->fc4NvmeLsRequests), atomic_read(&lport->fc4NvmeLsCmpls)); - if (phba->cfg_nvme_io_channel < 32) - maxch = phba->cfg_nvme_io_channel; + if (phba->cfg_hdw_queue < LPFC_HBA_HDWQ_MAX) + maxch = phba->cfg_hdw_queue; else - maxch = 32; + maxch = LPFC_HBA_HDWQ_MAX; totin = 0; totout = 0; - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { cstat = &lport->cstat[i]; tot = atomic_read(&cstat->fc4NvmeIoCmpls); totin += tot; @@ -3182,21 +3182,23 @@ lpfc_idiag_wqs_for_cq(struct lpfc_hba *phba, char *wqtype, char *pbuffer, struct lpfc_queue *qp; int qidx; - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) { - qp = phba->sli4_hba.fcp_wq[qidx]; + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].fcp_wq; if (qp->assoc_qid != cq_id) continue; *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len); if (*len >= max_cnt) return 1; } - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { - qp = phba->sli4_hba.nvme_wq[qidx]; - if (qp->assoc_qid != cq_id) - continue; - *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len); - if (*len >= max_cnt) - return 1; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].nvme_wq; + if (qp->assoc_qid != cq_id) + continue; + *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len); + if (*len >= max_cnt) + return 1; + } } return 0; } @@ -3262,8 +3264,8 @@ lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer, struct lpfc_queue *qp; int qidx, rc; - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) { - qp = phba->sli4_hba.fcp_cq[qidx]; + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].fcp_cq; if (qp->assoc_qid != eq_id) continue; @@ -3281,23 +3283,25 @@ lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer, return 1; } - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { - qp = phba->sli4_hba.nvme_cq[qidx]; - if (qp->assoc_qid != eq_id) - continue; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].nvme_cq; + if (qp->assoc_qid != eq_id) + continue; - *len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len); + *len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len); - /* Reset max counter */ - qp->CQ_max_cqe = 0; + /* Reset max counter */ + qp->CQ_max_cqe = 0; - if (*len >= max_cnt) - return 1; + if (*len >= max_cnt) + return 1; - rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len, - max_cnt, qp->queue_id); - if (rc) - return 1; + rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len, + max_cnt, qp->queue_id); + if (rc) + return 1; + } } if ((eqidx < phba->cfg_nvmet_mrq) && phba->nvmet_support) { @@ -3387,19 +3391,19 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, spin_lock_irq(&phba->hbalock); /* Fast-path event queue */ - if (phba->sli4_hba.hba_eq && phba->io_channel_irqs) { + if (phba->sli4_hba.hdwq && phba->cfg_hdw_queue) { x = phba->lpfc_idiag_last_eq; phba->lpfc_idiag_last_eq++; - if (phba->lpfc_idiag_last_eq >= phba->io_channel_irqs) + if (phba->lpfc_idiag_last_eq >= phba->cfg_hdw_queue) phba->lpfc_idiag_last_eq = 0; len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "EQ %d out of %d HBA EQs\n", - x, phba->io_channel_irqs); + x, phba->cfg_hdw_queue); /* Fast-path EQ */ - qp = phba->sli4_hba.hba_eq[x]; + qp = phba->sli4_hba.hdwq[x].hba_eq; if (!qp) goto out; @@ -3691,9 +3695,9 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, switch (quetp) { case LPFC_IDIAG_EQ: /* HBA event queue */ - if (phba->sli4_hba.hba_eq) { - for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) { - qp = phba->sli4_hba.hba_eq[qidx]; + if (phba->sli4_hba.hdwq) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].hba_eq; if (qp && qp->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check(qp, @@ -3742,10 +3746,10 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, goto pass_check; } /* FCP complete queue */ - if (phba->sli4_hba.fcp_cq) { - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; + if (phba->sli4_hba.hdwq) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { - qp = phba->sli4_hba.fcp_cq[qidx]; + qp = phba->sli4_hba.hdwq[qidx].fcp_cq; if (qp && qp->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( @@ -3758,23 +3762,20 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, } } /* NVME complete queue */ - if (phba->sli4_hba.nvme_cq) { + if (phba->sli4_hba.hdwq) { qidx = 0; do { - if (phba->sli4_hba.nvme_cq[qidx] && - phba->sli4_hba.nvme_cq[qidx]->queue_id == - queid) { + qp = phba->sli4_hba.hdwq[qidx].nvme_cq; + if (qp && qp->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( - phba->sli4_hba.nvme_cq[qidx], - index, count); + qp, index, count); if (rc) goto error_out; - idiag.ptr_private = - phba->sli4_hba.nvme_cq[qidx]; + idiag.ptr_private = qp; goto pass_check; } - } while (++qidx < phba->cfg_nvme_io_channel); + } while (++qidx < phba->cfg_hdw_queue); } goto error_out; break; @@ -3815,11 +3816,11 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, idiag.ptr_private = phba->sli4_hba.nvmels_wq; goto pass_check; } - /* FCP work queue */ - if (phba->sli4_hba.fcp_wq) { - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; - qidx++) { - qp = phba->sli4_hba.fcp_wq[qidx]; + + if (phba->sli4_hba.hdwq) { + /* FCP/SCSI work queue */ + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].fcp_wq; if (qp && qp->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( @@ -3830,12 +3831,9 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, goto pass_check; } } - } - /* NVME work queue */ - if (phba->sli4_hba.nvme_wq) { - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; - qidx++) { - qp = phba->sli4_hba.nvme_wq[qidx]; + /* NVME work queue */ + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = phba->sli4_hba.hdwq[qidx].nvme_wq; if (qp && qp->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( @@ -3848,26 +3846,6 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, } } - /* NVME work queues */ - if (phba->sli4_hba.nvme_wq) { - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; - qidx++) { - if (!phba->sli4_hba.nvme_wq[qidx]) - continue; - if (phba->sli4_hba.nvme_wq[qidx]->queue_id == - queid) { - /* Sanity check */ - rc = lpfc_idiag_que_param_check( - phba->sli4_hba.nvme_wq[qidx], - index, count); - if (rc) - goto error_out; - idiag.ptr_private = - phba->sli4_hba.nvme_wq[qidx]; - goto pass_check; - } - } - } goto error_out; break; case LPFC_IDIAG_RQ: @@ -5784,11 +5762,13 @@ lpfc_debug_dump_all_queues(struct lpfc_hba *phba) lpfc_debug_dump_wq(phba, DUMP_ELS, 0); lpfc_debug_dump_wq(phba, DUMP_NVMELS, 0); - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) lpfc_debug_dump_wq(phba, DUMP_FCP, idx); - for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++) - lpfc_debug_dump_wq(phba, DUMP_NVME, idx); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) + lpfc_debug_dump_wq(phba, DUMP_NVME, idx); + } lpfc_debug_dump_hdr_rq(phba); lpfc_debug_dump_dat_rq(phba); @@ -5799,15 +5779,17 @@ lpfc_debug_dump_all_queues(struct lpfc_hba *phba) lpfc_debug_dump_cq(phba, DUMP_ELS, 0); lpfc_debug_dump_cq(phba, DUMP_NVMELS, 0); - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) lpfc_debug_dump_cq(phba, DUMP_FCP, idx); - for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++) - lpfc_debug_dump_cq(phba, DUMP_NVME, idx); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) + lpfc_debug_dump_cq(phba, DUMP_NVME, idx); + } /* * Dump Event Queues (EQs) */ - for (idx = 0; idx < phba->io_channel_irqs; idx++) + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) lpfc_debug_dump_hba_eq(phba, idx); } diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 30efc7bf91bd..2c5bc494b247 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -410,10 +410,10 @@ lpfc_debug_dump_wq(struct lpfc_hba *phba, int qtype, int wqidx) char *qtypestr; if (qtype == DUMP_FCP) { - wq = phba->sli4_hba.fcp_wq[wqidx]; + wq = phba->sli4_hba.hdwq[wqidx].fcp_wq; qtypestr = "FCP"; } else if (qtype == DUMP_NVME) { - wq = phba->sli4_hba.nvme_wq[wqidx]; + wq = phba->sli4_hba.hdwq[wqidx].nvme_wq; qtypestr = "NVME"; } else if (qtype == DUMP_MBX) { wq = phba->sli4_hba.mbx_wq; @@ -454,14 +454,15 @@ lpfc_debug_dump_cq(struct lpfc_hba *phba, int qtype, int wqidx) int eqidx; /* fcp/nvme wq and cq are 1:1, thus same indexes */ + eq = NULL; if (qtype == DUMP_FCP) { - wq = phba->sli4_hba.fcp_wq[wqidx]; - cq = phba->sli4_hba.fcp_cq[wqidx]; + wq = phba->sli4_hba.hdwq[wqidx].fcp_wq; + cq = phba->sli4_hba.hdwq[wqidx].fcp_cq; qtypestr = "FCP"; } else if (qtype == DUMP_NVME) { - wq = phba->sli4_hba.nvme_wq[wqidx]; - cq = phba->sli4_hba.nvme_cq[wqidx]; + wq = phba->sli4_hba.hdwq[wqidx].nvme_wq; + cq = phba->sli4_hba.hdwq[wqidx].nvme_cq; qtypestr = "NVME"; } else if (qtype == DUMP_MBX) { wq = phba->sli4_hba.mbx_wq; @@ -478,17 +479,17 @@ lpfc_debug_dump_cq(struct lpfc_hba *phba, int qtype, int wqidx) } else return; - for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++) { - if (cq->assoc_qid == phba->sli4_hba.hba_eq[eqidx]->queue_id) + for (eqidx = 0; eqidx < phba->cfg_hdw_queue; eqidx++) { + eq = phba->sli4_hba.hdwq[eqidx].hba_eq; + if (cq->assoc_qid == eq->queue_id) break; } - if (eqidx == phba->io_channel_irqs) { + if (eqidx == phba->cfg_hdw_queue) { pr_err("Couldn't find EQ for CQ. Using EQ[0]\n"); eqidx = 0; + eq = phba->sli4_hba.hdwq[0].hba_eq; } - eq = phba->sli4_hba.hba_eq[eqidx]; - if (qtype == DUMP_FCP || qtype == DUMP_NVME) pr_err("%s CQ: WQ[Idx:%d|Qid%d]->CQ[Idx%d|Qid%d]" "->EQ[Idx:%d|Qid:%d]:\n", @@ -516,7 +517,7 @@ lpfc_debug_dump_hba_eq(struct lpfc_hba *phba, int qidx) { struct lpfc_queue *qp; - qp = phba->sli4_hba.hba_eq[qidx]; + qp = phba->sli4_hba.hdwq[qidx].hba_eq; pr_err("EQ[Idx:%d|Qid:%d]\n", qidx, qp->queue_id); @@ -564,21 +565,21 @@ lpfc_debug_dump_wq_by_id(struct lpfc_hba *phba, int qid) { int wq_idx; - for (wq_idx = 0; wq_idx < phba->cfg_fcp_io_channel; wq_idx++) - if (phba->sli4_hba.fcp_wq[wq_idx]->queue_id == qid) + for (wq_idx = 0; wq_idx < phba->cfg_hdw_queue; wq_idx++) + if (phba->sli4_hba.hdwq[wq_idx].fcp_wq->queue_id == qid) break; - if (wq_idx < phba->cfg_fcp_io_channel) { + if (wq_idx < phba->cfg_hdw_queue) { pr_err("FCP WQ[Idx:%d|Qid:%d]\n", wq_idx, qid); - lpfc_debug_dump_q(phba->sli4_hba.fcp_wq[wq_idx]); + lpfc_debug_dump_q(phba->sli4_hba.hdwq[wq_idx].fcp_wq); return; } - for (wq_idx = 0; wq_idx < phba->cfg_nvme_io_channel; wq_idx++) - if (phba->sli4_hba.nvme_wq[wq_idx]->queue_id == qid) + for (wq_idx = 0; wq_idx < phba->cfg_hdw_queue; wq_idx++) + if (phba->sli4_hba.hdwq[wq_idx].nvme_wq->queue_id == qid) break; - if (wq_idx < phba->cfg_nvme_io_channel) { + if (wq_idx < phba->cfg_hdw_queue) { pr_err("NVME WQ[Idx:%d|Qid:%d]\n", wq_idx, qid); - lpfc_debug_dump_q(phba->sli4_hba.nvme_wq[wq_idx]); + lpfc_debug_dump_q(phba->sli4_hba.hdwq[wq_idx].nvme_wq); return; } @@ -646,23 +647,23 @@ lpfc_debug_dump_cq_by_id(struct lpfc_hba *phba, int qid) { int cq_idx; - for (cq_idx = 0; cq_idx < phba->cfg_fcp_io_channel; cq_idx++) - if (phba->sli4_hba.fcp_cq[cq_idx]->queue_id == qid) + for (cq_idx = 0; cq_idx < phba->cfg_hdw_queue; cq_idx++) + if (phba->sli4_hba.hdwq[cq_idx].fcp_cq->queue_id == qid) break; - if (cq_idx < phba->cfg_fcp_io_channel) { + if (cq_idx < phba->cfg_hdw_queue) { pr_err("FCP CQ[Idx:%d|Qid:%d]\n", cq_idx, qid); - lpfc_debug_dump_q(phba->sli4_hba.fcp_cq[cq_idx]); + lpfc_debug_dump_q(phba->sli4_hba.hdwq[cq_idx].fcp_cq); return; } - for (cq_idx = 0; cq_idx < phba->cfg_nvme_io_channel; cq_idx++) - if (phba->sli4_hba.nvme_cq[cq_idx]->queue_id == qid) + for (cq_idx = 0; cq_idx < phba->cfg_hdw_queue; cq_idx++) + if (phba->sli4_hba.hdwq[cq_idx].nvme_cq->queue_id == qid) break; - if (cq_idx < phba->cfg_nvme_io_channel) { + if (cq_idx < phba->cfg_hdw_queue) { pr_err("NVME CQ[Idx:%d|Qid:%d]\n", cq_idx, qid); - lpfc_debug_dump_q(phba->sli4_hba.nvme_cq[cq_idx]); + lpfc_debug_dump_q(phba->sli4_hba.hdwq[cq_idx].nvme_cq); return; } @@ -697,13 +698,13 @@ lpfc_debug_dump_eq_by_id(struct lpfc_hba *phba, int qid) { int eq_idx; - for (eq_idx = 0; eq_idx < phba->io_channel_irqs; eq_idx++) - if (phba->sli4_hba.hba_eq[eq_idx]->queue_id == qid) + for (eq_idx = 0; eq_idx < phba->cfg_hdw_queue; eq_idx++) + if (phba->sli4_hba.hdwq[eq_idx].hba_eq->queue_id == qid) break; - if (eq_idx < phba->io_channel_irqs) { + if (eq_idx < phba->cfg_hdw_queue) { printk(KERN_ERR "FCP EQ[Idx:%d|Qid:%d]\n", eq_idx, qid); - lpfc_debug_dump_q(phba->sli4_hba.hba_eq[eq_idx]); + lpfc_debug_dump_q(phba->sli4_hba.hdwq[eq_idx].hba_eq); return; } } diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 9d9b965f796d..c4acd9ee55b1 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1315,7 +1315,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) localport->private; tot = 0; for (i = 0; - i < phba->cfg_nvme_io_channel; i++) { + i < phba->cfg_hdw_queue; i++) { cstat = &lport->cstat[i]; data1 = atomic_read( &cstat->fc4NvmeInputRequests); @@ -1331,15 +1331,15 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) } /* Interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / phba->io_channel_irqs; + val = phba->cfg_fcp_imax / phba->cfg_hdw_queue; tick_cqe = val / CONFIG_HZ; /* Per tick per EQ */ /* Assume 1 CQE/ISR, calc max CQEs allowed for time duration */ max_cqe = time_elapsed * tick_cqe; - for (i = 0; i < phba->io_channel_irqs; i++) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { /* Fast-path EQ */ - qp = phba->sli4_hba.hba_eq[i]; + qp = phba->sli4_hba.hdwq[i].hba_eq; if (!qp) continue; @@ -1361,7 +1361,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) if (val) { /* First, interrupts per sec per EQ */ val = phba->cfg_fcp_imax / - phba->io_channel_irqs; + phba->cfg_hdw_queue; /* us delay between each interrupt */ val = LPFC_SEC_TO_USEC / val; @@ -2945,7 +2945,8 @@ lpfc_sli4_stop_fcf_redisc_wait_timer(struct lpfc_hba *phba) void lpfc_stop_hba_timers(struct lpfc_hba *phba) { - lpfc_stop_vport_timers(phba->pport); + if (phba->pport) + lpfc_stop_vport_timers(phba->pport); del_timer_sync(&phba->sli.mbox_tmo); del_timer_sync(&phba->fabric_block_timer); del_timer_sync(&phba->eratt_poll); @@ -3989,7 +3990,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->max_lun = vport->cfg_max_luns; shost->this_id = -1; shost->max_cmd_len = 16; - shost->nr_hw_queues = phba->cfg_fcp_io_channel; + shost->nr_hw_queues = phba->cfg_hdw_queue; if (phba->sli_rev == LPFC_SLI_REV4) { shost->dma_boundary = phba->sli4_hba.pc_sli4_params.sge_supp_len-1; @@ -4248,7 +4249,8 @@ lpfc_stop_port_s4(struct lpfc_hba *phba) { /* Reset some HBA SLI4 setup states */ lpfc_stop_hba_timers(phba); - phba->pport->work_port_events = 0; + if (phba->pport) + phba->pport->work_port_events = 0; phba->sli4_hba.intr_enable = 0; } @@ -6475,9 +6477,9 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_remove_rpi_hdrs; } - phba->sli4_hba.hba_eq_hdl = kcalloc(phba->io_channel_irqs, - sizeof(struct lpfc_hba_eq_hdl), - GFP_KERNEL); + phba->sli4_hba.hba_eq_hdl = kcalloc(phba->cfg_hdw_queue, + sizeof(struct lpfc_hba_eq_hdl), + GFP_KERNEL); if (!phba->sli4_hba.hba_eq_hdl) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2572 Failed allocate memory for " @@ -8049,21 +8051,23 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) * NVMET, FCP io channel WQs are not created. */ length -= 6; - if (!phba->nvmet_support) - length -= phba->cfg_fcp_io_channel; - if (phba->cfg_nvme_io_channel > length) { + /* Take off FCP queues */ + if (!phba->nvmet_support) + length -= phba->cfg_hdw_queue; + + /* Check to see if there is enough for NVME */ + if (phba->cfg_hdw_queue > length) { lpfc_printf_log( phba, KERN_ERR, LOG_SLI, "2005 Reducing NVME IO channel to %d: " - "WQ %d CQ %d NVMEIO %d FCPIO %d\n", + "WQ %d CQ %d CommonIO %d\n", length, phba->sli4_hba.max_cfg_param.max_wq, phba->sli4_hba.max_cfg_param.max_cq, - phba->cfg_nvme_io_channel, - phba->cfg_fcp_io_channel); + phba->cfg_hdw_queue); - phba->cfg_nvme_io_channel = length; + phba->cfg_hdw_queue = length; } } } @@ -8276,52 +8280,30 @@ lpfc_setup_endian_order(struct lpfc_hba *phba) static int lpfc_sli4_queue_verify(struct lpfc_hba *phba) { - int io_channel; - /* * Sanity check for configured queue parameters against the run-time * device parameters */ - /* Sanity check on HBA EQ parameters */ - io_channel = phba->io_channel_irqs; - - if (phba->sli4_hba.num_online_cpu < io_channel) { - lpfc_printf_log(phba, - KERN_ERR, LOG_INIT, - "3188 Reducing IO channels to match number of " - "online CPUs: from %d to %d\n", - io_channel, phba->sli4_hba.num_online_cpu); - io_channel = phba->sli4_hba.num_online_cpu; - } - - if (io_channel > phba->sli4_hba.max_cfg_param.max_eq) { + if (phba->cfg_hdw_queue > phba->sli4_hba.max_cfg_param.max_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2575 Reducing IO channels to match number of " "available EQs: from %d to %d\n", - io_channel, + phba->cfg_hdw_queue, phba->sli4_hba.max_cfg_param.max_eq); - io_channel = phba->sli4_hba.max_cfg_param.max_eq; + phba->cfg_hdw_queue = phba->sli4_hba.max_cfg_param.max_eq; } - /* The actual number of FCP / NVME event queues adopted */ - if (io_channel != phba->io_channel_irqs) - phba->io_channel_irqs = io_channel; - if (phba->cfg_fcp_io_channel > io_channel) - phba->cfg_fcp_io_channel = io_channel; - if (phba->cfg_nvme_io_channel > io_channel) - phba->cfg_nvme_io_channel = io_channel; if (phba->nvmet_support) { - if (phba->cfg_nvme_io_channel < phba->cfg_nvmet_mrq) - phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel; + if (phba->cfg_hdw_queue < phba->cfg_nvmet_mrq) + phba->cfg_nvmet_mrq = phba->cfg_hdw_queue; } if (phba->cfg_nvmet_mrq > LPFC_NVMET_MRQ_MAX) phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_MAX; lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2574 IO channels: irqs %d fcp %d nvme %d MRQ: %d\n", - phba->io_channel_irqs, phba->cfg_fcp_io_channel, - phba->cfg_nvme_io_channel, phba->cfg_nvmet_mrq); + "2574 IO channels: hdwQ %d MRQ: %d\n", + phba->cfg_hdw_queue, phba->cfg_nvmet_mrq); /* Get EQ depth from module parameter, fake the default for now */ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B; @@ -8348,7 +8330,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) return 1; } qdesc->qe_valid = 1; - phba->sli4_hba.nvme_cq[wqidx] = qdesc; + phba->sli4_hba.hdwq[wqidx].nvme_cq = qdesc; qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, LPFC_WQE128_SIZE, LPFC_WQE_EXP_COUNT); @@ -8358,7 +8340,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) wqidx); return 1; } - phba->sli4_hba.nvme_wq[wqidx] = qdesc; + phba->sli4_hba.hdwq[wqidx].nvme_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; } @@ -8386,7 +8368,7 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) return 1; } qdesc->qe_valid = 1; - phba->sli4_hba.fcp_cq[wqidx] = qdesc; + phba->sli4_hba.hdwq[wqidx].fcp_cq = qdesc; /* Create Fast Path FCP WQs */ if (phba->enab_exp_wqcq_pages) { @@ -8407,7 +8389,7 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) wqidx); return 1; } - phba->sli4_hba.fcp_wq[wqidx] = qdesc; + phba->sli4_hba.hdwq[wqidx].fcp_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; } @@ -8430,16 +8412,12 @@ int lpfc_sli4_queue_create(struct lpfc_hba *phba) { struct lpfc_queue *qdesc; - int idx, io_channel; + int idx; /* * Create HBA Record arrays. * Both NVME and FCP will share that same vectors / EQs */ - io_channel = phba->io_channel_irqs; - if (!io_channel) - return -ERANGE; - phba->sli4_hba.mq_esize = LPFC_MQE_SIZE; phba->sli4_hba.mq_ecount = LPFC_MQE_DEF_COUNT; phba->sli4_hba.wq_esize = LPFC_WQE_SIZE; @@ -8451,87 +8429,17 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) phba->sli4_hba.cq_esize = LPFC_CQE_SIZE; phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT; - phba->sli4_hba.hba_eq = kcalloc(io_channel, - sizeof(struct lpfc_queue *), - GFP_KERNEL); - if (!phba->sli4_hba.hba_eq) { + phba->sli4_hba.hdwq = kcalloc(phba->cfg_hdw_queue, + sizeof(struct lpfc_sli4_hdw_queue), + GFP_KERNEL); + if (!phba->sli4_hba.hdwq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2576 Failed allocate memory for " - "fast-path EQ record array\n"); + "6427 Failed allocate memory for " + "fast-path Hardware Queue array\n"); goto out_error; } - if (phba->cfg_fcp_io_channel) { - phba->sli4_hba.fcp_cq = kcalloc(phba->cfg_fcp_io_channel, - sizeof(struct lpfc_queue *), - GFP_KERNEL); - if (!phba->sli4_hba.fcp_cq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2577 Failed allocate memory for " - "fast-path CQ record array\n"); - goto out_error; - } - phba->sli4_hba.fcp_wq = kcalloc(phba->cfg_fcp_io_channel, - sizeof(struct lpfc_queue *), - GFP_KERNEL); - if (!phba->sli4_hba.fcp_wq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2578 Failed allocate memory for " - "fast-path FCP WQ record array\n"); - goto out_error; - } - /* - * Since the first EQ can have multiple CQs associated with it, - * this array is used to quickly see if we have a FCP fast-path - * CQ match. - */ - phba->sli4_hba.fcp_cq_map = kcalloc(phba->cfg_fcp_io_channel, - sizeof(uint16_t), - GFP_KERNEL); - if (!phba->sli4_hba.fcp_cq_map) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2545 Failed allocate memory for " - "fast-path CQ map\n"); - goto out_error; - } - } - - if (phba->cfg_nvme_io_channel) { - phba->sli4_hba.nvme_cq = kcalloc(phba->cfg_nvme_io_channel, - sizeof(struct lpfc_queue *), - GFP_KERNEL); - if (!phba->sli4_hba.nvme_cq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6077 Failed allocate memory for " - "fast-path CQ record array\n"); - goto out_error; - } - - phba->sli4_hba.nvme_wq = kcalloc(phba->cfg_nvme_io_channel, - sizeof(struct lpfc_queue *), - GFP_KERNEL); - if (!phba->sli4_hba.nvme_wq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2581 Failed allocate memory for " - "fast-path NVME WQ record array\n"); - goto out_error; - } - - /* - * Since the first EQ can have multiple CQs associated with it, - * this array is used to quickly see if we have a NVME fast-path - * CQ match. - */ - phba->sli4_hba.nvme_cq_map = kcalloc(phba->cfg_nvme_io_channel, - sizeof(uint16_t), - GFP_KERNEL); - if (!phba->sli4_hba.nvme_cq_map) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6078 Failed allocate memory for " - "fast-path CQ map\n"); - goto out_error; - } - + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { if (phba->nvmet_support) { phba->sli4_hba.nvmet_cqset = kcalloc( phba->cfg_nvmet_mrq, @@ -8569,7 +8477,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) INIT_LIST_HEAD(&phba->sli4_hba.lpfc_wq_list); /* Create HBA Event Queues (EQs) */ - for (idx = 0; idx < io_channel; idx++) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { /* Create EQs */ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, phba->sli4_hba.eq_esize, @@ -8580,33 +8488,38 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } qdesc->qe_valid = 1; - phba->sli4_hba.hba_eq[idx] = qdesc; + phba->sli4_hba.hdwq[idx].hba_eq = qdesc; } - /* FCP and NVME io channels are not required to be balanced */ - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) + /* Allocate SCSI SLI4 CQ/WQs */ + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) if (lpfc_alloc_fcp_wq_cq(phba, idx)) goto out_error; - for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++) - if (lpfc_alloc_nvme_wq_cq(phba, idx)) - goto out_error; + /* Allocate NVME SLI4 CQ/WQs */ + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) + if (lpfc_alloc_nvme_wq_cq(phba, idx)) + goto out_error; - if (phba->nvmet_support) { - for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) { - qdesc = lpfc_sli4_queue_alloc(phba, + if (phba->nvmet_support) { + for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) { + qdesc = lpfc_sli4_queue_alloc( + phba, LPFC_DEFAULT_PAGE_SIZE, phba->sli4_hba.cq_esize, phba->sli4_hba.cq_ecount); - if (!qdesc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3142 Failed allocate NVME " - "CQ Set (%d)\n", idx); - goto out_error; + if (!qdesc) { + lpfc_printf_log( + phba, KERN_ERR, LOG_INIT, + "3142 Failed allocate NVME " + "CQ Set (%d)\n", idx); + goto out_error; + } + qdesc->qe_valid = 1; + phba->sli4_hba.nvmet_cqset[idx] = qdesc; } - qdesc->qe_valid = 1; - phba->sli4_hba.nvmet_cqset[idx] = qdesc; } } @@ -8723,7 +8636,8 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) } phba->sli4_hba.dat_rq = qdesc; - if (phba->nvmet_support) { + if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && + phba->nvmet_support) { for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) { /* Create NVMET Receive Queue for header */ qdesc = lpfc_sli4_queue_alloc(phba, @@ -8797,12 +8711,23 @@ lpfc_sli4_release_queues(struct lpfc_queue ***qs, int max) } static inline void -lpfc_sli4_release_queue_map(uint16_t **qmap) +lpfc_sli4_release_hdwq(struct lpfc_sli4_hdw_queue *hdwq, int max) { - if (*qmap != NULL) { - kfree(*qmap); - *qmap = NULL; + uint32_t idx; + + for (idx = 0; idx < max; idx++) { + lpfc_sli4_queue_free(hdwq[idx].hba_eq); + lpfc_sli4_queue_free(hdwq[idx].fcp_cq); + lpfc_sli4_queue_free(hdwq[idx].nvme_cq); + lpfc_sli4_queue_free(hdwq[idx].fcp_wq); + lpfc_sli4_queue_free(hdwq[idx].nvme_wq); + hdwq[idx].hba_eq = NULL; + hdwq[idx].fcp_cq = NULL; + hdwq[idx].nvme_cq = NULL; + hdwq[idx].fcp_wq = NULL; + hdwq[idx].nvme_wq = NULL; } + kfree(hdwq); } /** @@ -8821,29 +8746,10 @@ void lpfc_sli4_queue_destroy(struct lpfc_hba *phba) { /* Release HBA eqs */ - lpfc_sli4_release_queues(&phba->sli4_hba.hba_eq, phba->io_channel_irqs); - - /* Release FCP cqs */ - lpfc_sli4_release_queues(&phba->sli4_hba.fcp_cq, - phba->cfg_fcp_io_channel); - - /* Release FCP wqs */ - lpfc_sli4_release_queues(&phba->sli4_hba.fcp_wq, - phba->cfg_fcp_io_channel); - - /* Release FCP CQ mapping array */ - lpfc_sli4_release_queue_map(&phba->sli4_hba.fcp_cq_map); - - /* Release NVME cqs */ - lpfc_sli4_release_queues(&phba->sli4_hba.nvme_cq, - phba->cfg_nvme_io_channel); - - /* Release NVME wqs */ - lpfc_sli4_release_queues(&phba->sli4_hba.nvme_wq, - phba->cfg_nvme_io_channel); - - /* Release NVME CQ mapping array */ - lpfc_sli4_release_queue_map(&phba->sli4_hba.nvme_cq_map); + if (phba->sli4_hba.hdwq) + lpfc_sli4_release_hdwq(phba->sli4_hba.hdwq, + phba->cfg_hdw_queue); + phba->sli4_hba.hdwq = NULL; if (phba->nvmet_support) { lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_cqset, @@ -8927,7 +8833,7 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq, cq->chann = qidx; if (qtype != LPFC_MBOX) { - /* Setup nvme_cq_map for fast lookup */ + /* Setup cq_map for fast lookup */ if (cq_map) *cq_map = cq->queue_id; @@ -8990,9 +8896,10 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) { uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; + struct lpfc_sli4_hdw_queue *qp; LPFC_MBOXQ_t *mboxq; int qidx; - uint32_t length, io_channel; + uint32_t length; int rc = -ENOMEM; /* Check for dual-ULP support */ @@ -9043,25 +8950,25 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) /* * Set up HBA Event Queues (EQs) */ - io_channel = phba->io_channel_irqs; + qp = phba->sli4_hba.hdwq; /* Set up HBA event queue */ - if (io_channel && !phba->sli4_hba.hba_eq) { + if (!qp) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3147 Fast-path EQs not allocated\n"); rc = -ENOMEM; goto out_error; } - for (qidx = 0; qidx < io_channel; qidx++) { - if (!phba->sli4_hba.hba_eq[qidx]) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + if (!qp[qidx].hba_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0522 Fast-path EQ (%d) not " "allocated\n", qidx); rc = -ENOMEM; goto out_destroy; } - rc = lpfc_eq_create(phba, phba->sli4_hba.hba_eq[qidx], - phba->cfg_fcp_imax); + rc = lpfc_eq_create(phba, qp[qidx].hba_eq, + phba->cfg_fcp_imax); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0523 Failed setup of fast-path EQ " @@ -9070,26 +8977,17 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) goto out_destroy; } lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2584 HBA EQ setup: queue[%d]-id=%d\n", - qidx, phba->sli4_hba.hba_eq[qidx]->queue_id); + "2584 HBA EQ setup: queue[%d]-id=%d\n", qidx, + qp[qidx].hba_eq->queue_id); } - if (phba->cfg_nvme_io_channel) { - if (!phba->sli4_hba.nvme_cq || !phba->sli4_hba.nvme_wq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6084 Fast-path NVME %s array not allocated\n", - (phba->sli4_hba.nvme_cq) ? "CQ" : "WQ"); - rc = -ENOMEM; - goto out_destroy; - } - - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { rc = lpfc_create_wq_cq(phba, - phba->sli4_hba.hba_eq[ - qidx % io_channel], - phba->sli4_hba.nvme_cq[qidx], - phba->sli4_hba.nvme_wq[qidx], - &phba->sli4_hba.nvme_cq_map[qidx], + qp[qidx].hba_eq, + qp[qidx].nvme_cq, + qp[qidx].nvme_wq, + &phba->sli4_hba.hdwq[qidx].nvme_cq_map, qidx, LPFC_NVME); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -9101,31 +8999,19 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) } } - if (phba->cfg_fcp_io_channel) { - /* Set up fast-path FCP Response Complete Queue */ - if (!phba->sli4_hba.fcp_cq || !phba->sli4_hba.fcp_wq) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + rc = lpfc_create_wq_cq(phba, + qp[qidx].hba_eq, + qp[qidx].fcp_cq, + qp[qidx].fcp_wq, + &phba->sli4_hba.hdwq[qidx].fcp_cq_map, + qidx, LPFC_FCP); + if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3148 Fast-path FCP %s array not allocated\n", - phba->sli4_hba.fcp_cq ? "WQ" : "CQ"); - rc = -ENOMEM; - goto out_destroy; - } - - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) { - rc = lpfc_create_wq_cq(phba, - phba->sli4_hba.hba_eq[ - qidx % io_channel], - phba->sli4_hba.fcp_cq[qidx], - phba->sli4_hba.fcp_wq[qidx], - &phba->sli4_hba.fcp_cq_map[qidx], - qidx, LPFC_FCP); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0535 Failed to setup fastpath " "FCP WQ/CQ (%d), rc = 0x%x\n", qidx, (uint32_t)rc); - goto out_destroy; - } + goto out_destroy; } } @@ -9144,7 +9030,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) goto out_destroy; } - rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0], + rc = lpfc_create_wq_cq(phba, qp[0].hba_eq, phba->sli4_hba.mbx_cq, phba->sli4_hba.mbx_wq, NULL, 0, LPFC_MBOX); @@ -9165,7 +9051,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) if (phba->cfg_nvmet_mrq > 1) { rc = lpfc_cq_create_set(phba, phba->sli4_hba.nvmet_cqset, - phba->sli4_hba.hba_eq, + qp, LPFC_WCQ, LPFC_NVMET); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -9177,7 +9063,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) } else { /* Set up NVMET Receive Complete Queue */ rc = lpfc_cq_create(phba, phba->sli4_hba.nvmet_cqset[0], - phba->sli4_hba.hba_eq[0], + qp[0].hba_eq, LPFC_WCQ, LPFC_NVMET); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -9191,7 +9077,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) "6090 NVMET CQ setup: cq-id=%d, " "parent eq-id=%d\n", phba->sli4_hba.nvmet_cqset[0]->queue_id, - phba->sli4_hba.hba_eq[0]->queue_id); + qp[0].hba_eq->queue_id); } } @@ -9203,14 +9089,14 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) rc = -ENOMEM; goto out_destroy; } - rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0], - phba->sli4_hba.els_cq, - phba->sli4_hba.els_wq, - NULL, 0, LPFC_ELS); + rc = lpfc_create_wq_cq(phba, qp[0].hba_eq, + phba->sli4_hba.els_cq, + phba->sli4_hba.els_wq, + NULL, 0, LPFC_ELS); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0529 Failed setup of ELS WQ/CQ: rc = 0x%x\n", - (uint32_t)rc); + "0525 Failed setup of ELS WQ/CQ: rc = 0x%x\n", + (uint32_t)rc); goto out_destroy; } lpfc_printf_log(phba, KERN_INFO, LOG_INIT, @@ -9218,7 +9104,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.els_wq->queue_id, phba->sli4_hba.els_cq->queue_id); - if (phba->cfg_nvme_io_channel) { + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { /* Set up NVME LS Complete Queue */ if (!phba->sli4_hba.nvmels_cq || !phba->sli4_hba.nvmels_wq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -9227,14 +9113,14 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) rc = -ENOMEM; goto out_destroy; } - rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0], - phba->sli4_hba.nvmels_cq, - phba->sli4_hba.nvmels_wq, - NULL, 0, LPFC_NVME_LS); + rc = lpfc_create_wq_cq(phba, qp[0].hba_eq, + phba->sli4_hba.nvmels_cq, + phba->sli4_hba.nvmels_wq, + NULL, 0, LPFC_NVME_LS); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0529 Failed setup of NVVME LS WQ/CQ: " - "rc = 0x%x\n", (uint32_t)rc); + "0526 Failed setup of NVVME LS WQ/CQ: " + "rc = 0x%x\n", (uint32_t)rc); goto out_destroy; } @@ -9320,7 +9206,8 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.dat_rq->queue_id, phba->sli4_hba.els_cq->queue_id); - for (qidx = 0; qidx < io_channel; qidx += LPFC_MAX_EQ_DELAY_EQID_CNT) + for (qidx = 0; qidx < phba->cfg_hdw_queue; + qidx += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT, phba->cfg_fcp_imax); @@ -9347,6 +9234,7 @@ out_error: void lpfc_sli4_queue_unset(struct lpfc_hba *phba) { + struct lpfc_sli4_hdw_queue *qp; int qidx; /* Unset mailbox command work queue */ @@ -9366,17 +9254,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq); - /* Unset FCP work queue */ - if (phba->sli4_hba.fcp_wq) - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) - lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[qidx]); - - /* Unset NVME work queue */ - if (phba->sli4_hba.nvme_wq) { - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) - lpfc_wq_destroy(phba, phba->sli4_hba.nvme_wq[qidx]); - } - /* Unset mailbox command complete queue */ if (phba->sli4_hba.mbx_cq) lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq); @@ -9389,11 +9266,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) if (phba->sli4_hba.nvmels_cq) lpfc_cq_destroy(phba, phba->sli4_hba.nvmels_cq); - /* Unset NVME response complete queue */ - if (phba->sli4_hba.nvme_cq) - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) - lpfc_cq_destroy(phba, phba->sli4_hba.nvme_cq[qidx]); - if (phba->nvmet_support) { /* Unset NVMET MRQ queue */ if (phba->sli4_hba.nvmet_mrq_hdr) { @@ -9412,15 +9284,17 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) } } - /* Unset FCP response complete queue */ - if (phba->sli4_hba.fcp_cq) - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) - lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[qidx]); - - /* Unset fast-path event queue */ - if (phba->sli4_hba.hba_eq) - for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) - lpfc_eq_destroy(phba, phba->sli4_hba.hba_eq[qidx]); + /* Unset fast-path SLI4 queues */ + if (phba->sli4_hba.hdwq) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + qp = &phba->sli4_hba.hdwq[qidx]; + lpfc_wq_destroy(phba, qp->fcp_wq); + lpfc_wq_destroy(phba, qp->nvme_wq); + lpfc_cq_destroy(phba, qp->fcp_cq); + lpfc_cq_destroy(phba, qp->nvme_cq); + lpfc_eq_destroy(phba, qp->hba_eq); + } + } } /** @@ -10246,7 +10120,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) if (vec >= vectors) vec = 0; index++; - if (index >= phba->cfg_fcp_io_channel) + if (index >= phba->cfg_hdw_queue) index = 0; cpup++; } @@ -10271,7 +10145,7 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) char *name; /* Set up MSI-X multi-message vectors */ - vectors = phba->io_channel_irqs; + vectors = phba->cfg_hdw_queue; rc = pci_alloc_irq_vectors(phba->pcidev, (phba->nvmet_support) ? 1 : 2, @@ -10305,19 +10179,15 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) } } - if (vectors != phba->io_channel_irqs) { + if (vectors != phba->cfg_hdw_queue) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3238 Reducing IO channels to match number of " "MSI-X vectors, requested %d got %d\n", - phba->io_channel_irqs, vectors); - if (phba->cfg_fcp_io_channel > vectors) - phba->cfg_fcp_io_channel = vectors; - if (phba->cfg_nvme_io_channel > vectors) - phba->cfg_nvme_io_channel = vectors; - if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel) - phba->io_channel_irqs = phba->cfg_fcp_io_channel; - else - phba->io_channel_irqs = phba->cfg_nvme_io_channel; + phba->cfg_hdw_queue, vectors); + if (phba->cfg_hdw_queue > vectors) + phba->cfg_hdw_queue = vectors; + if (phba->cfg_nvmet_mrq > vectors) + phba->cfg_nvmet_mrq = vectors; } lpfc_cpu_affinity_check(phba, vectors); @@ -10374,7 +10244,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba) return rc; } - for (index = 0; index < phba->io_channel_irqs; index++) { + for (index = 0; index < phba->cfg_hdw_queue; index++) { phba->sli4_hba.hba_eq_hdl[index].idx = index; phba->sli4_hba.hba_eq_hdl[index].phba = phba; } @@ -10439,7 +10309,7 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) phba->intr_type = INTx; intr_mode = 0; - for (idx = 0; idx < phba->io_channel_irqs; idx++) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { eqhdl = &phba->sli4_hba.hba_eq_hdl[idx]; eqhdl->idx = idx; eqhdl->phba = phba; @@ -10467,7 +10337,7 @@ lpfc_sli4_disable_intr(struct lpfc_hba *phba) int index; /* Free up MSI-X multi-message vectors */ - for (index = 0; index < phba->io_channel_irqs; index++) + for (index = 0; index < phba->cfg_hdw_queue; index++) free_irq(pci_irq_vector(phba->pcidev, index), &phba->sli4_hba.hba_eq_hdl[index]); } else { @@ -10620,7 +10490,8 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) struct pci_dev *pdev = phba->pcidev; lpfc_stop_hba_timers(phba); - phba->sli4_hba.intr_enable = 0; + if (phba->pport) + phba->sli4_hba.intr_enable = 0; /* * Gracefully wait out the potential current outstanding asynchronous @@ -10839,8 +10710,6 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->nvme_support = 0; phba->nvmet_support = 0; phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_OFF; - phba->cfg_nvme_io_channel = 0; - phba->io_channel_irqs = phba->cfg_fcp_io_channel; lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_NVME, "6101 Disabling NVME support: " "Not supported by firmware: %d %d\n", @@ -11792,28 +11661,11 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Get the default values for Model Name and Description */ lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc); - /* Create SCSI host to the physical port */ - error = lpfc_create_shost(phba); - if (error) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1415 Failed to create scsi host.\n"); - goto out_unset_driver_resource; - } - - /* Configure sysfs attributes */ - vport = phba->pport; - error = lpfc_alloc_sysfs_attr(vport); - if (error) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1416 Failed to allocate sysfs attr\n"); - goto out_destroy_shost; - } - - shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */ /* Now, trying to enable interrupt and bring up the device */ cfg_mode = phba->cfg_use_msi; /* Put device to a known state before enabling interrupt */ + phba->pport = NULL; lpfc_stop_port(phba); /* Configure and enable interrupt */ @@ -11822,18 +11674,33 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0426 Failed to enable interrupt.\n"); error = -ENODEV; - goto out_free_sysfs_attr; + goto out_unset_driver_resource; } /* Default to single EQ for non-MSI-X */ if (phba->intr_type != MSIX) { - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) - phba->cfg_fcp_io_channel = 1; + phba->cfg_hdw_queue = 1; if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - phba->cfg_nvme_io_channel = 1; if (phba->nvmet_support) phba->cfg_nvmet_mrq = 1; } - phba->io_channel_irqs = 1; + } + + /* Create SCSI host to the physical port */ + error = lpfc_create_shost(phba); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "1415 Failed to create scsi host.\n"); + goto out_disable_intr; + } + vport = phba->pport; + shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */ + + /* Configure sysfs attributes */ + error = lpfc_alloc_sysfs_attr(vport); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "1416 Failed to allocate sysfs attr\n"); + goto out_destroy_shost; } /* Set up SLI-4 HBA */ @@ -11841,7 +11708,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "1421 Failed to set up hba\n"); error = -ENODEV; - goto out_disable_intr; + goto out_free_sysfs_attr; } /* Log the current active interrupt mode */ @@ -11877,7 +11744,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) phba, phba->sli4_hba.common_xri_max); if (len == 0) { error = -ENOMEM; - goto out_disable_intr; + goto out_free_sysfs_attr; } phba->total_common_bufs += len; } @@ -11895,12 +11762,12 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) return 0; -out_disable_intr: - lpfc_sli4_disable_intr(phba); out_free_sysfs_attr: lpfc_free_sysfs_attr(vport); out_destroy_shost: lpfc_destroy_shost(phba); +out_disable_intr: + lpfc_sli4_disable_intr(phba); out_unset_driver_resource: lpfc_unset_driver_resource_phase2(phba); out_unset_driver_resource_s4: diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 1068a5a7b126..c0f59fbaa173 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -239,7 +239,7 @@ lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport, if (qidx) { str = "IO "; /* IO queue */ qhandle->index = ((qidx - 1) % - vport->phba->cfg_nvme_io_channel); + vport->phba->cfg_hdw_queue); } else { str = "ADM"; /* Admin queue */ qhandle->index = qidx; @@ -247,7 +247,7 @@ lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport, lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, "6073 Binding %s HdwQueue %d (cpu %d) to " - "io_channel %d qhandle %p\n", str, + "hdw_queue %d qhandle %p\n", str, qidx, qhandle->cpu_id, qhandle->index, qhandle); *handle = (void *)qhandle; return 0; @@ -2083,10 +2083,10 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) * allocate + 3, one for cmd, one for rsp and one for this alignment */ lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1; - lpfc_nvme_template.max_hw_queues = phba->cfg_nvme_io_channel; + lpfc_nvme_template.max_hw_queues = phba->cfg_hdw_queue; cstat = kmalloc((sizeof(struct lpfc_nvme_ctrl_stat) * - phba->cfg_nvme_io_channel), GFP_KERNEL); + phba->cfg_hdw_queue), GFP_KERNEL); if (!cstat) return -ENOMEM; @@ -2130,7 +2130,7 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) atomic_set(&lport->fc4NvmeLsRequests, 0); atomic_set(&lport->fc4NvmeLsCmpls, 0); - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { cstat = &lport->cstat[i]; atomic_set(&cstat->fc4NvmeInputRequests, 0); atomic_set(&cstat->fc4NvmeOutputRequests, 0); @@ -2587,14 +2587,14 @@ lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba) struct lpfc_sli_ring *pring; u32 i, wait_cnt = 0; - if (phba->sli_rev < LPFC_SLI_REV4 || !phba->sli4_hba.nvme_wq) + if (phba->sli_rev < LPFC_SLI_REV4 || !phba->sli4_hba.hdwq) return; /* Cycle through all NVME rings and make sure all outstanding * WQEs have been removed from the txcmplqs. */ - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { - pring = phba->sli4_hba.nvme_wq[i]->pring; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].nvme_wq->pring; if (!pring) continue; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 6245f442d784..c64a8234d5bd 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -973,7 +973,7 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, * WQE release CQE */ ctxp->flag |= LPFC_NVMET_DEFER_WQFULL; - wq = phba->sli4_hba.nvme_wq[rsp->hwqid]; + wq = phba->sli4_hba.hdwq[rsp->hwqid].nvme_wq; pring = wq->pring; spin_lock_irqsave(&pring->ring_lock, iflags); list_add_tail(&nvmewqeq->list, &wq->wqfull_list); @@ -1047,7 +1047,7 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) { lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid); - wq = phba->sli4_hba.nvme_wq[ctxp->wqeq->hba_wqidx]; + wq = phba->sli4_hba.hdwq[ctxp->wqeq->hba_wqidx].nvme_wq; spin_unlock_irqrestore(&ctxp->ctxlock, flags); lpfc_nvmet_wqfull_flush(phba, wq, ctxp); return; @@ -1377,7 +1377,7 @@ lpfc_nvmet_create_targetport(struct lpfc_hba *phba) * allocate + 3, one for cmd, one for rsp and one for this alignment */ lpfc_tgttemplate.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1; - lpfc_tgttemplate.max_hw_queues = phba->cfg_nvme_io_channel; + lpfc_tgttemplate.max_hw_queues = phba->cfg_hdw_queue; lpfc_tgttemplate.target_features = NVMET_FCTGTFEAT_READDATA_RSP; #if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) @@ -1697,8 +1697,8 @@ lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba) return; if (phba->targetport) { tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { - wq = phba->sli4_hba.nvme_wq[qidx]; + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + wq = phba->sli4_hba.hdwq[qidx].nvme_wq; lpfc_nvmet_wqfull_flush(phba, wq, NULL); } init_completion(&tgtp->tport_unreg_done); diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 9793016046de..63841f49affb 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -3661,8 +3661,8 @@ int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, return hwq; } - if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU - && phba->cfg_fcp_io_channel > 1) { + if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU && + phba->cfg_hdw_queue > 1) { cpu = lpfc_cmd->cpu; if (cpu < phba->sli4_hba.num_present_cpu) { cpup = phba->sli4_hba.cpu_map; @@ -3671,7 +3671,7 @@ int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, } } chann = atomic_add_return(1, &phba->fcp_qidx); - chann = chann % phba->cfg_fcp_io_channel; + chann = chann % phba->cfg_hdw_queue; return chann; } @@ -4598,7 +4598,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) iocb = &lpfc_cmd->cur_iocbq; if (phba->sli_rev == LPFC_SLI_REV4) { - pring_s4 = phba->sli4_hba.fcp_wq[iocb->hba_wqidx]->pring; + pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].fcp_wq->pring; if (!pring_s4) { ret = FAILED; goto out_unlock; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 30a786304848..d41bfa4569a4 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -3981,8 +3981,8 @@ lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba) /* Look on all the FCP Rings for the iotag */ if (phba->sli_rev >= LPFC_SLI_REV4) { - for (i = 0; i < phba->cfg_fcp_io_channel; i++) { - pring = phba->sli4_hba.fcp_wq[i]->pring; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].fcp_wq->pring; lpfc_sli_abort_iocb_ring(phba, pring); } } else { @@ -4006,12 +4006,13 @@ lpfc_sli_abort_nvme_rings(struct lpfc_hba *phba) struct lpfc_sli_ring *pring; uint32_t i; - if (phba->sli_rev < LPFC_SLI_REV4) + if ((phba->sli_rev < LPFC_SLI_REV4) || + !(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return; /* Abort all IO on each NVME ring. */ - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { - pring = phba->sli4_hba.nvme_wq[i]->pring; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].nvme_wq->pring; lpfc_sli_abort_wqe_ring(phba, pring); } } @@ -4044,8 +4045,8 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) /* Look on all the FCP Rings for the iotag */ if (phba->sli_rev >= LPFC_SLI_REV4) { - for (i = 0; i < phba->cfg_fcp_io_channel; i++) { - pring = phba->sli4_hba.fcp_wq[i]->pring; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].fcp_wq->pring; spin_lock_irq(&pring->ring_lock); /* Retrieve everything on txq */ @@ -4110,7 +4111,8 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba) uint32_t i; struct lpfc_iocbq *piocb, *next_iocb; - if (phba->sli_rev < LPFC_SLI_REV4) + if ((phba->sli_rev < LPFC_SLI_REV4) || + !(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return; /* Hint to other driver operations that a flush is in progress. */ @@ -4122,8 +4124,8 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba) * a local driver reason code. This is a flush so no * abort exchange to FW. */ - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { - pring = phba->sli4_hba.nvme_wq[i]->pring; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].nvme_wq->pring; spin_lock_irq(&pring->ring_lock); list_for_each_entry_safe(piocb, next_iocb, @@ -5564,6 +5566,7 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) { int qidx; struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba; + struct lpfc_sli4_hdw_queue *qp; sli4_hba->sli4_cq_release(sli4_hba->mbx_cq, LPFC_QUEUE_REARM); sli4_hba->sli4_cq_release(sli4_hba->els_cq, LPFC_QUEUE_REARM); @@ -5571,20 +5574,19 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) sli4_hba->sli4_cq_release(sli4_hba->nvmels_cq, LPFC_QUEUE_REARM); - if (sli4_hba->fcp_cq) - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) - sli4_hba->sli4_cq_release(sli4_hba->fcp_cq[qidx], + qp = sli4_hba->hdwq; + if (sli4_hba->hdwq) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + sli4_hba->sli4_cq_release(qp[qidx].fcp_cq, LPFC_QUEUE_REARM); - - if (sli4_hba->nvme_cq) - for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) - sli4_hba->sli4_cq_release(sli4_hba->nvme_cq[qidx], + sli4_hba->sli4_cq_release(qp[qidx].nvme_cq, LPFC_QUEUE_REARM); + } - if (sli4_hba->hba_eq) - for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) - sli4_hba->sli4_eq_release(sli4_hba->hba_eq[qidx], - LPFC_QUEUE_REARM); + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) + sli4_hba->sli4_eq_release(qp[qidx].hba_eq, + LPFC_QUEUE_REARM); + } if (phba->nvmet_support) { for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) { @@ -7857,11 +7859,11 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba) /* Find the eq associated with the mcq */ - if (sli4_hba->hba_eq) - for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++) - if (sli4_hba->hba_eq[eqidx]->queue_id == + if (sli4_hba->hdwq) + for (eqidx = 0; eqidx < phba->cfg_hdw_queue; eqidx++) + if (sli4_hba->hdwq[eqidx].hba_eq->queue_id == sli4_hba->mbx_cq->assoc_qid) { - fpeq = sli4_hba->hba_eq[eqidx]; + fpeq = sli4_hba->hdwq[eqidx].hba_eq; break; } if (!fpeq) @@ -9866,7 +9868,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, /* Get the WQ */ if ((piocb->iocb_flag & LPFC_IO_FCP) || (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - wq = phba->sli4_hba.fcp_wq[piocb->hba_wqidx]; + wq = phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq; } else { wq = phba->sli4_hba.els_wq; } @@ -10001,7 +10003,7 @@ struct lpfc_sli_ring * lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) { if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { - if (unlikely(!phba->sli4_hba.fcp_wq)) + if (unlikely(!phba->sli4_hba.hdwq)) return NULL; /* * for abort iocb hba_wqidx should already @@ -10012,9 +10014,9 @@ lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) lpfc_sli4_scmd_to_wqidx_distr( phba, piocb->context1); piocb->hba_wqidx = piocb->hba_wqidx % - phba->cfg_fcp_io_channel; + phba->cfg_hdw_queue; } - return phba->sli4_hba.fcp_wq[piocb->hba_wqidx]->pring; + return phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq->pring; } else { if (unlikely(!phba->sli4_hba.els_wq)) return NULL; @@ -10063,7 +10065,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use)) { /* Get associated EQ with this index */ - fpeq = phba->sli4_hba.hba_eq[idx]; + fpeq = phba->sli4_hba.hdwq[idx].hba_eq; /* Turn off interrupts from this EQ */ phba->sli4_hba.sli4_eq_clr_intr(fpeq); @@ -10497,17 +10499,8 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) INIT_LIST_HEAD(&psli->mboxq); INIT_LIST_HEAD(&psli->mboxq_cmpl); /* Initialize list headers for txq and txcmplq as double linked lists */ - for (i = 0; i < phba->cfg_fcp_io_channel; i++) { - pring = phba->sli4_hba.fcp_wq[i]->pring; - pring->flag = 0; - pring->ringno = LPFC_FCP_RING; - INIT_LIST_HEAD(&pring->txq); - INIT_LIST_HEAD(&pring->txcmplq); - INIT_LIST_HEAD(&pring->iocb_continueq); - spin_lock_init(&pring->ring_lock); - } - for (i = 0; i < phba->cfg_nvme_io_channel; i++) { - pring = phba->sli4_hba.nvme_wq[i]->pring; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].fcp_wq->pring; pring->flag = 0; pring->ringno = LPFC_FCP_RING; INIT_LIST_HEAD(&pring->txq); @@ -10523,7 +10516,16 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) INIT_LIST_HEAD(&pring->iocb_continueq); spin_lock_init(&pring->ring_lock); - if (phba->cfg_nvme_io_channel) { + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { + pring = phba->sli4_hba.hdwq[i].nvme_wq->pring; + pring->flag = 0; + pring->ringno = LPFC_FCP_RING; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + spin_lock_init(&pring->ring_lock); + } pring = phba->sli4_hba.nvmels_wq->pring; pring->flag = 0; pring->ringno = LPFC_ELS_RING; @@ -14070,6 +14072,20 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, /* Get the reference to the corresponding CQ */ cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); + /* First check for NVME/SCSI completion */ + if (cqid == phba->sli4_hba.hdwq[qidx].nvme_cq_map) { + /* Process NVME / NVMET command completion */ + cq = phba->sli4_hba.hdwq[qidx].nvme_cq; + goto process_cq; + } + + if (cqid == phba->sli4_hba.hdwq[qidx].fcp_cq_map) { + /* Process FCP command completion */ + cq = phba->sli4_hba.hdwq[qidx].fcp_cq; + goto process_cq; + } + + /* Next check for NVMET completion */ if (phba->cfg_nvmet_mrq && phba->sli4_hba.nvmet_cqset) { id = phba->sli4_hba.nvmet_cqset[0]->queue_id; if ((cqid >= id) && (cqid < (id + phba->cfg_nvmet_mrq))) { @@ -14079,20 +14095,6 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, } } - if (phba->sli4_hba.nvme_cq_map && - (cqid == phba->sli4_hba.nvme_cq_map[qidx])) { - /* Process NVME / NVMET command completion */ - cq = phba->sli4_hba.nvme_cq[qidx]; - goto process_cq; - } - - if (phba->sli4_hba.fcp_cq_map && - (cqid == phba->sli4_hba.fcp_cq_map[qidx])) { - /* Process FCP command completion */ - cq = phba->sli4_hba.fcp_cq[qidx]; - goto process_cq; - } - if (phba->sli4_hba.nvmels_cq && (cqid == phba->sli4_hba.nvmels_cq->queue_id)) { /* Process NVME unsol rcv */ @@ -14101,7 +14103,8 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, /* Otherwise this is a Slow path event */ if (cq == NULL) { - lpfc_sli4_sp_handle_eqe(phba, eqe, phba->sli4_hba.hba_eq[qidx]); + lpfc_sli4_sp_handle_eqe(phba, eqe, + phba->sli4_hba.hdwq[qidx].hba_eq); return; } @@ -14115,7 +14118,7 @@ process_cq: } /* Save EQ associated with this CQ */ - cq->assoc_qp = phba->sli4_hba.hba_eq[qidx]; + cq->assoc_qp = phba->sli4_hba.hdwq[qidx].hba_eq; if (!queue_work(phba->wq, &cq->irqwork)) lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -14236,11 +14239,11 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) if (unlikely(!phba)) return IRQ_NONE; - if (unlikely(!phba->sli4_hba.hba_eq)) + if (unlikely(!phba->sli4_hba.hdwq)) return IRQ_NONE; /* Get to the EQ struct associated with this vector */ - fpeq = phba->sli4_hba.hba_eq[hba_eqidx]; + fpeq = phba->sli4_hba.hdwq[hba_eqidx].hba_eq; if (unlikely(!fpeq)) return IRQ_NONE; @@ -14340,7 +14343,7 @@ lpfc_sli4_intr_handler(int irq, void *dev_id) /* * Invoke fast-path host attention interrupt handling as appropriate. */ - for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) { + for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { hba_irq_rc = lpfc_sli4_hba_intr_handler(irq, &phba->sli4_hba.hba_eq_hdl[qidx]); if (hba_irq_rc == IRQ_HANDLED) @@ -14527,7 +14530,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, union lpfc_sli4_cfg_shdr *shdr; uint16_t dmult; - if (startq >= phba->io_channel_irqs) + if (startq >= phba->cfg_hdw_queue) return 0; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -14541,7 +14544,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, eq_delay = &mbox->u.mqe.un.eq_delay; /* Calculate delay multiper from maximum interrupt per second */ - result = imax / phba->io_channel_irqs; + result = imax / phba->cfg_hdw_queue; if (result > LPFC_DMULT_CONST || result == 0) dmult = 0; else @@ -14550,8 +14553,8 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, dmult = LPFC_DMULT_MAX; cnt = 0; - for (qidx = startq; qidx < phba->io_channel_irqs; qidx++) { - eq = phba->sli4_hba.hba_eq[qidx]; + for (qidx = startq; qidx < phba->cfg_hdw_queue; qidx++) { + eq = phba->sli4_hba.hdwq[qidx].hba_eq; if (!eq) continue; eq->q_mode = imax; @@ -14568,8 +14571,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, val = phba->cfg_fcp_imax; if (val) { /* First, interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / - phba->io_channel_irqs; + val = phba->cfg_fcp_imax / phba->cfg_hdw_queue; /* us delay between each interrupt */ val = LPFC_SEC_TO_USEC / val; @@ -14877,7 +14879,7 @@ out: * lpfc_cq_create_set - Create a set of Completion Queues on the HBA for MRQ * @phba: HBA structure that indicates port to create a queue on. * @cqp: The queue structure array to use to create the completion queues. - * @eqp: The event queue array to bind these completion queues to. + * @hdwq: The hardware queue array with the EQ to bind completion queues to. * * This function creates a set of completion queue, s to support MRQ * as detailed in @cqp, on a port, @@ -14897,7 +14899,8 @@ out: **/ int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, - struct lpfc_queue **eqp, uint32_t type, uint32_t subtype) + struct lpfc_sli4_hdw_queue *hdwq, uint32_t type, + uint32_t subtype) { struct lpfc_queue *cq; struct lpfc_queue *eq; @@ -14912,7 +14915,7 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, /* sanity check on queue memory */ numcq = phba->cfg_nvmet_mrq; - if (!cqp || !eqp || !numcq) + if (!cqp || !hdwq || !numcq) return -ENODEV; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -14939,7 +14942,7 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, for (idx = 0; idx < numcq; idx++) { cq = cqp[idx]; - eq = eqp[idx]; + eq = hdwq[idx].hba_eq; if (!cq || !eq) { status = -ENOMEM; goto out; @@ -19462,7 +19465,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) if (phba->link_flag & LS_MDS_LOOPBACK) { /* MDS WQE are posted only to first WQ*/ - wq = phba->sli4_hba.fcp_wq[0]; + wq = phba->sli4_hba.hdwq[0].fcp_wq; if (unlikely(!wq)) return 0; pring = wq->pring; @@ -19712,12 +19715,12 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, /* NVME_FCREQ and NVME_ABTS requests */ if (pwqe->iocb_flag & LPFC_IO_NVME) { /* Get the IO distribution (hba_wqidx) for WQ assignment. */ - pring = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]->pring; + pring = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq->pring; spin_lock_irqsave(&pring->ring_lock, iflags); - wq = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]; + wq = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq; bf_set(wqe_cqid, &wqe->generic.wqe_com, - phba->sli4_hba.nvme_cq[pwqe->hba_wqidx]->queue_id); + phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_cq->queue_id); ret = lpfc_sli4_wq_put(wq, wqe); if (ret) { spin_unlock_irqrestore(&pring->ring_lock, iflags); @@ -19731,7 +19734,7 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, /* NVMET requests */ if (pwqe->iocb_flag & LPFC_IO_NVMET) { /* Get the IO distribution (hba_wqidx) for WQ assignment. */ - pring = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]->pring; + pring = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq->pring; spin_lock_irqsave(&pring->ring_lock, iflags); ctxp = pwqe->context2; @@ -19742,9 +19745,9 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, } bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com, pwqe->sli4_xritag); - wq = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]; + wq = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq; bf_set(wqe_cqid, &wqe->generic.wqe_com, - phba->sli4_hba.nvme_cq[pwqe->hba_wqidx]->queue_id); + phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_cq->queue_id); ret = lpfc_sli4_wq_put(wq, wqe); if (ret) { spin_unlock_irqrestore(&pring->ring_lock, iflags); diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 645620175453..6e1e958df10a 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -36,18 +36,13 @@ #define LPFC_NEMBED_MBOX_SGL_CNT 254 /* Multi-queue arrangement for FCP EQ/CQ/WQ tuples */ -#define LPFC_HBA_IO_CHAN_MIN 0 -#define LPFC_HBA_IO_CHAN_MAX 32 -#define LPFC_FCP_IO_CHAN_DEF 4 -#define LPFC_NVME_IO_CHAN_DEF 0 +#define LPFC_HBA_HDWQ_MIN 0 +#define LPFC_HBA_HDWQ_MAX 64 +#define LPFC_HBA_HDWQ_DEF 0 /* Common buffer size to accomidate SCSI and NVME IO buffers */ #define LPFC_COMMON_IO_BUF_SZ 768 -/* Number of channels used for Flash Optimized Fabric (FOF) operations */ - -#define LPFC_FOF_IO_CHAN_NUM 1 - /* * Provide the default FCF Record attributes used by the driver * when nonFIP mode is configured and there is no other default @@ -534,6 +529,17 @@ struct lpfc_vector_map_info { #define LPFC_VECTOR_MAP_EMPTY 0xffff /* SLI4 HBA data structure entries */ +struct lpfc_sli4_hdw_queue { + /* Pointers to the constructed SLI4 queues */ + struct lpfc_queue *hba_eq; /* Event queues for HBA */ + struct lpfc_queue *fcp_cq; /* Fast-path FCP compl queue */ + struct lpfc_queue *nvme_cq; /* Fast-path NVME compl queue */ + struct lpfc_queue *fcp_wq; /* Fast-path FCP work queue */ + struct lpfc_queue *nvme_wq; /* Fast-path NVME work queue */ + uint16_t fcp_cq_map; + uint16_t nvme_cq_map; +}; + struct lpfc_sli4_hba { void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for * config space registers @@ -606,17 +612,13 @@ struct lpfc_sli4_hba { uint32_t (*sli4_cq_release)(struct lpfc_queue *q, bool arm); /* Pointers to the constructed SLI4 queues */ - struct lpfc_queue **hba_eq; /* Event queues for HBA */ - struct lpfc_queue **fcp_cq; /* Fast-path FCP compl queue */ - struct lpfc_queue **nvme_cq; /* Fast-path NVME compl queue */ + struct lpfc_sli4_hdw_queue *hdwq; + struct list_head lpfc_wq_list; + + /* Pointers to the constructed SLI4 queues for NVMET */ struct lpfc_queue **nvmet_cqset; /* Fast-path NVMET CQ Set queues */ struct lpfc_queue **nvmet_mrq_hdr; /* Fast-path NVMET hdr MRQs */ struct lpfc_queue **nvmet_mrq_data; /* Fast-path NVMET data MRQs */ - struct lpfc_queue **fcp_wq; /* Fast-path FCP work queue */ - struct lpfc_queue **nvme_wq; /* Fast-path NVME work queue */ - uint16_t *fcp_cq_map; - uint16_t *nvme_cq_map; - struct list_head lpfc_wq_list; struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */ struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */ @@ -817,7 +819,7 @@ int lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, int lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t, uint32_t); int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, - struct lpfc_queue **eqp, uint32_t type, + struct lpfc_sli4_hdw_queue *hdwq, uint32_t type, uint32_t subtype); int32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t); From 5e5b511d8bfaf765cb92a695cda336c936cb86dc Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:22 -0800 Subject: [PATCH 1207/4277] scsi: lpfc: Partition XRI buffer list across Hardware Queues Once the IO buff allocations were made shared, there was a single XRI buffer list shared by all hardware queues. A single list isn't great for performance when shared across the per-cpu hardware queues. Create a separate XRI IO buffer get/put list for each Hardware Queue. As SGLs and associated IO buffers get allocated/posted to the firmware; round robin their assignment across all available hardware Queues so that there is an equitable assignment. Modify SCSI and NVME IO submit code paths to use the Hardware Queue logic for XRI allocation. Add a debugfs interface to display hardware queue statistics Added new empty_io_bufs counter to track if a cpu runs out of XRIs. Replace common_ variables/names with io_ to make meanings clearer. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 8 +- drivers/scsi/lpfc/lpfc_attr.c | 2 +- drivers/scsi/lpfc/lpfc_crtn.h | 10 +- drivers/scsi/lpfc/lpfc_debugfs.c | 141 +++++++++- drivers/scsi/lpfc/lpfc_debugfs.h | 3 + drivers/scsi/lpfc/lpfc_init.c | 445 ++++++++++++++++++++----------- drivers/scsi/lpfc/lpfc_nvme.c | 90 ++++--- drivers/scsi/lpfc/lpfc_nvme.h | 3 +- drivers/scsi/lpfc/lpfc_nvmet.c | 22 +- drivers/scsi/lpfc/lpfc_scsi.c | 107 +++++--- drivers/scsi/lpfc/lpfc_scsi.h | 3 +- drivers/scsi/lpfc/lpfc_sli.c | 88 +++--- drivers/scsi/lpfc/lpfc_sli.h | 1 + drivers/scsi/lpfc/lpfc_sli4.h | 38 ++- 14 files changed, 623 insertions(+), 338 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index da12476dd933..19827ce7a4d9 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -965,13 +965,6 @@ struct lpfc_hba { struct list_head lpfc_scsi_buf_list_get; struct list_head lpfc_scsi_buf_list_put; uint32_t total_scsi_bufs; - spinlock_t common_buf_list_get_lock; /* Common buf alloc list lock */ - spinlock_t common_buf_list_put_lock; /* Common buf free list lock */ - struct list_head lpfc_common_buf_list_get; - struct list_head lpfc_common_buf_list_put; - uint32_t total_common_bufs; - uint32_t get_common_bufs; - uint32_t put_common_bufs; struct list_head lpfc_iocb_list; uint32_t total_iocbq_bufs; struct list_head active_rrq_list; @@ -1045,6 +1038,7 @@ struct lpfc_hba { struct dentry *debug_nvmeio_trc; struct lpfc_debugfs_nvmeio_trc *nvmeio_trc; + struct dentry *debug_hdwqinfo; atomic_t nvmeio_trc_cnt; uint32_t nvmeio_trc_size; uint32_t nvmeio_trc_output_idx; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index c6b1d432dd07..1671d9371d3b 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -337,7 +337,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, "XRI Dist lpfc%d Total %d IO %d ELS %d\n", phba->brd_no, phba->sli4_hba.max_cfg_param.max_xri, - phba->sli4_hba.common_xri_max, + phba->sli4_hba.io_xri_max, lpfc_sli4_get_els_iocb_cnt(phba)); if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) goto buffer_done; diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 6dc427d4228c..a623f6f619cc 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -515,10 +515,12 @@ int lpfc_sli4_read_config(struct lpfc_hba *); void lpfc_sli4_node_prep(struct lpfc_hba *); int lpfc_sli4_els_sgl_update(struct lpfc_hba *phba); int lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba); -int lpfc_sli4_common_sgl_update(struct lpfc_hba *phba); -int lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, - struct list_head *blist, int xricnt); -int lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc); +int lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *sglist); +int lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf); +int lpfc_sli4_io_sgl_update(struct lpfc_hba *phba); +int lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba, + struct list_head *blist, int xricnt); +int lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc); void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *); uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *); int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index fc5ec99bfde2..a1b7263bfe2a 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -378,6 +378,73 @@ skipit: return len; } +static int lpfc_debugfs_last_hdwq; + +/** + * lpfc_debugfs_hdwqinfo_data - Dump Hardware Queue info to a buffer + * @phba: The HBA to gather host buffer info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the Hardware Queue info from the @phba to @buf up to + * @size number of bytes. A header that describes the current hdwq state will be + * dumped to @buf first and then info on each hdwq entry will be dumped to @buf + * until @size bytes have been dumped or all the hdwq info has been dumped. + * + * Notes: + * This routine will rotate through each configured Hardware Queue each + * time called. + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_hdwqinfo_data(struct lpfc_hba *phba, char *buf, int size) +{ + struct lpfc_sli4_hdw_queue *qp; + int len = 0; + int i, out; + unsigned long iflag; + + if (phba->sli_rev != LPFC_SLI_REV4) + return 0; + + if (!phba->sli4_hba.hdwq) + return 0; + + for (i = 0; i < phba->cfg_hdw_queue; i++) { + if (len > (LPFC_HDWQINFO_SIZE - 80)) + break; + qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_hdwq]; + + len += snprintf(buf + len, size - len, "HdwQ %d Info ", i); + spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag); + spin_lock(&qp->abts_nvme_buf_list_lock); + spin_lock(&qp->io_buf_list_get_lock); + spin_lock(&qp->io_buf_list_put_lock); + out = qp->total_io_bufs - (qp->get_io_bufs + qp->put_io_bufs + + qp->abts_scsi_io_bufs + qp->abts_nvme_io_bufs); + len += snprintf(buf + len, size - len, + "tot:%d get:%d put:%d mt:%d " + "ABTS scsi:%d nvme:%d Out:%d\n", + qp->total_io_bufs, qp->get_io_bufs, qp->put_io_bufs, + qp->empty_io_bufs, qp->abts_scsi_io_bufs, + qp->abts_nvme_io_bufs, out); + spin_unlock(&qp->io_buf_list_put_lock); + spin_unlock(&qp->io_buf_list_get_lock); + spin_unlock(&qp->abts_nvme_buf_list_lock); + spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag); + + lpfc_debugfs_last_hdwq++; + if (lpfc_debugfs_last_hdwq >= phba->cfg_hdw_queue) + lpfc_debugfs_last_hdwq = 0; + } + + return len; +} + static int lpfc_debugfs_last_hba_slim_off; /** @@ -863,17 +930,17 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf + len, size - len, "\n"); cnt = 0; - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_for_each_entry_safe(ctxp, next_ctxp, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list, list) { cnt++; } - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); if (cnt) { len += snprintf(buf + len, size - len, "ABORT: %d ctx entries\n", cnt); - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_for_each_entry_safe(ctxp, next_ctxp, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list, list) { @@ -885,7 +952,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) ctxp->oxid, ctxp->state, ctxp->flag); } - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); } /* Calculate outstanding IOs */ @@ -1619,6 +1686,48 @@ out: return rc; } +/** + * lpfc_debugfs_hdwqinfo_open - Open the hdwqinfo debugfs buffer + * @inode: The inode pointer that contains a vport pointer. + * @file: The file pointer to attach the log output. + * + * Description: + * This routine is the entry point for the debugfs open file operation. It gets + * the vport from the i_private field in @inode, allocates the necessary buffer + * for the log, fills the buffer from the in-memory log for this vport, and then + * returns a pointer to that log in the private_data field in @file. + * + * Returns: + * This function returns zero if successful. On error it will return a negative + * error value. + **/ +static int +lpfc_debugfs_hdwqinfo_open(struct inode *inode, struct file *file) +{ + struct lpfc_hba *phba = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kmalloc(LPFC_HDWQINFO_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + debug->len = lpfc_debugfs_hdwqinfo_data(phba, debug->buffer, + LPFC_HBQINFO_SIZE); + file->private_data = debug; + + rc = 0; +out: + return rc; +} + /** * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer * @inode: The inode pointer that contains a vport pointer. @@ -4819,6 +4928,15 @@ static const struct file_operations lpfc_debugfs_op_hbqinfo = { .release = lpfc_debugfs_release, }; +#undef lpfc_debugfs_op_hdwqinfo +static const struct file_operations lpfc_debugfs_op_hdwqinfo = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_hdwqinfo_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .release = lpfc_debugfs_release, +}; + #undef lpfc_debugfs_op_dumpHBASlim static const struct file_operations lpfc_debugfs_op_dumpHBASlim = { .owner = THIS_MODULE, @@ -5244,6 +5362,18 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) phba->hba_debugfs_root, phba, &lpfc_debugfs_op_hbqinfo); + /* Setup hdwqinfo */ + snprintf(name, sizeof(name), "hdwqinfo"); + phba->debug_hdwqinfo = + debugfs_create_file(name, S_IFREG | 0644, + phba->hba_debugfs_root, + phba, &lpfc_debugfs_op_hdwqinfo); + if (!phba->debug_hdwqinfo) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0411 Cant create debugfs hdwqinfo\n"); + goto debug_failed; + } + /* Setup dumpHBASlim */ if (phba->sli_rev < LPFC_SLI_REV4) { snprintf(name, sizeof(name), "dumpHBASlim"); @@ -5630,6 +5760,9 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */ phba->debug_hbqinfo = NULL; + debugfs_remove(phba->debug_hdwqinfo); /* hdwqinfo */ + phba->debug_hdwqinfo = NULL; + debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */ phba->debug_dumpHBASlim = NULL; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 2c5bc494b247..c904fa74dfbc 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -284,6 +284,9 @@ struct lpfc_idiag { #endif +/* hdwqinfo output buffer size */ +#define LPFC_HDWQINFO_SIZE 8192 + enum { DUMP_FCP, DUMP_NVME, diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index c4acd9ee55b1..ed0318b9003e 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1039,12 +1039,12 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) { struct lpfc_scsi_buf *psb, *psb_next; struct lpfc_nvmet_rcv_ctx *ctxp, *ctxp_next; + struct lpfc_sli4_hdw_queue *qp; LIST_HEAD(aborts); LIST_HEAD(nvme_aborts); LIST_HEAD(nvmet_aborts); - unsigned long iflag = 0; struct lpfc_sglq *sglq_entry = NULL; - int cnt; + int cnt, idx; lpfc_sli_hbqbuf_free_all(phba); @@ -1071,57 +1071,65 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) spin_unlock(&phba->sli4_hba.sgl_list_lock); - /* abts_scsi_buf_list_lock required because worker thread uses this + + /* abts_xxxx_buf_list_lock required because worker thread uses this * list. */ - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { - spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); - list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list, + cnt = 0; + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + + spin_lock(&qp->abts_scsi_buf_list_lock); + list_splice_init(&qp->lpfc_abts_scsi_buf_list, &aborts); - spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); - } - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); - list_splice_init(&phba->sli4_hba.lpfc_abts_nvme_buf_list, - &nvme_aborts); - list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list, - &nvmet_aborts); - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); - } - - spin_unlock_irq(&phba->hbalock); - - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { list_for_each_entry_safe(psb, psb_next, &aborts, list) { psb->pCmd = NULL; psb->status = IOSTAT_SUCCESS; - } - spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); - list_splice(&aborts, &phba->lpfc_common_buf_list_put); - spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); - } - - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - cnt = 0; - list_for_each_entry_safe(psb, psb_next, &nvme_aborts, list) { - psb->pCmd = NULL; - psb->status = IOSTAT_SUCCESS; cnt++; } - spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); - phba->put_common_bufs += cnt; - list_splice(&nvme_aborts, &phba->lpfc_common_buf_list_put); - spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); + spin_lock(&qp->io_buf_list_put_lock); + list_splice_init(&aborts, &qp->lpfc_io_buf_list_put); + qp->put_io_bufs += qp->abts_scsi_io_bufs; + qp->abts_scsi_io_bufs = 0; + spin_unlock(&qp->io_buf_list_put_lock); + spin_unlock(&qp->abts_scsi_buf_list_lock); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + spin_lock(&qp->abts_nvme_buf_list_lock); + list_splice_init(&qp->lpfc_abts_nvme_buf_list, + &nvme_aborts); + list_for_each_entry_safe(psb, psb_next, &nvme_aborts, + list) { + psb->pCmd = NULL; + psb->status = IOSTAT_SUCCESS; + cnt++; + } + spin_lock(&qp->io_buf_list_put_lock); + qp->put_io_bufs += qp->abts_nvme_io_bufs; + qp->abts_nvme_io_bufs = 0; + list_splice_init(&nvme_aborts, + &qp->lpfc_io_buf_list_put); + spin_unlock(&qp->io_buf_list_put_lock); + spin_unlock(&qp->abts_nvme_buf_list_lock); + + } + } + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list, + &nvmet_aborts); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_for_each_entry_safe(ctxp, ctxp_next, &nvmet_aborts, list) { ctxp->flag &= ~(LPFC_NVMET_XBUSY | LPFC_NVMET_ABORT_OP); lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf); } } + spin_unlock_irq(&phba->hbalock); lpfc_sli4_free_sp_events(phba); - return 0; + return cnt; } /** @@ -3126,18 +3134,6 @@ lpfc_online(struct lpfc_hba *phba) "6132 NVME restore reg failed " "on nvmei error x%x\n", error); } - /* Don't post more new bufs if repost already recovered - * the nvme sgls. - */ - if (phba->sli4_hba.common_xri_cnt == 0) { - i = lpfc_new_common_buf(phba, - phba->sli4_hba.common_xri_max); - if (i == 0) { - lpfc_unblock_mgmt_io(phba); - return 1; - } - phba->total_common_bufs += i; - } } else { lpfc_sli_queue_init(phba); if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */ @@ -3372,7 +3368,7 @@ lpfc_scsi_free(struct lpfc_hba *phba) } /** - * lpfc_common_free - Free all the IO buffers and IOCBs from driver lists + * lpfc_io_free - Free all the IO buffers and IOCBs from driver lists * @phba: pointer to lpfc hba data structure. * * This routine is to free all the IO buffers and IOCBs from the driver @@ -3380,36 +3376,44 @@ lpfc_scsi_free(struct lpfc_hba *phba) * the internal resources before the device is removed from the system. **/ static void -lpfc_common_free(struct lpfc_hba *phba) +lpfc_io_free(struct lpfc_hba *phba) { struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + struct lpfc_sli4_hdw_queue *qp; + int idx; spin_lock_irq(&phba->hbalock); - /* Release all the lpfc_nvme_bufs maintained by this host. */ - spin_lock(&phba->common_buf_list_put_lock); - list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &phba->lpfc_common_buf_list_put, list) { - list_del(&lpfc_ncmd->list); - phba->put_common_bufs--; - dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, - lpfc_ncmd->dma_handle); - kfree(lpfc_ncmd); - phba->total_common_bufs--; - } - spin_unlock(&phba->common_buf_list_put_lock); + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + /* Release all the lpfc_nvme_bufs maintained by this host. */ + spin_lock(&qp->io_buf_list_put_lock); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &qp->lpfc_io_buf_list_put, + list) { + list_del(&lpfc_ncmd->list); + qp->put_io_bufs--; + dma_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + qp->total_io_bufs--; + } + spin_unlock(&qp->io_buf_list_put_lock); - spin_lock(&phba->common_buf_list_get_lock); - list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &phba->lpfc_common_buf_list_get, list) { - list_del(&lpfc_ncmd->list); - phba->get_common_bufs--; - dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, - lpfc_ncmd->dma_handle); - kfree(lpfc_ncmd); - phba->total_common_bufs--; + spin_lock(&qp->io_buf_list_get_lock); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &qp->lpfc_io_buf_list_get, + list) { + list_del(&lpfc_ncmd->list); + qp->get_io_bufs--; + dma_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + qp->total_io_bufs--; + } + spin_unlock(&qp->io_buf_list_get_lock); } - spin_unlock(&phba->common_buf_list_get_lock); + spin_unlock_irq(&phba->hbalock); } @@ -3654,8 +3658,101 @@ out_free_mem: return rc; } +int +lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *cbuf) +{ + LIST_HEAD(blist); + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_scsi_buf *iobufp, *prev_iobufp; + int idx, cnt, xri, inserted; + + cnt = 0; + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + spin_lock_irq(&qp->io_buf_list_get_lock); + spin_lock(&qp->io_buf_list_put_lock); + + /* Take everything off the get and put lists */ + list_splice_init(&qp->lpfc_io_buf_list_get, &blist); + list_splice(&qp->lpfc_io_buf_list_put, &blist); + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_get); + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); + cnt += qp->get_io_bufs + qp->put_io_bufs; + qp->get_io_bufs = 0; + qp->put_io_bufs = 0; + qp->total_io_bufs = 0; + spin_unlock(&qp->io_buf_list_put_lock); + spin_unlock_irq(&qp->io_buf_list_get_lock); + } + + /* + * Take IO buffers off blist and put on cbuf sorted by XRI. + * This is because POST_SGL takes a sequential range of XRIs + * to post to the firmware. + */ + for (idx = 0; idx < cnt; idx++) { + list_remove_head(&blist, lpfc_cmd, struct lpfc_scsi_buf, list); + if (!lpfc_cmd) + return cnt; + if (idx == 0) { + list_add_tail(&lpfc_cmd->list, cbuf); + continue; + } + xri = lpfc_cmd->cur_iocbq.sli4_xritag; + inserted = 0; + prev_iobufp = NULL; + list_for_each_entry(iobufp, cbuf, list) { + if (xri < iobufp->cur_iocbq.sli4_xritag) { + if (prev_iobufp) + list_add(&lpfc_cmd->list, + &prev_iobufp->list); + else + list_add(&lpfc_cmd->list, cbuf); + inserted = 1; + break; + } + prev_iobufp = iobufp; + } + if (!inserted) + list_add_tail(&lpfc_cmd->list, cbuf); + } + return cnt; +} + +int +lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf) +{ + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_scsi_buf *lpfc_cmd; + int idx, cnt; + + qp = phba->sli4_hba.hdwq; + cnt = 0; + while (!list_empty(cbuf)) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + list_remove_head(cbuf, lpfc_cmd, + struct lpfc_scsi_buf, list); + if (!lpfc_cmd) + return cnt; + cnt++; + qp = &phba->sli4_hba.hdwq[idx]; + lpfc_cmd->hdwq = idx; + lpfc_cmd->cur_iocbq.wqe_cmpl = NULL; + lpfc_cmd->cur_iocbq.iocb_cmpl = NULL; + spin_lock(&qp->io_buf_list_put_lock); + list_add_tail(&lpfc_cmd->list, + &qp->lpfc_io_buf_list_put); + qp->put_io_bufs++; + qp->total_io_bufs++; + spin_unlock(&qp->io_buf_list_put_lock); + } + } + return cnt; +} + /** - * lpfc_sli4_common_sgl_update - update xri-sgl sizing and mapping + * lpfc_sli4_io_sgl_update - update xri-sgl sizing and mapping * @phba: pointer to lpfc hba data structure. * * This routine first calculates the sizes of the current els and allocated @@ -3667,52 +3764,38 @@ out_free_mem: * 0 - successful (for now, it always returns 0) **/ int -lpfc_sli4_common_sgl_update(struct lpfc_hba *phba) +lpfc_sli4_io_sgl_update(struct lpfc_hba *phba) { struct lpfc_nvme_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL; uint16_t i, lxri, els_xri_cnt; - uint16_t common_xri_cnt, common_xri_max; - LIST_HEAD(common_sgl_list); + uint16_t io_xri_cnt, io_xri_max; + LIST_HEAD(io_sgl_list); int rc, cnt; - phba->total_common_bufs = 0; - phba->get_common_bufs = 0; - phba->put_common_bufs = 0; - /* * update on pci function's allocated nvme xri-sgl list */ /* maximum number of xris available for nvme buffers */ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); - common_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; - phba->sli4_hba.common_xri_max = common_xri_max; + io_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; + phba->sli4_hba.io_xri_max = io_xri_max; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "6074 Current allocated XRI sgl count:%d, " "maximum XRI count:%d\n", - phba->sli4_hba.common_xri_cnt, - phba->sli4_hba.common_xri_max); + phba->sli4_hba.io_xri_cnt, + phba->sli4_hba.io_xri_max); - spin_lock_irq(&phba->common_buf_list_get_lock); - spin_lock(&phba->common_buf_list_put_lock); - list_splice_init(&phba->lpfc_common_buf_list_get, &common_sgl_list); - list_splice(&phba->lpfc_common_buf_list_put, &common_sgl_list); - cnt = phba->get_common_bufs + phba->put_common_bufs; - phba->get_common_bufs = 0; - phba->put_common_bufs = 0; - spin_unlock(&phba->common_buf_list_put_lock); - spin_unlock_irq(&phba->common_buf_list_get_lock); + cnt = lpfc_io_buf_flush(phba, &io_sgl_list); - if (phba->sli4_hba.common_xri_cnt > phba->sli4_hba.common_xri_max) { + if (phba->sli4_hba.io_xri_cnt > phba->sli4_hba.io_xri_max) { /* max nvme xri shrunk below the allocated nvme buffers */ - spin_lock_irq(&phba->common_buf_list_get_lock); - common_xri_cnt = phba->sli4_hba.common_xri_cnt - - phba->sli4_hba.common_xri_max; - spin_unlock_irq(&phba->common_buf_list_get_lock); + io_xri_cnt = phba->sli4_hba.io_xri_cnt - + phba->sli4_hba.io_xri_max; /* release the extra allocated nvme buffers */ - for (i = 0; i < common_xri_cnt; i++) { - list_remove_head(&common_sgl_list, lpfc_ncmd, + for (i = 0; i < io_xri_cnt; i++) { + list_remove_head(&io_sgl_list, lpfc_ncmd, struct lpfc_nvme_buf, list); if (lpfc_ncmd) { dma_pool_free(phba->lpfc_sg_dma_buf_pool, @@ -3721,16 +3804,15 @@ lpfc_sli4_common_sgl_update(struct lpfc_hba *phba) kfree(lpfc_ncmd); } } - spin_lock_irq(&phba->common_buf_list_get_lock); - phba->sli4_hba.common_xri_cnt -= common_xri_cnt; - spin_unlock_irq(&phba->common_buf_list_get_lock); + phba->sli4_hba.io_xri_cnt -= io_xri_cnt; } /* update xris associated to remaining allocated nvme buffers */ lpfc_ncmd = NULL; lpfc_ncmd_next = NULL; + phba->sli4_hba.io_xri_cnt = cnt; list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &common_sgl_list, list) { + &io_sgl_list, list) { lxri = lpfc_sli4_next_xritag(phba); if (lxri == NO_XRI) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -3742,22 +3824,16 @@ lpfc_sli4_common_sgl_update(struct lpfc_hba *phba) lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri; lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; } - spin_lock_irq(&phba->common_buf_list_get_lock); - spin_lock(&phba->common_buf_list_put_lock); - list_splice_init(&common_sgl_list, &phba->lpfc_common_buf_list_get); - phba->get_common_bufs = cnt; - INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); - spin_unlock(&phba->common_buf_list_put_lock); - spin_unlock_irq(&phba->common_buf_list_get_lock); + cnt = lpfc_io_buf_replenish(phba, &io_sgl_list); return 0; out_free_mem: - lpfc_common_free(phba); + lpfc_io_free(phba); return rc; } /** - * lpfc_new_common_buf - IO buffer allocator for HBA with SLI4 IF spec + * lpfc_new_io_buf - IO buffer allocator for HBA with SLI4 IF spec * @vport: The virtual port for which this call being executed. * @num_to_allocate: The requested number of buffers to allocate. * @@ -3767,11 +3843,11 @@ out_free_mem: * them on a list, it post them to the port by using SGL block post. * * Return codes: - * int - number of nvme buffers that were allocated and posted. + * int - number of IO buffers that were allocated and posted. * 0 = failure, less than num_to_alloc is a partial failure. **/ int -lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc) +lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc) { struct lpfc_nvme_buf *lpfc_ncmd; struct lpfc_iocbq *pwqeq; @@ -3791,6 +3867,7 @@ lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc) return 0; } + phba->sli4_hba.io_xri_cnt = 0; for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { lpfc_ncmd = kzalloc(LPFC_COMMON_IO_BUF_SZ, GFP_KERNEL); if (!lpfc_ncmd) @@ -3857,9 +3934,7 @@ lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc) /* add the nvme buffer to a post list */ list_add_tail(&lpfc_ncmd->list, &post_nblist); - spin_lock_irq(&phba->common_buf_list_get_lock); - phba->sli4_hba.common_xri_cnt++; - spin_unlock_irq(&phba->common_buf_list_get_lock); + phba->sli4_hba.io_xri_cnt++; } lpfc_printf_log(phba, KERN_INFO, LOG_NVME, "6114 Allocate %d out of %d requested new NVME " @@ -3867,7 +3942,7 @@ lpfc_new_common_buf(struct lpfc_hba *phba, int num_to_alloc) /* post the list of nvme buffer sgls to port if available */ if (!list_empty(&post_nblist)) - num_posted = lpfc_sli4_post_common_sgl_list( + num_posted = lpfc_sli4_post_io_sgl_list( phba, &post_nblist, bcnt); else num_posted = 0; @@ -5855,14 +5930,6 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) spin_lock_init(&phba->scsi_buf_list_put_lock); INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - /* Initialize the IO buffer list used by driver for SLI4 SCSI/NVME */ - spin_lock_init(&phba->common_buf_list_get_lock); - INIT_LIST_HEAD(&phba->lpfc_common_buf_list_get); - phba->get_common_bufs = 0; - spin_lock_init(&phba->common_buf_list_put_lock); - INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); - phba->put_common_bufs = 0; - /* Initialize the fabric iocb list */ INIT_LIST_HEAD(&phba->fabric_iocb_list); @@ -6229,8 +6296,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { /* Initialize the Abort nvme buffer list used by driver */ - spin_lock_init(&phba->sli4_hba.abts_nvme_buf_list_lock); - INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvme_buf_list); + spin_lock_init(&phba->sli4_hba.abts_nvmet_buf_list_lock); INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list); INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_io_wait_list); } @@ -6903,7 +6969,7 @@ lpfc_init_sgl_list(struct lpfc_hba *phba) phba->sli4_hba.els_xri_cnt = 0; /* nvme xri-buffer book keeping */ - phba->sli4_hba.common_xri_cnt = 0; + phba->sli4_hba.io_xri_cnt = 0; } /** @@ -7117,6 +7183,9 @@ lpfc_hba_alloc(struct pci_dev *pdev) static void lpfc_hba_free(struct lpfc_hba *phba) { + if (phba->sli_rev == LPFC_SLI_REV4) + kfree(phba->sli4_hba.hdwq); + /* Release the driver assigned board number */ idr_remove(&lpfc_hba_index, phba->brd_no); @@ -8330,6 +8399,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) return 1; } qdesc->qe_valid = 1; + qdesc->hdwq = wqidx; phba->sli4_hba.hdwq[wqidx].nvme_cq = qdesc; qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, @@ -8340,6 +8410,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) wqidx); return 1; } + qdesc->hdwq = wqidx; phba->sli4_hba.hdwq[wqidx].nvme_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; @@ -8368,6 +8439,7 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) return 1; } qdesc->qe_valid = 1; + qdesc->hdwq = wqidx; phba->sli4_hba.hdwq[wqidx].fcp_cq = qdesc; /* Create Fast Path FCP WQs */ @@ -8389,6 +8461,7 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) wqidx); return 1; } + qdesc->hdwq = wqidx; phba->sli4_hba.hdwq[wqidx].fcp_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; @@ -8413,6 +8486,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) { struct lpfc_queue *qdesc; int idx; + struct lpfc_sli4_hdw_queue *qp; /* * Create HBA Record arrays. @@ -8429,14 +8503,33 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) phba->sli4_hba.cq_esize = LPFC_CQE_SIZE; phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT; - phba->sli4_hba.hdwq = kcalloc(phba->cfg_hdw_queue, - sizeof(struct lpfc_sli4_hdw_queue), - GFP_KERNEL); if (!phba->sli4_hba.hdwq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6427 Failed allocate memory for " - "fast-path Hardware Queue array\n"); - goto out_error; + phba->sli4_hba.hdwq = kcalloc( + phba->cfg_hdw_queue, sizeof(struct lpfc_sli4_hdw_queue), + GFP_KERNEL); + if (!phba->sli4_hba.hdwq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6427 Failed allocate memory for " + "fast-path Hardware Queue array\n"); + goto out_error; + } + /* Prepare hardware queues to take IO buffers */ + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + spin_lock_init(&qp->io_buf_list_get_lock); + spin_lock_init(&qp->io_buf_list_put_lock); + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_get); + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); + qp->get_io_bufs = 0; + qp->put_io_bufs = 0; + qp->total_io_bufs = 0; + spin_lock_init(&qp->abts_scsi_buf_list_lock); + INIT_LIST_HEAD(&qp->lpfc_abts_scsi_buf_list); + qp->abts_scsi_io_bufs = 0; + spin_lock_init(&qp->abts_nvme_buf_list_lock); + INIT_LIST_HEAD(&qp->lpfc_abts_nvme_buf_list); + qp->abts_nvme_io_bufs = 0; + } } if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { @@ -8488,6 +8581,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } qdesc->qe_valid = 1; + qdesc->hdwq = idx; phba->sli4_hba.hdwq[idx].hba_eq = qdesc; } @@ -8518,6 +8612,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } qdesc->qe_valid = 1; + qdesc->hdwq = idx; phba->sli4_hba.nvmet_cqset[idx] = qdesc; } } @@ -8650,6 +8745,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) "receive HRQ\n"); goto out_error; } + qdesc->hdwq = idx; phba->sli4_hba.nvmet_mrq_hdr[idx] = qdesc; /* Only needed for header of RQ pair */ @@ -8676,6 +8772,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) "receive DRQ\n"); goto out_error; } + qdesc->hdwq = idx; phba->sli4_hba.nvmet_mrq_data[idx] = qdesc; } } @@ -8727,7 +8824,6 @@ lpfc_sli4_release_hdwq(struct lpfc_sli4_hdw_queue *hdwq, int max) hdwq[idx].fcp_wq = NULL; hdwq[idx].nvme_wq = NULL; } - kfree(hdwq); } /** @@ -8749,7 +8845,6 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) if (phba->sli4_hba.hdwq) lpfc_sli4_release_hdwq(phba->sli4_hba.hdwq, phba->cfg_hdw_queue); - phba->sli4_hba.hdwq = NULL; if (phba->nvmet_support) { lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_cqset, @@ -10400,8 +10495,10 @@ lpfc_unset_hba(struct lpfc_hba *phba) static void lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) { + struct lpfc_sli4_hdw_queue *qp; + int idx, ccnt, fcnt; int wait_time = 0; - int common_xri_cmpl = 1; + int io_xri_cmpl = 1; int nvmet_xri_cmpl = 1; int fcp_xri_cmpl = 1; int els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list); @@ -10416,17 +10513,32 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) lpfc_nvme_wait_for_io_drain(phba); - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) - fcp_xri_cmpl = - list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list); + ccnt = 0; + fcnt = 0; + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + fcp_xri_cmpl = list_empty( + &qp->lpfc_abts_scsi_buf_list); + if (!fcp_xri_cmpl) /* if list is NOT empty */ + fcnt++; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + io_xri_cmpl = list_empty( + &qp->lpfc_abts_nvme_buf_list); + if (!io_xri_cmpl) /* if list is NOT empty */ + ccnt++; + } + } + if (ccnt) + io_xri_cmpl = 0; + if (fcnt) + fcp_xri_cmpl = 0; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - common_xri_cmpl = - list_empty(&phba->sli4_hba.lpfc_abts_nvme_buf_list); nvmet_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list); } - while (!fcp_xri_cmpl || !els_xri_cmpl || !common_xri_cmpl || + while (!fcp_xri_cmpl || !els_xri_cmpl || !io_xri_cmpl || !nvmet_xri_cmpl) { if (wait_time > LPFC_XRI_EXCH_BUSY_WAIT_TMO) { if (!nvmet_xri_cmpl) @@ -10434,7 +10546,7 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) "6424 NVMET XRI exchange busy " "wait time: %d seconds.\n", wait_time/1000); - if (!common_xri_cmpl) + if (!io_xri_cmpl) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "6100 NVME XRI exchange busy " "wait time: %d seconds.\n", @@ -10455,17 +10567,31 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) msleep(LPFC_XRI_EXCH_BUSY_WAIT_T1); wait_time += LPFC_XRI_EXCH_BUSY_WAIT_T1; } + + ccnt = 0; + fcnt = 0; + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + fcp_xri_cmpl = list_empty( + &qp->lpfc_abts_scsi_buf_list); + if (!fcp_xri_cmpl) /* if list is NOT empty */ + fcnt++; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + io_xri_cmpl = list_empty( + &qp->lpfc_abts_nvme_buf_list); + if (!io_xri_cmpl) /* if list is NOT empty */ + ccnt++; + } + } + if (ccnt) + io_xri_cmpl = 0; + if (fcnt) + fcp_xri_cmpl = 0; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - common_xri_cmpl = list_empty( - &phba->sli4_hba.lpfc_abts_nvme_buf_list); nvmet_xri_cmpl = list_empty( &phba->sli4_hba.lpfc_abts_nvmet_ctx_list); } - - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) - fcp_xri_cmpl = list_empty( - &phba->sli4_hba.lpfc_abts_scsi_buf_list); - els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list); @@ -11739,14 +11865,13 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Don't post more new bufs if repost already recovered * the nvme sgls. */ - if (phba->sli4_hba.common_xri_cnt == 0) { - len = lpfc_new_common_buf( - phba, phba->sli4_hba.common_xri_max); + if (phba->sli4_hba.io_xri_cnt == 0) { + len = lpfc_new_io_buf( + phba, phba->sli4_hba.io_xri_max); if (len == 0) { error = -ENOMEM; goto out_free_sysfs_attr; } - phba->total_common_bufs += len; } } @@ -11836,7 +11961,6 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) * the HBA FCoE function. */ lpfc_debugfs_terminate(vport); - lpfc_sli4_hba_unset(phba); lpfc_stop_hba_timers(phba); spin_lock_irq(&phba->port_list_lock); @@ -11846,8 +11970,9 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) /* Perform scsi free before driver resource_unset since scsi * buffers are released to their corresponding pools here. */ - lpfc_common_free(phba); + lpfc_io_free(phba); lpfc_free_iocb_list(phba); + lpfc_sli4_hba_unset(phba); lpfc_unset_driver_resource_phase2(phba); lpfc_sli4_driver_resource_unset(phba); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index c0f59fbaa173..0ecc73a6634f 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -58,7 +58,7 @@ static struct lpfc_nvme_buf * lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, - int expedite); + int idx, int expedite); static void lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_nvme_buf *); @@ -1545,7 +1545,8 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, } } - lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, expedite); + lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, + lpfc_queue_info->index, expedite); if (lpfc_ncmd == NULL) { atomic_inc(&lport->xmt_fcp_noxri); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, @@ -1913,24 +1914,26 @@ static struct nvme_fc_port_template lpfc_nvme_template = { }; static inline struct lpfc_nvme_buf * -lpfc_nvme_buf(struct lpfc_hba *phba) +lpfc_nvme_buf(struct lpfc_hba *phba, int idx) { + struct lpfc_sli4_hdw_queue *qp; struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + qp = &phba->sli4_hba.hdwq[idx]; list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &phba->lpfc_common_buf_list_get, list) { + &qp->lpfc_io_buf_list_get, list) { list_del_init(&lpfc_ncmd->list); - phba->get_common_bufs--; + qp->get_io_bufs--; return lpfc_ncmd; } return NULL; } /** - * lpfc_get_nvme_buf - Get a nvme buffer from lpfc_common_buf_list of the HBA + * lpfc_get_nvme_buf - Get a nvme buffer from io_buf_list of the HBA * @phba: The HBA for which this call is being executed. * - * This routine removes a nvme buffer from head of @phba lpfc_common_buf_list + * This routine removes a nvme buffer from head of @hdwq io_buf_list * and returns to caller. * * Return codes: @@ -1939,30 +1942,32 @@ lpfc_nvme_buf(struct lpfc_hba *phba) **/ static struct lpfc_nvme_buf * lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, - int expedite) + int idx, int expedite) { struct lpfc_nvme_buf *lpfc_ncmd = NULL; + struct lpfc_sli4_hdw_queue *qp; struct sli4_sge *sgl; struct lpfc_iocbq *pwqeq; union lpfc_wqe128 *wqe; unsigned long iflag = 0; - spin_lock_irqsave(&phba->common_buf_list_get_lock, iflag); - if (phba->get_common_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) - lpfc_ncmd = lpfc_nvme_buf(phba); + qp = &phba->sli4_hba.hdwq[idx]; + spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); + if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) + lpfc_ncmd = lpfc_nvme_buf(phba, idx); if (!lpfc_ncmd) { - spin_lock(&phba->common_buf_list_put_lock); - list_splice(&phba->lpfc_common_buf_list_put, - &phba->lpfc_common_buf_list_get); - phba->get_common_bufs += phba->put_common_bufs; - INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); - phba->put_common_bufs = 0; - spin_unlock(&phba->common_buf_list_put_lock); - if (phba->get_common_bufs > LPFC_NVME_EXPEDITE_XRICNT || + spin_lock(&qp->io_buf_list_put_lock); + list_splice(&qp->lpfc_io_buf_list_put, + &qp->lpfc_io_buf_list_get); + qp->get_io_bufs += qp->put_io_bufs; + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); + qp->put_io_bufs = 0; + spin_unlock(&qp->io_buf_list_put_lock); + if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) - lpfc_ncmd = lpfc_nvme_buf(phba); + lpfc_ncmd = lpfc_nvme_buf(phba, idx); } - spin_unlock_irqrestore(&phba->common_buf_list_get_lock, iflag); + spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag); if (lpfc_ncmd) { pwqeq = &(lpfc_ncmd->cur_iocbq); @@ -1975,6 +1980,7 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl; lpfc_ncmd->start_time = jiffies; lpfc_ncmd->flags = 0; + lpfc_ncmd->hdwq = idx; /* Rsp SGE will be filled in when we rcv an IO * from the NVME Layer to be sent. @@ -1993,7 +1999,10 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, atomic_inc(&ndlp->cmd_pending); lpfc_ncmd->flags |= LPFC_BUMP_QDEPTH; } - } + + } else + qp->empty_io_bufs++; + return lpfc_ncmd; } @@ -2003,13 +2012,14 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * @lpfc_ncmd: The nvme buffer which is being released. * * This routine releases @lpfc_ncmd nvme buffer by adding it to tail of @phba - * lpfc_common_buf_list list. For SLI4 XRI's are tied to the nvme buffer + * lpfc_io_buf_list list. For SLI4 XRI's are tied to the nvme buffer * and cannot be reused for at least RA_TOV amount of time if it was * aborted. **/ static void lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) { + struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; if ((lpfc_ncmd->flags & LPFC_BUMP_QDEPTH) && lpfc_ncmd->ndlp) @@ -2018,6 +2028,7 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) lpfc_ncmd->ndlp = NULL; lpfc_ncmd->flags &= ~LPFC_BUMP_QDEPTH; + qp = &phba->sli4_hba.hdwq[lpfc_ncmd->hdwq]; if (lpfc_ncmd->flags & LPFC_SBUF_XBUSY) { lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, "6310 XB release deferred for " @@ -2025,21 +2036,21 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) lpfc_ncmd->cur_iocbq.sli4_xritag, lpfc_ncmd->cur_iocbq.iotag); - spin_lock_irqsave(&phba->sli4_hba.abts_nvme_buf_list_lock, - iflag); + spin_lock_irqsave(&qp->abts_nvme_buf_list_lock, iflag); list_add_tail(&lpfc_ncmd->list, - &phba->sli4_hba.lpfc_abts_nvme_buf_list); - spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock, - iflag); + &qp->lpfc_abts_nvme_buf_list); + qp->abts_nvme_io_bufs++; + spin_unlock_irqrestore(&qp->abts_nvme_buf_list_lock, iflag); } else { /* MUST zero fields if buffer is reused by another protocol */ lpfc_ncmd->nvmeCmd = NULL; lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL; - spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); + + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); list_add_tail(&lpfc_ncmd->list, - &phba->lpfc_common_buf_list_put); - phba->put_common_bufs++; - spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); + &qp->lpfc_io_buf_list_put); + qp->put_io_bufs++; + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); } } @@ -2517,27 +2528,28 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) **/ void lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba, - struct sli4_wcqe_xri_aborted *axri) + struct sli4_wcqe_xri_aborted *axri, int idx) { uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri); struct lpfc_nvme_buf *lpfc_ncmd, *next_lpfc_ncmd; struct nvmefc_fcp_req *nvme_cmd = NULL; struct lpfc_nodelist *ndlp; + struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return; + qp = &phba->sli4_hba.hdwq[idx]; spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_lock(&qp->abts_nvme_buf_list_lock); list_for_each_entry_safe(lpfc_ncmd, next_lpfc_ncmd, - &phba->sli4_hba.lpfc_abts_nvme_buf_list, - list) { + &qp->lpfc_abts_nvme_buf_list, list) { if (lpfc_ncmd->cur_iocbq.sli4_xritag == xri) { list_del_init(&lpfc_ncmd->list); + qp->abts_nvme_io_bufs--; lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; lpfc_ncmd->status = IOSTAT_SUCCESS; - spin_unlock( - &phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&qp->abts_nvme_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); ndlp = lpfc_ncmd->ndlp; @@ -2563,7 +2575,7 @@ lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba, return; } } - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&qp->abts_nvme_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index e3a554417e98..4a020b9c8fbf 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -84,6 +84,8 @@ struct lpfc_nvme_buf { dma_addr_t dma_phys_sgl; struct sli4_sge *dma_sgl; struct lpfc_iocbq cur_iocbq; + uint16_t hdwq; + uint16_t cpu; /* NVME specific fields */ struct nvmefc_fcp_req *nvmeCmd; @@ -95,7 +97,6 @@ struct lpfc_nvme_buf { #define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ #define LPFC_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ - uint16_t cpu; uint16_t status; /* From IOCB Word 7- ulpStatus */ uint32_t result; /* From IOCB Word 4. */ diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index c64a8234d5bd..f2a30ee9702b 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -226,15 +226,15 @@ lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp) "6313 NVMET Defer ctx release xri x%x flg x%x\n", ctxp->oxid, ctxp->flag); - spin_lock_irqsave(&phba->sli4_hba.abts_nvme_buf_list_lock, iflag); + spin_lock_irqsave(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag); if (ctxp->flag & LPFC_NVMET_CTX_RLS) { - spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock, + spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag); return; } ctxp->flag |= LPFC_NVMET_CTX_RLS; list_add_tail(&ctxp->list, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list); - spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock, iflag); + spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag); } /** @@ -1162,9 +1162,9 @@ __lpfc_nvmet_clean_io_for_cpu(struct lpfc_hba *phba, spin_lock_irqsave(&infop->nvmet_ctx_list_lock, flags); list_for_each_entry_safe(ctx_buf, next_ctx_buf, &infop->nvmet_ctx_list, list) { - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_del_init(&ctx_buf->list); - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); __lpfc_clear_active_sglq(phba, ctx_buf->sglq->sli4_lxritag); ctx_buf->sglq->state = SGL_FREED; @@ -1502,7 +1502,7 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, } spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_for_each_entry_safe(ctxp, next_ctxp, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list, list) { @@ -1518,7 +1518,7 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, released = true; } ctxp->flag &= ~LPFC_NVMET_XBUSY; - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); rrq_empty = list_empty(&phba->active_rrq_list); spin_unlock_irqrestore(&phba->hbalock, iflag); @@ -1542,7 +1542,7 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, lpfc_worker_wake_up(phba); return; } - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); } @@ -1561,14 +1561,14 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport, xri = be16_to_cpu(fc_hdr->fh_ox_id); spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_for_each_entry_safe(ctxp, next_ctxp, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list, list) { if (ctxp->ctxbuf->sglq->sli4_xritag != xri) continue; - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); spin_lock_irqsave(&ctxp->ctxlock, iflag); @@ -1589,7 +1589,7 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport, lpfc_sli4_seq_abort_rsp(vport, fc_hdr, 1); return 0; } - spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); lpfc_nvmeio_data(phba, "NVMET ABTS RCV: xri x%x CPU %02x rjt %d\n", diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 63841f49affb..261d0ebf0093 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -525,19 +525,26 @@ lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; struct lpfc_scsi_buf *psb, *next_psb; + struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; + int idx; if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) return; + spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); - list_for_each_entry_safe(psb, next_psb, - &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) { - if (psb->rdata && psb->rdata->pnode - && psb->rdata->pnode->vport == vport) - psb->rdata = NULL; + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + qp = &phba->sli4_hba.hdwq[idx]; + + spin_lock(&qp->abts_scsi_buf_list_lock); + list_for_each_entry_safe(psb, next_psb, + &qp->lpfc_abts_scsi_buf_list, list) { + if (psb->rdata && psb->rdata->pnode && + psb->rdata->pnode->vport == vport) + psb->rdata = NULL; + } + spin_unlock(&qp->abts_scsi_buf_list_lock); } - spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); } @@ -551,11 +558,12 @@ lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) **/ void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, - struct sli4_wcqe_xri_aborted *axri) + struct sli4_wcqe_xri_aborted *axri, int idx) { uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri); uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri); struct lpfc_scsi_buf *psb, *next_psb; + struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; struct lpfc_iocbq *iocbq; int i; @@ -565,16 +573,19 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) return; + + qp = &phba->sli4_hba.hdwq[idx]; spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); + spin_lock(&qp->abts_scsi_buf_list_lock); list_for_each_entry_safe(psb, next_psb, - &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) { + &qp->lpfc_abts_scsi_buf_list, list) { if (psb->cur_iocbq.sli4_xritag == xri) { list_del(&psb->list); + qp->abts_scsi_io_bufs--; psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; spin_unlock( - &phba->sli4_hba.abts_scsi_buf_list_lock); + &qp->abts_scsi_buf_list_lock); if (psb->rdata && psb->rdata->pnode) ndlp = psb->rdata->pnode; else @@ -593,7 +604,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, return; } } - spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); + spin_unlock(&qp->abts_scsi_buf_list_lock); for (i = 1; i <= phba->sli.last_iotag; i++) { iocbq = phba->sli.iocbq_lookup[i]; @@ -652,10 +663,10 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) return lpfc_cmd; } /** - * lpfc_get_scsi_buf_s4 - Get a scsi buffer from lpfc_common_buf_list of the HBA + * lpfc_get_scsi_buf_s4 - Get a scsi buffer from io_buf_list of the HBA * @phba: The HBA for which this call is being executed. * - * This routine removes a scsi buffer from head of @phba lpfc_common_buf_list + * This routine removes a scsi buffer from head of @hdwq io_buf_list * and returns to caller. * * Return codes: @@ -666,48 +677,58 @@ static struct lpfc_scsi_buf* lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { struct lpfc_scsi_buf *lpfc_cmd, *lpfc_cmd_next; + struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; struct sli4_sge *sgl; IOCB_t *iocb; dma_addr_t pdma_phys_fcp_rsp; dma_addr_t pdma_phys_fcp_cmd; - uint32_t sgl_size; + uint32_t sgl_size, cpu, idx; int found = 0; - spin_lock_irqsave(&phba->common_buf_list_get_lock, iflag); + cpu = smp_processor_id(); + if (cpu < phba->cfg_hdw_queue) + idx = cpu; + else + idx = cpu % phba->cfg_hdw_queue; + + qp = &phba->sli4_hba.hdwq[idx]; + spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, - &phba->lpfc_common_buf_list_get, list) { + &qp->lpfc_io_buf_list_get, list) { if (lpfc_test_rrq_active(phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; list_del_init(&lpfc_cmd->list); - phba->get_common_bufs--; + qp->get_io_bufs--; found = 1; break; } if (!found) { - spin_lock(&phba->common_buf_list_put_lock); - list_splice(&phba->lpfc_common_buf_list_put, - &phba->lpfc_common_buf_list_get); - phba->get_common_bufs += phba->put_common_bufs; - INIT_LIST_HEAD(&phba->lpfc_common_buf_list_put); - phba->put_common_bufs = 0; - spin_unlock(&phba->common_buf_list_put_lock); + spin_lock(&qp->io_buf_list_put_lock); + list_splice(&qp->lpfc_io_buf_list_put, + &qp->lpfc_io_buf_list_get); + qp->get_io_bufs += qp->put_io_bufs; + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); + qp->put_io_bufs = 0; + spin_unlock(&qp->io_buf_list_put_lock); list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, - &phba->lpfc_common_buf_list_get, + &qp->lpfc_io_buf_list_get, list) { if (lpfc_test_rrq_active( phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; list_del_init(&lpfc_cmd->list); - phba->get_common_bufs--; + qp->get_io_bufs--; found = 1; break; } } - spin_unlock_irqrestore(&phba->common_buf_list_get_lock, iflag); - if (!found) + spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag); + if (!found) { + qp->empty_io_bufs++; return NULL; + } sgl_size = phba->cfg_sg_dma_buf_size - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp)); @@ -723,10 +744,11 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) lpfc_cmd->flags = 0; lpfc_cmd->start_time = jiffies; lpfc_cmd->waitq = NULL; - lpfc_cmd->cpu = smp_processor_id(); + lpfc_cmd->cpu = cpu; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS lpfc_cmd->prot_data_type = 0; #endif + lpfc_cmd->hdwq = idx; lpfc_cmd->fcp_cmnd = (lpfc_cmd->data + sgl_size); lpfc_cmd->fcp_rsp = (struct fcp_rsp *)((uint8_t *)lpfc_cmd->fcp_cmnd + @@ -825,35 +847,36 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) * @phba: The Hba for which this call is being executed. * @psb: The scsi buffer which is being released. * - * This routine releases @psb scsi buffer by adding it to tail of @phba - * lpfc_common_buf_list list. For SLI4 XRI's are tied to the scsi buffer + * This routine releases @psb scsi buffer by adding it to tail of @hdwq + * io_buf_list list. For SLI4 XRI's are tied to the scsi buffer * and cannot be reused for at least RA_TOV amount of time if it was * aborted. **/ static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) { + struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; psb->seg_cnt = 0; psb->prot_seg_cnt = 0; + qp = &phba->sli4_hba.hdwq[psb->hdwq]; if (psb->exch_busy) { - spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock, - iflag); + spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag); psb->pCmd = NULL; - list_add_tail(&psb->list, - &phba->sli4_hba.lpfc_abts_scsi_buf_list); - spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock, - iflag); + list_add_tail(&psb->list, &qp->lpfc_abts_scsi_buf_list); + qp->abts_scsi_io_bufs++; + spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag); } else { /* MUST zero fields if buffer is reused by another protocol */ psb->pCmd = NULL; psb->cur_iocbq.iocb_cmpl = NULL; - spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); - list_add_tail(&psb->list, &phba->lpfc_common_buf_list_put); - phba->put_common_bufs++; - spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); + + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + list_add_tail(&psb->list, &qp->lpfc_io_buf_list_put); + qp->put_io_bufs++; + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); } } diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index d784de3da0c0..ebb5d5860cdb 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -138,6 +138,8 @@ struct lpfc_scsi_buf { dma_addr_t dma_phys_sgl; struct ulp_bde64 *dma_sgl; struct lpfc_iocbq cur_iocbq; + uint16_t hdwq; + uint16_t cpu; /* SCSI specific fields */ struct scsi_cmnd *pCmd; @@ -150,7 +152,6 @@ struct lpfc_scsi_buf { #define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ #define LPFC_SBUF_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ - uint16_t cpu; uint16_t status; /* From IOCB Word 7- ulpStatus */ uint32_t result; /* From IOCB Word 4. */ diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d41bfa4569a4..ab1b9d9123b6 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -6023,7 +6023,7 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) list_add_tail(&rsrc_blks->list, ext_blk_list); rsrc_start = rsrc_id; if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) { - phba->sli4_hba.common_xri_start = rsrc_start + + phba->sli4_hba.io_xri_start = rsrc_start + lpfc_sli4_get_iocb_cnt(phba); } @@ -7051,37 +7051,30 @@ lpfc_sli4_repost_sgl_list(struct lpfc_hba *phba, } /** - * lpfc_sli4_repost_common_sgl_list - Repost all the allocated nvme buffer sgls + * lpfc_sli4_repost_io_sgl_list - Repost all the allocated nvme buffer sgls * @phba: pointer to lpfc hba data structure. * * This routine walks the list of nvme buffers that have been allocated and * repost them to the port by using SGL block post. This is needed after a * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list - * to the lpfc_common_buf_list. If the repost fails, reject all nvme buffers. + * to the lpfc_io_buf_list. If the repost fails, reject all nvme buffers. * * Returns: 0 = success, non-zero failure. **/ int -lpfc_sli4_repost_common_sgl_list(struct lpfc_hba *phba) +lpfc_sli4_repost_io_sgl_list(struct lpfc_hba *phba) { LIST_HEAD(post_nblist); int num_posted, rc = 0; /* get all NVME buffers need to repost to a local list */ - spin_lock_irq(&phba->common_buf_list_get_lock); - spin_lock(&phba->common_buf_list_put_lock); - list_splice_init(&phba->lpfc_common_buf_list_get, &post_nblist); - list_splice(&phba->lpfc_common_buf_list_put, &post_nblist); - phba->get_common_bufs = 0; - phba->put_common_bufs = 0; - spin_unlock(&phba->common_buf_list_put_lock); - spin_unlock_irq(&phba->common_buf_list_get_lock); + lpfc_io_buf_flush(phba, &post_nblist); /* post the list of nvme buffer sgls to port if available */ if (!list_empty(&post_nblist)) { - num_posted = lpfc_sli4_post_common_sgl_list( - phba, &post_nblist, phba->sli4_hba.common_xri_cnt); + num_posted = lpfc_sli4_post_io_sgl_list( + phba, &post_nblist, phba->sli4_hba.io_xri_cnt); /* failed to post any nvme buffer, return error */ if (num_posted == 0) rc = -EIO; @@ -7551,7 +7544,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) cnt += phba->sli4_hba.nvmet_xri_cnt; } else { /* update host common xri-sgl sizes and mappings */ - rc = lpfc_sli4_common_sgl_update(phba); + rc = lpfc_sli4_io_sgl_update(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "6082 Failed to update nvme-sgl size " @@ -7560,7 +7553,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) } /* register the allocated common sgl pool to the port */ - rc = lpfc_sli4_repost_common_sgl_list(phba); + rc = lpfc_sli4_repost_io_sgl_list(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "6116 Error %d during nvme sgl post " @@ -8562,7 +8555,6 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq); if (rc) goto exit; - /* * Initialize the bootstrap memory region to avoid stale data areas * in the mailbox post. Then copy the caller's mailbox contents to @@ -10002,6 +9994,8 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) struct lpfc_sli_ring * lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) { + struct lpfc_scsi_buf *lpfc_cmd; + if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { if (unlikely(!phba->sli4_hba.hdwq)) return NULL; @@ -10010,11 +10004,8 @@ lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) * be setup based on what work queue we used. */ if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - piocb->hba_wqidx = - lpfc_sli4_scmd_to_wqidx_distr( - phba, piocb->context1); - piocb->hba_wqidx = piocb->hba_wqidx % - phba->cfg_hdw_queue; + lpfc_cmd = (struct lpfc_scsi_buf *)piocb->context1; + piocb->hba_wqidx = lpfc_cmd->hdwq; } return phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq->pring; } else { @@ -12924,7 +12915,8 @@ void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba) cq_event, struct lpfc_cq_event, list); spin_unlock_irq(&phba->hbalock); /* Notify aborted XRI for FCP work queue */ - lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri); + lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri, + cq_event->hdwq); /* Free the event processed back to the free pool */ lpfc_sli4_cq_event_release(phba, cq_event); } @@ -13426,17 +13418,8 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba, switch (cq->subtype) { case LPFC_FCP: - cq_event = lpfc_cq_event_setup( - phba, wcqe, sizeof(struct sli4_wcqe_xri_aborted)); - if (!cq_event) - return false; - spin_lock_irqsave(&phba->hbalock, iflags); - list_add_tail(&cq_event->list, - &phba->sli4_hba.sp_fcp_xri_aborted_work_queue); - /* Set the fcp xri abort event flag */ - phba->hba_flag |= FCP_XRI_ABORT_EVENT; - spin_unlock_irqrestore(&phba->hbalock, iflags); - workposted = true; + lpfc_sli4_fcp_xri_aborted(phba, wcqe, cq->hdwq); + workposted = false; break; case LPFC_NVME_LS: /* NVME LS uses ELS resources */ case LPFC_ELS: @@ -13444,6 +13427,7 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba, phba, wcqe, sizeof(struct sli4_wcqe_xri_aborted)); if (!cq_event) return false; + cq_event->hdwq = cq->hdwq; spin_lock_irqsave(&phba->hbalock, iflags); list_add_tail(&cq_event->list, &phba->sli4_hba.sp_els_xri_aborted_work_queue); @@ -13457,7 +13441,7 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba, if (phba->nvmet_support) lpfc_sli4_nvmet_xri_aborted(phba, wcqe); else - lpfc_sli4_nvme_xri_aborted(phba, wcqe); + lpfc_sli4_nvme_xri_aborted(phba, wcqe, cq->hdwq); workposted = false; break; @@ -14073,7 +14057,8 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); /* First check for NVME/SCSI completion */ - if (cqid == phba->sli4_hba.hdwq[qidx].nvme_cq_map) { + if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && + (cqid == phba->sli4_hba.hdwq[qidx].nvme_cq_map)) { /* Process NVME / NVMET command completion */ cq = phba->sli4_hba.hdwq[qidx].nvme_cq; goto process_cq; @@ -16656,7 +16641,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba, } /** - * lpfc_sli4_post_common_sgl_block - post a block of nvme sgl list to firmware + * lpfc_sli4_post_io_sgl_block - post a block of nvme sgl list to firmware * @phba: pointer to lpfc hba data structure. * @nblist: pointer to nvme buffer list. * @count: number of scsi buffers on the list. @@ -16667,9 +16652,8 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba, * **/ static int -lpfc_sli4_post_common_sgl_block(struct lpfc_hba *phba, - struct list_head *nblist, - int count) +lpfc_sli4_post_io_sgl_block(struct lpfc_hba *phba, struct list_head *nblist, + int count) { struct lpfc_nvme_buf *lpfc_ncmd; struct lpfc_mbx_post_uembed_sgl_page1 *sgl; @@ -16770,7 +16754,7 @@ lpfc_sli4_post_common_sgl_block(struct lpfc_hba *phba, } /** - * lpfc_sli4_post_common_sgl_list - Post blocks of nvme buffer sgls from a list + * lpfc_sli4_post_io_sgl_list - Post blocks of nvme buffer sgls from a list * @phba: pointer to lpfc hba data structure. * @post_nblist: pointer to the nvme buffer list. * @@ -16784,8 +16768,8 @@ lpfc_sli4_post_common_sgl_block(struct lpfc_hba *phba, * Returns: 0 = failure, non-zero number of successfully posted buffers. **/ int -lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, - struct list_head *post_nblist, int sb_count) +lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba, + struct list_head *post_nblist, int sb_count) { struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; int status, sgl_size; @@ -16793,7 +16777,6 @@ lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, dma_addr_t pdma_phys_sgl1; int last_xritag = NO_XRI; int cur_xritag; - unsigned long iflag; LIST_HEAD(prep_nblist); LIST_HEAD(blck_nblist); LIST_HEAD(nvme_nblist); @@ -16864,8 +16847,8 @@ lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, continue; /* post block of NVME buffer list sgls */ - status = lpfc_sli4_post_common_sgl_block(phba, &blck_nblist, - post_cnt); + status = lpfc_sli4_post_io_sgl_block(phba, &blck_nblist, + post_cnt); /* don't reset xirtag due to hole in xri block */ if (block_cnt == 0) @@ -16891,17 +16874,8 @@ lpfc_sli4_post_common_sgl_list(struct lpfc_hba *phba, } } /* Push NVME buffers with sgl posted to the available list */ - while (!list_empty(&nvme_nblist)) { - list_remove_head(&nvme_nblist, lpfc_ncmd, - struct lpfc_nvme_buf, list); - lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL; - lpfc_ncmd->cur_iocbq.iocb_cmpl = NULL; - spin_lock_irqsave(&phba->common_buf_list_put_lock, iflag); - list_add_tail(&lpfc_ncmd->list, - &phba->lpfc_common_buf_list_put); - phba->put_common_bufs++; - spin_unlock_irqrestore(&phba->common_buf_list_put_lock, iflag); - } + lpfc_io_buf_replenish(phba, &nvme_nblist); + return num_posted; } diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 7abb395bb64a..3922e762ed31 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -33,6 +33,7 @@ typedef enum _lpfc_ctx_cmd { struct lpfc_cq_event { struct list_head list; + uint16_t hdwq; union { struct lpfc_mcqe mcqe_cmpl; struct lpfc_acqe_link acqe_link; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 6e1e958df10a..4862249732dd 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -214,6 +214,7 @@ struct lpfc_queue { struct work_struct spwork; uint64_t isr_timestamp; + uint16_t hdwq; uint8_t qe_valid; struct lpfc_queue *assoc_qp; union sli4_qe qe[1]; /* array to index entries (must be last) */ @@ -538,6 +539,22 @@ struct lpfc_sli4_hdw_queue { struct lpfc_queue *nvme_wq; /* Fast-path NVME work queue */ uint16_t fcp_cq_map; uint16_t nvme_cq_map; + + /* Keep track of IO buffers for this hardware queue */ + spinlock_t io_buf_list_get_lock; /* Common buf alloc list lock */ + struct list_head lpfc_io_buf_list_get; + spinlock_t io_buf_list_put_lock; /* Common buf free list lock */ + struct list_head lpfc_io_buf_list_put; + spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ + struct list_head lpfc_abts_scsi_buf_list; + spinlock_t abts_nvme_buf_list_lock; /* list of aborted NVME IOs */ + struct list_head lpfc_abts_nvme_buf_list; + uint32_t total_io_bufs; + uint32_t get_io_bufs; + uint32_t put_io_bufs; + uint32_t empty_io_bufs; + uint32_t abts_scsi_io_bufs; + uint32_t abts_nvme_io_bufs; }; struct lpfc_sli4_hba { @@ -662,19 +679,20 @@ struct lpfc_sli4_hba { uint16_t rpi_hdrs_in_use; /* must post rpi hdrs if set. */ uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */ uint16_t next_rpi; - uint16_t common_xri_max; - uint16_t common_xri_cnt; - uint16_t common_xri_start; + uint16_t io_xri_max; + uint16_t io_xri_cnt; + uint16_t io_xri_start; uint16_t els_xri_cnt; uint16_t nvmet_xri_cnt; uint16_t nvmet_io_wait_cnt; uint16_t nvmet_io_wait_total; struct list_head lpfc_els_sgl_list; struct list_head lpfc_abts_els_sgl_list; - struct list_head lpfc_nvmet_sgl_list; - struct list_head lpfc_abts_nvmet_ctx_list; + spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ struct list_head lpfc_abts_scsi_buf_list; - struct list_head lpfc_abts_nvme_buf_list; + struct list_head lpfc_nvmet_sgl_list; + spinlock_t abts_nvmet_buf_list_lock; /* list of aborted NVMET IOs */ + struct list_head lpfc_abts_nvmet_ctx_list; struct list_head lpfc_nvmet_io_wait_list; struct lpfc_nvmet_ctx_info *nvmet_ctx_info; struct lpfc_sglq **lpfc_sglq_active_list; @@ -703,8 +721,6 @@ struct lpfc_sli4_hba { #define LPFC_SLI4_PPNAME_NON 0 #define LPFC_SLI4_PPNAME_GET 1 struct lpfc_iov iov; - spinlock_t abts_nvme_buf_list_lock; /* list of aborted SCSI IOs */ - spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ spinlock_t sgl_list_lock; /* list of aborted els IOs */ spinlock_t nvmet_io_wait_lock; /* IOs waiting for ctx resources */ uint32_t physical_port; @@ -839,7 +855,7 @@ int lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *, int lpfc_sli4_queue_setup(struct lpfc_hba *); void lpfc_sli4_queue_unset(struct lpfc_hba *); int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t); -int lpfc_repost_common_sgl_list(struct lpfc_hba *phba); +int lpfc_repost_io_sgl_list(struct lpfc_hba *phba); uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *); void lpfc_sli4_free_xri(struct lpfc_hba *, int); int lpfc_sli4_post_async_mbox(struct lpfc_hba *); @@ -862,9 +878,9 @@ int lpfc_sli4_resume_rpi(struct lpfc_nodelist *, void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *); void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *); void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *, - struct sli4_wcqe_xri_aborted *); + struct sli4_wcqe_xri_aborted *, int); void lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba, - struct sli4_wcqe_xri_aborted *axri); + struct sli4_wcqe_xri_aborted *axri, int idx); void lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, struct sli4_wcqe_xri_aborted *axri); void lpfc_sli4_els_xri_aborted(struct lpfc_hba *, From 18c27a621614716485b2e881d7e29599f063bb03 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:23 -0800 Subject: [PATCH 1208/4277] scsi: lpfc: cleanup: Remove unused FCP_XRI_ABORT_EVENT slowpath event Both NVME and SCSI aborts are now processed off the CQ workqueue and do not generate events for the slowpath any more. Remove the unused event code. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 1 - drivers/scsi/lpfc/lpfc_hbadisc.c | 2 -- drivers/scsi/lpfc/lpfc_sli.c | 30 ------------------------------ 3 files changed, 33 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 19827ce7a4d9..feae8fb57623 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -711,7 +711,6 @@ struct lpfc_hba { #define HBA_FCOE_MODE 0x4 /* HBA function in FCoE Mode */ #define HBA_SP_QUEUE_EVT 0x8 /* Slow-path qevt posted to worker thread*/ #define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */ -#define FCP_XRI_ABORT_EVENT 0x20 #define ELS_XRI_ABORT_EVENT 0x40 #define ASYNC_EVENT 0x80 #define LINK_DISABLED 0x100 /* Link disabled by user */ diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index b183b882d506..62689a06c188 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -638,8 +638,6 @@ lpfc_work_done(struct lpfc_hba *phba) if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) { if (phba->hba_flag & HBA_RRQ_ACTIVE) lpfc_handle_rrq_active(phba); - if (phba->hba_flag & FCP_XRI_ABORT_EVENT) - lpfc_sli4_fcp_xri_abort_event_proc(phba); if (phba->hba_flag & ELS_XRI_ABORT_EVENT) lpfc_sli4_els_xri_abort_event_proc(phba); if (phba->hba_flag & ASYNC_EVENT) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index ab1b9d9123b6..7847ce2a9409 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -12892,36 +12892,6 @@ lpfc_sli_intr_handler(int irq, void *dev_id) return (sp_irq_rc == IRQ_HANDLED) ? sp_irq_rc : fp_irq_rc; } /* lpfc_sli_intr_handler */ -/** - * lpfc_sli4_fcp_xri_abort_event_proc - Process fcp xri abort event - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked by the worker thread to process all the pending - * SLI4 FCP abort XRI events. - **/ -void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba) -{ - struct lpfc_cq_event *cq_event; - - /* First, declare the fcp xri abort event has been handled */ - spin_lock_irq(&phba->hbalock); - phba->hba_flag &= ~FCP_XRI_ABORT_EVENT; - spin_unlock_irq(&phba->hbalock); - /* Now, handle all the fcp xri abort events */ - while (!list_empty(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue)) { - /* Get the first event from the head of the event queue */ - spin_lock_irq(&phba->hbalock); - list_remove_head(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue, - cq_event, struct lpfc_cq_event, list); - spin_unlock_irq(&phba->hbalock); - /* Notify aborted XRI for FCP work queue */ - lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri, - cq_event->hdwq); - /* Free the event processed back to the free pool */ - lpfc_sli4_cq_event_release(phba, cq_event); - } -} - /** * lpfc_sli4_els_xri_abort_event_proc - Process els xri abort event * @phba: pointer to lpfc hba data structure. From 63df6d637e3358e64b43e7a774939f8f963926cb Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:24 -0800 Subject: [PATCH 1209/4277] scsi: lpfc: Adapt cpucheck debugfs logic to Hardware Queues Similar to the io execution path that reports cpu context information, the debugfs routines for cpu information needs to be aligned with new hardware queue implementation. Convert debugfs cnd nvme cpucheck statistics to report information per Hardware Queue. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 5 -- drivers/scsi/lpfc/lpfc_debugfs.c | 131 ++++++++++++++++--------------- drivers/scsi/lpfc/lpfc_nvme.c | 37 ++++----- drivers/scsi/lpfc/lpfc_nvmet.c | 64 +++++++-------- drivers/scsi/lpfc/lpfc_sli4.h | 11 +++ 5 files changed, 128 insertions(+), 120 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index feae8fb57623..310437b6b51a 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1152,11 +1152,6 @@ struct lpfc_hba { uint16_t sfp_warning; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS -#define LPFC_CHECK_CPU_CNT 32 - uint32_t cpucheck_rcv_io[LPFC_CHECK_CPU_CNT]; - uint32_t cpucheck_xmt_io[LPFC_CHECK_CPU_CNT]; - uint32_t cpucheck_cmpl_io[LPFC_CHECK_CPU_CNT]; - uint32_t cpucheck_ccmpl_io[LPFC_CHECK_CPU_CNT]; uint16_t cpucheck_on; #define LPFC_CHECK_OFF 0 #define LPFC_CHECK_NVME_IO 1 diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index a1b7263bfe2a..d84fb8c088b1 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1366,62 +1366,67 @@ static int lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size) { struct lpfc_hba *phba = vport->phba; - int i; + struct lpfc_sli4_hdw_queue *qp; + int i, j; int len = 0; - uint32_t tot_xmt = 0; - uint32_t tot_rcv = 0; - uint32_t tot_cmpl = 0; - uint32_t tot_ccmpl = 0; + uint32_t tot_xmt; + uint32_t tot_rcv; + uint32_t tot_cmpl; - if (phba->nvmet_support == 0) { - /* NVME Initiator */ - len += snprintf(buf + len, PAGE_SIZE - len, - "CPUcheck %s\n", - (phba->cpucheck_on & LPFC_CHECK_NVME_IO ? - "Enabled" : "Disabled")); - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { - if (i >= LPFC_CHECK_CPU_CNT) - break; - len += snprintf(buf + len, PAGE_SIZE - len, - "%02d: xmit x%08x cmpl x%08x\n", - i, phba->cpucheck_xmt_io[i], - phba->cpucheck_cmpl_io[i]); - tot_xmt += phba->cpucheck_xmt_io[i]; - tot_cmpl += phba->cpucheck_cmpl_io[i]; - } - len += snprintf(buf + len, PAGE_SIZE - len, - "tot:xmit x%08x cmpl x%08x\n", - tot_xmt, tot_cmpl); - return len; - } - - /* NVME Target */ len += snprintf(buf + len, PAGE_SIZE - len, "CPUcheck %s ", - (phba->cpucheck_on & LPFC_CHECK_NVMET_IO ? - "IO Enabled - " : "IO Disabled - ")); - len += snprintf(buf + len, PAGE_SIZE - len, - "%s\n", - (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ? - "Rcv Enabled\n" : "Rcv Disabled\n")); - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { - if (i >= LPFC_CHECK_CPU_CNT) - break; + (phba->cpucheck_on & LPFC_CHECK_NVME_IO ? + "Enabled" : "Disabled")); + if (phba->nvmet_support) { len += snprintf(buf + len, PAGE_SIZE - len, - "%02d: xmit x%08x ccmpl x%08x " - "cmpl x%08x rcv x%08x\n", - i, phba->cpucheck_xmt_io[i], - phba->cpucheck_ccmpl_io[i], - phba->cpucheck_cmpl_io[i], - phba->cpucheck_rcv_io[i]); - tot_xmt += phba->cpucheck_xmt_io[i]; - tot_rcv += phba->cpucheck_rcv_io[i]; - tot_cmpl += phba->cpucheck_cmpl_io[i]; - tot_ccmpl += phba->cpucheck_ccmpl_io[i]; + "%s\n", + (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ? + "Rcv Enabled\n" : "Rcv Disabled\n")); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + } + + for (i = 0; i < phba->cfg_hdw_queue; i++) { + qp = &phba->sli4_hba.hdwq[i]; + + tot_rcv = 0; + tot_xmt = 0; + tot_cmpl = 0; + for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) { + tot_xmt += qp->cpucheck_xmt_io[j]; + tot_cmpl += qp->cpucheck_cmpl_io[j]; + if (phba->nvmet_support) + tot_rcv += qp->cpucheck_rcv_io[j]; + } + + /* Only display Hardware Qs with something */ + if (!tot_xmt && !tot_cmpl && !tot_rcv) + continue; + + len += snprintf(buf + len, PAGE_SIZE - len, + "HDWQ %03d: ", i); + for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) { + /* Only display non-zero counters */ + if (!qp->cpucheck_xmt_io[j] && + !qp->cpucheck_cmpl_io[j] && + !qp->cpucheck_rcv_io[j]) + continue; + if (phba->nvmet_support) { + len += snprintf(buf + len, PAGE_SIZE - len, + "CPU %03d: %x/%x/%x ", j, + qp->cpucheck_rcv_io[j], + qp->cpucheck_xmt_io[j], + qp->cpucheck_cmpl_io[j]); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "CPU %03d: %x/%x ", j, + qp->cpucheck_xmt_io[j], + qp->cpucheck_cmpl_io[j]); + } + } + len += snprintf(buf + len, PAGE_SIZE - len, + "Total: %x\n", tot_xmt); } - len += snprintf(buf + len, PAGE_SIZE - len, - "tot:xmit x%08x ccmpl x%08x cmpl x%08x rcv x%08x\n", - tot_xmt, tot_ccmpl, tot_cmpl, tot_rcv); return len; } @@ -2474,9 +2479,10 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf, struct lpfc_debug *debug = file->private_data; struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private; struct lpfc_hba *phba = vport->phba; + struct lpfc_sli4_hdw_queue *qp; char mybuf[64]; char *pbuf; - int i; + int i, j; if (nbytes > 64) nbytes = 64; @@ -2506,13 +2512,14 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf, return strlen(pbuf); } else if ((strncmp(pbuf, "zero", sizeof("zero") - 1) == 0)) { - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { - if (i >= LPFC_CHECK_CPU_CNT) - break; - phba->cpucheck_rcv_io[i] = 0; - phba->cpucheck_xmt_io[i] = 0; - phba->cpucheck_cmpl_io[i] = 0; - phba->cpucheck_ccmpl_io[i] = 0; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + qp = &phba->sli4_hba.hdwq[i]; + + for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) { + qp->cpucheck_rcv_io[j] = 0; + qp->cpucheck_xmt_io[j] = 0; + qp->cpucheck_cmpl_io[j] = 0; + } } return strlen(pbuf); } @@ -5358,9 +5365,9 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) /* Setup hbqinfo */ snprintf(name, sizeof(name), "hbqinfo"); phba->debug_hbqinfo = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, - phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_hbqinfo); + debugfs_create_file(name, S_IFREG | 0644, + phba->hba_debugfs_root, + phba, &lpfc_debugfs_op_hbqinfo); /* Setup hdwqinfo */ snprintf(name, sizeof(name), "hdwqinfo"); @@ -5370,7 +5377,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) phba, &lpfc_debugfs_op_hdwqinfo); if (!phba->debug_hdwqinfo) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0411 Cant create debugfs hdwqinfo\n"); + "0511 Cant create debugfs hdwqinfo\n"); goto debug_failed; } diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 0ecc73a6634f..fe0190b48abd 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -965,7 +965,7 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct lpfc_nvme_fcpreq_priv *freqpriv; struct lpfc_nvme_lport *lport; struct lpfc_nvme_ctrl_stat *cstat; - uint32_t code, status, idx; + uint32_t code, status, idx, cpu; uint16_t cid, sqhd, data; uint32_t *ptr; @@ -1136,13 +1136,17 @@ out_err: lpfc_nvme_ktime(phba, lpfc_ncmd); } if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) { - if (lpfc_ncmd->cpu != smp_processor_id()) - lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, - "6701 CPU Check cmpl: " - "cpu %d expect %d\n", - smp_processor_id(), lpfc_ncmd->cpu); - if (lpfc_ncmd->cpu < LPFC_CHECK_CPU_CNT) - phba->cpucheck_cmpl_io[lpfc_ncmd->cpu]++; + idx = lpfc_ncmd->cur_iocbq.hba_wqidx; + cpu = smp_processor_id(); + if (cpu < LPFC_CHECK_CPU_CNT) { + if (lpfc_ncmd->cpu != cpu) + lpfc_printf_vlog(vport, + KERN_INFO, LOG_NVME_IOERR, + "6701 CPU Check cmpl: " + "cpu %d expect %d\n", + cpu, lpfc_ncmd->cpu); + phba->sli4_hba.hdwq[idx].cpucheck_cmpl_io[cpu]++; + } } #endif @@ -1421,7 +1425,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, { int ret = 0; int expedite = 0; - int idx; + int idx, cpu; struct lpfc_nvme_lport *lport; struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_vport *vport; @@ -1620,21 +1624,18 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, lpfc_ncmd->ts_cmd_wqput = ktime_get_ns(); if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) { - lpfc_ncmd->cpu = smp_processor_id(); - if (lpfc_ncmd->cpu != lpfc_queue_info->index) { - /* Check for admin queue */ - if (lpfc_queue_info->qidx) { + cpu = smp_processor_id(); + if (cpu < LPFC_CHECK_CPU_CNT) { + lpfc_ncmd->cpu = cpu; + if (idx != cpu) lpfc_printf_vlog(vport, - KERN_ERR, LOG_NVME_IOERR, + KERN_INFO, LOG_NVME_IOERR, "6702 CPU Check cmd: " "cpu %d wq %d\n", lpfc_ncmd->cpu, lpfc_queue_info->index); - } - lpfc_ncmd->cpu = lpfc_queue_info->index; + phba->sli4_hba.hdwq[idx].cpucheck_xmt_io[cpu]++; } - if (lpfc_ncmd->cpu < LPFC_CHECK_CPU_CNT) - phba->cpucheck_xmt_io[lpfc_ncmd->cpu]++; } #endif return 0; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index f2a30ee9702b..b5e287cacc2a 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -744,16 +744,6 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, ktime_get_ns(); } } - if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { - id = smp_processor_id(); - if (ctxp->cpu != id) - lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, - "6703 CPU Check cmpl: " - "cpu %d expect %d\n", - id, ctxp->cpu); - if (ctxp->cpu < LPFC_CHECK_CPU_CNT) - phba->cpucheck_cmpl_io[id]++; - } #endif rsp->done(rsp); #ifdef CONFIG_SCSI_LPFC_DEBUG_FS @@ -771,19 +761,22 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, ctxp->ts_isr_data = cmdwqe->isr_timestamp; ctxp->ts_data_nvme = ktime_get_ns(); } - if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { - id = smp_processor_id(); - if (ctxp->cpu != id) - lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, - "6704 CPU Check cmdcmpl: " - "cpu %d expect %d\n", - id, ctxp->cpu); - if (ctxp->cpu < LPFC_CHECK_CPU_CNT) - phba->cpucheck_ccmpl_io[id]++; - } #endif rsp->done(rsp); } +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { + id = smp_processor_id(); + if (id < LPFC_CHECK_CPU_CNT) { + if (ctxp->cpu != id) + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR, + "6704 CPU Check cmdcmpl: " + "cpu %d expect %d\n", + id, ctxp->cpu); + phba->sli4_hba.hdwq[rsp->hwqid].cpucheck_cmpl_io[id]++; + } + } +#endif } static int @@ -910,16 +903,15 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, } if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { int id = smp_processor_id(); - ctxp->cpu = id; - if (id < LPFC_CHECK_CPU_CNT) - phba->cpucheck_xmt_io[id]++; - if (rsp->hwqid != id) { - lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, - "6705 CPU Check OP: " - "cpu %d expect %d\n", - id, rsp->hwqid); - ctxp->cpu = rsp->hwqid; + if (id < LPFC_CHECK_CPU_CNT) { + if (rsp->hwqid != id) + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR, + "6705 CPU Check OP: " + "cpu %d expect %d\n", + id, rsp->hwqid); + phba->sli4_hba.hdwq[rsp->hwqid].cpucheck_xmt_io[id]++; } + ctxp->cpu = id; /* Setup cpu for cmpl check */ } #endif @@ -1897,9 +1889,6 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, uint32_t size, oxid, sid, rc, qno; unsigned long iflag; int current_cpu; -#ifdef CONFIG_SCSI_LPFC_DEBUG_FS - uint32_t id; -#endif if (!IS_ENABLED(CONFIG_NVME_TARGET_FC)) return; @@ -1940,9 +1929,14 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, #ifdef CONFIG_SCSI_LPFC_DEBUG_FS if (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV) { - id = smp_processor_id(); - if (id < LPFC_CHECK_CPU_CNT) - phba->cpucheck_rcv_io[id]++; + if (current_cpu < LPFC_CHECK_CPU_CNT) { + if (idx != current_cpu) + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR, + "6703 CPU Check rcv: " + "cpu %d expect %d\n", + current_cpu, idx); + phba->sli4_hba.hdwq[idx].cpucheck_rcv_io[current_cpu]++; + } } #endif diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 4862249732dd..8e3e99d52f75 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -20,6 +20,10 @@ * included with this package. * *******************************************************************/ +#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS) +#define CONFIG_SCSI_LPFC_DEBUG_FS +#endif + #define LPFC_ACTIVE_MBOX_WAIT_CNT 100 #define LPFC_XRI_EXCH_BUSY_WAIT_TMO 10000 #define LPFC_XRI_EXCH_BUSY_WAIT_T1 10 @@ -555,6 +559,13 @@ struct lpfc_sli4_hdw_queue { uint32_t empty_io_bufs; uint32_t abts_scsi_io_bufs; uint32_t abts_nvme_io_bufs; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS +#define LPFC_CHECK_CPU_CNT 128 + uint32_t cpucheck_rcv_io[LPFC_CHECK_CPU_CNT]; + uint32_t cpucheck_xmt_io[LPFC_CHECK_CPU_CNT]; + uint32_t cpucheck_cmpl_io[LPFC_CHECK_CPU_CNT]; +#endif }; struct lpfc_sli4_hba { From 4c47efc140fa926f00aa59c248458d95bd7b5eab Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:25 -0800 Subject: [PATCH 1210/4277] scsi: lpfc: Move SCSI and NVME Stats to hardware queue structures Many io statistics were being sampled and saved using adapter-based data structures. This was creating a lot of contention and cache thrashing in the I/O path. Move the statistics to the hardware queue data structures. Given the per-queue data structures, use of atomic types is lessened. Add new sysfs and debugfs stat routines to collate the per hardware queue values and report at an adapter level. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 9 +- drivers/scsi/lpfc/lpfc_attr.c | 68 +++++++++++-- drivers/scsi/lpfc/lpfc_debugfs.c | 158 +++++++++++++++++++++++++++++-- drivers/scsi/lpfc/lpfc_debugfs.h | 3 + drivers/scsi/lpfc/lpfc_init.c | 40 +++++--- drivers/scsi/lpfc/lpfc_nvme.c | 57 ++++------- drivers/scsi/lpfc/lpfc_nvme.h | 11 +-- drivers/scsi/lpfc/lpfc_scsi.c | 47 +++++---- drivers/scsi/lpfc/lpfc_scsi.h | 3 + drivers/scsi/lpfc/lpfc_sli4.h | 11 +++ 10 files changed, 304 insertions(+), 103 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 310437b6b51a..9262c52e32d6 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -479,6 +479,7 @@ struct lpfc_vport { struct dentry *debug_disc_trc; struct dentry *debug_nodelist; struct dentry *debug_nvmestat; + struct dentry *debug_scsistat; struct dentry *debug_nvmektime; struct dentry *debug_cpucheck; struct dentry *vport_debugfs_root; @@ -946,14 +947,6 @@ struct lpfc_hba { struct timer_list eratt_poll; uint32_t eratt_poll_interval; - /* - * stat counters - */ - atomic_t fc4ScsiInputRequests; - atomic_t fc4ScsiOutputRequests; - atomic_t fc4ScsiControlRequests; - atomic_t fc4ScsiIoCmpls; - uint64_t bg_guard_err_cnt; uint64_t bg_apptag_err_cnt; uint64_t bg_reftag_err_cnt; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 1671d9371d3b..e10d930fcb6a 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -64,9 +64,6 @@ #define LPFC_MIN_MRQ_POST 512 #define LPFC_MAX_MRQ_POST 2048 -#define LPFC_MAX_NVME_INFO_TMP_LEN 100 -#define LPFC_NVME_INFO_MORE_STR "\nCould be more info...\n" - /* * Write key size should be multiple of 4. If write key is changed * make sure that library write key is also changed. @@ -155,7 +152,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, struct lpfc_nvme_rport *rport; struct lpfc_nodelist *ndlp; struct nvme_fc_remote_port *nrport; - struct lpfc_nvme_ctrl_stat *cstat; + struct lpfc_fc4_ctrl_stat *cstat; uint64_t data1, data2, data3; uint64_t totin, totout, tot; char *statep; @@ -457,12 +454,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, totin = 0; totout = 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { - cstat = &lport->cstat[i]; - tot = atomic_read(&cstat->fc4NvmeIoCmpls); + cstat = &phba->sli4_hba.hdwq[i].nvme_cstat; + tot = cstat->io_cmpls; totin += tot; - data1 = atomic_read(&cstat->fc4NvmeInputRequests); - data2 = atomic_read(&cstat->fc4NvmeOutputRequests); - data3 = atomic_read(&cstat->fc4NvmeControlRequests); + data1 = cstat->input_requests; + data2 = cstat->output_requests; + data3 = cstat->control_requests; totout += (data1 + data2 + data3); } scnprintf(tmp, sizeof(tmp), @@ -508,6 +505,57 @@ buffer_done: return len; } +static ssize_t +lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = shost_priv(shost); + struct lpfc_hba *phba = vport->phba; + int len; + struct lpfc_fc4_ctrl_stat *cstat; + u64 data1, data2, data3; + u64 tot, totin, totout; + int i; + char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; + + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || + (phba->sli_rev != LPFC_SLI_REV4)) + return 0; + + scnprintf(buf, PAGE_SIZE, "SCSI HDWQ Statistics\n"); + + totin = 0; + totout = 0; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + cstat = &phba->sli4_hba.hdwq[i].scsi_cstat; + tot = cstat->io_cmpls; + totin += tot; + data1 = cstat->input_requests; + data2 = cstat->output_requests; + data3 = cstat->control_requests; + totout += (data1 + data2 + data3); + + scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx " + "IO %016llx ", i, data1, data2, data3); + if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) + goto buffer_done; + + scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n", + tot, ((data1 + data2 + data3) - tot)); + if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE) + goto buffer_done; + } + scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx " + "OutIO %016llx\n", totin, totout, totout - totin); + strlcat(buf, tmp, PAGE_SIZE); + +buffer_done: + len = strnlen(buf, PAGE_SIZE); + + return len; +} + static ssize_t lpfc_bg_info_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2573,6 +2621,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \ static DEVICE_ATTR(nvme_info, 0444, lpfc_nvme_info_show, NULL); +static DEVICE_ATTR(scsi_stat, 0444, lpfc_scsi_stat_show, NULL); static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL); static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL); static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL); @@ -5642,6 +5691,7 @@ LPFC_ATTR_RW(enable_dpp, 1, 0, 1, "Enable Direct Packet Push"); struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_nvme_info, + &dev_attr_scsi_stat, &dev_attr_bg_info, &dev_attr_bg_guard_err, &dev_attr_bg_apptag_err, diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index d84fb8c088b1..218642d7c488 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -840,7 +840,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) struct lpfc_nvmet_tgtport *tgtp; struct lpfc_nvmet_rcv_ctx *ctxp, *next_ctxp; struct nvme_fc_local_port *localport; - struct lpfc_nvme_ctrl_stat *cstat; + struct lpfc_fc4_ctrl_stat *cstat; struct lpfc_nvme_lport *lport; uint64_t data1, data2, data3; uint64_t tot, totin, totout; @@ -979,7 +979,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) return len; len += snprintf(buf + len, size - len, - "\nNVME Lport Statistics\n"); + "\nNVME HDWQ Statistics\n"); len += snprintf(buf + len, size - len, "LS: Xmt %016x Cmpl %016x\n", @@ -993,12 +993,12 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) totin = 0; totout = 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { - cstat = &lport->cstat[i]; - tot = atomic_read(&cstat->fc4NvmeIoCmpls); + cstat = &phba->sli4_hba.hdwq[i].nvme_cstat; + tot = cstat->io_cmpls; totin += tot; - data1 = atomic_read(&cstat->fc4NvmeInputRequests); - data2 = atomic_read(&cstat->fc4NvmeOutputRequests); - data3 = atomic_read(&cstat->fc4NvmeControlRequests); + data1 = cstat->input_requests; + data2 = cstat->output_requests; + data3 = cstat->control_requests; totout += (data1 + data2 + data3); /* Limit to 32, debugfs display buffer limitation */ @@ -1006,7 +1006,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) continue; len += snprintf(buf + len, PAGE_SIZE - len, - "FCP (%d): Rd %016llx Wr %016llx " + "HDWQ (%d): Rd %016llx Wr %016llx " "IO %016llx ", i, data1, data2, data3); len += snprintf(buf + len, PAGE_SIZE - len, @@ -1046,6 +1046,66 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) return len; } +/** + * lpfc_debugfs_scsistat_data - Dump target node list to a buffer + * @vport: The vport to gather target node info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the SCSI statistics associated with @vport + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size) +{ + int len; + struct lpfc_hba *phba = vport->phba; + struct lpfc_fc4_ctrl_stat *cstat; + u64 data1, data2, data3; + u64 tot, totin, totout; + int i; + char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; + + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || + (phba->sli_rev != LPFC_SLI_REV4)) + return 0; + + scnprintf(buf, size, "SCSI HDWQ Statistics\n"); + + totin = 0; + totout = 0; + for (i = 0; i < phba->cfg_hdw_queue; i++) { + cstat = &phba->sli4_hba.hdwq[i].scsi_cstat; + tot = cstat->io_cmpls; + totin += tot; + data1 = cstat->input_requests; + data2 = cstat->output_requests; + data3 = cstat->control_requests; + totout += (data1 + data2 + data3); + + scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx " + "IO %016llx ", i, data1, data2, data3); + if (strlcat(buf, tmp, size) >= size) + goto buffer_done; + + scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n", + tot, ((data1 + data2 + data3) - tot)); + if (strlcat(buf, tmp, size) >= size) + goto buffer_done; + } + scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx " + "OutIO %016llx\n", totin, totout, totout - totin); + strlcat(buf, tmp, size); + +buffer_done: + len = strnlen(buf, size); + + return len; +} /** * lpfc_debugfs_nvmektime_data - Dump target node list to a buffer @@ -2211,6 +2271,64 @@ lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf, return nbytes; } +static int +lpfc_debugfs_scsistat_open(struct inode *inode, struct file *file) +{ + struct lpfc_vport *vport = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kzalloc(LPFC_SCSISTAT_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + debug->len = lpfc_debugfs_scsistat_data(vport, debug->buffer, + LPFC_SCSISTAT_SIZE); + + debug->i_private = inode->i_private; + file->private_data = debug; + + rc = 0; +out: + return rc; +} + +static ssize_t +lpfc_debugfs_scsistat_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private; + struct lpfc_hba *phba = vport->phba; + char mybuf[6] = {0}; + int i; + + /* Protect copy from user */ + if (!access_ok(buf, nbytes)) + return -EFAULT; + + if (copy_from_user(mybuf, buf, (nbytes >= sizeof(mybuf)) ? + (sizeof(mybuf) - 1) : nbytes)) + return -EFAULT; + + if ((strncmp(&mybuf[0], "reset", strlen("reset")) == 0) || + (strncmp(&mybuf[0], "zero", strlen("zero")) == 0)) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { + memset(&phba->sli4_hba.hdwq[i].scsi_cstat, 0, + sizeof(phba->sli4_hba.hdwq[i].scsi_cstat)); + } + } + + return nbytes; +} + static int lpfc_debugfs_nvmektime_open(struct inode *inode, struct file *file) { @@ -4972,6 +5090,16 @@ static const struct file_operations lpfc_debugfs_op_nvmestat = { .release = lpfc_debugfs_release, }; +#undef lpfc_debugfs_op_scsistat +static const struct file_operations lpfc_debugfs_op_scsistat = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_scsistat_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_scsistat_write, + .release = lpfc_debugfs_release, +}; + #undef lpfc_debugfs_op_nvmektime static const struct file_operations lpfc_debugfs_op_nvmektime = { .owner = THIS_MODULE, @@ -5612,6 +5740,17 @@ nvmeio_off: vport->vport_debugfs_root, vport, &lpfc_debugfs_op_nvmestat); + snprintf(name, sizeof(name), "scsistat"); + vport->debug_scsistat = + debugfs_create_file(name, 0644, + vport->vport_debugfs_root, + vport, &lpfc_debugfs_op_scsistat); + if (!vport->debug_scsistat) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0811 Cannot create debugfs scsistat\n"); + goto debug_failed; + } + snprintf(name, sizeof(name), "nvmektime"); vport->debug_nvmektime = debugfs_create_file(name, 0644, @@ -5750,6 +5889,9 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) debugfs_remove(vport->debug_nvmestat); /* nvmestat */ vport->debug_nvmestat = NULL; + debugfs_remove(vport->debug_scsistat); /* scsistat */ + vport->debug_scsistat = NULL; + debugfs_remove(vport->debug_nvmektime); /* nvmektime */ vport->debug_nvmektime = NULL; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index c904fa74dfbc..2360ec8fb848 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -50,6 +50,9 @@ #define LPFC_CPUCHECK_SIZE 8192 #define LPFC_NVMEIO_TRC_SIZE 8192 +/* scsistat output buffer size */ +#define LPFC_SCSISTAT_SIZE 8192 + #define LPFC_DEBUG_OUT_LINE_SZ 80 /* diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index ed0318b9003e..a15c3aa569b5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1282,7 +1282,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) struct lpfc_register reg_data; struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; - struct lpfc_nvme_ctrl_stat *cstat; + struct lpfc_fc4_ctrl_stat *cstat; void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr; vports = lpfc_create_vport_work_array(phba); @@ -1324,16 +1324,13 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) tot = 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { - cstat = &lport->cstat[i]; - data1 = atomic_read( - &cstat->fc4NvmeInputRequests); - data2 = atomic_read( - &cstat->fc4NvmeOutputRequests); - data3 = atomic_read( - &cstat->fc4NvmeControlRequests); + cstat = + &phba->sli4_hba.hdwq[i].nvme_cstat; + data1 = cstat->input_requests; + data2 = cstat->output_requests; + data3 = cstat->control_requests; tot += (data1 + data2 + data3); - tot -= atomic_read( - &cstat->fc4NvmeIoCmpls); + tot -= cstat->io_cmpls; } } } @@ -7221,10 +7218,6 @@ lpfc_create_shost(struct lpfc_hba *phba) phba->fc_arbtov = FF_DEF_ARBTOV; atomic_set(&phba->sdev_cnt, 0); - atomic_set(&phba->fc4ScsiInputRequests, 0); - atomic_set(&phba->fc4ScsiOutputRequests, 0); - atomic_set(&phba->fc4ScsiControlRequests, 0); - atomic_set(&phba->fc4ScsiIoCmpls, 0); vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev); if (!vport) return -ENODEV; @@ -8776,6 +8769,25 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) phba->sli4_hba.nvmet_mrq_data[idx] = qdesc; } } + +#if defined(BUILD_NVME) + /* Clear NVME stats */ + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + memset(&phba->sli4_hba.hdwq[idx].nvme_cstat, 0, + sizeof(phba->sli4_hba.hdwq[idx].nvme_cstat)); + } + } +#endif + + /* Clear SCSI stats */ + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + memset(&phba->sli4_hba.hdwq[idx].scsi_cstat, 0, + sizeof(phba->sli4_hba.hdwq[idx].scsi_cstat)); + } + } + return 0; out_error: diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index fe0190b48abd..c13638a3c0e7 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -964,7 +964,6 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct lpfc_nodelist *ndlp; struct lpfc_nvme_fcpreq_priv *freqpriv; struct lpfc_nvme_lport *lport; - struct lpfc_nvme_ctrl_stat *cstat; uint32_t code, status, idx, cpu; uint16_t cid, sqhd, data; uint32_t *ptr; @@ -991,17 +990,15 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, nCmd = lpfc_ncmd->nvmeCmd; status = bf_get(lpfc_wcqe_c_status, wcqe); + idx = lpfc_ncmd->cur_iocbq.hba_wqidx; + phba->sli4_hba.hdwq[idx].nvme_cstat.io_cmpls++; + if (vport->localport) { lport = (struct lpfc_nvme_lport *)vport->localport->private; - if (lport) { - idx = lpfc_ncmd->cur_iocbq.hba_wqidx; - cstat = &lport->cstat[idx]; - atomic_inc(&cstat->fc4NvmeIoCmpls); - if (status) { - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_fcp_xb); - atomic_inc(&lport->cmpl_fcp_err); - } + if (lport && status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_fcp_xb); + atomic_inc(&lport->cmpl_fcp_err); } } @@ -1186,7 +1183,7 @@ static int lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, struct lpfc_nvme_buf *lpfc_ncmd, struct lpfc_nodelist *pnode, - struct lpfc_nvme_ctrl_stat *cstat) + struct lpfc_fc4_ctrl_stat *cstat) { struct lpfc_hba *phba = vport->phba; struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; @@ -1224,7 +1221,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, } else { wqe->fcp_iwrite.initial_xfer_len = 0; } - atomic_inc(&cstat->fc4NvmeOutputRequests); + cstat->output_requests++; } else { /* From the iread template, initialize words 7 - 11 */ memcpy(&wqe->words[7], @@ -1237,13 +1234,13 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, /* Word 5 */ wqe->fcp_iread.rsrvd5 = 0; - atomic_inc(&cstat->fc4NvmeInputRequests); + cstat->input_requests++; } } else { /* From the icmnd template, initialize words 4 - 11 */ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4], sizeof(uint32_t) * 8); - atomic_inc(&cstat->fc4NvmeControlRequests); + cstat->control_requests++; } /* * Finish initializing those WQE fields that are independent @@ -1427,7 +1424,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, int expedite = 0; int idx, cpu; struct lpfc_nvme_lport *lport; - struct lpfc_nvme_ctrl_stat *cstat; + struct lpfc_fc4_ctrl_stat *cstat; struct lpfc_vport *vport; struct lpfc_hba *phba; struct lpfc_nodelist *ndlp; @@ -1590,7 +1587,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, */ idx = lpfc_queue_info->index; lpfc_ncmd->cur_iocbq.hba_wqidx = idx; - cstat = &lport->cstat[idx]; + cstat = &phba->sli4_hba.hdwq[idx].nvme_cstat; lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp, cstat); ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); @@ -1643,11 +1640,11 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, out_free_nvme_buf: if (lpfc_ncmd->nvmeCmd->sg_cnt) { if (lpfc_ncmd->nvmeCmd->io_dir == NVMEFC_FCP_WRITE) - atomic_dec(&cstat->fc4NvmeOutputRequests); + cstat->output_requests--; else - atomic_dec(&cstat->fc4NvmeInputRequests); + cstat->input_requests--; } else - atomic_dec(&cstat->fc4NvmeControlRequests); + cstat->control_requests--; lpfc_release_nvme_buf(phba, lpfc_ncmd); out_fail: return ret; @@ -2079,8 +2076,6 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) struct nvme_fc_port_info nfcp_info; struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; - struct lpfc_nvme_ctrl_stat *cstat; - int i; /* Initialize this localport instance. The vport wwn usage ensures * that NPIV is accounted for. @@ -2097,11 +2092,6 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1; lpfc_nvme_template.max_hw_queues = phba->cfg_hdw_queue; - cstat = kmalloc((sizeof(struct lpfc_nvme_ctrl_stat) * - phba->cfg_hdw_queue), GFP_KERNEL); - if (!cstat) - return -ENOMEM; - /* localport is allocated from the stack, but the registration * call allocates heap memory as well as the private area. */ @@ -2124,7 +2114,6 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) lport = (struct lpfc_nvme_lport *)localport->private; vport->localport = localport; lport->vport = vport; - lport->cstat = cstat; vport->nvmei_support = 1; atomic_set(&lport->xmt_fcp_noxri, 0); @@ -2141,16 +2130,7 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) atomic_set(&lport->cmpl_ls_err, 0); atomic_set(&lport->fc4NvmeLsRequests, 0); atomic_set(&lport->fc4NvmeLsCmpls, 0); - - for (i = 0; i < phba->cfg_hdw_queue; i++) { - cstat = &lport->cstat[i]; - atomic_set(&cstat->fc4NvmeInputRequests, 0); - atomic_set(&cstat->fc4NvmeOutputRequests, 0); - atomic_set(&cstat->fc4NvmeControlRequests, 0); - atomic_set(&cstat->fc4NvmeIoCmpls, 0); - } - } else - kfree(cstat); + } return ret; } @@ -2212,7 +2192,6 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) #if (IS_ENABLED(CONFIG_NVME_FC)) struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; - struct lpfc_nvme_ctrl_stat *cstat; int ret; if (vport->nvmei_support == 0) @@ -2221,7 +2200,6 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) localport = vport->localport; vport->localport = NULL; lport = (struct lpfc_nvme_lport *)localport->private; - cstat = lport->cstat; lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, "6011 Destroying NVME localport %p\n", @@ -2237,7 +2215,6 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) * indefinitely or succeeds */ lpfc_nvme_lport_unreg_wait(vport, lport); - kfree(cstat); /* Regardless of the unregister upcall response, clear * nvmei_support. All rports are unregistered and the diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 4a020b9c8fbf..974fbec7dd04 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -30,6 +30,9 @@ #define LPFC_NVME_FB_SHIFT 9 #define LPFC_NVME_MAX_FB (1 << 20) /* 1M */ +#define LPFC_MAX_NVME_INFO_TMP_LEN 100 +#define LPFC_NVME_INFO_MORE_STR "\nCould be more info...\n" + #define lpfc_ndlp_get_nrport(ndlp) \ ((!ndlp->nrport || (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG)) \ ? NULL : ndlp->nrport) @@ -40,19 +43,11 @@ struct lpfc_nvme_qhandle { uint32_t cpu_id; /* current cpu id at time of create */ }; -struct lpfc_nvme_ctrl_stat { - atomic_t fc4NvmeInputRequests; - atomic_t fc4NvmeOutputRequests; - atomic_t fc4NvmeControlRequests; - atomic_t fc4NvmeIoCmpls; -}; - /* Declare nvme-based local and remote port definitions. */ struct lpfc_nvme_lport { struct lpfc_vport *vport; struct completion lport_unreg_done; /* Add stats counters here */ - struct lpfc_nvme_ctrl_stat *cstat; atomic_t fc4NvmeLsRequests; atomic_t fc4NvmeLsCmpls; atomic_t xmt_fcp_noxri; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 261d0ebf0093..08f284ca5f1e 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -3722,14 +3722,18 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, unsigned long flags; struct lpfc_fast_path_event *fast_path_evt; struct Scsi_Host *shost; + int idx; uint32_t logit = LOG_FCP; - atomic_inc(&phba->fc4ScsiIoCmpls); - /* Sanity check on return of outstanding command */ cmd = lpfc_cmd->pCmd; if (!cmd) return; + + idx = lpfc_cmd->cur_iocbq.hba_wqidx; + if (phba->sli4_hba.hdwq) + phba->sli4_hba.hdwq[idx].scsi_cstat.io_cmpls++; + shost = cmd->device->host; lpfc_cmd->result = (pIocbOut->iocb.un.ulpWord[4] & IOERR_PARAM_MASK); @@ -3986,7 +3990,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq); + struct lpfc_sli4_hdw_queue *hdwq = NULL; int datadir = scsi_cmnd->sc_data_direction; + int idx; uint8_t *ptr; bool sli4; uint32_t fcpdl; @@ -4012,6 +4018,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, sli4 = (phba->sli_rev == LPFC_SLI_REV4); piocbq->iocb.un.fcpi.fcpi_XRdy = 0; + idx = lpfc_cmd->hdwq; + if (phba->sli4_hba.hdwq) + hdwq = &phba->sli4_hba.hdwq[idx]; /* * There are three possibilities here - use scatter-gather segment, use @@ -4033,19 +4042,22 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, vport->cfg_first_burst_size; } fcp_cmnd->fcpCntl3 = WRITE_DATA; - atomic_inc(&phba->fc4ScsiOutputRequests); + if (hdwq) + hdwq->scsi_cstat.output_requests++; } else { iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR; iocb_cmd->ulpPU = PARM_READ_CHECK; fcp_cmnd->fcpCntl3 = READ_DATA; - atomic_inc(&phba->fc4ScsiInputRequests); + if (hdwq) + hdwq->scsi_cstat.input_requests++; } } else { iocb_cmd->ulpCommand = CMD_FCP_ICMND64_CR; iocb_cmd->un.fcpi.fcpi_parm = 0; iocb_cmd->ulpPU = 0; fcp_cmnd->fcpCntl3 = 0; - atomic_inc(&phba->fc4ScsiControlRequests); + if (hdwq) + hdwq->scsi_cstat.control_requests++; } if (phba->sli_rev == 3 && !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) @@ -4397,7 +4409,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) struct lpfc_nodelist *ndlp; struct lpfc_scsi_buf *lpfc_cmd; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); - int err; + int err, idx; rdata = lpfc_rport_data_from_scsi_device(cmnd->device); @@ -4532,16 +4544,6 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) (uint32_t) (cmnd->request->timeout / 1000)); - switch (lpfc_cmd->fcp_cmnd->fcpCntl3) { - case WRITE_DATA: - atomic_dec(&phba->fc4ScsiOutputRequests); - break; - case READ_DATA: - atomic_dec(&phba->fc4ScsiInputRequests); - break; - default: - atomic_dec(&phba->fc4ScsiControlRequests); - } goto out_host_busy_free_buf; } if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { @@ -4555,7 +4557,20 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) return 0; out_host_busy_free_buf: + idx = lpfc_cmd->hdwq; lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); + if (phba->sli4_hba.hdwq) { + switch (lpfc_cmd->fcp_cmnd->fcpCntl3) { + case WRITE_DATA: + phba->sli4_hba.hdwq[idx].scsi_cstat.output_requests--; + break; + case READ_DATA: + phba->sli4_hba.hdwq[idx].scsi_cstat.input_requests--; + break; + default: + phba->sli4_hba.hdwq[idx].scsi_cstat.control_requests--; + } + } lpfc_release_scsi_buf(phba, lpfc_cmd); out_host_busy: return SCSI_MLQUEUE_HOST_BUSY; diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index ebb5d5860cdb..68e6c4ad3936 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -196,5 +196,8 @@ struct lpfc_scsi_buf { #define TXRDY_PAYLOAD_LEN 12 +/* For sysfs/debugfs tmp string max len */ +#define LPFC_MAX_SCSI_INFO_TMP_LEN 79 + int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd); diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 8e3e99d52f75..a33fb6715329 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -533,6 +533,13 @@ struct lpfc_vector_map_info { }; #define LPFC_VECTOR_MAP_EMPTY 0xffff +struct lpfc_fc4_ctrl_stat { + u32 input_requests; + u32 output_requests; + u32 control_requests; + u32 io_cmpls; +}; + /* SLI4 HBA data structure entries */ struct lpfc_sli4_hdw_queue { /* Pointers to the constructed SLI4 queues */ @@ -560,6 +567,10 @@ struct lpfc_sli4_hdw_queue { uint32_t abts_scsi_io_bufs; uint32_t abts_nvme_io_bufs; + /* FC-4 Stats counters */ + struct lpfc_fc4_ctrl_stat nvme_cstat; + struct lpfc_fc4_ctrl_stat scsi_cstat; + #ifdef CONFIG_SCSI_LPFC_DEBUG_FS #define LPFC_CHECK_CPU_CNT 128 uint32_t cpucheck_rcv_io[LPFC_CHECK_CPU_CNT]; From 1fbf97425002016a8d819a9c7b78c65ed449e64f Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:26 -0800 Subject: [PATCH 1211/4277] scsi: lpfc: Convert ring number to hardware queue for nvme wqe posting. SLI4 nvme functions are passing the SLI3 ring number when posting wqe to hardware. This should be indicating the hardware queue to use, not the ring number. Replace ring number with the hardware queue that should be used. Note: SCSI avoided this issue as it utilized an older lfpc_issue_iocb routine that properly adapts. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_crtn.h | 4 ++-- drivers/scsi/lpfc/lpfc_init.c | 3 ++- drivers/scsi/lpfc/lpfc_nvme.c | 11 ++++++----- drivers/scsi/lpfc/lpfc_nvme.h | 3 ++- drivers/scsi/lpfc/lpfc_nvmet.c | 34 ++++++++++++++++++++++++++-------- drivers/scsi/lpfc/lpfc_nvmet.h | 1 + drivers/scsi/lpfc/lpfc_scsi.c | 9 +++++---- drivers/scsi/lpfc/lpfc_scsi.h | 3 ++- drivers/scsi/lpfc/lpfc_sli.c | 26 ++++++++++++++------------ 9 files changed, 60 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index a623f6f619cc..1bd1362f39a0 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -315,8 +315,8 @@ void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *, LPFC_MBOXQ_t *); int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, uint32_t); -int lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t rnum, - struct lpfc_iocbq *iocbq); +int lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, + struct lpfc_iocbq *pwqe); struct lpfc_sglq *__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xri); struct lpfc_sglq *__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index a15c3aa569b5..36d9c32c9c87 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -3734,7 +3734,8 @@ lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf) return cnt; cnt++; qp = &phba->sli4_hba.hdwq[idx]; - lpfc_cmd->hdwq = idx; + lpfc_cmd->hdwq_no = idx; + lpfc_cmd->hdwq = qp; lpfc_cmd->cur_iocbq.wqe_cmpl = NULL; lpfc_cmd->cur_iocbq.iocb_cmpl = NULL; spin_lock(&qp->io_buf_list_put_lock); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index c13638a3c0e7..f1f697cd7e97 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -528,7 +528,7 @@ lpfc_nvme_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp, lpfc_nvmeio_data(phba, "NVME LS XMIT: xri x%x iotag x%x to x%06x\n", genwqe->sli4_xritag, genwqe->iotag, ndlp->nlp_DID); - rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, genwqe); + rc = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[0], genwqe); if (rc) { lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, "6045 Issue GEN REQ WQE to NPORT x%x " @@ -1605,7 +1605,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, lpfc_ncmd->cur_iocbq.sli4_xritag, lpfc_queue_info->index, ndlp->nlp_DID); - ret = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, &lpfc_ncmd->cur_iocbq); + ret = lpfc_sli4_issue_wqe(phba, lpfc_ncmd->hdwq, &lpfc_ncmd->cur_iocbq); if (ret) { atomic_inc(&lport->xmt_fcp_wqerr); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, @@ -1867,7 +1867,7 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, abts_buf->hba_wqidx = nvmereq_wqe->hba_wqidx; abts_buf->vport = vport; abts_buf->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl; - ret_val = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_buf); + ret_val = lpfc_sli4_issue_wqe(phba, lpfc_nbuf->hdwq, abts_buf); spin_unlock_irqrestore(&phba->hbalock, flags); if (ret_val) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, @@ -1978,7 +1978,8 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl; lpfc_ncmd->start_time = jiffies; lpfc_ncmd->flags = 0; - lpfc_ncmd->hdwq = idx; + lpfc_ncmd->hdwq = qp; + lpfc_ncmd->hdwq_no = idx; /* Rsp SGE will be filled in when we rcv an IO * from the NVME Layer to be sent. @@ -2026,7 +2027,7 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) lpfc_ncmd->ndlp = NULL; lpfc_ncmd->flags &= ~LPFC_BUMP_QDEPTH; - qp = &phba->sli4_hba.hdwq[lpfc_ncmd->hdwq]; + qp = lpfc_ncmd->hdwq; if (lpfc_ncmd->flags & LPFC_SBUF_XBUSY) { lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, "6310 XB release deferred for " diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 974fbec7dd04..13f900dd4eeb 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -79,7 +79,8 @@ struct lpfc_nvme_buf { dma_addr_t dma_phys_sgl; struct sli4_sge *dma_sgl; struct lpfc_iocbq cur_iocbq; - uint16_t hdwq; + struct lpfc_sli4_hdw_queue *hdwq; + uint16_t hdwq_no; uint16_t cpu; /* NVME specific fields */ diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index b5e287cacc2a..0d296aee2d82 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -845,7 +845,7 @@ lpfc_nvmet_xmt_ls_rsp(struct nvmet_fc_target_port *tgtport, lpfc_nvmeio_data(phba, "NVMET LS RESP: xri x%x wqidx x%x len x%x\n", ctxp->oxid, nvmewqeq->hba_wqidx, rsp->rsplen); - rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, nvmewqeq); + rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq); if (rc == WQE_SUCCESS) { /* * Okay to repost buffer here, but wait till cmpl @@ -901,6 +901,11 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, else ctxp->ts_nvme_data = ktime_get_ns(); } + + /* Setup the hdw queue if not already set */ + if (!ctxp->hdwq) + ctxp->hdwq = &phba->sli4_hba.hdwq[rsp->hwqid]; + if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { int id = smp_processor_id(); if (id < LPFC_CHECK_CPU_CNT) { @@ -946,7 +951,7 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, ctxp->oxid, rsp->op, rsp->rsplen); ctxp->flag |= LPFC_NVMET_IO_INP; - rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq); + rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq); if (rc == WQE_SUCCESS) { #ifdef CONFIG_SCSI_LPFC_DEBUG_FS if (!ctxp->ts_cmd_nvme) @@ -965,7 +970,7 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, * WQE release CQE */ ctxp->flag |= LPFC_NVMET_DEFER_WQFULL; - wq = phba->sli4_hba.hdwq[rsp->hwqid].nvme_wq; + wq = ctxp->hdwq->nvme_wq; pring = wq->pring; spin_lock_irqsave(&pring->ring_lock, iflags); list_add_tail(&nvmewqeq->list, &wq->wqfull_list); @@ -1015,6 +1020,9 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, if (phba->pport->load_flag & FC_UNLOADING) return; + if (!ctxp->hdwq) + ctxp->hdwq = &phba->sli4_hba.hdwq[0]; + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, "6103 NVMET Abort op: oxri x%x flg x%x ste %d\n", ctxp->oxid, ctxp->flag, ctxp->state); @@ -1039,7 +1047,7 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) { lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid); - wq = phba->sli4_hba.hdwq[ctxp->wqeq->hba_wqidx].nvme_wq; + wq = ctxp->hdwq->nvme_wq; spin_unlock_irqrestore(&ctxp->ctxlock, flags); lpfc_nvmet_wqfull_flush(phba, wq, ctxp); return; @@ -1649,6 +1657,7 @@ lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, #if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) struct lpfc_sli_ring *pring; struct lpfc_iocbq *nvmewqeq; + struct lpfc_nvmet_rcv_ctx *ctxp; unsigned long iflags; int rc; @@ -1662,7 +1671,8 @@ lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, list_remove_head(&wq->wqfull_list, nvmewqeq, struct lpfc_iocbq, list); spin_unlock_irqrestore(&pring->ring_lock, iflags); - rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq); + ctxp = (struct lpfc_nvmet_rcv_ctx *)nvmewqeq->context2; + rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, nvmewqeq); spin_lock_irqsave(&pring->ring_lock, iflags); if (rc == -EBUSY) { /* WQ was full again, so put it back on the list */ @@ -1765,6 +1775,7 @@ dropit: ctxp->state = LPFC_NVMET_STE_LS_RCV; ctxp->entry_cnt = 1; ctxp->rqb_buffer = (void *)nvmebuf; + ctxp->hdwq = &phba->sli4_hba.hdwq[0]; lpfc_nvmeio_data(phba, "NVMET LS RCV: xri x%x sz %d from %06x\n", oxid, size, sid); @@ -1987,6 +1998,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, ctxp->flag = 0; ctxp->ctxbuf = ctx_buf; ctxp->rqb_buffer = (void *)nvmebuf; + ctxp->hdwq = NULL; spin_lock_init(&ctxp->ctxlock); #ifdef CONFIG_SCSI_LPFC_DEBUG_FS @@ -3044,7 +3056,10 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, abts_wqeq->iocb_flag |= LPFC_IO_NVME; abts_wqeq->context2 = ctxp; abts_wqeq->vport = phba->pport; - rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_wqeq); + if (!ctxp->hdwq) + ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx]; + + rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq); spin_unlock_irqrestore(&phba->hbalock, flags); if (rc == WQE_SUCCESS) { atomic_inc(&tgtp->xmt_abort_sol); @@ -3096,7 +3111,10 @@ lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba, abts_wqeq->wqe_cmpl = lpfc_nvmet_unsol_fcp_abort_cmp; abts_wqeq->iocb_cmpl = NULL; abts_wqeq->iocb_flag |= LPFC_IO_NVMET; - rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_wqeq); + if (!ctxp->hdwq) + ctxp->hdwq = &phba->sli4_hba.hdwq[abts_wqeq->hba_wqidx]; + + rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq); spin_unlock_irqrestore(&phba->hbalock, flags); if (rc == WQE_SUCCESS) { return 0; @@ -3165,7 +3183,7 @@ lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *phba, abts_wqeq->wqe_cmpl = lpfc_nvmet_xmt_ls_abort_cmp; abts_wqeq->iocb_cmpl = 0; abts_wqeq->iocb_flag |= LPFC_IO_NVME_LS; - rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, abts_wqeq); + rc = lpfc_sli4_issue_wqe(phba, ctxp->hdwq, abts_wqeq); spin_unlock_irqrestore(&phba->hbalock, flags); if (rc == WQE_SUCCESS) { atomic_inc(&tgtp->xmt_abort_unsol); diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index 1aaff63f1f41..9b767c59de3d 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -140,6 +140,7 @@ struct lpfc_nvmet_rcv_ctx { #define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */ struct rqb_dmabuf *rqb_buffer; struct lpfc_nvmet_ctxbuf *ctxbuf; + struct lpfc_sli4_hdw_queue *hdwq; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS uint64_t ts_isr_cmd; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 08f284ca5f1e..55c58bbfee08 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -748,7 +748,8 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) #ifdef CONFIG_SCSI_LPFC_DEBUG_FS lpfc_cmd->prot_data_type = 0; #endif - lpfc_cmd->hdwq = idx; + lpfc_cmd->hdwq = qp; + lpfc_cmd->hdwq_no = idx; lpfc_cmd->fcp_cmnd = (lpfc_cmd->data + sgl_size); lpfc_cmd->fcp_rsp = (struct fcp_rsp *)((uint8_t *)lpfc_cmd->fcp_cmnd + @@ -861,7 +862,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) psb->seg_cnt = 0; psb->prot_seg_cnt = 0; - qp = &phba->sli4_hba.hdwq[psb->hdwq]; + qp = psb->hdwq; if (psb->exch_busy) { spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag); psb->pCmd = NULL; @@ -4018,7 +4019,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, sli4 = (phba->sli_rev == LPFC_SLI_REV4); piocbq->iocb.un.fcpi.fcpi_XRdy = 0; - idx = lpfc_cmd->hdwq; + idx = lpfc_cmd->hdwq_no; if (phba->sli4_hba.hdwq) hdwq = &phba->sli4_hba.hdwq[idx]; @@ -4557,7 +4558,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) return 0; out_host_busy_free_buf: - idx = lpfc_cmd->hdwq; + idx = lpfc_cmd->hdwq_no; lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); if (phba->sli4_hba.hdwq) { switch (lpfc_cmd->fcp_cmnd->fcpCntl3) { diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 68e6c4ad3936..58cb6fd575c3 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -138,7 +138,8 @@ struct lpfc_scsi_buf { dma_addr_t dma_phys_sgl; struct ulp_bde64 *dma_sgl; struct lpfc_iocbq cur_iocbq; - uint16_t hdwq; + struct lpfc_sli4_hdw_queue *hdwq; + uint16_t hdwq_no; uint16_t cpu; /* SCSI specific fields */ diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 7847ce2a9409..90f039a41480 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -10005,7 +10005,7 @@ lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) */ if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { lpfc_cmd = (struct lpfc_scsi_buf *)piocb->context1; - piocb->hba_wqidx = lpfc_cmd->hdwq; + piocb->hba_wqidx = lpfc_cmd->hdwq_no; } return phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq->pring; } else { @@ -11301,6 +11301,7 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *abtsiocbp; union lpfc_wqe128 *abts_wqe; int retval; + int idx = cmdiocb->hba_wqidx; /* * There are certain command types we don't want to abort. And we @@ -11356,7 +11357,8 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, abtsiocbp->iocb_flag |= LPFC_IO_NVME; abtsiocbp->vport = vport; abtsiocbp->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl; - retval = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abtsiocbp); + retval = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[idx], + abtsiocbp); if (retval) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, "6147 Failed abts issue_wqe with status x%x " @@ -19617,7 +19619,7 @@ lpfc_wqe_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeq, * @pwqe: Pointer to command WQE. **/ int -lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, +lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, struct lpfc_iocbq *pwqe) { union lpfc_wqe128 *wqe = &pwqe->wqe; @@ -19659,12 +19661,12 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, /* NVME_FCREQ and NVME_ABTS requests */ if (pwqe->iocb_flag & LPFC_IO_NVME) { /* Get the IO distribution (hba_wqidx) for WQ assignment. */ - pring = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq->pring; + wq = qp->nvme_wq; + pring = wq->pring; + + bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->nvme_cq_map); spin_lock_irqsave(&pring->ring_lock, iflags); - wq = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq; - bf_set(wqe_cqid, &wqe->generic.wqe_com, - phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_cq->queue_id); ret = lpfc_sli4_wq_put(wq, wqe); if (ret) { spin_unlock_irqrestore(&pring->ring_lock, iflags); @@ -19678,9 +19680,9 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, /* NVMET requests */ if (pwqe->iocb_flag & LPFC_IO_NVMET) { /* Get the IO distribution (hba_wqidx) for WQ assignment. */ - pring = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq->pring; + wq = qp->nvme_wq; + pring = wq->pring; - spin_lock_irqsave(&pring->ring_lock, iflags); ctxp = pwqe->context2; sglq = ctxp->ctxbuf->sglq; if (pwqe->sli4_xritag == NO_XRI) { @@ -19689,9 +19691,9 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, } bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com, pwqe->sli4_xritag); - wq = phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_wq; - bf_set(wqe_cqid, &wqe->generic.wqe_com, - phba->sli4_hba.hdwq[pwqe->hba_wqidx].nvme_cq->queue_id); + bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->nvme_cq_map); + + spin_lock_irqsave(&pring->ring_lock, iflags); ret = lpfc_sli4_wq_put(wq, wqe); if (ret) { spin_unlock_irqrestore(&pring->ring_lock, iflags); From ace44e48b1fc36055a8ff05a778110e726387032 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:27 -0800 Subject: [PATCH 1212/4277] scsi: lpfc: Synchronize hardware queues with SCSI MQ interface Now that the lower half has much better per-cpu parallelization using the hardware queues, the SCSI MQ support needs to be tied into it. The involves the following mods: - Use the hardware queue info from the midlayer to help select the hardware queue to utilize. This required change to the get_scsi-buf_xxx routines. - Remove lpfc_sli4_scmd_to_wqidx_distr() routine. No longer needed. - Includes fix for SLI-3 that does not have multi queue parallelization. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 3 +- drivers/scsi/lpfc/lpfc_init.c | 8 +++- drivers/scsi/lpfc/lpfc_scsi.c | 72 +++++++++-------------------------- drivers/scsi/lpfc/lpfc_scsi.h | 2 - 4 files changed, 27 insertions(+), 58 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 9262c52e32d6..755bf49c272c 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -619,7 +619,8 @@ struct lpfc_ras_fwlog { struct lpfc_hba { /* SCSI interface function jump table entries */ struct lpfc_scsi_buf * (*lpfc_get_scsi_buf) - (struct lpfc_hba *, struct lpfc_nodelist *); + (struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, + struct scsi_cmnd *cmnd); int (*lpfc_scsi_prep_dma_buf) (struct lpfc_hba *, struct lpfc_scsi_buf *); void (*lpfc_scsi_unprep_dma_buf) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 36d9c32c9c87..88b1c3ca26dc 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4063,12 +4063,16 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->max_lun = vport->cfg_max_luns; shost->this_id = -1; shost->max_cmd_len = 16; - shost->nr_hw_queues = phba->cfg_hdw_queue; if (phba->sli_rev == LPFC_SLI_REV4) { + shost->nr_hw_queues = phba->cfg_hdw_queue; shost->dma_boundary = phba->sli4_hba.pc_sli4_params.sge_supp_len-1; shost->sg_tablesize = phba->cfg_scsi_seg_cnt; - } + } else + /* SLI-3 has a limited number of hardware queues (3), + * thus there is only one for FCP processing. + */ + shost->nr_hw_queues = 1; /* * Set initial can_queue value since 0 is no longer supported and diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 55c58bbfee08..79a3765bdd9b 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -636,7 +636,8 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, * Pointer to lpfc_scsi_buf - Success **/ static struct lpfc_scsi_buf* -lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, + struct scsi_cmnd *cmnd) { struct lpfc_scsi_buf * lpfc_cmd = NULL; struct list_head *scsi_buf_list_get = &phba->lpfc_scsi_buf_list_get; @@ -674,7 +675,8 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) * Pointer to lpfc_scsi_buf - Success **/ static struct lpfc_scsi_buf* -lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, + struct scsi_cmnd *cmnd) { struct lpfc_scsi_buf *lpfc_cmd, *lpfc_cmd_next; struct lpfc_sli4_hdw_queue *qp; @@ -685,12 +687,18 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) dma_addr_t pdma_phys_fcp_cmd; uint32_t sgl_size, cpu, idx; int found = 0; + int tag; cpu = smp_processor_id(); - if (cpu < phba->cfg_hdw_queue) - idx = cpu; - else - idx = cpu % phba->cfg_hdw_queue; + if (cmnd) { + tag = blk_mq_unique_tag(cmnd->request); + idx = blk_mq_unique_tag_to_hwq(tag); + } else { + if (cpu < phba->cfg_hdw_queue) + idx = cpu; + else + idx = cpu % phba->cfg_hdw_queue; + } qp = &phba->sli4_hba.hdwq[idx]; spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); @@ -815,9 +823,10 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) * Pointer to lpfc_scsi_buf - Success **/ static struct lpfc_scsi_buf* -lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, + struct scsi_cmnd *cmnd) { - return phba->lpfc_get_scsi_buf(phba, ndlp); + return phba->lpfc_get_scsi_buf(phba, ndlp, cmnd); } /** @@ -3657,49 +3666,6 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb); } -/** - * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution - * @phba: Pointer to HBA context object. - * - * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index - * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock - * held. - * If scsi-mq is enabled, get the default block layer mapping of software queues - * to hardware queues. This information is saved in request tag. - * - * Return: index into SLI4 fast-path FCP queue index. - **/ -int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, - struct lpfc_scsi_buf *lpfc_cmd) -{ - struct scsi_cmnd *cmnd = lpfc_cmd->pCmd; - struct lpfc_vector_map_info *cpup; - int chann, cpu; - uint32_t tag; - uint16_t hwq; - - if (cmnd) { - tag = blk_mq_unique_tag(cmnd->request); - hwq = blk_mq_unique_tag_to_hwq(tag); - - return hwq; - } - - if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU && - phba->cfg_hdw_queue > 1) { - cpu = lpfc_cmd->cpu; - if (cpu < phba->sli4_hba.num_present_cpu) { - cpup = phba->sli4_hba.cpu_map; - cpup += cpu; - return cpup->channel_id; - } - } - chann = atomic_add_return(1, &phba->fcp_qidx); - chann = chann % phba->cfg_hdw_queue; - return chann; -} - - /** * lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine * @phba: The Hba for which this call is being executed. @@ -4474,7 +4440,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) } } - lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp); + lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp, cmnd); if (lpfc_cmd == NULL) { lpfc_rampdown_queue_depth(phba); @@ -4913,7 +4879,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct scsi_cmnd *cmnd, return FAILED; pnode = rdata->pnode; - lpfc_cmd = lpfc_get_scsi_buf(phba, pnode); + lpfc_cmd = lpfc_get_scsi_buf(phba, pnode, NULL); if (lpfc_cmd == NULL) return FAILED; lpfc_cmd->timeout = phba->cfg_task_mgmt_tmo; diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 58cb6fd575c3..696898087244 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -200,5 +200,3 @@ struct lpfc_scsi_buf { /* For sysfs/debugfs tmp string max len */ #define LPFC_MAX_SCSI_INFO_TMP_LEN 79 -int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, - struct lpfc_scsi_buf *lpfc_cmd); From c490850a094794e7515737a6939146966c826577 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:28 -0800 Subject: [PATCH 1213/4277] scsi: lpfc: Adapt partitioned XRI lists to efficient sharing The XRI get/put lists were partitioned per hardware queue. However, the adapter rarely had sufficient resources to give a large number of resources per queue. As such, it became common for a cpu to encounter a lack of XRI resource and request the upper io stack to retry after returning a BUSY condition. This occurred even though other cpus were idle and not using their resources. Create as efficient a scheme as possible to move resources to the cpus that need them. Each cpu maintains a small private pool which it allocates from for io. There is a watermark that the cpu attempts to keep in the private pool. The private pool, when empty, pulls from a global pool from the cpu. When the cpu's global pool is empty it will pull from other cpu's global pool. As there many cpu global pools (1 per cpu or hardware queue count) and as each cpu selects what cpu to pull from at different rates and at different times, it creates a radomizing effect that minimizes the number of cpu's that will contend with each other when the steal XRI's from another cpu's global pool. On io completion, a cpu will push the XRI back on to its private pool. A watermark level is maintained for the private pool such that when it is exceeded it will move XRI's to the CPU global pool so that other cpu's may allocate them. On NVME, as heartbeat commands are critical to get placed on the wire, a single expedite pool is maintained. When a heartbeat is to be sent, it will allocate an XRI from the expedite pool rather than the normal cpu private/global pools. On any io completion, if a reduction in the expedite pools is seen, it will be replenished before the XRI is placed on the cpu private pool. Statistics are added to aid understanding the XRI levels on each cpu and their behaviors. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 26 +- drivers/scsi/lpfc/lpfc_attr.c | 9 + drivers/scsi/lpfc/lpfc_crtn.h | 16 + drivers/scsi/lpfc/lpfc_debugfs.c | 262 +++++++++++ drivers/scsi/lpfc/lpfc_debugfs.h | 3 + drivers/scsi/lpfc/lpfc_init.c | 326 ++++++++++++-- drivers/scsi/lpfc/lpfc_nvme.c | 91 ++-- drivers/scsi/lpfc/lpfc_nvme.h | 45 +- drivers/scsi/lpfc/lpfc_scsi.c | 162 +++---- drivers/scsi/lpfc/lpfc_scsi.h | 53 --- drivers/scsi/lpfc/lpfc_sli.c | 720 +++++++++++++++++++++++++++++-- drivers/scsi/lpfc/lpfc_sli.h | 85 ++++ drivers/scsi/lpfc/lpfc_sli4.h | 56 +++ 13 files changed, 1527 insertions(+), 327 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 755bf49c272c..0f8964fdfecf 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -235,8 +235,6 @@ typedef struct lpfc_vpd { } sli3Feat; } lpfc_vpd_t; -struct lpfc_scsi_buf; - /* * lpfc stat counters @@ -597,6 +595,13 @@ struct lpfc_mbox_ext_buf_ctx { struct list_head ext_dmabuf_list; }; +struct lpfc_epd_pool { + /* Expedite pool */ + struct list_head list; + u32 count; + spinlock_t lock; /* lock for expedite pool */ +}; + struct lpfc_ras_fwlog { uint8_t *fwlog_buff; uint32_t fw_buffcount; /* Buffer size posted to FW */ @@ -618,19 +623,19 @@ struct lpfc_ras_fwlog { struct lpfc_hba { /* SCSI interface function jump table entries */ - struct lpfc_scsi_buf * (*lpfc_get_scsi_buf) + struct lpfc_io_buf * (*lpfc_get_scsi_buf) (struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, struct scsi_cmnd *cmnd); int (*lpfc_scsi_prep_dma_buf) - (struct lpfc_hba *, struct lpfc_scsi_buf *); + (struct lpfc_hba *, struct lpfc_io_buf *); void (*lpfc_scsi_unprep_dma_buf) - (struct lpfc_hba *, struct lpfc_scsi_buf *); + (struct lpfc_hba *, struct lpfc_io_buf *); void (*lpfc_release_scsi_buf) - (struct lpfc_hba *, struct lpfc_scsi_buf *); + (struct lpfc_hba *, struct lpfc_io_buf *); void (*lpfc_rampdown_queue_depth) (struct lpfc_hba *); void (*lpfc_scsi_prep_cmnd) - (struct lpfc_vport *, struct lpfc_scsi_buf *, + (struct lpfc_vport *, struct lpfc_io_buf *, struct lpfc_nodelist *); /* IOCB interface function jump table entries */ @@ -673,9 +678,12 @@ struct lpfc_hba { (struct lpfc_hba *); int (*lpfc_bg_scsi_prep_dma_buf) - (struct lpfc_hba *, struct lpfc_scsi_buf *); + (struct lpfc_hba *, struct lpfc_io_buf *); /* Add new entries here */ + /* expedite pool */ + struct lpfc_epd_pool epd_pool; + /* SLI4 specific HBA data structure */ struct lpfc_sli4_hba sli4_hba; @@ -789,6 +797,7 @@ struct lpfc_hba { /* HBA Config Parameters */ uint32_t cfg_ack0; + uint32_t cfg_xri_rebalancing; uint32_t cfg_enable_npiv; uint32_t cfg_enable_rrq; uint32_t cfg_topology; @@ -1014,6 +1023,7 @@ struct lpfc_hba { #ifdef CONFIG_SCSI_LPFC_DEBUG_FS struct dentry *hba_debugfs_root; atomic_t debugfs_vport_count; + struct dentry *debug_multixri_pools; struct dentry *debug_hbqinfo; struct dentry *debug_dumpHostSlim; struct dentry *debug_dumpHBASlim; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index e10d930fcb6a..47aa2af885a4 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -5266,6 +5266,12 @@ static DEVICE_ATTR_RW(lpfc_max_scsicmpl_time); */ LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support"); +/* +# lpfc_xri_rebalancing: enable or disable XRI rebalancing feature +# range is [0,1]. Default value is 1. +*/ +LPFC_ATTR_R(xri_rebalancing, 1, 0, 1, "Enable/Disable XRI rebalancing"); + /* * lpfc_io_sched: Determine scheduling algrithmn for issuing FCP cmds * range is [0,1]. Default value is 0. @@ -5723,6 +5729,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_use_adisc, &dev_attr_lpfc_first_burst_size, &dev_attr_lpfc_ack0, + &dev_attr_lpfc_xri_rebalancing, &dev_attr_lpfc_topology, &dev_attr_lpfc_scan_down, &dev_attr_lpfc_link_speed, @@ -6788,6 +6795,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_multi_ring_rctl_init(phba, lpfc_multi_ring_rctl); lpfc_multi_ring_type_init(phba, lpfc_multi_ring_type); lpfc_ack0_init(phba, lpfc_ack0); + lpfc_xri_rebalancing_init(phba, lpfc_xri_rebalancing); lpfc_topology_init(phba, lpfc_topology); lpfc_link_speed_init(phba, lpfc_link_speed); lpfc_poll_tmo_init(phba, lpfc_poll_tmo); @@ -6846,6 +6854,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) phba->nvmet_support = 0; phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP; phba->cfg_enable_bbcr = 0; + phba->cfg_xri_rebalancing = 0; } else { /* We MUST have FCP support */ if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 1bd1362f39a0..726cd6a7c452 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -521,6 +521,7 @@ int lpfc_sli4_io_sgl_update(struct lpfc_hba *phba); int lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba, struct list_head *blist, int xricnt); int lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc); +void lpfc_io_free(struct lpfc_hba *phba); void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *); uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *); int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t); @@ -573,6 +574,21 @@ void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba); void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_wcqe_complete *abts_cmpl); +void lpfc_create_multixri_pools(struct lpfc_hba *phba); +void lpfc_create_destroy_pools(struct lpfc_hba *phba); +void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid); +void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 cnt); +void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid); +void lpfc_keep_pvt_pool_above_lowwm(struct lpfc_hba *phba, u32 hwqid); +void lpfc_adjust_pvt_pool_count(struct lpfc_hba *phba, u32 hwqid); +#ifdef LPFC_MXP_STAT +void lpfc_snapshot_mxp(struct lpfc_hba *, u32); +#endif +struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba, + struct lpfc_nodelist *ndlp, u32 hwqid, + int); +void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *ncmd, + struct lpfc_sli4_hdw_queue *qp); void lpfc_nvme_cmd_template(void); void lpfc_nvmet_cmd_template(void); extern int lpfc_enable_nvmet_cnt; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 218642d7c488..2a2c46766eb6 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -378,6 +378,126 @@ skipit: return len; } +/** + * lpfc_debugfs_multixripools_data - Display multi-XRI pools information + * @phba: The HBA to gather host buffer info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine displays current multi-XRI pools information including XRI + * count in public, private and txcmplq. It also displays current high and + * low watermark. + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) +{ + u32 i; + u32 hwq_count; + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_pvt_pool *pvt_pool; + struct lpfc_pbl_pool *pbl_pool; + u32 txcmplq_cnt; + char tmp[LPFC_DEBUG_OUT_LINE_SZ] = {0}; + + /* + * Pbl: Current number of free XRIs in public pool + * Pvt: Current number of free XRIs in private pool + * Busy: Current number of outstanding XRIs + * HWM: Current high watermark + * pvt_empty: Incremented by 1 when IO submission fails (no xri) + * pbl_empty: Incremented by 1 when all pbl_pool are empty during + * IO submission + */ + scnprintf(tmp, sizeof(tmp), + "HWQ: Pbl Pvt Busy HWM | pvt_empty pbl_empty "); + if (strlcat(buf, tmp, size) >= size) + return strnlen(buf, size); + +#ifdef LPFC_MXP_STAT + /* + * MAXH: Max high watermark seen so far + * above_lmt: Incremented by 1 if xri_owned > xri_limit during + * IO submission + * below_lmt: Incremented by 1 if xri_owned <= xri_limit during + * IO submission + * locPbl_hit: Incremented by 1 if successfully get a batch of XRI from + * local pbl_pool + * othPbl_hit: Incremented by 1 if successfully get a batch of XRI from + * other pbl_pool + */ + scnprintf(tmp, sizeof(tmp), + "MAXH above_lmt below_lmt locPbl_hit othPbl_hit"); + if (strlcat(buf, tmp, size) >= size) + return strnlen(buf, size); + + /* + * sPbl: snapshot of Pbl 15 sec after stat gets cleared + * sPvt: snapshot of Pvt 15 sec after stat gets cleared + * sBusy: snapshot of Busy 15 sec after stat gets cleared + */ + scnprintf(tmp, sizeof(tmp), + " | sPbl sPvt sBusy"); + if (strlcat(buf, tmp, size) >= size) + return strnlen(buf, size); +#endif + + scnprintf(tmp, sizeof(tmp), "\n"); + if (strlcat(buf, tmp, size) >= size) + return strnlen(buf, size); + + hwq_count = phba->cfg_hdw_queue; + for (i = 0; i < hwq_count; i++) { + qp = &phba->sli4_hba.hdwq[i]; + multixri_pool = qp->p_multixri_pool; + if (!multixri_pool) + continue; + pbl_pool = &multixri_pool->pbl_pool; + pvt_pool = &multixri_pool->pvt_pool; + txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt; + if (qp->nvme_wq) + txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt; + + scnprintf(tmp, sizeof(tmp), + "%03d: %4d %4d %4d %4d | %10d %10d ", + i, pbl_pool->count, pvt_pool->count, + txcmplq_cnt, pvt_pool->high_watermark, + qp->empty_io_bufs, multixri_pool->pbl_empty_count); + if (strlcat(buf, tmp, size) >= size) + break; + +#ifdef LPFC_MXP_STAT + scnprintf(tmp, sizeof(tmp), + "%4d %10d %10d %10d %10d", + multixri_pool->stat_max_hwm, + multixri_pool->above_limit_count, + multixri_pool->below_limit_count, + multixri_pool->local_pbl_hit_count, + multixri_pool->other_pbl_hit_count); + if (strlcat(buf, tmp, size) >= size) + break; + + scnprintf(tmp, sizeof(tmp), + " | %4d %4d %5d", + multixri_pool->stat_pbl_count, + multixri_pool->stat_pvt_count, + multixri_pool->stat_busy_count); + if (strlcat(buf, tmp, size) >= size) + break; +#endif + + scnprintf(tmp, sizeof(tmp), "\n"); + if (strlcat(buf, tmp, size) >= size) + break; + } + return strnlen(buf, size); +} + static int lpfc_debugfs_last_hdwq; /** @@ -1751,6 +1871,53 @@ out: return rc; } +/** + * lpfc_debugfs_multixripools_open - Open the multixripool debugfs buffer + * @inode: The inode pointer that contains a hba pointer. + * @file: The file pointer to attach the log output. + * + * Description: + * This routine is the entry point for the debugfs open file operation. It gets + * the hba from the i_private field in @inode, allocates the necessary buffer + * for the log, fills the buffer from the in-memory log for this hba, and then + * returns a pointer to that log in the private_data field in @file. + * + * Returns: + * This function returns zero if successful. On error it will return a negative + * error value. + **/ +static int +lpfc_debugfs_multixripools_open(struct inode *inode, struct file *file) +{ + struct lpfc_hba *phba = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kzalloc(LPFC_DUMP_MULTIXRIPOOL_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + if (phba->cfg_xri_rebalancing) + debug->len = lpfc_debugfs_multixripools_data( + phba, debug->buffer, LPFC_DUMP_MULTIXRIPOOL_SIZE); + else + debug->len = 0; + + debug->i_private = inode->i_private; + file->private_data = debug; + + rc = 0; +out: + return rc; +} + /** * lpfc_debugfs_hdwqinfo_open - Open the hdwqinfo debugfs buffer * @inode: The inode pointer that contains a vport pointer. @@ -2182,6 +2349,75 @@ lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file) return 0; } +/** + * lpfc_debugfs_multixripools_write - Clear multi-XRI pools statistics + * @file: The file pointer to read from. + * @buf: The buffer to copy the user data from. + * @nbytes: The number of bytes to get. + * @ppos: The position in the file to start reading from. + * + * Description: + * This routine clears multi-XRI pools statistics when buf contains "clear". + * + * Return Value: + * It returns the @nbytges passing in from debugfs user space when successful. + * In case of error conditions, it returns proper error code back to the user + * space. + **/ +static ssize_t +lpfc_debugfs_multixripools_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + char mybuf[64]; + char *pbuf; + u32 i; + u32 hwq_count; + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_multixri_pool *multixri_pool; + + if (nbytes > 64) + nbytes = 64; + + /* Protect copy from user */ + if (!access_ok(buf, nbytes)) + return -EFAULT; + + memset(mybuf, 0, sizeof(mybuf)); + + if (copy_from_user(mybuf, buf, nbytes)) + return -EFAULT; + pbuf = &mybuf[0]; + + if ((strncmp(pbuf, "clear", strlen("clear"))) == 0) { + hwq_count = phba->cfg_hdw_queue; + for (i = 0; i < hwq_count; i++) { + qp = &phba->sli4_hba.hdwq[i]; + multixri_pool = qp->p_multixri_pool; + if (!multixri_pool) + continue; + + qp->empty_io_bufs = 0; + multixri_pool->pbl_empty_count = 0; +#ifdef LPFC_MXP_STAT + multixri_pool->above_limit_count = 0; + multixri_pool->below_limit_count = 0; + multixri_pool->stat_max_hwm = 0; + multixri_pool->local_pbl_hit_count = 0; + multixri_pool->other_pbl_hit_count = 0; + + multixri_pool->stat_pbl_count = 0; + multixri_pool->stat_pvt_count = 0; + multixri_pool->stat_busy_count = 0; + multixri_pool->stat_snapshot_taken = 0; +#endif + } + return strlen(pbuf); + } + + return -EINVAL; +} static int lpfc_debugfs_nvmestat_open(struct inode *inode, struct file *file) @@ -5044,6 +5280,16 @@ static const struct file_operations lpfc_debugfs_op_nodelist = { .release = lpfc_debugfs_release, }; +#undef lpfc_debugfs_op_multixripools +static const struct file_operations lpfc_debugfs_op_multixripools = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_multixripools_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_multixripools_write, + .release = lpfc_debugfs_release, +}; + #undef lpfc_debugfs_op_hbqinfo static const struct file_operations lpfc_debugfs_op_hbqinfo = { .owner = THIS_MODULE, @@ -5490,6 +5736,19 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) atomic_inc(&lpfc_debugfs_hba_count); atomic_set(&phba->debugfs_vport_count, 0); + /* Multi-XRI pools */ + snprintf(name, sizeof(name), "multixripools"); + phba->debug_multixri_pools = + debugfs_create_file(name, S_IFREG | 0644, + phba->hba_debugfs_root, + phba, + &lpfc_debugfs_op_multixripools); + if (!phba->debug_multixri_pools) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0527 Cannot create debugfs multixripools\n"); + goto debug_failed; + } + /* Setup hbqinfo */ snprintf(name, sizeof(name), "hbqinfo"); phba->debug_hbqinfo = @@ -5906,6 +6165,9 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) if (atomic_read(&phba->debugfs_vport_count) == 0) { + debugfs_remove(phba->debug_multixri_pools); /* multixripools*/ + phba->debug_multixri_pools = NULL; + debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */ phba->debug_hbqinfo = NULL; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 2360ec8fb848..cf256a6dca42 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -287,6 +287,9 @@ struct lpfc_idiag { #endif +/* multixripool output buffer size */ +#define LPFC_DUMP_MULTIXRIPOOL_SIZE 8192 + /* hdwqinfo output buffer size */ #define LPFC_HDWQINFO_SIZE 8192 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 88b1c3ca26dc..10e3ad5419f0 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1037,7 +1037,7 @@ lpfc_hba_down_post_s3(struct lpfc_hba *phba) static int lpfc_hba_down_post_s4(struct lpfc_hba *phba) { - struct lpfc_scsi_buf *psb, *psb_next; + struct lpfc_io_buf *psb, *psb_next; struct lpfc_nvmet_rcv_ctx *ctxp, *ctxp_next; struct lpfc_sli4_hdw_queue *qp; LIST_HEAD(aborts); @@ -1249,6 +1249,33 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq) return; } +/** + * lpfc_hb_mxp_handler - Multi-XRI pools handler to adjust XRI distribution + * @phba: pointer to lpfc hba data structure. + * + * For each heartbeat, this routine does some heuristic methods to adjust + * XRI distribution. The goal is to fully utilize free XRIs. + **/ +static void lpfc_hb_mxp_handler(struct lpfc_hba *phba) +{ + u32 i; + u32 hwq_count; + + hwq_count = phba->cfg_hdw_queue; + for (i = 0; i < hwq_count; i++) { + /* Adjust XRIs in private pool */ + lpfc_adjust_pvt_pool_count(phba, i); + + /* Adjust high watermark */ + lpfc_adjust_high_watermark(phba, i); + +#ifdef LPFC_MXP_STAT + /* Snapshot pbl, pvt and busy count */ + lpfc_snapshot_mxp(phba, i); +#endif + } +} + /** * lpfc_hb_timeout_handler - The HBA-timer timeout handler * @phba: pointer to lpfc hba data structure. @@ -1285,6 +1312,11 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) struct lpfc_fc4_ctrl_stat *cstat; void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr; + if (phba->cfg_xri_rebalancing) { + /* Multi-XRI pools handler */ + lpfc_hb_mxp_handler(phba); + } + vports = lpfc_create_vport_work_array(phba); if (vports != NULL) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { @@ -3078,6 +3110,242 @@ lpfc_sli4_node_prep(struct lpfc_hba *phba) lpfc_destroy_vport_work_array(phba, vports); } +/** + * lpfc_create_expedite_pool - create expedite pool + * @phba: pointer to lpfc hba data structure. + * + * This routine moves a batch of XRIs from lpfc_io_buf_list_put of HWQ 0 + * to expedite pool. Mark them as expedite. + **/ +void lpfc_create_expedite_pool(struct lpfc_hba *phba) +{ + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + struct lpfc_epd_pool *epd_pool; + unsigned long iflag; + + epd_pool = &phba->epd_pool; + qp = &phba->sli4_hba.hdwq[0]; + + spin_lock_init(&epd_pool->lock); + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + spin_lock(&epd_pool->lock); + INIT_LIST_HEAD(&epd_pool->list); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &qp->lpfc_io_buf_list_put, list) { + list_move_tail(&lpfc_ncmd->list, &epd_pool->list); + lpfc_ncmd->expedite = true; + qp->put_io_bufs--; + epd_pool->count++; + if (epd_pool->count >= XRI_BATCH) + break; + } + spin_unlock(&epd_pool->lock); + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); +} + +/** + * lpfc_destroy_expedite_pool - destroy expedite pool + * @phba: pointer to lpfc hba data structure. + * + * This routine returns XRIs from expedite pool to lpfc_io_buf_list_put + * of HWQ 0. Clear the mark. + **/ +void lpfc_destroy_expedite_pool(struct lpfc_hba *phba) +{ + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + struct lpfc_epd_pool *epd_pool; + unsigned long iflag; + + epd_pool = &phba->epd_pool; + qp = &phba->sli4_hba.hdwq[0]; + + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + spin_lock(&epd_pool->lock); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &epd_pool->list, list) { + list_move_tail(&lpfc_ncmd->list, + &qp->lpfc_io_buf_list_put); + lpfc_ncmd->flags = false; + qp->put_io_bufs++; + epd_pool->count--; + } + spin_unlock(&epd_pool->lock); + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); +} + +/** + * lpfc_create_multixri_pools - create multi-XRI pools + * @phba: pointer to lpfc hba data structure. + * + * This routine initialize public, private per HWQ. Then, move XRIs from + * lpfc_io_buf_list_put to public pool. High and low watermark are also + * Initialized. + **/ +void lpfc_create_multixri_pools(struct lpfc_hba *phba) +{ + u32 i, j; + u32 hwq_count; + u32 count_per_hwq; + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + unsigned long iflag; + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_pbl_pool *pbl_pool; + struct lpfc_pvt_pool *pvt_pool; + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "1234 num_hdw_queue=%d num_present_cpu=%d common_xri_cnt=%d\n", + phba->cfg_hdw_queue, phba->sli4_hba.num_present_cpu, + phba->sli4_hba.io_xri_cnt); + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) + lpfc_create_expedite_pool(phba); + + hwq_count = phba->cfg_hdw_queue; + count_per_hwq = phba->sli4_hba.io_xri_cnt / hwq_count; + + for (i = 0; i < hwq_count; i++) { + multixri_pool = kzalloc(sizeof(*multixri_pool), GFP_KERNEL); + + if (!multixri_pool) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "1238 Failed to allocate memory for " + "multixri_pool\n"); + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) + lpfc_destroy_expedite_pool(phba); + + j = 0; + while (j < i) { + qp = &phba->sli4_hba.hdwq[j]; + kfree(qp->p_multixri_pool); + j++; + } + phba->cfg_xri_rebalancing = 0; + return; + } + + qp = &phba->sli4_hba.hdwq[i]; + qp->p_multixri_pool = multixri_pool; + + multixri_pool->xri_limit = count_per_hwq; + multixri_pool->rrb_next_hwqid = i; + + /* Deal with public free xri pool */ + pbl_pool = &multixri_pool->pbl_pool; + spin_lock_init(&pbl_pool->lock); + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + spin_lock(&pbl_pool->lock); + INIT_LIST_HEAD(&pbl_pool->list); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &qp->lpfc_io_buf_list_put, list) { + list_move_tail(&lpfc_ncmd->list, &pbl_pool->list); + qp->put_io_bufs--; + pbl_pool->count++; + } + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "1235 Moved %d buffers from PUT list over to pbl_pool[%d]\n", + pbl_pool->count, i); + spin_unlock(&pbl_pool->lock); + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); + + /* Deal with private free xri pool */ + pvt_pool = &multixri_pool->pvt_pool; + pvt_pool->high_watermark = multixri_pool->xri_limit / 2; + pvt_pool->low_watermark = XRI_BATCH; + spin_lock_init(&pvt_pool->lock); + spin_lock_irqsave(&pvt_pool->lock, iflag); + INIT_LIST_HEAD(&pvt_pool->list); + pvt_pool->count = 0; + spin_unlock_irqrestore(&pvt_pool->lock, iflag); + } +} + +/** + * lpfc_destroy_multixri_pools - destroy multi-XRI pools + * @phba: pointer to lpfc hba data structure. + * + * This routine returns XRIs from public/private to lpfc_io_buf_list_put. + **/ +void lpfc_destroy_multixri_pools(struct lpfc_hba *phba) +{ + u32 i; + u32 hwq_count; + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + unsigned long iflag; + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_pbl_pool *pbl_pool; + struct lpfc_pvt_pool *pvt_pool; + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) + lpfc_destroy_expedite_pool(phba); + + hwq_count = phba->cfg_hdw_queue; + + for (i = 0; i < hwq_count; i++) { + qp = &phba->sli4_hba.hdwq[i]; + multixri_pool = qp->p_multixri_pool; + if (!multixri_pool) + continue; + + qp->p_multixri_pool = NULL; + + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + + /* Deal with public free xri pool */ + pbl_pool = &multixri_pool->pbl_pool; + spin_lock(&pbl_pool->lock); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "1236 Moving %d buffers from pbl_pool[%d] TO PUT list\n", + pbl_pool->count, i); + + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &pbl_pool->list, list) { + list_move_tail(&lpfc_ncmd->list, + &qp->lpfc_io_buf_list_put); + qp->put_io_bufs++; + pbl_pool->count--; + } + + INIT_LIST_HEAD(&pbl_pool->list); + pbl_pool->count = 0; + + spin_unlock(&pbl_pool->lock); + + /* Deal with private free xri pool */ + pvt_pool = &multixri_pool->pvt_pool; + spin_lock(&pvt_pool->lock); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "1237 Moving %d buffers from pvt_pool[%d] TO PUT list\n", + pvt_pool->count, i); + + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &pvt_pool->list, list) { + list_move_tail(&lpfc_ncmd->list, + &qp->lpfc_io_buf_list_put); + qp->put_io_bufs++; + pvt_pool->count--; + } + + INIT_LIST_HEAD(&pvt_pool->list); + pvt_pool->count = 0; + + spin_unlock(&pvt_pool->lock); + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); + + kfree(multixri_pool); + } +} + /** * lpfc_online - Initialize and bring a HBA online * @phba: pointer to lpfc hba data structure. @@ -3160,6 +3428,9 @@ lpfc_online(struct lpfc_hba *phba) } lpfc_destroy_vport_work_array(phba, vports); + if (phba->cfg_xri_rebalancing) + lpfc_create_multixri_pools(phba); + lpfc_unblock_mgmt_io(phba); return 0; } @@ -3318,6 +3589,9 @@ lpfc_offline(struct lpfc_hba *phba) spin_unlock_irq(shost->host_lock); } lpfc_destroy_vport_work_array(phba, vports); + + if (phba->cfg_xri_rebalancing) + lpfc_destroy_multixri_pools(phba); } /** @@ -3331,7 +3605,7 @@ lpfc_offline(struct lpfc_hba *phba) static void lpfc_scsi_free(struct lpfc_hba *phba) { - struct lpfc_scsi_buf *sb, *sb_next; + struct lpfc_io_buf *sb, *sb_next; if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) return; @@ -3372,10 +3646,10 @@ lpfc_scsi_free(struct lpfc_hba *phba) * list back to kernel. It is called from lpfc_pci_remove_one to free * the internal resources before the device is removed from the system. **/ -static void +void lpfc_io_free(struct lpfc_hba *phba) { - struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + struct lpfc_io_buf *lpfc_ncmd, *lpfc_ncmd_next; struct lpfc_sli4_hdw_queue *qp; int idx; @@ -3660,8 +3934,8 @@ lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *cbuf) { LIST_HEAD(blist); struct lpfc_sli4_hdw_queue *qp; - struct lpfc_scsi_buf *lpfc_cmd; - struct lpfc_scsi_buf *iobufp, *prev_iobufp; + struct lpfc_io_buf *lpfc_cmd; + struct lpfc_io_buf *iobufp, *prev_iobufp; int idx, cnt, xri, inserted; cnt = 0; @@ -3689,7 +3963,7 @@ lpfc_io_buf_flush(struct lpfc_hba *phba, struct list_head *cbuf) * to post to the firmware. */ for (idx = 0; idx < cnt; idx++) { - list_remove_head(&blist, lpfc_cmd, struct lpfc_scsi_buf, list); + list_remove_head(&blist, lpfc_cmd, struct lpfc_io_buf, list); if (!lpfc_cmd) return cnt; if (idx == 0) { @@ -3721,7 +3995,7 @@ int lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf) { struct lpfc_sli4_hdw_queue *qp; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; int idx, cnt; qp = phba->sli4_hba.hdwq; @@ -3729,7 +4003,7 @@ lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf) while (!list_empty(cbuf)) { for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { list_remove_head(cbuf, lpfc_cmd, - struct lpfc_scsi_buf, list); + struct lpfc_io_buf, list); if (!lpfc_cmd) return cnt; cnt++; @@ -3764,7 +4038,7 @@ lpfc_io_buf_replenish(struct lpfc_hba *phba, struct list_head *cbuf) int lpfc_sli4_io_sgl_update(struct lpfc_hba *phba) { - struct lpfc_nvme_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL; + struct lpfc_io_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL; uint16_t i, lxri, els_xri_cnt; uint16_t io_xri_cnt, io_xri_max; LIST_HEAD(io_sgl_list); @@ -3794,7 +4068,7 @@ lpfc_sli4_io_sgl_update(struct lpfc_hba *phba) /* release the extra allocated nvme buffers */ for (i = 0; i < io_xri_cnt; i++) { list_remove_head(&io_sgl_list, lpfc_ncmd, - struct lpfc_nvme_buf, list); + struct lpfc_io_buf, list); if (lpfc_ncmd) { dma_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, @@ -3847,7 +4121,7 @@ out_free_mem: int lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc) { - struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd; struct lpfc_iocbq *pwqeq; uint16_t iotag, lxri = 0; int bcnt, num_posted; @@ -3856,12 +4130,11 @@ lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc) LIST_HEAD(nvme_nblist); /* Sanity check to ensure our sizing is right for both SCSI and NVME */ - if ((sizeof(struct lpfc_scsi_buf) > LPFC_COMMON_IO_BUF_SZ) || - (sizeof(struct lpfc_nvme_buf) > LPFC_COMMON_IO_BUF_SZ)) { + if (sizeof(struct lpfc_io_buf) > LPFC_COMMON_IO_BUF_SZ) { lpfc_printf_log(phba, KERN_ERR, LOG_FCP, - "6426 Common buffer size mismatch: %ld %ld\n", - sizeof(struct lpfc_scsi_buf), - sizeof(struct lpfc_nvme_buf)); + "6426 Common buffer size %ld exceeds %d\n", + sizeof(struct lpfc_io_buf), + LPFC_COMMON_IO_BUF_SZ); return 0; } @@ -6434,6 +6707,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) " NVME_TARGET_FC infrastructure" " is not in kernel\n"); #endif + /* Not supported for NVMET */ + phba->cfg_xri_rebalancing = 0; break; } } @@ -11756,7 +12031,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) struct lpfc_hba *phba; struct lpfc_vport *vport = NULL; struct Scsi_Host *shost = NULL; - int error, len; + int error; uint32_t cfg_mode, intr_mode; /* Allocate memory for HBA structure */ @@ -11879,17 +12154,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) error); } } - /* Don't post more new bufs if repost already recovered - * the nvme sgls. - */ - if (phba->sli4_hba.io_xri_cnt == 0) { - len = lpfc_new_io_buf( - phba, phba->sli4_hba.io_xri_max); - if (len == 0) { - error = -ENOMEM; - goto out_free_sysfs_attr; - } - } } /* check for firmware upgrade or downgrade */ @@ -11972,6 +12236,10 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) lpfc_nvmet_destroy_targetport(phba); lpfc_nvme_destroy_localport(vport); + /* De-allocate multi-XRI pools */ + if (phba->cfg_xri_rebalancing) + lpfc_destroy_multixri_pools(phba); + /* * Bring down the SLI Layer. This step disables all interrupts, * clears the rings, discards all mailbox commands, and resets diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index f1f697cd7e97..0c6c91d39e2f 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -56,12 +56,12 @@ /* NVME initiator-based functions */ -static struct lpfc_nvme_buf * +static struct lpfc_io_buf * lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int idx, int expedite); static void -lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_nvme_buf *); +lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_io_buf *); static struct nvme_fc_port_template lpfc_nvme_template; @@ -760,7 +760,7 @@ lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport, /* Fix up the existing sgls for NVME IO. */ static inline void lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport, - struct lpfc_nvme_buf *lpfc_ncmd, + struct lpfc_io_buf *lpfc_ncmd, struct nvmefc_fcp_req *nCmd) { struct lpfc_hba *phba = vport->phba; @@ -857,7 +857,7 @@ lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport, #ifdef CONFIG_SCSI_LPFC_DEBUG_FS static void lpfc_nvme_ktime(struct lpfc_hba *phba, - struct lpfc_nvme_buf *lpfc_ncmd) + struct lpfc_io_buf *lpfc_ncmd) { uint64_t seg1, seg2, seg3, seg4; uint64_t segsum; @@ -955,8 +955,8 @@ static void lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct lpfc_wcqe_complete *wcqe) { - struct lpfc_nvme_buf *lpfc_ncmd = - (struct lpfc_nvme_buf *)pwqeIn->context1; + struct lpfc_io_buf *lpfc_ncmd = + (struct lpfc_io_buf *)pwqeIn->context1; struct lpfc_vport *vport = pwqeIn->vport; struct nvmefc_fcp_req *nCmd; struct nvme_fc_ersp_iu *ep; @@ -1181,7 +1181,7 @@ out_err: **/ static int lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, - struct lpfc_nvme_buf *lpfc_ncmd, + struct lpfc_io_buf *lpfc_ncmd, struct lpfc_nodelist *pnode, struct lpfc_fc4_ctrl_stat *cstat) { @@ -1287,7 +1287,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, **/ static int lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, - struct lpfc_nvme_buf *lpfc_ncmd) + struct lpfc_io_buf *lpfc_ncmd) { struct lpfc_hba *phba = vport->phba; struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; @@ -1428,7 +1428,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, struct lpfc_vport *vport; struct lpfc_hba *phba; struct lpfc_nodelist *ndlp; - struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd; struct lpfc_nvme_rport *rport; struct lpfc_nvme_qhandle *lpfc_queue_info; struct lpfc_nvme_fcpreq_priv *freqpriv; @@ -1616,6 +1616,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, goto out_free_nvme_buf; } + if (phba->cfg_xri_rebalancing) + lpfc_keep_pvt_pool_above_lowwm(phba, lpfc_ncmd->hdwq_no); + #ifdef CONFIG_SCSI_LPFC_DEBUG_FS if (lpfc_ncmd->ts_cmd_start) lpfc_ncmd->ts_cmd_wqput = ktime_get_ns(); @@ -1704,7 +1707,7 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, struct lpfc_nvme_lport *lport; struct lpfc_vport *vport; struct lpfc_hba *phba; - struct lpfc_nvme_buf *lpfc_nbuf; + struct lpfc_io_buf *lpfc_nbuf; struct lpfc_iocbq *abts_buf; struct lpfc_iocbq *nvmereq_wqe; struct lpfc_nvme_fcpreq_priv *freqpriv; @@ -1911,22 +1914,6 @@ static struct nvme_fc_port_template lpfc_nvme_template = { .fcprqst_priv_sz = sizeof(struct lpfc_nvme_fcpreq_priv), }; -static inline struct lpfc_nvme_buf * -lpfc_nvme_buf(struct lpfc_hba *phba, int idx) -{ - struct lpfc_sli4_hdw_queue *qp; - struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; - - qp = &phba->sli4_hba.hdwq[idx]; - list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, - &qp->lpfc_io_buf_list_get, list) { - list_del_init(&lpfc_ncmd->list); - qp->get_io_bufs--; - return lpfc_ncmd; - } - return NULL; -} - /** * lpfc_get_nvme_buf - Get a nvme buffer from io_buf_list of the HBA * @phba: The HBA for which this call is being executed. @@ -1938,34 +1925,17 @@ lpfc_nvme_buf(struct lpfc_hba *phba, int idx) * NULL - Error * Pointer to lpfc_nvme_buf - Success **/ -static struct lpfc_nvme_buf * +static struct lpfc_io_buf * lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int idx, int expedite) { - struct lpfc_nvme_buf *lpfc_ncmd = NULL; + struct lpfc_io_buf *lpfc_ncmd; struct lpfc_sli4_hdw_queue *qp; struct sli4_sge *sgl; struct lpfc_iocbq *pwqeq; union lpfc_wqe128 *wqe; - unsigned long iflag = 0; - qp = &phba->sli4_hba.hdwq[idx]; - spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); - if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) - lpfc_ncmd = lpfc_nvme_buf(phba, idx); - if (!lpfc_ncmd) { - spin_lock(&qp->io_buf_list_put_lock); - list_splice(&qp->lpfc_io_buf_list_put, - &qp->lpfc_io_buf_list_get); - qp->get_io_bufs += qp->put_io_bufs; - INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); - qp->put_io_bufs = 0; - spin_unlock(&qp->io_buf_list_put_lock); - if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || - expedite) - lpfc_ncmd = lpfc_nvme_buf(phba, idx); - } - spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag); + lpfc_ncmd = lpfc_get_io_buf(phba, NULL, idx, expedite); if (lpfc_ncmd) { pwqeq = &(lpfc_ncmd->cur_iocbq); @@ -1978,8 +1948,6 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl; lpfc_ncmd->start_time = jiffies; lpfc_ncmd->flags = 0; - lpfc_ncmd->hdwq = qp; - lpfc_ncmd->hdwq_no = idx; /* Rsp SGE will be filled in when we rcv an IO * from the NVME Layer to be sent. @@ -1996,11 +1964,13 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, if (lpfc_ndlp_check_qdepth(phba, ndlp)) { atomic_inc(&ndlp->cmd_pending); - lpfc_ncmd->flags |= LPFC_BUMP_QDEPTH; + lpfc_ncmd->flags |= LPFC_SBUF_BUMP_QDEPTH; } - } else + } else { + qp = &phba->sli4_hba.hdwq[idx]; qp->empty_io_bufs++; + } return lpfc_ncmd; } @@ -2016,16 +1986,16 @@ lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * aborted. **/ static void -lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) +lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd) { struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; - if ((lpfc_ncmd->flags & LPFC_BUMP_QDEPTH) && lpfc_ncmd->ndlp) + if ((lpfc_ncmd->flags & LPFC_SBUF_BUMP_QDEPTH) && lpfc_ncmd->ndlp) atomic_dec(&lpfc_ncmd->ndlp->cmd_pending); lpfc_ncmd->ndlp = NULL; - lpfc_ncmd->flags &= ~LPFC_BUMP_QDEPTH; + lpfc_ncmd->flags &= ~LPFC_SBUF_BUMP_QDEPTH; qp = lpfc_ncmd->hdwq; if (lpfc_ncmd->flags & LPFC_SBUF_XBUSY) { @@ -2040,17 +2010,8 @@ lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) &qp->lpfc_abts_nvme_buf_list); qp->abts_nvme_io_bufs++; spin_unlock_irqrestore(&qp->abts_nvme_buf_list_lock, iflag); - } else { - /* MUST zero fields if buffer is reused by another protocol */ - lpfc_ncmd->nvmeCmd = NULL; - lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL; - - spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); - list_add_tail(&lpfc_ncmd->list, - &qp->lpfc_io_buf_list_put); - qp->put_io_bufs++; - spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); - } + } else + lpfc_release_io_buf(phba, (struct lpfc_io_buf *)lpfc_ncmd, qp); } /** @@ -2510,7 +2471,7 @@ lpfc_sli4_nvme_xri_aborted(struct lpfc_hba *phba, struct sli4_wcqe_xri_aborted *axri, int idx) { uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri); - struct lpfc_nvme_buf *lpfc_ncmd, *next_lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd, *next_lpfc_ncmd; struct nvmefc_fcp_req *nvme_cmd = NULL; struct lpfc_nodelist *ndlp; struct lpfc_sli4_hdw_queue *qp; diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 13f900dd4eeb..07d5fa16dd39 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -71,49 +71,6 @@ struct lpfc_nvme_rport { struct completion rport_unreg_done; }; -struct lpfc_nvme_buf { - /* Common fields */ - struct list_head list; - void *data; - dma_addr_t dma_handle; - dma_addr_t dma_phys_sgl; - struct sli4_sge *dma_sgl; - struct lpfc_iocbq cur_iocbq; - struct lpfc_sli4_hdw_queue *hdwq; - uint16_t hdwq_no; - uint16_t cpu; - - /* NVME specific fields */ - struct nvmefc_fcp_req *nvmeCmd; - struct lpfc_nodelist *ndlp; - - uint32_t timeout; - - uint16_t flags; /* TBD convert exch_busy to flags */ -#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ -#define LPFC_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ - uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ - uint16_t status; /* From IOCB Word 7- ulpStatus */ - uint32_t result; /* From IOCB Word 4. */ - - uint32_t seg_cnt; /* Number of scatter-gather segments returned by - * dma_map_sg. The driver needs this for calls - * to dma_unmap_sg. - */ - wait_queue_head_t *waitq; - unsigned long start_time; - - uint16_t qidx; - -#ifdef CONFIG_SCSI_LPFC_DEBUG_FS - uint64_t ts_cmd_start; - uint64_t ts_last_cmd; - uint64_t ts_cmd_wqput; - uint64_t ts_isr_cmpl; - uint64_t ts_data_nvme; -#endif -}; - struct lpfc_nvme_fcpreq_priv { - struct lpfc_nvme_buf *nvme_buf; + struct lpfc_io_buf *nvme_buf; }; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 79a3765bdd9b..c824ed3be4f9 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -83,9 +83,9 @@ lpfc_rport_data_from_scsi_device(struct scsi_device *sdev) } static void -lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); +lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *psb); static void -lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); +lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *psb); static int lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc); @@ -180,7 +180,7 @@ lpfc_cmd_guard_csum(struct scsi_cmnd *sc) **/ static void lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba, - struct lpfc_scsi_buf *lpfc_cmd) + struct lpfc_io_buf *lpfc_cmd) { struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; if (sgl) { @@ -200,7 +200,7 @@ lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba, * function updates the statistical data for the command completion. **/ static void -lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) { struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; @@ -389,7 +389,7 @@ static int lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) { struct lpfc_hba *phba = vport->phba; - struct lpfc_scsi_buf *psb; + struct lpfc_io_buf *psb; struct ulp_bde64 *bpl; IOCB_t *iocb; dma_addr_t pdma_phys_fcp_cmd; @@ -408,7 +408,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) (int)sizeof(struct fcp_rsp), bpl_size); for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { - psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL); + psb = kzalloc(sizeof(struct lpfc_io_buf), GFP_KERNEL); if (!psb) break; @@ -442,7 +442,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) sizeof(struct fcp_rsp); /* Initialize local short-hand pointers. */ - bpl = psb->dma_sgl; + bpl = (struct ulp_bde64 *)psb->dma_sgl; pdma_phys_fcp_cmd = psb->dma_handle; pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd); pdma_phys_sgl = psb->dma_handle + sizeof(struct fcp_cmnd) + @@ -524,7 +524,7 @@ void lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; - struct lpfc_scsi_buf *psb, *next_psb; + struct lpfc_io_buf *psb, *next_psb; struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; int idx; @@ -562,7 +562,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, { uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri); uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri); - struct lpfc_scsi_buf *psb, *next_psb; + struct lpfc_io_buf *psb, *next_psb; struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; struct lpfc_iocbq *iocbq; @@ -613,7 +613,7 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, continue; if (iocbq->sli4_xritag != xri) continue; - psb = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq); + psb = container_of(iocbq, struct lpfc_io_buf, cur_iocbq); psb->exch_busy = 0; spin_unlock_irqrestore(&phba->hbalock, iflag); if (!list_empty(&pring->txq)) @@ -635,16 +635,16 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, * NULL - Error * Pointer to lpfc_scsi_buf - Success **/ -static struct lpfc_scsi_buf* +static struct lpfc_io_buf * lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, struct scsi_cmnd *cmnd) { - struct lpfc_scsi_buf * lpfc_cmd = NULL; + struct lpfc_io_buf *lpfc_cmd = NULL; struct list_head *scsi_buf_list_get = &phba->lpfc_scsi_buf_list_get; unsigned long iflag = 0; spin_lock_irqsave(&phba->scsi_buf_list_get_lock, iflag); - list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_scsi_buf, + list_remove_head(scsi_buf_list_get, lpfc_cmd, struct lpfc_io_buf, list); if (!lpfc_cmd) { spin_lock(&phba->scsi_buf_list_put_lock); @@ -652,7 +652,7 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, &phba->lpfc_scsi_buf_list_get); INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); list_remove_head(scsi_buf_list_get, lpfc_cmd, - struct lpfc_scsi_buf, list); + struct lpfc_io_buf, list); spin_unlock(&phba->scsi_buf_list_put_lock); } spin_unlock_irqrestore(&phba->scsi_buf_list_get_lock, iflag); @@ -674,19 +674,17 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * NULL - Error * Pointer to lpfc_scsi_buf - Success **/ -static struct lpfc_scsi_buf* +static struct lpfc_io_buf * lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, struct scsi_cmnd *cmnd) { - struct lpfc_scsi_buf *lpfc_cmd, *lpfc_cmd_next; + struct lpfc_io_buf *lpfc_cmd; struct lpfc_sli4_hdw_queue *qp; - unsigned long iflag = 0; struct sli4_sge *sgl; IOCB_t *iocb; dma_addr_t pdma_phys_fcp_rsp; dma_addr_t pdma_phys_fcp_cmd; uint32_t sgl_size, cpu, idx; - int found = 0; int tag; cpu = smp_processor_id(); @@ -700,40 +698,10 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, idx = cpu % phba->cfg_hdw_queue; } - qp = &phba->sli4_hba.hdwq[idx]; - spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); - list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, - &qp->lpfc_io_buf_list_get, list) { - if (lpfc_test_rrq_active(phba, ndlp, - lpfc_cmd->cur_iocbq.sli4_lxritag)) - continue; - list_del_init(&lpfc_cmd->list); - qp->get_io_bufs--; - found = 1; - break; - } - if (!found) { - spin_lock(&qp->io_buf_list_put_lock); - list_splice(&qp->lpfc_io_buf_list_put, - &qp->lpfc_io_buf_list_get); - qp->get_io_bufs += qp->put_io_bufs; - INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); - qp->put_io_bufs = 0; - spin_unlock(&qp->io_buf_list_put_lock); - list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, - &qp->lpfc_io_buf_list_get, - list) { - if (lpfc_test_rrq_active( - phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) - continue; - list_del_init(&lpfc_cmd->list); - qp->get_io_bufs--; - found = 1; - break; - } - } - spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag); - if (!found) { + lpfc_cmd = lpfc_get_io_buf(phba, ndlp, idx, + !phba->cfg_xri_rebalancing); + if (!lpfc_cmd) { + qp = &phba->sli4_hba.hdwq[idx]; qp->empty_io_bufs++; return NULL; } @@ -756,9 +724,6 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, #ifdef CONFIG_SCSI_LPFC_DEBUG_FS lpfc_cmd->prot_data_type = 0; #endif - lpfc_cmd->hdwq = qp; - lpfc_cmd->hdwq_no = idx; - lpfc_cmd->fcp_cmnd = (lpfc_cmd->data + sgl_size); lpfc_cmd->fcp_rsp = (struct fcp_rsp *)((uint8_t *)lpfc_cmd->fcp_cmnd + sizeof(struct fcp_cmnd)); @@ -789,7 +754,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, /* * Since the IOCB for the FCP I/O is built into this - * lpfc_scsi_buf, initialize it with all known data now. + * lpfc_io_buf, initialize it with all known data now. */ iocb = &lpfc_cmd->cur_iocbq.iocb; iocb->un.fcpi64.bdl.ulpIoTag32 = 0; @@ -822,7 +787,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * NULL - Error * Pointer to lpfc_scsi_buf - Success **/ -static struct lpfc_scsi_buf* +static struct lpfc_io_buf* lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, struct scsi_cmnd *cmnd) { @@ -838,7 +803,7 @@ lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * lpfc_scsi_buf_list list. **/ static void -lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) +lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *psb) { unsigned long iflag = 0; @@ -863,7 +828,7 @@ lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) * aborted. **/ static void -lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) +lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *psb) { struct lpfc_sli4_hdw_queue *qp; unsigned long iflag = 0; @@ -879,14 +844,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) qp->abts_scsi_io_bufs++; spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag); } else { - /* MUST zero fields if buffer is reused by another protocol */ - psb->pCmd = NULL; - psb->cur_iocbq.iocb_cmpl = NULL; - - spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); - list_add_tail(&psb->list, &qp->lpfc_io_buf_list_put); - qp->put_io_bufs++; - spin_unlock_irqrestore(&qp->io_buf_list_put_lock, iflag); + lpfc_release_io_buf(phba, (struct lpfc_io_buf *)psb, qp); } } @@ -899,7 +857,7 @@ lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) * lpfc_scsi_buf_list list. **/ static void -lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) +lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_io_buf *psb) { if ((psb->flags & LPFC_SBUF_BUMP_QDEPTH) && psb->ndlp) atomic_dec(&psb->ndlp->cmd_pending); @@ -923,12 +881,12 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) * 0 - Success **/ static int -lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct scatterlist *sgel = NULL; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; - struct ulp_bde64 *bpl = lpfc_cmd->dma_sgl; + struct ulp_bde64 *bpl = (struct ulp_bde64 *)lpfc_cmd->dma_sgl; struct lpfc_iocbq *iocbq = &lpfc_cmd->cur_iocbq; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; struct ulp_bde64 *data_bde = iocb_cmd->unsli3.fcp_ext.dbde; @@ -1075,7 +1033,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint32_t *reftag, uint16_t *apptag, uint32_t new_guard) { struct scatterlist *sgpe; /* s/g prot entry */ - struct lpfc_scsi_buf *lpfc_cmd = NULL; + struct lpfc_io_buf *lpfc_cmd = NULL; struct scsi_dif_tuple *src = NULL; struct lpfc_nodelist *ndlp; struct lpfc_rport_data *rdata; @@ -1134,7 +1092,7 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, if (sgpe) { src = (struct scsi_dif_tuple *)sg_virt(sgpe); src += blockoff; - lpfc_cmd = (struct lpfc_scsi_buf *)sc->host_scribble; + lpfc_cmd = (struct lpfc_io_buf *)sc->host_scribble; } /* Should we change the Reference Tag */ @@ -2439,7 +2397,7 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc) **/ static int lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba, - struct lpfc_scsi_buf *lpfc_cmd) + struct lpfc_io_buf *lpfc_cmd) { struct scsi_cmnd *sc = lpfc_cmd->pCmd; int fcpdl; @@ -2479,11 +2437,11 @@ lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba, **/ static int lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, - struct lpfc_scsi_buf *lpfc_cmd) + struct lpfc_io_buf *lpfc_cmd) { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; - struct ulp_bde64 *bpl = lpfc_cmd->dma_sgl; + struct ulp_bde64 *bpl = (struct ulp_bde64 *)lpfc_cmd->dma_sgl; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; uint32_t num_bde = 0; int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction; @@ -2659,7 +2617,7 @@ lpfc_bg_csum(uint8_t *data, int count) * what type of T10-DIF error occurred. */ static void -lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) { struct scatterlist *sgpe; /* s/g prot entry */ struct scatterlist *sgde; /* s/g data entry */ @@ -2844,8 +2802,8 @@ out: * -1 - Internal error (bad profile, ...etc) */ static int -lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, - struct lpfc_iocbq *pIocbOut) +lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd, + struct lpfc_iocbq *pIocbOut) { struct scsi_cmnd *cmd = lpfc_cmd->pCmd; struct sli3_bg_fields *bgf = &pIocbOut->iocb.unsli3.sli3_bg; @@ -3011,7 +2969,7 @@ out: * 0 - Success **/ static int -lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct scatterlist *sgel = NULL; @@ -3143,6 +3101,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) lpfc_cmd->cur_iocbq.priority = ((struct lpfc_device_data *) scsi_cmnd->device->hostdata)->priority; } + return 0; } @@ -3157,7 +3116,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) **/ static int lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, - struct lpfc_scsi_buf *lpfc_cmd) + struct lpfc_io_buf *lpfc_cmd) { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; @@ -3333,7 +3292,7 @@ err: * 0 - Success **/ static inline int -lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) { return phba->lpfc_scsi_prep_dma_buf(phba, lpfc_cmd); } @@ -3352,7 +3311,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) * 0 - Success **/ static inline int -lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) { return phba->lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd); } @@ -3369,7 +3328,7 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) **/ static void lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport, - struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) { + struct lpfc_io_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) { struct scsi_cmnd *cmnd = lpfc_cmd->pCmd; struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp; uint32_t resp_info = fcprsp->rspStatus2; @@ -3461,7 +3420,7 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport, * field of @lpfc_cmd for device with SLI-3 interface spec. **/ static void -lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) +lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_io_buf *psb) { /* * There are only two special cases to consider. (1) the scsi command @@ -3480,7 +3439,7 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) /** * lpfc_handler_fcp_err - FCP response handler * @vport: The virtual port for which this call is being executed. - * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure. + * @lpfc_cmd: Pointer to lpfc_io_buf data structure. * @rsp_iocb: The response IOCB which contains FCP error. * * This routine is called to process response IOCB with status field @@ -3488,7 +3447,7 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) * based upon SCSI and FCP error. **/ static void -lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, +lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd, struct lpfc_iocbq *rsp_iocb) { struct lpfc_hba *phba = vport->phba; @@ -3680,8 +3639,8 @@ static void lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct lpfc_iocbq *pIocbOut) { - struct lpfc_scsi_buf *lpfc_cmd = - (struct lpfc_scsi_buf *) pIocbIn->context1; + struct lpfc_io_buf *lpfc_cmd = + (struct lpfc_io_buf *) pIocbIn->context1; struct lpfc_vport *vport = pIocbIn->vport; struct lpfc_rport_data *rdata = lpfc_cmd->rdata; struct lpfc_nodelist *pnode = rdata->pnode; @@ -3949,7 +3908,7 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd) * to transfer for device with SLI3 interface spec. **/ static void -lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, +lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd, struct lpfc_nodelist *pnode) { struct lpfc_hba *phba = vport->phba; @@ -4052,7 +4011,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, /** * lpfc_scsi_prep_task_mgmt_cmd - Convert SLI3 scsi TM cmd to FCP info unit * @vport: The virtual port for which this call is being executed. - * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure. + * @lpfc_cmd: Pointer to lpfc_io_buf data structure. * @lun: Logical unit number. * @task_mgmt_cmd: SCSI task management command. * @@ -4065,7 +4024,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, **/ static int lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport, - struct lpfc_scsi_buf *lpfc_cmd, + struct lpfc_io_buf *lpfc_cmd, uint64_t lun, uint8_t task_mgmt_cmd) { @@ -4174,8 +4133,8 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, struct lpfc_iocbq *rspiocbq) { - struct lpfc_scsi_buf *lpfc_cmd = - (struct lpfc_scsi_buf *) cmdiocbq->context1; + struct lpfc_io_buf *lpfc_cmd = + (struct lpfc_io_buf *) cmdiocbq->context1; if (lpfc_cmd) lpfc_release_scsi_buf(phba, lpfc_cmd); return; @@ -4374,7 +4333,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) struct lpfc_hba *phba = vport->phba; struct lpfc_rport_data *rdata; struct lpfc_nodelist *ndlp; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); int err, idx; @@ -4521,6 +4480,9 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) lpfc_poll_rearm_timer(phba); } + if (phba->cfg_xri_rebalancing) + lpfc_keep_pvt_pool_above_lowwm(phba, lpfc_cmd->hdwq_no); + return 0; out_host_busy_free_buf: @@ -4569,7 +4531,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) struct lpfc_hba *phba = vport->phba; struct lpfc_iocbq *iocb; struct lpfc_iocbq *abtsiocb; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; IOCB_t *cmd, *icmd; int ret = SUCCESS, status = 0; struct lpfc_sli_ring *pring_s4 = NULL; @@ -4591,7 +4553,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) return FAILED; } - lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble; + lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble; if (!lpfc_cmd || !lpfc_cmd->pCmd) { spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, @@ -4621,7 +4583,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) return FAILED; } /* - * If pCmd field of the corresponding lpfc_scsi_buf structure + * If pCmd field of the corresponding lpfc_io_buf structure * points to a different SCSI command, then the driver has * already completed this command, but the midlayer did not * see the completion before the eh fired. Just return SUCCESS. @@ -4782,7 +4744,7 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd) /** * lpfc_check_fcp_rsp - check the returned fcp_rsp to see if task failed * @vport: The virtual port for which this call is being executed. - * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure. + * @lpfc_cmd: Pointer to lpfc_io_buf data structure. * * This routine checks the FCP RSP INFO to see if the tsk mgmt command succeded * @@ -4791,7 +4753,7 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd) * 0x2002 - Success **/ static int -lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd) +lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_io_buf *lpfc_cmd) { struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp; uint32_t rsp_info; @@ -4866,7 +4828,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct scsi_cmnd *cmnd, uint8_t task_mgmt_cmd) { struct lpfc_hba *phba = vport->phba; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct lpfc_iocbq *iocbq; struct lpfc_iocbq *iocbqrsp; struct lpfc_rport_data *rdata; diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 696898087244..9ee0ea4a1ef2 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -130,59 +130,6 @@ struct lpfc_scsicmd_bkt { uint32_t cmd_count; }; -struct lpfc_scsi_buf { - /* Common fields */ - struct list_head list; - void *data; - dma_addr_t dma_handle; - dma_addr_t dma_phys_sgl; - struct ulp_bde64 *dma_sgl; - struct lpfc_iocbq cur_iocbq; - struct lpfc_sli4_hdw_queue *hdwq; - uint16_t hdwq_no; - uint16_t cpu; - - /* SCSI specific fields */ - struct scsi_cmnd *pCmd; - struct lpfc_rport_data *rdata; - struct lpfc_nodelist *ndlp; - - uint32_t timeout; - - uint16_t flags; /* TBD convert exch_busy to flags */ -#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ -#define LPFC_SBUF_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ - uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ - uint16_t status; /* From IOCB Word 7- ulpStatus */ - uint32_t result; /* From IOCB Word 4. */ - - uint32_t seg_cnt; /* Number of scatter-gather segments returned by - * dma_map_sg. The driver needs this for calls - * to dma_unmap_sg. */ - uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */ - - /* - * data and dma_handle are the kernel virtual and bus address of the - * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter - * gather bde list that supports the sg_tablesize value. - */ - struct fcp_cmnd *fcp_cmnd; - struct fcp_rsp *fcp_rsp; - - wait_queue_head_t *waitq; - unsigned long start_time; - -#ifdef CONFIG_SCSI_LPFC_DEBUG_FS - /* Used to restore any changes to protection data for error injection */ - void *prot_data_segment; - uint32_t prot_data; - uint32_t prot_data_type; -#define LPFC_INJERR_REFTAG 1 -#define LPFC_INJERR_APPTAG 2 -#define LPFC_INJERR_GUARD 3 -#endif -}; - #define LPFC_SCSI_DMA_EXT_SIZE 264 #define LPFC_BPL_SIZE 1024 #define MDAC_DIRECT_CMD 0x22 diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 90f039a41480..4443d0d43053 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1133,14 +1133,14 @@ __lpfc_sli_get_els_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) struct list_head *lpfc_els_sgl_list = &phba->sli4_hba.lpfc_els_sgl_list; struct lpfc_sglq *sglq = NULL; struct lpfc_sglq *start_sglq = NULL; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct lpfc_nodelist *ndlp; int found = 0; lockdep_assert_held(&phba->hbalock); if (piocbq->iocb_flag & LPFC_IO_FCP) { - lpfc_cmd = (struct lpfc_scsi_buf *) piocbq->context1; + lpfc_cmd = (struct lpfc_io_buf *) piocbq->context1; ndlp = lpfc_cmd->rdata->pnode; } else if ((piocbq->iocb.ulpCommand == CMD_GEN_REQUEST64_CR) && !(piocbq->iocb_flag & LPFC_IO_LIBDFC)) { @@ -1596,6 +1596,7 @@ lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, list_add_tail(&piocb->list, &pring->txcmplq); piocb->iocb_flag |= LPFC_IO_ON_TXCMPLQ; + pring->txcmplq_cnt++; if ((unlikely(pring->ringno == LPFC_ELS_RING)) && (piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) && @@ -3008,6 +3009,7 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba, /* remove from txcmpl queue list */ list_del_init(&cmd_iocb->list); cmd_iocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; + pring->txcmplq_cnt--; return cmd_iocb; } } @@ -3045,6 +3047,7 @@ lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba, /* remove from txcmpl queue list */ list_del_init(&cmd_iocb->list); cmd_iocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ; + pring->txcmplq_cnt--; return cmd_iocb; } } @@ -7170,7 +7173,7 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq, int lpfc_sli4_hba_setup(struct lpfc_hba *phba) { - int rc, i, cnt; + int rc, i, cnt, len; LPFC_MBOXQ_t *mboxq; struct lpfc_mqe *mqe; uint8_t *vpd; @@ -7648,6 +7651,25 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) lpfc_sli_read_link_ste(phba); } + /* Don't post more new bufs if repost already recovered + * the nvme sgls. + */ + if (phba->nvmet_support == 0) { + if (phba->sli4_hba.io_xri_cnt == 0) { + len = lpfc_new_io_buf( + phba, phba->sli4_hba.io_xri_max); + if (len == 0) { + rc = -ENOMEM; + goto out_unset_queue; + } + + if (phba->cfg_xri_rebalancing) + lpfc_create_multixri_pools(phba); + } + } else { + phba->cfg_xri_rebalancing = 0; + } + /* Arm the CQs and then EQs on device */ lpfc_sli4_arm_cqeq_intr(phba); @@ -7727,18 +7749,21 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_SLI, "3104 Adapter failed to issue " "DOWN_LINK mbox cmd, rc:x%x\n", rc); - goto out_unset_queue; + goto out_io_buff_free; } } else if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK) { /* don't perform init_link on SLI4 FC port loopback test */ if (!(phba->link_flag & LS_LOOPBACK_MODE)) { rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT); if (rc) - goto out_unset_queue; + goto out_io_buff_free; } } mempool_free(mboxq, phba->mbox_mem_pool); return rc; +out_io_buff_free: + /* Free allocated IO Buffers */ + lpfc_io_free(phba); out_unset_queue: /* Unset all the queues set up in this routine when error out */ lpfc_sli4_queue_unset(phba); @@ -9472,7 +9497,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 0); if (phba->fcp_embed_io) { - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct sli4_sge *sgl; struct fcp_cmnd *fcp_cmnd; uint32_t *ptr; @@ -9536,7 +9561,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 0); if (phba->fcp_embed_io) { - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct sli4_sge *sgl; struct fcp_cmnd *fcp_cmnd; uint32_t *ptr; @@ -9593,7 +9618,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, /* Note, word 10 is already initialized to 0 */ if (phba->fcp_embed_io) { - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct sli4_sge *sgl; struct fcp_cmnd *fcp_cmnd; uint32_t *ptr; @@ -9994,7 +10019,7 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) struct lpfc_sli_ring * lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) { - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { if (unlikely(!phba->sli4_hba.hdwq)) @@ -10004,7 +10029,7 @@ lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) * be setup based on what work queue we used. */ if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - lpfc_cmd = (struct lpfc_scsi_buf *)piocb->context1; + lpfc_cmd = (struct lpfc_io_buf *)piocb->context1; piocb->hba_wqidx = lpfc_cmd->hdwq_no; } return phba->sli4_hba.hdwq[piocb->hba_wqidx].fcp_wq->pring; @@ -10494,6 +10519,7 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) pring = phba->sli4_hba.hdwq[i].fcp_wq->pring; pring->flag = 0; pring->ringno = LPFC_FCP_RING; + pring->txcmplq_cnt = 0; INIT_LIST_HEAD(&pring->txq); INIT_LIST_HEAD(&pring->txcmplq); INIT_LIST_HEAD(&pring->iocb_continueq); @@ -10502,6 +10528,7 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) pring = phba->sli4_hba.els_wq->pring; pring->flag = 0; pring->ringno = LPFC_ELS_RING; + pring->txcmplq_cnt = 0; INIT_LIST_HEAD(&pring->txq); INIT_LIST_HEAD(&pring->txcmplq); INIT_LIST_HEAD(&pring->iocb_continueq); @@ -10510,8 +10537,9 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { for (i = 0; i < phba->cfg_hdw_queue; i++) { pring = phba->sli4_hba.hdwq[i].nvme_wq->pring; - pring->flag = 0; + pring->flag = 0; pring->ringno = LPFC_FCP_RING; + pring->txcmplq_cnt = 0; INIT_LIST_HEAD(&pring->txq); INIT_LIST_HEAD(&pring->txcmplq); INIT_LIST_HEAD(&pring->iocb_continueq); @@ -10520,6 +10548,7 @@ lpfc_sli4_queue_init(struct lpfc_hba *phba) pring = phba->sli4_hba.nvmels_wq->pring; pring->flag = 0; pring->ringno = LPFC_ELS_RING; + pring->txcmplq_cnt = 0; INIT_LIST_HEAD(&pring->txq); INIT_LIST_HEAD(&pring->txcmplq); INIT_LIST_HEAD(&pring->iocb_continueq); @@ -11433,7 +11462,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport, uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd ctx_cmd) { - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; int rc = 1; if (iocbq->vport != vport) @@ -11443,7 +11472,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport, !(iocbq->iocb_flag & LPFC_IO_ON_TXCMPLQ)) return rc; - lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq); + lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq); if (lpfc_cmd->pCmd == NULL) return rc; @@ -11670,7 +11699,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd) { struct lpfc_hba *phba = vport->phba; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; struct lpfc_iocbq *abtsiocbq; struct lpfc_nodelist *ndlp; struct lpfc_iocbq *iocbq; @@ -11726,7 +11755,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, if (iocbq->iocb_flag & LPFC_IO_FOF) abtsiocbq->iocb_flag |= LPFC_IO_FOF; - lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq); + lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq); ndlp = lpfc_cmd->rdata->pnode; if (lpfc_is_link_up(phba) && @@ -11792,7 +11821,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba, { wait_queue_head_t *pdone_q; unsigned long iflags; - struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_io_buf *lpfc_cmd; spin_lock_irqsave(&phba->hbalock, iflags); if (cmdiocbq->iocb_flag & LPFC_IO_WAKE_TMO) { @@ -11821,7 +11850,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba, /* Set the exchange busy flag for task management commands */ if ((cmdiocbq->iocb_flag & LPFC_IO_FCP) && !(cmdiocbq->iocb_flag & LPFC_IO_LIBDFC)) { - lpfc_cmd = container_of(cmdiocbq, struct lpfc_scsi_buf, + lpfc_cmd = container_of(cmdiocbq, struct lpfc_io_buf, cur_iocbq); lpfc_cmd->exch_busy = rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY; } @@ -16627,7 +16656,7 @@ static int lpfc_sli4_post_io_sgl_block(struct lpfc_hba *phba, struct list_head *nblist, int count) { - struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd; struct lpfc_mbx_post_uembed_sgl_page1 *sgl; struct sgl_page_pairs *sgl_pg_pairs; void *viraddr; @@ -16743,7 +16772,7 @@ int lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba, struct list_head *post_nblist, int sb_count) { - struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + struct lpfc_io_buf *lpfc_ncmd, *lpfc_ncmd_next; int status, sgl_size; int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0; dma_addr_t pdma_phys_sgl1; @@ -16801,11 +16830,13 @@ lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba, phba, lpfc_ncmd->dma_phys_sgl, pdma_phys_sgl1, cur_xritag); if (status) { - /* failure, put on abort nvme list */ - lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; + /* Post error. Buffer unavailable. */ + lpfc_ncmd->flags |= + LPFC_SBUF_NOT_POSTED; } else { - /* success, put on NVME buffer list */ - lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; + /* Post success. Bffer available. */ + lpfc_ncmd->flags &= + ~LPFC_SBUF_NOT_POSTED; lpfc_ncmd->status = IOSTAT_SUCCESS; num_posted++; } @@ -16832,13 +16863,13 @@ lpfc_sli4_post_io_sgl_list(struct lpfc_hba *phba, /* put posted NVME buffer-sgl posted on NVME buffer sgl list */ while (!list_empty(&blck_nblist)) { list_remove_head(&blck_nblist, lpfc_ncmd, - struct lpfc_nvme_buf, list); + struct lpfc_io_buf, list); if (status) { - /* failure, put on abort nvme list */ - lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; + /* Post error. Mark buffer unavailable. */ + lpfc_ncmd->flags |= LPFC_SBUF_NOT_POSTED; } else { - /* success, put on NVME buffer list */ - lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; + /* Post success, Mark buffer available. */ + lpfc_ncmd->flags &= ~LPFC_SBUF_NOT_POSTED; lpfc_ncmd->status = IOSTAT_SUCCESS; num_posted++; } @@ -19705,3 +19736,636 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, } return WQE_ERROR; } + +#ifdef LPFC_MXP_STAT +/** + * lpfc_snapshot_mxp - Snapshot pbl, pvt and busy count + * @phba: pointer to lpfc hba data structure. + * @hwqid: belong to which HWQ. + * + * The purpose of this routine is to take a snapshot of pbl, pvt and busy count + * 15 seconds after a test case is running. + * + * The user should call lpfc_debugfs_multixripools_write before running a test + * case to clear stat_snapshot_taken. Then the user starts a test case. During + * test case is running, stat_snapshot_taken is incremented by 1 every time when + * this routine is called from heartbeat timer. When stat_snapshot_taken is + * equal to LPFC_MXP_SNAPSHOT_TAKEN, a snapshot is taken. + **/ +void lpfc_snapshot_mxp(struct lpfc_hba *phba, u32 hwqid) +{ + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_pvt_pool *pvt_pool; + struct lpfc_pbl_pool *pbl_pool; + u32 txcmplq_cnt; + + qp = &phba->sli4_hba.hdwq[hwqid]; + multixri_pool = qp->p_multixri_pool; + if (!multixri_pool) + return; + + if (multixri_pool->stat_snapshot_taken == LPFC_MXP_SNAPSHOT_TAKEN) { + pvt_pool = &qp->p_multixri_pool->pvt_pool; + pbl_pool = &qp->p_multixri_pool->pbl_pool; + txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt; + if (qp->nvme_wq) + txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt; + + multixri_pool->stat_pbl_count = pbl_pool->count; + multixri_pool->stat_pvt_count = pvt_pool->count; + multixri_pool->stat_busy_count = txcmplq_cnt; + } + + multixri_pool->stat_snapshot_taken++; +} +#endif + +/** + * lpfc_adjust_pvt_pool_count - Adjust private pool count + * @phba: pointer to lpfc hba data structure. + * @hwqid: belong to which HWQ. + * + * This routine moves some XRIs from private to public pool when private pool + * is not busy. + **/ +void lpfc_adjust_pvt_pool_count(struct lpfc_hba *phba, u32 hwqid) +{ + struct lpfc_multixri_pool *multixri_pool; + u32 io_req_count; + u32 prev_io_req_count; + + multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool; + if (!multixri_pool) + return; + io_req_count = multixri_pool->io_req_count; + prev_io_req_count = multixri_pool->prev_io_req_count; + + if (prev_io_req_count != io_req_count) { + /* Private pool is busy */ + multixri_pool->prev_io_req_count = io_req_count; + } else { + /* Private pool is not busy. + * Move XRIs from private to public pool. + */ + lpfc_move_xri_pvt_to_pbl(phba, hwqid); + } +} + +/** + * lpfc_adjust_high_watermark - Adjust high watermark + * @phba: pointer to lpfc hba data structure. + * @hwqid: belong to which HWQ. + * + * This routine sets high watermark as number of outstanding XRIs, + * but make sure the new value is between xri_limit/2 and xri_limit. + **/ +void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid) +{ + u32 new_watermark; + u32 watermark_max; + u32 watermark_min; + u32 xri_limit; + u32 txcmplq_cnt; + u32 abts_io_bufs; + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_sli4_hdw_queue *qp; + + qp = &phba->sli4_hba.hdwq[hwqid]; + multixri_pool = qp->p_multixri_pool; + if (!multixri_pool) + return; + xri_limit = multixri_pool->xri_limit; + + watermark_max = xri_limit; + watermark_min = xri_limit / 2; + + txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt; + abts_io_bufs = qp->abts_scsi_io_bufs; + if (qp->nvme_wq) { + txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt; + abts_io_bufs += qp->abts_nvme_io_bufs; + } + + new_watermark = txcmplq_cnt + abts_io_bufs; + new_watermark = min(watermark_max, new_watermark); + new_watermark = max(watermark_min, new_watermark); + multixri_pool->pvt_pool.high_watermark = new_watermark; + +#ifdef LPFC_MXP_STAT + multixri_pool->stat_max_hwm = max(multixri_pool->stat_max_hwm, + new_watermark); +#endif +} + +/** + * lpfc_move_xri_pvt_to_pbl - Move some XRIs from private to public pool + * @phba: pointer to lpfc hba data structure. + * @hwqid: belong to which HWQ. + * + * This routine is called from hearbeat timer when pvt_pool is idle. + * All free XRIs are moved from private to public pool on hwqid with 2 steps. + * The first step moves (all - low_watermark) amount of XRIs. + * The second step moves the rest of XRIs. + **/ +void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid) +{ + struct lpfc_pbl_pool *pbl_pool; + struct lpfc_pvt_pool *pvt_pool; + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + unsigned long iflag; + struct list_head tmp_list; + u32 tmp_count; + + pbl_pool = &phba->sli4_hba.hdwq[hwqid].p_multixri_pool->pbl_pool; + pvt_pool = &phba->sli4_hba.hdwq[hwqid].p_multixri_pool->pvt_pool; + tmp_count = 0; + + spin_lock_irqsave(&pbl_pool->lock, iflag); + spin_lock(&pvt_pool->lock); + + if (pvt_pool->count > pvt_pool->low_watermark) { + /* Step 1: move (all - low_watermark) from pvt_pool + * to pbl_pool + */ + + /* Move low watermark of bufs from pvt_pool to tmp_list */ + INIT_LIST_HEAD(&tmp_list); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &pvt_pool->list, list) { + list_move_tail(&lpfc_ncmd->list, &tmp_list); + tmp_count++; + if (tmp_count >= pvt_pool->low_watermark) + break; + } + + /* Move all bufs from pvt_pool to pbl_pool */ + list_splice_init(&pvt_pool->list, &pbl_pool->list); + + /* Move all bufs from tmp_list to pvt_pool */ + list_splice(&tmp_list, &pvt_pool->list); + + pbl_pool->count += (pvt_pool->count - tmp_count); + pvt_pool->count = tmp_count; + } else { + /* Step 2: move the rest from pvt_pool to pbl_pool */ + list_splice_init(&pvt_pool->list, &pbl_pool->list); + pbl_pool->count += pvt_pool->count; + pvt_pool->count = 0; + } + + spin_unlock(&pvt_pool->lock); + spin_unlock_irqrestore(&pbl_pool->lock, iflag); +} + +/** + * _lpfc_move_xri_pbl_to_pvt - Move some XRIs from public to private pool + * @phba: pointer to lpfc hba data structure + * @pbl_pool: specified public free XRI pool + * @pvt_pool: specified private free XRI pool + * @count: number of XRIs to move + * + * This routine tries to move some free common bufs from the specified pbl_pool + * to the specified pvt_pool. It might move less than count XRIs if there's not + * enough in public pool. + * + * Return: + * true - if XRIs are successfully moved from the specified pbl_pool to the + * specified pvt_pool + * false - if the specified pbl_pool is empty or locked by someone else + **/ +static bool +_lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, struct lpfc_pbl_pool *pbl_pool, + struct lpfc_pvt_pool *pvt_pool, u32 count) +{ + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + unsigned long iflag; + int ret; + + ret = spin_trylock_irqsave(&pbl_pool->lock, iflag); + if (ret) { + if (pbl_pool->count) { + /* Move a batch of XRIs from public to private pool */ + spin_lock(&pvt_pool->lock); + list_for_each_entry_safe(lpfc_ncmd, + lpfc_ncmd_next, + &pbl_pool->list, + list) { + list_move_tail(&lpfc_ncmd->list, + &pvt_pool->list); + pvt_pool->count++; + pbl_pool->count--; + count--; + if (count == 0) + break; + } + + spin_unlock(&pvt_pool->lock); + spin_unlock_irqrestore(&pbl_pool->lock, iflag); + return true; + } + spin_unlock_irqrestore(&pbl_pool->lock, iflag); + } + + return false; +} + +/** + * lpfc_move_xri_pbl_to_pvt - Move some XRIs from public to private pool + * @phba: pointer to lpfc hba data structure. + * @hwqid: belong to which HWQ. + * @count: number of XRIs to move + * + * This routine tries to find some free common bufs in one of public pools with + * Round Robin method. The search always starts from local hwqid, then the next + * HWQ which was found last time (rrb_next_hwqid). Once a public pool is found, + * a batch of free common bufs are moved to private pool on hwqid. + * It might move less than count XRIs if there's not enough in public pool. + **/ +void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 count) +{ + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_multixri_pool *next_multixri_pool; + struct lpfc_pvt_pool *pvt_pool; + struct lpfc_pbl_pool *pbl_pool; + u32 next_hwqid; + u32 hwq_count; + int ret; + + multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool; + pvt_pool = &multixri_pool->pvt_pool; + pbl_pool = &multixri_pool->pbl_pool; + + /* Check if local pbl_pool is available */ + ret = _lpfc_move_xri_pbl_to_pvt(phba, pbl_pool, pvt_pool, count); + if (ret) { +#ifdef LPFC_MXP_STAT + multixri_pool->local_pbl_hit_count++; +#endif + return; + } + + hwq_count = phba->cfg_hdw_queue; + + /* Get the next hwqid which was found last time */ + next_hwqid = multixri_pool->rrb_next_hwqid; + + do { + /* Go to next hwq */ + next_hwqid = (next_hwqid + 1) % hwq_count; + + next_multixri_pool = + phba->sli4_hba.hdwq[next_hwqid].p_multixri_pool; + pbl_pool = &next_multixri_pool->pbl_pool; + + /* Check if the public free xri pool is available */ + ret = _lpfc_move_xri_pbl_to_pvt( + phba, pbl_pool, pvt_pool, count); + + /* Exit while-loop if success or all hwqid are checked */ + } while (!ret && next_hwqid != multixri_pool->rrb_next_hwqid); + + /* Starting point for the next time */ + multixri_pool->rrb_next_hwqid = next_hwqid; + + if (!ret) { + /* stats: all public pools are empty*/ + multixri_pool->pbl_empty_count++; + } + +#ifdef LPFC_MXP_STAT + if (ret) { + if (next_hwqid == hwqid) + multixri_pool->local_pbl_hit_count++; + else + multixri_pool->other_pbl_hit_count++; + } +#endif +} + +/** + * lpfc_keep_pvt_pool_above_lowwm - Keep pvt_pool above low watermark + * @phba: pointer to lpfc hba data structure. + * @qp: belong to which HWQ. + * + * This routine get a batch of XRIs from pbl_pool if pvt_pool is less than + * low watermark. + **/ +void lpfc_keep_pvt_pool_above_lowwm(struct lpfc_hba *phba, u32 hwqid) +{ + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_pvt_pool *pvt_pool; + + multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool; + pvt_pool = &multixri_pool->pvt_pool; + + if (pvt_pool->count < pvt_pool->low_watermark) + lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH); +} + +/** + * lpfc_release_io_buf - Return one IO buf back to free pool + * @phba: pointer to lpfc hba data structure. + * @lpfc_ncmd: IO buf to be returned. + * @qp: belong to which HWQ. + * + * This routine returns one IO buf back to free pool. If this is an urgent IO, + * the IO buf is returned to expedite pool. If cfg_xri_rebalancing==1, + * the IO buf is returned to pbl_pool or pvt_pool based on watermark and + * xri_limit. If cfg_xri_rebalancing==0, the IO buf is returned to + * lpfc_io_buf_list_put. + **/ +void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd, + struct lpfc_sli4_hdw_queue *qp) +{ + unsigned long iflag; + struct lpfc_pbl_pool *pbl_pool; + struct lpfc_pvt_pool *pvt_pool; + struct lpfc_epd_pool *epd_pool; + u32 txcmplq_cnt; + u32 xri_owned; + u32 xri_limit; + u32 abts_io_bufs; + + /* MUST zero fields if buffer is reused by another protocol */ + lpfc_ncmd->nvmeCmd = NULL; + lpfc_ncmd->cur_iocbq.wqe_cmpl = NULL; + lpfc_ncmd->cur_iocbq.iocb_cmpl = NULL; + + if (phba->cfg_xri_rebalancing) { + if (lpfc_ncmd->expedite) { + /* Return to expedite pool */ + epd_pool = &phba->epd_pool; + spin_lock_irqsave(&epd_pool->lock, iflag); + list_add_tail(&lpfc_ncmd->list, &epd_pool->list); + epd_pool->count++; + spin_unlock_irqrestore(&epd_pool->lock, iflag); + return; + } + + /* Avoid invalid access if an IO sneaks in and is being rejected + * just _after_ xri pools are destroyed in lpfc_offline. + * Nothing much can be done at this point. + */ + if (!qp->p_multixri_pool) + return; + + pbl_pool = &qp->p_multixri_pool->pbl_pool; + pvt_pool = &qp->p_multixri_pool->pvt_pool; + + txcmplq_cnt = qp->fcp_wq->pring->txcmplq_cnt; + abts_io_bufs = qp->abts_scsi_io_bufs; + if (qp->nvme_wq) { + txcmplq_cnt += qp->nvme_wq->pring->txcmplq_cnt; + abts_io_bufs += qp->abts_nvme_io_bufs; + } + + xri_owned = pvt_pool->count + txcmplq_cnt + abts_io_bufs; + xri_limit = qp->p_multixri_pool->xri_limit; + +#ifdef LPFC_MXP_STAT + if (xri_owned <= xri_limit) + qp->p_multixri_pool->below_limit_count++; + else + qp->p_multixri_pool->above_limit_count++; +#endif + + /* XRI goes to either public or private free xri pool + * based on watermark and xri_limit + */ + if ((pvt_pool->count < pvt_pool->low_watermark) || + (xri_owned < xri_limit && + pvt_pool->count < pvt_pool->high_watermark)) { + spin_lock_irqsave(&pvt_pool->lock, iflag); + list_add_tail(&lpfc_ncmd->list, + &pvt_pool->list); + pvt_pool->count++; + spin_unlock_irqrestore(&pvt_pool->lock, iflag); + } else { + spin_lock_irqsave(&pbl_pool->lock, iflag); + list_add_tail(&lpfc_ncmd->list, + &pbl_pool->list); + pbl_pool->count++; + spin_unlock_irqrestore(&pbl_pool->lock, iflag); + } + } else { + spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + list_add_tail(&lpfc_ncmd->list, + &qp->lpfc_io_buf_list_put); + qp->put_io_bufs++; + spin_unlock_irqrestore(&qp->io_buf_list_put_lock, + iflag); + } +} + +/** + * lpfc_get_io_buf_from_private_pool - Get one free IO buf from private pool + * @phba: pointer to lpfc hba data structure. + * @pvt_pool: pointer to private pool data structure. + * @ndlp: pointer to lpfc nodelist data structure. + * + * This routine tries to get one free IO buf from private pool. + * + * Return: + * pointer to one free IO buf - if private pool is not empty + * NULL - if private pool is empty + **/ +static struct lpfc_io_buf * +lpfc_get_io_buf_from_private_pool(struct lpfc_hba *phba, + struct lpfc_pvt_pool *pvt_pool, + struct lpfc_nodelist *ndlp) +{ + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + unsigned long iflag; + + spin_lock_irqsave(&pvt_pool->lock, iflag); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &pvt_pool->list, list) { + if (lpfc_test_rrq_active( + phba, ndlp, lpfc_ncmd->cur_iocbq.sli4_lxritag)) + continue; + list_del(&lpfc_ncmd->list); + pvt_pool->count--; + spin_unlock_irqrestore(&pvt_pool->lock, iflag); + return lpfc_ncmd; + } + spin_unlock_irqrestore(&pvt_pool->lock, iflag); + + return NULL; +} + +/** + * lpfc_get_io_buf_from_expedite_pool - Get one free IO buf from expedite pool + * @phba: pointer to lpfc hba data structure. + * + * This routine tries to get one free IO buf from expedite pool. + * + * Return: + * pointer to one free IO buf - if expedite pool is not empty + * NULL - if expedite pool is empty + **/ +static struct lpfc_io_buf * +lpfc_get_io_buf_from_expedite_pool(struct lpfc_hba *phba) +{ + struct lpfc_io_buf *lpfc_ncmd; + struct lpfc_io_buf *lpfc_ncmd_next; + unsigned long iflag; + struct lpfc_epd_pool *epd_pool; + + epd_pool = &phba->epd_pool; + lpfc_ncmd = NULL; + + spin_lock_irqsave(&epd_pool->lock, iflag); + if (epd_pool->count > 0) { + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &epd_pool->list, list) { + list_del(&lpfc_ncmd->list); + epd_pool->count--; + break; + } + } + spin_unlock_irqrestore(&epd_pool->lock, iflag); + + return lpfc_ncmd; +} + +/** + * lpfc_get_io_buf_from_multixri_pools - Get one free IO bufs + * @phba: pointer to lpfc hba data structure. + * @ndlp: pointer to lpfc nodelist data structure. + * @hwqid: belong to which HWQ + * @expedite: 1 means this request is urgent. + * + * This routine will do the following actions and then return a pointer to + * one free IO buf. + * + * 1. If private free xri count is empty, move some XRIs from public to + * private pool. + * 2. Get one XRI from private free xri pool. + * 3. If we fail to get one from pvt_pool and this is an expedite request, + * get one free xri from expedite pool. + * + * Note: ndlp is only used on SCSI side for RRQ testing. + * The caller should pass NULL for ndlp on NVME side. + * + * Return: + * pointer to one free IO buf - if private pool is not empty + * NULL - if private pool is empty + **/ +static struct lpfc_io_buf * +lpfc_get_io_buf_from_multixri_pools(struct lpfc_hba *phba, + struct lpfc_nodelist *ndlp, + int hwqid, int expedite) +{ + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_multixri_pool *multixri_pool; + struct lpfc_pvt_pool *pvt_pool; + struct lpfc_io_buf *lpfc_ncmd; + + qp = &phba->sli4_hba.hdwq[hwqid]; + lpfc_ncmd = NULL; + multixri_pool = qp->p_multixri_pool; + pvt_pool = &multixri_pool->pvt_pool; + multixri_pool->io_req_count++; + + /* If pvt_pool is empty, move some XRIs from public to private pool */ + if (pvt_pool->count == 0) + lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH); + + /* Get one XRI from private free xri pool */ + lpfc_ncmd = lpfc_get_io_buf_from_private_pool(phba, pvt_pool, ndlp); + + if (lpfc_ncmd) { + lpfc_ncmd->hdwq = qp; + lpfc_ncmd->hdwq_no = hwqid; + } else if (expedite) { + /* If we fail to get one from pvt_pool and this is an expedite + * request, get one free xri from expedite pool. + */ + lpfc_ncmd = lpfc_get_io_buf_from_expedite_pool(phba); + } + + return lpfc_ncmd; +} + +static inline struct lpfc_io_buf * +lpfc_io_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int idx) +{ + struct lpfc_sli4_hdw_queue *qp; + struct lpfc_io_buf *lpfc_cmd, *lpfc_cmd_next; + + qp = &phba->sli4_hba.hdwq[idx]; + list_for_each_entry_safe(lpfc_cmd, lpfc_cmd_next, + &qp->lpfc_io_buf_list_get, list) { + if (lpfc_test_rrq_active(phba, ndlp, + lpfc_cmd->cur_iocbq.sli4_lxritag)) + continue; + + if (lpfc_cmd->flags & LPFC_SBUF_NOT_POSTED) + continue; + + list_del_init(&lpfc_cmd->list); + qp->get_io_bufs--; + lpfc_cmd->hdwq = qp; + lpfc_cmd->hdwq_no = idx; + return lpfc_cmd; + } + return NULL; +} + +/** + * lpfc_get_io_buf - Get one IO buffer from free pool + * @phba: The HBA for which this call is being executed. + * @ndlp: pointer to lpfc nodelist data structure. + * @hwqid: belong to which HWQ + * @expedite: 1 means this request is urgent. + * + * This routine gets one IO buffer from free pool. If cfg_xri_rebalancing==1, + * removes a IO buffer from multiXRI pools. If cfg_xri_rebalancing==0, removes + * a IO buffer from head of @hdwq io_buf_list and returns to caller. + * + * Note: ndlp is only used on SCSI side for RRQ testing. + * The caller should pass NULL for ndlp on NVME side. + * + * Return codes: + * NULL - Error + * Pointer to lpfc_io_buf - Success + **/ +struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba, + struct lpfc_nodelist *ndlp, + u32 hwqid, int expedite) +{ + struct lpfc_sli4_hdw_queue *qp; + unsigned long iflag; + struct lpfc_io_buf *lpfc_cmd; + + qp = &phba->sli4_hba.hdwq[hwqid]; + lpfc_cmd = NULL; + + if (phba->cfg_xri_rebalancing) + lpfc_cmd = lpfc_get_io_buf_from_multixri_pools( + phba, ndlp, hwqid, expedite); + else { + spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); + if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) + lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid); + if (!lpfc_cmd) { + spin_lock(&qp->io_buf_list_put_lock); + list_splice(&qp->lpfc_io_buf_list_put, + &qp->lpfc_io_buf_list_get); + qp->get_io_bufs += qp->put_io_bufs; + INIT_LIST_HEAD(&qp->lpfc_io_buf_list_put); + qp->put_io_bufs = 0; + spin_unlock(&qp->io_buf_list_put_lock); + if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || + expedite) + lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid); + } + spin_unlock_irqrestore(&qp->io_buf_list_get_lock, iflag); + } + + return lpfc_cmd; +} diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 3922e762ed31..658a40d3b1a2 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -20,6 +20,10 @@ * included with this package. * *******************************************************************/ +#if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS) +#define CONFIG_SCSI_LPFC_DEBUG_FS +#endif + /* forward declaration for LPFC_IOCB_t's use */ struct lpfc_hba; struct lpfc_vport; @@ -352,3 +356,84 @@ struct lpfc_sli { #define LPFC_MBOX_SLI4_CONFIG_EXTENDED_TMO 300 /* Timeout for other flash-based outstanding mbox command (Seconds) */ #define LPFC_MBOX_TMO_FLASH_CMD 300 + +struct lpfc_io_buf { + /* Common fields */ + struct list_head list; + void *data; + dma_addr_t dma_handle; + dma_addr_t dma_phys_sgl; + struct sli4_sge *dma_sgl; + struct lpfc_iocbq cur_iocbq; + struct lpfc_sli4_hdw_queue *hdwq; + uint16_t hdwq_no; + uint16_t cpu; + + struct lpfc_nodelist *ndlp; + uint32_t timeout; + uint16_t flags; /* TBD convert exch_busy to flags */ +#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ +#define LPFC_SBUF_BUMP_QDEPTH 0x2 /* bumped queue depth counter */ + /* External DIF device IO conversions */ +#define LPFC_SBUF_NORMAL_DIF 0x4 /* normal mode to insert/strip */ +#define LPFC_SBUF_PASS_DIF 0x8 /* insert/strip mode to passthru */ +#define LPFC_SBUF_NOT_POSTED 0x10 /* SGL failed post to FW. */ + uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ + uint16_t status; /* From IOCB Word 7- ulpStatus */ + uint32_t result; /* From IOCB Word 4. */ + + uint32_t seg_cnt; /* Number of scatter-gather segments returned by + * dma_map_sg. The driver needs this for calls + * to dma_unmap_sg. + */ + unsigned long start_time; + bool expedite; /* this is an expedite io_buf */ + + union { + /* SCSI specific fields */ + struct { + struct scsi_cmnd *pCmd; + struct lpfc_rport_data *rdata; + uint32_t prot_seg_cnt; /* seg_cnt's counterpart for + * protection data + */ + + /* + * data and dma_handle are the kernel virtual and bus + * address of the dma-able buffer containing the + * fcp_cmd, fcp_rsp and a scatter gather bde list that + * supports the sg_tablesize value. + */ + struct fcp_cmnd *fcp_cmnd; + struct fcp_rsp *fcp_rsp; + + wait_queue_head_t *waitq; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + /* Used to restore any changes to protection data for + * error injection + */ + void *prot_data_segment; + uint32_t prot_data; + uint32_t prot_data_type; +#define LPFC_INJERR_REFTAG 1 +#define LPFC_INJERR_APPTAG 2 +#define LPFC_INJERR_GUARD 3 +#endif + }; + + /* NVME specific fields */ + struct { + struct nvmefc_fcp_req *nvmeCmd; + uint16_t qidx; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint64_t ts_cmd_start; + uint64_t ts_last_cmd; + uint64_t ts_cmd_wqput; + uint64_t ts_isr_cmpl; + uint64_t ts_data_nvme; +#endif + }; + }; +}; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index a33fb6715329..6893e8547431 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -533,6 +533,59 @@ struct lpfc_vector_map_info { }; #define LPFC_VECTOR_MAP_EMPTY 0xffff +/* Multi-XRI pool */ +#define XRI_BATCH 8 + +struct lpfc_pbl_pool { + struct list_head list; + u32 count; + spinlock_t lock; /* lock for pbl_pool*/ +}; + +struct lpfc_pvt_pool { + u32 low_watermark; + u32 high_watermark; + + struct list_head list; + u32 count; + spinlock_t lock; /* lock for pvt_pool */ +}; + +struct lpfc_multixri_pool { + u32 xri_limit; + + /* Starting point when searching a pbl_pool with round-robin method */ + u32 rrb_next_hwqid; + + /* Used by lpfc_adjust_pvt_pool_count. + * io_req_count is incremented by 1 during IO submission. The heartbeat + * handler uses these two variables to determine if pvt_pool is idle or + * busy. + */ + u32 prev_io_req_count; + u32 io_req_count; + + /* statistics */ + u32 pbl_empty_count; +#ifdef LPFC_MXP_STAT + u32 above_limit_count; + u32 below_limit_count; + u32 local_pbl_hit_count; + u32 other_pbl_hit_count; + u32 stat_max_hwm; + +#define LPFC_MXP_SNAPSHOT_TAKEN 3 /* snapshot is taken at 3rd heartbeats */ + u32 stat_pbl_count; + u32 stat_pvt_count; + u32 stat_busy_count; + u32 stat_snapshot_taken; +#endif + + /* TODO: Separate pvt_pool into get and put list */ + struct lpfc_pbl_pool pbl_pool; /* Public free XRI pool */ + struct lpfc_pvt_pool pvt_pool; /* Private free XRI pool */ +}; + struct lpfc_fc4_ctrl_stat { u32 input_requests; u32 output_requests; @@ -567,6 +620,9 @@ struct lpfc_sli4_hdw_queue { uint32_t abts_scsi_io_bufs; uint32_t abts_nvme_io_bufs; + /* Multi-XRI pool per HWQ */ + struct lpfc_multixri_pool *p_multixri_pool; + /* FC-4 Stats counters */ struct lpfc_fc4_ctrl_stat nvme_cstat; struct lpfc_fc4_ctrl_stat scsi_cstat; From 45aa312e211f1c8c44a8e90c184a21bbb5b5572c Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:29 -0800 Subject: [PATCH 1214/4277] scsi: lpfc: Allow override of hardware queue selection policies Default behavior is to use the information from the upper IO stacks to select the hardware queue to use for IO submission. Which typically has good cpu affinity. However, the driver, when used on some variants of the upstream kernel, has found queuing information to be suboptimal for FCP or IO completion locked on particular cpus. For command submission situations, the lpfc_fcp_io_sched module parameter can be set to specify a hardware queue selection policy that overrides the os stack information. For IO completion situations, rather than queing cq processing based on the cpu servicing the interrupting event, schedule the cq processing on the cpu associated with the hardware queue's cq. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 11 ++++++----- drivers/scsi/lpfc/lpfc_hw4.h | 2 +- drivers/scsi/lpfc/lpfc_nvme.c | 14 +++++++++++--- drivers/scsi/lpfc/lpfc_scsi.c | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 47aa2af885a4..93a96491899c 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -5275,11 +5275,12 @@ LPFC_ATTR_R(xri_rebalancing, 1, 0, 1, "Enable/Disable XRI rebalancing"); /* * lpfc_io_sched: Determine scheduling algrithmn for issuing FCP cmds * range is [0,1]. Default value is 0. - * For [0], FCP commands are issued to Work Queues ina round robin fashion. + * For [0], FCP commands are issued to Work Queues based on upper layer + * hardware queue index. * For [1], FCP commands are issued to a Work Queue associated with the * current CPU. * - * LPFC_FCP_SCHED_ROUND_ROBIN == 0 + * LPFC_FCP_SCHED_BY_HDWQ == 0 * LPFC_FCP_SCHED_BY_CPU == 1 * * The driver dynamically sets this to 1 (BY_CPU) if it's able to set up cpu @@ -5287,11 +5288,11 @@ LPFC_ATTR_R(xri_rebalancing, 1, 0, 1, "Enable/Disable XRI rebalancing"); * CPU. Otherwise, the default 0 (Round Robin) scheduling of FCP/NVME I/Os * through WQs will be used. */ -LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_ROUND_ROBIN, - LPFC_FCP_SCHED_ROUND_ROBIN, +LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_BY_HDWQ, + LPFC_FCP_SCHED_BY_HDWQ, LPFC_FCP_SCHED_BY_CPU, "Determine scheduling algorithm for " - "issuing commands [0] - Round Robin, [1] - Current CPU"); + "issuing commands [0] - Hardware Queue, [1] - Current CPU"); /* * lpfc_ns_query: Determine algrithmn for NameServer queries after RSCN diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index c15b9b6fb840..cd39845c909f 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -194,7 +194,7 @@ struct lpfc_sli_intf { #define LPFC_ACT_INTR_CNT 4 /* Algrithmns for scheduling FCP commands to WQs */ -#define LPFC_FCP_SCHED_ROUND_ROBIN 0 +#define LPFC_FCP_SCHED_BY_HDWQ 0 #define LPFC_FCP_SCHED_BY_CPU 1 /* Algrithmns for NameServer Query after RSCN */ diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 0c6c91d39e2f..c9aacd56a449 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1546,8 +1546,17 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, } } - lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, - lpfc_queue_info->index, expedite); + if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) { + idx = lpfc_queue_info->index; + } else { + cpu = smp_processor_id(); + if (cpu < phba->cfg_hdw_queue) + idx = cpu; + else + idx = cpu % phba->cfg_hdw_queue; + } + + lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, idx, expedite); if (lpfc_ncmd == NULL) { atomic_inc(&lport->xmt_fcp_noxri); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, @@ -1585,7 +1594,6 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, * index to use and that they have affinitized a CPU to this hardware * queue. A hardware queue maps to a driver MSI-X vector/EQ/CQ/WQ. */ - idx = lpfc_queue_info->index; lpfc_ncmd->cur_iocbq.hba_wqidx = idx; cstat = &phba->sli4_hba.hdwq[idx].nvme_cstat; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index c824ed3be4f9..7b22cc995d7f 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -688,7 +688,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, int tag; cpu = smp_processor_id(); - if (cmnd) { + if (cmnd && phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) { tag = blk_mq_unique_tag(cmnd->request); idx = blk_mq_unique_tag_to_hwq(tag); } else { diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 4443d0d43053..c0f0adccdea7 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -14106,7 +14106,7 @@ process_cq: /* Save EQ associated with this CQ */ cq->assoc_qp = phba->sli4_hba.hdwq[qidx].hba_eq; - if (!queue_work(phba->wq, &cq->irqwork)) + if (!queue_work_on(cq->chann, phba->wq, &cq->irqwork)) lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0363 Cannot schedule soft IRQ " "for CQ eqcqid=%d, cqid=%d on CPU %d\n", From b3295c2a75333a4d6f0f945c7c148b1c0a453a8f Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:30 -0800 Subject: [PATCH 1215/4277] scsi: lpfc: Fix setting affinity hints to correlate with hardware queues The desired affinity for the hardware queue behavior is for hdwq 0 to be affinitized with cpu 0, hdwq 1 to cpu 1, and so on. The implementation so far does not do this if the number of cpus is greater than the number of hardware queues (e.g. hardware queue allocation was administratively reduced or hardware queue resources could not scale to the cpu count). Correct the queue affinitization logic when queue count is less than cpu count. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 38 +++++++++++++++++------ drivers/scsi/lpfc/lpfc_init.c | 58 ++++++++++++++++------------------- drivers/scsi/lpfc/lpfc_sli4.h | 2 +- 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 93a96491899c..787812dd57a9 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -5071,21 +5071,41 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, while (phba->sli4_hba.curr_disp_cpu < phba->sli4_hba.num_present_cpu) { cpup = &phba->sli4_hba.cpu_map[phba->sli4_hba.curr_disp_cpu]; - /* margin should fit in this and the truncated message */ - if (cpup->irq == LPFC_VECTOR_MAP_EMPTY) - len += snprintf(buf + len, PAGE_SIZE-len, - "CPU %02d io_chan %02d " + if (cpup->irq == LPFC_VECTOR_MAP_EMPTY) { + if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY) + len += snprintf( + buf + len, PAGE_SIZE - len, + "CPU %02d hdwq None " "physid %d coreid %d\n", phba->sli4_hba.curr_disp_cpu, - cpup->channel_id, cpup->phys_id, + cpup->phys_id, cpup->core_id); - else - len += snprintf(buf + len, PAGE_SIZE-len, - "CPU %02d io_chan %02d " + else + len += snprintf( + buf + len, PAGE_SIZE - len, + "CPU %02d hdwq %04d " + "physid %d coreid %d\n", + phba->sli4_hba.curr_disp_cpu, + cpup->hdwq, cpup->phys_id, + cpup->core_id); + } else { + if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY) + len += snprintf( + buf + len, PAGE_SIZE - len, + "CPU %02d hdwq None " "physid %d coreid %d IRQ %d\n", phba->sli4_hba.curr_disp_cpu, - cpup->channel_id, cpup->phys_id, + cpup->phys_id, cpup->core_id, cpup->irq); + else + len += snprintf( + buf + len, PAGE_SIZE - len, + "CPU %02d hdwq %04d " + "physid %d coreid %d IRQ %d\n", + phba->sli4_hba.curr_disp_cpu, + cpup->hdwq, cpup->phys_id, + cpup->core_id, cpup->irq); + } phba->sli4_hba.curr_disp_cpu++; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 10e3ad5419f0..d9db29817f6b 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -71,7 +71,6 @@ unsigned long _dump_buf_dif_order; spinlock_t _dump_buf_lock; /* Used when mapping IRQ vectors in a driver centric manner */ -uint16_t *lpfc_used_cpu; uint32_t lpfc_present_cpu; static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *); @@ -6841,20 +6840,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) rc = -ENOMEM; goto out_free_hba_eq_hdl; } - if (lpfc_used_cpu == NULL) { - lpfc_used_cpu = kcalloc(lpfc_present_cpu, sizeof(uint16_t), - GFP_KERNEL); - if (!lpfc_used_cpu) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3335 Failed allocate memory for msi-x " - "interrupt vector mapping\n"); - kfree(phba->sli4_hba.cpu_map); - rc = -ENOMEM; - goto out_free_hba_eq_hdl; - } - for (i = 0; i < lpfc_present_cpu; i++) - lpfc_used_cpu[i] = LPFC_VECTOR_MAP_EMPTY; - } /* * Enable sr-iov virtual functions if supported and configured @@ -10463,7 +10448,6 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba) /** * lpfc_cpu_affinity_check - Check vector CPU affinity mappings * @phba: pointer to lpfc hba data structure. - * @vectors: number of msix vectors allocated. * * The routine will figure out the CPU affinity assignment for every * MSI-X vector allocated for the HBA. The hba_eq_hdl will be updated @@ -10474,12 +10458,10 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba) * and the phba->sli4_hba.cpu_map array will reflect this. */ static void -lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) +lpfc_cpu_affinity_check(struct lpfc_hba *phba) { struct lpfc_vector_map_info *cpup; - int index = 0; - int vec = 0; - int cpu; + int cpu, idx; #ifdef CONFIG_X86 struct cpuinfo_x86 *cpuinfo; #endif @@ -10501,16 +10483,26 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) cpup->phys_id = 0; cpup->core_id = 0; #endif - cpup->channel_id = index; /* For now round robin */ - cpup->irq = pci_irq_vector(phba->pcidev, vec); - vec++; - if (vec >= vectors) - vec = 0; - index++; - if (index >= phba->cfg_hdw_queue) - index = 0; + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3328 CPU physid %d coreid %d\n", + cpup->phys_id, cpup->core_id); cpup++; } + + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + cpup = &phba->sli4_hba.cpu_map[idx]; + cpup->irq = pci_irq_vector(phba->pcidev, idx); + + /* For now assume vector N maps to CPU N */ + irq_set_affinity_hint(cpup->irq, get_cpu_mask(idx)); + cpup->hdwq = idx; + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3336 Set Affinity: CPU %d " + "hdwq %d irq %d\n", + cpu, cpup->hdwq, cpup->irq); + } + return; } @@ -10576,7 +10568,7 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) if (phba->cfg_nvmet_mrq > vectors) phba->cfg_nvmet_mrq = vectors; } - lpfc_cpu_affinity_check(phba, vectors); + lpfc_cpu_affinity_check(phba); return rc; @@ -10724,9 +10716,13 @@ lpfc_sli4_disable_intr(struct lpfc_hba *phba) int index; /* Free up MSI-X multi-message vectors */ - for (index = 0; index < phba->cfg_hdw_queue; index++) + for (index = 0; index < phba->cfg_hdw_queue; index++) { + irq_set_affinity_hint( + pci_irq_vector(phba->pcidev, index), + NULL); free_irq(pci_irq_vector(phba->pcidev, index), &phba->sli4_hba.hba_eq_hdl[index]); + } } else { free_irq(phba->pcidev->irq, phba); } @@ -12990,7 +12986,6 @@ lpfc_init(void) lpfc_nvmet_cmd_template(); /* Initialize in case vector mapping is needed */ - lpfc_used_cpu = NULL; lpfc_present_cpu = num_present_cpus(); error = pci_register_driver(&lpfc_driver); @@ -13029,7 +13024,6 @@ lpfc_exit(void) (1L << _dump_buf_dif_order), _dump_buf_dif); free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order); } - kfree(lpfc_used_cpu); idr_destroy(&lpfc_hba_index); } diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 6893e8547431..f5e58cd4c6ac 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -529,7 +529,7 @@ struct lpfc_vector_map_info { uint16_t phys_id; uint16_t core_id; uint16_t irq; - uint16_t channel_id; + uint16_t hdwq; }; #define LPFC_VECTOR_MAP_EMPTY 0xffff From 6a828b0f6192b4930894925d1c1d0dc1f1d99e6e Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:31 -0800 Subject: [PATCH 1216/4277] scsi: lpfc: Support non-uniform allocation of MSIX vectors to hardware queues So far MSIX vector allocation assumed it would be 1:1 with hardware queues. However, there are several reasons why fewer MSIX vectors may be allocated than hardware queues such as the platform being out of vectors or adapter limits being less than cpu count. This patch reworks the MSIX/EQ relationships with the per-cpu hardware queues so they can function independently. MSIX vectors will be equitably split been cpu sockets/cores and then the per-cpu hardware queues will be mapped to the vectors most efficient for them. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 7 +- drivers/scsi/lpfc/lpfc_attr.c | 96 +++--- drivers/scsi/lpfc/lpfc_crtn.h | 1 - drivers/scsi/lpfc/lpfc_debugfs.c | 303 +++++++++++++------ drivers/scsi/lpfc/lpfc_debugfs.h | 3 - drivers/scsi/lpfc/lpfc_hw4.h | 3 +- drivers/scsi/lpfc/lpfc_init.c | 501 +++++++++++++++++++++++++------ drivers/scsi/lpfc/lpfc_nvme.c | 18 +- drivers/scsi/lpfc/lpfc_scsi.c | 28 +- drivers/scsi/lpfc/lpfc_sli.c | 148 ++++----- drivers/scsi/lpfc/lpfc_sli4.h | 64 +++- 11 files changed, 830 insertions(+), 342 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 0f8964fdfecf..9fd2811ffa8b 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -84,8 +84,6 @@ struct lpfc_sli2_slim; #define LPFC_HB_MBOX_INTERVAL 5 /* Heart beat interval in seconds. */ #define LPFC_HB_MBOX_TIMEOUT 30 /* Heart beat timeout in seconds. */ -#define LPFC_LOOK_AHEAD_OFF 0 /* Look ahead logic is turned off */ - /* Error Attention event polling interval */ #define LPFC_ERATT_POLL_INTERVAL 5 /* EATT poll interval in seconds */ @@ -821,6 +819,7 @@ struct lpfc_hba { uint32_t cfg_fcp_imax; uint32_t cfg_fcp_cpu_map; uint32_t cfg_hdw_queue; + uint32_t cfg_irq_chann; uint32_t cfg_suppress_rsp; uint32_t cfg_nvme_oas; uint32_t cfg_nvme_embed_cmd; @@ -1042,6 +1041,9 @@ struct lpfc_hba { struct dentry *debug_nvmeio_trc; struct lpfc_debugfs_nvmeio_trc *nvmeio_trc; struct dentry *debug_hdwqinfo; +#ifdef LPFC_HDWQ_LOCK_STAT + struct dentry *debug_lockstat; +#endif atomic_t nvmeio_trc_cnt; uint32_t nvmeio_trc_size; uint32_t nvmeio_trc_output_idx; @@ -1161,6 +1163,7 @@ struct lpfc_hba { #define LPFC_CHECK_NVME_IO 1 #define LPFC_CHECK_NVMET_RCV 2 #define LPFC_CHECK_NVMET_IO 4 +#define LPFC_CHECK_SCSI_IO 8 uint16_t ktime_on; uint64_t ktime_data_samples; uint64_t ktime_status_samples; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 787812dd57a9..fc7f80d68638 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -4958,7 +4958,7 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, phba->cfg_fcp_imax = (uint32_t)val; phba->initial_imax = phba->cfg_fcp_imax; - for (i = 0; i < phba->cfg_hdw_queue; i += LPFC_MAX_EQ_DELAY_EQID_CNT) + for (i = 0; i < phba->cfg_irq_chann; i += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, i, LPFC_MAX_EQ_DELAY_EQID_CNT, val); @@ -5059,13 +5059,6 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, phba->cfg_fcp_cpu_map, phba->sli4_hba.num_online_cpu); break; - case 2: - len += snprintf(buf + len, PAGE_SIZE-len, - "fcp_cpu_map: Driver centric mapping (%d): " - "%d online CPUs\n", - phba->cfg_fcp_cpu_map, - phba->sli4_hba.num_online_cpu); - break; } while (phba->sli4_hba.curr_disp_cpu < phba->sli4_hba.num_present_cpu) { @@ -5076,35 +5069,35 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, len += snprintf( buf + len, PAGE_SIZE - len, "CPU %02d hdwq None " - "physid %d coreid %d\n", + "physid %d coreid %d ht %d\n", phba->sli4_hba.curr_disp_cpu, cpup->phys_id, - cpup->core_id); + cpup->core_id, cpup->hyper); else len += snprintf( buf + len, PAGE_SIZE - len, - "CPU %02d hdwq %04d " - "physid %d coreid %d\n", + "CPU %02d EQ %04d hdwq %04d " + "physid %d coreid %d ht %d\n", phba->sli4_hba.curr_disp_cpu, - cpup->hdwq, cpup->phys_id, - cpup->core_id); + cpup->eq, cpup->hdwq, cpup->phys_id, + cpup->core_id, cpup->hyper); } else { if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY) len += snprintf( buf + len, PAGE_SIZE - len, "CPU %02d hdwq None " - "physid %d coreid %d IRQ %d\n", + "physid %d coreid %d ht %d IRQ %d\n", phba->sli4_hba.curr_disp_cpu, cpup->phys_id, - cpup->core_id, cpup->irq); + cpup->core_id, cpup->hyper, cpup->irq); else len += snprintf( buf + len, PAGE_SIZE - len, - "CPU %02d hdwq %04d " - "physid %d coreid %d IRQ %d\n", + "CPU %02d EQ %04d hdwq %04d " + "physid %d coreid %d ht %d IRQ %d\n", phba->sli4_hba.curr_disp_cpu, - cpup->hdwq, cpup->phys_id, - cpup->core_id, cpup->irq); + cpup->eq, cpup->hdwq, cpup->phys_id, + cpup->core_id, cpup->hyper, cpup->irq); } phba->sli4_hba.curr_disp_cpu++; @@ -5146,14 +5139,13 @@ lpfc_fcp_cpu_map_store(struct device *dev, struct device_attribute *attr, # lpfc_fcp_cpu_map: Defines how to map CPUs to IRQ vectors # for the HBA. # -# Value range is [0 to 2]. Default value is LPFC_DRIVER_CPU_MAP (2). +# Value range is [0 to 1]. Default value is LPFC_HBA_CPU_MAP (1). # 0 - Do not affinitze IRQ vectors # 1 - Affintize HBA vectors with respect to each HBA # (start with CPU0 for each HBA) -# 2 - Affintize HBA vectors with respect to the entire driver -# (round robin thru all CPUs across all HBAs) +# This also defines how Hardware Queues are mapped to specific CPUs. */ -static int lpfc_fcp_cpu_map = LPFC_DRIVER_CPU_MAP; +static int lpfc_fcp_cpu_map = LPFC_HBA_CPU_MAP; module_param(lpfc_fcp_cpu_map, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(lpfc_fcp_cpu_map, "Defines how to map CPUs to IRQ vectors per HBA"); @@ -5187,7 +5179,7 @@ lpfc_fcp_cpu_map_init(struct lpfc_hba *phba, int val) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3326 lpfc_fcp_cpu_map: %d out of range, using " "default\n", val); - phba->cfg_fcp_cpu_map = LPFC_DRIVER_CPU_MAP; + phba->cfg_fcp_cpu_map = LPFC_HBA_CPU_MAP; return 0; } @@ -5308,7 +5300,7 @@ LPFC_ATTR_R(xri_rebalancing, 1, 0, 1, "Enable/Disable XRI rebalancing"); * CPU. Otherwise, the default 0 (Round Robin) scheduling of FCP/NVME I/Os * through WQs will be used. */ -LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_BY_HDWQ, +LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_BY_CPU, LPFC_FCP_SCHED_BY_HDWQ, LPFC_FCP_SCHED_BY_CPU, "Determine scheduling algorithm for " @@ -5474,24 +5466,40 @@ LPFC_ATTR_RW(nvme_embed_cmd, 1, 0, 2, "Embed NVME Command in WQE"); /* - * lpfc_hdw_queue: Set the number of IO channels the driver + * lpfc_hdw_queue: Set the number of Hardware Queues the driver * will advertise it supports to the NVME and SCSI layers. This also - * will map to the number of EQ/CQ/WQs the driver will create. + * will map to the number of CQ/WQ pairs the driver will create. * * The NVME Layer will try to create this many, plus 1 administrative * hardware queue. The administrative queue will always map to WQ 0 - * A hardware IO queue maps (qidx) to a specific driver WQ. + * A hardware IO queue maps (qidx) to a specific driver CQ/WQ. * * 0 = Configure the number of hdw queues to the number of active CPUs. - * 1,64 = Manually specify how many hdw queues to use. + * 1,128 = Manually specify how many hdw queues to use. * - * Value range is [0,64]. Default value is 0. + * Value range is [0,128]. Default value is 0. */ LPFC_ATTR_R(hdw_queue, LPFC_HBA_HDWQ_DEF, LPFC_HBA_HDWQ_MIN, LPFC_HBA_HDWQ_MAX, "Set the number of I/O Hardware Queues"); +/* + * lpfc_irq_chann: Set the number of IRQ vectors that are available + * for Hardware Queues to utilize. This also will map to the number + * of EQ / MSI-X vectors the driver will create. This should never be + * more than the number of Hardware Queues + * + * 0 = Configure number of IRQ Channels to the number of active CPUs. + * 1,128 = Manually specify how many IRQ Channels to use. + * + * Value range is [0,128]. Default value is 0. + */ +LPFC_ATTR_R(irq_chann, + LPFC_HBA_HDWQ_DEF, + LPFC_HBA_HDWQ_MIN, LPFC_HBA_HDWQ_MAX, + "Set the number of I/O IRQ Channels"); + /* # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware. # 0 = HBA resets disabled @@ -5532,16 +5540,6 @@ LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature."); */ LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support"); -/* -# lpfc_fcp_look_ahead: Look ahead for completions in FCP start routine -# 0 = disabled (default) -# 1 = enabled -# Value range is [0,1]. Default value is 0. -# -# This feature in under investigation and may be supported in the future. -*/ -unsigned int lpfc_fcp_look_ahead = LPFC_LOOK_AHEAD_OFF; - /* # lpfc_prot_mask: i # - Bit mask of host protection capabilities used to register with the @@ -5788,6 +5786,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_fcp_imax, &dev_attr_lpfc_fcp_cpu_map, &dev_attr_lpfc_hdw_queue, + &dev_attr_lpfc_irq_chann, &dev_attr_lpfc_suppress_rsp, &dev_attr_lpfc_nvmet_mrq, &dev_attr_lpfc_nvmet_mrq_post, @@ -6867,6 +6866,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb); lpfc_nvmet_fb_size_init(phba, lpfc_nvmet_fb_size); lpfc_hdw_queue_init(phba, lpfc_hdw_queue); + lpfc_irq_chann_init(phba, lpfc_irq_chann); lpfc_enable_bbcr_init(phba, lpfc_enable_bbcr); lpfc_enable_dpp_init(phba, lpfc_enable_dpp); @@ -6891,6 +6891,10 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) /* A value of 0 means use the number of CPUs found in the system */ if (phba->cfg_hdw_queue == 0) phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu; + if (phba->cfg_irq_chann == 0) + phba->cfg_irq_chann = phba->sli4_hba.num_present_cpu; + if (phba->cfg_irq_chann > phba->cfg_hdw_queue) + phba->cfg_irq_chann = phba->cfg_hdw_queue; phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; @@ -6933,6 +6937,10 @@ lpfc_nvme_mod_param_dep(struct lpfc_hba *phba) { if (phba->cfg_hdw_queue > phba->sli4_hba.num_present_cpu) phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu; + if (phba->cfg_irq_chann > phba->sli4_hba.num_present_cpu) + phba->cfg_irq_chann = phba->sli4_hba.num_present_cpu; + if (phba->cfg_irq_chann > phba->cfg_hdw_queue) + phba->cfg_irq_chann = phba->cfg_hdw_queue; if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME && phba->nvmet_support) { @@ -6953,11 +6961,11 @@ lpfc_nvme_mod_param_dep(struct lpfc_hba *phba) } if (!phba->cfg_nvmet_mrq) - phba->cfg_nvmet_mrq = phba->cfg_hdw_queue; + phba->cfg_nvmet_mrq = phba->cfg_irq_chann; /* Adjust lpfc_nvmet_mrq to avoid running out of WQE slots */ - if (phba->cfg_nvmet_mrq > phba->cfg_hdw_queue) { - phba->cfg_nvmet_mrq = phba->cfg_hdw_queue; + if (phba->cfg_nvmet_mrq > phba->cfg_irq_chann) { + phba->cfg_nvmet_mrq = phba->cfg_irq_chann; lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, "6018 Adjust lpfc_nvmet_mrq to %d\n", phba->cfg_nvmet_mrq); diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 726cd6a7c452..982401c31c12 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -440,7 +440,6 @@ extern spinlock_t _dump_buf_lock; extern int _dump_buf_done; extern spinlock_t pgcnt_lock; extern unsigned int pgcnt; -extern unsigned int lpfc_fcp_look_ahead; /* Interface exported by fabric iocb scheduler */ void lpfc_fabric_abort_nport(struct lpfc_nodelist *); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 2a2c46766eb6..72076b2cd4ff 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -378,6 +378,67 @@ skipit: return len; } +static int lpfc_debugfs_last_xripool; + +/** + * lpfc_debugfs_common_xri_data - Dump Hardware Queue info to a buffer + * @phba: The HBA to gather host buffer info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the Hardware Queue info from the @phba to @buf up to + * @size number of bytes. A header that describes the current hdwq state will be + * dumped to @buf first and then info on each hdwq entry will be dumped to @buf + * until @size bytes have been dumped or all the hdwq info has been dumped. + * + * Notes: + * This routine will rotate through each configured Hardware Queue each + * time called. + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_commonxripools_data(struct lpfc_hba *phba, char *buf, int size) +{ + struct lpfc_sli4_hdw_queue *qp; + int len = 0; + int i, out; + unsigned long iflag; + + for (i = 0; i < phba->cfg_hdw_queue; i++) { + if (len > (LPFC_DUMP_MULTIXRIPOOL_SIZE - 80)) + break; + qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_xripool]; + + len += snprintf(buf + len, size - len, "HdwQ %d Info ", i); + spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag); + spin_lock(&qp->abts_nvme_buf_list_lock); + spin_lock(&qp->io_buf_list_get_lock); + spin_lock(&qp->io_buf_list_put_lock); + out = qp->total_io_bufs - (qp->get_io_bufs + qp->put_io_bufs + + qp->abts_scsi_io_bufs + qp->abts_nvme_io_bufs); + len += snprintf(buf + len, size - len, + "tot:%d get:%d put:%d mt:%d " + "ABTS scsi:%d nvme:%d Out:%d\n", + qp->total_io_bufs, qp->get_io_bufs, qp->put_io_bufs, + qp->empty_io_bufs, qp->abts_scsi_io_bufs, + qp->abts_nvme_io_bufs, out); + spin_unlock(&qp->io_buf_list_put_lock); + spin_unlock(&qp->io_buf_list_get_lock); + spin_unlock(&qp->abts_nvme_buf_list_lock); + spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag); + + lpfc_debugfs_last_xripool++; + if (lpfc_debugfs_last_xripool >= phba->cfg_hdw_queue) + lpfc_debugfs_last_xripool = 0; + } + + return len; +} + /** * lpfc_debugfs_multixripools_data - Display multi-XRI pools information * @phba: The HBA to gather host buffer info from. @@ -405,6 +466,17 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) u32 txcmplq_cnt; char tmp[LPFC_DEBUG_OUT_LINE_SZ] = {0}; + if (phba->sli_rev != LPFC_SLI_REV4) + return 0; + + if (!phba->sli4_hba.hdwq) + return 0; + + if (!phba->cfg_xri_rebalancing) { + i = lpfc_debugfs_commonxripools_data(phba, buf, size); + return i; + } + /* * Pbl: Current number of free XRIs in public pool * Pvt: Current number of free XRIs in private pool @@ -498,10 +570,12 @@ lpfc_debugfs_multixripools_data(struct lpfc_hba *phba, char *buf, int size) return strnlen(buf, size); } -static int lpfc_debugfs_last_hdwq; + +#ifdef LPFC_HDWQ_LOCK_STAT +static int lpfc_debugfs_last_lock; /** - * lpfc_debugfs_hdwqinfo_data - Dump Hardware Queue info to a buffer + * lpfc_debugfs_lockstat_data - Dump Hardware Queue info to a buffer * @phba: The HBA to gather host buffer info from. * @buf: The buffer to dump log into. * @size: The maximum amount of data to process. @@ -521,12 +595,11 @@ static int lpfc_debugfs_last_hdwq; * not exceed @size. **/ static int -lpfc_debugfs_hdwqinfo_data(struct lpfc_hba *phba, char *buf, int size) +lpfc_debugfs_lockstat_data(struct lpfc_hba *phba, char *buf, int size) { struct lpfc_sli4_hdw_queue *qp; int len = 0; - int i, out; - unsigned long iflag; + int i; if (phba->sli_rev != LPFC_SLI_REV4) return 0; @@ -535,35 +608,40 @@ lpfc_debugfs_hdwqinfo_data(struct lpfc_hba *phba, char *buf, int size) return 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { - if (len > (LPFC_HDWQINFO_SIZE - 80)) + if (len > (LPFC_HDWQINFO_SIZE - 100)) break; - qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_hdwq]; + qp = &phba->sli4_hba.hdwq[lpfc_debugfs_last_lock]; - len += snprintf(buf + len, size - len, "HdwQ %d Info ", i); - spin_lock_irqsave(&qp->abts_scsi_buf_list_lock, iflag); - spin_lock(&qp->abts_nvme_buf_list_lock); - spin_lock(&qp->io_buf_list_get_lock); - spin_lock(&qp->io_buf_list_put_lock); - out = qp->total_io_bufs - (qp->get_io_bufs + qp->put_io_bufs + - qp->abts_scsi_io_bufs + qp->abts_nvme_io_bufs); - len += snprintf(buf + len, size - len, - "tot:%d get:%d put:%d mt:%d " - "ABTS scsi:%d nvme:%d Out:%d\n", - qp->total_io_bufs, qp->get_io_bufs, qp->put_io_bufs, - qp->empty_io_bufs, qp->abts_scsi_io_bufs, - qp->abts_nvme_io_bufs, out); - spin_unlock(&qp->io_buf_list_put_lock); - spin_unlock(&qp->io_buf_list_get_lock); - spin_unlock(&qp->abts_nvme_buf_list_lock); - spin_unlock_irqrestore(&qp->abts_scsi_buf_list_lock, iflag); + len += snprintf(buf + len, size - len, "HdwQ %03d Lock ", i); + if (phba->cfg_xri_rebalancing) { + len += snprintf(buf + len, size - len, + "get_pvt:%d mv_pvt:%d " + "mv2pub:%d mv2pvt:%d " + "put_pvt:%d put_pub:%d wq:%d\n", + qp->lock_conflict.alloc_pvt_pool, + qp->lock_conflict.mv_from_pvt_pool, + qp->lock_conflict.mv_to_pub_pool, + qp->lock_conflict.mv_to_pvt_pool, + qp->lock_conflict.free_pvt_pool, + qp->lock_conflict.free_pub_pool, + qp->lock_conflict.wq_access); + } else { + len += snprintf(buf + len, size - len, + "get:%d put:%d free:%d wq:%d\n", + qp->lock_conflict.alloc_xri_get, + qp->lock_conflict.alloc_xri_put, + qp->lock_conflict.free_xri, + qp->lock_conflict.wq_access); + } - lpfc_debugfs_last_hdwq++; - if (lpfc_debugfs_last_hdwq >= phba->cfg_hdw_queue) - lpfc_debugfs_last_hdwq = 0; + lpfc_debugfs_last_lock++; + if (lpfc_debugfs_last_lock >= phba->cfg_hdw_queue) + lpfc_debugfs_last_lock = 0; } return len; } +#endif static int lpfc_debugfs_last_hba_slim_off; @@ -964,7 +1042,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) struct lpfc_nvme_lport *lport; uint64_t data1, data2, data3; uint64_t tot, totin, totout; - int cnt, i, maxch; + int cnt, i; int len = 0; if (phba->nvmet_support) { @@ -1106,10 +1184,6 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) atomic_read(&lport->fc4NvmeLsRequests), atomic_read(&lport->fc4NvmeLsCmpls)); - if (phba->cfg_hdw_queue < LPFC_HBA_HDWQ_MAX) - maxch = phba->cfg_hdw_queue; - else - maxch = LPFC_HBA_HDWQ_MAX; totin = 0; totout = 0; for (i = 0; i < phba->cfg_hdw_queue; i++) { @@ -1547,7 +1621,7 @@ lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size) { struct lpfc_hba *phba = vport->phba; struct lpfc_sli4_hdw_queue *qp; - int i, j; + int i, j, max_cnt; int len = 0; uint32_t tot_xmt; uint32_t tot_rcv; @@ -1565,6 +1639,7 @@ lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size) } else { len += snprintf(buf + len, PAGE_SIZE - len, "\n"); } + max_cnt = size - LPFC_DEBUG_OUT_LINE_SZ; for (i = 0; i < phba->cfg_hdw_queue; i++) { qp = &phba->sli4_hba.hdwq[i]; @@ -1606,6 +1681,11 @@ lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size) } len += snprintf(buf + len, PAGE_SIZE - len, "Total: %x\n", tot_xmt); + if (len >= max_cnt) { + len += snprintf(buf + len, PAGE_SIZE - len, + "Truncated ...\n"); + return len; + } } return len; } @@ -1904,11 +1984,8 @@ lpfc_debugfs_multixripools_open(struct inode *inode, struct file *file) goto out; } - if (phba->cfg_xri_rebalancing) - debug->len = lpfc_debugfs_multixripools_data( - phba, debug->buffer, LPFC_DUMP_MULTIXRIPOOL_SIZE); - else - debug->len = 0; + debug->len = lpfc_debugfs_multixripools_data( + phba, debug->buffer, LPFC_DUMP_MULTIXRIPOOL_SIZE); debug->i_private = inode->i_private; file->private_data = debug; @@ -1918,8 +1995,9 @@ out: return rc; } +#ifdef LPFC_HDWQ_LOCK_STAT /** - * lpfc_debugfs_hdwqinfo_open - Open the hdwqinfo debugfs buffer + * lpfc_debugfs_lockstat_open - Open the lockstat debugfs buffer * @inode: The inode pointer that contains a vport pointer. * @file: The file pointer to attach the log output. * @@ -1934,7 +2012,7 @@ out: * error value. **/ static int -lpfc_debugfs_hdwqinfo_open(struct inode *inode, struct file *file) +lpfc_debugfs_lockstat_open(struct inode *inode, struct file *file) { struct lpfc_hba *phba = inode->i_private; struct lpfc_debug *debug; @@ -1951,7 +2029,7 @@ lpfc_debugfs_hdwqinfo_open(struct inode *inode, struct file *file) goto out; } - debug->len = lpfc_debugfs_hdwqinfo_data(phba, debug->buffer, + debug->len = lpfc_debugfs_lockstat_data(phba, debug->buffer, LPFC_HBQINFO_SIZE); file->private_data = debug; @@ -1960,6 +2038,48 @@ out: return rc; } +static ssize_t +lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + struct lpfc_sli4_hdw_queue *qp; + char mybuf[64]; + char *pbuf; + int i; + + /* Protect copy from user */ + if (!access_ok(buf, nbytes)) + return -EFAULT; + + memset(mybuf, 0, sizeof(mybuf)); + + if (copy_from_user(mybuf, buf, nbytes)) + return -EFAULT; + pbuf = &mybuf[0]; + + if ((strncmp(pbuf, "reset", strlen("reset")) == 0) || + (strncmp(pbuf, "zero", strlen("zero")) == 0)) { + for (i = 0; i < phba->cfg_hdw_queue; i++) { + qp = &phba->sli4_hba.hdwq[i]; + qp->lock_conflict.alloc_xri_get = 0; + qp->lock_conflict.alloc_xri_put = 0; + qp->lock_conflict.free_xri = 0; + qp->lock_conflict.wq_access = 0; + qp->lock_conflict.alloc_pvt_pool = 0; + qp->lock_conflict.mv_from_pvt_pool = 0; + qp->lock_conflict.mv_to_pub_pool = 0; + qp->lock_conflict.mv_to_pvt_pool = 0; + qp->lock_conflict.free_pvt_pool = 0; + qp->lock_conflict.free_pub_pool = 0; + qp->lock_conflict.wq_access = 0; + } + } + return nbytes; +} +#endif + /** * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer * @inode: The inode pointer that contains a vport pointer. @@ -2816,7 +2936,7 @@ lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file) } debug->len = lpfc_debugfs_cpucheck_data(vport, debug->buffer, - LPFC_NVMEKTIME_SIZE); + LPFC_CPUCHECK_SIZE); debug->i_private = inode->i_private; file->private_data = debug; @@ -2848,11 +2968,21 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf, pbuf = &mybuf[0]; if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) { + if (phba->nvmet_support) + phba->cpucheck_on |= LPFC_CHECK_NVMET_IO; + else + phba->cpucheck_on |= (LPFC_CHECK_NVME_IO | + LPFC_CHECK_SCSI_IO); + return strlen(pbuf); + } else if ((strncmp(pbuf, "nvme_on", sizeof("nvme_on") - 1) == 0)) { if (phba->nvmet_support) phba->cpucheck_on |= LPFC_CHECK_NVMET_IO; else phba->cpucheck_on |= LPFC_CHECK_NVME_IO; return strlen(pbuf); + } else if ((strncmp(pbuf, "scsi_on", sizeof("scsi_on") - 1) == 0)) { + phba->cpucheck_on |= LPFC_CHECK_SCSI_IO; + return strlen(pbuf); } else if ((strncmp(pbuf, "rcv", sizeof("rcv") - 1) == 0)) { if (phba->nvmet_support) @@ -3732,14 +3862,27 @@ lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer, int *len, int max_cnt, int eqidx, int eq_id) { struct lpfc_queue *qp; - int qidx, rc; + int rc; - for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { - qp = phba->sli4_hba.hdwq[qidx].fcp_cq; - if (qp->assoc_qid != eq_id) - continue; + qp = phba->sli4_hba.hdwq[eqidx].fcp_cq; - *len = __lpfc_idiag_print_cq(qp, "FCP", pbuffer, *len); + *len = __lpfc_idiag_print_cq(qp, "FCP", pbuffer, *len); + + /* Reset max counter */ + qp->CQ_max_cqe = 0; + + if (*len >= max_cnt) + return 1; + + rc = lpfc_idiag_wqs_for_cq(phba, "FCP", pbuffer, len, + max_cnt, qp->queue_id); + if (rc) + return 1; + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + qp = phba->sli4_hba.hdwq[eqidx].nvme_cq; + + *len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len); /* Reset max counter */ qp->CQ_max_cqe = 0; @@ -3747,33 +3890,12 @@ lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer, if (*len >= max_cnt) return 1; - rc = lpfc_idiag_wqs_for_cq(phba, "FCP", pbuffer, len, - max_cnt, qp->queue_id); + rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len, + max_cnt, qp->queue_id); if (rc) return 1; } - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { - qp = phba->sli4_hba.hdwq[qidx].nvme_cq; - if (qp->assoc_qid != eq_id) - continue; - - *len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len); - - /* Reset max counter */ - qp->CQ_max_cqe = 0; - - if (*len >= max_cnt) - return 1; - - rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len, - max_cnt, qp->queue_id); - if (rc) - return 1; - } - } - if ((eqidx < phba->cfg_nvmet_mrq) && phba->nvmet_support) { /* NVMET CQset */ qp = phba->sli4_hba.nvmet_cqset[eqidx]; @@ -3812,9 +3934,10 @@ __lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype, (unsigned long long)qp->q_cnt_4, qp->q_mode); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "EQID[%02d], QE-CNT[%04d], QE-SZ[%04d], " - "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]", + "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d] AFFIN[%03d]", qp->queue_id, qp->entry_count, qp->entry_size, - qp->host_index, qp->hba_index, qp->entry_repost); + qp->host_index, qp->hba_index, qp->entry_repost, + qp->chann); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); return len; @@ -3869,7 +3992,7 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, phba->lpfc_idiag_last_eq = 0; len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, - "EQ %d out of %d HBA EQs\n", + "HDWQ %d out of %d HBA HDWQs\n", x, phba->cfg_hdw_queue); /* Fast-path EQ */ @@ -5299,14 +5422,17 @@ static const struct file_operations lpfc_debugfs_op_hbqinfo = { .release = lpfc_debugfs_release, }; -#undef lpfc_debugfs_op_hdwqinfo -static const struct file_operations lpfc_debugfs_op_hdwqinfo = { +#ifdef LPFC_HDWQ_LOCK_STAT +#undef lpfc_debugfs_op_lockstat +static const struct file_operations lpfc_debugfs_op_lockstat = { .owner = THIS_MODULE, - .open = lpfc_debugfs_hdwqinfo_open, + .open = lpfc_debugfs_lockstat_open, .llseek = lpfc_debugfs_lseek, .read = lpfc_debugfs_read, + .write = lpfc_debugfs_lockstat_write, .release = lpfc_debugfs_release, }; +#endif #undef lpfc_debugfs_op_dumpHBASlim static const struct file_operations lpfc_debugfs_op_dumpHBASlim = { @@ -5756,17 +5882,19 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) phba->hba_debugfs_root, phba, &lpfc_debugfs_op_hbqinfo); - /* Setup hdwqinfo */ - snprintf(name, sizeof(name), "hdwqinfo"); - phba->debug_hdwqinfo = +#ifdef LPFC_HDWQ_LOCK_STAT + /* Setup lockstat */ + snprintf(name, sizeof(name), "lockstat"); + phba->debug_lockstat = debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_hdwqinfo); - if (!phba->debug_hdwqinfo) { + phba, &lpfc_debugfs_op_lockstat); + if (!phba->debug_lockstat) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0511 Cant create debugfs hdwqinfo\n"); + "0913 Cant create debugfs lockstat\n"); goto debug_failed; } +#endif /* Setup dumpHBASlim */ if (phba->sli_rev < LPFC_SLI_REV4) { @@ -6006,7 +6134,7 @@ nvmeio_off: vport, &lpfc_debugfs_op_scsistat); if (!vport->debug_scsistat) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0811 Cannot create debugfs scsistat\n"); + "0914 Cannot create debugfs scsistat\n"); goto debug_failed; } @@ -6171,9 +6299,10 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */ phba->debug_hbqinfo = NULL; - debugfs_remove(phba->debug_hdwqinfo); /* hdwqinfo */ - phba->debug_hdwqinfo = NULL; - +#ifdef LPFC_HDWQ_LOCK_STAT + debugfs_remove(phba->debug_lockstat); /* lockstat */ + phba->debug_lockstat = NULL; +#endif debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */ phba->debug_dumpHBASlim = NULL; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index cf256a6dca42..1fbee6496f85 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -290,9 +290,6 @@ struct lpfc_idiag { /* multixripool output buffer size */ #define LPFC_DUMP_MULTIXRIPOOL_SIZE 8192 -/* hdwqinfo output buffer size */ -#define LPFC_HDWQINFO_SIZE 8192 - enum { DUMP_FCP, DUMP_NVME, diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index cd39845c909f..665852291a4f 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -211,9 +211,8 @@ struct lpfc_sli_intf { #define LPFC_DEF_IMAX 150000 #define LPFC_MIN_CPU_MAP 0 -#define LPFC_MAX_CPU_MAP 2 +#define LPFC_MAX_CPU_MAP 1 #define LPFC_HBA_CPU_MAP 1 -#define LPFC_DRIVER_CPU_MAP 2 /* Default */ /* PORT_CAPABILITIES constants. */ #define LPFC_MAX_SUPPORTED_PAGES 8 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index d9db29817f6b..145c08f112a3 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -92,6 +93,8 @@ static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *); static void lpfc_sli4_disable_intr(struct lpfc_hba *); static uint32_t lpfc_sli4_enable_intr(struct lpfc_hba *, uint32_t); static void lpfc_sli4_oas_verify(struct lpfc_hba *phba); +static uint16_t lpfc_find_eq_handle(struct lpfc_hba *, uint16_t); +static uint16_t lpfc_find_cpu_handle(struct lpfc_hba *, uint16_t, int); static struct scsi_transport_template *lpfc_transport_template = NULL; static struct scsi_transport_template *lpfc_vport_transport_template = NULL; @@ -1367,13 +1370,13 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) } /* Interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / phba->cfg_hdw_queue; + val = phba->cfg_fcp_imax / phba->cfg_irq_chann; tick_cqe = val / CONFIG_HZ; /* Per tick per EQ */ /* Assume 1 CQE/ISR, calc max CQEs allowed for time duration */ max_cqe = time_elapsed * tick_cqe; - for (i = 0; i < phba->cfg_hdw_queue; i++) { + for (i = 0; i < phba->cfg_irq_chann; i++) { /* Fast-path EQ */ qp = phba->sli4_hba.hdwq[i].hba_eq; if (!qp) @@ -1397,7 +1400,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) if (val) { /* First, interrupts per sec per EQ */ val = phba->cfg_fcp_imax / - phba->cfg_hdw_queue; + phba->cfg_irq_chann; /* us delay between each interrupt */ val = LPFC_SEC_TO_USEC / val; @@ -4335,8 +4338,13 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->max_lun = vport->cfg_max_luns; shost->this_id = -1; shost->max_cmd_len = 16; + if (phba->sli_rev == LPFC_SLI_REV4) { - shost->nr_hw_queues = phba->cfg_hdw_queue; + if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) + shost->nr_hw_queues = phba->cfg_hdw_queue; + else + shost->nr_hw_queues = phba->sli4_hba.num_present_cpu; + shost->dma_boundary = phba->sli4_hba.pc_sli4_params.sge_supp_len-1; shost->sg_tablesize = phba->cfg_scsi_seg_cnt; @@ -6819,7 +6827,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_remove_rpi_hdrs; } - phba->sli4_hba.hba_eq_hdl = kcalloc(phba->cfg_hdw_queue, + phba->sli4_hba.hba_eq_hdl = kcalloc(phba->cfg_irq_chann, sizeof(struct lpfc_hba_eq_hdl), GFP_KERNEL); if (!phba->sli4_hba.hba_eq_hdl) { @@ -8257,7 +8265,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) struct lpfc_rsrc_desc_fcfcoe *desc; char *pdesc_0; uint16_t forced_link_speed; - uint32_t if_type; + uint32_t if_type, qmin; int length, i, rc = 0, rc2; pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -8362,40 +8370,44 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->sli4_hba.max_cfg_param.max_rq); /* - * Calculate NVME queue resources based on how - * many WQ/CQs are available. + * Calculate queue resources based on how + * many WQ/CQ/EQs are available. */ - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - length = phba->sli4_hba.max_cfg_param.max_wq; - if (phba->sli4_hba.max_cfg_param.max_cq < - phba->sli4_hba.max_cfg_param.max_wq) - length = phba->sli4_hba.max_cfg_param.max_cq; + qmin = phba->sli4_hba.max_cfg_param.max_wq; + if (phba->sli4_hba.max_cfg_param.max_cq < qmin) + qmin = phba->sli4_hba.max_cfg_param.max_cq; + if (phba->sli4_hba.max_cfg_param.max_eq < qmin) + qmin = phba->sli4_hba.max_cfg_param.max_eq; + /* + * Whats left after this can go toward NVME / FCP. + * The minus 4 accounts for ELS, NVME LS, MBOX + * plus one extra. When configured for + * NVMET, FCP io channel WQs are not created. + */ + qmin -= 4; - /* - * Whats left after this can go toward NVME. - * The minus 6 accounts for ELS, NVME LS, MBOX - * plus a couple extra. When configured for - * NVMET, FCP io channel WQs are not created. - */ - length -= 6; + /* If NVME is configured, double the number of CQ/WQs needed */ + if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && + !phba->nvmet_support) + qmin /= 2; - /* Take off FCP queues */ - if (!phba->nvmet_support) - length -= phba->cfg_hdw_queue; - - /* Check to see if there is enough for NVME */ - if (phba->cfg_hdw_queue > length) { - lpfc_printf_log( - phba, KERN_ERR, LOG_SLI, - "2005 Reducing NVME IO channel to %d: " - "WQ %d CQ %d CommonIO %d\n", - length, + /* Check to see if there is enough for NVME */ + if ((phba->cfg_irq_chann > qmin) || + (phba->cfg_hdw_queue > qmin)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2005 Reducing Queues: " + "WQ %d CQ %d EQ %d: min %d: " + "IRQ %d HDWQ %d\n", phba->sli4_hba.max_cfg_param.max_wq, phba->sli4_hba.max_cfg_param.max_cq, + phba->sli4_hba.max_cfg_param.max_eq, + qmin, phba->cfg_irq_chann, phba->cfg_hdw_queue); - phba->cfg_hdw_queue = length; - } + if (phba->cfg_irq_chann > qmin) + phba->cfg_irq_chann = qmin; + if (phba->cfg_hdw_queue > qmin) + phba->cfg_hdw_queue = qmin; } } @@ -8612,25 +8624,17 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) * device parameters */ - if (phba->cfg_hdw_queue > phba->sli4_hba.max_cfg_param.max_eq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2575 Reducing IO channels to match number of " - "available EQs: from %d to %d\n", - phba->cfg_hdw_queue, - phba->sli4_hba.max_cfg_param.max_eq); - phba->cfg_hdw_queue = phba->sli4_hba.max_cfg_param.max_eq; - } - if (phba->nvmet_support) { - if (phba->cfg_hdw_queue < phba->cfg_nvmet_mrq) - phba->cfg_nvmet_mrq = phba->cfg_hdw_queue; + if (phba->cfg_irq_chann < phba->cfg_nvmet_mrq) + phba->cfg_nvmet_mrq = phba->cfg_irq_chann; } if (phba->cfg_nvmet_mrq > LPFC_NVMET_MRQ_MAX) phba->cfg_nvmet_mrq = LPFC_NVMET_MRQ_MAX; lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2574 IO channels: hdwQ %d MRQ: %d\n", - phba->cfg_hdw_queue, phba->cfg_nvmet_mrq); + "2574 IO channels: hdwQ %d IRQ %d MRQ: %d\n", + phba->cfg_hdw_queue, phba->cfg_irq_chann, + phba->cfg_nvmet_mrq); /* Get EQ depth from module parameter, fake the default for now */ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B; @@ -8658,6 +8662,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) } qdesc->qe_valid = 1; qdesc->hdwq = wqidx; + qdesc->chann = lpfc_find_cpu_handle(phba, wqidx, LPFC_FIND_BY_HDWQ); phba->sli4_hba.hdwq[wqidx].nvme_cq = qdesc; qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE, @@ -8669,6 +8674,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) return 1; } qdesc->hdwq = wqidx; + qdesc->chann = wqidx; phba->sli4_hba.hdwq[wqidx].nvme_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; @@ -8698,6 +8704,7 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) } qdesc->qe_valid = 1; qdesc->hdwq = wqidx; + qdesc->chann = lpfc_find_cpu_handle(phba, wqidx, LPFC_FIND_BY_HDWQ); phba->sli4_hba.hdwq[wqidx].fcp_cq = qdesc; /* Create Fast Path FCP WQs */ @@ -8720,6 +8727,7 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) return 1; } qdesc->hdwq = wqidx; + qdesc->chann = wqidx; phba->sli4_hba.hdwq[wqidx].fcp_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; @@ -8743,7 +8751,7 @@ int lpfc_sli4_queue_create(struct lpfc_hba *phba) { struct lpfc_queue *qdesc; - int idx; + int idx, eqidx; struct lpfc_sli4_hdw_queue *qp; /* @@ -8829,7 +8837,18 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) /* Create HBA Event Queues (EQs) */ for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { - /* Create EQs */ + /* + * If there are more Hardware Queues than available + * CQs, multiple Hardware Queues may share a common EQ. + */ + if (idx >= phba->cfg_irq_chann) { + /* Share an existing EQ */ + eqidx = lpfc_find_eq_handle(phba, idx); + phba->sli4_hba.hdwq[idx].hba_eq = + phba->sli4_hba.hdwq[eqidx].hba_eq; + continue; + } + /* Create an EQ */ qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE, phba->sli4_hba.eq_esize, phba->sli4_hba.eq_ecount); @@ -8840,20 +8859,27 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) } qdesc->qe_valid = 1; qdesc->hdwq = idx; + + /* Save the CPU this EQ is affinitised to */ + eqidx = lpfc_find_eq_handle(phba, idx); + qdesc->chann = lpfc_find_cpu_handle(phba, eqidx, + LPFC_FIND_BY_EQ); phba->sli4_hba.hdwq[idx].hba_eq = qdesc; } /* Allocate SCSI SLI4 CQ/WQs */ - for (idx = 0; idx < phba->cfg_hdw_queue; idx++) + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { if (lpfc_alloc_fcp_wq_cq(phba, idx)) goto out_error; + } /* Allocate NVME SLI4 CQ/WQs */ if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - for (idx = 0; idx < phba->cfg_hdw_queue; idx++) + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { if (lpfc_alloc_nvme_wq_cq(phba, idx)) goto out_error; + } if (phba->nvmet_support) { for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) { @@ -8871,6 +8897,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) } qdesc->qe_valid = 1; qdesc->hdwq = idx; + qdesc->chann = idx; phba->sli4_hba.nvmet_cqset[idx] = qdesc; } } @@ -8902,6 +8929,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } qdesc->qe_valid = 1; + qdesc->chann = 0; phba->sli4_hba.els_cq = qdesc; @@ -8919,6 +8947,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) "0505 Failed allocate slow-path MQ\n"); goto out_error; } + qdesc->chann = 0; phba->sli4_hba.mbx_wq = qdesc; /* @@ -8934,6 +8963,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) "0504 Failed allocate slow-path ELS WQ\n"); goto out_error; } + qdesc->chann = 0; phba->sli4_hba.els_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); @@ -8947,6 +8977,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) "6079 Failed allocate NVME LS CQ\n"); goto out_error; } + qdesc->chann = 0; qdesc->qe_valid = 1; phba->sli4_hba.nvmels_cq = qdesc; @@ -8959,6 +8990,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) "6080 Failed allocate NVME LS WQ\n"); goto out_error; } + qdesc->chann = 0; phba->sli4_hba.nvmels_wq = qdesc; list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); } @@ -9085,17 +9117,21 @@ lpfc_sli4_release_queues(struct lpfc_queue ***qs, int max) } static inline void -lpfc_sli4_release_hdwq(struct lpfc_sli4_hdw_queue *hdwq, int max) +lpfc_sli4_release_hdwq(struct lpfc_hba *phba) { + struct lpfc_sli4_hdw_queue *hdwq; uint32_t idx; - for (idx = 0; idx < max; idx++) { - lpfc_sli4_queue_free(hdwq[idx].hba_eq); + hdwq = phba->sli4_hba.hdwq; + for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + if (idx < phba->cfg_irq_chann) + lpfc_sli4_queue_free(hdwq[idx].hba_eq); + hdwq[idx].hba_eq = NULL; + lpfc_sli4_queue_free(hdwq[idx].fcp_cq); lpfc_sli4_queue_free(hdwq[idx].nvme_cq); lpfc_sli4_queue_free(hdwq[idx].fcp_wq); lpfc_sli4_queue_free(hdwq[idx].nvme_wq); - hdwq[idx].hba_eq = NULL; hdwq[idx].fcp_cq = NULL; hdwq[idx].nvme_cq = NULL; hdwq[idx].fcp_wq = NULL; @@ -9120,8 +9156,7 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) { /* Release HBA eqs */ if (phba->sli4_hba.hdwq) - lpfc_sli4_release_hdwq(phba->sli4_hba.hdwq, - phba->cfg_hdw_queue); + lpfc_sli4_release_hdwq(phba); if (phba->nvmet_support) { lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_cqset, @@ -9202,7 +9237,6 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq, qidx, (uint32_t)rc); return rc; } - cq->chann = qidx; if (qtype != LPFC_MBOX) { /* Setup cq_map for fast lookup */ @@ -9222,7 +9256,6 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq, /* no need to tear down cq - caller will do so */ return rc; } - wq->chann = qidx; /* Bind this CQ/WQ to the NVME ring */ pring = wq->pring; @@ -9251,6 +9284,38 @@ lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq, return 0; } +/** + * lpfc_setup_cq_lookup - Setup the CQ lookup table + * @phba: pointer to lpfc hba data structure. + * + * This routine will populate the cq_lookup table by all + * available CQ queue_id's. + **/ +void +lpfc_setup_cq_lookup(struct lpfc_hba *phba) +{ + struct lpfc_queue *eq, *childq; + struct lpfc_sli4_hdw_queue *qp; + int qidx; + + qp = phba->sli4_hba.hdwq; + memset(phba->sli4_hba.cq_lookup, 0, + (sizeof(struct lpfc_queue *) * (phba->sli4_hba.cq_max + 1))); + for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) { + eq = qp[qidx].hba_eq; + if (!eq) + continue; + list_for_each_entry(childq, &eq->child_list, list) { + if (childq->queue_id > phba->sli4_hba.cq_max) + continue; + if ((childq->subtype == LPFC_FCP) || + (childq->subtype == LPFC_NVME)) + phba->sli4_hba.cq_lookup[childq->queue_id] = + childq; + } + } +} + /** * lpfc_sli4_queue_setup - Set up all the SLI4 queues * @phba: pointer to lpfc hba data structure. @@ -9331,7 +9396,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) rc = -ENOMEM; goto out_error; } - for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) { if (!qp[qidx].hba_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0522 Fast-path EQ (%d) not " @@ -9578,11 +9643,23 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.dat_rq->queue_id, phba->sli4_hba.els_cq->queue_id); - for (qidx = 0; qidx < phba->cfg_hdw_queue; + for (qidx = 0; qidx < phba->cfg_irq_chann; qidx += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT, phba->cfg_fcp_imax); + if (phba->sli4_hba.cq_max) { + kfree(phba->sli4_hba.cq_lookup); + phba->sli4_hba.cq_lookup = kcalloc((phba->sli4_hba.cq_max + 1), + sizeof(struct lpfc_queue *), GFP_KERNEL); + if (!phba->sli4_hba.cq_lookup) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0549 Failed setup of CQ Lookup table: " + "size 0x%x\n", phba->sli4_hba.cq_max); + goto out_destroy; + } + lpfc_setup_cq_lookup(phba); + } return 0; out_destroy: @@ -9664,9 +9741,14 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba) lpfc_wq_destroy(phba, qp->nvme_wq); lpfc_cq_destroy(phba, qp->fcp_cq); lpfc_cq_destroy(phba, qp->nvme_cq); - lpfc_eq_destroy(phba, qp->hba_eq); + if (qidx < phba->cfg_irq_chann) + lpfc_eq_destroy(phba, qp->hba_eq); } } + + kfree(phba->sli4_hba.cq_lookup); + phba->sli4_hba.cq_lookup = NULL; + phba->sli4_hba.cq_max = 0; } /** @@ -10445,23 +10527,199 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba) phba->sli.slistat.sli_intr = 0; } +/** + * lpfc_find_cpu_handle - Find the CPU that corresponds to the specified EQ + * @phba: pointer to lpfc hba data structure. + * @id: EQ vector index or Hardware Queue index + * @match: LPFC_FIND_BY_EQ = match by EQ + * LPFC_FIND_BY_HDWQ = match by Hardware Queue + */ +static uint16_t +lpfc_find_cpu_handle(struct lpfc_hba *phba, uint16_t id, int match) +{ + struct lpfc_vector_map_info *cpup; + int cpu; + + /* Find the desired phys_id for the specified EQ */ + cpup = phba->sli4_hba.cpu_map; + for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + if ((match == LPFC_FIND_BY_EQ) && + (cpup->irq != LPFC_VECTOR_MAP_EMPTY) && + (cpup->eq == id)) + return cpu; + if ((match == LPFC_FIND_BY_HDWQ) && (cpup->hdwq == id)) + return cpu; + cpup++; + } + return 0; +} + +/** + * lpfc_find_eq_handle - Find the EQ that corresponds to the specified + * Hardware Queue + * @phba: pointer to lpfc hba data structure. + * @hdwq: Hardware Queue index + */ +static uint16_t +lpfc_find_eq_handle(struct lpfc_hba *phba, uint16_t hdwq) +{ + struct lpfc_vector_map_info *cpup; + int cpu; + + /* Find the desired phys_id for the specified EQ */ + cpup = phba->sli4_hba.cpu_map; + for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + if (cpup->hdwq == hdwq) + return cpup->eq; + cpup++; + } + return 0; +} + +/** + * lpfc_find_phys_id_eq - Find the next EQ that corresponds to the specified + * Physical Id. + * @phba: pointer to lpfc hba data structure. + * @eqidx: EQ index + * @phys_id: CPU package physical id + */ +static uint16_t +lpfc_find_phys_id_eq(struct lpfc_hba *phba, uint16_t eqidx, uint16_t phys_id) +{ + struct lpfc_vector_map_info *cpup; + int cpu, desired_phys_id; + + desired_phys_id = LPFC_VECTOR_MAP_EMPTY; + + /* Find the desired phys_id for the specified EQ */ + cpup = phba->sli4_hba.cpu_map; + for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + if ((cpup->irq != LPFC_VECTOR_MAP_EMPTY) && + (cpup->eq == eqidx)) { + desired_phys_id = cpup->phys_id; + break; + } + cpup++; + } + if (phys_id == desired_phys_id) + return eqidx; + + /* Find a EQ thats on the specified phys_id */ + cpup = phba->sli4_hba.cpu_map; + for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + if ((cpup->irq != LPFC_VECTOR_MAP_EMPTY) && + (cpup->phys_id == phys_id)) + return cpup->eq; + cpup++; + } + return 0; +} + +/** + * lpfc_find_cpu_map - Find next available CPU map entry that matches the + * phys_id and core_id. + * @phba: pointer to lpfc hba data structure. + * @phys_id: CPU package physical id + * @core_id: CPU core id + * @hdwqidx: Hardware Queue index + * @eqidx: EQ index + * @isr_avail: Should an IRQ be associated with this entry + */ +static struct lpfc_vector_map_info * +lpfc_find_cpu_map(struct lpfc_hba *phba, uint16_t phys_id, uint16_t core_id, + uint16_t hdwqidx, uint16_t eqidx, int isr_avail) +{ + struct lpfc_vector_map_info *cpup; + int cpu; + + cpup = phba->sli4_hba.cpu_map; + for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + /* Does the cpup match the one we are looking for */ + if ((cpup->phys_id == phys_id) && + (cpup->core_id == core_id)) { + /* If it has been already assigned, then skip it */ + if (cpup->hdwq != LPFC_VECTOR_MAP_EMPTY) { + cpup++; + continue; + } + /* Ensure we are on the same phys_id as the first one */ + if (!isr_avail) + cpup->eq = lpfc_find_phys_id_eq(phba, eqidx, + phys_id); + else + cpup->eq = eqidx; + + cpup->hdwq = hdwqidx; + if (isr_avail) { + cpup->irq = + pci_irq_vector(phba->pcidev, eqidx); + + /* Now affinitize to the selected CPU */ + irq_set_affinity_hint(cpup->irq, + get_cpu_mask(cpu)); + irq_set_status_flags(cpup->irq, + IRQ_NO_BALANCING); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3330 Set Affinity: CPU %d " + "EQ %d irq %d (HDWQ %x)\n", + cpu, cpup->eq, + cpup->irq, cpup->hdwq); + } + return cpup; + } + cpup++; + } + return 0; +} + +#ifdef CONFIG_X86 +/** + * lpfc_find_hyper - Determine if the CPU map entry is hyper-threaded + * @phba: pointer to lpfc hba data structure. + * @cpu: CPU map index + * @phys_id: CPU package physical id + * @core_id: CPU core id + */ +static int +lpfc_find_hyper(struct lpfc_hba *phba, int cpu, + uint16_t phys_id, uint16_t core_id) +{ + struct lpfc_vector_map_info *cpup; + int idx; + + cpup = phba->sli4_hba.cpu_map; + for (idx = 0; idx < phba->sli4_hba.num_present_cpu; idx++) { + /* Does the cpup match the one we are looking for */ + if ((cpup->phys_id == phys_id) && + (cpup->core_id == core_id) && + (cpu != idx)) { + return 1; + } + cpup++; + } + return 0; +} +#endif + /** * lpfc_cpu_affinity_check - Check vector CPU affinity mappings * @phba: pointer to lpfc hba data structure. + * @vectors: number of msix vectors allocated. * * The routine will figure out the CPU affinity assignment for every - * MSI-X vector allocated for the HBA. The hba_eq_hdl will be updated - * with a pointer to the CPU mask that defines ALL the CPUs this vector - * can be associated with. If the vector can be unquely associated with - * a single CPU, that CPU will be recorded in hba_eq_hdl[index].cpu. + * MSI-X vector allocated for the HBA. * In addition, the CPU to IO channel mapping will be calculated * and the phba->sli4_hba.cpu_map array will reflect this. */ static void -lpfc_cpu_affinity_check(struct lpfc_hba *phba) +lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) { + int i, j, idx, phys_id; + int max_phys_id, min_phys_id; + int max_core_id, min_core_id; struct lpfc_vector_map_info *cpup; - int cpu, idx; + int cpu, eqidx, hdwqidx, isr_avail; #ifdef CONFIG_X86 struct cpuinfo_x86 *cpuinfo; #endif @@ -10471,6 +10729,12 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba) (sizeof(struct lpfc_vector_map_info) * phba->sli4_hba.num_present_cpu)); + max_phys_id = 0; + min_phys_id = 0xffff; + max_core_id = 0; + min_core_id = 0xffff; + phys_id = 0; + /* Update CPU map with physical id and core id of each CPU */ cpup = phba->sli4_hba.cpu_map; for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { @@ -10478,34 +10742,91 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba) cpuinfo = &cpu_data(cpu); cpup->phys_id = cpuinfo->phys_proc_id; cpup->core_id = cpuinfo->cpu_core_id; + cpup->hyper = lpfc_find_hyper(phba, cpu, + cpup->phys_id, cpup->core_id); #else /* No distinction between CPUs for other platforms */ cpup->phys_id = 0; - cpup->core_id = 0; + cpup->core_id = cpu; + cpup->hyper = 0; #endif + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "3328 CPU physid %d coreid %d\n", cpup->phys_id, cpup->core_id); + + if (cpup->phys_id > max_phys_id) + max_phys_id = cpup->phys_id; + if (cpup->phys_id < min_phys_id) + min_phys_id = cpup->phys_id; + + if (cpup->core_id > max_core_id) + max_core_id = cpup->core_id; + if (cpup->core_id < min_core_id) + min_core_id = cpup->core_id; + cpup++; } - for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { - cpup = &phba->sli4_hba.cpu_map[idx]; - cpup->irq = pci_irq_vector(phba->pcidev, idx); + /* + * If the number of IRQ vectors == number of CPUs, + * mapping is pretty simple: 1 to 1. + * This is the desired path if NVME is enabled. + */ + if (vectors == phba->sli4_hba.num_present_cpu) { + cpup = phba->sli4_hba.cpu_map; + for (idx = 0; idx < vectors; idx++) { + cpup->eq = idx; + cpup->hdwq = idx; + cpup->irq = pci_irq_vector(phba->pcidev, idx); - /* For now assume vector N maps to CPU N */ - irq_set_affinity_hint(cpup->irq, get_cpu_mask(idx)); - cpup->hdwq = idx; + /* Now affinitize to the selected CPU */ + irq_set_affinity_hint( + pci_irq_vector(phba->pcidev, idx), + get_cpu_mask(idx)); + irq_set_status_flags(cpup->irq, IRQ_NO_BALANCING); - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "3336 Set Affinity: CPU %d " - "hdwq %d irq %d\n", - cpu, cpup->hdwq, cpup->irq); + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3336 Set Affinity: CPU %d " + "EQ %d irq %d\n", + idx, cpup->eq, + pci_irq_vector(phba->pcidev, idx)); + cpup++; + } + return; + } + + idx = 0; + isr_avail = 1; + eqidx = 0; + hdwqidx = 0; + + /* Mapping is more complicated for this case. Hardware Queues are + * assigned in a "ping pong" fashion, ping pong-ing between the + * available phys_id's. + */ + while (idx < phba->sli4_hba.num_present_cpu) { + for (i = min_core_id; i <= max_core_id; i++) { + for (j = min_phys_id; j <= max_phys_id; j++) { + cpup = lpfc_find_cpu_map(phba, j, i, hdwqidx, + eqidx, isr_avail); + if (!cpup) + continue; + idx++; + hdwqidx++; + if (hdwqidx >= phba->cfg_hdw_queue) + hdwqidx = 0; + eqidx++; + if (eqidx >= phba->cfg_irq_chann) { + isr_avail = 0; + eqidx = 0; + } + } + } } return; } - /** * lpfc_sli4_enable_msix - Enable MSI-X interrupt mode to SLI-4 device * @phba: pointer to lpfc hba data structure. @@ -10524,7 +10845,7 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) char *name; /* Set up MSI-X multi-message vectors */ - vectors = phba->cfg_hdw_queue; + vectors = phba->cfg_irq_chann; rc = pci_alloc_irq_vectors(phba->pcidev, (phba->nvmet_support) ? 1 : 2, @@ -10545,7 +10866,6 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) phba->sli4_hba.hba_eq_hdl[index].idx = index; phba->sli4_hba.hba_eq_hdl[index].phba = phba; - atomic_set(&phba->sli4_hba.hba_eq_hdl[index].hba_eq_in_use, 1); rc = request_irq(pci_irq_vector(phba->pcidev, index), &lpfc_sli4_hba_intr_handler, 0, name, @@ -10558,17 +10878,16 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) } } - if (vectors != phba->cfg_hdw_queue) { + if (vectors != phba->cfg_irq_chann) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3238 Reducing IO channels to match number of " "MSI-X vectors, requested %d got %d\n", - phba->cfg_hdw_queue, vectors); - if (phba->cfg_hdw_queue > vectors) - phba->cfg_hdw_queue = vectors; + phba->cfg_irq_chann, vectors); + if (phba->cfg_irq_chann > vectors) + phba->cfg_irq_chann = vectors; if (phba->cfg_nvmet_mrq > vectors) phba->cfg_nvmet_mrq = vectors; } - lpfc_cpu_affinity_check(phba); return rc; @@ -10623,7 +10942,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba) return rc; } - for (index = 0; index < phba->cfg_hdw_queue; index++) { + for (index = 0; index < phba->cfg_irq_chann; index++) { phba->sli4_hba.hba_eq_hdl[index].idx = index; phba->sli4_hba.hba_eq_hdl[index].phba = phba; } @@ -10688,11 +11007,10 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) phba->intr_type = INTx; intr_mode = 0; - for (idx = 0; idx < phba->cfg_hdw_queue; idx++) { + for (idx = 0; idx < phba->cfg_irq_chann; idx++) { eqhdl = &phba->sli4_hba.hba_eq_hdl[idx]; eqhdl->idx = idx; eqhdl->phba = phba; - atomic_set(&eqhdl->hba_eq_in_use, 1); } } } @@ -10716,7 +11034,7 @@ lpfc_sli4_disable_intr(struct lpfc_hba *phba) int index; /* Free up MSI-X multi-message vectors */ - for (index = 0; index < phba->cfg_hdw_queue; index++) { + for (index = 0; index < phba->cfg_irq_chann; index++) { irq_set_affinity_hint( pci_irq_vector(phba->pcidev, index), NULL); @@ -12092,12 +12410,13 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) } /* Default to single EQ for non-MSI-X */ if (phba->intr_type != MSIX) { - phba->cfg_hdw_queue = 1; + phba->cfg_irq_chann = 1; if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { if (phba->nvmet_support) phba->cfg_nvmet_mrq = 1; } } + lpfc_cpu_affinity_check(phba, phba->cfg_irq_chann); /* Create SCSI host to the physical port */ error = lpfc_create_shost(phba); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index c9aacd56a449..9480257c5143 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -239,7 +239,7 @@ lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport, if (qidx) { str = "IO "; /* IO queue */ qhandle->index = ((qidx - 1) % - vport->phba->cfg_hdw_queue); + lpfc_nvme_template.max_hw_queues); } else { str = "ADM"; /* Admin queue */ qhandle->index = qidx; @@ -1546,14 +1546,12 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, } } + /* Lookup Hardware Queue index based on fcp_io_sched module parameter */ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) { idx = lpfc_queue_info->index; } else { cpu = smp_processor_id(); - if (cpu < phba->cfg_hdw_queue) - idx = cpu; - else - idx = cpu % phba->cfg_hdw_queue; + idx = phba->sli4_hba.cpu_map[cpu].hdwq; } lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp, idx, expedite); @@ -2060,7 +2058,13 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) * allocate + 3, one for cmd, one for rsp and one for this alignment */ lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1; - lpfc_nvme_template.max_hw_queues = phba->cfg_hdw_queue; + + /* Advertise how many hw queues we support based on fcp_io_sched */ + if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) + lpfc_nvme_template.max_hw_queues = phba->cfg_hdw_queue; + else + lpfc_nvme_template.max_hw_queues = + phba->sli4_hba.num_present_cpu; /* localport is allocated from the stack, but the registration * call allocates heap memory as well as the private area. @@ -2554,6 +2558,8 @@ lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba) * WQEs have been removed from the txcmplqs. */ for (i = 0; i < phba->cfg_hdw_queue; i++) { + if (!phba->sli4_hba.hdwq[i].nvme_wq) + continue; pring = phba->sli4_hba.hdwq[i].nvme_wq->pring; if (!pring) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 7b22cc995d7f..a827520789f1 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -692,10 +692,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, tag = blk_mq_unique_tag(cmnd->request); idx = blk_mq_unique_tag_to_hwq(tag); } else { - if (cpu < phba->cfg_hdw_queue) - idx = cpu; - else - idx = cpu % phba->cfg_hdw_queue; + idx = phba->sli4_hba.cpu_map[cpu].hdwq; } lpfc_cmd = lpfc_get_io_buf(phba, ndlp, idx, @@ -3650,6 +3647,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct Scsi_Host *shost; int idx; uint32_t logit = LOG_FCP; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + int cpu; +#endif /* Sanity check on return of outstanding command */ cmd = lpfc_cmd->pCmd; @@ -3660,6 +3660,13 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, if (phba->sli4_hba.hdwq) phba->sli4_hba.hdwq[idx].scsi_cstat.io_cmpls++; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->cpucheck_on & LPFC_CHECK_SCSI_IO) { + cpu = smp_processor_id(); + if (cpu < LPFC_CHECK_CPU_CNT) + phba->sli4_hba.hdwq[idx].cpucheck_cmpl_io[cpu]++; + } +#endif shost = cmd->device->host; lpfc_cmd->result = (pIocbOut->iocb.un.ulpWord[4] & IOERR_PARAM_MASK); @@ -4336,6 +4343,9 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) struct lpfc_io_buf *lpfc_cmd; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); int err, idx; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + int cpu; +#endif rdata = lpfc_rport_data_from_scsi_device(cmnd->device); @@ -4450,6 +4460,16 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp); +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->cpucheck_on & LPFC_CHECK_SCSI_IO) { + cpu = smp_processor_id(); + if (cpu < LPFC_CHECK_CPU_CNT) { + struct lpfc_sli4_hdw_queue *hdwq = + &phba->sli4_hba.hdwq[lpfc_cmd->hdwq_no]; + hdwq->cpucheck_xmt_io[cpu]++; + } + } +#endif err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB); if (err) { diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index c0f0adccdea7..0cc81321643d 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -5586,7 +5586,7 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) LPFC_QUEUE_REARM); } - for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) + for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) sli4_hba->sli4_eq_release(qp[qidx].hba_eq, LPFC_QUEUE_REARM); } @@ -7878,7 +7878,7 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba) /* Find the eq associated with the mcq */ if (sli4_hba->hdwq) - for (eqidx = 0; eqidx < phba->cfg_hdw_queue; eqidx++) + for (eqidx = 0; eqidx < phba->cfg_irq_chann; eqidx++) if (sli4_hba->hdwq[eqidx].hba_eq->queue_id == sli4_hba->mbx_cq->assoc_qid) { fpeq = sli4_hba->hdwq[eqidx].hba_eq; @@ -10058,12 +10058,9 @@ int lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, struct lpfc_iocbq *piocb, uint32_t flag) { - struct lpfc_hba_eq_hdl *hba_eq_hdl; struct lpfc_sli_ring *pring; - struct lpfc_queue *fpeq; - struct lpfc_eqe *eqe; unsigned long iflags; - int rc, idx; + int rc; if (phba->sli_rev == LPFC_SLI_REV4) { pring = lpfc_sli4_calc_ring(phba, piocb); @@ -10073,34 +10070,6 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, spin_lock_irqsave(&pring->ring_lock, iflags); rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag); spin_unlock_irqrestore(&pring->ring_lock, iflags); - - if (lpfc_fcp_look_ahead && (piocb->iocb_flag & LPFC_IO_FCP)) { - idx = piocb->hba_wqidx; - hba_eq_hdl = &phba->sli4_hba.hba_eq_hdl[idx]; - - if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use)) { - - /* Get associated EQ with this index */ - fpeq = phba->sli4_hba.hdwq[idx].hba_eq; - - /* Turn off interrupts from this EQ */ - phba->sli4_hba.sli4_eq_clr_intr(fpeq); - - /* - * Process all the events on FCP EQ - */ - while ((eqe = lpfc_sli4_eq_get(fpeq))) { - lpfc_sli4_hba_handle_eqe(phba, - eqe, idx); - fpeq->EQ_processed++; - } - - /* Always clear and re-arm the EQ */ - phba->sli4_hba.sli4_eq_release(fpeq, - LPFC_QUEUE_REARM); - } - atomic_inc(&hba_eq_hdl->hba_eq_in_use); - } } else { /* For now, SLI2/3 will still use hbalock */ spin_lock_irqsave(&phba->hbalock, iflags); @@ -13651,7 +13620,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, /* Save EQ associated with this CQ */ cq->assoc_qp = speq; - if (!queue_work(phba->wq, &cq->spwork)) + if (!queue_work_on(cq->chann, phba->wq, &cq->spwork)) lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0390 Cannot schedule soft IRQ " "for CQ eqcqid=%d, cqid=%d on CPU %d\n", @@ -14057,18 +14026,11 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, /* Get the reference to the corresponding CQ */ cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); - /* First check for NVME/SCSI completion */ - if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && - (cqid == phba->sli4_hba.hdwq[qidx].nvme_cq_map)) { - /* Process NVME / NVMET command completion */ - cq = phba->sli4_hba.hdwq[qidx].nvme_cq; - goto process_cq; - } - - if (cqid == phba->sli4_hba.hdwq[qidx].fcp_cq_map) { - /* Process FCP command completion */ - cq = phba->sli4_hba.hdwq[qidx].fcp_cq; - goto process_cq; + /* Use the fast lookup method first */ + if (cqid <= phba->sli4_hba.cq_max) { + cq = phba->sli4_hba.cq_lookup[cqid]; + if (cq) + goto work_cq; } /* Next check for NVMET completion */ @@ -14103,9 +14065,7 @@ process_cq: return; } - /* Save EQ associated with this CQ */ - cq->assoc_qp = phba->sli4_hba.hdwq[qidx].hba_eq; - +work_cq: if (!queue_work_on(cq->chann, phba->wq, &cq->irqwork)) lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0363 Cannot schedule soft IRQ " @@ -14233,15 +14193,6 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) if (unlikely(!fpeq)) return IRQ_NONE; - if (lpfc_fcp_look_ahead) { - if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use)) - phba->sli4_hba.sli4_eq_clr_intr(fpeq); - else { - atomic_inc(&hba_eq_hdl->hba_eq_in_use); - return IRQ_NONE; - } - } - /* Check device state for handling interrupt */ if (unlikely(lpfc_intr_state_check(phba))) { /* Check again for link_state with lock held */ @@ -14250,8 +14201,6 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) /* Flush, clear interrupt, and rearm the EQ */ lpfc_sli4_eq_flush(phba, fpeq); spin_unlock_irqrestore(&phba->hbalock, iflag); - if (lpfc_fcp_look_ahead) - atomic_inc(&hba_eq_hdl->hba_eq_in_use); return IRQ_NONE; } @@ -14274,12 +14223,6 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) if (unlikely(ecount == 0)) { fpeq->EQ_no_entry++; - - if (lpfc_fcp_look_ahead) { - atomic_inc(&hba_eq_hdl->hba_eq_in_use); - return IRQ_NONE; - } - if (phba->intr_type == MSIX) /* MSI-X treated interrupt served as no EQ share INT */ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, @@ -14289,9 +14232,6 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) return IRQ_NONE; } - if (lpfc_fcp_look_ahead) - atomic_inc(&hba_eq_hdl->hba_eq_in_use); - return IRQ_HANDLED; } /* lpfc_sli4_fp_intr_handler */ @@ -14329,7 +14269,7 @@ lpfc_sli4_intr_handler(int irq, void *dev_id) /* * Invoke fast-path host attention interrupt handling as appropriate. */ - for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { + for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) { hba_irq_rc = lpfc_sli4_hba_intr_handler(irq, &phba->sli4_hba.hba_eq_hdl[qidx]); if (hba_irq_rc == IRQ_HANDLED) @@ -14516,7 +14456,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, union lpfc_sli4_cfg_shdr *shdr; uint16_t dmult; - if (startq >= phba->cfg_hdw_queue) + if (startq >= phba->cfg_irq_chann) return 0; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -14530,7 +14470,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, eq_delay = &mbox->u.mqe.un.eq_delay; /* Calculate delay multiper from maximum interrupt per second */ - result = imax / phba->cfg_hdw_queue; + result = imax / phba->cfg_irq_chann; if (result > LPFC_DMULT_CONST || result == 0) dmult = 0; else @@ -14539,7 +14479,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, dmult = LPFC_DMULT_MAX; cnt = 0; - for (qidx = startq; qidx < phba->cfg_hdw_queue; qidx++) { + for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) { eq = phba->sli4_hba.hdwq[qidx].hba_eq; if (!eq) continue; @@ -14557,7 +14497,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, val = phba->cfg_fcp_imax; if (val) { /* First, interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / phba->cfg_hdw_queue; + val = phba->cfg_fcp_imax / phba->cfg_irq_chann; /* us delay between each interrupt */ val = LPFC_SEC_TO_USEC / val; @@ -14852,10 +14792,13 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, cq->subtype = subtype; cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response); cq->assoc_qid = eq->queue_id; + cq->assoc_qp = eq; cq->host_index = 0; cq->hba_index = 0; cq->entry_repost = LPFC_CQ_REPOST; + if (cq->queue_id > phba->sli4_hba.cq_max) + phba->sli4_hba.cq_max = cq->queue_id; out: mempool_free(mbox, phba->mbox_mem_pool); return status; @@ -15061,6 +15004,7 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, cq->type = type; cq->subtype = subtype; cq->assoc_qid = eq->queue_id; + cq->assoc_qp = eq; cq->host_index = 0; cq->hba_index = 0; cq->entry_repost = LPFC_CQ_REPOST; @@ -15101,6 +15045,8 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, for (idx = 0; idx < numcq; idx++) { cq = cqp[idx]; cq->queue_id = rc + idx; + if (cq->queue_id > phba->sli4_hba.cq_max) + phba->sli4_hba.cq_max = cq->queue_id; } out: @@ -19664,7 +19610,8 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, /* NVME_LS and NVME_LS ABTS requests. */ if (pwqe->iocb_flag & LPFC_IO_NVME_LS) { pring = phba->sli4_hba.nvmels_wq->pring; - spin_lock_irqsave(&pring->ring_lock, iflags); + lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags, + qp, wq_access); sglq = __lpfc_sli_get_els_sglq(phba, pwqe); if (!sglq) { spin_unlock_irqrestore(&pring->ring_lock, iflags); @@ -19697,7 +19644,8 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->nvme_cq_map); - spin_lock_irqsave(&pring->ring_lock, iflags); + lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags, + qp, wq_access); ret = lpfc_sli4_wq_put(wq, wqe); if (ret) { spin_unlock_irqrestore(&pring->ring_lock, iflags); @@ -19724,7 +19672,8 @@ lpfc_sli4_issue_wqe(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, pwqe->sli4_xritag); bf_set(wqe_cqid, &wqe->generic.wqe_com, qp->nvme_cq_map); - spin_lock_irqsave(&pring->ring_lock, iflags); + lpfc_qp_spin_lock_irqsave(&pring->ring_lock, iflags, + qp, wq_access); ret = lpfc_sli4_wq_put(wq, wqe); if (ret) { spin_unlock_irqrestore(&pring->ring_lock, iflags); @@ -19872,18 +19821,20 @@ void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid) { struct lpfc_pbl_pool *pbl_pool; struct lpfc_pvt_pool *pvt_pool; + struct lpfc_sli4_hdw_queue *qp; struct lpfc_io_buf *lpfc_ncmd; struct lpfc_io_buf *lpfc_ncmd_next; unsigned long iflag; struct list_head tmp_list; u32 tmp_count; - pbl_pool = &phba->sli4_hba.hdwq[hwqid].p_multixri_pool->pbl_pool; - pvt_pool = &phba->sli4_hba.hdwq[hwqid].p_multixri_pool->pvt_pool; + qp = &phba->sli4_hba.hdwq[hwqid]; + pbl_pool = &qp->p_multixri_pool->pbl_pool; + pvt_pool = &qp->p_multixri_pool->pvt_pool; tmp_count = 0; - spin_lock_irqsave(&pbl_pool->lock, iflag); - spin_lock(&pvt_pool->lock); + lpfc_qp_spin_lock_irqsave(&pbl_pool->lock, iflag, qp, mv_to_pub_pool); + lpfc_qp_spin_lock(&pvt_pool->lock, qp, mv_from_pvt_pool); if (pvt_pool->count > pvt_pool->low_watermark) { /* Step 1: move (all - low_watermark) from pvt_pool @@ -19936,7 +19887,8 @@ void lpfc_move_xri_pvt_to_pbl(struct lpfc_hba *phba, u32 hwqid) * false - if the specified pbl_pool is empty or locked by someone else **/ static bool -_lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, struct lpfc_pbl_pool *pbl_pool, +_lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, struct lpfc_sli4_hdw_queue *qp, + struct lpfc_pbl_pool *pbl_pool, struct lpfc_pvt_pool *pvt_pool, u32 count) { struct lpfc_io_buf *lpfc_ncmd; @@ -19948,7 +19900,7 @@ _lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, struct lpfc_pbl_pool *pbl_pool, if (ret) { if (pbl_pool->count) { /* Move a batch of XRIs from public to private pool */ - spin_lock(&pvt_pool->lock); + lpfc_qp_spin_lock(&pvt_pool->lock, qp, mv_to_pvt_pool); list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, &pbl_pool->list, @@ -19990,16 +19942,18 @@ void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 count) struct lpfc_multixri_pool *next_multixri_pool; struct lpfc_pvt_pool *pvt_pool; struct lpfc_pbl_pool *pbl_pool; + struct lpfc_sli4_hdw_queue *qp; u32 next_hwqid; u32 hwq_count; int ret; - multixri_pool = phba->sli4_hba.hdwq[hwqid].p_multixri_pool; + qp = &phba->sli4_hba.hdwq[hwqid]; + multixri_pool = qp->p_multixri_pool; pvt_pool = &multixri_pool->pvt_pool; pbl_pool = &multixri_pool->pbl_pool; /* Check if local pbl_pool is available */ - ret = _lpfc_move_xri_pbl_to_pvt(phba, pbl_pool, pvt_pool, count); + ret = _lpfc_move_xri_pbl_to_pvt(phba, qp, pbl_pool, pvt_pool, count); if (ret) { #ifdef LPFC_MXP_STAT multixri_pool->local_pbl_hit_count++; @@ -20022,7 +19976,7 @@ void lpfc_move_xri_pbl_to_pvt(struct lpfc_hba *phba, u32 hwqid, u32 count) /* Check if the public free xri pool is available */ ret = _lpfc_move_xri_pbl_to_pvt( - phba, pbl_pool, pvt_pool, count); + phba, qp, pbl_pool, pvt_pool, count); /* Exit while-loop if success or all hwqid are checked */ } while (!ret && next_hwqid != multixri_pool->rrb_next_hwqid); @@ -20138,20 +20092,23 @@ void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd, if ((pvt_pool->count < pvt_pool->low_watermark) || (xri_owned < xri_limit && pvt_pool->count < pvt_pool->high_watermark)) { - spin_lock_irqsave(&pvt_pool->lock, iflag); + lpfc_qp_spin_lock_irqsave(&pvt_pool->lock, iflag, + qp, free_pvt_pool); list_add_tail(&lpfc_ncmd->list, &pvt_pool->list); pvt_pool->count++; spin_unlock_irqrestore(&pvt_pool->lock, iflag); } else { - spin_lock_irqsave(&pbl_pool->lock, iflag); + lpfc_qp_spin_lock_irqsave(&pbl_pool->lock, iflag, + qp, free_pub_pool); list_add_tail(&lpfc_ncmd->list, &pbl_pool->list); pbl_pool->count++; spin_unlock_irqrestore(&pbl_pool->lock, iflag); } } else { - spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag); + lpfc_qp_spin_lock_irqsave(&qp->io_buf_list_put_lock, iflag, + qp, free_xri); list_add_tail(&lpfc_ncmd->list, &qp->lpfc_io_buf_list_put); qp->put_io_bufs++; @@ -20174,6 +20131,7 @@ void lpfc_release_io_buf(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd, **/ static struct lpfc_io_buf * lpfc_get_io_buf_from_private_pool(struct lpfc_hba *phba, + struct lpfc_sli4_hdw_queue *qp, struct lpfc_pvt_pool *pvt_pool, struct lpfc_nodelist *ndlp) { @@ -20181,7 +20139,7 @@ lpfc_get_io_buf_from_private_pool(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_ncmd_next; unsigned long iflag; - spin_lock_irqsave(&pvt_pool->lock, iflag); + lpfc_qp_spin_lock_irqsave(&pvt_pool->lock, iflag, qp, alloc_pvt_pool); list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, &pvt_pool->list, list) { if (lpfc_test_rrq_active( @@ -20276,7 +20234,7 @@ lpfc_get_io_buf_from_multixri_pools(struct lpfc_hba *phba, lpfc_move_xri_pbl_to_pvt(phba, hwqid, XRI_BATCH); /* Get one XRI from private free xri pool */ - lpfc_ncmd = lpfc_get_io_buf_from_private_pool(phba, pvt_pool, ndlp); + lpfc_ncmd = lpfc_get_io_buf_from_private_pool(phba, qp, pvt_pool, ndlp); if (lpfc_ncmd) { lpfc_ncmd->hdwq = qp; @@ -20349,11 +20307,13 @@ struct lpfc_io_buf *lpfc_get_io_buf(struct lpfc_hba *phba, lpfc_cmd = lpfc_get_io_buf_from_multixri_pools( phba, ndlp, hwqid, expedite); else { - spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag); + lpfc_qp_spin_lock_irqsave(&qp->io_buf_list_get_lock, iflag, + qp, alloc_xri_get); if (qp->get_io_bufs > LPFC_NVME_EXPEDITE_XRICNT || expedite) lpfc_cmd = lpfc_io_buf(phba, ndlp, hwqid); if (!lpfc_cmd) { - spin_lock(&qp->io_buf_list_put_lock); + lpfc_qp_spin_lock(&qp->io_buf_list_put_lock, + qp, alloc_xri_put); list_splice(&qp->lpfc_io_buf_list_put, &qp->lpfc_io_buf_list_get); qp->get_io_bufs += qp->put_io_bufs; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index f5e58cd4c6ac..c381f2cb4909 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -41,7 +41,7 @@ /* Multi-queue arrangement for FCP EQ/CQ/WQ tuples */ #define LPFC_HBA_HDWQ_MIN 0 -#define LPFC_HBA_HDWQ_MAX 64 +#define LPFC_HBA_HDWQ_MAX 128 #define LPFC_HBA_HDWQ_DEF 0 /* Common buffer size to accomidate SCSI and NVME IO buffers */ @@ -166,16 +166,19 @@ struct lpfc_queue { uint32_t assoc_qid; /* Queue ID associated with, for CQ/WQ/MQ */ uint32_t host_index; /* The host's index for putting or getting */ uint32_t hba_index; /* The last known hba index for get or put */ + uint32_t q_mode; struct lpfc_sli_ring *pring; /* ptr to io ring associated with q */ struct lpfc_rqb *rqbp; /* ptr to RQ buffers */ - uint32_t q_mode; uint16_t page_count; /* Number of pages allocated for this queue */ uint16_t page_size; /* size of page allocated for this queue */ #define LPFC_EXPANDED_PAGE_SIZE 16384 #define LPFC_DEFAULT_PAGE_SIZE 4096 - uint16_t chann; /* IO channel this queue is associated with */ + uint16_t chann; /* Hardware Queue association WQ/CQ */ + /* CPU affinity for EQ */ +#define LPFC_FIND_BY_EQ 0 +#define LPFC_FIND_BY_HDWQ 1 uint8_t db_format; #define LPFC_DB_RING_FORMAT 0x01 #define LPFC_DB_LIST_FORMAT 0x02 @@ -431,11 +434,6 @@ struct lpfc_hba_eq_hdl { uint32_t idx; char handler_name[LPFC_SLI4_HANDLER_NAME_SZ]; struct lpfc_hba *phba; - atomic_t hba_eq_in_use; - struct cpumask *cpumask; - /* CPU affinitsed to or 0xffffffff if multiple */ - uint32_t cpu; -#define LPFC_MULTI_CPU_AFFINITY 0xffffffff }; /*BB Credit recovery value*/ @@ -529,7 +527,9 @@ struct lpfc_vector_map_info { uint16_t phys_id; uint16_t core_id; uint16_t irq; + uint16_t eq; uint16_t hdwq; + uint16_t hyper; }; #define LPFC_VECTOR_MAP_EMPTY 0xffff @@ -593,6 +593,21 @@ struct lpfc_fc4_ctrl_stat { u32 io_cmpls; }; +#ifdef LPFC_HDWQ_LOCK_STAT +struct lpfc_lock_stat { + uint32_t alloc_xri_get; + uint32_t alloc_xri_put; + uint32_t free_xri; + uint32_t wq_access; + uint32_t alloc_pvt_pool; + uint32_t mv_from_pvt_pool; + uint32_t mv_to_pub_pool; + uint32_t mv_to_pvt_pool; + uint32_t free_pub_pool; + uint32_t free_pvt_pool; +}; +#endif + /* SLI4 HBA data structure entries */ struct lpfc_sli4_hdw_queue { /* Pointers to the constructed SLI4 queues */ @@ -626,6 +641,9 @@ struct lpfc_sli4_hdw_queue { /* FC-4 Stats counters */ struct lpfc_fc4_ctrl_stat nvme_cstat; struct lpfc_fc4_ctrl_stat scsi_cstat; +#ifdef LPFC_HDWQ_LOCK_STAT + struct lpfc_lock_stat lock_conflict; +#endif #ifdef CONFIG_SCSI_LPFC_DEBUG_FS #define LPFC_CHECK_CPU_CNT 128 @@ -635,6 +653,34 @@ struct lpfc_sli4_hdw_queue { #endif }; +#ifdef LPFC_HDWQ_LOCK_STAT +/* compile time trylock stats */ +#define lpfc_qp_spin_lock_irqsave(lock, flag, qp, lstat) \ + { \ + int only_once = 1; \ + while (spin_trylock_irqsave(lock, flag) == 0) { \ + if (only_once) { \ + only_once = 0; \ + qp->lock_conflict.lstat++; \ + } \ + } \ + } +#define lpfc_qp_spin_lock(lock, qp, lstat) \ + { \ + int only_once = 1; \ + while (spin_trylock(lock) == 0) { \ + if (only_once) { \ + only_once = 0; \ + qp->lock_conflict.lstat++; \ + } \ + } \ + } +#else +#define lpfc_qp_spin_lock_irqsave(lock, flag, qp, lstat) \ + spin_lock_irqsave(lock, flag) +#define lpfc_qp_spin_lock(lock, qp, lstat) spin_lock(lock) +#endif + struct lpfc_sli4_hba { void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for * config space registers @@ -764,6 +810,8 @@ struct lpfc_sli4_hba { uint16_t nvmet_xri_cnt; uint16_t nvmet_io_wait_cnt; uint16_t nvmet_io_wait_total; + uint16_t cq_max; + struct lpfc_queue **cq_lookup; struct list_head lpfc_els_sgl_list; struct list_head lpfc_abts_els_sgl_list; spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ From cb733e358787b7386bd1aeec088a35e03c53da3b Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:32 -0800 Subject: [PATCH 1217/4277] scsi: lpfc: cleanup: convert eq_delay to usdelay Review of the eq coalescing logic showed the code was a bit fragmented. Sometimes it would save/set via an interrupt max value, while in others it would do so via a usdelay. There were also two places changing eq delay, one place that issued mailbox commands, and another that changed via register writes if supported. Clean this up by: - Standardizing the operation of lpfc_modify_hba_eq_delay() routine so that it is always told of a us delay to impose. The routine then chooses the best way to set that - via register or via mbx. - Rather than two value types stored in eq->q_mode (usdelay if change via register, imax if change via mbox) - q_mode always contains usdelay. Before any value change, old vs new value is compared and only if different is a change done. - Revised the dmult calculation. dmult is not set based on overall imax divided by hardware queues - instead imax applies to a single cpu and the value will be replicated to all cpus. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 8 ++- drivers/scsi/lpfc/lpfc_init.c | 9 ++- drivers/scsi/lpfc/lpfc_sli.c | 120 ++++++++++++++++++++-------------- drivers/scsi/lpfc/lpfc_sli4.h | 4 +- 4 files changed, 86 insertions(+), 55 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index fc7f80d68638..ed8caeefe3a2 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -4935,6 +4935,7 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; struct lpfc_hba *phba = vport->phba; + uint32_t usdelay; int val = 0, i; /* fcp_imax is only valid for SLI4 */ @@ -4958,9 +4959,14 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, phba->cfg_fcp_imax = (uint32_t)val; phba->initial_imax = phba->cfg_fcp_imax; + if (phba->cfg_fcp_imax) + usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax; + else + usdelay = 0; + for (i = 0; i < phba->cfg_irq_chann; i += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, i, LPFC_MAX_EQ_DELAY_EQID_CNT, - val); + usdelay); return strlen(buf); } diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 145c08f112a3..22aec1a9b689 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -9336,7 +9336,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) struct lpfc_sli4_hdw_queue *qp; LPFC_MBOXQ_t *mboxq; int qidx; - uint32_t length; + uint32_t length, usdelay; int rc = -ENOMEM; /* Check for dual-ULP support */ @@ -9643,10 +9643,15 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.dat_rq->queue_id, phba->sli4_hba.els_cq->queue_id); + if (phba->cfg_fcp_imax) + usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax; + else + usdelay = 0; + for (qidx = 0; qidx < phba->cfg_irq_chann; qidx += LPFC_MAX_EQ_DELAY_EQID_CNT) lpfc_modify_hba_eq_delay(phba, qidx, LPFC_MAX_EQ_DELAY_EQID_CNT, - phba->cfg_fcp_imax); + usdelay); if (phba->sli4_hba.cq_max) { kfree(phba->sli4_hba.cq_lookup); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 0cc81321643d..a3477cecb0c9 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -14425,43 +14425,86 @@ lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset) } /** - * lpfc_modify_hba_eq_delay - Modify Delay Multiplier on FCP EQs - * @phba: HBA structure that indicates port to create a queue on. - * @startq: The starting FCP EQ to modify + * lpfc_modify_hba_eq_delay - Modify Delay Multiplier on EQs + * @phba: HBA structure that EQs are on. + * @startq: The starting EQ index to modify + * @numq: The number of EQs (consecutive indexes) to modify + * @usdelay: amount of delay * - * This function sends an MODIFY_EQ_DELAY mailbox command to the HBA. - * The command allows up to LPFC_MAX_EQ_DELAY_EQID_CNT EQ ID's to be - * updated in one mailbox command. + * This function revises the EQ delay on 1 or more EQs. The EQ delay + * is set either by writing to a register (if supported by the SLI Port) + * or by mailbox command. The mailbox command allows several EQs to be + * updated at once. * - * The @phba struct is used to send mailbox command to HBA. The @startq - * is used to get the starting FCP EQ to change. - * This function is asynchronous and will wait for the mailbox - * command to finish before continuing. + * The @phba struct is used to send a mailbox command to HBA. The @startq + * is used to get the starting EQ index to change. The @numq value is + * used to specify how many consecutive EQ indexes, starting at EQ index, + * are to be changed. This function is asynchronous and will wait for any + * mailbox commands to finish before returning. * - * On success this function will return a zero. If unable to allocate enough - * memory this function will return -ENOMEM. If the queue create mailbox command - * fails this function will return -ENXIO. + * On success this function will return a zero. If unable to allocate + * enough memory this function will return -ENOMEM. If a mailbox command + * fails this function will return -ENXIO. Note: on ENXIO, some EQs may + * have had their delay multipler changed. **/ -int +void lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, - uint32_t numq, uint32_t imax) + uint32_t numq, uint32_t usdelay) { struct lpfc_mbx_modify_eq_delay *eq_delay; LPFC_MBOXQ_t *mbox; struct lpfc_queue *eq; - int cnt, rc, length, status = 0; + int cnt = 0, rc, length; uint32_t shdr_status, shdr_add_status; - uint32_t result, val; + uint32_t dmult; + struct lpfc_register reg_data; int qidx; union lpfc_sli4_cfg_shdr *shdr; - uint16_t dmult; if (startq >= phba->cfg_irq_chann) - return 0; + return; + + if (usdelay > 0xFFFF) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_FCP | LOG_NVME, + "6429 usdelay %d too large. Scaled down to " + "0xFFFF.\n", usdelay); + usdelay = 0xFFFF; + } + + /* set values by EQ_DELAY register if supported */ + if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) { + for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) { + eq = phba->sli4_hba.hdwq[qidx].hba_eq; + if (!eq) + continue; + + /* save value last set */ + eq->q_mode = usdelay; + + /* write register */ + reg_data.word0 = 0; + bf_set(lpfc_sliport_eqdelay_id, ®_data, + eq->queue_id); + bf_set(lpfc_sliport_eqdelay_delay, ®_data, usdelay); + writel(reg_data.word0, + phba->sli4_hba.u.if_type2.EQDregaddr); + + if (++cnt >= numq) + break; + } + + return; + } + + /* Otherwise, set values by mailbox cmd */ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); - if (!mbox) - return -ENOMEM; + if (!mbox) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_FCP | LOG_NVME, + "6428 Failed allocating mailbox cmd buffer." + " EQ delay was not set.\n"); + return; + } length = (sizeof(struct lpfc_mbx_modify_eq_delay) - sizeof(struct lpfc_sli4_cfg_mhdr)); lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, @@ -14470,44 +14513,22 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, eq_delay = &mbox->u.mqe.un.eq_delay; /* Calculate delay multiper from maximum interrupt per second */ - result = imax / phba->cfg_irq_chann; - if (result > LPFC_DMULT_CONST || result == 0) - dmult = 0; - else - dmult = LPFC_DMULT_CONST/result - 1; + dmult = (usdelay * LPFC_DMULT_CONST) / LPFC_SEC_TO_USEC; + if (dmult) + dmult--; if (dmult > LPFC_DMULT_MAX) dmult = LPFC_DMULT_MAX; - cnt = 0; for (qidx = startq; qidx < phba->cfg_irq_chann; qidx++) { eq = phba->sli4_hba.hdwq[qidx].hba_eq; if (!eq) continue; - eq->q_mode = imax; + eq->q_mode = usdelay; eq_delay->u.request.eq[cnt].eq_id = eq->queue_id; eq_delay->u.request.eq[cnt].phase = 0; eq_delay->u.request.eq[cnt].delay_multi = dmult; - cnt++; - /* q_mode is only used for auto_imax */ - if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) { - /* Use EQ Delay Register method for q_mode */ - - /* Convert for EQ Delay register */ - val = phba->cfg_fcp_imax; - if (val) { - /* First, interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / phba->cfg_irq_chann; - - /* us delay between each interrupt */ - val = LPFC_SEC_TO_USEC / val; - } - eq->q_mode = val; - } else { - eq->q_mode = imax; - } - - if (cnt >= numq) + if (++cnt >= numq) break; } eq_delay->u.request.num_eq = cnt; @@ -14525,10 +14546,9 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, "2512 MODIFY_EQ_DELAY mailbox failed with " "status x%x add_status x%x, mbx status x%x\n", shdr_status, shdr_add_status, rc); - status = -ENXIO; } mempool_free(mbox, phba->mbox_mem_pool); - return status; + return; } /** diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index c381f2cb4909..accccca3a027 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -956,8 +956,8 @@ struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t, uint32_t, uint32_t); void lpfc_sli4_queue_free(struct lpfc_queue *); int lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint32_t); -int lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, - uint32_t numq, uint32_t imax); +void lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, + uint32_t numq, uint32_t usdelay); int lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t, uint32_t); int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, From 32517fc0975bf8dd3967e43c2a6350f038a3af28 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:33 -0800 Subject: [PATCH 1218/4277] scsi: lpfc: Rework EQ/CQ processing to address interrupt coalescing When driving high iop counts, auto_imax coalescing kicks in and drives the performance to extremely small iops levels. There are two issues: 1) auto_imax is enabled by default. The auto algorithm, when iops gets high, divides the iops by the hdwq count and uses that value to calculate EQ_Delay. The EQ_Delay is set uniformly on all EQs whether they have load or not. The EQ_delay is only manipulated every 5s (a long time). Thus there were large 5s swings of no interrupt delay followed by large/maximum delay, before repeating. 2) When processing a CQ, the driver got mixed up on the rate of when to ring the doorbell to keep the chip appraised of the eqe or cqe consumption as well as how how long to sit in the thread and process queue entries. Currently, the driver capped its work at 64 entries (very small) and exited/rearmed the CQ. Thus, on heavy loads, additional overheads were taken to exit and re-enter the interrupt handler. Worse, if in the large/maximum coalescing windows,k it could be a while before getting back to servicing. The issues are corrected by the following: - A change in defaults. Auto_imax is turned OFF and fcp_imax is set to 0. Thus all interrupts are immediate. - Cleanup of field names and their meanings. Existing names were non-intuitive or used for duplicate things. - Added max_proc_limit field, to control the length of time the handlers would service completions. - Reworked EQ handling: Added common routine that walks eq, applying notify interval and max processing limits. Use queue_claimed to claim ownership of the queue while processing. Always rearm the queue whenever the common routine is called. Rework queue element processing, namely to eliminate hba_index vs host_index. Only one index is necessary. The queue entry can be marked invalid and the host_index updated immediately after eqe processing. After rework, xx_release routines are now DB write functions. Renamed the routines as such. Moved lpfc_sli4_eq_flush(), which does similar action, to same area. Replaced the 2 individual loops that walk an eq with a call to the common routine. Slightly revised lpfc_sli4_hba_handle_eqe() calling syntax. Added per-cpu counters to detect interrupt rates and scale interrupt coalescing values. - Reworked CQ handling: Added common routine that walks cq, applying notify interval and max processing limits. Use queue_claimed to claim ownership of the queue while processing. Always rearm the queue whenever the common routine is called. Rework queue element processing, namely to eliminate hba_index vs host_index. Only one index is necessary. The queue entry can be marked invalid and the host_index updated immediately after cqe processing. After rework, xx_release routines are now DB write functions. Renamed the routines as such. Replaced the 3 individual loops that walk a cq with a call to the common routine. Redefined lpfc_sli4_sp_handle_mcqe() to commong handler definition with queue reference. Add increment for mbox completion to handler. - Added a new module/sysfs attribute: lpfc_cq_max_proc_limit To allow dynamic changing of the CQ max_proc_limit value being used. Although this leaves an EQ as an immediate interrupt, that interrupt will only occur if a CQ bound to it is in an armed state and has cqe's to process. By staying in the cq processing routine longer, high loads will avoid generating more interrupts as they will only rearm as the processing thread exits. The immediately interrupt is also beneficial to idle or lower-processing CQ's as they get serviced immediately without being penalized by sharing an EQ with a more loaded CQ. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 25 +- drivers/scsi/lpfc/lpfc_attr.c | 141 +++++- drivers/scsi/lpfc/lpfc_debugfs.c | 22 +- drivers/scsi/lpfc/lpfc_hw4.h | 9 +- drivers/scsi/lpfc/lpfc_init.c | 205 ++++----- drivers/scsi/lpfc/lpfc_sli.c | 735 +++++++++++++++++-------------- drivers/scsi/lpfc/lpfc_sli4.h | 70 ++- 7 files changed, 730 insertions(+), 477 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 9fd2811ffa8b..0bc498172add 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -686,6 +686,7 @@ struct lpfc_hba { struct lpfc_sli4_hba sli4_hba; struct workqueue_struct *wq; + struct delayed_work eq_delay_work; struct lpfc_sli sli; uint8_t pci_dev_grp; /* lpfc PCI dev group: 0x0, 0x1, 0x2,... */ @@ -789,7 +790,6 @@ struct lpfc_hba { uint8_t nvmet_support; /* driver supports NVMET */ #define LPFC_NVMET_MAX_PORTS 32 uint8_t mds_diags_support; - uint32_t initial_imax; uint8_t bbcredit_support; uint8_t enab_exp_wqcq_pages; @@ -817,6 +817,8 @@ struct lpfc_hba { uint32_t cfg_use_msi; uint32_t cfg_auto_imax; uint32_t cfg_fcp_imax; + uint32_t cfg_cq_poll_threshold; + uint32_t cfg_cq_max_proc_limit; uint32_t cfg_fcp_cpu_map; uint32_t cfg_hdw_queue; uint32_t cfg_irq_chann; @@ -1084,7 +1086,6 @@ struct lpfc_hba { uint8_t temp_sensor_support; /* Fields used for heart beat. */ - unsigned long last_eqdelay_time; unsigned long last_completion_time; unsigned long skipped_hb; struct timer_list hb_tmofunc; @@ -1287,3 +1288,23 @@ lpfc_phba_elsring(struct lpfc_hba *phba) } return &phba->sli.sli3_ring[LPFC_ELS_RING]; } + +/** + * lpfc_sli4_mod_hba_eq_delay - update EQ delay + * @phba: Pointer to HBA context object. + * @q: The Event Queue to update. + * @delay: The delay value (in us) to be written. + * + **/ +static inline void +lpfc_sli4_mod_hba_eq_delay(struct lpfc_hba *phba, struct lpfc_queue *eq, + u32 delay) +{ + struct lpfc_register reg_data; + + reg_data.word0 = 0; + bf_set(lpfc_sliport_eqdelay_id, ®_data, eq->queue_id); + bf_set(lpfc_sliport_eqdelay_delay, ®_data, delay); + writel(reg_data.word0, phba->sli4_hba.u.if_type2.EQDregaddr); + eq->q_mode = delay; +} diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index ed8caeefe3a2..2864cb53b1e8 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -4935,6 +4935,7 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; struct lpfc_hba *phba = vport->phba; + struct lpfc_eq_intr_info *eqi; uint32_t usdelay; int val = 0, i; @@ -4956,8 +4957,18 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, if (val && (val < LPFC_MIN_IMAX || val > LPFC_MAX_IMAX)) return -EINVAL; + phba->cfg_auto_imax = (val) ? 0 : 1; + if (phba->cfg_fcp_imax && !val) { + queue_delayed_work(phba->wq, &phba->eq_delay_work, + msecs_to_jiffies(LPFC_EQ_DELAY_MSECS)); + + for_each_present_cpu(i) { + eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i); + eqi->icnt = 0; + } + } + phba->cfg_fcp_imax = (uint32_t)val; - phba->initial_imax = phba->cfg_fcp_imax; if (phba->cfg_fcp_imax) usdelay = LPFC_SEC_TO_USEC / phba->cfg_fcp_imax; @@ -5020,15 +5031,119 @@ lpfc_fcp_imax_init(struct lpfc_hba *phba, int val) static DEVICE_ATTR_RW(lpfc_fcp_imax); +/** + * lpfc_cq_max_proc_limit_store + * + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: string with the cq max processing limit of cqes + * @count: unused variable. + * + * Description: + * If val is in a valid range, then set value on each cq + * + * Returns: + * The length of the buf: if successful + * -ERANGE: if val is not in the valid range + * -EINVAL: if bad value format or intended mode is not supported. + **/ +static ssize_t +lpfc_cq_max_proc_limit_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct lpfc_queue *eq, *cq; + unsigned long val; + int i; + + /* cq_max_proc_limit is only valid for SLI4 */ + if (phba->sli_rev != LPFC_SLI_REV4) + return -EINVAL; + + /* Sanity check on user data */ + if (!isdigit(buf[0])) + return -EINVAL; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < LPFC_CQ_MIN_PROC_LIMIT || val > LPFC_CQ_MAX_PROC_LIMIT) + return -ERANGE; + + phba->cfg_cq_max_proc_limit = (uint32_t)val; + + /* set the values on the cq's */ + for (i = 0; i < phba->cfg_irq_chann; i++) { + eq = phba->sli4_hba.hdwq[i].hba_eq; + if (!eq) + continue; + + list_for_each_entry(cq, &eq->child_list, list) + cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit, + cq->entry_count); + } + + return strlen(buf); +} + /* - * lpfc_auto_imax: Controls Auto-interrupt coalescing values support. - * 0 No auto_imax support - * 1 auto imax on - * Auto imax will change the value of fcp_imax on a per EQ basis, using - * the EQ Delay Multiplier, depending on the activity for that EQ. - * Value range [0,1]. Default value is 1. + * lpfc_cq_max_proc_limit: The maximum number CQE entries processed in an + * itteration of CQ processing. */ -LPFC_ATTR_RW(auto_imax, 1, 0, 1, "Enable Auto imax"); +static int lpfc_cq_max_proc_limit = LPFC_CQ_DEF_MAX_PROC_LIMIT; +module_param(lpfc_cq_max_proc_limit, int, 0644); +MODULE_PARM_DESC(lpfc_cq_max_proc_limit, + "Set the maximum number CQEs processed in an iteration of " + "CQ processing"); +lpfc_param_show(cq_max_proc_limit) + +/* + * lpfc_cq_poll_threshold: Set the threshold of CQE completions in a + * single handler call which should request a polled completion rather + * than re-enabling interrupts. + */ +LPFC_ATTR_RW(cq_poll_threshold, LPFC_CQ_DEF_THRESHOLD_TO_POLL, + LPFC_CQ_MIN_THRESHOLD_TO_POLL, + LPFC_CQ_MAX_THRESHOLD_TO_POLL, + "CQE Processing Threshold to enable Polling"); + +/** + * lpfc_cq_max_proc_limit_init - Set the initial cq max_proc_limit + * @phba: lpfc_hba pointer. + * @val: entry limit + * + * Description: + * If val is in a valid range, then initialize the adapter's maximum + * value. + * + * Returns: + * Always returns 0 for success, even if value not always set to + * requested value. If value out of range or not supported, will fall + * back to default. + **/ +static int +lpfc_cq_max_proc_limit_init(struct lpfc_hba *phba, int val) +{ + phba->cfg_cq_max_proc_limit = LPFC_CQ_DEF_MAX_PROC_LIMIT; + + if (phba->sli_rev != LPFC_SLI_REV4) + return 0; + + if (val >= LPFC_CQ_MIN_PROC_LIMIT && val <= LPFC_CQ_MAX_PROC_LIMIT) { + phba->cfg_cq_max_proc_limit = val; + return 0; + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0371 "LPFC_DRIVER_NAME"_cq_max_proc_limit: " + "%d out of range, using default\n", + phba->cfg_cq_max_proc_limit); + + return 0; +} + +static DEVICE_ATTR_RW(lpfc_cq_max_proc_limit); /** * lpfc_state_show - Display current driver CPU affinity @@ -5788,8 +5903,9 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_use_msi, &dev_attr_lpfc_nvme_oas, &dev_attr_lpfc_nvme_embed_cmd, - &dev_attr_lpfc_auto_imax, &dev_attr_lpfc_fcp_imax, + &dev_attr_lpfc_cq_poll_threshold, + &dev_attr_lpfc_cq_max_proc_limit, &dev_attr_lpfc_fcp_cpu_map, &dev_attr_lpfc_hdw_queue, &dev_attr_lpfc_irq_chann, @@ -6834,8 +6950,9 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_use_msi_init(phba, lpfc_use_msi); lpfc_nvme_oas_init(phba, lpfc_nvme_oas); lpfc_nvme_embed_cmd_init(phba, lpfc_nvme_embed_cmd); - lpfc_auto_imax_init(phba, lpfc_auto_imax); lpfc_fcp_imax_init(phba, lpfc_fcp_imax); + lpfc_cq_poll_threshold_init(phba, lpfc_cq_poll_threshold); + lpfc_cq_max_proc_limit_init(phba, lpfc_cq_max_proc_limit); lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map); lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset); lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat); @@ -6888,9 +7005,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) phba->cfg_enable_fc4_type |= LPFC_ENABLE_FCP; } - if (phba->cfg_auto_imax && !phba->cfg_fcp_imax) - phba->cfg_auto_imax = 0; - phba->initial_imax = phba->cfg_fcp_imax; + phba->cfg_auto_imax = (phba->cfg_fcp_imax) ? 0 : 1; phba->cfg_enable_pbde = 0; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 72076b2cd4ff..16b461778e59 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -3766,10 +3766,10 @@ __lpfc_idiag_print_wq(struct lpfc_queue *qp, char *wqtype, (unsigned long long)qp->q_cnt_4); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\t\tWQID[%02d], QE-CNT[%04d], QE-SZ[%04d], " - "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]", + "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]", qp->queue_id, qp->entry_count, qp->entry_size, qp->host_index, - qp->hba_index, qp->entry_repost); + qp->hba_index, qp->notify_interval); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); return len; @@ -3819,10 +3819,10 @@ __lpfc_idiag_print_cq(struct lpfc_queue *qp, char *cqtype, qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\tCQID[%02d], QE-CNT[%04d], QE-SZ[%04d], " - "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]", + "HST-IDX[%04d], NTFI[%03d], PLMT[%03d]", qp->queue_id, qp->entry_count, qp->entry_size, qp->host_index, - qp->hba_index, qp->entry_repost); + qp->notify_interval, qp->max_proc_limit); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); @@ -3845,15 +3845,15 @@ __lpfc_idiag_print_rqpair(struct lpfc_queue *qp, struct lpfc_queue *datqp, qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\t\tHQID[%02d], QE-CNT[%04d], QE-SZ[%04d], " - "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]\n", + "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]\n", qp->queue_id, qp->entry_count, qp->entry_size, - qp->host_index, qp->hba_index, qp->entry_repost); + qp->host_index, qp->hba_index, qp->notify_interval); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\t\tDQID[%02d], QE-CNT[%04d], QE-SZ[%04d], " - "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]\n", + "HST-IDX[%04d], PRT-IDX[%04d], NTFI[%03d]\n", datqp->queue_id, datqp->entry_count, datqp->entry_size, datqp->host_index, - datqp->hba_index, datqp->entry_repost); + datqp->hba_index, datqp->notify_interval); return len; } @@ -3934,10 +3934,10 @@ __lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype, (unsigned long long)qp->q_cnt_4, qp->q_mode); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "EQID[%02d], QE-CNT[%04d], QE-SZ[%04d], " - "HST-IDX[%04d], PRT-IDX[%04d], PST[%03d] AFFIN[%03d]", + "HST-IDX[%04d], NTFI[%03d], PLMT[%03d], AFFIN[%03d]", qp->queue_id, qp->entry_count, qp->entry_size, - qp->host_index, qp->hba_index, qp->entry_repost, - qp->chann); + qp->host_index, qp->notify_interval, + qp->max_proc_limit, qp->chann); len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); return len; diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 665852291a4f..c9a056ef321a 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -208,7 +208,14 @@ struct lpfc_sli_intf { /* Configuration of Interrupts / sec for entire HBA port */ #define LPFC_MIN_IMAX 5000 #define LPFC_MAX_IMAX 5000000 -#define LPFC_DEF_IMAX 150000 +#define LPFC_DEF_IMAX 0 + +#define LPFC_IMAX_THRESHOLD 1000 +#define LPFC_MAX_AUTO_EQ_DELAY 120 +#define LPFC_EQ_DELAY_STEP 15 +#define LPFC_EQD_ISR_TRIGGER 20000 +/* 1s intervals */ +#define LPFC_EQ_DELAY_MSECS 1000 #define LPFC_MIN_CPU_MAP 0 #define LPFC_MAX_CPU_MAP 1 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 22aec1a9b689..3fa7240a8e31 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1251,6 +1251,69 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq) return; } +static void +lpfc_hb_eq_delay_work(struct work_struct *work) +{ + struct lpfc_hba *phba = container_of(to_delayed_work(work), + struct lpfc_hba, eq_delay_work); + struct lpfc_eq_intr_info *eqi, *eqi_new; + struct lpfc_queue *eq, *eq_next; + unsigned char *eqcnt = NULL; + uint32_t usdelay; + int i; + + if (!phba->cfg_auto_imax || phba->pport->load_flag & FC_UNLOADING) + return; + + if (phba->link_state == LPFC_HBA_ERROR || + phba->pport->fc_flag & FC_OFFLINE_MODE) + goto requeue; + + eqcnt = kcalloc(num_possible_cpus(), sizeof(unsigned char), + GFP_KERNEL); + if (!eqcnt) + goto requeue; + + for (i = 0; i < phba->cfg_irq_chann; i++) { + eq = phba->sli4_hba.hdwq[i].hba_eq; + if (eq && eqcnt[eq->last_cpu] < 2) + eqcnt[eq->last_cpu]++; + continue; + } + + for_each_present_cpu(i) { + if (phba->cfg_irq_chann > 1 && eqcnt[i] < 2) + continue; + + eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i); + + usdelay = (eqi->icnt / LPFC_IMAX_THRESHOLD) * + LPFC_EQ_DELAY_STEP; + if (usdelay > LPFC_MAX_AUTO_EQ_DELAY) + usdelay = LPFC_MAX_AUTO_EQ_DELAY; + + eqi->icnt = 0; + + list_for_each_entry_safe(eq, eq_next, &eqi->list, cpu_list) { + if (eq->last_cpu != i) { + eqi_new = per_cpu_ptr(phba->sli4_hba.eq_info, + eq->last_cpu); + list_move_tail(&eq->cpu_list, &eqi_new->list); + continue; + } + if (usdelay != eq->q_mode) + lpfc_modify_hba_eq_delay(phba, eq->hdwq, 1, + usdelay); + } + } + + kfree(eqcnt); + +requeue: + queue_delayed_work(phba->wq, &phba->eq_delay_work, + msecs_to_jiffies(LPFC_EQ_DELAY_MSECS)); +} + /** * lpfc_hb_mxp_handler - Multi-XRI pools handler to adjust XRI distribution * @phba: pointer to lpfc hba data structure. @@ -1303,16 +1366,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) int retval, i; struct lpfc_sli *psli = &phba->sli; LIST_HEAD(completions); - struct lpfc_queue *qp; - unsigned long time_elapsed; - uint32_t tick_cqe, max_cqe, val; - uint64_t tot, data1, data2, data3; - struct lpfc_nvmet_tgtport *tgtp; - struct lpfc_register reg_data; - struct nvme_fc_local_port *localport; - struct lpfc_nvme_lport *lport; - struct lpfc_fc4_ctrl_stat *cstat; - void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr; if (phba->cfg_xri_rebalancing) { /* Multi-XRI pools handler */ @@ -1332,104 +1385,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) (phba->pport->fc_flag & FC_OFFLINE_MODE)) return; - if (phba->cfg_auto_imax) { - if (!phba->last_eqdelay_time) { - phba->last_eqdelay_time = jiffies; - goto skip_eqdelay; - } - time_elapsed = jiffies - phba->last_eqdelay_time; - phba->last_eqdelay_time = jiffies; - - tot = 0xffff; - /* Check outstanding IO count */ - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { - if (phba->nvmet_support) { - tgtp = phba->targetport->private; - /* Calculate outstanding IOs */ - tot = atomic_read(&tgtp->rcv_fcp_cmd_drop); - tot += atomic_read(&tgtp->xmt_fcp_release); - tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot; - } else { - localport = phba->pport->localport; - if (!localport || !localport->private) - goto skip_eqdelay; - lport = (struct lpfc_nvme_lport *) - localport->private; - tot = 0; - for (i = 0; - i < phba->cfg_hdw_queue; i++) { - cstat = - &phba->sli4_hba.hdwq[i].nvme_cstat; - data1 = cstat->input_requests; - data2 = cstat->output_requests; - data3 = cstat->control_requests; - tot += (data1 + data2 + data3); - tot -= cstat->io_cmpls; - } - } - } - - /* Interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / phba->cfg_irq_chann; - tick_cqe = val / CONFIG_HZ; /* Per tick per EQ */ - - /* Assume 1 CQE/ISR, calc max CQEs allowed for time duration */ - max_cqe = time_elapsed * tick_cqe; - - for (i = 0; i < phba->cfg_irq_chann; i++) { - /* Fast-path EQ */ - qp = phba->sli4_hba.hdwq[i].hba_eq; - if (!qp) - continue; - - /* Use no EQ delay if we don't have many outstanding - * IOs, or if we are only processing 1 CQE/ISR or less. - * Otherwise, assume we can process up to lpfc_fcp_imax - * interrupts per HBA. - */ - if (tot < LPFC_NODELAY_MAX_IO || - qp->EQ_cqe_cnt <= max_cqe) - val = 0; - else - val = phba->cfg_fcp_imax; - - if (phba->sli.sli_flag & LPFC_SLI_USE_EQDR) { - /* Use EQ Delay Register method */ - - /* Convert for EQ Delay register */ - if (val) { - /* First, interrupts per sec per EQ */ - val = phba->cfg_fcp_imax / - phba->cfg_irq_chann; - - /* us delay between each interrupt */ - val = LPFC_SEC_TO_USEC / val; - } - if (val != qp->q_mode) { - reg_data.word0 = 0; - bf_set(lpfc_sliport_eqdelay_id, - ®_data, qp->queue_id); - bf_set(lpfc_sliport_eqdelay_delay, - ®_data, val); - writel(reg_data.word0, eqdreg); - } - } else { - /* Use mbox command method */ - if (val != qp->q_mode) - lpfc_modify_hba_eq_delay(phba, i, - 1, val); - } - - /* - * val is cfg_fcp_imax or 0 for mbox delay or us delay - * between interrupts for EQDR. - */ - qp->q_mode = val; - qp->EQ_cqe_cnt = 0; - } - } - -skip_eqdelay: spin_lock_irq(&phba->pport->work_port_lock); if (time_after(phba->last_completion_time + @@ -2986,6 +2941,7 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba) { if (phba->pport) lpfc_stop_vport_timers(phba->pport); + cancel_delayed_work_sync(&phba->eq_delay_work); del_timer_sync(&phba->sli.mbox_tmo); del_timer_sync(&phba->fabric_block_timer); del_timer_sync(&phba->eratt_poll); @@ -6234,6 +6190,8 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) /* Heartbeat timer */ timer_setup(&phba->hb_tmofunc, lpfc_hb_timeout, 0); + INIT_DELAYED_WORK(&phba->eq_delay_work, lpfc_hb_eq_delay_work); + return 0; } @@ -6849,6 +6807,13 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_hba_eq_hdl; } + phba->sli4_hba.eq_info = alloc_percpu(struct lpfc_eq_intr_info); + if (!phba->sli4_hba.eq_info) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3321 Failed allocation for per_cpu stats\n"); + rc = -ENOMEM; + goto out_free_hba_cpu_map; + } /* * Enable sr-iov virtual functions if supported and configured * through the module parameter. @@ -6868,6 +6833,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) return 0; +out_free_hba_cpu_map: + kfree(phba->sli4_hba.cpu_map); out_free_hba_eq_hdl: kfree(phba->sli4_hba.hba_eq_hdl); out_free_fcf_rr_bmask: @@ -6897,6 +6864,8 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) { struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry; + free_percpu(phba->sli4_hba.eq_info); + /* Free memory allocated for msi-x interrupt vector to CPU mapping */ kfree(phba->sli4_hba.cpu_map); phba->sli4_hba.num_present_cpu = 0; @@ -8753,6 +8722,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) struct lpfc_queue *qdesc; int idx, eqidx; struct lpfc_sli4_hdw_queue *qp; + struct lpfc_eq_intr_info *eqi; /* * Create HBA Record arrays. @@ -8865,6 +8835,9 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) qdesc->chann = lpfc_find_cpu_handle(phba, eqidx, LPFC_FIND_BY_EQ); phba->sli4_hba.hdwq[idx].hba_eq = qdesc; + qdesc->last_cpu = qdesc->chann; + eqi = per_cpu_ptr(phba->sli4_hba.eq_info, qdesc->last_cpu); + list_add(&qdesc->cpu_list, &eqi->list); } @@ -10246,13 +10219,13 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) case LPFC_SLI_INTF_IF_TYPE_0: case LPFC_SLI_INTF_IF_TYPE_2: phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_eq_clr_intr; - phba->sli4_hba.sli4_eq_release = lpfc_sli4_eq_release; - phba->sli4_hba.sli4_cq_release = lpfc_sli4_cq_release; + phba->sli4_hba.sli4_write_eq_db = lpfc_sli4_write_eq_db; + phba->sli4_hba.sli4_write_cq_db = lpfc_sli4_write_cq_db; break; case LPFC_SLI_INTF_IF_TYPE_6: phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_if6_eq_clr_intr; - phba->sli4_hba.sli4_eq_release = lpfc_sli4_if6_eq_release; - phba->sli4_hba.sli4_cq_release = lpfc_sli4_if6_cq_release; + phba->sli4_hba.sli4_write_eq_db = lpfc_sli4_if6_write_eq_db; + phba->sli4_hba.sli4_write_cq_db = lpfc_sli4_if6_write_cq_db; break; default: break; @@ -10773,6 +10746,14 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) cpup++; } + for_each_possible_cpu(i) { + struct lpfc_eq_intr_info *eqi = + per_cpu_ptr(phba->sli4_hba.eq_info, i); + + INIT_LIST_HEAD(&eqi->list); + eqi->icnt = 0; + } + /* * If the number of IRQ vectors == number of CPUs, * mapping is pretty simple: 1 to 1. diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index a3477cecb0c9..ec385d3413f2 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -78,12 +78,13 @@ static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *, struct hbq_dmabuf *); static void lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf); -static int lpfc_sli4_fp_handle_cqe(struct lpfc_hba *, struct lpfc_queue *, - struct lpfc_cqe *); +static bool lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, + struct lpfc_queue *cq, struct lpfc_cqe *cqe); static int lpfc_sli4_post_sgl_list(struct lpfc_hba *, struct list_head *, int); static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, - struct lpfc_eqe *eqe, uint32_t qidx); + struct lpfc_queue *eq, + struct lpfc_eqe *eqe); static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba); static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba); static int lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, @@ -160,7 +161,7 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe) } q->WQ_posted++; /* set consumption flag every once in a while */ - if (!((q->host_index + 1) % q->entry_repost)) + if (!((q->host_index + 1) % q->notify_interval)) bf_set(wqe_wqec, &wqe->generic.wqe_com, 1); else bf_set(wqe_wqec, &wqe->generic.wqe_com, 0); @@ -325,29 +326,16 @@ lpfc_sli4_mq_release(struct lpfc_queue *q) static struct lpfc_eqe * lpfc_sli4_eq_get(struct lpfc_queue *q) { - struct lpfc_hba *phba; struct lpfc_eqe *eqe; - uint32_t idx; /* sanity check on queue memory */ if (unlikely(!q)) return NULL; - phba = q->phba; - eqe = q->qe[q->hba_index].eqe; + eqe = q->qe[q->host_index].eqe; /* If the next EQE is not valid then we are done */ if (bf_get_le32(lpfc_eqe_valid, eqe) != q->qe_valid) return NULL; - /* If the host has not yet processed the next entry then we are done */ - idx = ((q->hba_index + 1) % q->entry_count); - if (idx == q->host_index) - return NULL; - - q->hba_index = idx; - /* if the index wrapped around, toggle the valid bit */ - if (phba->sli4_hba.pc_sli4_params.eqav && !q->hba_index) - q->qe_valid = (q->qe_valid) ? 0 : 1; - /* * insert barrier for instruction interlock : data from the hardware @@ -397,44 +385,25 @@ lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q) } /** - * lpfc_sli4_eq_release - Indicates the host has finished processing an EQ + * lpfc_sli4_write_eq_db - write EQ DB for eqe's consumed or arm state + * @phba: adapter with EQ * @q: The Event Queue that the host has completed processing for. + * @count: Number of elements that have been consumed * @arm: Indicates whether the host wants to arms this CQ. * - * This routine will mark all Event Queue Entries on @q, from the last - * known completed entry to the last entry that was processed, as completed - * by clearing the valid bit for each completion queue entry. Then it will - * notify the HBA, by ringing the doorbell, that the EQEs have been processed. - * The internal host index in the @q will be updated by this routine to indicate - * that the host has finished processing the entries. The @arm parameter - * indicates that the queue should be rearmed when ringing the doorbell. - * - * This function will return the number of EQEs that were popped. + * This routine will notify the HBA, by ringing the doorbell, that count + * number of EQEs have been processed. The @arm parameter indicates whether + * the queue should be rearmed when ringing the doorbell. **/ -uint32_t -lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm) +void +lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm) { - uint32_t released = 0; - struct lpfc_hba *phba; - struct lpfc_eqe *temp_eqe; struct lpfc_register doorbell; /* sanity check on queue memory */ - if (unlikely(!q)) - return 0; - phba = q->phba; - - /* while there are valid entries */ - while (q->hba_index != q->host_index) { - if (!phba->sli4_hba.pc_sli4_params.eqav) { - temp_eqe = q->qe[q->host_index].eqe; - bf_set_le32(lpfc_eqe_valid, temp_eqe, 0); - } - released++; - q->host_index = ((q->host_index + 1) % q->entry_count); - } - if (unlikely(released == 0 && !arm)) - return 0; + if (unlikely(!q || (count == 0 && !arm))) + return; /* ring doorbell for number popped */ doorbell.word0 = 0; @@ -442,7 +411,7 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm) bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1); bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1); } - bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released); + bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count); bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT); bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell, (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT)); @@ -451,60 +420,112 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm) /* PCI read to flush PCI pipeline on re-arming for INTx mode */ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM)) readl(q->phba->sli4_hba.EQDBregaddr); - return released; } /** - * lpfc_sli4_if6_eq_release - Indicates the host has finished processing an EQ + * lpfc_sli4_if6_write_eq_db - write EQ DB for eqe's consumed or arm state + * @phba: adapter with EQ * @q: The Event Queue that the host has completed processing for. + * @count: Number of elements that have been consumed * @arm: Indicates whether the host wants to arms this CQ. * - * This routine will mark all Event Queue Entries on @q, from the last - * known completed entry to the last entry that was processed, as completed - * by clearing the valid bit for each completion queue entry. Then it will - * notify the HBA, by ringing the doorbell, that the EQEs have been processed. - * The internal host index in the @q will be updated by this routine to indicate - * that the host has finished processing the entries. The @arm parameter - * indicates that the queue should be rearmed when ringing the doorbell. - * - * This function will return the number of EQEs that were popped. + * This routine will notify the HBA, by ringing the doorbell, that count + * number of EQEs have been processed. The @arm parameter indicates whether + * the queue should be rearmed when ringing the doorbell. **/ -uint32_t -lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm) +void +lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm) { - uint32_t released = 0; - struct lpfc_hba *phba; - struct lpfc_eqe *temp_eqe; struct lpfc_register doorbell; /* sanity check on queue memory */ - if (unlikely(!q)) - return 0; - phba = q->phba; - - /* while there are valid entries */ - while (q->hba_index != q->host_index) { - if (!phba->sli4_hba.pc_sli4_params.eqav) { - temp_eqe = q->qe[q->host_index].eqe; - bf_set_le32(lpfc_eqe_valid, temp_eqe, 0); - } - released++; - q->host_index = ((q->host_index + 1) % q->entry_count); - } - if (unlikely(released == 0 && !arm)) - return 0; + if (unlikely(!q || (count == 0 && !arm))) + return; /* ring doorbell for number popped */ doorbell.word0 = 0; if (arm) bf_set(lpfc_if6_eq_doorbell_arm, &doorbell, 1); - bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, released); + bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, count); bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id); writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr); /* PCI read to flush PCI pipeline on re-arming for INTx mode */ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM)) readl(q->phba->sli4_hba.EQDBregaddr); - return released; +} + +static void +__lpfc_sli4_consume_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq, + struct lpfc_eqe *eqe) +{ + if (!phba->sli4_hba.pc_sli4_params.eqav) + bf_set_le32(lpfc_eqe_valid, eqe, 0); + + eq->host_index = ((eq->host_index + 1) % eq->entry_count); + + /* if the index wrapped around, toggle the valid bit */ + if (phba->sli4_hba.pc_sli4_params.eqav && !eq->host_index) + eq->qe_valid = (eq->qe_valid) ? 0 : 1; +} + +static void +lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq) +{ + struct lpfc_eqe *eqe; + uint32_t count = 0; + + /* walk all the EQ entries and drop on the floor */ + eqe = lpfc_sli4_eq_get(eq); + while (eqe) { + __lpfc_sli4_consume_eqe(phba, eq, eqe); + count++; + eqe = lpfc_sli4_eq_get(eq); + } + + /* Clear and re-arm the EQ */ + phba->sli4_hba.sli4_write_eq_db(phba, eq, count, LPFC_QUEUE_REARM); +} + +static int +lpfc_sli4_process_eq(struct lpfc_hba *phba, struct lpfc_queue *eq) +{ + struct lpfc_eqe *eqe; + int count = 0, consumed = 0; + + if (cmpxchg(&eq->queue_claimed, 0, 1) != 0) + goto rearm_and_exit; + + eqe = lpfc_sli4_eq_get(eq); + while (eqe) { + lpfc_sli4_hba_handle_eqe(phba, eq, eqe); + __lpfc_sli4_consume_eqe(phba, eq, eqe); + + consumed++; + if (!(++count % eq->max_proc_limit)) + break; + + if (!(count % eq->notify_interval)) { + phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed, + LPFC_QUEUE_NOARM); + consumed = 0; + } + + eqe = lpfc_sli4_eq_get(eq); + } + eq->EQ_processed += count; + + /* Track the max number of EQEs processed in 1 intr */ + if (count > eq->EQ_max_eqe) + eq->EQ_max_eqe = count; + + eq->queue_claimed = 0; + +rearm_and_exit: + /* Always clear and re-arm the EQ */ + phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed, LPFC_QUEUE_REARM); + + return count; } /** @@ -519,28 +540,16 @@ lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm) static struct lpfc_cqe * lpfc_sli4_cq_get(struct lpfc_queue *q) { - struct lpfc_hba *phba; struct lpfc_cqe *cqe; - uint32_t idx; /* sanity check on queue memory */ if (unlikely(!q)) return NULL; - phba = q->phba; - cqe = q->qe[q->hba_index].cqe; + cqe = q->qe[q->host_index].cqe; /* If the next CQE is not valid then we are done */ if (bf_get_le32(lpfc_cqe_valid, cqe) != q->qe_valid) return NULL; - /* If the host has not yet processed the next entry then we are done */ - idx = ((q->hba_index + 1) % q->entry_count); - if (idx == q->host_index) - return NULL; - - q->hba_index = idx; - /* if the index wrapped around, toggle the valid bit */ - if (phba->sli4_hba.pc_sli4_params.cqav && !q->hba_index) - q->qe_valid = (q->qe_valid) ? 0 : 1; /* * insert barrier for instruction interlock : data from the hardware @@ -554,107 +563,81 @@ lpfc_sli4_cq_get(struct lpfc_queue *q) return cqe; } +static void +__lpfc_sli4_consume_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, + struct lpfc_cqe *cqe) +{ + if (!phba->sli4_hba.pc_sli4_params.cqav) + bf_set_le32(lpfc_cqe_valid, cqe, 0); + + cq->host_index = ((cq->host_index + 1) % cq->entry_count); + + /* if the index wrapped around, toggle the valid bit */ + if (phba->sli4_hba.pc_sli4_params.cqav && !cq->host_index) + cq->qe_valid = (cq->qe_valid) ? 0 : 1; +} + /** - * lpfc_sli4_cq_release - Indicates the host has finished processing a CQ + * lpfc_sli4_write_cq_db - write cq DB for entries consumed or arm state. + * @phba: the adapter with the CQ * @q: The Completion Queue that the host has completed processing for. + * @count: the number of elements that were consumed * @arm: Indicates whether the host wants to arms this CQ. * - * This routine will mark all Completion queue entries on @q, from the last - * known completed entry to the last entry that was processed, as completed - * by clearing the valid bit for each completion queue entry. Then it will - * notify the HBA, by ringing the doorbell, that the CQEs have been processed. - * The internal host index in the @q will be updated by this routine to indicate - * that the host has finished processing the entries. The @arm parameter - * indicates that the queue should be rearmed when ringing the doorbell. - * - * This function will return the number of CQEs that were released. + * This routine will notify the HBA, by ringing the doorbell, that the + * CQEs have been processed. The @arm parameter specifies whether the + * queue should be rearmed when ringing the doorbell. **/ -uint32_t -lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm) +void +lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm) { - uint32_t released = 0; - struct lpfc_hba *phba; - struct lpfc_cqe *temp_qe; struct lpfc_register doorbell; /* sanity check on queue memory */ - if (unlikely(!q)) - return 0; - phba = q->phba; - - /* while there are valid entries */ - while (q->hba_index != q->host_index) { - if (!phba->sli4_hba.pc_sli4_params.cqav) { - temp_qe = q->qe[q->host_index].cqe; - bf_set_le32(lpfc_cqe_valid, temp_qe, 0); - } - released++; - q->host_index = ((q->host_index + 1) % q->entry_count); - } - if (unlikely(released == 0 && !arm)) - return 0; + if (unlikely(!q || (count == 0 && !arm))) + return; /* ring doorbell for number popped */ doorbell.word0 = 0; if (arm) bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1); - bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released); + bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count); bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION); bf_set(lpfc_eqcq_doorbell_cqid_hi, &doorbell, (q->queue_id >> LPFC_CQID_HI_FIELD_SHIFT)); bf_set(lpfc_eqcq_doorbell_cqid_lo, &doorbell, q->queue_id); writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr); - return released; } /** - * lpfc_sli4_if6_cq_release - Indicates the host has finished processing a CQ + * lpfc_sli4_if6_write_cq_db - write cq DB for entries consumed or arm state. + * @phba: the adapter with the CQ * @q: The Completion Queue that the host has completed processing for. + * @count: the number of elements that were consumed * @arm: Indicates whether the host wants to arms this CQ. * - * This routine will mark all Completion queue entries on @q, from the last - * known completed entry to the last entry that was processed, as completed - * by clearing the valid bit for each completion queue entry. Then it will - * notify the HBA, by ringing the doorbell, that the CQEs have been processed. - * The internal host index in the @q will be updated by this routine to indicate - * that the host has finished processing the entries. The @arm parameter - * indicates that the queue should be rearmed when ringing the doorbell. - * - * This function will return the number of CQEs that were released. + * This routine will notify the HBA, by ringing the doorbell, that the + * CQEs have been processed. The @arm parameter specifies whether the + * queue should be rearmed when ringing the doorbell. **/ -uint32_t -lpfc_sli4_if6_cq_release(struct lpfc_queue *q, bool arm) +void +lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm) { - uint32_t released = 0; - struct lpfc_hba *phba; - struct lpfc_cqe *temp_qe; struct lpfc_register doorbell; /* sanity check on queue memory */ - if (unlikely(!q)) - return 0; - phba = q->phba; - - /* while there are valid entries */ - while (q->hba_index != q->host_index) { - if (!phba->sli4_hba.pc_sli4_params.cqav) { - temp_qe = q->qe[q->host_index].cqe; - bf_set_le32(lpfc_cqe_valid, temp_qe, 0); - } - released++; - q->host_index = ((q->host_index + 1) % q->entry_count); - } - if (unlikely(released == 0 && !arm)) - return 0; + if (unlikely(!q || (count == 0 && !arm))) + return; /* ring doorbell for number popped */ doorbell.word0 = 0; if (arm) bf_set(lpfc_if6_cq_doorbell_arm, &doorbell, 1); - bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, released); + bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, count); bf_set(lpfc_if6_cq_doorbell_cqid, &doorbell, q->queue_id); writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr); - return released; } /** @@ -703,15 +686,15 @@ lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq, hq->RQ_buf_posted++; /* Ring The Header Receive Queue Doorbell */ - if (!(hq->host_index % hq->entry_repost)) { + if (!(hq->host_index % hq->notify_interval)) { doorbell.word0 = 0; if (hq->db_format == LPFC_DB_RING_FORMAT) { bf_set(lpfc_rq_db_ring_fm_num_posted, &doorbell, - hq->entry_repost); + hq->notify_interval); bf_set(lpfc_rq_db_ring_fm_id, &doorbell, hq->queue_id); } else if (hq->db_format == LPFC_DB_LIST_FORMAT) { bf_set(lpfc_rq_db_list_fm_num_posted, &doorbell, - hq->entry_repost); + hq->notify_interval); bf_set(lpfc_rq_db_list_fm_index, &doorbell, hq->host_index); bf_set(lpfc_rq_db_list_fm_id, &doorbell, hq->queue_id); @@ -5571,30 +5554,30 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba; struct lpfc_sli4_hdw_queue *qp; - sli4_hba->sli4_cq_release(sli4_hba->mbx_cq, LPFC_QUEUE_REARM); - sli4_hba->sli4_cq_release(sli4_hba->els_cq, LPFC_QUEUE_REARM); + sli4_hba->sli4_write_cq_db(phba, sli4_hba->mbx_cq, 0, LPFC_QUEUE_REARM); + sli4_hba->sli4_write_cq_db(phba, sli4_hba->els_cq, 0, LPFC_QUEUE_REARM); if (sli4_hba->nvmels_cq) - sli4_hba->sli4_cq_release(sli4_hba->nvmels_cq, - LPFC_QUEUE_REARM); + sli4_hba->sli4_write_cq_db(phba, sli4_hba->nvmels_cq, 0, + LPFC_QUEUE_REARM); qp = sli4_hba->hdwq; if (sli4_hba->hdwq) { for (qidx = 0; qidx < phba->cfg_hdw_queue; qidx++) { - sli4_hba->sli4_cq_release(qp[qidx].fcp_cq, - LPFC_QUEUE_REARM); - sli4_hba->sli4_cq_release(qp[qidx].nvme_cq, - LPFC_QUEUE_REARM); + sli4_hba->sli4_write_cq_db(phba, qp[qidx].fcp_cq, 0, + LPFC_QUEUE_REARM); + sli4_hba->sli4_write_cq_db(phba, qp[qidx].nvme_cq, 0, + LPFC_QUEUE_REARM); } for (qidx = 0; qidx < phba->cfg_irq_chann; qidx++) - sli4_hba->sli4_eq_release(qp[qidx].hba_eq, - LPFC_QUEUE_REARM); + sli4_hba->sli4_write_eq_db(phba, qp[qidx].hba_eq, + 0, LPFC_QUEUE_REARM); } if (phba->nvmet_support) { for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) { - sli4_hba->sli4_cq_release( - sli4_hba->nvmet_cqset[qidx], + sli4_hba->sli4_write_cq_db(phba, + sli4_hba->nvmet_cqset[qidx], 0, LPFC_QUEUE_REARM); } } @@ -7698,6 +7681,11 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) phba->hb_outstanding = 0; phba->last_completion_time = jiffies; + /* start eq_delay heartbeat */ + if (phba->cfg_auto_imax) + queue_delayed_work(phba->wq, &phba->eq_delay_work, + msecs_to_jiffies(LPFC_EQ_DELAY_MSECS)); + /* Start error attention (ERATT) polling timer */ mod_timer(&phba->eratt_poll, jiffies + msecs_to_jiffies(1000 * phba->eratt_poll_interval)); @@ -7869,7 +7857,6 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba) struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba; uint32_t eqidx; struct lpfc_queue *fpeq = NULL; - struct lpfc_eqe *eqe; bool mbox_pending; if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4)) @@ -7903,14 +7890,11 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba) */ if (mbox_pending) - while ((eqe = lpfc_sli4_eq_get(fpeq))) { - lpfc_sli4_hba_handle_eqe(phba, eqe, eqidx); - fpeq->EQ_processed++; - } - - /* Always clear and re-arm the EQ */ - - sli4_hba->sli4_eq_release(fpeq, LPFC_QUEUE_REARM); + /* process and rearm the EQ */ + lpfc_sli4_process_eq(phba, fpeq); + else + /* Always clear and re-arm the EQ */ + sli4_hba->sli4_write_eq_db(phba, fpeq, 0, LPFC_QUEUE_REARM); return mbox_pending; @@ -13265,11 +13249,14 @@ out_no_mqe_complete: * Return: true if work posted to worker thread, otherwise false. **/ static bool -lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe) +lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, + struct lpfc_cqe *cqe) { struct lpfc_mcqe mcqe; bool workposted; + cq->CQ_mbox++; + /* Copy the mailbox MCQE and convert endian order as needed */ lpfc_sli4_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe)); @@ -13528,7 +13515,7 @@ out: * lpfc_sli4_sp_handle_cqe - Process a slow path completion queue entry * @phba: Pointer to HBA context object. * @cq: Pointer to the completion queue. - * @wcqe: Pointer to a completion queue entry. + * @cqe: Pointer to a completion queue entry. * * This routine process a slow-path work-queue or receive queue completion queue * entry. @@ -13628,60 +13615,129 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, } /** - * lpfc_sli4_sp_process_cq - Process a slow-path event queue entry + * __lpfc_sli4_process_cq - Process elements of a CQ * @phba: Pointer to HBA context object. + * @cq: Pointer to CQ to be processed + * @handler: Routine to process each cqe + * @delay: Pointer to usdelay to set in case of rescheduling of the handler * - * This routine process a event queue entry from the slow-path event queue. - * It will check the MajorCode and MinorCode to determine this is for a - * completion event on a completion queue, if not, an error shall be logged - * and just return. Otherwise, it will get to the corresponding completion - * queue and process all the entries on that completion queue, rearm the - * completion queue, and then return. + * This routine processes completion queue entries in a CQ. While a valid + * queue element is found, the handler is called. During processing checks + * are made for periodic doorbell writes to let the hardware know of + * element consumption. * + * If the max limit on cqes to process is hit, or there are no more valid + * entries, the loop stops. If we processed a sufficient number of elements, + * meaning there is sufficient load, rather than rearming and generating + * another interrupt, a cq rescheduling delay will be set. A delay of 0 + * indicates no rescheduling. + * + * Returns True if work scheduled, False otherwise. **/ -static void -lpfc_sli4_sp_process_cq(struct work_struct *work) +static bool +__lpfc_sli4_process_cq(struct lpfc_hba *phba, struct lpfc_queue *cq, + bool (*handler)(struct lpfc_hba *, struct lpfc_queue *, + struct lpfc_cqe *), unsigned long *delay) { - struct lpfc_queue *cq = - container_of(work, struct lpfc_queue, spwork); - struct lpfc_hba *phba = cq->phba; struct lpfc_cqe *cqe; bool workposted = false; - int ccount = 0; + int count = 0, consumed = 0; + bool arm = true; + + /* default - no reschedule */ + *delay = 0; + + if (cmpxchg(&cq->queue_claimed, 0, 1) != 0) + goto rearm_and_exit; /* Process all the entries to the CQ */ - switch (cq->type) { - case LPFC_MCQ: - while ((cqe = lpfc_sli4_cq_get(cq))) { - workposted |= lpfc_sli4_sp_handle_mcqe(phba, cqe); - if (!(++ccount % cq->entry_repost)) - break; - cq->CQ_mbox++; - } - break; - case LPFC_WCQ: - while ((cqe = lpfc_sli4_cq_get(cq))) { - if (cq->subtype == LPFC_FCP || - cq->subtype == LPFC_NVME) { -#ifdef CONFIG_SCSI_LPFC_DEBUG_FS - if (phba->ktime_on) - cq->isr_timestamp = ktime_get_ns(); - else - cq->isr_timestamp = 0; + cqe = lpfc_sli4_cq_get(cq); + while (cqe) { +#if defined(CONFIG_SCSI_LPFC_DEBUG_FS) && defined(BUILD_NVME) + if (phba->ktime_on) + cq->isr_timestamp = ktime_get_ns(); + else + cq->isr_timestamp = 0; #endif - workposted |= lpfc_sli4_fp_handle_cqe(phba, cq, - cqe); - } else { - workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, - cqe); - } - if (!(++ccount % cq->entry_repost)) - break; + workposted |= handler(phba, cq, cqe); + __lpfc_sli4_consume_cqe(phba, cq, cqe); + + consumed++; + if (!(++count % cq->max_proc_limit)) + break; + + if (!(count % cq->notify_interval)) { + phba->sli4_hba.sli4_write_cq_db(phba, cq, consumed, + LPFC_QUEUE_NOARM); + consumed = 0; } - /* Track the max number of CQEs processed in 1 EQ */ - if (ccount > cq->CQ_max_cqe) - cq->CQ_max_cqe = ccount; + cqe = lpfc_sli4_cq_get(cq); + } + if (count >= phba->cfg_cq_poll_threshold) { + *delay = 1; + arm = false; + } + + /* Track the max number of CQEs processed in 1 EQ */ + if (count > cq->CQ_max_cqe) + cq->CQ_max_cqe = count; + + cq->assoc_qp->EQ_cqe_cnt += count; + + /* Catch the no cq entry condition */ + if (unlikely(count == 0)) + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "0369 No entry from completion queue " + "qid=%d\n", cq->queue_id); + + cq->queue_claimed = 0; + +rearm_and_exit: + phba->sli4_hba.sli4_write_cq_db(phba, cq, consumed, + arm ? LPFC_QUEUE_REARM : LPFC_QUEUE_NOARM); + + return workposted; +} + +/** + * lpfc_sli4_sp_process_cq - Process a slow-path event queue entry + * @cq: pointer to CQ to process + * + * This routine calls the cq processing routine with a handler specific + * to the type of queue bound to it. + * + * The CQ routine returns two values: the first is the calling status, + * which indicates whether work was queued to the background discovery + * thread. If true, the routine should wakeup the discovery thread; + * the second is the delay parameter. If non-zero, rather than rearming + * the CQ and yet another interrupt, the CQ handler should be queued so + * that it is processed in a subsequent polling action. The value of + * the delay indicates when to reschedule it. + **/ +static void +__lpfc_sli4_sp_process_cq(struct lpfc_queue *cq) +{ + struct lpfc_hba *phba = cq->phba; + unsigned long delay; + bool workposted = false; + + /* Process and rearm the CQ */ + switch (cq->type) { + case LPFC_MCQ: + workposted |= __lpfc_sli4_process_cq(phba, cq, + lpfc_sli4_sp_handle_mcqe, + &delay); + break; + case LPFC_WCQ: + if (cq->subtype == LPFC_FCP || cq->subtype == LPFC_NVME) + workposted |= __lpfc_sli4_process_cq(phba, cq, + lpfc_sli4_fp_handle_cqe, + &delay); + else + workposted |= __lpfc_sli4_process_cq(phba, cq, + lpfc_sli4_sp_handle_cqe, + &delay); break; default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -13690,20 +13746,50 @@ lpfc_sli4_sp_process_cq(struct work_struct *work) return; } - /* Catch the no cq entry condition, log an error */ - if (unlikely(ccount == 0)) - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0371 No entry from the CQ: identifier " - "(x%x), type (%d)\n", cq->queue_id, cq->type); - - /* In any case, flash and re-arm the RCQ */ - phba->sli4_hba.sli4_cq_release(cq, LPFC_QUEUE_REARM); + if (delay) { + if (!queue_delayed_work_on(cq->chann, phba->wq, + &cq->sched_spwork, delay)) + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "0394 Cannot schedule soft IRQ " + "for cqid=%d on CPU %d\n", + cq->queue_id, cq->chann); + } /* wake up worker thread if there are works to be done */ if (workposted) lpfc_worker_wake_up(phba); } +/** + * lpfc_sli4_sp_process_cq - slow-path work handler when started by + * interrupt + * @work: pointer to work element + * + * translates from the work handler and calls the slow-path handler. + **/ +static void +lpfc_sli4_sp_process_cq(struct work_struct *work) +{ + struct lpfc_queue *cq = container_of(work, struct lpfc_queue, spwork); + + __lpfc_sli4_sp_process_cq(cq); +} + +/** + * lpfc_sli4_dly_sp_process_cq - slow-path work handler when started by timer + * @work: pointer to work element + * + * translates from the work handler and calls the slow-path handler. + **/ +static void +lpfc_sli4_dly_sp_process_cq(struct work_struct *work) +{ + struct lpfc_queue *cq = container_of(to_delayed_work(work), + struct lpfc_queue, sched_spwork); + + __lpfc_sli4_sp_process_cq(cq); +} + /** * lpfc_sli4_fp_handle_fcp_wcqe - Process fast-path work queue completion entry * @phba: Pointer to HBA context object. @@ -13935,13 +14021,16 @@ out: /** * lpfc_sli4_fp_handle_cqe - Process fast-path work queue completion entry + * @phba: adapter with cq * @cq: Pointer to the completion queue. * @eqe: Pointer to fast-path completion queue entry. * * This routine process a fast-path work queue completion entry from fast-path * event queue for FCP command response completion. + * + * Return: true if work posted to worker thread, otherwise false. **/ -static int +static bool lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, struct lpfc_cqe *cqe) { @@ -14008,10 +14097,11 @@ lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, * completion queue, and then return. **/ static void -lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, - uint32_t qidx) +lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq, + struct lpfc_eqe *eqe) { struct lpfc_queue *cq = NULL; + uint32_t qidx = eq->hdwq; uint16_t cqid, id; if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) { @@ -14074,72 +14164,74 @@ work_cq: } /** - * lpfc_sli4_hba_process_cq - Process a fast-path event queue entry - * @phba: Pointer to HBA context object. - * @eqe: Pointer to fast-path event queue entry. + * __lpfc_sli4_hba_process_cq - Process a fast-path event queue entry + * @cq: Pointer to CQ to be processed * - * This routine process a event queue entry from the fast-path event queue. - * It will check the MajorCode and MinorCode to determine this is for a - * completion event on a completion queue, if not, an error shall be logged - * and just return. Otherwise, it will get to the corresponding completion - * queue and process all the entries on the completion queue, rearm the - * completion queue, and then return. + * This routine calls the cq processing routine with the handler for + * fast path CQEs. + * + * The CQ routine returns two values: the first is the calling status, + * which indicates whether work was queued to the background discovery + * thread. If true, the routine should wakeup the discovery thread; + * the second is the delay parameter. If non-zero, rather than rearming + * the CQ and yet another interrupt, the CQ handler should be queued so + * that it is processed in a subsequent polling action. The value of + * the delay indicates when to reschedule it. **/ static void -lpfc_sli4_hba_process_cq(struct work_struct *work) +__lpfc_sli4_hba_process_cq(struct lpfc_queue *cq) { - struct lpfc_queue *cq = - container_of(work, struct lpfc_queue, irqwork); struct lpfc_hba *phba = cq->phba; - struct lpfc_cqe *cqe; + unsigned long delay; bool workposted = false; - int ccount = 0; - /* Process all the entries to the CQ */ - while ((cqe = lpfc_sli4_cq_get(cq))) { -#ifdef CONFIG_SCSI_LPFC_DEBUG_FS - if (phba->ktime_on) - cq->isr_timestamp = ktime_get_ns(); - else - cq->isr_timestamp = 0; -#endif - workposted |= lpfc_sli4_fp_handle_cqe(phba, cq, cqe); - if (!(++ccount % cq->entry_repost)) - break; + /* process and rearm the CQ */ + workposted |= __lpfc_sli4_process_cq(phba, cq, lpfc_sli4_fp_handle_cqe, + &delay); + + if (delay) { + if (!queue_delayed_work_on(cq->chann, phba->wq, + &cq->sched_irqwork, delay)) + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "0367 Cannot schedule soft IRQ " + "for cqid=%d on CPU %d\n", + cq->queue_id, cq->chann); } - /* Track the max number of CQEs processed in 1 EQ */ - if (ccount > cq->CQ_max_cqe) - cq->CQ_max_cqe = ccount; - cq->assoc_qp->EQ_cqe_cnt += ccount; - - /* Catch the no cq entry condition */ - if (unlikely(ccount == 0)) - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0369 No entry from fast-path completion " - "queue fcpcqid=%d\n", cq->queue_id); - - /* In any case, flash and re-arm the CQ */ - phba->sli4_hba.sli4_cq_release(cq, LPFC_QUEUE_REARM); - /* wake up worker thread if there are works to be done */ if (workposted) lpfc_worker_wake_up(phba); } +/** + * lpfc_sli4_hba_process_cq - fast-path work handler when started by + * interrupt + * @work: pointer to work element + * + * translates from the work handler and calls the fast-path handler. + **/ static void -lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq) +lpfc_sli4_hba_process_cq(struct work_struct *work) { - struct lpfc_eqe *eqe; + struct lpfc_queue *cq = container_of(work, struct lpfc_queue, irqwork); - /* walk all the EQ entries and drop on the floor */ - while ((eqe = lpfc_sli4_eq_get(eq))) - ; - - /* Clear and re-arm the EQ */ - phba->sli4_hba.sli4_eq_release(eq, LPFC_QUEUE_REARM); + __lpfc_sli4_hba_process_cq(cq); } +/** + * lpfc_sli4_hba_process_cq - fast-path work handler when started by timer + * @work: pointer to work element + * + * translates from the work handler and calls the fast-path handler. + **/ +static void +lpfc_sli4_dly_hba_process_cq(struct work_struct *work) +{ + struct lpfc_queue *cq = container_of(to_delayed_work(work), + struct lpfc_queue, sched_irqwork); + + __lpfc_sli4_hba_process_cq(cq); +} /** * lpfc_sli4_hba_intr_handler - HBA interrupt handler to SLI-4 device @@ -14173,10 +14265,11 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) struct lpfc_hba *phba; struct lpfc_hba_eq_hdl *hba_eq_hdl; struct lpfc_queue *fpeq; - struct lpfc_eqe *eqe; unsigned long iflag; int ecount = 0; int hba_eqidx; + struct lpfc_eq_intr_info *eqi; + uint32_t icnt; /* Get the driver's phba structure from the dev_id */ hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id; @@ -14204,22 +14297,19 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) return IRQ_NONE; } - /* - * Process all the event on FCP fast-path EQ - */ - while ((eqe = lpfc_sli4_eq_get(fpeq))) { - lpfc_sli4_hba_handle_eqe(phba, eqe, hba_eqidx); - if (!(++ecount % fpeq->entry_repost)) - break; - fpeq->EQ_processed++; - } + eqi = phba->sli4_hba.eq_info; + icnt = this_cpu_inc_return(eqi->icnt); + fpeq->last_cpu = smp_processor_id(); - /* Track the max number of EQEs processed in 1 intr */ - if (ecount > fpeq->EQ_max_eqe) - fpeq->EQ_max_eqe = ecount; + if (icnt > LPFC_EQD_ISR_TRIGGER && + phba->cfg_irq_chann == 1 && + phba->cfg_auto_imax && + fpeq->q_mode != LPFC_MAX_AUTO_EQ_DELAY && + phba->sli.sli_flag & LPFC_SLI_USE_EQDR) + lpfc_sli4_mod_hba_eq_delay(phba, fpeq, LPFC_MAX_AUTO_EQ_DELAY); - /* Always clear and re-arm the fast-path EQ */ - phba->sli4_hba.sli4_eq_release(fpeq, LPFC_QUEUE_REARM); + /* process and rearm the EQ */ + ecount = lpfc_sli4_process_eq(phba, fpeq); if (unlikely(ecount == 0)) { fpeq->EQ_no_entry++; @@ -14307,6 +14397,9 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue) kfree(queue->rqbp); } + if (!list_empty(&queue->cpu_list)) + list_del(&queue->cpu_list); + if (!list_empty(&queue->wq_list)) list_del(&queue->wq_list); @@ -14355,6 +14448,7 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size, INIT_LIST_HEAD(&queue->wqfull_list); INIT_LIST_HEAD(&queue->page_list); INIT_LIST_HEAD(&queue->child_list); + INIT_LIST_HEAD(&queue->cpu_list); /* Set queue parameters now. If the system cannot provide memory * resources, the free routine needs to know what was allocated. @@ -14387,8 +14481,10 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size, } INIT_WORK(&queue->irqwork, lpfc_sli4_hba_process_cq); INIT_WORK(&queue->spwork, lpfc_sli4_sp_process_cq); + INIT_DELAYED_WORK(&queue->sched_irqwork, lpfc_sli4_dly_hba_process_cq); + INIT_DELAYED_WORK(&queue->sched_spwork, lpfc_sli4_dly_sp_process_cq); - /* entry_repost will be set during q creation */ + /* notify_interval will be set during q creation */ return queue; out_fail: @@ -14457,7 +14553,6 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, int cnt = 0, rc, length; uint32_t shdr_status, shdr_add_status; uint32_t dmult; - struct lpfc_register reg_data; int qidx; union lpfc_sli4_cfg_shdr *shdr; @@ -14478,16 +14573,7 @@ lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, if (!eq) continue; - /* save value last set */ - eq->q_mode = usdelay; - - /* write register */ - reg_data.word0 = 0; - bf_set(lpfc_sliport_eqdelay_id, ®_data, - eq->queue_id); - bf_set(lpfc_sliport_eqdelay_delay, ®_data, usdelay); - writel(reg_data.word0, - phba->sli4_hba.u.if_type2.EQDregaddr); + lpfc_sli4_mod_hba_eq_delay(phba, eq, usdelay); if (++cnt >= numq) break; @@ -14673,8 +14759,8 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint32_t imax) if (eq->queue_id == 0xFFFF) status = -ENXIO; eq->host_index = 0; - eq->hba_index = 0; - eq->entry_repost = LPFC_EQ_REPOST; + eq->notify_interval = LPFC_EQ_NOTIFY_INTRVL; + eq->max_proc_limit = LPFC_EQ_MAX_PROC_LIMIT; mempool_free(mbox, phba->mbox_mem_pool); return status; @@ -14814,8 +14900,8 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, cq->assoc_qid = eq->queue_id; cq->assoc_qp = eq; cq->host_index = 0; - cq->hba_index = 0; - cq->entry_repost = LPFC_CQ_REPOST; + cq->notify_interval = LPFC_CQ_NOTIFY_INTRVL; + cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit, cq->entry_count); if (cq->queue_id > phba->sli4_hba.cq_max) phba->sli4_hba.cq_max = cq->queue_id; @@ -15026,8 +15112,9 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, cq->assoc_qid = eq->queue_id; cq->assoc_qp = eq; cq->host_index = 0; - cq->hba_index = 0; - cq->entry_repost = LPFC_CQ_REPOST; + cq->notify_interval = LPFC_CQ_NOTIFY_INTRVL; + cq->max_proc_limit = min(phba->cfg_cq_max_proc_limit, + cq->entry_count); cq->chann = idx; rc = 0; @@ -15279,7 +15366,6 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, mq->subtype = subtype; mq->host_index = 0; mq->hba_index = 0; - mq->entry_repost = LPFC_MQ_REPOST; /* link the mq onto the parent cq child list */ list_add_tail(&mq->list, &cq->child_list); @@ -15545,7 +15631,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, wq->subtype = subtype; wq->host_index = 0; wq->hba_index = 0; - wq->entry_repost = LPFC_RELEASE_NOTIFICATION_INTERVAL; + wq->notify_interval = LPFC_WQ_NOTIFY_INTRVL; /* link the wq onto the parent cq child list */ list_add_tail(&wq->list, &cq->child_list); @@ -15739,7 +15825,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, hrq->subtype = subtype; hrq->host_index = 0; hrq->hba_index = 0; - hrq->entry_repost = LPFC_RQ_REPOST; + hrq->notify_interval = LPFC_RQ_NOTIFY_INTRVL; /* now create the data queue */ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, @@ -15832,7 +15918,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, drq->subtype = subtype; drq->host_index = 0; drq->hba_index = 0; - drq->entry_repost = LPFC_RQ_REPOST; + drq->notify_interval = LPFC_RQ_NOTIFY_INTRVL; /* link the header and data RQs onto the parent cq child list */ list_add_tail(&hrq->list, &cq->child_list); @@ -15990,7 +16076,7 @@ lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp, hrq->subtype = subtype; hrq->host_index = 0; hrq->hba_index = 0; - hrq->entry_repost = LPFC_RQ_REPOST; + hrq->notify_interval = LPFC_RQ_NOTIFY_INTRVL; drq->db_format = LPFC_DB_RING_FORMAT; drq->db_regaddr = phba->sli4_hba.RQDBregaddr; @@ -15999,7 +16085,7 @@ lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp, drq->subtype = subtype; drq->host_index = 0; drq->hba_index = 0; - drq->entry_repost = LPFC_RQ_REPOST; + drq->notify_interval = LPFC_RQ_NOTIFY_INTRVL; list_add_tail(&hrq->list, &cq->child_list); list_add_tail(&drq->list, &cq->child_list); @@ -16059,6 +16145,7 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq) /* sanity check on queue memory */ if (!eq) return -ENODEV; + mbox = mempool_alloc(eq->phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index accccca3a027..20566c506e5f 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -154,14 +154,41 @@ struct lpfc_queue { struct list_head child_list; struct list_head page_list; struct list_head sgl_list; + struct list_head cpu_list; uint32_t entry_count; /* Number of entries to support on the queue */ uint32_t entry_size; /* Size of each queue entry. */ - uint32_t entry_repost; /* Count of entries before doorbell is rung */ -#define LPFC_EQ_REPOST 8 -#define LPFC_MQ_REPOST 8 -#define LPFC_CQ_REPOST 64 -#define LPFC_RQ_REPOST 64 -#define LPFC_RELEASE_NOTIFICATION_INTERVAL 32 /* For WQs */ + uint32_t notify_interval; /* Queue Notification Interval + * For chip->host queues (EQ, CQ, RQ): + * specifies the interval (number of + * entries) where the doorbell is rung to + * notify the chip of entry consumption. + * For host->chip queues (WQ): + * specifies the interval (number of + * entries) where consumption CQE is + * requested to indicate WQ entries + * consumed by the chip. + * Not used on an MQ. + */ +#define LPFC_EQ_NOTIFY_INTRVL 16 +#define LPFC_CQ_NOTIFY_INTRVL 16 +#define LPFC_WQ_NOTIFY_INTRVL 16 +#define LPFC_RQ_NOTIFY_INTRVL 16 + uint32_t max_proc_limit; /* Queue Processing Limit + * For chip->host queues (EQ, CQ): + * specifies the maximum number of + * entries to be consumed in one + * processing iteration sequence. Queue + * will be rearmed after each iteration. + * Not used on an MQ, RQ or WQ. + */ +#define LPFC_EQ_MAX_PROC_LIMIT 256 +#define LPFC_CQ_MIN_PROC_LIMIT 64 +#define LPFC_CQ_MAX_PROC_LIMIT LPFC_CQE_EXP_COUNT // 4096 +#define LPFC_CQ_DEF_MAX_PROC_LIMIT LPFC_CQE_DEF_COUNT // 1024 +#define LPFC_CQ_MIN_THRESHOLD_TO_POLL 64 +#define LPFC_CQ_MAX_THRESHOLD_TO_POLL LPFC_CQ_DEF_MAX_PROC_LIMIT +#define LPFC_CQ_DEF_THRESHOLD_TO_POLL LPFC_CQ_DEF_MAX_PROC_LIMIT + uint32_t queue_claimed; /* indicates queue is being processed */ uint32_t queue_id; /* Queue ID assigned by the hardware */ uint32_t assoc_qid; /* Queue ID associated with, for CQ/WQ/MQ */ uint32_t host_index; /* The host's index for putting or getting */ @@ -217,11 +244,14 @@ struct lpfc_queue { #define RQ_buf_posted q_cnt_3 #define RQ_rcv_buf q_cnt_4 - struct work_struct irqwork; - struct work_struct spwork; + struct work_struct irqwork; + struct work_struct spwork; + struct delayed_work sched_irqwork; + struct delayed_work sched_spwork; uint64_t isr_timestamp; uint16_t hdwq; + uint16_t last_cpu; /* most recent cpu */ uint8_t qe_valid; struct lpfc_queue *assoc_qp; union sli4_qe qe[1]; /* array to index entries (must be last) */ @@ -608,6 +638,11 @@ struct lpfc_lock_stat { }; #endif +struct lpfc_eq_intr_info { + struct list_head list; + uint32_t icnt; +}; + /* SLI4 HBA data structure entries */ struct lpfc_sli4_hdw_queue { /* Pointers to the constructed SLI4 queues */ @@ -749,8 +784,10 @@ struct lpfc_sli4_hba { struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */ void (*sli4_eq_clr_intr)(struct lpfc_queue *q); - uint32_t (*sli4_eq_release)(struct lpfc_queue *q, bool arm); - uint32_t (*sli4_cq_release)(struct lpfc_queue *q, bool arm); + void (*sli4_write_eq_db)(struct lpfc_hba *phba, struct lpfc_queue *eq, + uint32_t count, bool arm); + void (*sli4_write_cq_db)(struct lpfc_hba *phba, struct lpfc_queue *cq, + uint32_t count, bool arm); /* Pointers to the constructed SLI4 queues */ struct lpfc_sli4_hdw_queue *hdwq; @@ -856,6 +893,7 @@ struct lpfc_sli4_hba { uint16_t num_online_cpu; uint16_t num_present_cpu; uint16_t curr_disp_cpu; + struct lpfc_eq_intr_info __percpu *eq_info; uint32_t conf_trunk; #define lpfc_conf_trunk_port0_WORD conf_trunk #define lpfc_conf_trunk_port0_SHIFT 0 @@ -1020,11 +1058,15 @@ int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *); int lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba); int lpfc_sli4_init_vpi(struct lpfc_vport *); inline void lpfc_sli4_eq_clr_intr(struct lpfc_queue *); -uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool); -uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool); +void lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm); +void lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm); inline void lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q); -uint32_t lpfc_sli4_if6_cq_release(struct lpfc_queue *q, bool arm); -uint32_t lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm); +void lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm); +void lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q, + uint32_t count, bool arm); void lpfc_sli4_fcfi_unreg(struct lpfc_hba *, uint16_t); int lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *, uint16_t); int lpfc_sli4_fcf_rr_read_fcf_rec(struct lpfc_hba *, uint16_t); From 75508a8b8b2de1b39b72e7dc31505cddc8354a01 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:34 -0800 Subject: [PATCH 1219/4277] scsi: lpfc: Utilize new IRQ API when allocating MSI-X vectors Current driver uses the older IRQ API for MSIX allocation Change driver to utilize pci_alloc_irq_vectors when allocating IRQ vectors. Make lpfc_cpu_affinity_check use pci_irq_get_affinity to determine how the kernel mapped all the IRQs. Remove msix_entries from SLI4 structure, replaced with pci_irq_vector() usage. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 162 +++------------------------------- 1 file changed, 13 insertions(+), 149 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 3fa7240a8e31..05919480e430 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -10554,103 +10554,6 @@ lpfc_find_eq_handle(struct lpfc_hba *phba, uint16_t hdwq) return 0; } -/** - * lpfc_find_phys_id_eq - Find the next EQ that corresponds to the specified - * Physical Id. - * @phba: pointer to lpfc hba data structure. - * @eqidx: EQ index - * @phys_id: CPU package physical id - */ -static uint16_t -lpfc_find_phys_id_eq(struct lpfc_hba *phba, uint16_t eqidx, uint16_t phys_id) -{ - struct lpfc_vector_map_info *cpup; - int cpu, desired_phys_id; - - desired_phys_id = LPFC_VECTOR_MAP_EMPTY; - - /* Find the desired phys_id for the specified EQ */ - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { - if ((cpup->irq != LPFC_VECTOR_MAP_EMPTY) && - (cpup->eq == eqidx)) { - desired_phys_id = cpup->phys_id; - break; - } - cpup++; - } - if (phys_id == desired_phys_id) - return eqidx; - - /* Find a EQ thats on the specified phys_id */ - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { - if ((cpup->irq != LPFC_VECTOR_MAP_EMPTY) && - (cpup->phys_id == phys_id)) - return cpup->eq; - cpup++; - } - return 0; -} - -/** - * lpfc_find_cpu_map - Find next available CPU map entry that matches the - * phys_id and core_id. - * @phba: pointer to lpfc hba data structure. - * @phys_id: CPU package physical id - * @core_id: CPU core id - * @hdwqidx: Hardware Queue index - * @eqidx: EQ index - * @isr_avail: Should an IRQ be associated with this entry - */ -static struct lpfc_vector_map_info * -lpfc_find_cpu_map(struct lpfc_hba *phba, uint16_t phys_id, uint16_t core_id, - uint16_t hdwqidx, uint16_t eqidx, int isr_avail) -{ - struct lpfc_vector_map_info *cpup; - int cpu; - - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { - /* Does the cpup match the one we are looking for */ - if ((cpup->phys_id == phys_id) && - (cpup->core_id == core_id)) { - /* If it has been already assigned, then skip it */ - if (cpup->hdwq != LPFC_VECTOR_MAP_EMPTY) { - cpup++; - continue; - } - /* Ensure we are on the same phys_id as the first one */ - if (!isr_avail) - cpup->eq = lpfc_find_phys_id_eq(phba, eqidx, - phys_id); - else - cpup->eq = eqidx; - - cpup->hdwq = hdwqidx; - if (isr_avail) { - cpup->irq = - pci_irq_vector(phba->pcidev, eqidx); - - /* Now affinitize to the selected CPU */ - irq_set_affinity_hint(cpup->irq, - get_cpu_mask(cpu)); - irq_set_status_flags(cpup->irq, - IRQ_NO_BALANCING); - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "3330 Set Affinity: CPU %d " - "EQ %d irq %d (HDWQ %x)\n", - cpu, cpup->eq, - cpup->irq, cpup->hdwq); - } - return cpup; - } - cpup++; - } - return 0; -} - #ifdef CONFIG_X86 /** * lpfc_find_hyper - Determine if the CPU map entry is hyper-threaded @@ -10693,11 +10596,11 @@ lpfc_find_hyper(struct lpfc_hba *phba, int cpu, static void lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) { - int i, j, idx, phys_id; + int i, cpu, idx, phys_id; int max_phys_id, min_phys_id; int max_core_id, min_core_id; struct lpfc_vector_map_info *cpup; - int cpu, eqidx, hdwqidx, isr_avail; + const struct cpumask *maskp; #ifdef CONFIG_X86 struct cpuinfo_x86 *cpuinfo; #endif @@ -10754,60 +10657,21 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) eqi->icnt = 0; } - /* - * If the number of IRQ vectors == number of CPUs, - * mapping is pretty simple: 1 to 1. - * This is the desired path if NVME is enabled. - */ - if (vectors == phba->sli4_hba.num_present_cpu) { - cpup = phba->sli4_hba.cpu_map; - for (idx = 0; idx < vectors; idx++) { + for (idx = 0; idx < phba->cfg_irq_chann; idx++) { + maskp = pci_irq_get_affinity(phba->pcidev, idx); + if (!maskp) + continue; + + for_each_cpu_and(cpu, maskp, cpu_present_mask) { + cpup = &phba->sli4_hba.cpu_map[cpu]; cpup->eq = idx; cpup->hdwq = idx; cpup->irq = pci_irq_vector(phba->pcidev, idx); - /* Now affinitize to the selected CPU */ - irq_set_affinity_hint( - pci_irq_vector(phba->pcidev, idx), - get_cpu_mask(idx)); - irq_set_status_flags(cpup->irq, IRQ_NO_BALANCING); - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3336 Set Affinity: CPU %d " - "EQ %d irq %d\n", - idx, cpup->eq, - pci_irq_vector(phba->pcidev, idx)); - cpup++; - } - return; - } - - idx = 0; - isr_avail = 1; - eqidx = 0; - hdwqidx = 0; - - /* Mapping is more complicated for this case. Hardware Queues are - * assigned in a "ping pong" fashion, ping pong-ing between the - * available phys_id's. - */ - while (idx < phba->sli4_hba.num_present_cpu) { - for (i = min_core_id; i <= max_core_id; i++) { - for (j = min_phys_id; j <= max_phys_id; j++) { - cpup = lpfc_find_cpu_map(phba, j, i, hdwqidx, - eqidx, isr_avail); - if (!cpup) - continue; - idx++; - hdwqidx++; - if (hdwqidx >= phba->cfg_hdw_queue) - hdwqidx = 0; - eqidx++; - if (eqidx >= phba->cfg_irq_chann) { - isr_avail = 0; - eqidx = 0; - } - } + "hdwq %d irq %d\n", + cpu, cpup->hdwq, cpup->irq); } } return; @@ -10834,7 +10698,7 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) vectors = phba->cfg_irq_chann; rc = pci_alloc_irq_vectors(phba->pcidev, - (phba->nvmet_support) ? 1 : 2, + 1, vectors, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); if (rc < 0) { lpfc_printf_log(phba, KERN_INFO, LOG_INIT, From 222e9239c60888b7c9331f4b3d0a99d2f27dca6b Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:35 -0800 Subject: [PATCH 1220/4277] scsi: lpfc: Resize cpu maps structures based on possible cpus The work done to date utilized the number of present cpus when sizing per-cpu structures. Structures should have been sized based on the max possible cpu count. Convert the driver over to possible cpu count for sizing allocation. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 23 ++++++++++++++-------- drivers/scsi/lpfc/lpfc_init.c | 32 +++++++++++++------------------ drivers/scsi/lpfc/lpfc_nvmet.c | 35 +++++++++++++++++++++------------- drivers/scsi/lpfc/lpfc_sli4.h | 2 +- 4 files changed, 51 insertions(+), 41 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 2864cb53b1e8..a114965a376c 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -5176,16 +5176,22 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, case 1: len += snprintf(buf + len, PAGE_SIZE-len, "fcp_cpu_map: HBA centric mapping (%d): " - "%d online CPUs\n", - phba->cfg_fcp_cpu_map, - phba->sli4_hba.num_online_cpu); + "%d of %d CPUs online from %d possible CPUs\n", + phba->cfg_fcp_cpu_map, num_online_cpus(), + num_present_cpus(), + phba->sli4_hba.num_possible_cpu); break; } - while (phba->sli4_hba.curr_disp_cpu < phba->sli4_hba.num_present_cpu) { + while (phba->sli4_hba.curr_disp_cpu < + phba->sli4_hba.num_possible_cpu) { cpup = &phba->sli4_hba.cpu_map[phba->sli4_hba.curr_disp_cpu]; - if (cpup->irq == LPFC_VECTOR_MAP_EMPTY) { + if (!cpu_present(phba->sli4_hba.curr_disp_cpu)) + len += snprintf(buf + len, PAGE_SIZE - len, + "CPU %02d not present\n", + phba->sli4_hba.curr_disp_cpu); + else if (cpup->irq == LPFC_VECTOR_MAP_EMPTY) { if (cpup->hdwq == LPFC_VECTOR_MAP_EMPTY) len += snprintf( buf + len, PAGE_SIZE - len, @@ -5225,14 +5231,15 @@ lpfc_fcp_cpu_map_show(struct device *dev, struct device_attribute *attr, /* display max number of CPUs keeping some margin */ if (phba->sli4_hba.curr_disp_cpu < - phba->sli4_hba.num_present_cpu && + phba->sli4_hba.num_possible_cpu && (len >= (PAGE_SIZE - 64))) { - len += snprintf(buf + len, PAGE_SIZE-len, "more...\n"); + len += snprintf(buf + len, + PAGE_SIZE - len, "more...\n"); break; } } - if (phba->sli4_hba.curr_disp_cpu == phba->sli4_hba.num_present_cpu) + if (phba->sli4_hba.curr_disp_cpu == phba->sli4_hba.num_possible_cpu) phba->sli4_hba.curr_disp_cpu = 0; return len; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 05919480e430..8ba2861db7b6 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -6373,8 +6373,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) u32 if_type; u32 if_fam; - phba->sli4_hba.num_online_cpu = num_online_cpus(); phba->sli4_hba.num_present_cpu = lpfc_present_cpu; + phba->sli4_hba.num_possible_cpu = num_possible_cpus(); phba->sli4_hba.curr_disp_cpu = 0; /* Get all the module params for configuring this host */ @@ -6796,7 +6796,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_fcf_rr_bmask; } - phba->sli4_hba.cpu_map = kcalloc(phba->sli4_hba.num_present_cpu, + phba->sli4_hba.cpu_map = kcalloc(phba->sli4_hba.num_possible_cpu, sizeof(struct lpfc_vector_map_info), GFP_KERNEL); if (!phba->sli4_hba.cpu_map) { @@ -6868,8 +6868,8 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) /* Free memory allocated for msi-x interrupt vector to CPU mapping */ kfree(phba->sli4_hba.cpu_map); + phba->sli4_hba.num_possible_cpu = 0; phba->sli4_hba.num_present_cpu = 0; - phba->sli4_hba.num_online_cpu = 0; phba->sli4_hba.curr_disp_cpu = 0; /* Free memory allocated for fast-path work queue handles */ @@ -10519,15 +10519,14 @@ lpfc_find_cpu_handle(struct lpfc_hba *phba, uint16_t id, int match) int cpu; /* Find the desired phys_id for the specified EQ */ - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + for_each_present_cpu(cpu) { + cpup = &phba->sli4_hba.cpu_map[cpu]; if ((match == LPFC_FIND_BY_EQ) && (cpup->irq != LPFC_VECTOR_MAP_EMPTY) && (cpup->eq == id)) return cpu; if ((match == LPFC_FIND_BY_HDWQ) && (cpup->hdwq == id)) return cpu; - cpup++; } return 0; } @@ -10545,11 +10544,10 @@ lpfc_find_eq_handle(struct lpfc_hba *phba, uint16_t hdwq) int cpu; /* Find the desired phys_id for the specified EQ */ - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + for_each_present_cpu(cpu) { + cpup = &phba->sli4_hba.cpu_map[cpu]; if (cpup->hdwq == hdwq) return cpup->eq; - cpup++; } return 0; } @@ -10569,15 +10567,13 @@ lpfc_find_hyper(struct lpfc_hba *phba, int cpu, struct lpfc_vector_map_info *cpup; int idx; - cpup = phba->sli4_hba.cpu_map; - for (idx = 0; idx < phba->sli4_hba.num_present_cpu; idx++) { + for_each_present_cpu(idx) { + cpup = &phba->sli4_hba.cpu_map[idx]; /* Does the cpup match the one we are looking for */ if ((cpup->phys_id == phys_id) && (cpup->core_id == core_id) && - (cpu != idx)) { + (cpu != idx)) return 1; - } - cpup++; } return 0; } @@ -10608,7 +10604,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) /* Init cpu_map array */ memset(phba->sli4_hba.cpu_map, 0xff, (sizeof(struct lpfc_vector_map_info) * - phba->sli4_hba.num_present_cpu)); + phba->sli4_hba.num_possible_cpu)); max_phys_id = 0; min_phys_id = 0xffff; @@ -10617,8 +10613,8 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) phys_id = 0; /* Update CPU map with physical id and core id of each CPU */ - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { + for_each_present_cpu(cpu) { + cpup = &phba->sli4_hba.cpu_map[cpu]; #ifdef CONFIG_X86 cpuinfo = &cpu_data(cpu); cpup->phys_id = cpuinfo->phys_proc_id; @@ -10645,8 +10641,6 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) max_core_id = cpup->core_id; if (cpup->core_id < min_core_id) min_core_id = cpup->core_id; - - cpup++; } for_each_possible_cpu(i) { diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 0d296aee2d82..0b27e8c5ae32 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1194,9 +1194,9 @@ lpfc_nvmet_cleanup_io_context(struct lpfc_hba *phba) /* Cycle the the entire CPU context list for every MRQ */ for (i = 0; i < phba->cfg_nvmet_mrq; i++) { - for (j = 0; j < phba->sli4_hba.num_present_cpu; j++) { + for_each_present_cpu(j) { + infop = lpfc_get_ctx_list(phba, j, i); __lpfc_nvmet_clean_io_for_cpu(phba, infop); - infop++; /* next */ } } kfree(phba->sli4_hba.nvmet_ctx_info); @@ -1211,14 +1211,14 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) union lpfc_wqe128 *wqe; struct lpfc_nvmet_ctx_info *last_infop; struct lpfc_nvmet_ctx_info *infop; - int i, j, idx; + int i, j, idx, cpu; lpfc_printf_log(phba, KERN_INFO, LOG_NVME, "6403 Allocate NVMET resources for %d XRIs\n", phba->sli4_hba.nvmet_xri_cnt); phba->sli4_hba.nvmet_ctx_info = kcalloc( - phba->sli4_hba.num_present_cpu * phba->cfg_nvmet_mrq, + phba->sli4_hba.num_possible_cpu * phba->cfg_nvmet_mrq, sizeof(struct lpfc_nvmet_ctx_info), GFP_KERNEL); if (!phba->sli4_hba.nvmet_ctx_info) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -1246,13 +1246,12 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) * of the IO completion. Thus a context that was allocated for MRQ A * whose IO completed on CPU B will be freed to cpuB/mrqA. */ - infop = phba->sli4_hba.nvmet_ctx_info; - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + for_each_possible_cpu(i) { for (j = 0; j < phba->cfg_nvmet_mrq; j++) { + infop = lpfc_get_ctx_list(phba, i, j); INIT_LIST_HEAD(&infop->nvmet_ctx_list); spin_lock_init(&infop->nvmet_ctx_list_lock); infop->nvmet_ctx_list_cnt = 0; - infop++; } } @@ -1262,8 +1261,10 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) * MRQ 1 cycling thru CPUs 0 - X, and so on. */ for (j = 0; j < phba->cfg_nvmet_mrq; j++) { - last_infop = lpfc_get_ctx_list(phba, 0, j); - for (i = phba->sli4_hba.num_present_cpu - 1; i >= 0; i--) { + last_infop = lpfc_get_ctx_list(phba, + cpumask_first(cpu_present_mask), + j); + for (i = phba->sli4_hba.num_possible_cpu - 1; i >= 0; i--) { infop = lpfc_get_ctx_list(phba, i, j); infop->nvmet_ctx_next_cpu = last_infop; last_infop = infop; @@ -1274,6 +1275,7 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) * received command on a per xri basis. */ idx = 0; + cpu = cpumask_first(cpu_present_mask); for (i = 0; i < phba->sli4_hba.nvmet_xri_cnt; i++) { ctx_buf = kzalloc(sizeof(*ctx_buf), GFP_KERNEL); if (!ctx_buf) { @@ -1327,7 +1329,7 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) * is MRQidx will be associated with CPUidx. This association * can change on the fly. */ - infop = lpfc_get_ctx_list(phba, idx, idx); + infop = lpfc_get_ctx_list(phba, cpu, idx); spin_lock(&infop->nvmet_ctx_list_lock); list_add_tail(&ctx_buf->list, &infop->nvmet_ctx_list); infop->nvmet_ctx_list_cnt++; @@ -1335,11 +1337,18 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) /* Spread ctx structures evenly across all MRQs */ idx++; - if (idx >= phba->cfg_nvmet_mrq) + if (idx >= phba->cfg_nvmet_mrq) { idx = 0; + cpu = cpumask_first(cpu_present_mask); + continue; + } + cpu = cpumask_next(cpu, cpu_present_mask); + if (cpu == nr_cpu_ids) + cpu = cpumask_first(cpu_present_mask); + } - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + for_each_present_cpu(i) { for (j = 0; j < phba->cfg_nvmet_mrq; j++) { infop = lpfc_get_ctx_list(phba, i, j); lpfc_printf_log(phba, KERN_INFO, LOG_NVME | LOG_INIT, @@ -1839,7 +1848,7 @@ lpfc_nvmet_replenish_context(struct lpfc_hba *phba, else get_infop = current_infop->nvmet_ctx_next_cpu; - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + for (i = 0; i < phba->sli4_hba.num_possible_cpu; i++) { if (get_infop == current_infop) { get_infop = get_infop->nvmet_ctx_next_cpu; continue; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 20566c506e5f..1e3d7f534eaa 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -890,7 +890,7 @@ struct lpfc_sli4_hba { /* CPU to vector mapping information */ struct lpfc_vector_map_info *cpu_map; - uint16_t num_online_cpu; + uint16_t num_possible_cpu; uint16_t num_present_cpu; uint16_t curr_disp_cpu; struct lpfc_eq_intr_info __percpu *eq_info; From b1684a0b42ecda41ae5e3ecd3084cafa532b4ca6 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:36 -0800 Subject: [PATCH 1221/4277] scsi: lpfc: Enable SCSI and NVME fc4s by default Now that performance mods don't split resources by protocol and enable both protocols by default, there's no reason not to enable concurrent SCSI and NVME fc4 support. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index a114965a376c..4006cb425f16 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -3772,9 +3772,9 @@ LPFC_ATTR_R(nvmet_mrq_post, * lpfc_enable_fc4_type: Defines what FC4 types are supported. * Supported Values: 1 - register just FCP * 3 - register both FCP and NVME - * Supported values are [1,3]. Default value is 1 + * Supported values are [1,3]. Default value is 3 */ -LPFC_ATTR_R(enable_fc4_type, LPFC_ENABLE_FCP, +LPFC_ATTR_R(enable_fc4_type, LPFC_ENABLE_BOTH, LPFC_ENABLE_FCP, LPFC_ENABLE_BOTH, "Enable FC4 Protocol support - FCP / NVME"); From c2017260eea2db62e7bb1b7cbb1759f1d11ed067 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:37 -0800 Subject: [PATCH 1222/4277] scsi: lpfc: Rework locking on SCSI io completion A scsi host lock is taken on every io completion to check whether the abort handler is waiting on the io completion. This is an expensive lock to take on all completion when rarely in an abort condition. Replace scsi host lock with command-specific lock. Synchronize completion and abort paths by new cmd lock. Ensure all flag changing and nulling of context pointers taken under lock. When adding lock to task management abort, realized it was missing other synchronization locks. Added that synchronization to match normal paths. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 1 + drivers/scsi/lpfc/lpfc_nvme.c | 48 +++++++++++------- drivers/scsi/lpfc/lpfc_scsi.c | 94 +++++++++++++++++------------------ drivers/scsi/lpfc/lpfc_sli.c | 42 ++++++++++++---- drivers/scsi/lpfc/lpfc_sli.h | 1 + 5 files changed, 112 insertions(+), 74 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 8ba2861db7b6..1188354ecd99 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4160,6 +4160,7 @@ lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc) lpfc_ncmd->dma_sgl = lpfc_ncmd->data; lpfc_ncmd->dma_phys_sgl = lpfc_ncmd->dma_handle; lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd; + spin_lock_init(&lpfc_ncmd->buf_lock); /* add the nvme buffer to a post list */ list_add_tail(&lpfc_ncmd->list, &post_nblist); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 9480257c5143..271ad42be7f4 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -969,15 +969,19 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, uint32_t *ptr; /* Sanity check on return of outstanding command */ - if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd) { - if (!lpfc_ncmd) { - lpfc_printf_vlog(vport, KERN_ERR, - LOG_NODE | LOG_NVME_IOERR, - "6071 Null lpfc_ncmd pointer. No " - "release, skip completion\n"); - return; - } + if (!lpfc_ncmd) { + lpfc_printf_vlog(vport, KERN_ERR, + LOG_NODE | LOG_NVME_IOERR, + "6071 Null lpfc_ncmd pointer. No " + "release, skip completion\n"); + return; + } + /* Guard against abort handler being called at same time */ + spin_lock(&lpfc_ncmd->buf_lock); + + if (!lpfc_ncmd->nvmeCmd) { + spin_unlock(&lpfc_ncmd->buf_lock); lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, "6066 Missing cmpl ptrs: lpfc_ncmd %p, " "nvmeCmd %p\n", @@ -1154,9 +1158,11 @@ out_err: if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) { freqpriv = nCmd->private; freqpriv->nvme_buf = NULL; - nCmd->done(nCmd); lpfc_ncmd->nvmeCmd = NULL; - } + spin_unlock(&lpfc_ncmd->buf_lock); + nCmd->done(nCmd); + } else + spin_unlock(&lpfc_ncmd->buf_lock); /* Call release with XB=1 to queue the IO into the abort list. */ lpfc_release_nvme_buf(phba, lpfc_ncmd); @@ -1781,6 +1787,9 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, } nvmereq_wqe = &lpfc_nbuf->cur_iocbq; + /* Guard against IO completion being called at same time */ + spin_lock(&lpfc_nbuf->buf_lock); + /* * The lpfc_nbuf and the mapped nvme_fcreq in the driver's * state must match the nvme_fcreq passed by the nvme @@ -1789,24 +1798,22 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, * has not seen it yet. */ if (lpfc_nbuf->nvmeCmd != pnvme_fcreq) { - spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, "6143 NVME req mismatch: " "lpfc_nbuf %p nvmeCmd %p, " "pnvme_fcreq %p. Skipping Abort xri x%x\n", lpfc_nbuf, lpfc_nbuf->nvmeCmd, pnvme_fcreq, nvmereq_wqe->sli4_xritag); - return; + goto out_unlock; } /* Don't abort IOs no longer on the pending queue. */ if (!(nvmereq_wqe->iocb_flag & LPFC_IO_ON_TXCMPLQ)) { - spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, "6142 NVME IO req %p not queued - skipping " "abort req xri x%x\n", pnvme_fcreq, nvmereq_wqe->sli4_xritag); - return; + goto out_unlock; } atomic_inc(&lport->xmt_fcp_abort); @@ -1816,24 +1823,22 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, /* Outstanding abort is in progress */ if (nvmereq_wqe->iocb_flag & LPFC_DRIVER_ABORTED) { - spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, "6144 Outstanding NVME I/O Abort Request " "still pending on nvme_fcreq %p, " "lpfc_ncmd %p xri x%x\n", pnvme_fcreq, lpfc_nbuf, nvmereq_wqe->sli4_xritag); - return; + goto out_unlock; } abts_buf = __lpfc_sli_get_iocbq(phba); if (!abts_buf) { - spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, "6136 No available abort wqes. Skipping " "Abts req for nvme_fcreq %p xri x%x\n", pnvme_fcreq, nvmereq_wqe->sli4_xritag); - return; + goto out_unlock; } /* Ready - mark outstanding as aborted by driver. */ @@ -1877,6 +1882,7 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, abts_buf->vport = vport; abts_buf->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl; ret_val = lpfc_sli4_issue_wqe(phba, lpfc_nbuf->hdwq, abts_buf); + spin_unlock(&lpfc_nbuf->buf_lock); spin_unlock_irqrestore(&phba->hbalock, flags); if (ret_val) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, @@ -1892,6 +1898,12 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, "ox_id x%x on reqtag x%x\n", nvmereq_wqe->sli4_xritag, abts_buf->iotag); + return; + +out_unlock: + spin_unlock(&lpfc_nbuf->buf_lock); + spin_unlock_irqrestore(&phba->hbalock, flags); + return; } /* Declare and initialization an instance of the FC NVME template. */ diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index a827520789f1..c0065452bc9a 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -506,6 +506,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) psb->status = IOSTAT_SUCCESS; /* Put it back into the SCSI buffer list */ psb->cur_iocbq.context1 = psb; + spin_lock_init(&psb->buf_lock); lpfc_release_scsi_buf_s3(phba, psb); } @@ -712,7 +713,6 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, lpfc_cmd->cur_iocbq.iocb_flag = LPFC_IO_FCP; lpfc_cmd->prot_seg_cnt = 0; lpfc_cmd->seg_cnt = 0; - lpfc_cmd->waitq = NULL; lpfc_cmd->timeout = 0; lpfc_cmd->flags = 0; lpfc_cmd->start_time = jiffies; @@ -3651,10 +3651,17 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, int cpu; #endif + /* Guard against abort handler being called at same time */ + spin_lock(&lpfc_cmd->buf_lock); + /* Sanity check on return of outstanding command */ cmd = lpfc_cmd->pCmd; - if (!cmd) + if (!cmd) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "2621 IO completion: Not an active IO\n"); + spin_unlock(&lpfc_cmd->buf_lock); return; + } idx = lpfc_cmd->cur_iocbq.hba_wqidx; if (phba->sli4_hba.hdwq) @@ -3860,29 +3867,24 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); - /* If pCmd was set to NULL from abort path, do not call scsi_done */ - if (xchg(&lpfc_cmd->pCmd, NULL) == NULL) { - lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, - "5688 FCP cmd already NULL, sid: 0x%06x, " - "did: 0x%06x, oxid: 0x%04x\n", - vport->fc_myDID, - (pnode) ? pnode->nlp_DID : 0, - phba->sli_rev == LPFC_SLI_REV4 ? - lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff); - return; - } + lpfc_cmd->pCmd = NULL; + spin_unlock(&lpfc_cmd->buf_lock); /* The sdev is not guaranteed to be valid post scsi_done upcall. */ cmd->scsi_done(cmd); /* - * If there is a thread waiting for command completion + * If there is an abort thread waiting for command completion * wake up the thread. */ - spin_lock_irqsave(shost->host_lock, flags); - if (lpfc_cmd->waitq) - wake_up(lpfc_cmd->waitq); - spin_unlock_irqrestore(shost->host_lock, flags); + spin_lock(&lpfc_cmd->buf_lock); + if (unlikely(lpfc_cmd->cur_iocbq.iocb_flag & LPFC_DRIVER_ABORTED)) { + lpfc_cmd->cur_iocbq.iocb_flag &= ~LPFC_DRIVER_ABORTED; + if (lpfc_cmd->waitq) + wake_up(lpfc_cmd->waitq); + lpfc_cmd->waitq = NULL; + } + spin_unlock(&lpfc_cmd->buf_lock); lpfc_release_scsi_buf(phba, lpfc_cmd); } @@ -4563,24 +4565,29 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) if (status != 0 && status != SUCCESS) return status; + lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble; + if (!lpfc_cmd) + return ret; + spin_lock_irqsave(&phba->hbalock, flags); /* driver queued commands are in process of being flushed */ if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) { - spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "3168 SCSI Layer abort requested I/O has been " "flushed by LLD.\n"); - return FAILED; + ret = FAILED; + goto out_unlock; } - lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble; - if (!lpfc_cmd || !lpfc_cmd->pCmd) { - spin_unlock_irqrestore(&phba->hbalock, flags); + /* Guard against IO completion being called at same time */ + spin_lock(&lpfc_cmd->buf_lock); + + if (!lpfc_cmd->pCmd) { lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "2873 SCSI Layer I/O Abort Request IO CMPL Status " "x%x ID %d LUN %llu\n", SUCCESS, cmnd->device->id, cmnd->device->lun); - return SUCCESS; + goto out_unlock_buf; } iocb = &lpfc_cmd->cur_iocbq; @@ -4588,19 +4595,17 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].fcp_wq->pring; if (!pring_s4) { ret = FAILED; - goto out_unlock; + goto out_unlock_buf; } spin_lock(&pring_s4->ring_lock); } /* the command is in process of being cancelled */ if (!(iocb->iocb_flag & LPFC_IO_ON_TXCMPLQ)) { - if (phba->sli_rev == LPFC_SLI_REV4) - spin_unlock(&pring_s4->ring_lock); - spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "3169 SCSI Layer abort requested I/O has been " "cancelled by LLD.\n"); - return FAILED; + ret = FAILED; + goto out_unlock_ring; } /* * If pCmd field of the corresponding lpfc_io_buf structure @@ -4609,12 +4614,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) * see the completion before the eh fired. Just return SUCCESS. */ if (lpfc_cmd->pCmd != cmnd) { - if (phba->sli_rev == LPFC_SLI_REV4) - spin_unlock(&pring_s4->ring_lock); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "3170 SCSI Layer abort requested I/O has been " "completed by LLD.\n"); - goto out_unlock; + goto out_unlock_ring; } BUG_ON(iocb->context1 != lpfc_cmd); @@ -4625,6 +4628,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) "3389 SCSI Layer I/O Abort Request is pending\n"); if (phba->sli_rev == LPFC_SLI_REV4) spin_unlock(&pring_s4->ring_lock); + spin_unlock(&lpfc_cmd->buf_lock); spin_unlock_irqrestore(&phba->hbalock, flags); goto wait_for_cmpl; } @@ -4632,9 +4636,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) abtsiocb = __lpfc_sli_get_iocbq(phba); if (abtsiocb == NULL) { ret = FAILED; - if (phba->sli_rev == LPFC_SLI_REV4) - spin_unlock(&pring_s4->ring_lock); - goto out_unlock; + goto out_unlock_ring; } /* Indicate the IO is being aborted by the driver. */ @@ -4684,24 +4686,18 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) /* no longer need the lock after this point */ spin_unlock_irqrestore(&phba->hbalock, flags); - if (ret_val == IOCB_ERROR) { - if (phba->sli_rev == LPFC_SLI_REV4) - spin_lock_irqsave(&pring_s4->ring_lock, flags); - else - spin_lock_irqsave(&phba->hbalock, flags); /* Indicate the IO is not being aborted by the driver. */ iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED; lpfc_cmd->waitq = NULL; - if (phba->sli_rev == LPFC_SLI_REV4) - spin_unlock_irqrestore(&pring_s4->ring_lock, flags); - else - spin_unlock_irqrestore(&phba->hbalock, flags); + spin_unlock(&lpfc_cmd->buf_lock); lpfc_sli_release_iocbq(phba, abtsiocb); ret = FAILED; goto out; } + spin_unlock(&lpfc_cmd->buf_lock); + if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_sli_handle_fast_ring_event(phba, &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ); @@ -4712,9 +4708,7 @@ wait_for_cmpl: (lpfc_cmd->pCmd != cmnd), msecs_to_jiffies(2*vport->cfg_devloss_tmo*1000)); - spin_lock_irqsave(shost->host_lock, flags); - lpfc_cmd->waitq = NULL; - spin_unlock_irqrestore(shost->host_lock, flags); + spin_lock(&lpfc_cmd->buf_lock); if (lpfc_cmd->pCmd == cmnd) { ret = FAILED; @@ -4725,8 +4719,14 @@ wait_for_cmpl: iocb->sli4_xritag, ret, cmnd->device->id, cmnd->device->lun); } + spin_unlock(&lpfc_cmd->buf_lock); goto out; +out_unlock_ring: + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring_s4->ring_lock); +out_unlock_buf: + spin_unlock(&lpfc_cmd->buf_lock); out_unlock: spin_unlock_irqrestore(&phba->hbalock, flags); out: diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index ec385d3413f2..d548066a7e91 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -11659,7 +11659,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, IOCB_t *icmd; int sum, i, ret_val; unsigned long iflags; - struct lpfc_sli_ring *pring_s4; + struct lpfc_sli_ring *pring_s4 = NULL; spin_lock_irqsave(&phba->hbalock, iflags); @@ -11677,17 +11677,46 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, cmd) != 0) continue; + /* Guard against IO completion being called at same time */ + lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq); + spin_lock(&lpfc_cmd->buf_lock); + + if (!lpfc_cmd->pCmd) { + spin_unlock(&lpfc_cmd->buf_lock); + continue; + } + + if (phba->sli_rev == LPFC_SLI_REV4) { + pring_s4 = + phba->sli4_hba.hdwq[iocbq->hba_wqidx].fcp_wq->pring; + if (!pring_s4) { + spin_unlock(&lpfc_cmd->buf_lock); + continue; + } + /* Note: both hbalock and ring_lock must be set here */ + spin_lock(&pring_s4->ring_lock); + } + /* * If the iocbq is already being aborted, don't take a second * action, but do count it. */ - if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED) + if ((iocbq->iocb_flag & LPFC_DRIVER_ABORTED) || + !(iocbq->iocb_flag & LPFC_IO_ON_TXCMPLQ)) { + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring_s4->ring_lock); + spin_unlock(&lpfc_cmd->buf_lock); continue; + } /* issue ABTS for this IOCB based on iotag */ abtsiocbq = __lpfc_sli_get_iocbq(phba); - if (abtsiocbq == NULL) + if (!abtsiocbq) { + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring_s4->ring_lock); + spin_unlock(&lpfc_cmd->buf_lock); continue; + } icmd = &iocbq->iocb; abtsiocbq->iocb.un.acxri.abortType = ABORT_TYPE_ABTS; @@ -11708,7 +11737,6 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, if (iocbq->iocb_flag & LPFC_IO_FOF) abtsiocbq->iocb_flag |= LPFC_IO_FOF; - lpfc_cmd = container_of(iocbq, struct lpfc_io_buf, cur_iocbq); ndlp = lpfc_cmd->rdata->pnode; if (lpfc_is_link_up(phba) && @@ -11727,11 +11755,6 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, iocbq->iocb_flag |= LPFC_DRIVER_ABORTED; if (phba->sli_rev == LPFC_SLI_REV4) { - pring_s4 = lpfc_sli4_calc_ring(phba, abtsiocbq); - if (!pring_s4) - continue; - /* Note: both hbalock and ring_lock must be set here */ - spin_lock(&pring_s4->ring_lock); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, abtsiocbq, 0); spin_unlock(&pring_s4->ring_lock); @@ -11740,6 +11763,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, abtsiocbq, 0); } + spin_unlock(&lpfc_cmd->buf_lock); if (ret_val == IOCB_ERROR) __lpfc_sli_release_iocbq(phba, abtsiocbq); diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 658a40d3b1a2..d203a666497f 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -387,6 +387,7 @@ struct lpfc_io_buf { * to dma_unmap_sg. */ unsigned long start_time; + spinlock_t buf_lock; /* lock used in case of simultaneous abort */ bool expedite; /* this is an expedite io_buf */ union { From f6e84790520ac7a14abd921db5a2a1e790e363f9 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:38 -0800 Subject: [PATCH 1223/4277] scsi: lpfc: Fix default driver parameter collision for allowing NPIV support The conversion to enable SCSI and NVME fc4 support ran into an issue with NPIV support. With NVME, NPIV is not currently supported, but with SCSI it was. The driver reverted to its lowest setting meaning NPIV with SCSI was not allowed. Convert the NPIV checks and implementation so that SCSI can continue to allow NPIV support. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 3 ++- drivers/scsi/lpfc/lpfc_attr.c | 4 ++-- drivers/scsi/lpfc/lpfc_ct.c | 16 ++++++------- drivers/scsi/lpfc/lpfc_debugfs.c | 4 ++-- drivers/scsi/lpfc/lpfc_els.c | 4 ++-- drivers/scsi/lpfc/lpfc_hbadisc.c | 36 +++++++++++++----------------- drivers/scsi/lpfc/lpfc_init.c | 3 +++ drivers/scsi/lpfc/lpfc_nportdisc.c | 8 +++---- drivers/scsi/lpfc/lpfc_scsi.c | 2 +- drivers/scsi/lpfc/lpfc_vport.c | 25 +++++---------------- 10 files changed, 46 insertions(+), 59 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 0bc498172add..b710994a352e 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -462,6 +462,7 @@ struct lpfc_vport { uint32_t cfg_use_adisc; uint32_t cfg_discovery_threads; uint32_t cfg_log_verbose; + uint32_t cfg_enable_fc4_type; uint32_t cfg_max_luns; uint32_t cfg_enable_da_id; uint32_t cfg_max_scsicmpl_time; @@ -860,6 +861,7 @@ struct lpfc_hba { uint32_t cfg_prot_guard; uint32_t cfg_hostmem_hgp; uint32_t cfg_log_verbose; + uint32_t cfg_enable_fc4_type; uint32_t cfg_aer_support; uint32_t cfg_sriov_nr_virtfn; uint32_t cfg_request_firmware_upgrade; @@ -880,7 +882,6 @@ struct lpfc_hba { uint32_t cfg_ras_fwlog_level; uint32_t cfg_ras_fwlog_buffsize; uint32_t cfg_ras_fwlog_func; - uint32_t cfg_enable_fc4_type; uint32_t cfg_enable_bbcr; /* Enable BB Credit Recovery */ uint32_t cfg_enable_dpp; /* Enable Direct Packet Push */ #define LPFC_ENABLE_FCP 1 diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 4006cb425f16..212bfae1966a 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -160,7 +160,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, int len = 0; char tmp[LPFC_MAX_NVME_INFO_TMP_LEN] = {0}; - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { + if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { len = scnprintf(buf, PAGE_SIZE, "NVME Disabled\n"); return len; } @@ -519,7 +519,7 @@ lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr, int i; char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || + if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || (phba->sli_rev != LPFC_SLI_REV4)) return 0; diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 552da8bf43e4..98faa3aae35c 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1656,16 +1656,16 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, CtReq->un.rft.PortId = cpu_to_be32(vport->fc_myDID); /* Register FC4 FCP type if enabled. */ - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) + if (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH || + vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP) CtReq->un.rft.fcpReg = 1; /* Register NVME type if enabled. Defined LE and swapped. * rsvd[0] is used as word1 because of the hard-coded * word0 usage in the ct_request data structure. */ - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) + if (vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH || + vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME) CtReq->un.rft.rsvd[0] = cpu_to_be32(LPFC_FC4_TYPE_BITMASK); @@ -1732,8 +1732,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, * caller can specify NVME (type x28) as well. But only * these that FC4 type is supported. */ - if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) && + if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) && (context == FC_TYPE_NVME)) { if ((vport == phba->pport) && phba->nvmet_support) { CtReq->un.rff.fbits = (FC4_FEATURE_TARGET | @@ -1744,8 +1744,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, } CtReq->un.rff.type_code = context; - } else if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) && + } else if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) && (context == FC_TYPE_FCP)) CtReq->un.rff.type_code = context; diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 16b461778e59..19d13a8789b9 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1166,7 +1166,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) phba->sli4_hba.nvmet_io_wait_total, tot); } else { - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) + if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return len; localport = vport->localport; @@ -1264,7 +1264,7 @@ lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size) int i; char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0}; - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || + if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP) || (phba->sli_rev != LPFC_SLI_REV4)) return 0; diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index b3a4789468c3..3df2cfd02b41 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2827,8 +2827,8 @@ out: !(vport->fc_flag & FC_PT2PT_PLOGI)) { phba->pport->fc_myDID = 0; - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { if (phba->nvmet_support) lpfc_nvmet_update_targetport(phba); else diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 62689a06c188..47608d4b8e8c 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -857,10 +857,9 @@ lpfc_port_link_failure(struct lpfc_vport *vport) void lpfc_linkdown_port(struct lpfc_vport *vport) { - struct lpfc_hba *phba = vport->phba; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); - if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME) fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKDOWN, 0); @@ -923,8 +922,8 @@ lpfc_linkdown(struct lpfc_hba *phba) vports[i]->fc_myDID = 0; - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { if (phba->nvmet_support) lpfc_nvmet_update_targetport(phba); else @@ -1010,7 +1009,7 @@ lpfc_linkup_port(struct lpfc_vport *vport) (vport != phba->pport)) return; - if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME) fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKUP, 0); @@ -3658,8 +3657,8 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_unlock_irq(shost->host_lock); vport->fc_myDID = 0; - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { if (phba->nvmet_support) lpfc_nvmet_update_targetport(phba); else @@ -3921,11 +3920,9 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) int lpfc_issue_gidft(struct lpfc_vport *vport) { - struct lpfc_hba *phba = vport->phba; - /* Good status, issue CT Request to NameServer */ - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) { + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) { if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_FCP)) { /* Cannot issue NameServer FCP Query, so finish up * discovery @@ -3940,8 +3937,8 @@ lpfc_issue_gidft(struct lpfc_vport *vport) vport->gidft_inp++; } - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_NVME)) { /* Cannot issue NameServer NVME Query, so finish up * discovery @@ -4057,12 +4054,12 @@ out: lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0); lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0); - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, FC_TYPE_FCP); - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, FC_TYPE_NVME); @@ -4098,7 +4095,7 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) struct fc_rport_identifiers rport_ids; struct lpfc_hba *phba = vport->phba; - if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME) + if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME) return; /* Remote port has reappeared. Re-register w/ FC transport */ @@ -4173,9 +4170,8 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp) { struct fc_rport *rport = ndlp->rport; struct lpfc_vport *vport = ndlp->vport; - struct lpfc_hba *phba = vport->phba; - if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME) + if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME) return; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 1188354ecd99..563b6009ffce 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4290,6 +4290,9 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) vport->fc_rscn_flush = 0; lpfc_get_vport_cfgparam(vport); + /* Adjust value in vport */ + vport->cfg_enable_fc4_type = phba->cfg_enable_fc4_type; + shost->unique_id = instance; shost->max_id = LPFC_MAX_TARGET; shost->max_lun = vport->cfg_max_luns; diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 96bc3789a166..acfce67dacde 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -825,7 +825,7 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, "rport rolechg: role:x%x did:x%x flg:x%x", roles, ndlp->nlp_DID, ndlp->nlp_flag); - if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + if (vport->cfg_enable_fc4_type != LPFC_ENABLE_NVME) fc_remote_port_rolechg(rport, roles); } } @@ -1789,8 +1789,8 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, * is configured try it. */ ndlp->nlp_fc4_type |= NLP_FC4_FCP; - if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if ((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { ndlp->nlp_fc4_type |= NLP_FC4_NVME; /* We need to update the localport also */ lpfc_nvme_update_localport(vport); @@ -1804,7 +1804,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, * should just issue PRLI for FCP. Otherwise issue * GFT_ID to determine if remote port supports NVME. */ - if (phba->cfg_enable_fc4_type != LPFC_ENABLE_FCP) { + if (vport->cfg_enable_fc4_type != LPFC_ENABLE_FCP) { rc = lpfc_ns_cmd(vport, SLI_CTNS_GFT_ID, 0, ndlp->nlp_DID); return ndlp->nlp_state; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index c0065452bc9a..af263b0e8bad 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -530,7 +530,7 @@ lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) unsigned long iflag = 0; int idx; - if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + if (!(vport->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) return; spin_lock_irqsave(&phba->hbalock, iflag); diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 102a011ff6d4..0d712eb04e2b 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -313,11 +313,11 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) goto error_out; } - /* NPIV is not supported if HBA has NVME enabled */ - if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + /* NPIV is not supported if HBA has NVME Target enabled */ + if (phba->nvmet_support) { lpfc_printf_log(phba, KERN_ERR, LOG_VPORT, "3189 Create VPORT failed: " - "NPIV is not supported on NVME\n"); + "NPIV is not supported on NVME Target\n"); rc = VPORT_INVAL; goto error_out; } @@ -403,6 +403,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) /* Set the DFT_LUN_Q_DEPTH accordingly */ vport->cfg_lun_queue_depth = phba->pport->cfg_lun_queue_depth; + /* Only the physical port can support NVME for now */ + vport->cfg_enable_fc4_type = LPFC_ENABLE_FCP; + *(struct lpfc_vport **)fc_vport->dd_data = vport; vport->fc_vport = fc_vport; @@ -415,22 +418,6 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) vport->fdmi_port_mask = phba->pport->fdmi_port_mask; } - if ((phba->nvmet_support == 0) && - ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || - (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME))) { - /* Create NVME binding with nvme_fc_transport. This - * ensures the vport is initialized. - */ - rc = lpfc_nvme_create_localport(vport); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "6003 %s status x%x\n", - "NVME registration failed, ", - rc); - goto error_out; - } - } - /* * In SLI4, the vpi must be activated before it can be used * by the port. From 472e146d1cf3410a898b49834500fa9e33ac41a2 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:39 -0800 Subject: [PATCH 1224/4277] scsi: lpfc: Correct upcalling nvmet_fc transport during io done downcall When the transport calls into the lpfc target to release an IO job structure, which corresponds to an exchange, and if the driver was waiting for an exchange in order to post a previously received command to the transport, the driver immediately takes the IO job and reuses the context for the prior command and calls nvmet_fc_rcv_fcp_req() to tell the transport about a newly received command. Problem is, the execution of the IO job release may be in the context of the back end driver and its bio completion handlers, thus it may be in a irq context and protection code kicks in in the bio and request layers that are subsequently called. Rework lpfc so that instead of immediately upcalling, queue it to a deferred work thread and have the thread make the upcall. Took advantage of this change to remove duplicated code with the normal command receive path that preps the IO job and upcalls nvmet_fc. Created a common routine both paths use. Also corrected some errors that were found during review of the context freeing and reuse - basically unlocked operations and a somewhat disjoint set of calls to release associated job elements. Cleaned up this path and added locks for coherency. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 1 + drivers/scsi/lpfc/lpfc_nvmet.c | 247 ++++++++++++++++++--------------- drivers/scsi/lpfc/lpfc_nvmet.h | 1 + 3 files changed, 137 insertions(+), 112 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index b710994a352e..ea97d82f99f9 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -144,6 +144,7 @@ struct lpfc_nvmet_ctxbuf { struct lpfc_nvmet_rcv_ctx *context; struct lpfc_iocbq *iocbq; struct lpfc_sglq *sglq; + struct work_struct defer_work; }; struct lpfc_dma_pool { diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 0b27e8c5ae32..0d10dfc74018 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -73,6 +73,9 @@ static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *, uint32_t, uint16_t); static void lpfc_nvmet_wqfull_flush(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_nvmet_rcv_ctx *); +static void lpfc_nvmet_fcp_rqst_defer_work(struct work_struct *); + +static void lpfc_nvmet_process_rcv_fcp_req(struct lpfc_nvmet_ctxbuf *ctx_buf); static union lpfc_wqe128 lpfc_tsend_cmd_template; static union lpfc_wqe128 lpfc_treceive_cmd_template; @@ -220,21 +223,19 @@ lpfc_nvmet_cmd_template(void) void lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp) { - unsigned long iflag; + lockdep_assert_held(&ctxp->ctxlock); lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, "6313 NVMET Defer ctx release xri x%x flg x%x\n", ctxp->oxid, ctxp->flag); - spin_lock_irqsave(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag); - if (ctxp->flag & LPFC_NVMET_CTX_RLS) { - spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock, - iflag); + if (ctxp->flag & LPFC_NVMET_CTX_RLS) return; - } + ctxp->flag |= LPFC_NVMET_CTX_RLS; + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_add_tail(&ctxp->list, &phba->sli4_hba.lpfc_abts_nvmet_ctx_list); - spin_unlock_irqrestore(&phba->sli4_hba.abts_nvmet_buf_list_lock, iflag); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); } /** @@ -325,7 +326,7 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) struct rqb_dmabuf *nvmebuf; struct lpfc_nvmet_ctx_info *infop; uint32_t *payload; - uint32_t size, oxid, sid, rc; + uint32_t size, oxid, sid; int cpu; unsigned long iflag; @@ -341,6 +342,20 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) "6411 NVMET free, already free IO x%x: %d %d\n", ctxp->oxid, ctxp->state, ctxp->entry_cnt); } + + if (ctxp->rqb_buffer) { + nvmebuf = ctxp->rqb_buffer; + spin_lock_irqsave(&ctxp->ctxlock, iflag); + ctxp->rqb_buffer = NULL; + if (ctxp->flag & LPFC_NVMET_CTX_REUSE_WQ) { + ctxp->flag &= ~LPFC_NVMET_CTX_REUSE_WQ; + spin_unlock_irqrestore(&ctxp->ctxlock, iflag); + nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); + } else { + spin_unlock_irqrestore(&ctxp->ctxlock, iflag); + lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ + } + } ctxp->state = LPFC_NVMET_STE_FREE; spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag); @@ -388,46 +403,30 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf) } #endif atomic_inc(&tgtp->rcv_fcp_cmd_in); - /* - * The calling sequence should be: - * nvmet_fc_rcv_fcp_req->lpfc_nvmet_xmt_fcp_op/cmp- req->done - * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp. - * When we return from nvmet_fc_rcv_fcp_req, all relevant info - * the NVME command / FC header is stored. - * A buffer has already been reposted for this IO, so just free - * the nvmebuf. + + /* flag new work queued, replacement buffer has already + * been reposted */ - rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req, - payload, size); + spin_lock_irqsave(&ctxp->ctxlock, iflag); + ctxp->flag |= LPFC_NVMET_CTX_REUSE_WQ; + spin_unlock_irqrestore(&ctxp->ctxlock, iflag); - /* Process FCP command */ - if (rc == 0) { - ctxp->rqb_buffer = NULL; - atomic_inc(&tgtp->rcv_fcp_cmd_out); - nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); - return; + if (!queue_work(phba->wq, &ctx_buf->defer_work)) { + atomic_inc(&tgtp->rcv_fcp_cmd_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "6181 Unable to queue deferred work " + "for oxid x%x. " + "FCP Drop IO [x%x x%x x%x]\n", + ctxp->oxid, + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_out), + atomic_read(&tgtp->xmt_fcp_release)); + + spin_lock_irqsave(&ctxp->ctxlock, iflag); + lpfc_nvmet_defer_release(phba, ctxp); + spin_unlock_irqrestore(&ctxp->ctxlock, iflag); + lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid); } - - /* Processing of FCP command is deferred */ - if (rc == -EOVERFLOW) { - lpfc_nvmeio_data(phba, - "NVMET RCV BUSY: xri x%x sz %d " - "from %06x\n", - oxid, size, sid); - atomic_inc(&tgtp->rcv_fcp_cmd_out); - return; - } - atomic_inc(&tgtp->rcv_fcp_cmd_drop); - lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, - "2582 FCP Drop IO x%x: err x%x: x%x x%x x%x\n", - ctxp->oxid, rc, - atomic_read(&tgtp->rcv_fcp_cmd_in), - atomic_read(&tgtp->rcv_fcp_cmd_out), - atomic_read(&tgtp->xmt_fcp_release)); - - lpfc_nvmet_defer_release(phba, ctxp); - lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid); - nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); return; } spin_unlock_irqrestore(&phba->sli4_hba.nvmet_io_wait_lock, iflag); @@ -1113,6 +1112,8 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport, container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req); struct rqb_dmabuf *nvmebuf = ctxp->rqb_buffer; struct lpfc_hba *phba = ctxp->phba; + unsigned long iflag; + lpfc_nvmeio_data(phba, "NVMET DEFERRCV: xri x%x sz %d CPU %02x\n", ctxp->oxid, ctxp->size, smp_processor_id()); @@ -1131,6 +1132,9 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport, /* Free the nvmebuf since a new buffer already replaced it */ nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); + spin_lock_irqsave(&ctxp->ctxlock, iflag); + ctxp->rqb_buffer = NULL; + spin_unlock_irqrestore(&ctxp->ctxlock, iflag); } static struct nvmet_fc_target_template lpfc_tgttemplate = { @@ -1323,6 +1327,7 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) "6407 Ran out of NVMET XRIs\n"); return -ENOMEM; } + INIT_WORK(&ctx_buf->defer_work, lpfc_nvmet_fcp_rqst_defer_work); /* * Add ctx to MRQidx context list. Our initial assumption @@ -1824,6 +1829,86 @@ dropit: #endif } +static void +lpfc_nvmet_process_rcv_fcp_req(struct lpfc_nvmet_ctxbuf *ctx_buf) +{ +#if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) + struct lpfc_nvmet_rcv_ctx *ctxp = ctx_buf->context; + struct lpfc_hba *phba = ctxp->phba; + struct rqb_dmabuf *nvmebuf = ctxp->rqb_buffer; + struct lpfc_nvmet_tgtport *tgtp; + uint32_t *payload; + uint32_t rc; + unsigned long iflags; + + if (!nvmebuf) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6159 process_rcv_fcp_req, nvmebuf is NULL, " + "oxid: x%x flg: x%x state: x%x\n", + ctxp->oxid, ctxp->flag, ctxp->state); + spin_lock_irqsave(&ctxp->ctxlock, iflags); + lpfc_nvmet_defer_release(phba, ctxp); + spin_unlock_irqrestore(&ctxp->ctxlock, iflags); + lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, + ctxp->oxid); + return; + } + + payload = (uint32_t *)(nvmebuf->dbuf.virt); + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + /* + * The calling sequence should be: + * nvmet_fc_rcv_fcp_req->lpfc_nvmet_xmt_fcp_op/cmp- req->done + * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp. + * When we return from nvmet_fc_rcv_fcp_req, all relevant info + * the NVME command / FC header is stored. + * A buffer has already been reposted for this IO, so just free + * the nvmebuf. + */ + rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req, + payload, ctxp->size); + /* Process FCP command */ + if (rc == 0) { + atomic_inc(&tgtp->rcv_fcp_cmd_out); + return; + } + + /* Processing of FCP command is deferred */ + if (rc == -EOVERFLOW) { + lpfc_nvmeio_data(phba, "NVMET RCV BUSY: xri x%x sz %d " + "from %06x\n", + ctxp->oxid, ctxp->size, ctxp->sid); + atomic_inc(&tgtp->rcv_fcp_cmd_out); + atomic_inc(&tgtp->defer_fod); + return; + } + atomic_inc(&tgtp->rcv_fcp_cmd_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "2582 FCP Drop IO x%x: err x%x: x%x x%x x%x\n", + ctxp->oxid, rc, + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_out), + atomic_read(&tgtp->xmt_fcp_release)); + lpfc_nvmeio_data(phba, "NVMET FCP DROP: xri x%x sz %d from %06x\n", + ctxp->oxid, ctxp->size, ctxp->sid); + spin_lock_irqsave(&ctxp->ctxlock, iflags); + lpfc_nvmet_defer_release(phba, ctxp); + spin_unlock_irqrestore(&ctxp->ctxlock, iflags); + lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid); +#endif +} + +static void +lpfc_nvmet_fcp_rqst_defer_work(struct work_struct *work) +{ +#if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) + struct lpfc_nvmet_ctxbuf *ctx_buf = + container_of(work, struct lpfc_nvmet_ctxbuf, defer_work); + + lpfc_nvmet_process_rcv_fcp_req(ctx_buf); +#endif +} + static struct lpfc_nvmet_ctxbuf * lpfc_nvmet_replenish_context(struct lpfc_hba *phba, struct lpfc_nvmet_ctx_info *current_infop) @@ -1906,7 +1991,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf; struct lpfc_nvmet_ctx_info *current_infop; uint32_t *payload; - uint32_t size, oxid, sid, rc, qno; + uint32_t size, oxid, sid, qno; unsigned long iflag; int current_cpu; @@ -1917,11 +2002,9 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, if (!nvmebuf || !phba->targetport) { lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, "6157 NVMET FCP Drop IO\n"); - oxid = 0; - size = 0; - sid = 0; - ctxp = NULL; - goto dropit; + if (nvmebuf) + lpfc_rq_buf_free(phba, &nvmebuf->hbuf); + return; } /* @@ -2028,67 +2111,7 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, #endif atomic_inc(&tgtp->rcv_fcp_cmd_in); - /* - * The calling sequence should be: - * nvmet_fc_rcv_fcp_req -> lpfc_nvmet_xmt_fcp_op/cmp -> req->done - * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp. - * When we return from nvmet_fc_rcv_fcp_req, all relevant info in - * the NVME command / FC header is stored, so we are free to repost - * the buffer. - */ - rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req, - payload, size); - - /* Process FCP command */ - if (rc == 0) { - ctxp->rqb_buffer = NULL; - atomic_inc(&tgtp->rcv_fcp_cmd_out); - lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ - return; - } - - /* Processing of FCP command is deferred */ - if (rc == -EOVERFLOW) { - /* - * Post a brand new DMA buffer to RQ and defer - * freeing rcv buffer till .defer_rcv callback - */ - qno = nvmebuf->idx; - lpfc_post_rq_buffer( - phba, phba->sli4_hba.nvmet_mrq_hdr[qno], - phba->sli4_hba.nvmet_mrq_data[qno], 1, qno); - - lpfc_nvmeio_data(phba, - "NVMET RCV BUSY: xri x%x sz %d from %06x\n", - oxid, size, sid); - atomic_inc(&tgtp->rcv_fcp_cmd_out); - atomic_inc(&tgtp->defer_fod); - return; - } - ctxp->rqb_buffer = nvmebuf; - - atomic_inc(&tgtp->rcv_fcp_cmd_drop); - lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, - "6159 FCP Drop IO x%x: err x%x: x%x x%x x%x\n", - ctxp->oxid, rc, - atomic_read(&tgtp->rcv_fcp_cmd_in), - atomic_read(&tgtp->rcv_fcp_cmd_out), - atomic_read(&tgtp->xmt_fcp_release)); -dropit: - lpfc_nvmeio_data(phba, "NVMET FCP DROP: xri x%x sz %d from %06x\n", - oxid, size, sid); - if (oxid) { - lpfc_nvmet_defer_release(phba, ctxp); - lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid); - lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ - return; - } - - if (ctx_buf) - lpfc_nvmet_ctxbuf_post(phba, ctx_buf); - - if (nvmebuf) - lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ + lpfc_nvmet_process_rcv_fcp_req(ctx_buf); } /** diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index 9b767c59de3d..b8c342a41d98 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -137,6 +137,7 @@ struct lpfc_nvmet_rcv_ctx { #define LPFC_NVMET_XBUSY 0x4 /* XB bit set on IO cmpl */ #define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */ #define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */ +#define LPFC_NVMET_CTX_REUSE_WQ 0x20 /* ctx reused via WQ */ #define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */ struct rqb_dmabuf *rqb_buffer; struct lpfc_nvmet_ctxbuf *ctxbuf; From c160c0f8063e4225c069ac904a3f2cf89dcb3a81 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:40 -0800 Subject: [PATCH 1225/4277] scsi: lpfc: Fix nvmet issues when link bounce under IO load Various null pointer dereference and general protection fault panics occur when there is a link bounce under load. There are a large number of "error" message 6413 indicating "bad release". The issues resolve to list corruptions due to missing or inconsistent lock protection. Lockups are due to nested locks in the unsolicited abort path. The unsolicited abort path calls the wrong abort processing routine. There was also duplicate context release while aborts were still active in the hardware. Removed duplicate locks and added lock protection around list item removal. Commonized lock handling around the abort processing routines. Prevent context release while still in ABTS list. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvmet.c | 50 +++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 0d10dfc74018..4aadb3d5e718 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1032,7 +1032,6 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, atomic_inc(&lpfc_nvmep->xmt_fcp_abort); spin_lock_irqsave(&ctxp->ctxlock, flags); - ctxp->state = LPFC_NVMET_STE_ABORT; /* Since iaab/iaar are NOT set, we need to check * if the firmware is in process of aborting IO @@ -1044,13 +1043,14 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, ctxp->flag |= LPFC_NVMET_ABORT_OP; if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) { + spin_unlock_irqrestore(&ctxp->ctxlock, flags); lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid); wq = ctxp->hdwq->nvme_wq; - spin_unlock_irqrestore(&ctxp->ctxlock, flags); lpfc_nvmet_wqfull_flush(phba, wq, ctxp); return; } + spin_unlock_irqrestore(&ctxp->ctxlock, flags); /* An state of LPFC_NVMET_STE_RCV means we have just received * the NVME command and have not started processing it. @@ -1062,7 +1062,6 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport, else lpfc_nvmet_sol_fcp_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid); - spin_unlock_irqrestore(&ctxp->ctxlock, flags); } static void @@ -1076,14 +1075,18 @@ lpfc_nvmet_xmt_fcp_release(struct nvmet_fc_target_port *tgtport, unsigned long flags; bool aborting = false; - if (ctxp->state != LPFC_NVMET_STE_DONE && - ctxp->state != LPFC_NVMET_STE_ABORT) { + spin_lock_irqsave(&ctxp->ctxlock, flags); + if (ctxp->flag & LPFC_NVMET_XBUSY) + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR, + "6027 NVMET release with XBUSY flag x%x" + " oxid x%x\n", + ctxp->flag, ctxp->oxid); + else if (ctxp->state != LPFC_NVMET_STE_DONE && + ctxp->state != LPFC_NVMET_STE_ABORT) lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, "6413 NVMET release bad state %d %d oxid x%x\n", ctxp->state, ctxp->entry_cnt, ctxp->oxid); - } - spin_lock_irqsave(&ctxp->ctxlock, flags); if ((ctxp->flag & LPFC_NVMET_ABORT_OP) || (ctxp->flag & LPFC_NVMET_XBUSY)) { aborting = true; @@ -1523,6 +1526,7 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, if (ctxp->ctxbuf->sglq->sli4_xritag != xri) continue; + spin_lock(&ctxp->ctxlock); /* Check if we already received a free context call * and we have completed processing an abort situation. */ @@ -1532,6 +1536,7 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, released = true; } ctxp->flag &= ~LPFC_NVMET_XBUSY; + spin_unlock(&ctxp->ctxlock); spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); rrq_empty = list_empty(&phba->active_rrq_list); @@ -1563,7 +1568,6 @@ lpfc_sli4_nvmet_xri_aborted(struct lpfc_hba *phba, int lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport, struct fc_frame_header *fc_hdr) - { #if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) struct lpfc_hba *phba = vport->phba; @@ -2696,15 +2700,17 @@ lpfc_nvmet_sol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, if (ctxp->flag & LPFC_NVMET_ABORT_OP) atomic_inc(&tgtp->xmt_fcp_abort_cmpl); + spin_lock_irqsave(&ctxp->ctxlock, flags); ctxp->state = LPFC_NVMET_STE_DONE; /* Check if we already received a free context call * and we have completed processing an abort situation. */ - spin_lock_irqsave(&ctxp->ctxlock, flags); if ((ctxp->flag & LPFC_NVMET_CTX_RLS) && !(ctxp->flag & LPFC_NVMET_XBUSY)) { + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_del(&ctxp->list); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); released = true; } ctxp->flag &= ~LPFC_NVMET_ABORT_OP; @@ -2770,6 +2776,7 @@ lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, } tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + spin_lock_irqsave(&ctxp->ctxlock, flags); if (ctxp->flag & LPFC_NVMET_ABORT_OP) atomic_inc(&tgtp->xmt_fcp_abort_cmpl); @@ -2784,10 +2791,11 @@ lpfc_nvmet_unsol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, * and we have completed processing an abort situation. */ ctxp->state = LPFC_NVMET_STE_DONE; - spin_lock_irqsave(&ctxp->ctxlock, flags); if ((ctxp->flag & LPFC_NVMET_CTX_RLS) && !(ctxp->flag & LPFC_NVMET_XBUSY)) { + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_del(&ctxp->list); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); released = true; } ctxp->flag &= ~LPFC_NVMET_ABORT_OP; @@ -2993,12 +3001,15 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, (ndlp) ? ndlp->nlp_state : NLP_STE_MAX_STATE); /* No failure to an ABTS request. */ + spin_lock_irqsave(&ctxp->ctxlock, flags); ctxp->flag &= ~LPFC_NVMET_ABORT_OP; + spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0; } /* Issue ABTS for this WQE based on iotag */ ctxp->abort_wqeq = lpfc_sli_get_iocbq(phba); + spin_lock_irqsave(&ctxp->ctxlock, flags); if (!ctxp->abort_wqeq) { atomic_inc(&tgtp->xmt_abort_rsp_error); lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS, @@ -3006,11 +3017,13 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, "xri: x%x\n", ctxp->oxid); /* No failure to an ABTS request. */ ctxp->flag &= ~LPFC_NVMET_ABORT_OP; + spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0; } abts_wqeq = ctxp->abort_wqeq; abts_wqe = &abts_wqeq->wqe; ctxp->state = LPFC_NVMET_STE_ABORT; + spin_unlock_irqrestore(&ctxp->ctxlock, flags); /* Announce entry to new IO submit field. */ lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, @@ -3031,7 +3044,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, "NVME Req now. hba_flag x%x oxid x%x\n", phba->hba_flag, ctxp->oxid); lpfc_sli_release_iocbq(phba, abts_wqeq); + spin_lock_irqsave(&ctxp->ctxlock, flags); ctxp->flag &= ~LPFC_NVMET_ABORT_OP; + spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0; } @@ -3044,7 +3059,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, "still pending on oxid x%x\n", ctxp->oxid); lpfc_sli_release_iocbq(phba, abts_wqeq); + spin_lock_irqsave(&ctxp->ctxlock, flags); ctxp->flag &= ~LPFC_NVMET_ABORT_OP; + spin_unlock_irqrestore(&ctxp->ctxlock, flags); return 0; } @@ -3099,7 +3116,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, } atomic_inc(&tgtp->xmt_abort_rsp_error); + spin_lock_irqsave(&ctxp->ctxlock, flags); ctxp->flag &= ~LPFC_NVMET_ABORT_OP; + spin_unlock_irqrestore(&ctxp->ctxlock, flags); lpfc_sli_release_iocbq(phba, abts_wqeq); lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS, "6166 Failed ABORT issue_wqe with status x%x " @@ -3108,7 +3127,6 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, return 1; } - static int lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp, @@ -3117,6 +3135,7 @@ lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba, struct lpfc_nvmet_tgtport *tgtp; struct lpfc_iocbq *abts_wqeq; unsigned long flags; + bool released = false; int rc; tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; @@ -3154,8 +3173,12 @@ lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba, aerr: spin_lock_irqsave(&ctxp->ctxlock, flags); - if (ctxp->flag & LPFC_NVMET_CTX_RLS) + if (ctxp->flag & LPFC_NVMET_CTX_RLS) { + spin_lock(&phba->sli4_hba.abts_nvmet_buf_list_lock); list_del(&ctxp->list); + spin_unlock(&phba->sli4_hba.abts_nvmet_buf_list_lock); + released = true; + } ctxp->flag &= ~(LPFC_NVMET_ABORT_OP | LPFC_NVMET_CTX_RLS); spin_unlock_irqrestore(&ctxp->ctxlock, flags); @@ -3163,7 +3186,8 @@ aerr: lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS, "6135 Failed to Issue ABTS for oxid x%x. Status x%x\n", ctxp->oxid, rc); - lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf); + if (released) + lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf); return 1; } From 0d041215f0b4420bf193f3b6e13a1887ffc8320c Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:41 -0800 Subject: [PATCH 1226/4277] scsi: lpfc: Update 12.2.0.0 file copyrights to 2019 For files modified as part of 12.2.0.0 patches, update copyright to 2019 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 2 +- drivers/scsi/lpfc/lpfc_attr.c | 2 +- drivers/scsi/lpfc/lpfc_crtn.h | 2 +- drivers/scsi/lpfc/lpfc_ct.c | 2 +- drivers/scsi/lpfc/lpfc_debugfs.c | 2 +- drivers/scsi/lpfc/lpfc_debugfs.h | 2 +- drivers/scsi/lpfc/lpfc_els.c | 2 +- drivers/scsi/lpfc/lpfc_hbadisc.c | 2 +- drivers/scsi/lpfc/lpfc_hw4.h | 2 +- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/scsi/lpfc/lpfc_nportdisc.c | 2 +- drivers/scsi/lpfc/lpfc_nvme.c | 2 +- drivers/scsi/lpfc/lpfc_nvme.h | 2 +- drivers/scsi/lpfc/lpfc_nvmet.c | 2 +- drivers/scsi/lpfc/lpfc_nvmet.h | 2 +- drivers/scsi/lpfc/lpfc_scsi.c | 2 +- drivers/scsi/lpfc/lpfc_scsi.h | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 2 +- drivers/scsi/lpfc/lpfc_sli.h | 2 +- drivers/scsi/lpfc/lpfc_sli4.h | 2 +- drivers/scsi/lpfc/lpfc_version.h | 2 +- drivers/scsi/lpfc/lpfc_vport.c | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index ea97d82f99f9..41d849f283f6 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 212bfae1966a..ce3e541434dc 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 982401c31c12..e0b14d791b8c 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 98faa3aae35c..7290573110fe 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 19d13a8789b9..ee98ea1c68f9 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2007-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 1fbee6496f85..93ab7dfb8ee0 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2007-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 3df2cfd02b41..fc077cb87900 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 47608d4b8e8c..aa4961a2caf8 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index c9a056ef321a..ff875b833192 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 563b6009ffce..d24af076afa8 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index acfce67dacde..6172682a24ba 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 271ad42be7f4..e73895674f39 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 07d5fa16dd39..6b6d88cefdb8 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index 4aadb3d5e718..caf641ee6210 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channsel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index b8c342a41d98..c3ca93d55856 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index af263b0e8bad..c98f264f1d83 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 9ee0ea4a1ef2..f76667b7da7b 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d548066a7e91..c4c319245a66 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index d203a666497f..7a1a761efdd6 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 1e3d7f534eaa..40c85091c805 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 3f4398ffb567..a248a895c7d0 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 0d712eb04e2b..343bc71d4615 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2019 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * From 42fb055a57cab8d58db422efa4d867d3d8b39f8e Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 28 Jan 2019 11:14:42 -0800 Subject: [PATCH 1227/4277] scsi: lpfc: Update lpfc version to 12.2.0.0 Update lpfc version to 12.2.0.0 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index a248a895c7d0..43fd693cf042 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "12.0.0.10" +#define LPFC_DRIVER_VERSION "12.2.0.0" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ From d6a9f7336d925364daca00557afa59a68e78b422 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 5 Feb 2019 10:02:00 -0800 Subject: [PATCH 1228/4277] EDAC, i10nm: Fix randconfig builds I10NM_EDAC depends on CONFIG_ACPI so make that dependency explicit. Reported-by: Borislav Petkov Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov Cc: Aristeu Rozanski Cc: James Morse Cc: Mauro Carvalho Chehab Cc: Qiuxu Zhuo Cc: linux-edac Link: https://lkml.kernel.org/r/20190205180200.26865-1-tony.luck@intel.com --- drivers/edac/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 0825c9c26d2b..102a47a09f25 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -243,10 +243,10 @@ config EDAC_SKX config EDAC_I10NM tristate "Intel 10nm server Integrated MC" - depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG + depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG && ACPI depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_I10NM can't be y select DMI - select ACPI_ADXL if ACPI + select ACPI_ADXL help Support for error detection and correction the Intel 10nm server Integrated Memory Controllers. If your From cbfa482f7e2becbb774dd30117efac48819252f8 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 5 Feb 2019 10:21:09 -0800 Subject: [PATCH 1229/4277] EDAC, skx_common: Add code to recognise new compound error code A new error code for systems that use DRAM as an extra level of cache looks like: 000F 0010 1MMM CCCC where the MMM and CCCC bits are used for the same purpose as the original code. For this new class of errors the ADXL translation will provide details of both the DIMM used as cache for the error location and the component that is being cached. Note: This new error code is first supported in Skylake. Older EDAC drivers do not need to be updated. Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov Cc: Aristeu Rozanski Cc: James Morse Cc: Mauro Carvalho Chehab Cc: Qiuxu Zhuo Cc: linux-edac Link: https://lkml.kernel.org/r/20190205182109.27828-1-tony.luck@intel.com --- drivers/edac/skx_common.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index 513523ad5649..0e96e7b5b0a7 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -494,9 +494,11 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, } /* - * According with Table 15-9 of the Intel Architecture spec vol 3A, - * memory errors should fit in this mask: + * According to Intel Architecture spec vol 3B, + * Table 15-10 "IA32_MCi_Status [15:0] Compound Error Code Encoding" + * memory errors should fit one of these masks: * 000f 0000 1mmm cccc (binary) + * 000f 0010 1mmm cccc (binary) [RAM used as cache] * where: * f = Correction Report Filtering Bit. If 1, subsequent errors * won't be shown @@ -504,7 +506,7 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, * cccc = channel * If the mask doesn't match, report an error to the parsing logic */ - if (!((errcode & 0xef80) == 0x80)) { + if (!((errcode & 0xef80) == 0x80 || (errcode & 0xef80) == 0x280)) { optype = "Can't parse: it is not a mem"; } else { switch (optypenum) { From 5870970b9a828d8693aa6d15742573289d7dbcd0 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:39 +0000 Subject: [PATCH 1230/4277] arm64: Fix HCR.TGE status for NMI contexts When using VHE, the host needs to clear HCR_EL2.TGE bit in order to interact with guest TLBs, switching from EL2&0 translation regime to EL1&0. However, some non-maskable asynchronous event could happen while TGE is cleared like SDEI. Because of this address translation operations relying on EL2&0 translation regime could fail (tlb invalidation, userspace access, ...). Fix this by properly setting HCR_EL2.TGE when entering NMI context and clear it if necessary when returning to the interrupted context. Signed-off-by: Julien Thierry Suggested-by: Marc Zyngier Reviewed-by: Marc Zyngier Reviewed-by: James Morse Cc: Arnd Bergmann Cc: Will Deacon Cc: Marc Zyngier Cc: James Morse Cc: linux-arch@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/hardirq.h | 31 +++++++++++++++++++++++++++++++ arch/arm64/kernel/irq.c | 3 +++ include/linux/hardirq.h | 7 +++++++ 3 files changed, 41 insertions(+) diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h index 1473fc2f7ab7..89691c86640a 100644 --- a/arch/arm64/include/asm/hardirq.h +++ b/arch/arm64/include/asm/hardirq.h @@ -17,8 +17,12 @@ #define __ASM_HARDIRQ_H #include +#include #include +#include #include +#include +#include #define NR_IPI 7 @@ -37,6 +41,33 @@ u64 smp_irq_stat_cpu(unsigned int cpu); #define __ARCH_IRQ_EXIT_IRQS_DISABLED 1 +struct nmi_ctx { + u64 hcr; +}; + +DECLARE_PER_CPU(struct nmi_ctx, nmi_contexts); + +#define arch_nmi_enter() \ + do { \ + if (is_kernel_in_hyp_mode()) { \ + struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts); \ + nmi_ctx->hcr = read_sysreg(hcr_el2); \ + if (!(nmi_ctx->hcr & HCR_TGE)) { \ + write_sysreg(nmi_ctx->hcr | HCR_TGE, hcr_el2); \ + isb(); \ + } \ + } \ + } while (0) + +#define arch_nmi_exit() \ + do { \ + if (is_kernel_in_hyp_mode()) { \ + struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts); \ + if (!(nmi_ctx->hcr & HCR_TGE)) \ + write_sysreg(nmi_ctx->hcr, hcr_el2); \ + } \ + } while (0) + static inline void ack_bad_irq(unsigned int irq) { extern unsigned long irq_err_count; diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index 780a12f59a8f..92fa81798fb9 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -33,6 +33,9 @@ unsigned long irq_err_count; +/* Only access this in an NMI enter/exit */ +DEFINE_PER_CPU(struct nmi_ctx, nmi_contexts); + DEFINE_PER_CPU(unsigned long *, irq_stack_ptr); int arch_show_interrupts(struct seq_file *p, int prec) diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 0fbbcdf0c178..da0af631ded5 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -60,8 +60,14 @@ extern void irq_enter(void); */ extern void irq_exit(void); +#ifndef arch_nmi_enter +#define arch_nmi_enter() do { } while (0) +#define arch_nmi_exit() do { } while (0) +#endif + #define nmi_enter() \ do { \ + arch_nmi_enter(); \ printk_nmi_enter(); \ lockdep_off(); \ ftrace_nmi_enter(); \ @@ -80,6 +86,7 @@ extern void irq_exit(void); ftrace_nmi_exit(); \ lockdep_on(); \ printk_nmi_exit(); \ + arch_nmi_exit(); \ } while (0) #endif /* LINUX_HARDIRQ_H */ From a82785a953e03444fe38616aed4d27b01da79a97 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:40 +0000 Subject: [PATCH 1231/4277] arm64: Remove unused daif related functions/macros There are some helpers to modify PSR.[DAIF] bits that are not referenced anywhere. The less these bits are available outside of local_irq_* functions the better. Get rid of those unused helpers. Signed-off-by: Julien Thierry Reviewed-by: Mark Rutland Acked-by: Catalin Marinas Acked-by: Marc Zyngier Cc: Will Deacon Cc: James Morse Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/assembler.h | 10 +--------- arch/arm64/include/asm/daifflags.h | 10 ---------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 4feb6119c3c9..7acf2436b578 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -62,16 +62,8 @@ .endm /* - * Enable and disable interrupts. + * Save/restore interrupts. */ - .macro disable_irq - msr daifset, #2 - .endm - - .macro enable_irq - msr daifclr, #2 - .endm - .macro save_and_disable_irq, flags mrs \flags, daif msr daifset, #2 diff --git a/arch/arm64/include/asm/daifflags.h b/arch/arm64/include/asm/daifflags.h index 8d91f2233135..546bc398553e 100644 --- a/arch/arm64/include/asm/daifflags.h +++ b/arch/arm64/include/asm/daifflags.h @@ -43,16 +43,6 @@ static inline unsigned long local_daif_save(void) return flags; } -static inline void local_daif_unmask(void) -{ - trace_hardirqs_on(); - asm volatile( - "msr daifclr, #0xf // local_daif_unmask" - : - : - : "memory"); -} - static inline void local_daif_restore(unsigned long flags) { if (!arch_irqs_disabled_flags(flags)) From c9bfdf734d4c0e7dc25f39dd636ba1952994865a Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:41 +0000 Subject: [PATCH 1232/4277] arm64: cpufeature: Set SYSREG_GIC_CPUIF as a boot system feature It is not supported to have some CPUs using GICv3 sysreg CPU interface while some others do not. Once ICC_SRE_EL1.SRE is set on a CPU, the bit cannot be cleared. Since matching this feature require setting ICC_SRE_EL1.SRE, it cannot be turned off if found on a CPU. Set the feature as STRICT_BOOT, if boot CPU has it, all other CPUs are required to have it. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Reviewed-by: Suzuki K Poulose Reviewed-by: Mark Rutland Acked-by: Catalin Marinas Acked-by: Marc Zyngier Cc: Will Deacon Cc: Suzuki K Poulose Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm64/kernel/cpufeature.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index f6d84e2c92fe..b9c0adf71a54 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1207,7 +1207,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = { { .desc = "GIC system register CPU interface", .capability = ARM64_HAS_SYSREG_GIC_CPUIF, - .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE, .matches = has_useable_gicv3_cpuif, .sys_reg = SYS_ID_AA64PFR0_EL1, .field_pos = ID_AA64PFR0_GIC_SHIFT, From b90d2b22afdc7ce150a9ee7a8d82378bcfc395a5 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:42 +0000 Subject: [PATCH 1233/4277] arm64: cpufeature: Add cpufeature for IRQ priority masking Add a cpufeature indicating whether a cpu supports masking interrupts by priority. The feature will be properly enabled in a later patch. Signed-off-by: Julien Thierry Reviewed-by: Suzuki K Poulose Reviewed-by: Mark Rutland Acked-by: Catalin Marinas Acked-by: Marc Zyngier Cc: Will Deacon Cc: Marc Zyngier Cc: Suzuki K Poulose Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/cpucaps.h | 3 ++- arch/arm64/include/asm/cpufeature.h | 6 ++++++ arch/arm64/kernel/cpufeature.c | 23 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h index 82e9099834ae..f6a76e43f39e 100644 --- a/arch/arm64/include/asm/cpucaps.h +++ b/arch/arm64/include/asm/cpucaps.h @@ -60,7 +60,8 @@ #define ARM64_HAS_ADDRESS_AUTH_IMP_DEF 39 #define ARM64_HAS_GENERIC_AUTH_ARCH 40 #define ARM64_HAS_GENERIC_AUTH_IMP_DEF 41 +#define ARM64_HAS_IRQ_PRIO_MASKING 42 -#define ARM64_NCAPS 42 +#define ARM64_NCAPS 43 #endif /* __ASM_CPUCAPS_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index dfcfba725d72..89c3f318f6be 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -612,6 +612,12 @@ static inline bool system_supports_generic_auth(void) cpus_have_const_cap(ARM64_HAS_GENERIC_AUTH_IMP_DEF)); } +static inline bool system_uses_irq_prio_masking(void) +{ + return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && + cpus_have_const_cap(ARM64_HAS_IRQ_PRIO_MASKING); +} + #define ARM64_SSBD_UNKNOWN -1 #define ARM64_SSBD_FORCE_DISABLE 0 #define ARM64_SSBD_KERNEL 1 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index b9c0adf71a54..6f56e0ab63a1 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1203,6 +1203,14 @@ static void cpu_enable_address_auth(struct arm64_cpu_capabilities const *cap) } #endif /* CONFIG_ARM64_PTR_AUTH */ +#ifdef CONFIG_ARM64_PSEUDO_NMI +static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry, + int scope) +{ + return false; +} +#endif + static const struct arm64_cpu_capabilities arm64_features[] = { { .desc = "GIC system register CPU interface", @@ -1480,6 +1488,21 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, }, #endif /* CONFIG_ARM64_PTR_AUTH */ +#ifdef CONFIG_ARM64_PSEUDO_NMI + { + /* + * Depends on having GICv3 + */ + .desc = "IRQ priority masking", + .capability = ARM64_HAS_IRQ_PRIO_MASKING, + .type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE, + .matches = can_use_gic_priorities, + .sys_reg = SYS_ID_AA64PFR0_EL1, + .field_pos = ID_AA64PFR0_GIC_SHIFT, + .sign = FTR_UNSIGNED, + .min_field_value = 1, + }, +#endif {}, }; From e99da7c6f51b487280406d8dc31cc7532cfb2017 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:43 +0000 Subject: [PATCH 1234/4277] arm/arm64: gic-v3: Add PMR and RPR accessors Add helper functions to access system registers related to interrupt priorities: PMR and RPR. Signed-off-by: Julien Thierry Reviewed-by: Mark Rutland Acked-by: Catalin Marinas Reviewed-by: Marc Zyngier Cc: Russell King Cc: Will Deacon Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm/include/asm/arch_gicv3.h | 16 ++++++++++++++++ arch/arm64/include/asm/arch_gicv3.h | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h index 0bd530702118..bef0b5de8dca 100644 --- a/arch/arm/include/asm/arch_gicv3.h +++ b/arch/arm/include/asm/arch_gicv3.h @@ -34,6 +34,7 @@ #define ICC_SRE __ACCESS_CP15(c12, 0, c12, 5) #define ICC_IGRPEN1 __ACCESS_CP15(c12, 0, c12, 7) #define ICC_BPR1 __ACCESS_CP15(c12, 0, c12, 3) +#define ICC_RPR __ACCESS_CP15(c12, 0, c11, 3) #define __ICC_AP0Rx(x) __ACCESS_CP15(c12, 0, c8, 4 | x) #define ICC_AP0R0 __ICC_AP0Rx(0) @@ -245,6 +246,21 @@ static inline void gic_write_bpr1(u32 val) write_sysreg(val, ICC_BPR1); } +static inline u32 gic_read_pmr(void) +{ + return read_sysreg(ICC_PMR); +} + +static inline void gic_write_pmr(u32 val) +{ + write_sysreg(val, ICC_PMR); +} + +static inline u32 gic_read_rpr(void) +{ + return read_sysreg(ICC_RPR); +} + /* * Even in 32bit systems that use LPAE, there is no guarantee that the I/O * interface provides true 64bit atomic accesses, so using strd/ldrd doesn't diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index e278f94df0c9..37193e224a50 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -114,6 +114,21 @@ static inline void gic_write_bpr1(u32 val) write_sysreg_s(val, SYS_ICC_BPR1_EL1); } +static inline u32 gic_read_pmr(void) +{ + return read_sysreg_s(SYS_ICC_PMR_EL1); +} + +static inline void gic_write_pmr(u32 val) +{ + write_sysreg_s(val, SYS_ICC_PMR_EL1); +} + +static inline u32 gic_read_rpr(void) +{ + return read_sysreg_s(SYS_ICC_RPR_EL1); +} + #define gic_read_typer(c) readq_relaxed(c) #define gic_write_irouter(v, c) writeq_relaxed(v, c) #define gic_read_lpir(c) readq_relaxed(c) From 3f1f3234bc2db1c16b9818b9a15a5d58ad45251c Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:44 +0000 Subject: [PATCH 1235/4277] irqchip/gic-v3: Switch to PMR masking before calling IRQ handler Mask the IRQ priority through PMR and re-enable IRQs at CPU level, allowing only higher priority interrupts to be received during interrupt handling. Signed-off-by: Julien Thierry Acked-by: Catalin Marinas Acked-by: Marc Zyngier Cc: Will Deacon Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm/include/asm/arch_gicv3.h | 17 +++++++++++++++++ arch/arm64/include/asm/arch_gicv3.h | 17 +++++++++++++++++ drivers/irqchip/irq-gic-v3.c | 5 +++++ 3 files changed, 39 insertions(+) diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h index bef0b5de8dca..f6f485f4744e 100644 --- a/arch/arm/include/asm/arch_gicv3.h +++ b/arch/arm/include/asm/arch_gicv3.h @@ -363,5 +363,22 @@ static inline void gits_write_vpendbaser(u64 val, void * __iomem addr) #define gits_read_vpendbaser(c) __gic_readq_nonatomic(c) +static inline bool gic_prio_masking_enabled(void) +{ + return false; +} + +static inline void gic_pmr_mask_irqs(void) +{ + /* Should not get called. */ + WARN_ON_ONCE(true); +} + +static inline void gic_arch_enable_irqs(void) +{ + /* Should not get called. */ + WARN_ON_ONCE(true); +} + #endif /* !__ASSEMBLY__ */ #endif /* !__ASM_ARCH_GICV3_H */ diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index 37193e224a50..b5f8142bf802 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -155,5 +155,22 @@ static inline u32 gic_read_rpr(void) #define gits_write_vpendbaser(v, c) writeq_relaxed(v, c) #define gits_read_vpendbaser(c) readq_relaxed(c) +static inline bool gic_prio_masking_enabled(void) +{ + return system_uses_irq_prio_masking(); +} + +static inline void gic_pmr_mask_irqs(void) +{ + /* Should not get called yet. */ + WARN_ON_ONCE(true); +} + +static inline void gic_arch_enable_irqs(void) +{ + /* Should not get called yet. */ + WARN_ON_ONCE(true); +} + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ARCH_GICV3_H */ diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 0868a9d81c3c..8148a9250be2 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -356,6 +356,11 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs irqnr = gic_read_iar(); + if (gic_prio_masking_enabled()) { + gic_pmr_mask_irqs(); + gic_arch_enable_irqs(); + } + if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { int err; From cdbc81ddef43c8fdcbd3a26e1a7530c70b629cfc Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:45 +0000 Subject: [PATCH 1236/4277] arm64: ptrace: Provide definitions for PMR values Introduce fixed values for PMR that are going to be used to mask and unmask interrupts by priority. The current priority given to GIC interrupts is 0xa0, so clearing PMR's most significant bit is enough to mask interrupts. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Acked-by: Catalin Marinas Acked-by: Marc Zyngier Cc: Oleg Nesterov Cc: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/ptrace.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index fce22c4b2f73..8b131bc8984d 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -25,6 +25,18 @@ #define CurrentEL_EL1 (1 << 2) #define CurrentEL_EL2 (2 << 2) +/* + * PMR values used to mask/unmask interrupts. + * + * GIC priority masking works as follows: if an IRQ's priority is a higher value + * than the value held in PMR, that IRQ is masked. Lowering the value of PMR + * means masking more IRQs (or at least that the same IRQs remain masked). + * + * To mask interrupts, we clear the most significant bit of PMR. + */ +#define GIC_PRIO_IRQON 0xf0 +#define GIC_PRIO_IRQOFF (GIC_PRIO_IRQON & ~0x80) + /* Additional SPSR bits not exposed in the UABI */ #define PSR_IL_BIT (1 << 20) From 133d05186325ce04494ea6488a6b86e50a446c12 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:46 +0000 Subject: [PATCH 1237/4277] arm64: Make PMR part of task context In order to replace PSR.I interrupt disabling/enabling with ICC_PMR_EL1 interrupt masking, ICC_PMR_EL1 needs to be saved/restored when taking/returning from an exception. This mimics the way hardware saves and restores PSR.I bit in spsr_el1 for exceptions and ERET. Add PMR to the registers to save in the pt_regs struct upon kernel entry, and restore it before ERET. Also, initialize it to a sane value when creating new tasks. Signed-off-by: Julien Thierry Reviewed-by: Catalin Marinas Reviewed-by: Marc Zyngier Cc: Will Deacon Cc: Oleg Nesterov Cc: Dave Martin Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/processor.h | 3 +++ arch/arm64/include/asm/ptrace.h | 14 +++++++++++--- arch/arm64/kernel/asm-offsets.c | 1 + arch/arm64/kernel/entry.S | 14 ++++++++++++++ arch/arm64/kernel/process.c | 6 ++++++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index f1a7ab18faf3..5d9ce62bdebd 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -191,6 +191,9 @@ static inline void start_thread_common(struct pt_regs *regs, unsigned long pc) memset(regs, 0, sizeof(*regs)); forget_syscall(regs); regs->pc = pc; + + if (system_uses_irq_prio_masking()) + regs->pmr_save = GIC_PRIO_IRQON; } static inline void start_thread(struct pt_regs *regs, unsigned long pc, diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index 8b131bc8984d..ec60174c8c18 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -19,6 +19,8 @@ #ifndef __ASM_PTRACE_H #define __ASM_PTRACE_H +#include + #include /* Current Exception Level values, as contained in CurrentEL */ @@ -179,7 +181,8 @@ struct pt_regs { #endif u64 orig_addr_limit; - u64 unused; // maintain 16 byte alignment + /* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */ + u64 pmr_save; u64 stackframe[2]; }; @@ -214,8 +217,13 @@ static inline void forget_syscall(struct pt_regs *regs) #define processor_mode(regs) \ ((regs)->pstate & PSR_MODE_MASK) -#define interrupts_enabled(regs) \ - (!((regs)->pstate & PSR_I_BIT)) +#define irqs_priority_unmasked(regs) \ + (system_uses_irq_prio_masking() ? \ + (regs)->pmr_save == GIC_PRIO_IRQON : \ + true) + +#define interrupts_enabled(regs) \ + (!((regs)->pstate & PSR_I_BIT) && irqs_priority_unmasked(regs)) #define fast_interrupts_enabled(regs) \ (!((regs)->pstate & PSR_F_BIT)) diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 0552b91d7666..7f40dcbdd51d 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -73,6 +73,7 @@ int main(void) DEFINE(S_PC, offsetof(struct pt_regs, pc)); DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno)); DEFINE(S_ORIG_ADDR_LIMIT, offsetof(struct pt_regs, orig_addr_limit)); + DEFINE(S_PMR_SAVE, offsetof(struct pt_regs, pmr_save)); DEFINE(S_STACKFRAME, offsetof(struct pt_regs, stackframe)); DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs)); BLANK(); diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 4d0c81f29a60..02f809a5c823 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -249,6 +249,12 @@ alternative_else_nop_endif msr sp_el0, tsk .endif + /* Save pmr */ +alternative_if ARM64_HAS_IRQ_PRIO_MASKING + mrs_s x20, SYS_ICC_PMR_EL1 + str x20, [sp, #S_PMR_SAVE] +alternative_else_nop_endif + /* * Registers that may be useful after this macro is invoked: * @@ -269,6 +275,14 @@ alternative_else_nop_endif /* No need to restore UAO, it will be restored from SPSR_EL1 */ .endif + /* Restore pmr */ +alternative_if ARM64_HAS_IRQ_PRIO_MASKING + ldr x20, [sp, #S_PMR_SAVE] + msr_s SYS_ICC_PMR_EL1, x20 + /* Ensure priority change is seen by redistributor */ + dsb sy +alternative_else_nop_endif + ldp x21, x22, [sp, #S_PC] // load ELR, SPSR .if \el == 0 ct_user_enter diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index a0f985a6ac50..6d410fc2849b 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -232,6 +232,9 @@ void __show_regs(struct pt_regs *regs) printk("sp : %016llx\n", sp); + if (system_uses_irq_prio_masking()) + printk("pmr_save: %08llx\n", regs->pmr_save); + i = top_reg; while (i >= 0) { @@ -363,6 +366,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, if (arm64_get_ssbd_state() == ARM64_SSBD_FORCE_DISABLE) childregs->pstate |= PSR_SSBS_BIT; + if (system_uses_irq_prio_masking()) + childregs->pmr_save = GIC_PRIO_IRQON; + p->thread.cpu_context.x19 = stack_start; p->thread.cpu_context.x20 = stk_sz; } From a9806aa259feb2f6fd582b6342c835a3482fccc6 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:47 +0000 Subject: [PATCH 1238/4277] arm64: Unmask PMR before going idle CPU does not received signals for interrupts with a priority masked by ICC_PMR_EL1. This means the CPU might not come back from a WFI instruction. Make sure ICC_PMR_EL1 does not mask interrupts when doing a WFI. Since the logic of cpu_do_idle is becoming a bit more complex than just two instructions, lets turn it from ASM to C. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Reviewed-by: Catalin Marinas Reviewed-by: Marc Zyngier Cc: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/kernel/process.c | 45 +++++++++++++++++++++++++++++++++++++ arch/arm64/mm/proc.S | 11 --------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 6d410fc2849b..3767fb21a5b8 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -74,6 +75,50 @@ EXPORT_SYMBOL_GPL(pm_power_off); void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); +static void __cpu_do_idle(void) +{ + dsb(sy); + wfi(); +} + +static void __cpu_do_idle_irqprio(void) +{ + unsigned long pmr; + unsigned long daif_bits; + + daif_bits = read_sysreg(daif); + write_sysreg(daif_bits | PSR_I_BIT, daif); + + /* + * Unmask PMR before going idle to make sure interrupts can + * be raised. + */ + pmr = gic_read_pmr(); + gic_write_pmr(GIC_PRIO_IRQON); + + __cpu_do_idle(); + + gic_write_pmr(pmr); + write_sysreg(daif_bits, daif); +} + +/* + * cpu_do_idle() + * + * Idle the processor (wait for interrupt). + * + * If the CPU supports priority masking we must do additional work to + * ensure that interrupts are not masked at the PMR (because the core will + * not wake up if we block the wake up signal in the interrupt controller). + */ +void cpu_do_idle(void) +{ + if (system_uses_irq_prio_masking()) + __cpu_do_idle_irqprio(); + else + __cpu_do_idle(); +} + /* * This is our default idle handler. */ diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 73886a5f1f30..3ea4f3b84a8b 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -55,17 +55,6 @@ #define MAIR(attr, mt) ((attr) << ((mt) * 8)) -/* - * cpu_do_idle() - * - * Idle the processor (wait for interrupt). - */ -ENTRY(cpu_do_idle) - dsb sy // WFI may enter a low-power mode - wfi - ret -ENDPROC(cpu_do_idle) - #ifdef CONFIG_CPU_PM /** * cpu_do_suspend - save CPU registers context From 85738e05dc38a80921e1e1944e5b835f6668fc30 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:48 +0000 Subject: [PATCH 1239/4277] arm64: kvm: Unmask PMR before entering guest Interrupts masked by ICC_PMR_EL1 will not be signaled to the CPU. This means that hypervisor will not receive masked interrupts while running a guest. We need to make sure that all maskable interrupts are masked from the time we call local_irq_disable() in the main run loop, and remain so until we call local_irq_enable() after returning from the guest, and we need to ensure that we see no interrupts at all (including pseudo-NMIs) in the middle of the VM world-switch, while at the same time we need to ensure we exit the guest when there are interrupts for the host. We can accomplish this with pseudo-NMIs enabled by: (1) local_irq_disable: set the priority mask (2) enter guest: set PSTATE.I (3) clear the priority mask (4) eret to guest (5) exit guest: set the priotiy mask clear PSTATE.I (and restore other host PSTATE bits) (6) local_irq_enable: clear the priority mask. Signed-off-by: Julien Thierry Acked-by: Catalin Marinas Reviewed-by: Marc Zyngier Reviewed-by: Christoffer Dall Cc: Christoffer Dall Cc: Marc Zyngier Cc: Will Deacon Cc: kvmarm@lists.cs.columbia.edu Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/kvm_host.h | 16 ++++++++++++++++ arch/arm64/kvm/hyp/switch.c | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 7732d0ba4e60..292c88263fd1 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -474,10 +475,25 @@ static inline int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) static inline void kvm_arm_vhe_guest_enter(void) { local_daif_mask(); + + /* + * Having IRQs masked via PMR when entering the guest means the GIC + * will not signal the CPU of interrupts of lower priority, and the + * only way to get out will be via guest exceptions. + * Naturally, we want to avoid this. + */ + if (system_uses_irq_prio_masking()) { + gic_write_pmr(GIC_PRIO_IRQON); + dsb(sy); + } } static inline void kvm_arm_vhe_guest_exit(void) { + /* + * local_daif_restore() takes care to properly restore PSTATE.DAIF + * and the GIC PMR if the host is using IRQ priorities. + */ local_daif_restore(DAIF_PROCCTX_NOIRQ); /* diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c index b0b1478094b4..6a4c2d6c3287 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -22,6 +22,7 @@ #include +#include #include #include #include @@ -521,6 +522,17 @@ int __hyp_text __kvm_vcpu_run_nvhe(struct kvm_vcpu *vcpu) struct kvm_cpu_context *guest_ctxt; u64 exit_code; + /* + * Having IRQs masked via PMR when entering the guest means the GIC + * will not signal the CPU of interrupts of lower priority, and the + * only way to get out will be via guest exceptions. + * Naturally, we want to avoid this. + */ + if (system_uses_irq_prio_masking()) { + gic_write_pmr(GIC_PRIO_IRQON); + dsb(sy); + } + vcpu = kern_hyp_va(vcpu); host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context); @@ -573,6 +585,10 @@ int __hyp_text __kvm_vcpu_run_nvhe(struct kvm_vcpu *vcpu) */ __debug_switch_to_host(vcpu); + /* Returning to host will clear PSR.I, remask PMR if needed */ + if (system_uses_irq_prio_masking()) + gic_write_pmr(GIC_PRIO_IRQOFF); + return exit_code; } From 13b210ddf474d9f3368766008a89fe82a6f90b48 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:49 +0000 Subject: [PATCH 1240/4277] efi: Let architectures decide the flags that should be saved/restored Currently, irqflags are saved before calling runtime services and checked for mismatch on return. Provide a pair of overridable macros to save and restore (if needed) the state that need to be preserved on return from a runtime service. This allows to check for flags that are not necesarly related to irqflags. Signed-off-by: Julien Thierry Acked-by: Catalin Marinas Acked-by: Ard Biesheuvel Acked-by: Marc Zyngier Cc: Ard Biesheuvel Cc: linux-efi@vger.kernel.org Signed-off-by: Catalin Marinas --- drivers/firmware/efi/runtime-wrappers.c | 17 +++++++++++++++-- include/linux/efi.h | 5 +++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 8903b9ccfc2b..c70df5ae7c4a 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -89,11 +89,24 @@ exit: \ efi_rts_work.status; \ }) +#ifndef arch_efi_save_flags +#define arch_efi_save_flags(state_flags) local_save_flags(state_flags) +#define arch_efi_restore_flags(state_flags) local_irq_restore(state_flags) +#endif + +unsigned long efi_call_virt_save_flags(void) +{ + unsigned long flags; + + arch_efi_save_flags(flags); + return flags; +} + void efi_call_virt_check_flags(unsigned long flags, const char *call) { unsigned long cur_flags, mismatch; - local_save_flags(cur_flags); + cur_flags = efi_call_virt_save_flags(); mismatch = flags ^ cur_flags; if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK)) @@ -102,7 +115,7 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call) add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n", flags, cur_flags, call); - local_irq_restore(flags); + arch_efi_restore_flags(flags); } /* diff --git a/include/linux/efi.h b/include/linux/efi.h index 45ff763fba76..bd80b7ec35db 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1607,6 +1607,7 @@ efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg, bool efi_runtime_disabled(void); extern void efi_call_virt_check_flags(unsigned long flags, const char *call); +extern unsigned long efi_call_virt_save_flags(void); enum efi_secureboot_mode { efi_secureboot_mode_unset, @@ -1652,7 +1653,7 @@ void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table); \ arch_efi_call_virt_setup(); \ \ - local_save_flags(__flags); \ + __flags = efi_call_virt_save_flags(); \ __s = arch_efi_call_virt(p, f, args); \ efi_call_virt_check_flags(__flags, __stringify(f)); \ \ @@ -1667,7 +1668,7 @@ void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table); \ arch_efi_call_virt_setup(); \ \ - local_save_flags(__flags); \ + __flags = efi_call_virt_save_flags(); \ arch_efi_call_virt(p, f, args); \ efi_call_virt_check_flags(__flags, __stringify(f)); \ \ From 4a503217ce37e1f4f3d9b681bbcbbac103776bf1 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:50 +0000 Subject: [PATCH 1241/4277] arm64: irqflags: Use ICC_PMR_EL1 for interrupt masking Instead disabling interrupts by setting the PSR.I bit, use a priority higher than the one used for interrupts to mask them via PMR. When using PMR to disable interrupts, the value of PMR will be used instead of PSR.[DAIF] for the irqflags. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Acked-by: Ard Biesheuvel Reviewed-by: Catalin Marinas Cc: Will Deacon Cc: Ard Biesheuvel Cc: Oleg Nesterov Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/efi.h | 11 ++++ arch/arm64/include/asm/irqflags.h | 100 +++++++++++++++++++++--------- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 7ed320895d1f..c9e9a6978e73 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -44,6 +44,17 @@ efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); #define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) +/* + * Even when Linux uses IRQ priorities for IRQ disabling, EFI does not. + * And EFI shouldn't really play around with priority masking as it is not aware + * which priorities the OS has assigned to its interrupts. + */ +#define arch_efi_save_flags(state_flags) \ + ((void)((state_flags) = read_sysreg(daif))) + +#define arch_efi_restore_flags(state_flags) write_sysreg(state_flags, daif) + + /* arch specific definitions used by the stub code */ /* diff --git a/arch/arm64/include/asm/irqflags.h b/arch/arm64/include/asm/irqflags.h index 24692edf1a69..d4597b2c5729 100644 --- a/arch/arm64/include/asm/irqflags.h +++ b/arch/arm64/include/asm/irqflags.h @@ -18,7 +18,9 @@ #ifdef __KERNEL__ +#include #include +#include /* * Aarch64 has flags for masking: Debug, Asynchronous (serror), Interrupts and @@ -36,33 +38,27 @@ /* * CPU interrupt mask handling. */ -static inline unsigned long arch_local_irq_save(void) -{ - unsigned long flags; - asm volatile( - "mrs %0, daif // arch_local_irq_save\n" - "msr daifset, #2" - : "=r" (flags) - : - : "memory"); - return flags; -} - static inline void arch_local_irq_enable(void) { - asm volatile( - "msr daifclr, #2 // arch_local_irq_enable" - : + asm volatile(ALTERNATIVE( + "msr daifclr, #2 // arch_local_irq_enable\n" + "nop", + "msr_s " __stringify(SYS_ICC_PMR_EL1) ",%0\n" + "dsb sy", + ARM64_HAS_IRQ_PRIO_MASKING) : + : "r" (GIC_PRIO_IRQON) : "memory"); } static inline void arch_local_irq_disable(void) { - asm volatile( - "msr daifset, #2 // arch_local_irq_disable" - : + asm volatile(ALTERNATIVE( + "msr daifset, #2 // arch_local_irq_disable", + "msr_s " __stringify(SYS_ICC_PMR_EL1) ", %0", + ARM64_HAS_IRQ_PRIO_MASKING) : + : "r" (GIC_PRIO_IRQOFF) : "memory"); } @@ -71,12 +67,44 @@ static inline void arch_local_irq_disable(void) */ static inline unsigned long arch_local_save_flags(void) { + unsigned long daif_bits; unsigned long flags; - asm volatile( - "mrs %0, daif // arch_local_save_flags" - : "=r" (flags) - : + + daif_bits = read_sysreg(daif); + + /* + * The asm is logically equivalent to: + * + * if (system_uses_irq_prio_masking()) + * flags = (daif_bits & PSR_I_BIT) ? + * GIC_PRIO_IRQOFF : + * read_sysreg_s(SYS_ICC_PMR_EL1); + * else + * flags = daif_bits; + */ + asm volatile(ALTERNATIVE( + "mov %0, %1\n" + "nop\n" + "nop", + "mrs_s %0, " __stringify(SYS_ICC_PMR_EL1) "\n" + "ands %1, %1, " __stringify(PSR_I_BIT) "\n" + "csel %0, %0, %2, eq", + ARM64_HAS_IRQ_PRIO_MASKING) + : "=&r" (flags), "+r" (daif_bits) + : "r" (GIC_PRIO_IRQOFF) : "memory"); + + return flags; +} + +static inline unsigned long arch_local_irq_save(void) +{ + unsigned long flags; + + flags = arch_local_save_flags(); + + arch_local_irq_disable(); + return flags; } @@ -85,16 +113,32 @@ static inline unsigned long arch_local_save_flags(void) */ static inline void arch_local_irq_restore(unsigned long flags) { - asm volatile( - "msr daif, %0 // arch_local_irq_restore" - : - : "r" (flags) - : "memory"); + asm volatile(ALTERNATIVE( + "msr daif, %0\n" + "nop", + "msr_s " __stringify(SYS_ICC_PMR_EL1) ", %0\n" + "dsb sy", + ARM64_HAS_IRQ_PRIO_MASKING) + : "+r" (flags) + : + : "memory"); } static inline int arch_irqs_disabled_flags(unsigned long flags) { - return flags & PSR_I_BIT; + int res; + + asm volatile(ALTERNATIVE( + "and %w0, %w1, #" __stringify(PSR_I_BIT) "\n" + "nop", + "cmp %w1, #" __stringify(GIC_PRIO_IRQOFF) "\n" + "cset %w0, ls", + ARM64_HAS_IRQ_PRIO_MASKING) + : "=&r" (res) + : "r" ((int) flags) + : "memory"); + + return res; } #endif #endif From 8cb7eff32cc00697d4a37b1ed569c72ee2039ca4 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:51 +0000 Subject: [PATCH 1242/4277] arm64: daifflags: Include PMR in daifflags restore operations The addition of PMR should not bypass the semantics of daifflags. When DA_F are set, I bit is also set as no interrupts (even of higher priority) is allowed. When DA_F are cleared, I bit is cleared and interrupt enabling/disabling goes through ICC_PMR_EL1. Signed-off-by: Julien Thierry Reviewed-by: Catalin Marinas Reviewed-by: Marc Zyngier Cc: Will Deacon Cc: James Morse Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/daifflags.h | 50 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/daifflags.h b/arch/arm64/include/asm/daifflags.h index 546bc398553e..1dd3d7a38d34 100644 --- a/arch/arm64/include/asm/daifflags.h +++ b/arch/arm64/include/asm/daifflags.h @@ -18,6 +18,8 @@ #include +#include + #define DAIF_PROCCTX 0 #define DAIF_PROCCTX_NOIRQ PSR_I_BIT @@ -36,7 +38,13 @@ static inline unsigned long local_daif_save(void) { unsigned long flags; - flags = arch_local_save_flags(); + flags = read_sysreg(daif); + + if (system_uses_irq_prio_masking()) { + /* If IRQs are masked with PMR, reflect it in the flags */ + if (read_sysreg_s(SYS_ICC_PMR_EL1) <= GIC_PRIO_IRQOFF) + flags |= PSR_I_BIT; + } local_daif_mask(); @@ -45,12 +53,46 @@ static inline unsigned long local_daif_save(void) static inline void local_daif_restore(unsigned long flags) { - if (!arch_irqs_disabled_flags(flags)) + bool irq_disabled = flags & PSR_I_BIT; + + if (!irq_disabled) { trace_hardirqs_on(); - arch_local_irq_restore(flags); + if (system_uses_irq_prio_masking()) + arch_local_irq_enable(); + } else if (!(flags & PSR_A_BIT)) { + /* + * If interrupts are disabled but we can take + * asynchronous errors, we can take NMIs + */ + if (system_uses_irq_prio_masking()) { + flags &= ~PSR_I_BIT; + /* + * There has been concern that the write to daif + * might be reordered before this write to PMR. + * From the ARM ARM DDI 0487D.a, section D1.7.1 + * "Accessing PSTATE fields": + * Writes to the PSTATE fields have side-effects on + * various aspects of the PE operation. All of these + * side-effects are guaranteed: + * - Not to be visible to earlier instructions in + * the execution stream. + * - To be visible to later instructions in the + * execution stream + * + * Also, writes to PMR are self-synchronizing, so no + * interrupts with a lower priority than PMR is signaled + * to the PE after the write. + * + * So we don't need additional synchronization here. + */ + arch_local_irq_disable(); + } + } - if (arch_irqs_disabled_flags(flags)) + write_sysreg(flags, daif); + + if (irq_disabled) trace_hardirqs_off(); } From e9ab7a2e333615497b3fc426c379c330230c2b50 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:52 +0000 Subject: [PATCH 1243/4277] arm64: alternative: Allow alternative status checking per cpufeature In preparation for the application of alternatives at different points during the boot process, provide the possibility to check whether alternatives for a feature of interest was already applied instead of having a global boolean for all alternatives. Make VHE enablement code check for the VHE feature instead of considering all alternatives. Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Will Deacon Cc: Suzuki K Poulose Cc: Marc Zyngier Cc: Christoffer Dall Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/alternative.h | 3 +-- arch/arm64/kernel/alternative.c | 21 +++++++++++++++++---- arch/arm64/kernel/cpufeature.c | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h index 4b650ec1d7dd..9806a2357e70 100644 --- a/arch/arm64/include/asm/alternative.h +++ b/arch/arm64/include/asm/alternative.h @@ -14,8 +14,6 @@ #include #include -extern int alternatives_applied; - struct alt_instr { s32 orig_offset; /* offset to original instruction */ s32 alt_offset; /* offset to replacement instruction */ @@ -28,6 +26,7 @@ typedef void (*alternative_cb_t)(struct alt_instr *alt, __le32 *origptr, __le32 *updptr, int nr_inst); void __init apply_alternatives_all(void); +bool alternative_is_applied(u16 cpufeature); #ifdef CONFIG_MODULES void apply_alternatives_module(void *start, size_t length); diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c index b5d603992d40..c947d2246017 100644 --- a/arch/arm64/kernel/alternative.c +++ b/arch/arm64/kernel/alternative.c @@ -32,13 +32,23 @@ #define ALT_ORIG_PTR(a) __ALT_PTR(a, orig_offset) #define ALT_REPL_PTR(a) __ALT_PTR(a, alt_offset) -int alternatives_applied; +static int all_alternatives_applied; + +static DECLARE_BITMAP(applied_alternatives, ARM64_NCAPS); struct alt_region { struct alt_instr *begin; struct alt_instr *end; }; +bool alternative_is_applied(u16 cpufeature) +{ + if (WARN_ON(cpufeature >= ARM64_NCAPS)) + return false; + + return test_bit(cpufeature, applied_alternatives); +} + /* * Check if the target PC is within an alternative block. */ @@ -192,6 +202,9 @@ static void __apply_alternatives(void *alt_region, bool is_module) dsb(ish); __flush_icache_all(); isb(); + + /* We applied all that was available */ + bitmap_copy(applied_alternatives, cpu_hwcaps, ARM64_NCAPS); } } @@ -208,14 +221,14 @@ static int __apply_alternatives_multi_stop(void *unused) /* We always have a CPU 0 at this point (__init) */ if (smp_processor_id()) { - while (!READ_ONCE(alternatives_applied)) + while (!READ_ONCE(all_alternatives_applied)) cpu_relax(); isb(); } else { - BUG_ON(alternatives_applied); + BUG_ON(all_alternatives_applied); __apply_alternatives(®ion, false); /* Barriers provided by the cache flushing */ - WRITE_ONCE(alternatives_applied, 1); + WRITE_ONCE(all_alternatives_applied, 1); } return 0; diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 6f56e0ab63a1..d607ea33228c 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1118,7 +1118,7 @@ static void cpu_copy_el2regs(const struct arm64_cpu_capabilities *__unused) * that, freshly-onlined CPUs will set tpidr_el2, so we don't need to * do anything here. */ - if (!alternatives_applied) + if (!alternative_is_applied(ARM64_HAS_VIRT_HOST_EXTN)) write_sysreg(read_sysreg(tpidr_el1), tpidr_el2); } #endif From 0ceb0d56905e3d141fae77e5936d00eee9233473 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Thu, 31 Jan 2019 14:58:53 +0000 Subject: [PATCH 1244/4277] arm64: alternative: Apply alternatives early in boot process Currently alternatives are applied very late in the boot process (and a long time after we enable scheduling). Some alternative sequences, such as those that alter the way CPU context is stored, must be applied much earlier in the boot sequence. Introduce apply_boot_alternatives() to allow some alternatives to be applied immediately after we detect the CPU features of the boot CPU. Signed-off-by: Daniel Thompson [julien.thierry@arm.com: rename to fit new cpufeature framework better, apply BOOT_SCOPE feature early in boot] Signed-off-by: Julien Thierry Reviewed-by: Suzuki K Poulose Reviewed-by: Marc Zyngier Cc: Will Deacon Cc: Christoffer Dall Cc: Suzuki K Poulose Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/alternative.h | 1 + arch/arm64/include/asm/cpufeature.h | 4 +++ arch/arm64/kernel/alternative.c | 43 ++++++++++++++++++++++++---- arch/arm64/kernel/cpufeature.c | 6 ++++ arch/arm64/kernel/smp.c | 7 +++++ 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h index 9806a2357e70..b9f8d787eea9 100644 --- a/arch/arm64/include/asm/alternative.h +++ b/arch/arm64/include/asm/alternative.h @@ -25,6 +25,7 @@ struct alt_instr { typedef void (*alternative_cb_t)(struct alt_instr *alt, __le32 *origptr, __le32 *updptr, int nr_inst); +void __init apply_boot_alternatives(void); void __init apply_alternatives_all(void); bool alternative_is_applied(u16 cpufeature); diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 89c3f318f6be..e505e1fbd2b9 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -391,6 +391,10 @@ extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS); extern struct static_key_false cpu_hwcap_keys[ARM64_NCAPS]; extern struct static_key_false arm64_const_caps_ready; +/* ARM64 CAPS + alternative_cb */ +#define ARM64_NPATCHABLE (ARM64_NCAPS + 1) +extern DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE); + #define for_each_available_cap(cap) \ for_each_set_bit(cap, cpu_hwcaps, ARM64_NCAPS) diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c index c947d2246017..a9b467763153 100644 --- a/arch/arm64/kernel/alternative.c +++ b/arch/arm64/kernel/alternative.c @@ -155,7 +155,8 @@ static void clean_dcache_range_nopatch(u64 start, u64 end) } while (cur += d_size, cur < end); } -static void __apply_alternatives(void *alt_region, bool is_module) +static void __apply_alternatives(void *alt_region, bool is_module, + unsigned long *feature_mask) { struct alt_instr *alt; struct alt_region *region = alt_region; @@ -165,6 +166,9 @@ static void __apply_alternatives(void *alt_region, bool is_module) for (alt = region->begin; alt < region->end; alt++) { int nr_inst; + if (!test_bit(alt->cpufeature, feature_mask)) + continue; + /* Use ARM64_CB_PATCH as an unconditional patch */ if (alt->cpufeature < ARM64_CB_PATCH && !cpus_have_cap(alt->cpufeature)) @@ -203,8 +207,11 @@ static void __apply_alternatives(void *alt_region, bool is_module) __flush_icache_all(); isb(); - /* We applied all that was available */ - bitmap_copy(applied_alternatives, cpu_hwcaps, ARM64_NCAPS); + /* Ignore ARM64_CB bit from feature mask */ + bitmap_or(applied_alternatives, applied_alternatives, + feature_mask, ARM64_NCAPS); + bitmap_and(applied_alternatives, applied_alternatives, + cpu_hwcaps, ARM64_NCAPS); } } @@ -225,8 +232,13 @@ static int __apply_alternatives_multi_stop(void *unused) cpu_relax(); isb(); } else { + DECLARE_BITMAP(remaining_capabilities, ARM64_NPATCHABLE); + + bitmap_complement(remaining_capabilities, boot_capabilities, + ARM64_NPATCHABLE); + BUG_ON(all_alternatives_applied); - __apply_alternatives(®ion, false); + __apply_alternatives(®ion, false, remaining_capabilities); /* Barriers provided by the cache flushing */ WRITE_ONCE(all_alternatives_applied, 1); } @@ -240,6 +252,24 @@ void __init apply_alternatives_all(void) stop_machine(__apply_alternatives_multi_stop, NULL, cpu_online_mask); } +/* + * This is called very early in the boot process (directly after we run + * a feature detect on the boot CPU). No need to worry about other CPUs + * here. + */ +void __init apply_boot_alternatives(void) +{ + struct alt_region region = { + .begin = (struct alt_instr *)__alt_instructions, + .end = (struct alt_instr *)__alt_instructions_end, + }; + + /* If called on non-boot cpu things could go wrong */ + WARN_ON(smp_processor_id() != 0); + + __apply_alternatives(®ion, false, &boot_capabilities[0]); +} + #ifdef CONFIG_MODULES void apply_alternatives_module(void *start, size_t length) { @@ -247,7 +277,10 @@ void apply_alternatives_module(void *start, size_t length) .begin = start, .end = start + length, }; + DECLARE_BITMAP(all_capabilities, ARM64_NPATCHABLE); - __apply_alternatives(®ion, true); + bitmap_fill(all_capabilities, ARM64_NPATCHABLE); + + __apply_alternatives(®ion, true, &all_capabilities[0]); } #endif diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index d607ea33228c..b530fb24e6c6 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -54,6 +54,9 @@ DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS); EXPORT_SYMBOL(cpu_hwcaps); static struct arm64_cpu_capabilities const __ro_after_init *cpu_hwcaps_ptrs[ARM64_NCAPS]; +/* Need also bit for ARM64_CB_PATCH */ +DECLARE_BITMAP(boot_capabilities, ARM64_NPATCHABLE); + /* * Flag to indicate if we have computed the system wide * capabilities based on the boot time active CPUs. This @@ -1677,6 +1680,9 @@ static void update_cpu_capabilities(u16 scope_mask) if (caps->desc) pr_info("detected: %s\n", caps->desc); cpus_set_cap(caps->capability); + + if ((scope_mask & SCOPE_BOOT_CPU) && (caps->type & SCOPE_BOOT_CPU)) + set_bit(caps->capability, boot_capabilities); } } diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 1598d6f7200a..a944edd39d2d 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -419,6 +419,13 @@ void __init smp_prepare_boot_cpu(void) */ jump_label_init(); cpuinfo_store_boot_cpu(); + + /* + * We now know enough about the boot CPU to apply the + * alternatives that cannot wait until interrupt handling + * and/or scheduling is enabled. + */ + apply_boot_alternatives(); } static u64 __init of_get_cpu_mpidr(struct device_node *dn) From b5cf607370d0ee257e4bfa80740952fa6110c2c7 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:54 +0000 Subject: [PATCH 1245/4277] irqchip/gic-v3: Factor group0 detection into functions The code to detect whether Linux has access to group0 interrupts can prove useful in other parts of the driver. Provide a separate function to do this. Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- drivers/irqchip/irq-gic-v3.c | 55 +++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 8148a9250be2..da547e013c1a 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -400,6 +400,39 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs } } +static u32 gic_get_pribits(void) +{ + u32 pribits; + + pribits = gic_read_ctlr(); + pribits &= ICC_CTLR_EL1_PRI_BITS_MASK; + pribits >>= ICC_CTLR_EL1_PRI_BITS_SHIFT; + pribits++; + + return pribits; +} + +static bool gic_has_group0(void) +{ + u32 val; + + /* + * Let's find out if Group0 is under control of EL3 or not by + * setting the highest possible, non-zero priority in PMR. + * + * If SCR_EL3.FIQ is set, the priority gets shifted down in + * order for the CPU interface to set bit 7, and keep the + * actual priority in the non-secure range. In the process, it + * looses the least significant bit and the actual priority + * becomes 0x80. Reading it back returns 0, indicating that + * we're don't have access to Group0. + */ + gic_write_pmr(BIT(8 - gic_get_pribits())); + val = gic_read_pmr(); + + return val != 0; +} + static void __init gic_dist_init(void) { unsigned int i; @@ -541,7 +574,7 @@ static void gic_cpu_sys_reg_init(void) u64 mpidr = cpu_logical_map(cpu); u64 need_rss = MPIDR_RS(mpidr); bool group0; - u32 val, pribits; + u32 pribits; /* * Need to check that the SRE bit has actually been set. If @@ -553,25 +586,9 @@ static void gic_cpu_sys_reg_init(void) if (!gic_enable_sre()) pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n"); - pribits = gic_read_ctlr(); - pribits &= ICC_CTLR_EL1_PRI_BITS_MASK; - pribits >>= ICC_CTLR_EL1_PRI_BITS_SHIFT; - pribits++; + pribits = gic_get_pribits(); - /* - * Let's find out if Group0 is under control of EL3 or not by - * setting the highest possible, non-zero priority in PMR. - * - * If SCR_EL3.FIQ is set, the priority gets shifted down in - * order for the CPU interface to set bit 7, and keep the - * actual priority in the non-secure range. In the process, it - * looses the least significant bit and the actual priority - * becomes 0x80. Reading it back returns 0, indicating that - * we're don't have access to Group0. - */ - write_gicreg(BIT(8 - pribits), ICC_PMR_EL1); - val = read_gicreg(ICC_PMR_EL1); - group0 = val != 0; + group0 = gic_has_group0(); /* Set priority mask register */ write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1); From e79321883842ca7b77d8a58fe8303e8da35c085e Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:55 +0000 Subject: [PATCH 1246/4277] arm64: Switch to PMR masking when starting CPUs Once the boot CPU has been prepared or a new secondary CPU has been brought up, use ICC_PMR_EL1 to mask interrupts on that CPU and clear PSR.I bit. Since ICC_PMR_EL1 is initialized at CPU bringup, avoid overwriting it in the GICv3 driver. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Acked-by: Marc Zyngier Cc: Will Deacon Cc: James Morse Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm64/kernel/smp.c | 26 ++++++++++++++++++++++++++ drivers/irqchip/irq-gic-v3.c | 8 +++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index a944edd39d2d..824de7038967 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -180,6 +181,24 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) return ret; } +static void init_gic_priority_masking(void) +{ + u32 cpuflags; + + if (WARN_ON(!gic_enable_sre())) + return; + + cpuflags = read_sysreg(daif); + + WARN_ON(!(cpuflags & PSR_I_BIT)); + + gic_write_pmr(GIC_PRIO_IRQOFF); + + /* We can only unmask PSR.I if we can take aborts */ + if (!(cpuflags & PSR_A_BIT)) + write_sysreg(cpuflags & ~PSR_I_BIT, daif); +} + /* * This is the secondary CPU boot entry. We're using this CPUs * idle thread stack, but a set of temporary page tables. @@ -206,6 +225,9 @@ asmlinkage notrace void secondary_start_kernel(void) */ cpu_uninstall_idmap(); + if (system_uses_irq_prio_masking()) + init_gic_priority_masking(); + preempt_disable(); trace_hardirqs_off(); @@ -426,6 +448,10 @@ void __init smp_prepare_boot_cpu(void) * and/or scheduling is enabled. */ apply_boot_alternatives(); + + /* Conditionally switch to GIC PMR for interrupt masking */ + if (system_uses_irq_prio_masking()) + init_gic_priority_masking(); } static u64 __init of_get_cpu_mpidr(struct device_node *dn) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index da547e013c1a..5a703ae09ce5 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -415,6 +415,9 @@ static u32 gic_get_pribits(void) static bool gic_has_group0(void) { u32 val; + u32 old_pmr; + + old_pmr = gic_read_pmr(); /* * Let's find out if Group0 is under control of EL3 or not by @@ -430,6 +433,8 @@ static bool gic_has_group0(void) gic_write_pmr(BIT(8 - gic_get_pribits())); val = gic_read_pmr(); + gic_write_pmr(old_pmr); + return val != 0; } @@ -591,7 +596,8 @@ static void gic_cpu_sys_reg_init(void) group0 = gic_has_group0(); /* Set priority mask register */ - write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1); + if (!gic_prio_masking_enabled()) + write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1); /* * Some firmwares hand over to the kernel with the BPR changed from From b334481ab76b2a9031aef5393b07de6d21a08244 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:56 +0000 Subject: [PATCH 1247/4277] arm64: gic-v3: Implement arch support for priority masking Implement architecture specific primitive allowing the GICv3 driver to use priorities to mask interrupts. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Acked-by: Marc Zyngier Cc: Marc Zyngier Cc: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/arch_gicv3.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index b5f8142bf802..14b41ddc68ba 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -22,6 +22,7 @@ #ifndef __ASSEMBLY__ +#include #include #include #include @@ -162,14 +163,13 @@ static inline bool gic_prio_masking_enabled(void) static inline void gic_pmr_mask_irqs(void) { - /* Should not get called yet. */ - WARN_ON_ONCE(true); + BUILD_BUG_ON(GICD_INT_DEF_PRI <= GIC_PRIO_IRQOFF); + gic_write_pmr(GIC_PRIO_IRQOFF); } static inline void gic_arch_enable_irqs(void) { - /* Should not get called yet. */ - WARN_ON_ONCE(true); + asm volatile ("msr daifclr, #2" : : : "memory"); } #endif /* __ASSEMBLY__ */ From d98d0a990ca1446d3c0ca8f0b9ac127a66e40cdf Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:57 +0000 Subject: [PATCH 1248/4277] irqchip/gic-v3: Detect if GIC can support pseudo-NMIs The values non secure EL1 needs to use for PMR and RPR registers depends on the value of SCR_EL3.FIQ. The values non secure EL1 sees from the distributor and redistributor depend on whether security is enabled for the GIC or not. To avoid having to deal with two sets of values for PMR masking/unmasking, only enable pseudo-NMIs when GIC has non-secure view of priorities. Also, add firmware requirements related to SCR_EL3. Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Will Deacon Cc: Jonathan Corbet Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- Documentation/arm64/booting.txt | 5 +++ drivers/irqchip/irq-gic-v3.c | 66 +++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/Documentation/arm64/booting.txt b/Documentation/arm64/booting.txt index 8df9f4658d6f..fbab7e21d116 100644 --- a/Documentation/arm64/booting.txt +++ b/Documentation/arm64/booting.txt @@ -188,6 +188,11 @@ Before jumping into the kernel, the following conditions must be met: the kernel image will be entered must be initialised by software at a higher exception level to prevent execution in an UNKNOWN state. + - SCR_EL3.FIQ must have the same value across all CPUs the kernel is + executing on. + - The value of SCR_EL3.FIQ must be the same as the one present at boot + time whenever the kernel is executing. + For systems with a GICv3 interrupt controller to be used in v3 mode: - If EL3 is present: ICC_SRE_EL3.Enable (bit 3) must be initialiased to 0b1. diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 5a703ae09ce5..eb9d9486f90b 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -66,6 +66,31 @@ struct gic_chip_data { static struct gic_chip_data gic_data __read_mostly; static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key); +/* + * The behaviours of RPR and PMR registers differ depending on the value of + * SCR_EL3.FIQ, and the behaviour of non-secure priority registers of the + * distributor and redistributors depends on whether security is enabled in the + * GIC. + * + * When security is enabled, non-secure priority values from the (re)distributor + * are presented to the GIC CPUIF as follow: + * (GIC_(R)DIST_PRI[irq] >> 1) | 0x80; + * + * If SCR_EL3.FIQ == 1, the values writen to/read from PMR and RPR at non-secure + * EL1 are subject to a similar operation thus matching the priorities presented + * from the (re)distributor when security is enabled. + * + * see GICv3/GICv4 Architecture Specification (IHI0069D): + * - section 4.8.1 Non-secure accesses to register fields for Secure interrupt + * priorities. + * - Figure 4-7 Secure read of the priority field for a Non-secure Group 1 + * interrupt. + * + * For now, we only support pseudo-NMIs if we have non-secure view of + * priorities. + */ +static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis); + static struct gic_kvm_info gic_v3_kvm_info; static DEFINE_PER_CPU(bool, has_rss); @@ -232,6 +257,12 @@ static void gic_unmask_irq(struct irq_data *d) gic_poke_irq(d, GICD_ISENABLER); } +static inline bool gic_supports_nmi(void) +{ + return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && + static_branch_likely(&supports_pseudo_nmis); +} + static int gic_irq_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool val) { @@ -573,6 +604,12 @@ static void gic_update_vlpi_properties(void) !gic_data.rdists.has_direct_lpi ? "no " : ""); } +/* Check whether it's single security state view */ +static inline bool gic_dist_security_disabled(void) +{ + return readl_relaxed(gic_data.dist_base + GICD_CTLR) & GICD_CTLR_DS; +} + static void gic_cpu_sys_reg_init(void) { int i, cpu = smp_processor_id(); @@ -596,8 +633,17 @@ static void gic_cpu_sys_reg_init(void) group0 = gic_has_group0(); /* Set priority mask register */ - if (!gic_prio_masking_enabled()) + if (!gic_prio_masking_enabled()) { write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1); + } else { + /* + * Mismatch configuration with boot CPU, the system is likely + * to die as interrupt masking will not work properly on all + * CPUs + */ + WARN_ON(gic_supports_nmi() && group0 && + !gic_dist_security_disabled()); + } /* * Some firmwares hand over to the kernel with the BPR changed from @@ -852,12 +898,6 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, #endif #ifdef CONFIG_CPU_PM -/* Check whether it's single security state view */ -static bool gic_dist_security_disabled(void) -{ - return readl_relaxed(gic_data.dist_base + GICD_CTLR) & GICD_CTLR_DS; -} - static int gic_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, void *v) { @@ -1110,6 +1150,11 @@ static bool gic_enable_quirk_msm8996(void *data) return true; } +static void gic_enable_nmi_support(void) +{ + static_branch_enable(&supports_pseudo_nmis); +} + static int __init gic_init_bases(void __iomem *dist_base, struct redist_region *rdist_regs, u32 nr_redist_regions, @@ -1179,6 +1224,13 @@ static int __init gic_init_bases(void __iomem *dist_base, its_cpu_init(); } + if (gic_prio_masking_enabled()) { + if (!gic_has_group0() || gic_dist_security_disabled()) + gic_enable_nmi_support(); + else + pr_warn("SCR_EL3.FIQ is cleared, cannot enable use of pseudo-NMIs\n"); + } + return 0; out_free: From f32c926651dcd1683f4d896ee52609000a62a3dc Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:58 +0000 Subject: [PATCH 1249/4277] irqchip/gic-v3: Handle pseudo-NMIs Provide a higher priority to be used for pseudo-NMIs. When such an interrupt is received, keep interrupts fully disabled at CPU level to prevent receiving other pseudo-NMIs while handling the current one. Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- drivers/irqchip/irq-gic-v3.c | 42 ++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index eb9d9486f90b..c2767fbc2e66 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -41,6 +41,8 @@ #include "irq-gic-common.h" +#define GICD_INT_NMI_PRI (GICD_INT_DEF_PRI & ~0x80) + #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) struct redist_region { @@ -381,12 +383,45 @@ static u64 gic_mpidr_to_affinity(unsigned long mpidr) return aff; } +static void gic_deactivate_unhandled(u32 irqnr) +{ + if (static_branch_likely(&supports_deactivate_key)) { + if (irqnr < 8192) + gic_write_dir(irqnr); + } else { + gic_write_eoir(irqnr); + } +} + +static inline void gic_handle_nmi(u32 irqnr, struct pt_regs *regs) +{ + int err; + + if (static_branch_likely(&supports_deactivate_key)) + gic_write_eoir(irqnr); + /* + * Leave the PSR.I bit set to prevent other NMIs to be + * received while handling this one. + * PSR.I will be restored when we ERET to the + * interrupted context. + */ + err = handle_domain_nmi(gic_data.domain, irqnr, regs); + if (err) + gic_deactivate_unhandled(irqnr); +} + static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqnr; irqnr = gic_read_iar(); + if (gic_supports_nmi() && + unlikely(gic_read_rpr() == GICD_INT_NMI_PRI)) { + gic_handle_nmi(irqnr, regs); + return; + } + if (gic_prio_masking_enabled()) { gic_pmr_mask_irqs(); gic_arch_enable_irqs(); @@ -403,12 +438,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs err = handle_domain_irq(gic_data.domain, irqnr, regs); if (err) { WARN_ONCE(true, "Unexpected interrupt received!\n"); - if (static_branch_likely(&supports_deactivate_key)) { - if (irqnr < 8192) - gic_write_dir(irqnr); - } else { - gic_write_eoir(irqnr); - } + gic_deactivate_unhandled(irqnr); } return; } From 101b35f7def1775bf589d86676983bc359843916 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:58:59 +0000 Subject: [PATCH 1250/4277] irqchip/gic-v3: Allow interrupts to be set as pseudo-NMI Implement NMI callbacks for GICv3 irqchip. Install NMI safe handlers when setting up interrupt line as NMI. Only SPIs and PPIs are allowed to be set up as NMI. Signed-off-by: Julien Thierry Reviewed-by: Marc Zyngier Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- drivers/irqchip/irq-gic-v3.c | 91 ++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index c2767fbc2e66..15e55d327505 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,9 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key); */ static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis); +/* ppi_nmi_refs[n] == number of cpus having ppi[n + 16] set as NMI */ +static refcount_t ppi_nmi_refs[16]; + static struct gic_kvm_info gic_v3_kvm_info; static DEFINE_PER_CPU(bool, has_rss); @@ -320,6 +324,79 @@ static int gic_irq_get_irqchip_state(struct irq_data *d, return 0; } +static void gic_irq_set_prio(struct irq_data *d, u8 prio) +{ + void __iomem *base = gic_dist_base(d); + + writeb_relaxed(prio, base + GICD_IPRIORITYR + gic_irq(d)); +} + +static int gic_irq_nmi_setup(struct irq_data *d) +{ + struct irq_desc *desc = irq_to_desc(d->irq); + + if (!gic_supports_nmi()) + return -EINVAL; + + if (gic_peek_irq(d, GICD_ISENABLER)) { + pr_err("Cannot set NMI property of enabled IRQ %u\n", d->irq); + return -EINVAL; + } + + /* + * A secondary irq_chip should be in charge of LPI request, + * it should not be possible to get there + */ + if (WARN_ON(gic_irq(d) >= 8192)) + return -EINVAL; + + /* desc lock should already be held */ + if (gic_irq(d) < 32) { + /* Setting up PPI as NMI, only switch handler for first NMI */ + if (!refcount_inc_not_zero(&ppi_nmi_refs[gic_irq(d) - 16])) { + refcount_set(&ppi_nmi_refs[gic_irq(d) - 16], 1); + desc->handle_irq = handle_percpu_devid_fasteoi_nmi; + } + } else { + desc->handle_irq = handle_fasteoi_nmi; + } + + gic_irq_set_prio(d, GICD_INT_NMI_PRI); + + return 0; +} + +static void gic_irq_nmi_teardown(struct irq_data *d) +{ + struct irq_desc *desc = irq_to_desc(d->irq); + + if (WARN_ON(!gic_supports_nmi())) + return; + + if (gic_peek_irq(d, GICD_ISENABLER)) { + pr_err("Cannot set NMI property of enabled IRQ %u\n", d->irq); + return; + } + + /* + * A secondary irq_chip should be in charge of LPI request, + * it should not be possible to get there + */ + if (WARN_ON(gic_irq(d) >= 8192)) + return; + + /* desc lock should already be held */ + if (gic_irq(d) < 32) { + /* Tearing down NMI, only switch handler for last NMI */ + if (refcount_dec_and_test(&ppi_nmi_refs[gic_irq(d) - 16])) + desc->handle_irq = handle_percpu_devid_irq; + } else { + desc->handle_irq = handle_fasteoi_irq; + } + + gic_irq_set_prio(d, GICD_INT_DEF_PRI); +} + static void gic_eoi_irq(struct irq_data *d) { gic_write_eoir(gic_irq(d)); @@ -964,6 +1041,8 @@ static struct irq_chip gic_chip = { .irq_set_affinity = gic_set_affinity, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, + .irq_nmi_setup = gic_irq_nmi_setup, + .irq_nmi_teardown = gic_irq_nmi_teardown, .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, @@ -979,6 +1058,8 @@ static struct irq_chip gic_eoimode1_chip = { .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, + .irq_nmi_setup = gic_irq_nmi_setup, + .irq_nmi_teardown = gic_irq_nmi_teardown, .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, @@ -1182,7 +1263,17 @@ static bool gic_enable_quirk_msm8996(void *data) static void gic_enable_nmi_support(void) { + int i; + + for (i = 0; i < 16; i++) + refcount_set(&ppi_nmi_refs[i], 0); + static_branch_enable(&supports_pseudo_nmis); + + if (static_branch_likely(&supports_deactivate_key)) + gic_eoimode1_chip.flags |= IRQCHIP_SUPPORTS_NMI; + else + gic_chip.flags |= IRQCHIP_SUPPORTS_NMI; } static int __init gic_init_bases(void __iomem *dist_base, From 7d31464adf20fb8c075a3a3dfe2002a195566510 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:59:00 +0000 Subject: [PATCH 1251/4277] arm64: Handle serror in NMI context Per definition of the daifflags, Serrors can occur during any interrupt context, that includes NMI contexts. Trying to nmi_enter in an nmi context will crash. Skip nmi_enter/nmi_exit when serror occurred during an NMI. Suggested-by: James Morse Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Will Deacon Cc: Mark Rutland Cc: Dave Martin Cc: James Morse Signed-off-by: Catalin Marinas --- arch/arm64/kernel/traps.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 4e2fb877f8d5..8ad119c3f665 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -898,13 +898,17 @@ bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr) asmlinkage void do_serror(struct pt_regs *regs, unsigned int esr) { - nmi_enter(); + const bool was_in_nmi = in_nmi(); + + if (!was_in_nmi) + nmi_enter(); /* non-RAS errors are not containable */ if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(regs, esr)) arm64_serror_panic(regs, esr); - nmi_exit(); + if (!was_in_nmi) + nmi_exit(); } void __pte_error(const char *file, int line, unsigned long val) From 1234ad686fb1bde5a9c2447fc4c9df8430358763 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:59:01 +0000 Subject: [PATCH 1252/4277] arm64: Skip preemption when exiting an NMI Handling of an NMI should not set any TIF flags. For NMIs received from EL0 the current exit path is safe to use. However, an NMI received at EL1 could have interrupted some task context that has set the TIF_NEED_RESCHED flag. Preempting a task should not happen as a result of an NMI. Skip preemption after handling an NMI from EL1. Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Will Deacon Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm64/kernel/entry.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 02f809a5c823..35ed48469506 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -624,7 +624,15 @@ el1_irq: #ifdef CONFIG_PREEMPT ldr x24, [tsk, #TSK_TI_PREEMPT] // get preempt count - cbnz x24, 1f // preempt count != 0 +alternative_if ARM64_HAS_IRQ_PRIO_MASKING + /* + * DA_F were cleared at start of handling. If anything is set in DAIF, + * we come back from an NMI, so skip preemption + */ + mrs x0, daif + orr x24, x24, x0 +alternative_else_nop_endif + cbnz x24, 1f // preempt count != 0 || NMI return path bl preempt_schedule_irq // irq en/disable is done inside 1: #endif From c25349fd3c8024cfebcc9b01ee6cfb093fab9be0 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:59:02 +0000 Subject: [PATCH 1253/4277] arm64: Skip irqflags tracing for NMI in IRQs disabled context When an NMI is raised while interrupts where disabled, the IRQ tracing already is in the correct state (i.e. hardirqs_off) and should be left as such when returning to the interrupted context. Check whether PMR was masking interrupts when the NMI was raised and skip IRQ tracing if necessary. Signed-off-by: Julien Thierry Acked-by: Marc Zyngier Cc: Will Deacon Cc: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm64/kernel/entry.S | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 35ed48469506..6bf7e12f9a2b 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -617,7 +617,18 @@ el1_irq: kernel_entry 1 enable_da_f #ifdef CONFIG_TRACE_IRQFLAGS +#ifdef CONFIG_ARM64_PSEUDO_NMI +alternative_if ARM64_HAS_IRQ_PRIO_MASKING + ldr x20, [sp, #S_PMR_SAVE] +alternative_else + mov x20, #GIC_PRIO_IRQON +alternative_endif + cmp x20, #GIC_PRIO_IRQOFF + /* Irqs were disabled, don't trace */ + b.ls 1f +#endif bl trace_hardirqs_off +1: #endif irq_handler @@ -637,8 +648,18 @@ alternative_else_nop_endif 1: #endif #ifdef CONFIG_TRACE_IRQFLAGS - bl trace_hardirqs_on +#ifdef CONFIG_ARM64_PSEUDO_NMI + /* + * if IRQs were disabled when we received the interrupt, we have an NMI + * and we are not re-enabling interrupt upon eret. Skip tracing. + */ + cmp x20, #GIC_PRIO_IRQOFF + b.ls 1f #endif + bl trace_hardirqs_on +1: +#endif + kernel_exit 1 ENDPROC(el1_irq) From bc3c03ccb4641fb940b27a0d369431876923a8fe Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Thu, 31 Jan 2019 14:59:03 +0000 Subject: [PATCH 1254/4277] arm64: Enable the support of pseudo-NMIs Add a build option and a command line parameter to build and enable the support of pseudo-NMIs. Signed-off-by: Julien Thierry Suggested-by: Daniel Thompson Cc: Will Deacon Signed-off-by: Catalin Marinas --- Documentation/admin-guide/kernel-parameters.txt | 5 +++++ arch/arm64/Kconfig | 14 ++++++++++++++ arch/arm64/kernel/cpufeature.c | 10 +++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b799bcf67d7b..4d85fa524109 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1831,6 +1831,11 @@ to let secondary kernels in charge of setting up LPIs. + irqchip.gicv3_pseudo_nmi= [ARM64] + Enables support for pseudo-NMIs in the kernel. This + requires the kernel to be built with + CONFIG_ARM64_PSEUDO_NMI. + irqfixup [HW] When an interrupt is not handled search all handlers for it. Intended to get systems with badly broken diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4cad67b9ec0a..c7a44bcfc385 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1327,6 +1327,20 @@ config ARM64_MODULE_PLTS bool select HAVE_MOD_ARCH_SPECIFIC +config ARM64_PSEUDO_NMI + bool "Support for NMI-like interrupts" + select CONFIG_ARM_GIC_V3 + help + Adds support for mimicking Non-Maskable Interrupts through the use of + GIC interrupt priority. This support requires version 3 or later of + Arm GIC. + + This high priority configuration for interrupts needs to be + explicitly enabled by setting the kernel parameter + "irqchip.gicv3_pseudo_nmi" to 1. + + If unsure, say N + config RELOCATABLE bool help diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index b530fb24e6c6..e24e94d28767 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1207,10 +1207,18 @@ static void cpu_enable_address_auth(struct arm64_cpu_capabilities const *cap) #endif /* CONFIG_ARM64_PTR_AUTH */ #ifdef CONFIG_ARM64_PSEUDO_NMI +static bool enable_pseudo_nmi; + +static int __init early_enable_pseudo_nmi(char *p) +{ + return strtobool(p, &enable_pseudo_nmi); +} +early_param("irqchip.gicv3_pseudo_nmi", early_enable_pseudo_nmi); + static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry, int scope) { - return false; + return enable_pseudo_nmi && has_useable_gicv3_cpuif(entry, scope); } #endif From 9acd8de69d107537a68d010c9149fa9d9aba91f4 Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Tue, 1 Jan 2019 23:46:10 +0800 Subject: [PATCH 1255/4277] function_graph: Support displaying relative timestamp When function_graph is used for latency tracers, relative timestamp is more straightforward than absolute timestamp as function trace does. This change adds relative timestamp support to function_graph and applies to latency tracers (wakeup and irqsoff). Instead of: # tracer: irqsoff # # irqsoff latency trace v1.1.5 on 5.0.0-rc1-test # -------------------------------------------------------------------- # latency: 521 us, #1125/1125, CPU#2 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:8) # ----------------- # | task: swapper/2-0 (uid:0 nice:0 policy:0 rt_prio:0) # ----------------- # => started at: __schedule # => ended at: _raw_spin_unlock_irq # # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / # TIME CPU TASK/PID |||| DURATION FUNCTION CALLS # | | | | |||| | | | | | | 124.974306 | 2) systemd-693 | d..1 0.000 us | __schedule(); 124.974307 | 2) systemd-693 | d..1 | rcu_note_context_switch() { 124.974308 | 2) systemd-693 | d..1 0.487 us | rcu_preempt_deferred_qs(); 124.974309 | 2) systemd-693 | d..1 0.451 us | rcu_qs(); 124.974310 | 2) systemd-693 | d..1 2.301 us | } [..] 124.974826 | 2) -0 | d..2 | finish_task_switch() { 124.974826 | 2) -0 | d..2 | _raw_spin_unlock_irq() { 124.974827 | 2) -0 | d..2 0.000 us | _raw_spin_unlock_irq(); 124.974828 | 2) -0 | d..2 0.000 us | tracer_hardirqs_on(); -0 2d..2 552us : => __schedule => schedule_idle => do_idle => cpu_startup_entry => start_secondary => secondary_startup_64 Show: # tracer: irqsoff # # irqsoff latency trace v1.1.5 on 5.0.0-rc1-test+ # -------------------------------------------------------------------- # latency: 511 us, #1053/1053, CPU#7 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:8) # ----------------- # | task: swapper/7-0 (uid:0 nice:0 policy:0 rt_prio:0) # ----------------- # => started at: __schedule # => ended at: _raw_spin_unlock_irq # # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / # REL TIME CPU TASK/PID |||| DURATION FUNCTION CALLS # | | | | |||| | | | | | | 0 us | 7) sshd-1704 | d..1 0.000 us | __schedule(); 1 us | 7) sshd-1704 | d..1 | rcu_note_context_switch() { 1 us | 7) sshd-1704 | d..1 0.611 us | rcu_preempt_deferred_qs(); 2 us | 7) sshd-1704 | d..1 0.484 us | rcu_qs(); 3 us | 7) sshd-1704 | d..1 2.599 us | } [..] 509 us | 7) -0 | d..2 | finish_task_switch() { 510 us | 7) -0 | d..2 | _raw_spin_unlock_irq() { 510 us | 7) -0 | d..2 0.000 us | _raw_spin_unlock_irq(); 512 us | 7) -0 | d..2 0.000 us | tracer_hardirqs_on(); -0 7d..2 543us : => __schedule => schedule_idle => do_idle => cpu_startup_entry => start_secondary => secondary_startup_64 Link: http://lkml.kernel.org/r/20190101154614.8887-2-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace.h | 9 +++++---- kernel/trace/trace_functions_graph.c | 25 +++++++++++++++++++++++++ kernel/trace/trace_irqsoff.c | 2 +- kernel/trace/trace_sched_wakeup.c | 2 +- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 08900828d282..a34fa5e76abb 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -855,10 +855,11 @@ static __always_inline bool ftrace_hash_empty(struct ftrace_hash *hash) #define TRACE_GRAPH_PRINT_PROC 0x8 #define TRACE_GRAPH_PRINT_DURATION 0x10 #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 -#define TRACE_GRAPH_PRINT_IRQS 0x40 -#define TRACE_GRAPH_PRINT_TAIL 0x80 -#define TRACE_GRAPH_SLEEP_TIME 0x100 -#define TRACE_GRAPH_GRAPH_TIME 0x200 +#define TRACE_GRAPH_PRINT_REL_TIME 0x40 +#define TRACE_GRAPH_PRINT_IRQS 0x80 +#define TRACE_GRAPH_PRINT_TAIL 0x100 +#define TRACE_GRAPH_SLEEP_TIME 0x200 +#define TRACE_GRAPH_GRAPH_TIME 0x400 #define TRACE_GRAPH_PRINT_FILL_SHIFT 28 #define TRACE_GRAPH_PRINT_FILL_MASK (0x3 << TRACE_GRAPH_PRINT_FILL_SHIFT) diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index c2af1560e856..16ebbdd7b22e 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -500,6 +500,17 @@ static void print_graph_abs_time(u64 t, struct trace_seq *s) (unsigned long)t, usecs_rem); } +static void +print_graph_rel_time(struct trace_iterator *iter, struct trace_seq *s) +{ + unsigned long long usecs; + + usecs = iter->ts - iter->trace_buffer->time_start; + do_div(usecs, NSEC_PER_USEC); + + trace_seq_printf(s, "%9llu us | ", usecs); +} + static void print_graph_irq(struct trace_iterator *iter, unsigned long addr, enum trace_type type, int cpu, pid_t pid, u32 flags) @@ -517,6 +528,10 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr, if (flags & TRACE_GRAPH_PRINT_ABS_TIME) print_graph_abs_time(iter->ts, s); + /* Relative time */ + if (flags & TRACE_GRAPH_PRINT_REL_TIME) + print_graph_rel_time(iter, s); + /* Cpu */ if (flags & TRACE_GRAPH_PRINT_CPU) print_graph_cpu(s, cpu); @@ -725,6 +740,10 @@ print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, if (flags & TRACE_GRAPH_PRINT_ABS_TIME) print_graph_abs_time(iter->ts, s); + /* Relative time */ + if (flags & TRACE_GRAPH_PRINT_REL_TIME) + print_graph_rel_time(iter, s); + /* Cpu */ if (flags & TRACE_GRAPH_PRINT_CPU) print_graph_cpu(s, cpu); @@ -1101,6 +1120,8 @@ static void print_lat_header(struct seq_file *s, u32 flags) if (flags & TRACE_GRAPH_PRINT_ABS_TIME) size += 16; + if (flags & TRACE_GRAPH_PRINT_REL_TIME) + size += 16; if (flags & TRACE_GRAPH_PRINT_CPU) size += 4; if (flags & TRACE_GRAPH_PRINT_PROC) @@ -1125,6 +1146,8 @@ static void __print_graph_headers_flags(struct trace_array *tr, seq_putc(s, '#'); if (flags & TRACE_GRAPH_PRINT_ABS_TIME) seq_puts(s, " TIME "); + if (flags & TRACE_GRAPH_PRINT_REL_TIME) + seq_puts(s, " REL TIME "); if (flags & TRACE_GRAPH_PRINT_CPU) seq_puts(s, " CPU"); if (flags & TRACE_GRAPH_PRINT_PROC) @@ -1139,6 +1162,8 @@ static void __print_graph_headers_flags(struct trace_array *tr, seq_putc(s, '#'); if (flags & TRACE_GRAPH_PRINT_ABS_TIME) seq_puts(s, " | "); + if (flags & TRACE_GRAPH_PRINT_REL_TIME) + seq_puts(s, " | "); if (flags & TRACE_GRAPH_PRINT_CPU) seq_puts(s, " | "); if (flags & TRACE_GRAPH_PRINT_PROC) diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index d3294721f119..e6a62e7c6b69 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -238,7 +238,7 @@ static void irqsoff_trace_close(struct trace_iterator *iter) #define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_CPU | \ TRACE_GRAPH_PRINT_PROC | \ - TRACE_GRAPH_PRINT_ABS_TIME | \ + TRACE_GRAPH_PRINT_REL_TIME | \ TRACE_GRAPH_PRINT_DURATION) static enum print_line_t irqsoff_print_line(struct trace_iterator *iter) diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 4ea7e6845efb..b6c5fa10347e 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -180,7 +180,7 @@ static void wakeup_trace_close(struct trace_iterator *iter) } #define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_PROC | \ - TRACE_GRAPH_PRINT_ABS_TIME | \ + TRACE_GRAPH_PRINT_REL_TIME | \ TRACE_GRAPH_PRINT_DURATION) static enum print_line_t wakeup_print_line(struct trace_iterator *iter) From 91457c018f15591682d160ae7f0f31f81c4a9f33 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:30:37 +0100 Subject: [PATCH 1256/4277] tracing: Annotate implicit fall through in parse_probe_arg() There is a plan to build the kernel with -Wimplicit-fallthrough and this place in the code produced a warning (W=1). This commit remove the following warning: kernel/trace/trace_probe.c:302:6: warning: this statement may fall through [-Wimplicit-fallthrough=] Link: http://lkml.kernel.org/r/20190114203039.16535-1-malat@debian.org Signed-off-by: Mathieu Malaterre Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_probe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 9962cb5da8ac..89da34b326e3 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -300,6 +300,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, case '+': /* deref memory */ arg++; /* Skip '+', because kstrtol() rejects it. */ + /* fall through */ case '-': tmp = strchr(arg, '('); if (!tmp) From 9399ca21d203f01a39696a6554344b5d6613ba7b Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:30:38 +0100 Subject: [PATCH 1257/4277] tracing: Annotate implicit fall through in predicate_parse() There is a plan to build the kernel with -Wimplicit-fallthrough and this place in the code produced a warning (W=1). This commit remove the following warning: kernel/trace/trace_events_filter.c:494:8: warning: this statement may fall through [-Wimplicit-fallthrough=] Link: http://lkml.kernel.org/r/20190114203039.16535-2-malat@debian.org Signed-off-by: Mathieu Malaterre Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_events_filter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 27821480105e..eb694756c4bb 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -495,6 +495,7 @@ predicate_parse(const char *str, int nr_parens, int nr_preds, ptr++; break; } + /* fall through */ default: parse_error(pe, FILT_ERR_TOO_MANY_PREDS, next - str); From 6c6dbce196c201810348b6cef6fc5ec77d4d0973 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Mon, 14 Jan 2019 16:37:53 -0500 Subject: [PATCH 1258/4277] tracing: Add comment to predicate_parse() about "&&" or "||" As the predicat_parse() code is rather complex, commenting subtleties is important. The switch case statement should be commented to describe that it is only looking for two '&' or '|' together, which is why the fall through to an error is after the check. Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_events_filter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index eb694756c4bb..f052ecb085e9 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -491,6 +491,7 @@ predicate_parse(const char *str, int nr_parens, int nr_preds, break; case '&': case '|': + /* accepting only "&&" or "||" */ if (next[1] == next[0]) { ptr++; break; From 97f0a3bcdf34f66c2017bad51445eafef1224870 Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Tue, 1 Jan 2019 23:46:11 +0800 Subject: [PATCH 1259/4277] tracing: Show more info for funcgraph wakeup tracers Add these info fields to funcgraph wakeup tracers: o Show CPU info since the waker could be on a different CPU. o Show function duration and overhead. o Show IRQ markers. Link: http://lkml.kernel.org/r/20190101154614.8887-3-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_sched_wakeup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index b6c5fa10347e..da5b6e012840 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -180,8 +180,11 @@ static void wakeup_trace_close(struct trace_iterator *iter) } #define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_PROC | \ + TRACE_GRAPH_PRINT_CPU | \ TRACE_GRAPH_PRINT_REL_TIME | \ - TRACE_GRAPH_PRINT_DURATION) + TRACE_GRAPH_PRINT_DURATION | \ + TRACE_GRAPH_PRINT_OVERHEAD | \ + TRACE_GRAPH_PRINT_IRQS) static enum print_line_t wakeup_print_line(struct trace_iterator *iter) { From afbab501c66bece057bc30656f71f856cd1b3baa Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Tue, 1 Jan 2019 23:46:12 +0800 Subject: [PATCH 1260/4277] tracing: Put a margin between flags and duration for wakeup tracers Don't mix context flags with function duration info. Instead of this: # tracer: wakeup_rt # # wakeup_rt latency trace v1.1.5 on 5.0.0-rc1-test+ # -------------------------------------------------------------------- # latency: 177 us, #545/545, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:8) # ----------------- # | task: migration/0-11 (uid:0 nice:0 policy:1 rt_prio:99) # ----------------- # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / # REL TIME CPU TASK/PID |||| DURATION FUNCTION CALLS # | | | | |||| | | | | | | 0 us | 0) -0 | dNh5 | /* 0:120:R + [000] 11: 0:R migration/0 */ 2 us | 0) -0 | dNh5 0.000 us | (null)(); 4 us | 0) -0 | dNh4 | _raw_spin_unlock() { 4 us | 0) -0 | dNh4 0.304 us | preempt_count_sub(); 5 us | 0) -0 | dNh3 1.063 us | } 5 us | 0) -0 | dNh3 0.266 us | ttwu_stat(); 6 us | 0) -0 | dNh3 | _raw_spin_unlock_irqrestore() { 6 us | 0) -0 | dNh3 0.273 us | preempt_count_sub(); 6 us | 0) -0 | dNh2 0.818 us | } Show this: # tracer: wakeup # # wakeup latency trace v1.1.5 on 4.20.0+ # -------------------------------------------------------------------- # latency: 593 us, #674/674, CPU#0 | (M:desktop VP:0, KP:0, SP:0 HP:0 #P:4) # ----------------- # | task: kworker/0:1H-339 (uid:0 nice:-20 policy:0 rt_prio:0) # ----------------- # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / # REL TIME CPU TASK/PID |||| DURATION FUNCTION CALLS # | | | | |||| | | | | | | 0 us | 0) -0 | dNs. | | /* 0:120:R + [000] 339:100:R kworker/0:1H */ 3 us | 0) -0 | dNs. | 0.000 us | (null)(); 67 us | 0) -0 | dNs. | 0.721 us | ttwu_stat(); 69 us | 0) -0 | dNs. | 0.607 us | _raw_spin_unlock_irqrestore(); 71 us | 0) -0 | .Ns. | 0.598 us | _raw_spin_lock_irq(); 72 us | 0) -0 | .Ns. | 0.584 us | _raw_spin_lock_irq(); 73 us | 0) -0 | dNs. | + 11.118 us | __next_timer_interrupt(); 75 us | 0) -0 | dNs. | | call_timer_fn() { 76 us | 0) -0 | dNs. | | delayed_work_timer_fn() { 76 us | 0) -0 | dNs. | | __queue_work() { ... Link: http://lkml.kernel.org/r/20190101154614.8887-4-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_functions_graph.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 16ebbdd7b22e..69ebf3c2f1b5 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -380,6 +380,7 @@ static void print_graph_lat_fmt(struct trace_seq *s, struct trace_entry *entry) { trace_seq_putc(s, ' '); trace_print_lat_fmt(s, entry); + trace_seq_puts(s, " | "); } /* If the pid changed since the last trace, output this event */ @@ -1153,7 +1154,7 @@ static void __print_graph_headers_flags(struct trace_array *tr, if (flags & TRACE_GRAPH_PRINT_PROC) seq_puts(s, " TASK/PID "); if (lat) - seq_puts(s, "||||"); + seq_puts(s, "|||| "); if (flags & TRACE_GRAPH_PRINT_DURATION) seq_puts(s, " DURATION "); seq_puts(s, " FUNCTION CALLS\n"); @@ -1169,7 +1170,7 @@ static void __print_graph_headers_flags(struct trace_array *tr, if (flags & TRACE_GRAPH_PRINT_PROC) seq_puts(s, " | | "); if (lat) - seq_puts(s, "||||"); + seq_puts(s, "|||| "); if (flags & TRACE_GRAPH_PRINT_DURATION) seq_puts(s, " | | "); seq_puts(s, " | | | |\n"); From 88d380eb0668b7fe76834c1febef4165508098b6 Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Tue, 1 Jan 2019 23:46:14 +0800 Subject: [PATCH 1261/4277] tracing/doc: Add latency tracer funcgraph example This add an example about how to use funcgraph with latency tracers. Link: http://lkml.kernel.org/r/20190101154614.8887-6-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Steven Rostedt (VMware) --- Documentation/trace/ftrace.rst | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst index 0131df7f5968..6ce2763a2a3e 100644 --- a/Documentation/trace/ftrace.rst +++ b/Documentation/trace/ftrace.rst @@ -1396,6 +1396,57 @@ enabling function tracing, we incur an added overhead. This overhead may extend the latency times. But nevertheless, this trace has provided some very helpful debugging information. +If we prefer function graph output instead of function, we can set +display-graph option:: + with echo 1 > options/display-graph + + # tracer: irqsoff + # + # irqsoff latency trace v1.1.5 on 4.20.0-rc6+ + # -------------------------------------------------------------------- + # latency: 3751 us, #274/274, CPU#0 | (M:desktop VP:0, KP:0, SP:0 HP:0 #P:4) + # ----------------- + # | task: bash-1507 (uid:0 nice:0 policy:0 rt_prio:0) + # ----------------- + # => started at: free_debug_processing + # => ended at: return_to_handler + # + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / + # REL TIME CPU TASK/PID |||| DURATION FUNCTION CALLS + # | | | | |||| | | | | | | + 0 us | 0) bash-1507 | d... | 0.000 us | _raw_spin_lock_irqsave(); + 0 us | 0) bash-1507 | d..1 | 0.378 us | do_raw_spin_trylock(); + 1 us | 0) bash-1507 | d..2 | | set_track() { + 2 us | 0) bash-1507 | d..2 | | save_stack_trace() { + 2 us | 0) bash-1507 | d..2 | | __save_stack_trace() { + 3 us | 0) bash-1507 | d..2 | | __unwind_start() { + 3 us | 0) bash-1507 | d..2 | | get_stack_info() { + 3 us | 0) bash-1507 | d..2 | 0.351 us | in_task_stack(); + 4 us | 0) bash-1507 | d..2 | 1.107 us | } + [...] + 3750 us | 0) bash-1507 | d..1 | 0.516 us | do_raw_spin_unlock(); + 3750 us | 0) bash-1507 | d..1 | 0.000 us | _raw_spin_unlock_irqrestore(); + 3764 us | 0) bash-1507 | d..1 | 0.000 us | tracer_hardirqs_on(); + bash-1507 0d..1 3792us : + => free_debug_processing + => __slab_free + => kmem_cache_free + => vm_area_free + => remove_vma + => exit_mmap + => mmput + => flush_old_exec + => load_elf_binary + => search_binary_handler + => __do_execve_file.isra.32 + => __x64_sys_execve + => do_syscall_64 + => entry_SYSCALL_64_after_hwframe preemptoff ---------- From f52d569f3d929d9530d75976baa191d2d50fc51b Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Thu, 17 Jan 2019 00:02:49 +0800 Subject: [PATCH 1262/4277] tracing: Show stacktrace for wakeup tracers This align the behavior of wakeup tracers with irqsoff latency tracer that we record stacktrace at the beginning and end of waking up. The stacktrace shows us what is happening in the kernel. Link: http://lkml.kernel.org/r/20190116160249.7554-1-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_sched_wakeup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index da5b6e012840..f4fe7d1781e9 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -475,6 +475,7 @@ probe_wakeup_sched_switch(void *ignore, bool preempt, __trace_function(wakeup_trace, CALLER_ADDR0, CALLER_ADDR1, flags, pc); tracing_sched_switch_trace(wakeup_trace, prev, next, flags, pc); + __trace_stack(wakeup_trace, flags, 0, pc); T0 = data->preempt_timestamp; T1 = ftrace_now(cpu); @@ -586,6 +587,7 @@ probe_wakeup(void *ignore, struct task_struct *p) data = per_cpu_ptr(wakeup_trace->trace_buffer.data, wakeup_cpu); data->preempt_timestamp = ftrace_now(cpu); tracing_sched_wakeup_trace(wakeup_trace, p, current, flags, pc); + __trace_stack(wakeup_trace, flags, 0, pc); /* * We must be careful in using CALLER_ADDR2. But since wake_up From d325c402964e7c63db94e9138c530832269a1297 Mon Sep 17 00:00:00 2001 From: Miroslav Benes Date: Fri, 28 Dec 2018 14:38:47 +0100 Subject: [PATCH 1263/4277] ring-buffer: Remove unused function ring_buffer_page_len() Commit 6b7e633fe9c2 ("tracing: Remove extra zeroing out of the ring buffer page") removed the only caller of ring_buffer_page_len(). The function is now unused and may be removed. Link: http://lkml.kernel.org/r/20181228133847.106177-1-mbenes@suse.cz Signed-off-by: Miroslav Benes Signed-off-by: Steven Rostedt (VMware) --- include/linux/ring_buffer.h | 2 -- kernel/trace/ring_buffer.c | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 5b9ae62272bb..f1429675f252 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -187,8 +187,6 @@ void ring_buffer_set_clock(struct ring_buffer *buffer, void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs); bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer); -size_t ring_buffer_page_len(void *page); - size_t ring_buffer_nr_pages(struct ring_buffer *buffer, int cpu); size_t ring_buffer_nr_dirty_pages(struct ring_buffer *buffer, int cpu); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 06e864a334bb..9a91479bbbfe 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -353,20 +353,6 @@ static void rb_init_page(struct buffer_data_page *bpage) local_set(&bpage->commit, 0); } -/** - * ring_buffer_page_len - the size of data on the page. - * @page: The page to read - * - * Returns the amount of data on the page, including buffer page header. - */ -size_t ring_buffer_page_len(void *page) -{ - struct buffer_data_page *bpage = page; - - return (local_read(&bpage->commit) & ~RB_MISSED_FLAGS) - + BUF_PAGE_HDR_SIZE; -} - /* * Also stolen from mm/slob.c. Thanks to Mathieu Desnoyers for pointing * this issue out. From a5c5e50cce9db7b550691e3ae801ecd4c1e59787 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 5 Feb 2019 16:44:07 -0800 Subject: [PATCH 1264/4277] Input: gpio-keys - add shutdown callback On some platforms (e.g.: ARCH_BRCMSTB) it is possible to enter "poweroff" while leaving some wake-up sources enabled such as key presses in order to allow for the system to wake-up. Wire up a .shutdown() callback which calls into the existing gpio_keys_suspend() since the logic is essentially the same. Signed-off-by: Florian Fainelli Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 492a971b95b5..6cd199e8a370 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -1015,8 +1015,18 @@ static int __maybe_unused gpio_keys_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); +static void gpio_keys_shutdown(struct platform_device *pdev) +{ + int ret; + + ret = gpio_keys_suspend(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "failed to shutdown\n"); +} + static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, + .shutdown = gpio_keys_shutdown, .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, From 0f681d09e66ea6833e6173180ff3892e9026ab71 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Wed, 6 Feb 2019 09:49:41 -0800 Subject: [PATCH 1265/4277] Input: add new vibrator driver for various MSM SOCs This patch adds a new vibrator driver that supports various Qualcomm MSM SOCs. Driver was tested on a LG Nexus 5 (hammerhead) phone. Signed-off-by: Brian Masney Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/msm-vibrator.txt | 36 +++ drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 1 + drivers/input/misc/msm-vibrator.c | 282 ++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/msm-vibrator.txt create mode 100644 drivers/input/misc/msm-vibrator.c diff --git a/Documentation/devicetree/bindings/input/msm-vibrator.txt b/Documentation/devicetree/bindings/input/msm-vibrator.txt new file mode 100644 index 000000000000..8dcf014ef2e5 --- /dev/null +++ b/Documentation/devicetree/bindings/input/msm-vibrator.txt @@ -0,0 +1,36 @@ +* Device tree bindings for the Qualcomm MSM vibrator + +Required properties: + + - compatible: Should be one of + "qcom,msm8226-vibrator" + "qcom,msm8974-vibrator" + - reg: the base address and length of the IO memory for the registers. + - pinctrl-names: set to default. + - pinctrl-0: phandles pointing to pin configuration nodes. See + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + - clock-names: set to pwm + - clocks: phandle of the clock. See + Documentation/devicetree/bindings/clock/clock-bindings.txt + - enable-gpios: GPIO that enables the vibrator. + +Optional properties: + + - vcc-supply: phandle to the regulator that provides power to the sensor. + +Example from a LG Nexus 5 (hammerhead) phone: + +vibrator@fd8c3450 { + reg = <0xfd8c3450 0x400>; + compatible = "qcom,msm8974-vibrator"; + + vcc-supply = <&pm8941_l19>; + + clocks = <&mmcc CAMSS_GP1_CLK>; + clock-names = "pwm"; + + enable-gpios = <&msmgpio 60 GPIO_ACTIVE_HIGH>; + + pinctrl-names = "default"; + pinctrl-0 = <&vibrator_pin>; +}; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ca59a2be9bc5..e39aef84f357 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -117,6 +117,16 @@ config INPUT_E3X0_BUTTON To compile this driver as a module, choose M here: the module will be called e3x0_button. +config INPUT_MSM_VIBRATOR + tristate "Qualcomm MSM vibrator driver" + select INPUT_FF_MEMLESS + help + Support for the vibrator that is found on various Qualcomm MSM + SOCs. + + To compile this driver as a module, choose M here: the module + will be called msm_vibrator. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9d0f9d1ff68f..96a6419cb1f2 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o +obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o diff --git a/drivers/input/misc/msm-vibrator.c b/drivers/input/misc/msm-vibrator.c new file mode 100644 index 000000000000..c06941021447 --- /dev/null +++ b/drivers/input/misc/msm-vibrator.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm MSM vibrator driver + * + * Copyright (c) 2018 Brian Masney + * + * Based on qcom,pwm-vibrator.c from: + * Copyright (c) 2018 Jonathan Marek + * + * Based on msm_pwm_vibrator.c from downstream Android sources: + * Copyright (C) 2009-2014 LGE, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CMD_RCGR 0x00 +#define REG_CFG_RCGR 0x04 +#define REG_M 0x08 +#define REG_N 0x0C +#define REG_D 0x10 +#define REG_CBCR 0x24 +#define MMSS_CC_M_DEFAULT 1 + +struct msm_vibrator { + struct input_dev *input; + struct mutex mutex; + struct work_struct worker; + void __iomem *base; + struct regulator *vcc; + struct clk *clk; + struct gpio_desc *enable_gpio; + u16 magnitude; + bool enabled; +}; + +static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset, + u32 value) +{ + writel(value, vibrator->base + offset); +} + +static int msm_vibrator_start(struct msm_vibrator *vibrator) +{ + int d_reg_val, ret = 0; + + mutex_lock(&vibrator->mutex); + + if (!vibrator->enabled) { + ret = clk_set_rate(vibrator->clk, 24000); + if (ret) { + dev_err(&vibrator->input->dev, + "Failed to set clock rate: %d\n", ret); + goto unlock; + } + + ret = clk_prepare_enable(vibrator->clk); + if (ret) { + dev_err(&vibrator->input->dev, + "Failed to enable clock: %d\n", ret); + goto unlock; + } + + ret = regulator_enable(vibrator->vcc); + if (ret) { + dev_err(&vibrator->input->dev, + "Failed to enable regulator: %d\n", ret); + clk_disable(vibrator->clk); + goto unlock; + } + + gpiod_set_value_cansleep(vibrator->enable_gpio, 1); + + vibrator->enabled = true; + } + + d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff); + msm_vibrator_write(vibrator, REG_CFG_RCGR, + (2 << 12) | /* dual edge mode */ + (0 << 8) | /* cxo */ + (7 << 0)); + msm_vibrator_write(vibrator, REG_M, 1); + msm_vibrator_write(vibrator, REG_N, 128); + msm_vibrator_write(vibrator, REG_D, d_reg_val); + msm_vibrator_write(vibrator, REG_CMD_RCGR, 1); + msm_vibrator_write(vibrator, REG_CBCR, 1); + +unlock: + mutex_unlock(&vibrator->mutex); + + return ret; +} + +static void msm_vibrator_stop(struct msm_vibrator *vibrator) +{ + mutex_lock(&vibrator->mutex); + + if (vibrator->enabled) { + gpiod_set_value_cansleep(vibrator->enable_gpio, 0); + regulator_disable(vibrator->vcc); + clk_disable(vibrator->clk); + vibrator->enabled = false; + } + + mutex_unlock(&vibrator->mutex); +} + +static void msm_vibrator_worker(struct work_struct *work) +{ + struct msm_vibrator *vibrator = container_of(work, + struct msm_vibrator, + worker); + + if (vibrator->magnitude) + msm_vibrator_start(vibrator); + else + msm_vibrator_stop(vibrator); +} + +static int msm_vibrator_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct msm_vibrator *vibrator = input_get_drvdata(dev); + + mutex_lock(&vibrator->mutex); + + if (effect->u.rumble.strong_magnitude > 0) + vibrator->magnitude = effect->u.rumble.strong_magnitude; + else + vibrator->magnitude = effect->u.rumble.weak_magnitude; + + mutex_unlock(&vibrator->mutex); + + schedule_work(&vibrator->worker); + + return 0; +} + +static void msm_vibrator_close(struct input_dev *input) +{ + struct msm_vibrator *vibrator = input_get_drvdata(input); + + cancel_work_sync(&vibrator->worker); + msm_vibrator_stop(vibrator); +} + +static int msm_vibrator_probe(struct platform_device *pdev) +{ + struct msm_vibrator *vibrator; + struct resource *res; + int ret; + + vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL); + if (!vibrator) + return -ENOMEM; + + vibrator->input = devm_input_allocate_device(&pdev->dev); + if (!vibrator->input) + return -ENOMEM; + + vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(vibrator->vcc)) { + if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get regulator: %ld\n", + PTR_ERR(vibrator->vcc)); + return PTR_ERR(vibrator->vcc); + } + + vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(vibrator->enable_gpio)) { + if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n", + PTR_ERR(vibrator->enable_gpio)); + return PTR_ERR(vibrator->enable_gpio); + } + + vibrator->clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(vibrator->clk)) { + if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n", + PTR_ERR(vibrator->clk)); + return PTR_ERR(vibrator->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + + vibrator->base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!vibrator->base) { + dev_err(&pdev->dev, "Failed to iomap resource: %ld\n", + PTR_ERR(vibrator->base)); + return -ENOMEM; + } + + vibrator->enabled = false; + mutex_init(&vibrator->mutex); + INIT_WORK(&vibrator->worker, msm_vibrator_worker); + + vibrator->input->name = "msm-vibrator"; + vibrator->input->id.bustype = BUS_HOST; + vibrator->input->close = msm_vibrator_close; + + input_set_drvdata(vibrator->input, vibrator); + input_set_capability(vibrator->input, EV_FF, FF_RUMBLE); + + ret = input_ff_create_memless(vibrator->input, NULL, + msm_vibrator_play_effect); + if (ret) { + dev_err(&pdev->dev, "Failed to create ff memless: %d", ret); + return ret; + } + + ret = input_register_device(vibrator->input); + if (ret) { + dev_err(&pdev->dev, "Failed to register input device: %d", ret); + return ret; + } + + platform_set_drvdata(pdev, vibrator); + + return 0; +} + +static int __maybe_unused msm_vibrator_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_vibrator *vibrator = platform_get_drvdata(pdev); + + cancel_work_sync(&vibrator->worker); + + if (vibrator->enabled) + msm_vibrator_stop(vibrator); + + return 0; +} + +static int __maybe_unused msm_vibrator_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_vibrator *vibrator = platform_get_drvdata(pdev); + + if (vibrator->enabled) + msm_vibrator_start(vibrator); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend, + msm_vibrator_resume); + +static const struct of_device_id msm_vibrator_of_match[] = { + { .compatible = "qcom,msm8226-vibrator" }, + { .compatible = "qcom,msm8974-vibrator" }, + {}, +}; +MODULE_DEVICE_TABLE(of, msm_vibrator_of_match); + +static struct platform_driver msm_vibrator_driver = { + .probe = msm_vibrator_probe, + .driver = { + .name = "msm-vibrator", + .pm = &msm_vibrator_pm_ops, + .of_match_table = of_match_ptr(msm_vibrator_of_match), + }, +}; +module_platform_driver(msm_vibrator_driver); + +MODULE_AUTHOR("Brian Masney "); +MODULE_DESCRIPTION("Qualcomm MSM vibrator driver"); +MODULE_LICENSE("GPL"); From fd9dc93e36231fb6d520e0edd467058fad4fd12d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 6 Feb 2019 13:07:11 -0500 Subject: [PATCH 1266/4277] XArray: Change xa_insert to return -EBUSY Userspace translates EEXIST to "File exists" which isn't a very good error message for the problem. "Device or resource busy" is a better indication of what went wrong. Signed-off-by: Matthew Wilcox --- Documentation/core-api/xarray.rst | 2 +- fs/nilfs2/btnode.c | 2 +- include/linux/xarray.h | 6 +++--- lib/test_xarray.c | 4 ++-- lib/xarray.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Documentation/core-api/xarray.rst b/Documentation/core-api/xarray.rst index 5d54b27c6eba..42bb1a62650f 100644 --- a/Documentation/core-api/xarray.rst +++ b/Documentation/core-api/xarray.rst @@ -85,7 +85,7 @@ which was at that index; if it returns the same entry which was passed as If you want to only store a new entry to an index if the current entry at that index is ``NULL``, you can use :c:func:`xa_insert` which -returns ``-EEXIST`` if the entry is not empty. +returns ``-EBUSY`` if the entry is not empty. You can enquire whether a mark is set on an entry by using :c:func:`xa_get_mark`. If the entry is not ``NULL``, you can set a mark diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index f2129a5d9f23..4391fd3abd8f 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -189,7 +189,7 @@ retry: */ if (!err) return 0; - else if (err != -EEXIST) + else if (err != -EBUSY) goto failed_unlock; err = invalidate_inode_pages2_range(btnc, newkey, newkey); diff --git a/include/linux/xarray.h b/include/linux/xarray.h index e11841537631..57cf35c4d094 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -664,7 +664,7 @@ static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index, * * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. - * Return: 0 if the store succeeded. -EEXIST if another entry was present. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ static inline int xa_insert(struct xarray *xa, unsigned long index, @@ -693,7 +693,7 @@ static inline int xa_insert(struct xarray *xa, unsigned long index, * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. - * Return: 0 if the store succeeded. -EEXIST if another entry was present. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ static inline int xa_insert_bh(struct xarray *xa, unsigned long index, @@ -722,7 +722,7 @@ static inline int xa_insert_bh(struct xarray *xa, unsigned long index, * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. - * Return: 0 if the store succeeded. -EEXIST if another entry was present. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ static inline int xa_insert_irq(struct xarray *xa, unsigned long index, diff --git a/lib/test_xarray.c b/lib/test_xarray.c index 671a93ee09e6..9d894e93456c 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c @@ -346,7 +346,7 @@ static noinline void check_cmpxchg(struct xarray *xa) XA_BUG_ON(xa, !xa_empty(xa)); XA_BUG_ON(xa, xa_store_index(xa, 12345678, GFP_KERNEL) != NULL); - XA_BUG_ON(xa, xa_insert(xa, 12345678, xa, GFP_KERNEL) != -EEXIST); + XA_BUG_ON(xa, xa_insert(xa, 12345678, xa, GFP_KERNEL) != -EBUSY); XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, SIX, FIVE, GFP_KERNEL) != LOTS); XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, LOTS, FIVE, GFP_KERNEL) != LOTS); XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, FIVE, LOTS, GFP_KERNEL) != FIVE); @@ -388,7 +388,7 @@ static noinline void check_reserve(struct xarray *xa) /* But xa_insert does not */ xa_reserve(xa, 12345678, GFP_KERNEL); XA_BUG_ON(xa, xa_insert(xa, 12345678, xa_mk_value(12345678), 0) != - -EEXIST); + -EBUSY); XA_BUG_ON(xa, xa_empty(xa)); XA_BUG_ON(xa, xa_erase(xa, 12345678) != NULL); XA_BUG_ON(xa, !xa_empty(xa)); diff --git a/lib/xarray.c b/lib/xarray.c index fb783bf2a441..1b97ca58bd15 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1451,7 +1451,7 @@ EXPORT_SYMBOL(__xa_cmpxchg); * * Context: Any context. Expects xa_lock to be held on entry. May * release and reacquire xa_lock if @gfp flags permit. - * Return: 0 if the store succeeded. -EEXIST if another entry was present. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) @@ -1471,7 +1471,7 @@ int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) if (xa_track_free(xa)) xas_clear_mark(&xas, XA_FREE_MARK); } else { - xas_set_err(&xas, -EEXIST); + xas_set_err(&xas, -EBUSY); } } while (__xas_nomem(&xas, gfp)); From 3ccaf57a6a63ad171a951dcaddffc453b2414c7b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 26 Oct 2018 14:43:22 -0400 Subject: [PATCH 1267/4277] XArray: Add support for 1s-based allocation A lot of places want to allocate IDs starting at 1 instead of 0. While the xa_alloc() API supports this, it's not very efficient if lots of IDs are allocated, due to having to walk down to the bottom of the tree to see if ID 1 is available, then all the way over to the next non-allocated ID. This method marks ID 0 as being occupied which wastes one slot in the XArray, but preserves xa_empty() as working. Signed-off-by: Matthew Wilcox --- Documentation/core-api/xarray.rst | 10 ++-- include/linux/xarray.h | 14 ++++- lib/test_xarray.c | 88 +++++++++++++++++++------------ lib/xarray.c | 11 ++++ 4 files changed, 86 insertions(+), 37 deletions(-) diff --git a/Documentation/core-api/xarray.rst b/Documentation/core-api/xarray.rst index 42bb1a62650f..e90c4925cd37 100644 --- a/Documentation/core-api/xarray.rst +++ b/Documentation/core-api/xarray.rst @@ -131,17 +131,21 @@ If you use :c:func:`DEFINE_XARRAY_ALLOC` to define the XArray, or initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`, the XArray changes to track whether entries are in use or not. -You can call :c:func:`xa_alloc` to store the entry at any unused index +You can call :c:func:`xa_alloc` to store the entry at an unused index in the XArray. If you need to modify the array from interrupt context, you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable interrupts while allocating the ID. -Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` -will mark the entry as being allocated. Unlike a normal XArray, storing +Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` will +also mark the entry as being allocated. Unlike a normal XArray, storing ``NULL`` will mark the entry as being in use, like :c:func:`xa_reserve`. To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if you only want to free the entry if it's ``NULL``). +By default, the lowest free entry is allocated starting from 0. If you +want to allocate entries starting at 1, it is more efficient to use +:c:func:`DEFINE_XARRAY_ALLOC1` or ``XA_FLAGS_ALLOC1``. + You cannot use ``XA_MARK_0`` with an allocating XArray as this mark is used to track whether an entry is free or not. The other marks are available for your use. diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 57cf35c4d094..99dd0838b4ba 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -220,10 +220,13 @@ enum xa_lock_type { #define XA_FLAGS_LOCK_IRQ ((__force gfp_t)XA_LOCK_IRQ) #define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH) #define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) +#define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U) #define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ (__force unsigned)(mark))) +/* ALLOC is for a normal 0-based alloc. ALLOC1 is for an 1-based alloc */ #define XA_FLAGS_ALLOC (XA_FLAGS_TRACK_FREE | XA_FLAGS_MARK(XA_FREE_MARK)) +#define XA_FLAGS_ALLOC1 (XA_FLAGS_TRACK_FREE | XA_FLAGS_ZERO_BUSY) /** * struct xarray - The anchor of the XArray. @@ -279,7 +282,7 @@ struct xarray { #define DEFINE_XARRAY(name) DEFINE_XARRAY_FLAGS(name, 0) /** - * DEFINE_XARRAY_ALLOC() - Define an XArray which can allocate IDs. + * DEFINE_XARRAY_ALLOC() - Define an XArray which allocates IDs starting at 0. * @name: A string that names your XArray. * * This is intended for file scope definitions of allocating XArrays. @@ -287,6 +290,15 @@ struct xarray { */ #define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC) +/** + * DEFINE_XARRAY_ALLOC1() - Define an XArray which allocates IDs starting at 1. + * @name: A string that names your XArray. + * + * This is intended for file scope definitions of allocating XArrays. + * See also DEFINE_XARRAY(). + */ +#define DEFINE_XARRAY_ALLOC1(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC1) + void *xa_load(struct xarray *, unsigned long index); void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); void *xa_erase(struct xarray *, unsigned long index); diff --git a/lib/test_xarray.c b/lib/test_xarray.c index 9d894e93456c..cd74f8f32abe 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c @@ -589,64 +589,86 @@ static noinline void check_multi_store(struct xarray *xa) #endif } -static DEFINE_XARRAY_ALLOC(xa0); - -static noinline void check_xa_alloc(void) +static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base) { int i; u32 id; - /* An empty array should assign 0 to the first alloc */ - xa_alloc_index(&xa0, 0, GFP_KERNEL); + XA_BUG_ON(xa, !xa_empty(xa)); + /* An empty array should assign %base to the first alloc */ + xa_alloc_index(xa, base, GFP_KERNEL); /* Erasing it should make the array empty again */ - xa_erase_index(&xa0, 0); - XA_BUG_ON(&xa0, !xa_empty(&xa0)); + xa_erase_index(xa, base); + XA_BUG_ON(xa, !xa_empty(xa)); - /* And it should assign 0 again */ - xa_alloc_index(&xa0, 0, GFP_KERNEL); + /* And it should assign %base again */ + xa_alloc_index(xa, base, GFP_KERNEL); - /* The next assigned ID should be 1 */ - xa_alloc_index(&xa0, 1, GFP_KERNEL); - xa_erase_index(&xa0, 1); + /* Allocating and then erasing a lot should not lose base */ + for (i = base + 1; i < 2 * XA_CHUNK_SIZE; i++) + xa_alloc_index(xa, i, GFP_KERNEL); + for (i = base; i < 2 * XA_CHUNK_SIZE; i++) + xa_erase_index(xa, i); + xa_alloc_index(xa, base, GFP_KERNEL); + + /* Destroying the array should do the same as erasing */ + xa_destroy(xa); + + /* And it should assign %base again */ + xa_alloc_index(xa, base, GFP_KERNEL); + + /* The next assigned ID should be base+1 */ + xa_alloc_index(xa, base + 1, GFP_KERNEL); + xa_erase_index(xa, base + 1); /* Storing a value should mark it used */ - xa_store_index(&xa0, 1, GFP_KERNEL); - xa_alloc_index(&xa0, 2, GFP_KERNEL); + xa_store_index(xa, base + 1, GFP_KERNEL); + xa_alloc_index(xa, base + 2, GFP_KERNEL); - /* If we then erase 0, it should be free */ - xa_erase_index(&xa0, 0); - xa_alloc_index(&xa0, 0, GFP_KERNEL); + /* If we then erase base, it should be free */ + xa_erase_index(xa, base); + xa_alloc_index(xa, base, GFP_KERNEL); - xa_erase_index(&xa0, 1); - xa_erase_index(&xa0, 2); + xa_erase_index(xa, base + 1); + xa_erase_index(xa, base + 2); for (i = 1; i < 5000; i++) { - xa_alloc_index(&xa0, i, GFP_KERNEL); + xa_alloc_index(xa, base + i, GFP_KERNEL); } - xa_destroy(&xa0); + xa_destroy(xa); + /* Check that we fail properly at the limit of allocation */ id = 0xfffffffeU; - XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id), + XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), GFP_KERNEL) != 0); - XA_BUG_ON(&xa0, id != 0xfffffffeU); - XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id), + XA_BUG_ON(xa, id != 0xfffffffeU); + XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), GFP_KERNEL) != 0); - XA_BUG_ON(&xa0, id != 0xffffffffU); - XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id), + XA_BUG_ON(xa, id != 0xffffffffU); + XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), GFP_KERNEL) != -ENOSPC); - XA_BUG_ON(&xa0, id != 0xffffffffU); - xa_destroy(&xa0); + XA_BUG_ON(xa, id != 0xffffffffU); + xa_destroy(xa); id = 10; - XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, 5, xa_mk_index(id), + XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), GFP_KERNEL) != -ENOSPC); - XA_BUG_ON(&xa0, xa_store_index(&xa0, 3, GFP_KERNEL) != 0); - XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, 5, xa_mk_index(id), + XA_BUG_ON(xa, xa_store_index(xa, 3, GFP_KERNEL) != 0); + XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), GFP_KERNEL) != -ENOSPC); - xa_erase_index(&xa0, 3); - XA_BUG_ON(&xa0, !xa_empty(&xa0)); + xa_erase_index(xa, 3); + XA_BUG_ON(xa, !xa_empty(xa)); +} + +static DEFINE_XARRAY_ALLOC(xa0); +static DEFINE_XARRAY_ALLOC1(xa1); + +static noinline void check_xa_alloc(void) +{ + check_xa_alloc_1(&xa0, 0); + check_xa_alloc_1(&xa1, 1); } static noinline void __check_store_iter(struct xarray *xa, unsigned long start, diff --git a/lib/xarray.c b/lib/xarray.c index 1b97ca58bd15..468fb7b7963f 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -57,6 +57,11 @@ static inline bool xa_track_free(const struct xarray *xa) return xa->xa_flags & XA_FLAGS_TRACK_FREE; } +static inline bool xa_zero_busy(const struct xarray *xa) +{ + return xa->xa_flags & XA_FLAGS_ZERO_BUSY; +} + static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark) { if (!(xa->xa_flags & XA_FLAGS_MARK(mark))) @@ -432,6 +437,8 @@ static void xas_shrink(struct xa_state *xas) break; if (!xa_is_node(entry) && node->shift) break; + if (xa_is_zero(entry) && xa_zero_busy(xa)) + entry = NULL; xas->xa_node = XAS_BOUNDS; RCU_INIT_POINTER(xa->xa_head, entry); @@ -628,6 +635,8 @@ static void *xas_create(struct xa_state *xas, bool allow_root) if (xas_top(node)) { entry = xa_head_locked(xa); xas->xa_node = NULL; + if (!entry && xa_zero_busy(xa)) + entry = XA_ZERO_ENTRY; shift = xas_expand(xas, entry); if (shift < 0) return NULL; @@ -1942,6 +1951,8 @@ void xa_destroy(struct xarray *xa) entry = xa_head_locked(xa); RCU_INIT_POINTER(xa->xa_head, NULL); xas_init_marks(&xas); + if (xa_zero_busy(xa)) + xa_mark_clear(xa, XA_FREE_MARK); /* lockdep checks we're still holding the lock in xas_free_nodes() */ if (xa_is_node(entry)) xas_free_nodes(&xas, xa_to_node(entry)); From a3e4d3f97ec844de005a679585c04c5c03dfbdb6 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 31 Dec 2018 10:41:01 -0500 Subject: [PATCH 1268/4277] XArray: Redesign xa_alloc API It was too easy to forget to initialise the start index. Add an xa_limit data structure which can be used to pass min & max, and define a couple of special values for common cases. Also add some more tests cribbed from the IDR test suite. Change the return value from -ENOSPC to -EBUSY to match xa_insert(). Signed-off-by: Matthew Wilcox --- include/linux/xarray.h | 80 +++++++++++++++++++++++++-------------- lib/test_xarray.c | 86 ++++++++++++++++++++++++++++++++++-------- lib/xarray.c | 29 +++++++------- 3 files changed, 135 insertions(+), 60 deletions(-) diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 99dd0838b4ba..883bb958e462 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -200,6 +200,27 @@ static inline int xa_err(void *entry) return 0; } +/** + * struct xa_limit - Represents a range of IDs. + * @min: The lowest ID to allocate (inclusive). + * @max: The maximum ID to allocate (inclusive). + * + * This structure is used either directly or via the XA_LIMIT() macro + * to communicate the range of IDs that are valid for allocation. + * Two common ranges are predefined for you: + * * xa_limit_32b - [0 - UINT_MAX] + * * xa_limit_31b - [0 - INT_MAX] + */ +struct xa_limit { + u32 max; + u32 min; +}; + +#define XA_LIMIT(_min, _max) (struct xa_limit) { .min = _min, .max = _max } + +#define xa_limit_32b XA_LIMIT(0, UINT_MAX) +#define xa_limit_31b XA_LIMIT(0, INT_MAX) + typedef unsigned __bitwise xa_mark_t; #define XA_MARK_0 ((__force xa_mark_t)0U) #define XA_MARK_1 ((__force xa_mark_t)1U) @@ -476,7 +497,8 @@ void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old, void *entry, gfp_t); int __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t); -int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t); +int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry, + struct xa_limit, gfp_t); int __xa_reserve(struct xarray *, unsigned long index, gfp_t); void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); @@ -753,26 +775,26 @@ static inline int xa_insert_irq(struct xarray *xa, unsigned long index, * xa_alloc() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. - * @max: Maximum ID to allocate (inclusive). * @entry: New entry. + * @limit: Range of ID to allocate. * @gfp: Memory allocation flags. * - * Allocates an unused ID in the range specified by @id and @max. - * Updates the @id pointer with the index, then stores the entry at that - * index. A concurrent lookup will not see an uninitialised @id. + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. * - * Context: Process context. Takes and releases the xa_lock. May sleep if + * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. - * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if - * there is no more space in the XArray. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. */ -static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, - gfp_t gfp) +static inline __must_check int xa_alloc(struct xarray *xa, u32 *id, + void *entry, struct xa_limit limit, gfp_t gfp) { int err; xa_lock(xa); - err = __xa_alloc(xa, id, max, entry, gfp); + err = __xa_alloc(xa, id, entry, limit, gfp); xa_unlock(xa); return err; @@ -782,26 +804,26 @@ static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, * xa_alloc_bh() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. - * @max: Maximum ID to allocate (inclusive). * @entry: New entry. + * @limit: Range of ID to allocate. * @gfp: Memory allocation flags. * - * Allocates an unused ID in the range specified by @id and @max. - * Updates the @id pointer with the index, then stores the entry at that - * index. A concurrent lookup will not see an uninitialised @id. + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. - * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if - * there is no more space in the XArray. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. */ -static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry, - gfp_t gfp) +static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id, + void *entry, struct xa_limit limit, gfp_t gfp) { int err; xa_lock_bh(xa); - err = __xa_alloc(xa, id, max, entry, gfp); + err = __xa_alloc(xa, id, entry, limit, gfp); xa_unlock_bh(xa); return err; @@ -811,26 +833,26 @@ static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry, * xa_alloc_irq() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. - * @max: Maximum ID to allocate (inclusive). * @entry: New entry. + * @limit: Range of ID to allocate. * @gfp: Memory allocation flags. * - * Allocates an unused ID in the range specified by @id and @max. - * Updates the @id pointer with the index, then stores the entry at that - * index. A concurrent lookup will not see an uninitialised @id. + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. - * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if - * there is no more space in the XArray. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. */ -static inline int xa_alloc_irq(struct xarray *xa, u32 *id, u32 max, void *entry, - gfp_t gfp) +static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id, + void *entry, struct xa_limit limit, gfp_t gfp) { int err; xa_lock_irq(xa); - err = __xa_alloc(xa, id, max, entry, gfp); + err = __xa_alloc(xa, id, entry, limit, gfp); xa_unlock_irq(xa); return err; diff --git a/lib/test_xarray.c b/lib/test_xarray.c index cd74f8f32abe..b5a6b981454d 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c @@ -40,9 +40,9 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp) static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp) { - u32 id = 0; + u32 id; - XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(index), + XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(index), xa_limit_32b, gfp) != 0); XA_BUG_ON(xa, id != index); } @@ -640,28 +640,81 @@ static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base) xa_destroy(xa); /* Check that we fail properly at the limit of allocation */ - id = 0xfffffffeU; - XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), + XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX - 1), + XA_LIMIT(UINT_MAX - 1, UINT_MAX), GFP_KERNEL) != 0); XA_BUG_ON(xa, id != 0xfffffffeU); - XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), + XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX), + XA_LIMIT(UINT_MAX - 1, UINT_MAX), GFP_KERNEL) != 0); XA_BUG_ON(xa, id != 0xffffffffU); - XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), - GFP_KERNEL) != -ENOSPC); - XA_BUG_ON(xa, id != 0xffffffffU); + id = 3; + XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(0), + XA_LIMIT(UINT_MAX - 1, UINT_MAX), + GFP_KERNEL) != -EBUSY); + XA_BUG_ON(xa, id != 3); xa_destroy(xa); - id = 10; - XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), - GFP_KERNEL) != -ENOSPC); + XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5), + GFP_KERNEL) != -EBUSY); XA_BUG_ON(xa, xa_store_index(xa, 3, GFP_KERNEL) != 0); - XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), - GFP_KERNEL) != -ENOSPC); + XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5), + GFP_KERNEL) != -EBUSY); xa_erase_index(xa, 3); XA_BUG_ON(xa, !xa_empty(xa)); } +static noinline void check_xa_alloc_2(struct xarray *xa, unsigned int base) +{ + unsigned int i, id; + unsigned long index; + void *entry; + + /* Allocate and free a NULL and check xa_empty() behaves */ + XA_BUG_ON(xa, !xa_empty(xa)); + XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != base); + XA_BUG_ON(xa, xa_empty(xa)); + XA_BUG_ON(xa, xa_erase(xa, id) != NULL); + XA_BUG_ON(xa, !xa_empty(xa)); + + /* Ditto, but check destroy instead of erase */ + XA_BUG_ON(xa, !xa_empty(xa)); + XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != base); + XA_BUG_ON(xa, xa_empty(xa)); + xa_destroy(xa); + XA_BUG_ON(xa, !xa_empty(xa)); + + for (i = base; i < base + 10; i++) { + XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, + GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != i); + } + + XA_BUG_ON(xa, xa_store(xa, 3, xa_mk_index(3), GFP_KERNEL) != NULL); + XA_BUG_ON(xa, xa_store(xa, 4, xa_mk_index(4), GFP_KERNEL) != NULL); + XA_BUG_ON(xa, xa_store(xa, 4, NULL, GFP_KERNEL) != xa_mk_index(4)); + XA_BUG_ON(xa, xa_erase(xa, 5) != NULL); + XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != 5); + + xa_for_each(xa, index, entry) { + xa_erase_index(xa, index); + } + + for (i = base; i < base + 9; i++) { + XA_BUG_ON(xa, xa_erase(xa, i) != NULL); + XA_BUG_ON(xa, xa_empty(xa)); + } + XA_BUG_ON(xa, xa_erase(xa, 8) != NULL); + XA_BUG_ON(xa, xa_empty(xa)); + XA_BUG_ON(xa, xa_erase(xa, base + 9) != NULL); + XA_BUG_ON(xa, !xa_empty(xa)); + + xa_destroy(xa); +} + static DEFINE_XARRAY_ALLOC(xa0); static DEFINE_XARRAY_ALLOC1(xa1); @@ -669,6 +722,8 @@ static noinline void check_xa_alloc(void) { check_xa_alloc_1(&xa0, 0); check_xa_alloc_1(&xa1, 1); + check_xa_alloc_2(&xa0, 0); + check_xa_alloc_2(&xa1, 1); } static noinline void __check_store_iter(struct xarray *xa, unsigned long start, @@ -1219,9 +1274,8 @@ static void check_align_1(struct xarray *xa, char *name) void *entry; for (i = 0; i < 8; i++) { - id = 0; - XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, name + i, GFP_KERNEL) - != 0); + XA_BUG_ON(xa, xa_alloc(xa, &id, name + i, xa_limit_32b, + GFP_KERNEL) != 0); XA_BUG_ON(xa, id != i); } xa_for_each(xa, index, entry) diff --git a/lib/xarray.c b/lib/xarray.c index 468fb7b7963f..c707388fb05e 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1615,23 +1615,23 @@ EXPORT_SYMBOL(xa_store_range); * __xa_alloc() - Find somewhere to store this entry in the XArray. * @xa: XArray. * @id: Pointer to ID. - * @max: Maximum ID to allocate (inclusive). + * @limit: Range for allocated ID. * @entry: New entry. * @gfp: Memory allocation flags. * - * Allocates an unused ID in the range specified by @id and @max. - * Updates the @id pointer with the index, then stores the entry at that - * index. A concurrent lookup will not see an uninitialised @id. + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. * * Context: Any context. Expects xa_lock to be held on entry. May * release and reacquire xa_lock if @gfp flags permit. - * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if - * there is no more space in the XArray. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. */ -int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp) +int __xa_alloc(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, gfp_t gfp) { XA_STATE(xas, xa, 0); - int err; if (WARN_ON_ONCE(xa_is_advanced(entry))) return -EINVAL; @@ -1642,18 +1642,17 @@ int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp) entry = XA_ZERO_ENTRY; do { - xas.xa_index = *id; - xas_find_marked(&xas, max, XA_FREE_MARK); + xas.xa_index = limit.min; + xas_find_marked(&xas, limit.max, XA_FREE_MARK); if (xas.xa_node == XAS_RESTART) - xas_set_err(&xas, -ENOSPC); + xas_set_err(&xas, -EBUSY); + else + *id = xas.xa_index; xas_store(&xas, entry); xas_clear_mark(&xas, XA_FREE_MARK); } while (__xas_nomem(&xas, gfp)); - err = xas_error(&xas); - if (!err) - *id = xas.xa_index; - return err; + return xas_error(&xas); } EXPORT_SYMBOL(__xa_alloc); From 2fa044e51a1f35d7b04cbde07ec513b0ba195e38 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 6 Nov 2018 14:13:35 -0500 Subject: [PATCH 1269/4277] XArray: Add cyclic allocation This differs slightly from the IDR equivalent in five ways. 1. It can allocate up to UINT_MAX instead of being limited to INT_MAX, like xa_alloc(). Also like xa_alloc(), it will write to the 'id' pointer before placing the entry in the XArray. 2. The 'next' cursor is allocated separately from the XArray instead of being part of the IDR. This saves memory for all the users which do not use the cyclic allocation API and suits some users better. 3. It returns -EBUSY instead of -ENOSPC. 4. It will attempt to wrap back to the minimum value on memory allocation failure as well as on an -EBUSY error, assuming that a user would rather allocate a small ID than suffer an ID allocation failure. 5. It reports whether it has wrapped, which is important to some users. Signed-off-by: Matthew Wilcox --- Documentation/core-api/xarray.rst | 4 +- include/linux/xarray.h | 102 ++++++++++++++++++++++++++++++ lib/test_xarray.c | 53 ++++++++++++++++ lib/xarray.c | 50 +++++++++++++++ 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/Documentation/core-api/xarray.rst b/Documentation/core-api/xarray.rst index e90c4925cd37..c7436da5c4ad 100644 --- a/Documentation/core-api/xarray.rst +++ b/Documentation/core-api/xarray.rst @@ -144,7 +144,9 @@ you only want to free the entry if it's ``NULL``). By default, the lowest free entry is allocated starting from 0. If you want to allocate entries starting at 1, it is more efficient to use -:c:func:`DEFINE_XARRAY_ALLOC1` or ``XA_FLAGS_ALLOC1``. +:c:func:`DEFINE_XARRAY_ALLOC1` or ``XA_FLAGS_ALLOC1``. If you want to +allocate IDs up to a maximum, then wrap back around to the lowest free +ID, you can use :c:func:`xa_alloc_cyclic`. You cannot use ``XA_MARK_0`` with an allocating XArray as this mark is used to track whether an entry is free or not. The other marks are diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 883bb958e462..5ed6b462e754 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -242,6 +242,7 @@ enum xa_lock_type { #define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH) #define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) #define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U) +#define XA_FLAGS_ALLOC_WRAPPED ((__force gfp_t)16U) #define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ (__force unsigned)(mark))) @@ -499,6 +500,8 @@ void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old, int __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t); int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry, struct xa_limit, gfp_t); +int __must_check __xa_alloc_cyclic(struct xarray *, u32 *id, void *entry, + struct xa_limit, u32 *next, gfp_t); int __xa_reserve(struct xarray *, unsigned long index, gfp_t); void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); @@ -858,6 +861,105 @@ static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id, return err; } +/** + * xa_alloc_cyclic() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Any context. Takes and releases the xa_lock. May sleep if + * the @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +static inline int xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + int err; + + xa_lock(xa); + err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); + xa_unlock(xa); + + return err; +} + +/** + * xa_alloc_cyclic_bh() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. May sleep if the @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +static inline int xa_alloc_cyclic_bh(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + int err; + + xa_lock_bh(xa); + err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); + xa_unlock_bh(xa); + + return err; +} + +/** + * xa_alloc_cyclic_irq() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. May sleep if the @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +static inline int xa_alloc_cyclic_irq(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + int err; + + xa_lock_irq(xa); + err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); + xa_unlock_irq(xa); + + return err; +} + /** * xa_reserve() - Reserve this index in the XArray. * @xa: XArray. diff --git a/lib/test_xarray.c b/lib/test_xarray.c index b5a6b981454d..eaf53f742c72 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c @@ -715,6 +715,57 @@ static noinline void check_xa_alloc_2(struct xarray *xa, unsigned int base) xa_destroy(xa); } +static noinline void check_xa_alloc_3(struct xarray *xa, unsigned int base) +{ + struct xa_limit limit = XA_LIMIT(1, 0x3fff); + u32 next = 0; + unsigned int i, id; + unsigned long index; + void *entry; + + XA_BUG_ON(xa, xa_alloc_cyclic(xa, &id, xa_mk_index(1), limit, + &next, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != 1); + + next = 0x3ffd; + XA_BUG_ON(xa, xa_alloc_cyclic(xa, &id, xa_mk_index(0x3ffd), limit, + &next, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != 0x3ffd); + xa_erase_index(xa, 0x3ffd); + xa_erase_index(xa, 1); + XA_BUG_ON(xa, !xa_empty(xa)); + + for (i = 0x3ffe; i < 0x4003; i++) { + if (i < 0x4000) + entry = xa_mk_index(i); + else + entry = xa_mk_index(i - 0x3fff); + XA_BUG_ON(xa, xa_alloc_cyclic(xa, &id, entry, limit, + &next, GFP_KERNEL) != (id == 1)); + XA_BUG_ON(xa, xa_mk_index(id) != entry); + } + + /* Check wrap-around is handled correctly */ + if (base != 0) + xa_erase_index(xa, base); + xa_erase_index(xa, base + 1); + next = UINT_MAX; + XA_BUG_ON(xa, xa_alloc_cyclic(xa, &id, xa_mk_index(UINT_MAX), + xa_limit_32b, &next, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != UINT_MAX); + XA_BUG_ON(xa, xa_alloc_cyclic(xa, &id, xa_mk_index(base), + xa_limit_32b, &next, GFP_KERNEL) != 1); + XA_BUG_ON(xa, id != base); + XA_BUG_ON(xa, xa_alloc_cyclic(xa, &id, xa_mk_index(base + 1), + xa_limit_32b, &next, GFP_KERNEL) != 0); + XA_BUG_ON(xa, id != base + 1); + + xa_for_each(xa, index, entry) + xa_erase_index(xa, index); + + XA_BUG_ON(xa, !xa_empty(xa)); +} + static DEFINE_XARRAY_ALLOC(xa0); static DEFINE_XARRAY_ALLOC1(xa1); @@ -724,6 +775,8 @@ static noinline void check_xa_alloc(void) check_xa_alloc_1(&xa1, 1); check_xa_alloc_2(&xa0, 0); check_xa_alloc_2(&xa1, 1); + check_xa_alloc_3(&xa0, 0); + check_xa_alloc_3(&xa1, 1); } static noinline void __check_store_iter(struct xarray *xa, unsigned long start, diff --git a/lib/xarray.c b/lib/xarray.c index c707388fb05e..89e37ac50850 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1656,6 +1656,56 @@ int __xa_alloc(struct xarray *xa, u32 *id, void *entry, } EXPORT_SYMBOL(__xa_alloc); +/** + * __xa_alloc_cyclic() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Any context. Expects xa_lock to be held on entry. May + * release and reacquire xa_lock if @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +int __xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + u32 min = limit.min; + int ret; + + limit.min = max(min, *next); + ret = __xa_alloc(xa, id, entry, limit, gfp); + if ((xa->xa_flags & XA_FLAGS_ALLOC_WRAPPED) && ret == 0) { + xa->xa_flags &= ~XA_FLAGS_ALLOC_WRAPPED; + ret = 1; + } + + if (ret < 0 && limit.min > min) { + limit.min = min; + ret = __xa_alloc(xa, id, entry, limit, gfp); + if (ret == 0) + ret = 1; + } + + if (ret >= 0) { + *next = *id + 1; + if (*next == 0) + xa->xa_flags |= XA_FLAGS_ALLOC_WRAPPED; + } + return ret; +} +EXPORT_SYMBOL(__xa_alloc_cyclic); + /** * __xa_set_mark() - Set this mark on this entry while locked. * @xa: XArray. From 5c56dfe63b6e7da3d9fd84a6c553ff4d2753dd1c Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Mon, 3 Dec 2018 11:13:08 +0000 Subject: [PATCH 1270/4277] clk: Add comment about __of_clk_get_by_name() error values It's not immediately obvious from the code that failure to get a clock provider can return either -ENOENT or -EINVAL. Therefore, add a comment to highlight this. Signed-off-by: Phil Edworthy Reviewed-by: Andy Shevchenko Cc: Russell King [sboyd@kernel.org: Fix comment to be proper C with ==] Signed-off-by: Stephen Boyd --- drivers/clk/clkdev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9ab3db8b3988..5284fd191cca 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -52,6 +52,12 @@ struct clk *of_clk_get(struct device_node *np, int index) } EXPORT_SYMBOL(of_clk_get); +/* + * Beware the return values when np is valid, but no clock provider is found. + * If name == NULL, the function returns -ENOENT. + * If name != NULL, the function returns -EINVAL. This is because __of_clk_get() + * is called even if of_property_match_string() returns an error. + */ static struct clk *__of_clk_get_by_name(struct device_node *np, const char *dev_id, const char *name) From 60b8f0ddf1a927ef02141a6610fd52575134f821 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Mon, 3 Dec 2018 11:13:09 +0000 Subject: [PATCH 1271/4277] clk: Add (devm_)clk_get_optional() functions This adds clk_get_optional() and devm_clk_get_optional() functions to get optional clocks. They behave the same as (devm_)clk_get() except where there is no clock producer. In this case, instead of returning -ENOENT, the function returns NULL. This makes error checking simpler and allows clk_prepare_enable, etc to be called on the returned reference without additional checks. Signed-off-by: Phil Edworthy Reviewed-by: Andy Shevchenko Cc: Russell King [sboyd@kernel.org: Document in devres.txt] Signed-off-by: Stephen Boyd --- Documentation/driver-model/devres.txt | 1 + drivers/clk/clk-devres.c | 11 ++++++++ include/linux/clk.h | 36 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index b277cafce71e..83f38c0439cd 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -242,6 +242,7 @@ certainly invest a bit more effort into libata core layer). CLOCK devm_clk_get() + devm_clk_get_optional() devm_clk_put() devm_clk_hw_register() devm_of_clk_add_hw_provider() diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index c9a86156ced8..daa1fc8fba53 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -29,6 +29,17 @@ struct clk *devm_clk_get(struct device *dev, const char *id) } EXPORT_SYMBOL(devm_clk_get); +struct clk *devm_clk_get_optional(struct device *dev, const char *id) +{ + struct clk *clk = devm_clk_get(dev, id); + + if (clk == ERR_PTR(-ENOENT)) + return NULL; + + return clk; +} +EXPORT_SYMBOL(devm_clk_get_optional); + struct clk_bulk_devres { struct clk_bulk_data *clks; int num_clks; diff --git a/include/linux/clk.h b/include/linux/clk.h index a7773b5c0b9f..d8bc1a856b39 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -383,6 +383,17 @@ int __must_check devm_clk_bulk_get_all(struct device *dev, */ struct clk *devm_clk_get(struct device *dev, const char *id); +/** + * devm_clk_get_optional - lookup and obtain a managed reference to an optional + * clock producer. + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Behaves the same as devm_clk_get() except where there is no clock producer. + * In this case, instead of returning -ENOENT, the function returns NULL. + */ +struct clk *devm_clk_get_optional(struct device *dev, const char *id); + /** * devm_get_clk_from_child - lookup and obtain a managed reference to a * clock producer from child node. @@ -718,6 +729,12 @@ static inline struct clk *devm_clk_get(struct device *dev, const char *id) return NULL; } +static inline struct clk *devm_clk_get_optional(struct device *dev, + const char *id) +{ + return NULL; +} + static inline int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, struct clk_bulk_data *clks) { @@ -862,6 +879,25 @@ static inline void clk_bulk_disable_unprepare(int num_clks, clk_bulk_unprepare(num_clks, clks); } +/** + * clk_get_optional - lookup and obtain a reference to an optional clock + * producer. + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Behaves the same as clk_get() except where there is no clock producer. In + * this case, instead of returning -ENOENT, the function returns NULL. + */ +static inline struct clk *clk_get_optional(struct device *dev, const char *id) +{ + struct clk *clk = clk_get(dev, id); + + if (clk == ERR_PTR(-ENOENT)) + return NULL; + + return clk; +} + #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) struct clk *of_clk_get(struct device_node *np, int index); struct clk *of_clk_get_by_name(struct device_node *np, const char *name); From 3eee6c7d119cd8563ad25898f94d6c1b514da548 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 7 Dec 2018 13:09:39 +0200 Subject: [PATCH 1272/4277] clkdev: add managed clkdev lookup registration Clkdev registration lacks of managed registration functions and it seems few drivers do not drop clkdev lookups at exit. Add devm_clk_hw_register_clkdev and devm_clk_release_clkdev to ease lookup releasing at exit. Signed-off-by: Matti Vaittinen Signed-off-by: Stephen Boyd --- Documentation/driver-model/devres.txt | 1 + drivers/clk/clkdev.c | 113 ++++++++++++++++++++------ include/linux/clkdev.h | 4 + 3 files changed, 94 insertions(+), 24 deletions(-) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index b277cafce71e..805b7bf5d98f 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -245,6 +245,7 @@ CLOCK devm_clk_put() devm_clk_hw_register() devm_of_clk_add_hw_provider() + devm_clk_hw_register_clkdev() DMA dmaenginem_async_device_register() diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9ab3db8b3988..4621f8a91fc0 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -401,6 +401,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw, return cl; } +static int do_clk_register_clkdev(struct clk_hw *hw, + struct clk_lookup **cl, const char *con_id, const char *dev_id) +{ + if (IS_ERR(hw)) + return PTR_ERR(hw); + /* + * Since dev_id can be NULL, and NULL is handled specially, we must + * pass it as either a NULL format string, or with "%s". + */ + if (dev_id) + *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); + else + *cl = __clk_register_clkdev(hw, con_id, NULL); + + return *cl ? 0 : -ENOMEM; +} + /** * clk_register_clkdev - register one clock lookup for a struct clk * @clk: struct clk to associate with all clk_lookups @@ -423,17 +440,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, if (IS_ERR(clk)) return PTR_ERR(clk); - /* - * Since dev_id can be NULL, and NULL is handled specially, we must - * pass it as either a NULL format string, or with "%s". - */ - if (dev_id) - cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s", - dev_id); - else - cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL); - - return cl ? 0 : -ENOMEM; + return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id, + dev_id); } EXPORT_SYMBOL(clk_register_clkdev); @@ -456,18 +464,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, { struct clk_lookup *cl; - if (IS_ERR(hw)) - return PTR_ERR(hw); - - /* - * Since dev_id can be NULL, and NULL is handled specially, we must - * pass it as either a NULL format string, or with "%s". - */ - if (dev_id) - cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); - else - cl = __clk_register_clkdev(hw, con_id, NULL); - - return cl ? 0 : -ENOMEM; + return do_clk_register_clkdev(hw, &cl, con_id, dev_id); } EXPORT_SYMBOL(clk_hw_register_clkdev); + +static void devm_clkdev_release(struct device *dev, void *res) +{ + clkdev_drop(*(struct clk_lookup **)res); +} + +static int devm_clk_match_clkdev(struct device *dev, void *res, void *data) +{ + struct clk_lookup **l = res; + + return *l == data; +} + +/** + * devm_clk_release_clkdev - Resource managed clkdev lookup release + * @dev: device this lookup is bound + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * Drop the clkdev lookup created with devm_clk_hw_register_clkdev. + * Normally this function will not need to be called and the resource + * management code will ensure that the resource is freed. + */ +void devm_clk_release_clkdev(struct device *dev, const char *con_id, + const char *dev_id) +{ + struct clk_lookup *cl; + int rval; + + cl = clk_find(dev_id, con_id); + WARN_ON(!cl); + rval = devres_release(dev, devm_clkdev_release, + devm_clk_match_clkdev, cl); + WARN_ON(rval); +} +EXPORT_SYMBOL(devm_clk_release_clkdev); + +/** + * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw + * @dev: device this lookup is bound + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. + * + * To make things easier for mass registration, we detect error clk_hws + * from a previous clk_hw_register_*() call, and return the error code for + * those. This is to permit this function to be called immediately + * after clk_hw_register_*(). + */ +int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, + const char *con_id, const char *dev_id) +{ + int rval = -ENOMEM; + struct clk_lookup **cl; + + cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL); + if (cl) { + rval = do_clk_register_clkdev(hw, cl, con_id, dev_id); + if (!rval) + devres_add(dev, cl); + else + devres_free(cl); + } + return rval; +} +EXPORT_SYMBOL(devm_clk_hw_register_clkdev); diff --git a/include/linux/clkdev.h b/include/linux/clkdev.h index 4890ff033220..ccb32af5848b 100644 --- a/include/linux/clkdev.h +++ b/include/linux/clkdev.h @@ -52,4 +52,8 @@ int clk_add_alias(const char *, const char *, const char *, struct device *); int clk_register_clkdev(struct clk *, const char *, const char *); int clk_hw_register_clkdev(struct clk_hw *, const char *, const char *); +int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, + const char *con_id, const char *dev_id); +void devm_clk_release_clkdev(struct device *dev, const char *con_id, + const char *dev_id); #endif From 8144e1e8cacf30b74e331120edc368270b0ec1c0 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 7 Dec 2018 13:10:15 +0200 Subject: [PATCH 1273/4277] clk: clk-max77686: Clean clkdev lookup leak and use devm clk-max77686 never clean clkdev lookup at remove. This can cause oops if clk-max77686 is removed and inserted again. Fix leak by using new devm clkdev lookup registration. Simplify also error path by using new devm_of_clk_add_hw_provider. Signed-off-by: Matti Vaittinen Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/clk-max77686.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 22c937644c93..3727d5472450 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -235,8 +235,9 @@ static int max77686_clk_probe(struct platform_device *pdev) return ret; } - ret = clk_hw_register_clkdev(&max_clk_data->hw, - max_clk_data->clk_idata.name, NULL); + ret = devm_clk_hw_register_clkdev(dev, &max_clk_data->hw, + max_clk_data->clk_idata.name, + NULL); if (ret < 0) { dev_err(dev, "Failed to clkdev register: %d\n", ret); return ret; @@ -244,8 +245,8 @@ static int max77686_clk_probe(struct platform_device *pdev) } if (parent->of_node) { - ret = of_clk_add_hw_provider(parent->of_node, of_clk_max77686_get, - drv_data); + ret = devm_of_clk_add_hw_provider(dev, of_clk_max77686_get, + drv_data); if (ret < 0) { dev_err(dev, "Failed to register OF clock provider: %d\n", @@ -261,27 +262,11 @@ static int max77686_clk_probe(struct platform_device *pdev) 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); if (ret < 0) { dev_err(dev, "Failed to config low-jitter: %d\n", ret); - goto remove_of_clk_provider; + return ret; } } return 0; - -remove_of_clk_provider: - if (parent->of_node) - of_clk_del_provider(parent->of_node); - - return ret; -} - -static int max77686_clk_remove(struct platform_device *pdev) -{ - struct device *parent = pdev->dev.parent; - - if (parent->of_node) - of_clk_del_provider(parent->of_node); - - return 0; } static const struct platform_device_id max77686_clk_id[] = { @@ -297,7 +282,6 @@ static struct platform_driver max77686_clk_driver = { .name = "max77686-clk", }, .probe = max77686_clk_probe, - .remove = max77686_clk_remove, .id_table = max77686_clk_id, }; From d3236214e7dead87e7aefd32259aaf0bf41cf050 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 7 Dec 2018 13:10:51 +0200 Subject: [PATCH 1274/4277] clk: clk-st: avoid clkdev lookup leak at remove Use devm based clkdev lookup registration to avoid leaking lookup structures. Signed-off-by: Matti Vaittinen Signed-off-by: Stephen Boyd --- drivers/clk/x86/clk-st.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/x86/clk-st.c b/drivers/clk/x86/clk-st.c index 3a0996f2d556..25d4b97aff9b 100644 --- a/drivers/clk/x86/clk-st.c +++ b/drivers/clk/x86/clk-st.c @@ -52,7 +52,8 @@ static int st_clk_probe(struct platform_device *pdev) 0, st_data->base + MISCCLKCNTL1, OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL); - clk_hw_register_clkdev(hws[ST_CLK_GATE], "oscout1", NULL); + devm_clk_hw_register_clkdev(&pdev->dev, hws[ST_CLK_GATE], "oscout1", + NULL); return 0; } From 67e9ac8c565a095b8614b54d9305ba90232c662b Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Fri, 1 Feb 2019 10:41:33 +0100 Subject: [PATCH 1275/4277] MAINTAINERS: chrome-platform: change the git tree to a chrome-platform group git tree Update the git tree URL for Chrome Platform drivers to point to a Chrome Platform Group managed URL. Signed-off-by: Enric Balletbo i Serra Signed-off-by: Benson Leung --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 51029a425dbe..a3c8b094228c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3675,7 +3675,7 @@ CHROME HARDWARE PLATFORM SUPPORT M: Benson Leung M: Enric Balletbo i Serra S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/bleung/chrome-platform.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git F: drivers/platform/chrome/ CHROMEOS EC SUBDRIVERS From 1c3da4452d185e4be663e76a1b9842184d8f9c4c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Jan 2019 09:47:03 +0300 Subject: [PATCH 1276/4277] nfsd: fix an IS_ERR() vs NULL check The get_backchannel_cred() used to return error pointers on error but now it returns NULL pointers. Fixes: 97f68c6b02e0 ("SUNRPC: add 'struct cred *' to auth_cred and rpc_cre") Signed-off-by: Dan Carpenter Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index c74e4538d0eb..9b38dab1c21b 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -913,9 +913,9 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c return PTR_ERR(client); } cred = get_backchannel_cred(clp, client, ses); - if (IS_ERR(cred)) { + if (!cred) { rpc_shutdown_client(client); - return PTR_ERR(cred); + return -ENOMEM; } clp->cl_cb_client = client; clp->cl_cb_cred = cred; From 1602a7b7d33741d0490682e620bb29e96ad684d7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 3 Jan 2019 09:17:12 -0500 Subject: [PATCH 1277/4277] SUNRPC: Don't allow compiler optimisation of svc_xprt_release_slot() Use READ_ONCE() to tell the compiler to not optimse away the read of xprt->xpt_flags in svc_xprt_release_slot(). Signed-off-by: Trond Myklebust Signed-off-by: J. Bruce Fields --- net/sunrpc/svc_xprt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 4eb8fbf2508d..c3af1aaef551 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -363,9 +363,13 @@ static void svc_xprt_release_slot(struct svc_rqst *rqstp) static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt) { - if (xprt->xpt_flags & ((1<xpt_flags); + + if (xpt_flags & (BIT(XPT_CONN) | BIT(XPT_CLOSE))) return true; - if (xprt->xpt_flags & ((1<xpt_ops->xpo_has_wspace(xprt) && svc_xprt_slots_in_range(xprt)) return true; From 66c898caefd346a88fbef242eb7892fd959308f6 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 11 Jan 2019 15:57:09 -0500 Subject: [PATCH 1278/4277] svcrpc: svc_xprt_has_something_to_do seems a little long The long name seemed cute till I wanted to refer to it somewhere else. Signed-off-by: J. Bruce Fields --- net/sunrpc/svc_xprt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index c3af1aaef551..a2435d3811a9 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -361,7 +361,7 @@ static void svc_xprt_release_slot(struct svc_rqst *rqstp) } } -static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt) +static bool svc_xprt_ready(struct svc_xprt *xprt) { unsigned long xpt_flags; @@ -385,7 +385,7 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt) struct svc_rqst *rqstp = NULL; int cpu; - if (!svc_xprt_has_something_to_do(xprt)) + if (!svc_xprt_ready(xprt)) return; /* Mark transport as busy. It will remain in this state until From 95503d295ad6af20f09efff193e085481a962fd2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 11 Jan 2019 15:36:40 -0500 Subject: [PATCH 1279/4277] svcrpc: fix unlikely races preventing queueing of sockets In the rpc server, When something happens that might be reason to wake up a thread to do something, what we do is - modify xpt_flags, sk_sock->flags, xpt_reserved, or xpt_nr_rqsts to indicate the new situation - call svc_xprt_enqueue() to decide whether to wake up a thread. svc_xprt_enqueue may require multiple conditions to be true before queueing up a thread to handle the xprt. In the SMP case, one of the other CPU's may have set another required condition, and in that case, although both CPUs run svc_xprt_enqueue(), it's possible that neither call sees the writes done by the other CPU in time, and neither one recognizes that all the required conditions have been set. A socket could therefore be ignored indefinitely. Add memory barries to ensure that any svc_xprt_enqueue() call will always see the conditions changed by other CPUs before deciding to ignore a socket. I've never seen this race reported. In the unlikely event it happens, another event will usually come along and the problem will fix itself. So I don't think this is worth backporting to stable. Chuck tried this patch and said "I don't see any performance regressions, but my server has only a single last-level CPU cache." Tested-by: Chuck Lever Signed-off-by: J. Bruce Fields --- net/sunrpc/svc_xprt.c | 12 +++++++++++- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 3 ++- net/sunrpc/xprtrdma/svc_rdma_rw.c | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index a2435d3811a9..61530b1b7754 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -357,6 +357,7 @@ static void svc_xprt_release_slot(struct svc_rqst *rqstp) struct svc_xprt *xprt = rqstp->rq_xprt; if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) { atomic_dec(&xprt->xpt_nr_rqsts); + smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ svc_xprt_enqueue(xprt); } } @@ -365,6 +366,15 @@ static bool svc_xprt_ready(struct svc_xprt *xprt) { unsigned long xpt_flags; + /* + * If another cpu has recently updated xpt_flags, + * sk_sock->flags, xpt_reserved, or xpt_nr_rqsts, we need to + * know about it; otherwise it's possible that both that cpu and + * this one could call svc_xprt_enqueue() without either + * svc_xprt_enqueue() recognizing that the conditions below + * are satisfied, and we could stall indefinitely: + */ + smp_rmb(); xpt_flags = READ_ONCE(xprt->xpt_flags); if (xpt_flags & (BIT(XPT_CONN) | BIT(XPT_CLOSE))) @@ -479,7 +489,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space) if (xprt && space < rqstp->rq_reserved) { atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved); rqstp->rq_reserved = space; - + smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */ svc_xprt_enqueue(xprt); } } diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 828b149eaaef..3ebb158c2279 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -314,8 +314,9 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) spin_lock(&rdma->sc_rq_dto_lock); list_add_tail(&ctxt->rc_list, &rdma->sc_rq_dto_q); - spin_unlock(&rdma->sc_rq_dto_lock); + /* Note the unlock pairs with the smp_rmb in svc_xprt_ready: */ set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); + spin_unlock(&rdma->sc_rq_dto_lock); if (!test_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags)) svc_xprt_enqueue(&rdma->sc_xprt); goto out; diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index dc1951759a8e..c35753691960 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -287,9 +287,10 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) spin_lock(&rdma->sc_rq_dto_lock); list_add_tail(&info->ri_readctxt->rc_list, &rdma->sc_read_complete_q); + /* Note the unlock pairs with the smp_rmb in svc_xprt_ready: */ + set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); spin_unlock(&rdma->sc_rq_dto_lock); - set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); svc_xprt_enqueue(&rdma->sc_xprt); } From 14cfbd94998a3ad6aaa67da46d997eea9e31897e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 15 Jan 2019 15:11:40 -0600 Subject: [PATCH 1280/4277] svcrdma: Use struct_size() in kmalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; struct boo entry[]; }; instance = kmalloc(sizeof(struct foo) + count * sizeof(struct boo), GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = kmalloc(struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Chuck Lever Signed-off-by: J. Bruce Fields --- net/sunrpc/xprtrdma/svc_rdma_rw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index c35753691960..65ee6fd60162 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -64,8 +64,7 @@ svc_rdma_get_rw_ctxt(struct svcxprt_rdma *rdma, unsigned int sges) spin_unlock(&rdma->sc_rw_ctxt_lock); } else { spin_unlock(&rdma->sc_rw_ctxt_lock); - ctxt = kmalloc(sizeof(*ctxt) + - SG_CHUNK_SIZE * sizeof(struct scatterlist), + ctxt = kmalloc(struct_size(ctxt, rw_first_sgl, SG_CHUNK_SIZE), GFP_KERNEL); if (!ctxt) goto out; From c7920f06ae755e2b2f3f14895a5fa148e81db7e2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 6 Feb 2019 12:00:52 -0500 Subject: [PATCH 1281/4277] svcrdma: Squelch compiler warning when SUNRPC_DEBUG is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC [M] net/sunrpc/xprtrdma/svc_rdma_transport.o linux/net/sunrpc/xprtrdma/svc_rdma_transport.c: In function ‘svc_rdma_accept’: linux/net/sunrpc/xprtrdma/svc_rdma_transport.c:452:19: warning: variable ‘sap’ set but not used [-Wunused-but-set-variable] struct sockaddr *sap; ^ Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- net/sunrpc/xprtrdma/svc_rdma_transport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 57f86c63a463..ef6afcf5e9c4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -390,8 +390,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) struct ib_qp_init_attr qp_attr; unsigned int ctxts, rq_depth; struct ib_device *dev; - struct sockaddr *sap; int ret = 0; + RPC_IFDEBUG(struct sockaddr *sap); listen_rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); clear_bit(XPT_CONN, &xprt->xpt_flags); @@ -525,6 +525,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) if (ret) goto errout; +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) dprintk("svcrdma: new connection %p accepted:\n", newxprt); sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.src_addr; dprintk(" local address : %pIS:%u\n", sap, rpc_get_port(sap)); @@ -535,6 +536,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) dprintk(" rdma_rw_ctxs : %d\n", ctxts); dprintk(" max_requests : %d\n", newxprt->sc_max_requests); dprintk(" ord : %d\n", conn_param.initiator_depth); +#endif trace_svcrdma_xprt_accept(&newxprt->sc_xprt); return &newxprt->sc_xprt; From 8820bcaa5bd73db2e28caae98f080a04cb6e2abb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 6 Feb 2019 12:00:57 -0500 Subject: [PATCH 1282/4277] svcrdma: Remove syslog warnings in work completion handlers These can result in a lot of log noise, and are able to be triggered by client misbehavior. Since there are trace points in these handlers now, there's no need to spam the log. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 9 +-------- net/sunrpc/xprtrdma/svc_rdma_rw.c | 11 +---------- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 4 ---- net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 ----- 4 files changed, 2 insertions(+), 27 deletions(-) diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 3ebb158c2279..65e2fb9aac65 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -272,11 +272,8 @@ bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma) return false; ctxt->rc_temp = true; ret = __svc_rdma_post_recv(rdma, ctxt); - if (ret) { - pr_err("svcrdma: failure posting recv buffers: %d\n", - ret); + if (ret) return false; - } } return true; } @@ -322,10 +319,6 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) goto out; flushed: - if (wc->status != IB_WC_WR_FLUSH_ERR) - pr_err("svcrdma: Recv: %s (%u/0x%x)\n", - ib_wc_status_msg(wc->status), - wc->status, wc->vendor_err); post_err: svc_rdma_recv_ctxt_put(rdma, ctxt); set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 65ee6fd60162..2121c9b4d275 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -212,13 +212,8 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); wake_up(&rdma->sc_send_wait); - if (unlikely(wc->status != IB_WC_SUCCESS)) { + if (unlikely(wc->status != IB_WC_SUCCESS)) set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); - if (wc->status != IB_WC_WR_FLUSH_ERR) - pr_err("svcrdma: write ctx: %s (%u/0x%x)\n", - ib_wc_status_msg(wc->status), - wc->status, wc->vendor_err); - } svc_rdma_write_info_free(info); } @@ -277,10 +272,6 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) if (unlikely(wc->status != IB_WC_SUCCESS)) { set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); - if (wc->status != IB_WC_WR_FLUSH_ERR) - pr_err("svcrdma: read ctx: %s (%u/0x%x)\n", - ib_wc_status_msg(wc->status), - wc->status, wc->vendor_err); svc_rdma_recv_ctxt_put(rdma, info->ri_readctxt); } else { spin_lock(&rdma->sc_rq_dto_lock); diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 1f200119268c..6fdba72f89f4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -272,10 +272,6 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc) if (unlikely(wc->status != IB_WC_SUCCESS)) { set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); svc_xprt_enqueue(&rdma->sc_xprt); - if (wc->status != IB_WC_WR_FLUSH_ERR) - pr_err("svcrdma: Send: %s (%u/0x%x)\n", - ib_wc_status_msg(wc->status), - wc->status, wc->vendor_err); } svc_xprt_put(&rdma->sc_xprt); diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index ef6afcf5e9c4..027a3b07d329 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -590,11 +590,6 @@ static void __svc_rdma_free(struct work_struct *work) if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) ib_drain_qp(rdma->sc_qp); - /* We should only be called from kref_put */ - if (kref_read(&xprt->xpt_ref) != 0) - pr_err("svcrdma: sc_xprt still in use? (%d)\n", - kref_read(&xprt->xpt_ref)); - svc_rdma_flush_recv_queues(rdma); /* Final put of backchannel client transport */ From 560f28bcceb25c6c94b4c54afaf7b886d408e05a Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Mon, 4 Feb 2019 14:16:57 +0530 Subject: [PATCH 1283/4277] static_keys.txt: Fix trivial spelling mistake Fix the spelling of 'functionnality' -> 'functionality'. Signed-off-by: Kamalesh Babulal Signed-off-by: Jonathan Corbet --- Documentation/static-keys.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/static-keys.txt b/Documentation/static-keys.txt index d68135560895..9803e14639bf 100644 --- a/Documentation/static-keys.txt +++ b/Documentation/static-keys.txt @@ -159,7 +159,7 @@ particularly the CPU hotplug lock (in order to avoid races against CPUs being brought in the kernel while the kernel is getting patched). Calling the static key API from within a hotplug notifier is thus a sure deadlock recipe. In order to still allow use of the -functionnality, the following functions are provided: +functionality, the following functions are provided: static_key_enable_cpuslocked() static_key_disable_cpuslocked() From 31dcbbefd385ae59b1384cbcacb31bc2e8dcc94b Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Sun, 3 Feb 2019 21:00:19 +0100 Subject: [PATCH 1284/4277] doc: kernel-parameters.txt: fix documentation of elevator parameter Legacy IO schedulers (cfq, deadline and noop) were removed in f382fb0bcef4. The documentation for deadline was retained because it carries over to mq-deadline as well, but location of the doc file was changed over time. The old iosched algorithms were removed from elevator= kernel parameter and mq-deadline, kyber and bfq were added with a reference to their documentation. Fixes: f382fb0bcef4 ("block: remove legacy IO schedulers") Signed-off-by: Otto Sabart Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/kernel-parameters.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b799bcf67d7b..2238fb3c7dcf 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1182,9 +1182,10 @@ arch/x86/kernel/cpu/cpufreq/elanfreq.c. elevator= [IOSCHED] - Format: {"cfq" | "deadline" | "noop"} - See Documentation/block/cfq-iosched.txt and - Documentation/block/deadline-iosched.txt for details. + Format: { "mq-deadline" | "kyber" | "bfq" } + See Documentation/block/deadline-iosched.txt, + Documentation/block/kyber-iosched.txt and + Documentation/block/bfq-iosched.txt for details. elfcorehdr=[size[KMG]@]offset[KMG] [IA64,PPC,SH,X86,S390] Specifies physical address of start of kernel core From 5eadc169fc80d483db88c9997d159ecf0e5fe21a Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Wed, 23 Jan 2019 21:22:55 +0100 Subject: [PATCH 1285/4277] doc:it_IT: update coding-style - expectations around bool This patch translates in Italian the content of the following patch 7967656ffbfa coding-style: Clarify the expectations around bool Signed-off-by: Federico Vaga Signed-off-by: Jonathan Corbet --- .../it_IT/process/coding-style.rst | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/Documentation/translations/it_IT/process/coding-style.rst b/Documentation/translations/it_IT/process/coding-style.rst index dfdefb951b89..2fd0e7f79d55 100644 --- a/Documentation/translations/it_IT/process/coding-style.rst +++ b/Documentation/translations/it_IT/process/coding-style.rst @@ -949,7 +949,40 @@ qualche valore fuori dai limiti. Un tipico esempio è quello delle funzioni che ritornano un puntatore; queste utilizzano NULL o ERR_PTR come meccanismo di notifica degli errori. -17) Non reinventate le macro del kernel +17) L'uso di bool +----------------- + +Nel kernel Linux il tipo bool deriva dal tipo _Bool dello standard C99. +Un valore bool può assumere solo i valori 0 o 1, e implicitamente o +esplicitamente la conversione a bool converte i valori in vero (*true*) o +falso (*false*). Quando si usa un tipo bool il costrutto !! non sarà più +necessario, e questo va ad eliminare una certa serie di bachi. + +Quando si usano i valori booleani, dovreste utilizzare le definizioni di true +e false al posto dei valori 1 e 0. + +Per il valore di ritorno delle funzioni e per le variabili sullo stack, l'uso +del tipo bool è sempre appropriato. L'uso di bool viene incoraggiato per +migliorare la leggibilità e spesso è molto meglio di 'int' nella gestione di +valori booleani. + +Non usate bool se per voi sono importanti l'ordine delle righe di cache o +la loro dimensione; la dimensione e l'allineamento cambia a seconda +dell'architettura per la quale è stato compilato. Le strutture che sono state +ottimizzate per l'allineamento o la dimensione non dovrebbero usare bool. + +Se una struttura ha molti valori true/false, considerate l'idea di raggrupparli +in un intero usando campi da 1 bit, oppure usate un tipo dalla larghezza fissa, +come u8. + +Come per gli argomenti delle funzioni, molti valori true/false possono essere +raggruppati in un singolo argomento a bit denominato 'flags'; spesso 'flags' è +un'alternativa molto più leggibile se si hanno valori costanti per true/false. + +Detto ciò, un uso parsimonioso di bool nelle strutture dati e negli argomenti +può migliorare la leggibilità. + +18) Non reinventate le macro del kernel --------------------------------------- Il file di intestazione include/linux/kernel.h contiene un certo numero @@ -973,7 +1006,7 @@ rigido sui tipi. Sentitevi liberi di leggere attentamente questo file d'intestazione per scoprire cos'altro è stato definito che non dovreste reinventare nel vostro codice. -18) Linee di configurazione degli editor e altre schifezze +19) Linee di configurazione degli editor e altre schifezze ----------------------------------------------------------- Alcuni editor possono interpretare dei parametri di configurazione integrati @@ -1007,8 +1040,8 @@ d'indentazione e di modalità d'uso. Le persone potrebbero aver configurato una modalità su misura, oppure potrebbero avere qualche altra magia per far funzionare bene l'indentazione. -19) Inline assembly ---------------------- +20) Inline assembly +------------------- Nel codice specifico per un'architettura, potreste aver bisogno di codice *inline assembly* per interfacciarvi col processore o con una funzionalità @@ -1040,7 +1073,7 @@ al fine di allineare correttamente l'assembler che verrà generato: "more_magic %reg2, %reg3" : /* outputs */ : /* inputs */ : /* clobbers */); -20) Compilazione sotto condizione +21) Compilazione sotto condizione --------------------------------- Ovunque sia possibile, non usate le direttive condizionali del preprocessore From 0c5e194947fc5ccc82bdd656d0efcd2d13b6e743 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Mon, 4 Feb 2019 22:30:47 +0100 Subject: [PATCH 1286/4277] doc:it_IT: add translations in process/ This patch adds the Italian translation for the following documents in Documentation/process: - applying-patches - submit-checklist - submitting-drivers - changes - stable api nonsense Signed-off-by: Federico Vaga Signed-off-by: Jonathan Corbet --- .../translations/it_IT/doc-guide/sphinx.rst | 2 + .../it_IT/process/applying-patches.rst | 12 +- .../translations/it_IT/process/changes.rst | 487 +++++++++++++++++- .../it_IT/process/stable-api-nonsense.rst | 202 +++++++- .../it_IT/process/submit-checklist.rst | 127 ++++- .../it_IT/process/submitting-drivers.rst | 8 +- 6 files changed, 822 insertions(+), 16 deletions(-) diff --git a/Documentation/translations/it_IT/doc-guide/sphinx.rst b/Documentation/translations/it_IT/doc-guide/sphinx.rst index 474b7e127893..793b5cc33403 100644 --- a/Documentation/translations/it_IT/doc-guide/sphinx.rst +++ b/Documentation/translations/it_IT/doc-guide/sphinx.rst @@ -3,6 +3,8 @@ .. note:: Per leggere la documentazione originale in inglese: :ref:`Documentation/doc-guide/index.rst ` +.. _it_sphinxdoc: + Introduzione ============ diff --git a/Documentation/translations/it_IT/process/applying-patches.rst b/Documentation/translations/it_IT/process/applying-patches.rst index f5e9c7d0b16d..1d30e5cd2a3d 100644 --- a/Documentation/translations/it_IT/process/applying-patches.rst +++ b/Documentation/translations/it_IT/process/applying-patches.rst @@ -1,13 +1,15 @@ .. include:: ../disclaimer-ita.rst :Original: :ref:`Documentation/process/applying-patches.rst ` - +:Translator: Federico Vaga .. _it_applying_patches: -Applicare modifiche al kernel Linux -=================================== +Applicare patch al kernel Linux ++++++++++++++++++++++++++++++++ -.. warning:: +.. note:: - TODO ancora da tradurre + Questo documento è obsoleto. Nella maggior parte dei casi, piuttosto + che usare ``patch`` manualmente, vorrete usare Git. Per questo motivo + il documento non verrà tradotto. diff --git a/Documentation/translations/it_IT/process/changes.rst b/Documentation/translations/it_IT/process/changes.rst index 956cf95a1214..d0874327f301 100644 --- a/Documentation/translations/it_IT/process/changes.rst +++ b/Documentation/translations/it_IT/process/changes.rst @@ -1,12 +1,495 @@ .. include:: ../disclaimer-ita.rst :Original: :ref:`Documentation/process/changes.rst ` +:Translator: Federico Vaga .. _it_changes: Requisiti minimi per compilare il kernel ++++++++++++++++++++++++++++++++++++++++ -.. warning:: +Introduzione +============ - TODO ancora da tradurre +Questo documento fornisce una lista dei software necessari per eseguire i +kernel 4.x. + +Questo documento è basato sul file "Changes" del kernel 2.0.x e quindi le +persone che lo scrissero meritano credito (Jared Mauch, Axel Boldt, +Alessandro Sigala, e tanti altri nella rete). + +Requisiti minimi correnti +************************* + +Prima di pensare d'avere trovato un baco, aggiornate i seguenti programmi +**almeno** alla versione indicata! Se non siete certi della versione che state +usando, il comando indicato dovrebbe dirvelo. + +Questa lista presume che abbiate già un kernel Linux funzionante. In aggiunta, +non tutti gli strumenti sono necessari ovunque; ovviamente, se non avete un +modem ISDN, per esempio, probabilmente non dovreste preoccuparvi di +isdn4k-utils. + +====================== ================= ======================================== + Programma Versione minima Comando per verificare la versione +====================== ================= ======================================== +GNU C 4.6 gcc --version +GNU make 3.81 make --version +binutils 2.20 ld -v +flex 2.5.35 flex --version +bison 2.0 bison --version +util-linux 2.10o fdformat --version +kmod 13 depmod -V +e2fsprogs 1.41.4 e2fsck -V +jfsutils 1.1.3 fsck.jfs -V +reiserfsprogs 3.6.3 reiserfsck -V +xfsprogs 2.6.0 xfs_db -V +squashfs-tools 4.0 mksquashfs -version +btrfs-progs 0.18 btrfsck +pcmciautils 004 pccardctl -V +quota-tools 3.09 quota -V +PPP 2.4.0 pppd --version +isdn4k-utils 3.1pre1 isdnctrl 2>&1|grep version +nfs-utils 1.0.5 showmount --version +procps 3.2.0 ps --version +oprofile 0.9 oprofiled --version +udev 081 udevd --version +grub 0.93 grub --version || grub-install --version +mcelog 0.6 mcelog --version +iptables 1.4.2 iptables -V +openssl & libcrypto 1.0.0 openssl version +bc 1.06.95 bc --version +Sphinx\ [#f1]_ 1.3 sphinx-build --version +====================== ================= ======================================== + +.. [#f1] Sphinx è necessario solo per produrre la documentazione del Kernel + +Compilazione del kernel +*********************** + +GCC +--- + +La versione necessaria di gcc potrebbe variare a seconda del tipo di CPU nel +vostro calcolatore. + +Make +---- + +Per compilare il kernel vi servirà GNU make 3.81 o successivo. + +Binutils +-------- + +Il sistema di compilazione, dalla versione 4.13, per la produzione dei passi +intermedi, si è convertito all'uso di *thin archive* (`ar T`) piuttosto che +all'uso del *linking* incrementale (`ld -r`). Questo richiede binutils 2.20 o +successivo. + +pkg-config +---------- + +Il sistema di compilazione, dalla versione 4.18, richiede pkg-config per +verificare l'esistenza degli strumenti kconfig e per determinare le +impostazioni da usare in 'make {g,x}config'. Precedentemente pkg-config +veniva usato ma non verificato o documentato. + +Flex +---- + +Dalla versione 4.16, il sistema di compilazione, durante l'esecuzione, genera +un analizzatore lessicale. Questo richiede flex 2.5.35 o successivo. + +Bison +----- + +Dalla versione 4.16, il sistema di compilazione, durante l'esecuzione, genera +un parsificatore. Questo richiede bison 2.0 o successivo. + +Perl +---- + +Per compilare il kernel vi servirà perl 5 e i seguenti moduli ``Getopt::Long``, +``Getopt::Std``, ``File::Basename``, e ``File::Find``. + +BC +-- + +Vi servirà bc per compilare i kernel dal 3.10 in poi. + +OpenSSL +------- + +Il programma OpenSSL e la libreria crypto vengono usati per la firma dei moduli +e la gestione dei certificati; sono usati per la creazione della chiave e +la generazione della firma. + +Se la firma dei moduli è abilitata, allora vi servirà openssl per compilare il +kernel 3.7 e successivi. Vi serviranno anche i pacchetti di sviluppo di +openssl per compilare il kernel 4.3 o successivi. + + +Strumenti di sistema +******************** + +Modifiche architetturali +------------------------ + +DevFS è stato reso obsoleto da udev +(http://www.kernel.org/pub/linux/utils/kernel/hotplug/) + +Il supporto per UID a 32-bit è ora disponibile. Divertitevi! + +La documentazione delle funzioni in Linux è una fase di transizione +verso una documentazione integrata nei sorgenti stessi usando dei commenti +formattati in modo speciale e posizionati vicino alle funzioni che descrivono. +Al fine di arricchire la documentazione, questi commenti possono essere +combinati con i file ReST presenti in Documentation/; questi potranno +poi essere convertiti in formato PostScript, HTML, LaTex, ePUB o PDF. +Per convertire i documenti da ReST al formato che volete, avete bisogno di +Sphinx. + +Util-linux +---------- + +Le versioni più recenti di util-linux: forniscono il supporto a ``fdisk`` per +dischi di grandi dimensioni; supportano le nuove opzioni di mount; riconoscono +più tipi di partizioni; hanno un fdformat che funziona con i kernel 2.4; +e altre chicche. Probabilmente vorrete aggiornarlo. + +Ksymoops +-------- + +Se l'impensabile succede e il kernel va in oops, potrebbe servirvi lo strumento +ksymoops per decodificarlo, ma nella maggior parte dei casi non vi servirà. +Generalmente è preferibile compilare il kernel con l'opzione ``CONFIG_KALLSYMS`` +cosicché venga prodotto un output più leggibile che può essere usato così com'è +(produce anche un output migliore di ksymoops). Se per qualche motivo il +vostro kernel non è stato compilato con ``CONFIG_KALLSYMS`` e non avete modo di +ricompilarlo e riprodurre l'oops con quell'opzione abilitata, allora potete +usare ksymoops per decodificare l'oops. + +Mkinitrd +-------- + +I cambiamenti della struttura in ``/lib/modules`` necessita l'aggiornamento di +mkinitrd. + +E2fsprogs +--------- + +L'ultima versione di ``e2fsprogs`` corregge diversi bachi in fsck e debugfs. +Ovviamente, aggiornarlo è una buona idea. + +JFSutils +-------- + +Il pacchetto ``jfsutils`` contiene programmi per il file-system JFS. +Sono disponibili i seguenti strumenti: + +- ``fsck.jfs`` - avvia la ripetizione del log delle transizioni, e verifica e + ripara una partizione formattata secondo JFS + +- ``mkfs.jfs`` - crea una partizione formattata secondo JFS + +- sono disponibili altri strumenti per il file-system. + +Reiserfsprogs +------------- + +Il pacchetto reiserfsprogs dovrebbe essere usato con reiserfs-3.6.x (Linux +kernel 2.4.x). Questo è un pacchetto combinato che contiene versioni +funzionanti di ``mkreiserfs``, ``resize_reiserfs``, ``debugreiserfs`` e +``reiserfsck``. Questi programmi funzionano sulle piattaforme i386 e alpha. + +Xfsprogs +-------- + +L'ultima versione di ``xfsprogs`` contiene, fra i tanti, i programmi +``mkfs.xfs``, ``xfs_db`` e ``xfs_repair`` per il file-system XFS. +Dipendono dell'architettura e qualsiasi versione dalla 2.0.0 in poi +dovrebbe funzionare correttamente con la versione corrente del codice +XFS nel kernel (sono raccomandate le versioni 2.6.0 o successive per via +di importanti miglioramenti). + +PCMCIAutils +----------- + +PCMCIAutils sostituisce ``pcmica-cs``. Serve ad impostare correttamente i +connettori PCMCIA all'avvio del sistema e a caricare i moduli necessari per +i dispositivi a 16-bit se il kernel è stato modularizzato e il sottosistema +hotplug è in uso. + +Quota-tools +----------- + +Il supporto per uid e gid a 32 bit richiedono l'uso della versione 2 del +formato quota. La versione 3.07 e successive di quota-tools supportano +questo formato. Usate la versione raccomandata nella lista qui sopra o una +successiva. + +Micro codice per Intel IA32 +--------------------------- + +Per poter aggiornare il micro codice per Intel IA32, è stato aggiunto un +apposito driver; il driver è accessibile come un normale dispositivo a +caratteri (misc). Se non state usando udev probabilmente sarà necessario +eseguire i seguenti comandi come root prima di poterlo aggiornare:: + + mkdir /dev/cpu + mknod /dev/cpu/microcode c 10 184 + chmod 0644 /dev/cpu/microcode + +Probabilmente, vorrete anche il programma microcode_ctl da usare con questo +dispositivo. + +udev +---- + +``udev`` è un programma in spazio utente il cui scopo è quello di popolare +dinamicamente la cartella ``/dev`` coi dispositivi effettivamente presenti. +``udev`` sostituisce le funzionalità base di devfs, consentendo comunque +nomi persistenti per i dispositivi. + +FUSE +---- + +Serve libfuse 2.4.0 o successiva. Il requisito minimo assoluto è 2.3.0 ma +le opzioni di mount ``direct_io`` e ``kernel_cache`` non funzioneranno. + + +Rete +**** + +Cambiamenti generali +-------------------- + +Se per quanto riguarda la configurazione di rete avete esigenze di un certo +livello dovreste prendere in considerazione l'uso degli strumenti in ip-route2. + +Filtro dei pacchetti / NAT +-------------------------- + +Il codice per filtraggio dei pacchetti e il NAT fanno uso degli stessi +strumenti come nelle versioni del kernel antecedenti la 2.4.x (iptables). +Include ancora moduli di compatibilità per 2.2.x ipchains e 2.0.x ipdwadm. + +PPP +--- + +Il driver per PPP è stato ristrutturato per supportare collegamenti multipli e +per funzionare su diversi livelli. Se usate PPP, aggiornate pppd almeno alla +versione 2.4.0. + +Se non usate udev, dovete avere un file /dev/ppp che può essere creato da root +col seguente comando:: + + mknod /dev/ppp c 108 0 + +Isdn4k-utils +------------ + +Per via della modifica del campo per il numero di telefono, il pacchetto +isdn4k-utils dev'essere ricompilato o (preferibilmente) aggiornato. + +NFS-utils +--------- + +Nei kernel più antichi (2.4 e precedenti), il server NFS doveva essere +informato sui clienti ai quali si voleva fornire accesso via NFS. Questa +informazione veniva passata al kernel quando un cliente montava un file-system +mediante ``mountd``, oppure usando ``exportfs`` all'avvio del sistema. +exportfs prende le informazioni circa i clienti attivi da ``/var/lib/nfs/rmtab``. + +Questo approccio è piuttosto delicato perché dipende dalla correttezza di +rmtab, che non è facile da garantire, in particolare quando si cerca di +implementare un *failover*. Anche quando il sistema funziona bene, ``rmtab`` +ha il problema di accumulare vecchie voci inutilizzate. + +Sui kernel più recenti il kernel ha la possibilità di informare mountd quando +arriva una richiesta da una macchina sconosciuta, e mountd può dare al kernel +le informazioni corrette per l'esportazione. Questo rimuove la dipendenza con +``rmtab`` e significa che il kernel deve essere al corrente solo dei clienti +attivi. + +Per attivare questa funzionalità, dovete eseguire il seguente comando prima di +usare exportfs o mountd:: + + mount -t nfsd nfsd /proc/fs/nfsd + +Dove possibile, raccomandiamo di proteggere tutti i servizi NFS dall'accesso +via internet mediante un firewall. + +mcelog +------ + +Quando ``CONFIG_x86_MCE`` è attivo, il programma mcelog processa e registra +gli eventi *machine check*. Gli eventi *machine check* sono errori riportati +dalla CPU. Incoraggiamo l'analisi di questi errori. + + +Documentazione del kernel +************************* + +Sphinx +------ + +Per i dettaglio sui requisiti di Sphinx, fate riferimento a :ref:`it_sphinx_install` +in :ref:`Documentation/translations/it_IT/doc-guide/sphinx.rst ` + +Ottenere software aggiornato +============================ + +Compilazione del kernel +*********************** + +gcc +--- + +- + +Make +---- + +- + +Binutils +-------- + +- + +Flex +---- + +- + +Bison +----- + +- + +OpenSSL +------- + +- + +Strumenti di sistema +******************** + +Util-linux +---------- + +- + +Kmod +---- + +- +- + +Ksymoops +-------- + +- + +Mkinitrd +-------- + +- + +E2fsprogs +--------- + +- + +JFSutils +-------- + +- + +Reiserfsprogs +------------- + +- + +Xfsprogs +-------- + +- + +Pcmciautils +----------- + +- + +Quota-tools +----------- + +- + + +Microcodice Intel P6 +-------------------- + +- + +udev +---- + +- + +FUSE +---- + +- + +mcelog +------ + +- + +Rete +**** + +PPP +--- + +- + +Isdn4k-utils +------------ + +- + +NFS-utils +--------- + +- + +Iptables +-------- + +- + +Ip-route2 +--------- + +- + +OProfile +-------- + +- + +NFS-Utils +--------- + +- + +Documentazione del kernel +************************* + +Sphinx +------ + +- diff --git a/Documentation/translations/it_IT/process/stable-api-nonsense.rst b/Documentation/translations/it_IT/process/stable-api-nonsense.rst index d4fa4abf8dd3..cdfc509b8566 100644 --- a/Documentation/translations/it_IT/process/stable-api-nonsense.rst +++ b/Documentation/translations/it_IT/process/stable-api-nonsense.rst @@ -1,13 +1,209 @@ .. include:: ../disclaimer-ita.rst :Original: :ref:`Documentation/process/stable-api-nonsense.rst ` - +:Translator: Federico Vaga .. _it_stable_api_nonsense: L'interfaccia dei driver per il kernel Linux ============================================ -.. warning:: +(tutte le risposte alle vostre domande e altro) - TODO ancora da tradurre +Greg Kroah-Hartman + +Questo è stato scritto per cercare di spiegare perché Linux **non ha +un'interfaccia binaria, e non ha nemmeno un'interfaccia stabile**. + +.. note:: + + Questo articolo parla di interfacce **interne al kernel**, non delle + interfacce verso lo spazio utente. + + L'interfaccia del kernel verso lo spazio utente è quella usata dai + programmi, ovvero le chiamate di sistema. Queste interfacce sono **molto** + stabili nel tempo e non verranno modificate. Ho vecchi programmi che sono + stati compilati su un kernel 0.9 (circa) e tuttora funzionano sulle versioni + 2.6 del kernel. Queste interfacce sono quelle che gli utenti e i + programmatori possono considerare stabili. + +Riepilogo generale +------------------ + +Pensate di volere un'interfaccia del kernel stabile, ma in realtà non la +volete, e nemmeno sapete di non volerla. Quello che volete è un driver +stabile che funzioni, e questo può essere ottenuto solo se il driver si trova +nei sorgenti del kernel. Ci sono altri vantaggi nell'avere il proprio driver +nei sorgenti del kernel, ognuno dei quali hanno reso Linux un sistema operativo +robusto, stabile e maturo; questi sono anche i motivi per cui avete scelto +Linux. + +Introduzione +------------ + +Solo le persone un po' strambe vorrebbero scrivere driver per il kernel con +la costante preoccupazione per i cambiamenti alle interfacce interne. Per il +resto del mondo, queste interfacce sono invisibili o non di particolare +interesse. + +Innanzitutto, non tratterò **alcun** problema legale riguardante codice +chiuso, nascosto, avvolto, blocchi binari, o qualsia altra cosa che descrive +driver che non hanno i propri sorgenti rilasciati con licenza GPL. Per favore +fate riferimento ad un avvocato per qualsiasi questione legale, io sono un +programmatore e perciò qui vi parlerò soltanto delle questioni tecniche (non +per essere superficiali sui problemi legali, sono veri e dovete esserne a +conoscenza in ogni circostanza). + +Dunque, ci sono due tematiche principali: interfacce binarie del kernel e +interfacce stabili nei sorgenti. Ognuna dipende dall'altra, ma discuteremo +prima delle cose binarie per toglierle di mezzo. + +Interfaccia binaria del kernel +------------------------------ + +Supponiamo d'avere un'interfaccia stabile nei sorgenti del kernel, di +conseguenza un'interfaccia binaria dovrebbe essere anche'essa stabile, giusto? +Sbagliato. Prendete in considerazione i seguenti fatti che riguardano il +kernel Linux: + + - A seconda della versione del compilatore C che state utilizzando, diverse + strutture dati del kernel avranno un allineamento diverso, e possibilmente + un modo diverso di includere le funzioni (renderle inline oppure no). + L'organizzazione delle singole funzioni non è poi così importante, ma la + spaziatura (*padding*) nelle strutture dati, invece, lo è. + + - In base alle opzioni che sono state selezionate per generare il kernel, + un certo numero di cose potrebbero succedere: + + - strutture dati differenti potrebbero contenere campi differenti + - alcune funzioni potrebbero non essere implementate (per esempio, + alcuni *lock* spariscono se compilati su sistemi mono-processore) + - la memoria interna del kernel può essere allineata in differenti modi + a seconda delle opzioni di compilazione. + + - Linux funziona su una vasta gamma di architetture di processore. Non esiste + alcuna possibilità che il binario di un driver per un'architettura funzioni + correttamente su un'altra. + +Alcuni di questi problemi possono essere risolti compilando il proprio modulo +con la stessa identica configurazione del kernel, ed usando la stessa versione +del compilatore usato per compilare il kernel. Questo è sufficiente se volete +fornire un modulo per uno specifico rilascio su una specifica distribuzione +Linux. Ma moltiplicate questa singola compilazione per il numero di +distribuzioni Linux e il numero dei rilasci supportati da quest'ultime e vi +troverete rapidamente in un incubo fatto di configurazioni e piattaforme +hardware (differenti processori con differenti opzioni); dunque, anche per il +singolo rilascio di un modulo, dovreste creare differenti versioni dello +stesso. + +Fidatevi, se tenterete questa via, col tempo, diventerete pazzi; l'ho imparato +a mie spese molto tempo fa... + + +Interfaccia stabile nei sorgenti del kernel +------------------------------------------- + +Se parlate con le persone che cercano di mantenere aggiornato un driver per +Linux ma che non si trova nei sorgenti, allora per queste persone l'argomento +sarà "ostico". + +Lo sviluppo del kernel Linux è continuo e viaggia ad un ritmo sostenuto, e non +rallenta mai. Perciò, gli sviluppatori del kernel trovano bachi nelle +interfacce attuali, o trovano modi migliori per fare le cose. Se le trovano, +allora le correggeranno per migliorarle. In questo frangente, i nomi delle +funzioni potrebbero cambiare, le strutture dati potrebbero diventare più grandi +o più piccole, e gli argomenti delle funzioni potrebbero essere ripensati. +Se questo dovesse succedere, nello stesso momento, tutte le istanze dove questa +interfaccia viene utilizzata verranno corrette, garantendo che tutto continui +a funzionare senza problemi. + +Portiamo ad esempio l'interfaccia interna per il sottosistema USB che ha subito +tre ristrutturazioni nel corso della sua vita. Queste ristrutturazioni furono +fatte per risolvere diversi problemi: + + - È stato fatto un cambiamento da un flusso di dati sincrono ad uno + asincrono. Questo ha ridotto la complessità di molti driver e ha + aumentato la capacità di trasmissione di tutti i driver fino a raggiungere + quasi la velocità massima possibile. + - È stato fatto un cambiamento nell'allocazione dei pacchetti da parte del + sottosistema USB per conto dei driver, cosicché ora i driver devono fornire + più informazioni al sottosistema USB al fine di correggere un certo numero + di stalli. + +Questo è completamente l'opposto di quello che succede in alcuni sistemi +operativi proprietari che hanno dovuto mantenere, nel tempo, il supporto alle +vecchie interfacce USB. I nuovi sviluppatori potrebbero usare accidentalmente +le vecchie interfacce e sviluppare codice nel modo sbagliato, portando, di +conseguenza, all'instabilità del sistema. + +In entrambe gli scenari, gli sviluppatori hanno ritenuto che queste importanti +modifiche erano necessarie, e quindi le hanno fatte con qualche sofferenza. +Se Linux avesse assicurato di mantenere stabile l'interfaccia interna, si +sarebbe dovuto procedere alla creazione di una nuova, e quelle vecchie, e +mal funzionanti, avrebbero dovuto ricevere manutenzione, creando lavoro +aggiuntivo per gli sviluppatori del sottosistema USB. Dato che gli +sviluppatori devono dedicare il proprio tempo a questo genere di lavoro, +chiedergli di dedicarne dell'altro, senza benefici, magari gratuitamente, non +è contemplabile. + +Le problematiche relative alla sicurezza sono molto importanti per Linux. +Quando viene trovato un problema di sicurezza viene corretto in breve tempo. +A volte, per prevenire il problema di sicurezza, si sono dovute cambiare +delle interfacce interne al kernel. Quando è successo, allo stesso tempo, +tutti i driver che usavano quelle interfacce sono stati aggiornati, garantendo +la correzione definitiva del problema senza doversi preoccupare di rivederlo +per sbaglio in futuro. Se non si fossero cambiate le interfacce interne, +sarebbe stato impossibile correggere il problema e garantire che non si sarebbe +più ripetuto. + +Nel tempo le interfacce del kernel subiscono qualche ripulita. Se nessuno +sta più usando un'interfaccia, allora questa verrà rimossa. Questo permette +al kernel di rimanere il più piccolo possibile, e garantisce che tutte le +potenziali interfacce sono state verificate nel limite del possibile (le +interfacce inutilizzate sono impossibili da verificare). + + +Cosa fare +--------- + +Dunque, se avete un driver per il kernel Linux che non si trova nei sorgenti +principali del kernel, come sviluppatori, cosa dovreste fare? Rilasciare un +file binario del driver per ogni versione del kernel e per ogni distribuzione, +è un incubo; inoltre, tenere il passo con tutti i cambiamenti del kernel è un +brutto lavoro. + +Semplicemente, fate sì che il vostro driver per il kernel venga incluso nei +sorgenti principali (ricordatevi, stiamo parlando di driver rilasciati secondo +una licenza compatibile con la GPL; se il vostro codice non ricade in questa +categoria: buona fortuna, arrangiatevi, siete delle sanguisughe) + +Se il vostro driver è nei sorgenti del kernel e un'interfaccia cambia, il +driver verrà corretto immediatamente dalla persona che l'ha modificata. Questo +garantisce che sia sempre possibile compilare il driver, che funzioni, e tutto +con un minimo sforzo da parte vostra. + +Avere il proprio driver nei sorgenti principali del kernel ha i seguenti +vantaggi: + + - La qualità del driver aumenterà e i costi di manutenzione (per lo + sviluppatore originale) diminuiranno. + - Altri sviluppatori aggiungeranno nuove funzionalità al vostro driver. + - Altri persone troveranno e correggeranno bachi nel vostro driver. + - Altri persone troveranno degli aggiustamenti da fare al vostro driver. + - Altri persone aggiorneranno il driver quando è richiesto da un cambiamento + di un'interfaccia. + - Il driver sarà automaticamente reso disponibile in tutte le distribuzioni + Linux senza dover chiedere a nessuna di queste di aggiungerlo. + +Dato che Linux supporta più dispositivi di qualsiasi altro sistema operativo, +e che girano su molti più tipi di processori di qualsiasi altro sistema +operativo; ciò dimostra che questo modello di sviluppo qualcosa di giusto, +dopo tutto, lo fa :) + + + +------ + +Dei ringraziamenti vanno a Randy Dunlap, Andrew Morton, David Brownell, +Hanna Linder, Robert Love, e Nishanth Aravamudan per la loro revisione +e per i loro commenti sulle prime bozze di questo articolo. diff --git a/Documentation/translations/it_IT/process/submit-checklist.rst b/Documentation/translations/it_IT/process/submit-checklist.rst index b6b4dd94a660..70e65a7b3620 100644 --- a/Documentation/translations/it_IT/process/submit-checklist.rst +++ b/Documentation/translations/it_IT/process/submit-checklist.rst @@ -1,12 +1,131 @@ .. include:: ../disclaimer-ita.rst :Original: :ref:`Documentation/process/submit-checklist.rst ` +:Translator: Federico Vaga .. _it_submitchecklist: -Lista delle cose da fare per inviare una modifica al kernel Linux -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Lista delle verifiche da fare prima di inviare una patch per il kernel Linux +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. warning:: +Qui troverete una lista di cose che uno sviluppatore dovrebbe fare per +vedere le proprie patch accettate più rapidamente. - TODO ancora da tradurre +Tutti questi punti integrano la documentazione fornita riguardo alla +sottomissione delle patch, in particolare +:ref:`Documentation/translations/it_IT/process/submitting-patches.rst `. + +1) Se state usando delle funzionalità del kernel allora includete (#include) + i file che le dichiarano/definiscono. Non dipendente dal fatto che un file + d'intestazione include anche quelli usati da voi. + +2) Compilazione pulita: + + a) con le opzioni ``CONFIG`` negli stati ``=y``, ``=m`` e ``=n``. Nessun + avviso/errore di ``gcc`` e nessun avviso/errore dal linker. + + b) con ``allnoconfig``, ``allmodconfig`` + + c) quando si usa ``O=builddir`` + +3) Compilare per diverse architetture di processore usando strumenti per + la cross-compilazione o altri. + +4) Una buona architettura per la verifica della cross-compilazione è la ppc64 + perché tende ad usare ``unsigned long`` per le quantità a 64-bit. + +5) Controllate lo stile del codice della vostra patch secondo le direttive + scritte in :ref:`Documentation/translations/it_IT/process/coding-style.rst `. + Prima dell'invio della patch, usate il verificatore di stile + (``script/checkpatch.pl``) per scovare le violazioni più semplici. + Dovreste essere in grado di giustificare tutte le violazioni rimanenti nella + vostra patch. + +6) Le opzioni ``CONFIG``, nuove o modificate, non scombussolano il menu + di configurazione e sono preimpostate come disabilitate a meno che non + soddisfino i criteri descritti in ``Documentation/kbuild/kconfig-language.txt`` + alla punto "Voci di menu: valori predefiniti". + +7) Tutte le nuove opzioni ``Kconfig`` hanno un messaggio di aiuto. + +8) La patch è stata accuratamente revisionata rispetto alle più importanti + configurazioni ``Kconfig``. Questo è molto difficile da fare + correttamente - un buono lavoro di testa sarà utile. + +9) Verificare con sparse. + +10) Usare ``make checkstack`` e ``make namespacecheck`` e correggere tutti i + problemi rilevati. + + .. note:: + + ``checkstack`` non evidenzia esplicitamente i problemi, ma una funzione + che usa più di 512 byte sullo stack è una buona candidata per una + correzione. + +11) Includete commenti :ref:`kernel-doc ` per documentare API + globali del kernel. Usate ``make htmldocs`` o ``make pdfdocs`` per + verificare i commenti :ref:`kernel-doc ` ed eventualmente + correggerli. + +12) La patch è stata verificata con le seguenti opzioni abilitate + contemporaneamente: ``CONFIG_PREEMPT``, ``CONFIG_DEBUG_PREEMPT``, + ``CONFIG_DEBUG_SLAB``, ``CONFIG_DEBUG_PAGEALLOC``, ``CONFIG_DEBUG_MUTEXES``, + ``CONFIG_DEBUG_SPINLOCK``, ``CONFIG_DEBUG_ATOMIC_SLEEP``, + ``CONFIG_PROVE_RCU`` e ``CONFIG_DEBUG_OBJECTS_RCU_HEAD``. + +13) La patch è stata compilata e verificata in esecuzione con, e senza, + le opzioni ``CONFIG_SMP`` e ``CONFIG_PREEMPT``. + +14) Se la patch ha effetti sull'IO dei dischi, eccetera: allora dev'essere + verificata con, e senza, l'opzione ``CONFIG_LBDAF``. + +15) Tutti i percorsi del codice sono stati verificati con tutte le funzionalità + di lockdep abilitate. + +16) Tutti i nuovi elementi in ``/proc`` sono documentati in ``Documentation/``. + +17) Tutti i nuovi parametri d'avvio del kernel sono documentati in + ``Documentation/admin-guide/kernel-parameters.rst``. + +18) Tutti i nuovi parametri dei moduli sono documentati con ``MODULE_PARM_DESC()``. + +19) Tutte le nuove interfacce verso lo spazio utente sono documentate in + ``Documentation/ABI/``. Leggete ``Documentation/ABI/README`` per maggiori + informazioni. Le patch che modificano le interfacce utente dovrebbero + essere inviate in copia anche a linux-api@vger.kernel.org. + +20) Verifica che il kernel passi con successo ``make headers_check`` + +21) La patch è stata verificata con l'iniezione di fallimenti in slab e + nell'allocazione di pagine. Vedere ``Documentation/fault-injection/``. + + Se il nuovo codice è corposo, potrebbe essere opportuno aggiungere + l'iniezione di fallimenti specifici per il sottosistema. + +22) Il nuovo codice è stato compilato con ``gcc -W`` (usate + ``make EXTRA_CFLAGS=-W``). Questo genererà molti avvisi, ma è ottimo + per scovare bachi come "warning: comparison between signed and unsigned". + +23) La patch è stata verificata dopo essere stata inclusa nella serie di patch + -mm; questo al fine di assicurarsi che continui a funzionare assieme a + tutte le altre patch in coda e i vari cambiamenti nei sottosistemi VM, VFS + e altri. + +24) Tutte le barriere di sincronizzazione {per esempio, ``barrier()``, + ``rmb()``, ``wmb()``} devono essere accompagnate da un commento nei + sorgenti che ne spieghi la logica: cosa fanno e perché. + +25) Se la patch aggiunge nuove chiamate ioctl, allora aggiornate + ``Documentation/ioctl/ioctl-number.txt``. + +26) Se il codice che avete modificato dipende o usa una qualsiasi interfaccia o + funzionalità del kernel che è associata a uno dei seguenti simboli + ``Kconfig``, allora verificate che il kernel compili con diverse + configurazioni dove i simboli sono disabilitati e/o ``=m`` (se c'è la + possibilità) [non tutti contemporaneamente, solo diverse combinazioni + casuali]: + + ``CONFIG_SMP``, ``CONFIG_SYSFS``, ``CONFIG_PROC_FS``, ``CONFIG_INPUT``, + ``CONFIG_PCI``, ``CONFIG_BLOCK``, ``CONFIG_PM``, ``CONFIG_MAGIC_SYSRQ``, + ``CONFIG_NET``, ``CONFIG_INET=n`` (ma l'ultimo con ``CONFIG_NET=y``). diff --git a/Documentation/translations/it_IT/process/submitting-drivers.rst b/Documentation/translations/it_IT/process/submitting-drivers.rst index 16df950ef808..dadd77e47613 100644 --- a/Documentation/translations/it_IT/process/submitting-drivers.rst +++ b/Documentation/translations/it_IT/process/submitting-drivers.rst @@ -1,12 +1,16 @@ .. include:: ../disclaimer-ita.rst :Original: :ref:`Documentation/process/submitting-drivers.rst ` +:Translator: Federico Vaga .. _it_submittingdrivers: Sottomettere driver per il kernel Linux ======================================= -.. warning:: +.. note:: - TODO ancora da tradurre + Questo documento è vecchio e negli ultimi anni non è stato più aggiornato; + dovrebbe essere aggiornato, o forse meglio, rimosso. La maggior parte di + quello che viene detto qui può essere trovato anche negli altri documenti + dedicati allo sviluppo. Per questo motivo il documento non verrà tradotto. From de19055564c8f8f9d366f8db3395836da0b2176c Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Fri, 25 Jan 2019 12:07:00 -0600 Subject: [PATCH 1287/4277] Documentation: Document arm64 kpti control For a while Arm64 has been capable of force enabling or disabling the kpti mitigations. Lets make sure the documentation reflects that. Signed-off-by: Jeremy Linton Reviewed-by: Andre Przywara Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/kernel-parameters.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 2238fb3c7dcf..b251494d1b66 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1983,6 +1983,12 @@ Built with CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y, the default is off. + kpti= [ARM64] Control page table isolation of user + and kernel address spaces. + Default: enabled on cores which need mitigation. + 0: force disabled + 1: force enabled + kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs. Default is 0 (don't ignore, but inject #GP) From 41a852e002e65ab7a1e6841b485d72d022e95df2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 21:53:00 -0800 Subject: [PATCH 1288/4277] Input: ili210x - add DT binding document Add DT binding document for the Ilitek ILI210x and ILI251x touchscreen controllers. Signed-off-by: Marek Vasut Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/ilitek,ili2xxx.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt diff --git a/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt new file mode 100644 index 000000000000..b2a76301e632 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt @@ -0,0 +1,25 @@ +Ilitek ILI210x/ILI251x touchscreen controller + +Required properties: +- compatible: + ilitek,ili210x for ILI210x + ilitek,ili251x for ILI251x + +- reg: The I2C address of the device + +- interrupts: The sink for the touchscreen's IRQ output + See ../interrupt-controller/interrupts.txt + +Optional properties for main touchpad device: + +- reset-gpios: GPIO specifier for the touchscreen's reset pin (active low) + +Example: + + touchscreen@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; + interrupt-parent = <&gpio4>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>; + }; From 626feb863274da93e44d644a9fd4a59b46851794 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 21:53:48 -0800 Subject: [PATCH 1289/4277] Input: ili210x - drop platform data support There is not a single user of the ili210x platform data in the kernel, just drop it. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 12 ++---------- include/linux/input/ili210x.h | 11 ----------- 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 include/linux/input/ili210x.h diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 6f76eeedf465..25b0ca6c07d9 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -6,7 +6,6 @@ #include #include #include -#include #define MAX_TOUCHES 2 #define DEFAULT_POLL_PERIOD 20 @@ -184,7 +183,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - const struct ili210x_platform_data *pdata = dev_get_platdata(dev); struct ili210x *priv; struct input_dev *input; struct panel_info panel; @@ -194,11 +192,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); - if (!pdata) { - dev_err(dev, "No platform data!\n"); - return -EINVAL; - } - if (client->irq <= 0) { dev_err(dev, "No IRQ!\n"); return -EINVAL; @@ -233,8 +226,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->client = client; priv->input = input; - priv->get_pendown_state = pdata->get_pendown_state; - priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + priv->poll_period = DEFAULT_POLL_PERIOD; INIT_DELAYED_WORK(&priv->dwork, ili210x_work); /* Setup input device */ @@ -258,7 +250,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, priv); - error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, + error = request_irq(client->irq, ili210x_irq, 0, client->name, priv); if (error) { dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h deleted file mode 100644 index b76e7c1404cd..000000000000 --- a/include/linux/input/ili210x.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ILI210X_H -#define _ILI210X_H - -struct ili210x_platform_data { - unsigned long irq_flags; - unsigned int poll_period; - bool (*get_pendown_state)(void); -}; - -#endif From 2fa928390f3f04fbb29267b29ca09d980b17f9d1 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 21:54:37 -0800 Subject: [PATCH 1290/4277] Input: ili210x - drop get_pendown_state The .get_pendown_state callback is set only by the platform data code, which was just removed. Thus, get_pendown_state() always returns false, so drop that altogether. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 25b0ca6c07d9..11007bf8113c 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -43,7 +43,6 @@ struct firmware_version { struct ili210x { struct i2c_client *client; struct input_dev *input; - bool (*get_pendown_state)(void); unsigned int poll_period; struct delayed_work dwork; }; @@ -102,16 +101,6 @@ static void ili210x_report_events(struct input_dev *input, input_sync(input); } -static bool get_pendown_state(const struct ili210x *priv) -{ - bool state = false; - - if (priv->get_pendown_state) - state = priv->get_pendown_state(); - - return state; -} - static void ili210x_work(struct work_struct *work) { struct ili210x *priv = container_of(work, struct ili210x, @@ -130,7 +119,7 @@ static void ili210x_work(struct work_struct *work) ili210x_report_events(priv->input, &touchdata); - if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + if (touchdata.status & 0xf3) schedule_delayed_work(&priv->dwork, msecs_to_jiffies(priv->poll_period)); } From 63083fd582b9eb46a67fff6d4077a931d986d066 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 21:54:59 -0800 Subject: [PATCH 1291/4277] Input: ili210x - convert to devm_ functions Convert the driver to dev-managed allocations. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 11007bf8113c..788f4140e302 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -206,12 +206,13 @@ static int ili210x_i2c_probe(struct i2c_client *client, xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - input = input_allocate_device(); - if (!priv || !input) { - error = -ENOMEM; - goto err_free_mem; - } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; priv->client = client; priv->input = input; @@ -273,8 +274,6 @@ err_remove_sysfs: err_free_irq: free_irq(client->irq, priv); err_free_mem: - input_free_device(input); - kfree(priv); return error; } @@ -285,8 +284,6 @@ static int ili210x_i2c_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); free_irq(priv->client->irq, priv); cancel_delayed_work_sync(&priv->dwork); - input_unregister_device(priv->input); - kfree(priv); return 0; } From 201f3c803544c052aa1bab9e562e0ada4aefe03d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 21:55:26 -0800 Subject: [PATCH 1292/4277] Input: ili210x - add reset GPIO support The touchscreen can have a reset GPIO connected to it, add support for such an arrangement. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 788f4140e302..f8b20d302384 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -6,6 +6,7 @@ #include #include #include +#include #define MAX_TOUCHES 2 #define DEFAULT_POLL_PERIOD 20 @@ -45,6 +46,7 @@ struct ili210x { struct input_dev *input; unsigned int poll_period; struct delayed_work dwork; + struct gpio_desc *reset_gpio; }; static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, @@ -168,11 +170,19 @@ static const struct attribute_group ili210x_attr_group = { .attrs = ili210x_attributes, }; +static void ili210x_power_down(void *data) +{ + struct gpio_desc *reset_gpio = data; + + gpiod_set_value_cansleep(reset_gpio, 1); +} + static int ili210x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct ili210x *priv; + struct gpio_desc *reset_gpio; struct input_dev *input; struct panel_info panel; struct firmware_version firmware; @@ -186,6 +196,21 @@ static int ili210x_i2c_probe(struct i2c_client *client, return -EINVAL; } + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + + if (reset_gpio) { + error = devm_add_action_or_reset(dev, ili210x_power_down, + reset_gpio); + if (error) + return error; + + usleep_range(50, 100); + gpiod_set_value_cansleep(reset_gpio, 0); + msleep(100); + } + /* Get firmware version */ error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, &firmware, sizeof(firmware)); @@ -218,6 +243,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->input = input; priv->poll_period = DEFAULT_POLL_PERIOD; INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + priv->reset_gpio = reset_gpio; /* Setup input device */ input->name = "ILI210x Touchscreen"; From 1bdec5d9818c47e080c19784dfd25d1d9c20807e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 22:00:44 -0800 Subject: [PATCH 1293/4277] Input: ili210x - convert to devm IRQ Convert the driver to devm_request_irq(), drop the related unmanaged deregistration code and add ili210x_irq_teardown() to tear the IRQ down and cancel possible touchscreen pending work. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index f8b20d302384..bc674ece61f3 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -177,6 +177,13 @@ static void ili210x_power_down(void *data) gpiod_set_value_cansleep(reset_gpio, 1); } +static void ili210x_cancel_work(void *data) +{ + struct ili210x *priv = data; + + cancel_delayed_work_sync(&priv->dwork); +} + static int ili210x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -266,19 +273,23 @@ static int ili210x_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, priv); - error = request_irq(client->irq, ili210x_irq, 0, - client->name, priv); + error = devm_add_action(dev, ili210x_cancel_work, priv); + if (error) + return error; + + error = devm_request_irq(dev, client->irq, ili210x_irq, 0, + client->name, priv); if (error) { dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); - goto err_free_mem; + return error; } error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); if (error) { dev_err(dev, "Unable to create sysfs attributes, err: %d\n", error); - goto err_free_irq; + return error; } error = input_register_device(priv->input); @@ -297,19 +308,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, err_remove_sysfs: sysfs_remove_group(&dev->kobj, &ili210x_attr_group); -err_free_irq: - free_irq(client->irq, priv); -err_free_mem: return error; } static int ili210x_i2c_remove(struct i2c_client *client) { - struct ili210x *priv = i2c_get_clientdata(client); - sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); - free_irq(priv->client->irq, priv); - cancel_delayed_work_sync(&priv->dwork); return 0; } From e3559442afd2a3d9b09eedc236f19698c515472e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 22:01:07 -0800 Subject: [PATCH 1294/4277] Input: ili210x - rework the touchscreen sample processing Get rid of the packed structures for representing data as that does not apply to other similar Ilitek touchscreens. Instead, implement a function which parses the data and reports touch events and coordinates. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 55 ++++++++++++++++------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index bc674ece61f3..c345d6df783f 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -7,6 +7,7 @@ #include #include #include +#include #define MAX_TOUCHES 2 #define DEFAULT_POLL_PERIOD 20 @@ -17,20 +18,11 @@ #define REG_FIRMWARE_VERSION 0x40 #define REG_CALIBRATE 0xcc -struct finger { +struct panel_info { u8 x_low; u8 x_high; u8 y_low; u8 y_high; -} __packed; - -struct touchdata { - u8 status; - struct finger finger[MAX_TOUCHES]; -} __packed; - -struct panel_info { - struct finger finger_max; u8 xchannel_num; u8 ychannel_num; } __packed; @@ -75,25 +67,35 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, return 0; } -static void ili210x_report_events(struct input_dev *input, - const struct touchdata *touchdata) +static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) { + if (finger >= MAX_TOUCHES) + return false; + + if (touchdata[0] & BIT(finger)) + return false; + + *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); + *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); + + return true; +} + +static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) +{ + struct input_dev *input = priv->input; int i; bool touch; unsigned int x, y; - const struct finger *finger; for (i = 0; i < MAX_TOUCHES; i++) { input_mt_slot(input, i); - finger = &touchdata->finger[i]; - - touch = touchdata->status & (1 << i); + touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); if (touch) { - x = finger->x_low | (finger->x_high << 8); - y = finger->y_low | (finger->y_high << 8); - input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); } @@ -101,6 +103,8 @@ static void ili210x_report_events(struct input_dev *input, input_mt_report_pointer_emulation(input, false); input_sync(input); + + return touchdata[0] & 0xf3; } static void ili210x_work(struct work_struct *work) @@ -108,20 +112,21 @@ static void ili210x_work(struct work_struct *work) struct ili210x *priv = container_of(work, struct ili210x, dwork.work); struct i2c_client *client = priv->client; - struct touchdata touchdata; + u8 touchdata[1 + 4 * MAX_TOUCHES]; + bool touch; int error; error = ili210x_read_reg(client, REG_TOUCHDATA, - &touchdata, sizeof(touchdata)); + touchdata, sizeof(touchdata)); if (error) { dev_err(&client->dev, "Unable to get touchdata, err = %d\n", error); return; } - ili210x_report_events(priv->input, &touchdata); + touch = ili210x_report_events(priv, touchdata); - if (touchdata.status & 0xf3) + if (touch) schedule_delayed_work(&priv->dwork, msecs_to_jiffies(priv->poll_period)); } @@ -235,8 +240,8 @@ static int ili210x_i2c_probe(struct i2c_client *client, return error; } - xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); - ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + xmax = panel.x_low | (panel.x_high << 8); + ymax = panel.y_low | (panel.y_high << 8); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) From 122945776271efee91012c2a345b720d39b15b7c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 22:01:30 -0800 Subject: [PATCH 1295/4277] Input: ili210x - reorder probe Perform the register access only after the I2C client data are set, this is only done in preparation for the subsequent patch which uses the I2C client data in the register IO function. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index c345d6df783f..ba2634582364 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -223,6 +223,22 @@ static int ili210x_i2c_probe(struct i2c_client *client, msleep(100); } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + priv->client = client; + priv->input = input; + priv->poll_period = DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + priv->reset_gpio = reset_gpio; + + i2c_set_clientdata(client, priv); + /* Get firmware version */ error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, &firmware, sizeof(firmware)); @@ -243,20 +259,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, xmax = panel.x_low | (panel.x_high << 8); ymax = panel.y_low | (panel.y_high << 8); - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - input = devm_input_allocate_device(dev); - if (!input) - return -ENOMEM; - - priv->client = client; - priv->input = input; - priv->poll_period = DEFAULT_POLL_PERIOD; - INIT_DELAYED_WORK(&priv->dwork, ili210x_work); - priv->reset_gpio = reset_gpio; - /* Setup input device */ input->name = "ILI210x Touchscreen"; input->id.bustype = BUS_I2C; @@ -276,8 +278,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); - i2c_set_clientdata(client, priv); - error = devm_add_action(dev, ili210x_cancel_work, priv); if (error) return error; From c5d0e4b5154ac81cffdf6472d5f5e1408faa8ccd Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 22:01:51 -0800 Subject: [PATCH 1296/4277] Input: ili210x - add OF match table Add OF match table for the ili210x touchscreen. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index ba2634582364..4e550fe0cb15 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -352,10 +352,17 @@ static const struct i2c_device_id ili210x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); +static const struct of_device_id ili210x_dt_ids[] = { + { .compatible = "ilitek,ili210x", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ili210x_dt_ids); + static struct i2c_driver ili210x_ts_driver = { .driver = { .name = "ili210x_i2c", .pm = &ili210x_i2c_pm, + .of_match_table = ili210x_dt_ids, }, .id_table = ili210x_i2c_id, .probe = ili210x_i2c_probe, From 4958891764749304ac1511f6140ae3888c088e23 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Feb 2019 22:02:02 -0800 Subject: [PATCH 1297/4277] Input: ili210x - add ILI251X support Add support for ILI251x touch controller. This controller is similar to the ILI210x, except for the following differences: - Does not support I2C R-W transfer, Read must be followed by an obscenely long delay, and then followed by Write - Does support 10 simultaneous touch inputs. - Touch data format is slightly different, pressure reporting does not work although the touch data contain such information. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 118 ++++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 15 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 4e550fe0cb15..6cfe463ac118 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -7,9 +7,11 @@ #include #include #include +#include #include -#define MAX_TOUCHES 2 +#define ILI210X_TOUCHES 2 +#define ILI251X_TOUCHES 10 #define DEFAULT_POLL_PERIOD 20 /* Touchscreen commands */ @@ -33,17 +35,25 @@ struct firmware_version { u8 minor; } __packed; +enum ili2xxx_model { + MODEL_ILI210X, + MODEL_ILI251X, +}; + struct ili210x { struct i2c_client *client; struct input_dev *input; unsigned int poll_period; struct delayed_work dwork; struct gpio_desc *reset_gpio; + enum ili2xxx_model model; + unsigned int max_touches; }; static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, size_t len) { + struct ili210x *priv = i2c_get_clientdata(client); struct i2c_msg msg[2] = { { .addr = client->addr, @@ -59,7 +69,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, } }; - if (i2c_transfer(client->adapter, msg, 2) != 2) { + if (priv->model == MODEL_ILI251X) { + if (i2c_transfer(client->adapter, msg, 1) != 1) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + usleep_range(5000, 5500); + + if (i2c_transfer(client->adapter, msg + 1, 1) != 1) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + } else { + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + } + + return 0; +} + +static int ili210x_read(struct i2c_client *client, void *buf, size_t len) +{ + struct i2c_msg msg = { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }; + + if (i2c_transfer(client->adapter, &msg, 1) != 1) { dev_err(&client->dev, "i2c transfer failed\n"); return -EIO; } @@ -71,7 +112,7 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { - if (finger >= MAX_TOUCHES) + if (finger >= ILI210X_TOUCHES) return false; if (touchdata[0] & BIT(finger)) @@ -83,17 +124,43 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, return true; } +static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) +{ + if (finger >= ILI251X_TOUCHES) + return false; + + *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); + if (!(*x & BIT(15))) /* Touch indication */ + return false; + + *x &= 0x3fff; + *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); + + return true; +} + static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) { struct input_dev *input = priv->input; int i; - bool touch; - unsigned int x, y; + bool contact = false, touch = false; + unsigned int x = 0, y = 0; - for (i = 0; i < MAX_TOUCHES; i++) { + for (i = 0; i < priv->max_touches; i++) { input_mt_slot(input, i); - touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); + if (priv->model == MODEL_ILI210X) { + touch = ili210x_touchdata_to_coords(priv, touchdata, + i, &x, &y); + } else if (priv->model == MODEL_ILI251X) { + touch = ili251x_touchdata_to_coords(priv, touchdata, + i, &x, &y); + if (touch) + contact = true; + } + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); if (touch) { input_report_abs(input, ABS_MT_POSITION_X, x); @@ -104,7 +171,10 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) input_mt_report_pointer_emulation(input, false); input_sync(input); - return touchdata[0] & 0xf3; + if (priv->model == MODEL_ILI210X) + contact = touchdata[0] & 0xf3; + + return contact; } static void ili210x_work(struct work_struct *work) @@ -112,12 +182,20 @@ static void ili210x_work(struct work_struct *work) struct ili210x *priv = container_of(work, struct ili210x, dwork.work); struct i2c_client *client = priv->client; - u8 touchdata[1 + 4 * MAX_TOUCHES]; + u8 touchdata[64] = { 0 }; bool touch; - int error; + int error = -EINVAL; + + if (priv->model == MODEL_ILI210X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, sizeof(touchdata)); + } else if (priv->model == MODEL_ILI251X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, 31); + if (!error && touchdata[0] == 2) + error = ili210x_read(client, &touchdata[31], 20); + } - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, sizeof(touchdata)); if (error) { dev_err(&client->dev, "Unable to get touchdata, err = %d\n", error); @@ -198,9 +276,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, struct input_dev *input; struct panel_info panel; struct firmware_version firmware; + enum ili2xxx_model model; int xmax, ymax; int error; + model = (enum ili2xxx_model)id->driver_data; + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); if (client->irq <= 0) { @@ -236,6 +317,11 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->poll_period = DEFAULT_POLL_PERIOD; INIT_DELAYED_WORK(&priv->dwork, ili210x_work); priv->reset_gpio = reset_gpio; + priv->model = model; + if (model == MODEL_ILI210X) + priv->max_touches = ILI210X_TOUCHES; + if (model == MODEL_ILI251X) + priv->max_touches = ILI251X_TOUCHES; i2c_set_clientdata(client, priv); @@ -274,7 +360,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); /* Multi touch */ - input_mt_init_slots(input, MAX_TOUCHES, 0); + input_mt_init_slots(input, priv->max_touches, 0); input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); @@ -347,13 +433,15 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, ili210x_i2c_suspend, ili210x_i2c_resume); static const struct i2c_device_id ili210x_i2c_id[] = { - { "ili210x", 0 }, + { "ili210x", MODEL_ILI210X }, + { "ili251x", MODEL_ILI251X }, { } }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); static const struct of_device_id ili210x_dt_ids[] = { - { .compatible = "ilitek,ili210x", }, + { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, + { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, { }, }; MODULE_DEVICE_TABLE(of, ili210x_dt_ids); From ae7b8eda27b33b1f688dfdebe4d46f690a8f9162 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 22 Jan 2019 10:56:36 -0600 Subject: [PATCH 1298/4277] mfd: sm501: Fix potential NULL pointer dereference There is a potential NULL pointer dereference in case devm_kzalloc() fails and returns NULL. Fix this by adding a NULL check on *lookup* This bug was detected with the help of Coccinelle. Fixes: b2e63555592f ("i2c: gpio: Convert to use descriptors") Cc: stable@vger.kernel.org Signed-off-by: Gustavo A. R. Silva Signed-off-by: Lee Jones --- drivers/mfd/sm501.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index a530972c5a7e..e0173bf4b0dc 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1145,6 +1145,9 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, lookup = devm_kzalloc(&pdev->dev, sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup), GFP_KERNEL); + if (!lookup) + return -ENOMEM; + lookup->dev_id = "i2c-gpio"; if (iic->pin_sda < 32) lookup->table[0].chip_label = "SM501-LOW"; From 9f208eca4f2faa38def458f663ec142a0b6f451b Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 22 Jan 2019 10:58:20 -0600 Subject: [PATCH 1299/4277] mfd: sm501: Use struct_size() in devm_kzalloc() One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; struct boo entry[]; }; instance = devm_kzalloc(dev, sizeof(struct foo) + count * sizeof(struct boo), GFP_KERNEL); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: instance = devm_kzalloc(dev, struct_size(instance, entry, count), GFP_KERNEL); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Lee Jones --- drivers/mfd/sm501.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index e0173bf4b0dc..d217debf382e 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1142,8 +1142,7 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, return -ENOMEM; /* Create a gpiod lookup using gpiochip-local offsets */ - lookup = devm_kzalloc(&pdev->dev, - sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup), + lookup = devm_kzalloc(&pdev->dev, struct_size(lookup, table, 3), GFP_KERNEL); if (!lookup) return -ENOMEM; From 83761f9663a228f148ee318cc0531035cfa5654c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 29 Jan 2019 12:25:53 +0100 Subject: [PATCH 1300/4277] mfd: sec-core: Return gracefully instead of BUG() if device cannot match Replace impossible BUG() in probe with a proper return. This can be triggered only in case of a clear bug (e.g. adding broken half-support for new S2MPSXX flavor) but BUG() is discouraged and the boot process can actually try to continue. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index e0835c9df7a1..00321011de0a 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -471,8 +471,9 @@ static int sec_pmic_probe(struct i2c_client *i2c, num_sec_devs = ARRAY_SIZE(s2mpu02_devs); break; default: - /* If this happens the probe function is problem */ - BUG(); + dev_err(&i2c->dev, "Unsupported device type (%lu)\n", + sec_pmic->device_type); + return -ENODEV; } ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs, NULL, 0, NULL); From 469de49e88a3f877425009d45d6ac801f9aefe19 Mon Sep 17 00:00:00 2001 From: Vijay Khemka Date: Tue, 29 Jan 2019 15:05:38 -0800 Subject: [PATCH 1301/4277] dt-bindings: mfd: aspeed-lpc: Make parameter optional Memory-region and flash phandle is not a required parameter, it is optional to describe in device tree and needed only use basis. Signed-off-by: Vijay Khemka Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/aspeed-lpc.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt index 34dd89087cff..86446074e206 100644 --- a/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt +++ b/Documentation/devicetree/bindings/mfd/aspeed-lpc.txt @@ -135,6 +135,8 @@ Required properties: - clocks: contains a phandle to the syscon node describing the clocks. There should then be one cell representing the clock to use +Optional properties: + - memory-region: A phandle to a reserved_memory region to be used for the LPC to AHB mapping From fdc98f070b149b038a66e57dea11f2fabb5fdfbc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 30 Jan 2019 11:41:23 +0000 Subject: [PATCH 1302/4277] mfd: lochnagar: Add initial binding documentation Lochnagar is an evaluation and development board for Cirrus Logic Smart CODEC and Amp devices. It allows the connection of most Cirrus Logic devices on mini-cards, as well as allowing connection of various application processor systems to provide a full evaluation platform. This driver supports the board controller chip on the Lochnagar board. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- .../bindings/mfd/cirrus,lochnagar.txt | 68 +++++++++ include/dt-bindings/clk/lochnagar.h | 26 ++++ include/dt-bindings/pinctrl/lochnagar.h | 132 ++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt create mode 100644 include/dt-bindings/clk/lochnagar.h create mode 100644 include/dt-bindings/pinctrl/lochnagar.h diff --git a/Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt new file mode 100644 index 000000000000..004b0158cf4d --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt @@ -0,0 +1,68 @@ +Cirrus Logic Lochnagar Audio Development Board + +Lochnagar is an evaluation and development board for Cirrus Logic +Smart CODEC and Amp devices. It allows the connection of most Cirrus +Logic devices on mini-cards, as well as allowing connection of +various application processor systems to provide a full evaluation +platform. Audio system topology, clocking and power can all be +controlled through the Lochnagar, allowing the device under test +to be used in a variety of possible use cases. + +Also see these documents for generic binding information: + [1] GPIO : ../gpio/gpio.txt + +And these for relevant defines: + [2] include/dt-bindings/pinctrl/lochnagar.h + [3] include/dt-bindings/clock/lochnagar.h + +And these documents for the required sub-node binding details: + [4] Clock: ../clock/cirrus,lochnagar.txt + [5] Pinctrl: ../pinctrl/cirrus,lochnagar.txt + [6] Regulator: ../regulator/cirrus,lochnagar.txt + +Required properties: + + - compatible : One of the following strings: + "cirrus,lochnagar1" + "cirrus,lochnagar2" + + - reg : I2C slave address + + - reset-gpios : Reset line to the Lochnagar, see [1]. + +Required sub-nodes: + + - lochnagar-clk : Binding for the clocking components, see [4]. + + - lochnagar-pinctrl : Binding for the pin control components, see [5]. + +Optional sub-nodes: + + - Bindings for the regulator components, see [6]. Only available on + Lochnagar 2. + +Optional properties: + + - present-gpios : Host present line, indicating the presence of a + host system, see [1]. This can be omitted if the present line is + tied in hardware. + +Example: + +lochnagar: lochnagar@22 { + compatible = "cirrus,lochnagar2"; + reg = <0x22>; + + reset-gpios = <&gpio0 55 0>; + present-gpios = <&gpio0 60 0>; + + lochnagar-clk { + compatible = "cirrus,lochnagar2-clk"; + ... + }; + + lochnagar-pinctrl { + compatible = "cirrus,lochnagar-pinctrl"; + ... + }; +}; diff --git a/include/dt-bindings/clk/lochnagar.h b/include/dt-bindings/clk/lochnagar.h new file mode 100644 index 000000000000..8fa20551ff17 --- /dev/null +++ b/include/dt-bindings/clk/lochnagar.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Device Tree defines for Lochnagar clocking + * + * Copyright (c) 2017-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#ifndef DT_BINDINGS_CLK_LOCHNAGAR_H +#define DT_BINDINGS_CLK_LOCHNAGAR_H + +#define LOCHNAGAR_CDC_MCLK1 0 +#define LOCHNAGAR_CDC_MCLK2 1 +#define LOCHNAGAR_DSP_CLKIN 2 +#define LOCHNAGAR_GF_CLKOUT1 3 +#define LOCHNAGAR_GF_CLKOUT2 4 +#define LOCHNAGAR_PSIA1_MCLK 5 +#define LOCHNAGAR_PSIA2_MCLK 6 +#define LOCHNAGAR_SPDIF_MCLK 7 +#define LOCHNAGAR_ADAT_MCLK 8 +#define LOCHNAGAR_SOUNDCARD_MCLK 9 +#define LOCHNAGAR_SPDIF_CLKOUT 10 + +#endif diff --git a/include/dt-bindings/pinctrl/lochnagar.h b/include/dt-bindings/pinctrl/lochnagar.h new file mode 100644 index 000000000000..644760bf5725 --- /dev/null +++ b/include/dt-bindings/pinctrl/lochnagar.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Device Tree defines for Lochnagar pinctrl + * + * Copyright (c) 2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#ifndef DT_BINDINGS_PINCTRL_LOCHNAGAR_H +#define DT_BINDINGS_PINCTRL_LOCHNAGAR_H + +#define LOCHNAGAR1_PIN_CDC_RESET 0 +#define LOCHNAGAR1_PIN_DSP_RESET 1 +#define LOCHNAGAR1_PIN_CDC_CIF1MODE 2 +#define LOCHNAGAR1_PIN_NUM_GPIOS 3 + +#define LOCHNAGAR2_PIN_CDC_RESET 0 +#define LOCHNAGAR2_PIN_DSP_RESET 1 +#define LOCHNAGAR2_PIN_CDC_CIF1MODE 2 +#define LOCHNAGAR2_PIN_CDC_LDOENA 3 +#define LOCHNAGAR2_PIN_SPDIF_HWMODE 4 +#define LOCHNAGAR2_PIN_SPDIF_RESET 5 +#define LOCHNAGAR2_PIN_FPGA_GPIO1 6 +#define LOCHNAGAR2_PIN_FPGA_GPIO2 7 +#define LOCHNAGAR2_PIN_FPGA_GPIO3 8 +#define LOCHNAGAR2_PIN_FPGA_GPIO4 9 +#define LOCHNAGAR2_PIN_FPGA_GPIO5 10 +#define LOCHNAGAR2_PIN_FPGA_GPIO6 11 +#define LOCHNAGAR2_PIN_CDC_GPIO1 12 +#define LOCHNAGAR2_PIN_CDC_GPIO2 13 +#define LOCHNAGAR2_PIN_CDC_GPIO3 14 +#define LOCHNAGAR2_PIN_CDC_GPIO4 15 +#define LOCHNAGAR2_PIN_CDC_GPIO5 16 +#define LOCHNAGAR2_PIN_CDC_GPIO6 17 +#define LOCHNAGAR2_PIN_CDC_GPIO7 18 +#define LOCHNAGAR2_PIN_CDC_GPIO8 19 +#define LOCHNAGAR2_PIN_DSP_GPIO1 20 +#define LOCHNAGAR2_PIN_DSP_GPIO2 21 +#define LOCHNAGAR2_PIN_DSP_GPIO3 22 +#define LOCHNAGAR2_PIN_DSP_GPIO4 23 +#define LOCHNAGAR2_PIN_DSP_GPIO5 24 +#define LOCHNAGAR2_PIN_DSP_GPIO6 25 +#define LOCHNAGAR2_PIN_GF_GPIO2 26 +#define LOCHNAGAR2_PIN_GF_GPIO3 27 +#define LOCHNAGAR2_PIN_GF_GPIO7 28 +#define LOCHNAGAR2_PIN_CDC_AIF1_BCLK 29 +#define LOCHNAGAR2_PIN_CDC_AIF1_RXDAT 30 +#define LOCHNAGAR2_PIN_CDC_AIF1_LRCLK 31 +#define LOCHNAGAR2_PIN_CDC_AIF1_TXDAT 32 +#define LOCHNAGAR2_PIN_CDC_AIF2_BCLK 33 +#define LOCHNAGAR2_PIN_CDC_AIF2_RXDAT 34 +#define LOCHNAGAR2_PIN_CDC_AIF2_LRCLK 35 +#define LOCHNAGAR2_PIN_CDC_AIF2_TXDAT 36 +#define LOCHNAGAR2_PIN_CDC_AIF3_BCLK 37 +#define LOCHNAGAR2_PIN_CDC_AIF3_RXDAT 38 +#define LOCHNAGAR2_PIN_CDC_AIF3_LRCLK 39 +#define LOCHNAGAR2_PIN_CDC_AIF3_TXDAT 40 +#define LOCHNAGAR2_PIN_DSP_AIF1_BCLK 41 +#define LOCHNAGAR2_PIN_DSP_AIF1_RXDAT 42 +#define LOCHNAGAR2_PIN_DSP_AIF1_LRCLK 43 +#define LOCHNAGAR2_PIN_DSP_AIF1_TXDAT 44 +#define LOCHNAGAR2_PIN_DSP_AIF2_BCLK 45 +#define LOCHNAGAR2_PIN_DSP_AIF2_RXDAT 46 +#define LOCHNAGAR2_PIN_DSP_AIF2_LRCLK 47 +#define LOCHNAGAR2_PIN_DSP_AIF2_TXDAT 48 +#define LOCHNAGAR2_PIN_PSIA1_BCLK 49 +#define LOCHNAGAR2_PIN_PSIA1_RXDAT 50 +#define LOCHNAGAR2_PIN_PSIA1_LRCLK 51 +#define LOCHNAGAR2_PIN_PSIA1_TXDAT 52 +#define LOCHNAGAR2_PIN_PSIA2_BCLK 53 +#define LOCHNAGAR2_PIN_PSIA2_RXDAT 54 +#define LOCHNAGAR2_PIN_PSIA2_LRCLK 55 +#define LOCHNAGAR2_PIN_PSIA2_TXDAT 56 +#define LOCHNAGAR2_PIN_GF_AIF3_BCLK 57 +#define LOCHNAGAR2_PIN_GF_AIF3_RXDAT 58 +#define LOCHNAGAR2_PIN_GF_AIF3_LRCLK 59 +#define LOCHNAGAR2_PIN_GF_AIF3_TXDAT 60 +#define LOCHNAGAR2_PIN_GF_AIF4_BCLK 61 +#define LOCHNAGAR2_PIN_GF_AIF4_RXDAT 62 +#define LOCHNAGAR2_PIN_GF_AIF4_LRCLK 63 +#define LOCHNAGAR2_PIN_GF_AIF4_TXDAT 64 +#define LOCHNAGAR2_PIN_GF_AIF1_BCLK 65 +#define LOCHNAGAR2_PIN_GF_AIF1_RXDAT 66 +#define LOCHNAGAR2_PIN_GF_AIF1_LRCLK 67 +#define LOCHNAGAR2_PIN_GF_AIF1_TXDAT 68 +#define LOCHNAGAR2_PIN_GF_AIF2_BCLK 69 +#define LOCHNAGAR2_PIN_GF_AIF2_RXDAT 70 +#define LOCHNAGAR2_PIN_GF_AIF2_LRCLK 71 +#define LOCHNAGAR2_PIN_GF_AIF2_TXDAT 72 +#define LOCHNAGAR2_PIN_DSP_UART1_RX 73 +#define LOCHNAGAR2_PIN_DSP_UART1_TX 74 +#define LOCHNAGAR2_PIN_DSP_UART2_RX 75 +#define LOCHNAGAR2_PIN_DSP_UART2_TX 76 +#define LOCHNAGAR2_PIN_GF_UART2_RX 77 +#define LOCHNAGAR2_PIN_GF_UART2_TX 78 +#define LOCHNAGAR2_PIN_USB_UART_RX 79 +#define LOCHNAGAR2_PIN_CDC_PDMCLK1 80 +#define LOCHNAGAR2_PIN_CDC_PDMDAT1 81 +#define LOCHNAGAR2_PIN_CDC_PDMCLK2 82 +#define LOCHNAGAR2_PIN_CDC_PDMDAT2 83 +#define LOCHNAGAR2_PIN_CDC_DMICCLK1 84 +#define LOCHNAGAR2_PIN_CDC_DMICDAT1 85 +#define LOCHNAGAR2_PIN_CDC_DMICCLK2 86 +#define LOCHNAGAR2_PIN_CDC_DMICDAT2 87 +#define LOCHNAGAR2_PIN_CDC_DMICCLK3 88 +#define LOCHNAGAR2_PIN_CDC_DMICDAT3 89 +#define LOCHNAGAR2_PIN_CDC_DMICCLK4 90 +#define LOCHNAGAR2_PIN_CDC_DMICDAT4 91 +#define LOCHNAGAR2_PIN_DSP_DMICCLK1 92 +#define LOCHNAGAR2_PIN_DSP_DMICDAT1 93 +#define LOCHNAGAR2_PIN_DSP_DMICCLK2 94 +#define LOCHNAGAR2_PIN_DSP_DMICDAT2 95 +#define LOCHNAGAR2_PIN_I2C2_SCL 96 +#define LOCHNAGAR2_PIN_I2C2_SDA 97 +#define LOCHNAGAR2_PIN_I2C3_SCL 98 +#define LOCHNAGAR2_PIN_I2C3_SDA 99 +#define LOCHNAGAR2_PIN_I2C4_SCL 100 +#define LOCHNAGAR2_PIN_I2C4_SDA 101 +#define LOCHNAGAR2_PIN_DSP_STANDBY 102 +#define LOCHNAGAR2_PIN_CDC_MCLK1 103 +#define LOCHNAGAR2_PIN_CDC_MCLK2 104 +#define LOCHNAGAR2_PIN_DSP_CLKIN 105 +#define LOCHNAGAR2_PIN_PSIA1_MCLK 106 +#define LOCHNAGAR2_PIN_PSIA2_MCLK 107 +#define LOCHNAGAR2_PIN_GF_GPIO1 108 +#define LOCHNAGAR2_PIN_GF_GPIO5 109 +#define LOCHNAGAR2_PIN_DSP_GPIO20 110 +#define LOCHNAGAR2_PIN_NUM_GPIOS 111 + +#endif From 422dcafe477c7240d03c7b150704c45e0b17be57 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 30 Jan 2019 11:41:26 +0000 Subject: [PATCH 1303/4277] mfd: lochnagar: Add support for the Cirrus Logic Lochnagar Lochnagar is an evaluation and development board for Cirrus Logic Smart CODEC and Amp devices. It allows the connection of most Cirrus Logic devices on mini-cards, as well as allowing connection of various application processor systems to provide a full evaluation platform. This driver supports the board controller chip on the Lochnagar board. Audio system topology, clocking and power can all be controlled through the Lochnagar controller chip, allowing the device under test to be used in a variety of possible use cases. As the Lochnagar is a fairly complex device this MFD driver allows the drivers for the various features to be bound in. Initially clocking, regulator and pinctrl will be added as these are necessary to configure the system. But in time at least audio and voltage/current monitoring will also be added. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- MAINTAINERS | 17 ++ drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 2 + drivers/mfd/lochnagar-i2c.c | 398 ++++++++++++++++++++++++++++ include/linux/mfd/lochnagar.h | 55 ++++ include/linux/mfd/lochnagar1_regs.h | 157 +++++++++++ include/linux/mfd/lochnagar2_regs.h | 291 ++++++++++++++++++++ 7 files changed, 928 insertions(+) create mode 100644 drivers/mfd/lochnagar-i2c.c create mode 100644 include/linux/mfd/lochnagar.h create mode 100644 include/linux/mfd/lochnagar1_regs.h create mode 100644 include/linux/mfd/lochnagar2_regs.h diff --git a/MAINTAINERS b/MAINTAINERS index 51029a425dbe..3e3f0384362b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3700,6 +3700,23 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/cirrus/ep93xx_eth.c +CIRRUS LOGIC LOCHNAGAR DRIVER +M: Charles Keepax +M: Richard Fitzgerald +L: patches@opensource.cirrus.com +S: Supported +F: drivers/clk/clk-lochnagar.c +F: drivers/mfd/lochnagar-i2c.c +F: drivers/pinctrl/cirrus/pinctrl-lochnagar.c +F: drivers/regulator/lochnagar-regulator.c +F: include/dt-bindings/clk/lochnagar.h +F: include/dt-bindings/pinctrl/lochnagar.h +F: include/linux/mfd/lochnagar* +F: Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt +F: Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt +F: Documentation/devicetree/bindings/pinctrl/cirrus,lochnagar.txt +F: Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt + CISCO FCOE HBA DRIVER M: Satish Kharat M: Sesidhar Baddela diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6e58221f5c28..f38f8741c68e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1686,6 +1686,14 @@ config MFD_VX855 VIA VX855/VX875 south bridge. You will need to enable the vx855_spi and/or vx855_gpio drivers for this to do anything useful. +config MFD_LOCHNAGAR + bool "Cirrus Logic Lochnagar Audio Development Board" + select MFD_CORE + select REGMAP_I2C + depends on I2C=y && OF + help + Support for Cirrus Logic Lochnagar audio development board. + config MFD_ARIZONA select REGMAP select REGMAP_IRQ diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a62fb0112d9f..a406fd3b8681 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -37,6 +37,8 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o +obj-$(CONFIG_MFD_LOCHNAGAR) += lochnagar-i2c.o + obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o diff --git a/drivers/mfd/lochnagar-i2c.c b/drivers/mfd/lochnagar-i2c.c new file mode 100644 index 000000000000..3a65d9938902 --- /dev/null +++ b/drivers/mfd/lochnagar-i2c.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Lochnagar I2C bus interface + * + * Copyright (c) 2012-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOCHNAGAR_BOOT_RETRIES 10 +#define LOCHNAGAR_BOOT_DELAY_MS 350 + +#define LOCHNAGAR_CONFIG_POLL_US 10000 + +static bool lochnagar1_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOCHNAGAR_SOFTWARE_RESET: + case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2: + case LOCHNAGAR1_CDC_AIF1_SEL...LOCHNAGAR1_CDC_AIF3_SEL: + case LOCHNAGAR1_CDC_MCLK1_SEL...LOCHNAGAR1_CDC_MCLK2_SEL: + case LOCHNAGAR1_CDC_AIF_CTRL1...LOCHNAGAR1_CDC_AIF_CTRL2: + case LOCHNAGAR1_EXT_AIF_CTRL: + case LOCHNAGAR1_DSP_AIF1_SEL...LOCHNAGAR1_DSP_AIF2_SEL: + case LOCHNAGAR1_DSP_CLKIN_SEL: + case LOCHNAGAR1_DSP_AIF: + case LOCHNAGAR1_GF_AIF1...LOCHNAGAR1_GF_AIF2: + case LOCHNAGAR1_PSIA_AIF: + case LOCHNAGAR1_PSIA1_SEL...LOCHNAGAR1_PSIA2_SEL: + case LOCHNAGAR1_SPDIF_AIF_SEL: + case LOCHNAGAR1_GF_AIF3_SEL...LOCHNAGAR1_GF_AIF4_SEL: + case LOCHNAGAR1_GF_CLKOUT1_SEL: + case LOCHNAGAR1_GF_AIF1_SEL...LOCHNAGAR1_GF_AIF2_SEL: + case LOCHNAGAR1_GF_GPIO2...LOCHNAGAR1_GF_GPIO7: + case LOCHNAGAR1_RST: + case LOCHNAGAR1_LED1...LOCHNAGAR1_LED2: + case LOCHNAGAR1_I2C_CTRL: + return true; + default: + return false; + } +} + +static const struct regmap_config lochnagar1_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = 0x50, + .readable_reg = lochnagar1_readable_register, + + .use_single_read = true, + .use_single_write = true, + + .cache_type = REGCACHE_RBTREE, +}; + +static const struct reg_sequence lochnagar1_patch[] = { + { 0x40, 0x0083 }, + { 0x47, 0x0018 }, + { 0x50, 0x0000 }, +}; + +static bool lochnagar2_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOCHNAGAR_SOFTWARE_RESET: + case LOCHNAGAR_FIRMWARE_ID1...LOCHNAGAR_FIRMWARE_ID2: + case LOCHNAGAR2_CDC_AIF1_CTRL...LOCHNAGAR2_CDC_AIF3_CTRL: + case LOCHNAGAR2_DSP_AIF1_CTRL...LOCHNAGAR2_DSP_AIF2_CTRL: + case LOCHNAGAR2_PSIA1_CTRL...LOCHNAGAR2_PSIA2_CTRL: + case LOCHNAGAR2_GF_AIF3_CTRL...LOCHNAGAR2_GF_AIF4_CTRL: + case LOCHNAGAR2_GF_AIF1_CTRL...LOCHNAGAR2_GF_AIF2_CTRL: + case LOCHNAGAR2_SPDIF_AIF_CTRL: + case LOCHNAGAR2_USB_AIF1_CTRL...LOCHNAGAR2_USB_AIF2_CTRL: + case LOCHNAGAR2_ADAT_AIF_CTRL: + case LOCHNAGAR2_CDC_MCLK1_CTRL...LOCHNAGAR2_CDC_MCLK2_CTRL: + case LOCHNAGAR2_DSP_CLKIN_CTRL: + case LOCHNAGAR2_PSIA1_MCLK_CTRL...LOCHNAGAR2_PSIA2_MCLK_CTRL: + case LOCHNAGAR2_SPDIF_MCLK_CTRL: + case LOCHNAGAR2_GF_CLKOUT1_CTRL...LOCHNAGAR2_GF_CLKOUT2_CTRL: + case LOCHNAGAR2_ADAT_MCLK_CTRL: + case LOCHNAGAR2_SOUNDCARD_MCLK_CTRL: + case LOCHNAGAR2_GPIO_FPGA_GPIO1...LOCHNAGAR2_GPIO_FPGA_GPIO6: + case LOCHNAGAR2_GPIO_CDC_GPIO1...LOCHNAGAR2_GPIO_CDC_GPIO8: + case LOCHNAGAR2_GPIO_DSP_GPIO1...LOCHNAGAR2_GPIO_DSP_GPIO6: + case LOCHNAGAR2_GPIO_GF_GPIO2...LOCHNAGAR2_GPIO_GF_GPIO7: + case LOCHNAGAR2_GPIO_CDC_AIF1_BCLK...LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT: + case LOCHNAGAR2_GPIO_DSP_AIF1_BCLK...LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT: + case LOCHNAGAR2_GPIO_PSIA1_BCLK...LOCHNAGAR2_GPIO_PSIA2_TXDAT: + case LOCHNAGAR2_GPIO_GF_AIF3_BCLK...LOCHNAGAR2_GPIO_GF_AIF4_TXDAT: + case LOCHNAGAR2_GPIO_GF_AIF1_BCLK...LOCHNAGAR2_GPIO_GF_AIF2_TXDAT: + case LOCHNAGAR2_GPIO_DSP_UART1_RX...LOCHNAGAR2_GPIO_DSP_UART2_TX: + case LOCHNAGAR2_GPIO_GF_UART2_RX...LOCHNAGAR2_GPIO_GF_UART2_TX: + case LOCHNAGAR2_GPIO_USB_UART_RX: + case LOCHNAGAR2_GPIO_CDC_PDMCLK1...LOCHNAGAR2_GPIO_CDC_PDMDAT2: + case LOCHNAGAR2_GPIO_CDC_DMICCLK1...LOCHNAGAR2_GPIO_CDC_DMICDAT4: + case LOCHNAGAR2_GPIO_DSP_DMICCLK1...LOCHNAGAR2_GPIO_DSP_DMICDAT2: + case LOCHNAGAR2_GPIO_I2C2_SCL...LOCHNAGAR2_GPIO_I2C4_SDA: + case LOCHNAGAR2_GPIO_DSP_STANDBY: + case LOCHNAGAR2_GPIO_CDC_MCLK1...LOCHNAGAR2_GPIO_CDC_MCLK2: + case LOCHNAGAR2_GPIO_DSP_CLKIN: + case LOCHNAGAR2_GPIO_PSIA1_MCLK...LOCHNAGAR2_GPIO_PSIA2_MCLK: + case LOCHNAGAR2_GPIO_GF_GPIO1...LOCHNAGAR2_GPIO_GF_GPIO5: + case LOCHNAGAR2_GPIO_DSP_GPIO20: + case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16: + case LOCHNAGAR2_MINICARD_RESETS: + case LOCHNAGAR2_ANALOGUE_PATH_CTRL1...LOCHNAGAR2_ANALOGUE_PATH_CTRL2: + case LOCHNAGAR2_COMMS_CTRL4: + case LOCHNAGAR2_SPDIF_CTRL: + case LOCHNAGAR2_IMON_CTRL1...LOCHNAGAR2_IMON_CTRL4: + case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2: + case LOCHNAGAR2_POWER_CTRL: + case LOCHNAGAR2_MICVDD_CTRL1: + case LOCHNAGAR2_MICVDD_CTRL2: + case LOCHNAGAR2_VDDCORE_CDC_CTRL1: + case LOCHNAGAR2_VDDCORE_CDC_CTRL2: + case LOCHNAGAR2_SOUNDCARD_AIF_CTRL: + return true; + default: + return false; + } +} + +static bool lochnagar2_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LOCHNAGAR2_GPIO_CHANNEL1...LOCHNAGAR2_GPIO_CHANNEL16: + case LOCHNAGAR2_ANALOGUE_PATH_CTRL1: + case LOCHNAGAR2_IMON_CTRL3...LOCHNAGAR2_IMON_CTRL4: + case LOCHNAGAR2_IMON_DATA1...LOCHNAGAR2_IMON_DATA2: + return true; + default: + return false; + } +} + +static const struct regmap_config lochnagar2_i2c_regmap = { + .reg_bits = 16, + .val_bits = 16, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = 0x1F1F, + .readable_reg = lochnagar2_readable_register, + .volatile_reg = lochnagar2_volatile_register, + + .cache_type = REGCACHE_RBTREE, +}; + +static const struct reg_sequence lochnagar2_patch[] = { + { 0x00EE, 0x0000 }, +}; + +struct lochnagar_config { + int id; + const char * const name; + enum lochnagar_type type; + const struct regmap_config *regmap; + const struct reg_sequence *patch; + int npatch; +}; + +static struct lochnagar_config lochnagar_configs[] = { + { + .id = 0x50, + .name = "lochnagar1", + .type = LOCHNAGAR1, + .regmap = &lochnagar1_i2c_regmap, + .patch = lochnagar1_patch, + .npatch = ARRAY_SIZE(lochnagar1_patch), + }, + { + .id = 0xCB58, + .name = "lochnagar2", + .type = LOCHNAGAR2, + .regmap = &lochnagar2_i2c_regmap, + .patch = lochnagar2_patch, + .npatch = ARRAY_SIZE(lochnagar2_patch), + }, +}; + +static const struct of_device_id lochnagar_of_match[] = { + { .compatible = "cirrus,lochnagar1", .data = &lochnagar_configs[0] }, + { .compatible = "cirrus,lochnagar2", .data = &lochnagar_configs[1] }, + {}, +}; + +static int lochnagar_wait_for_boot(struct regmap *regmap, unsigned int *id) +{ + int i, ret; + + for (i = 0; i < LOCHNAGAR_BOOT_RETRIES; ++i) { + msleep(LOCHNAGAR_BOOT_DELAY_MS); + + /* The reset register will return the device ID when read */ + ret = regmap_read(regmap, LOCHNAGAR_SOFTWARE_RESET, id); + if (!ret) + return ret; + } + + return -ETIMEDOUT; +} + +/** + * lochnagar_update_config - Synchronise the boards analogue configuration to + * the hardware. + * + * @lochnagar: A pointer to the primary core data structure. + * + * Return: Zero on success or an appropriate negative error code on failure. + */ +int lochnagar_update_config(struct lochnagar *lochnagar) +{ + struct regmap *regmap = lochnagar->regmap; + unsigned int done = LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK; + int timeout_ms = LOCHNAGAR_BOOT_DELAY_MS * LOCHNAGAR_BOOT_RETRIES; + unsigned int val = 0; + int ret; + + lockdep_assert_held(&lochnagar->analogue_config_lock); + + if (lochnagar->type != LOCHNAGAR2) + return 0; + + /* + * Toggle the ANALOGUE_PATH_UPDATE bit and wait for the device to + * acknowledge that any outstanding changes to the analogue + * configuration have been applied. + */ + ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, 0); + if (ret < 0) + return ret; + + ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, + LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(regmap, + LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val, + (val & done), LOCHNAGAR_CONFIG_POLL_US, + timeout_ms * 1000); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(lochnagar_update_config); + +static int lochnagar_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + const struct lochnagar_config *config = NULL; + const struct of_device_id *of_id; + struct lochnagar *lochnagar; + struct gpio_desc *reset, *present; + unsigned int val; + unsigned int firmwareid; + unsigned int devid, rev; + int ret; + + lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL); + if (!lochnagar) + return -ENOMEM; + + of_id = of_match_device(lochnagar_of_match, dev); + if (!of_id) + return -EINVAL; + + config = of_id->data; + + lochnagar->dev = dev; + mutex_init(&lochnagar->analogue_config_lock); + + dev_set_drvdata(dev, lochnagar); + + reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) { + ret = PTR_ERR(reset); + dev_err(dev, "Failed to get reset GPIO: %d\n", ret); + return ret; + } + + present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH); + if (IS_ERR(present)) { + ret = PTR_ERR(present); + dev_err(dev, "Failed to get present GPIO: %d\n", ret); + return ret; + } + + /* Leave the Lochnagar in reset for a reasonable amount of time */ + msleep(20); + + /* Bring Lochnagar out of reset */ + gpiod_set_value_cansleep(reset, 1); + + /* Identify Lochnagar */ + lochnagar->type = config->type; + + lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap); + if (IS_ERR(lochnagar->regmap)) { + ret = PTR_ERR(lochnagar->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + /* Wait for Lochnagar to boot */ + ret = lochnagar_wait_for_boot(lochnagar->regmap, &val); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + return ret; + } + + devid = val & LOCHNAGAR_DEVICE_ID_MASK; + rev = val & LOCHNAGAR_REV_ID_MASK; + + if (devid != config->id) { + dev_err(dev, + "ID does not match %s (expected 0x%x got 0x%x)\n", + config->name, config->id, devid); + return -ENODEV; + } + + /* Identify firmware */ + ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val); + if (ret < 0) { + dev_err(dev, "Failed to read firmware id 1: %d\n", ret); + return ret; + } + + firmwareid = val; + + ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val); + if (ret < 0) { + dev_err(dev, "Failed to read firmware id 2: %d\n", ret); + return ret; + } + + firmwareid |= (val << config->regmap->val_bits); + + dev_info(dev, "Found %s (0x%x) revision %u firmware 0x%.6x\n", + config->name, devid, rev + 1, firmwareid); + + ret = regmap_register_patch(lochnagar->regmap, config->patch, + config->npatch); + if (ret < 0) { + dev_err(dev, "Failed to register patch: %d\n", ret); + return ret; + } + + ret = devm_of_platform_populate(dev); + if (ret < 0) { + dev_err(dev, "Failed to populate child nodes: %d\n", ret); + return ret; + } + + return ret; +} + +static struct i2c_driver lochnagar_i2c_driver = { + .driver = { + .name = "lochnagar", + .of_match_table = of_match_ptr(lochnagar_of_match), + .suppress_bind_attrs = true, + }, + .probe_new = lochnagar_i2c_probe, +}; + +static int __init lochnagar_i2c_init(void) +{ + int ret; + + ret = i2c_add_driver(&lochnagar_i2c_driver); + if (ret) + pr_err("Failed to register Lochnagar driver: %d\n", ret); + + return ret; +} +subsys_initcall(lochnagar_i2c_init); diff --git a/include/linux/mfd/lochnagar.h b/include/linux/mfd/lochnagar.h new file mode 100644 index 000000000000..ff9e64cfc9fb --- /dev/null +++ b/include/linux/mfd/lochnagar.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Lochnagar internals + * + * Copyright (c) 2013-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#include +#include +#include + +#ifndef CIRRUS_LOCHNAGAR_H +#define CIRRUS_LOCHNAGAR_H + +enum lochnagar_type { + LOCHNAGAR1, + LOCHNAGAR2, +}; + +/** + * struct lochnagar - Core data for the Lochnagar audio board driver. + * + * @type: The type of Lochnagar device connected. + * @dev: A pointer to the struct device for the main MFD. + * @regmap: The devices main register map. + * @analogue_config_lock: Lock used to protect updates in the analogue + * configuration as these must not be changed whilst the hardware is processing + * the last update. + */ +struct lochnagar { + enum lochnagar_type type; + struct device *dev; + struct regmap *regmap; + + /* Lock to protect updates to the analogue configuration */ + struct mutex analogue_config_lock; +}; + +/* Register Addresses */ +#define LOCHNAGAR_SOFTWARE_RESET 0x00 +#define LOCHNAGAR_FIRMWARE_ID1 0x01 +#define LOCHNAGAR_FIRMWARE_ID2 0x02 + +/* (0x0000) Software Reset */ +#define LOCHNAGAR_DEVICE_ID_MASK 0xFFFC +#define LOCHNAGAR_DEVICE_ID_SHIFT 2 +#define LOCHNAGAR_REV_ID_MASK 0x0003 +#define LOCHNAGAR_REV_ID_SHIFT 0 + +int lochnagar_update_config(struct lochnagar *lochnagar); + +#endif diff --git a/include/linux/mfd/lochnagar1_regs.h b/include/linux/mfd/lochnagar1_regs.h new file mode 100644 index 000000000000..114b846245d9 --- /dev/null +++ b/include/linux/mfd/lochnagar1_regs.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Lochnagar1 register definitions + * + * Copyright (c) 2017-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#ifndef LOCHNAGAR1_REGISTERS_H +#define LOCHNAGAR1_REGISTERS_H + +/* Register Addresses */ +#define LOCHNAGAR1_CDC_AIF1_SEL 0x0008 +#define LOCHNAGAR1_CDC_AIF2_SEL 0x0009 +#define LOCHNAGAR1_CDC_AIF3_SEL 0x000A +#define LOCHNAGAR1_CDC_MCLK1_SEL 0x000B +#define LOCHNAGAR1_CDC_MCLK2_SEL 0x000C +#define LOCHNAGAR1_CDC_AIF_CTRL1 0x000D +#define LOCHNAGAR1_CDC_AIF_CTRL2 0x000E +#define LOCHNAGAR1_EXT_AIF_CTRL 0x000F +#define LOCHNAGAR1_DSP_AIF1_SEL 0x0010 +#define LOCHNAGAR1_DSP_AIF2_SEL 0x0011 +#define LOCHNAGAR1_DSP_CLKIN_SEL 0x0012 +#define LOCHNAGAR1_DSP_AIF 0x0013 +#define LOCHNAGAR1_GF_AIF1 0x0014 +#define LOCHNAGAR1_GF_AIF2 0x0015 +#define LOCHNAGAR1_PSIA_AIF 0x0016 +#define LOCHNAGAR1_PSIA1_SEL 0x0017 +#define LOCHNAGAR1_PSIA2_SEL 0x0018 +#define LOCHNAGAR1_SPDIF_AIF_SEL 0x0019 +#define LOCHNAGAR1_GF_AIF3_SEL 0x001C +#define LOCHNAGAR1_GF_AIF4_SEL 0x001D +#define LOCHNAGAR1_GF_CLKOUT1_SEL 0x001E +#define LOCHNAGAR1_GF_AIF1_SEL 0x001F +#define LOCHNAGAR1_GF_AIF2_SEL 0x0020 +#define LOCHNAGAR1_GF_GPIO2 0x0026 +#define LOCHNAGAR1_GF_GPIO3 0x0027 +#define LOCHNAGAR1_GF_GPIO7 0x0028 +#define LOCHNAGAR1_RST 0x0029 +#define LOCHNAGAR1_LED1 0x002A +#define LOCHNAGAR1_LED2 0x002B +#define LOCHNAGAR1_I2C_CTRL 0x0046 + +/* + * (0x0008 - 0x000C, 0x0010 - 0x0012, 0x0017 - 0x0020) + * CDC_AIF1_SEL - GF_AIF2_SEL + */ +#define LOCHNAGAR1_SRC_MASK 0xFF +#define LOCHNAGAR1_SRC_SHIFT 0 + +/* (0x000D) CDC_AIF_CTRL1 */ +#define LOCHNAGAR1_CDC_AIF2_LRCLK_DIR_MASK 0x40 +#define LOCHNAGAR1_CDC_AIF2_LRCLK_DIR_SHIFT 6 +#define LOCHNAGAR1_CDC_AIF2_BCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_CDC_AIF2_BCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_CDC_AIF2_ENA_MASK 0x10 +#define LOCHNAGAR1_CDC_AIF2_ENA_SHIFT 4 +#define LOCHNAGAR1_CDC_AIF1_LRCLK_DIR_MASK 0x04 +#define LOCHNAGAR1_CDC_AIF1_LRCLK_DIR_SHIFT 2 +#define LOCHNAGAR1_CDC_AIF1_BCLK_DIR_MASK 0x02 +#define LOCHNAGAR1_CDC_AIF1_BCLK_DIR_SHIFT 1 +#define LOCHNAGAR1_CDC_AIF1_ENA_MASK 0x01 +#define LOCHNAGAR1_CDC_AIF1_ENA_SHIFT 0 + +/* (0x000E) CDC_AIF_CTRL2 */ +#define LOCHNAGAR1_CDC_AIF3_LRCLK_DIR_MASK 0x40 +#define LOCHNAGAR1_CDC_AIF3_LRCLK_DIR_SHIFT 6 +#define LOCHNAGAR1_CDC_AIF3_BCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_CDC_AIF3_BCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_CDC_AIF3_ENA_MASK 0x10 +#define LOCHNAGAR1_CDC_AIF3_ENA_SHIFT 4 +#define LOCHNAGAR1_CDC_MCLK1_ENA_MASK 0x02 +#define LOCHNAGAR1_CDC_MCLK1_ENA_SHIFT 1 +#define LOCHNAGAR1_CDC_MCLK2_ENA_MASK 0x01 +#define LOCHNAGAR1_CDC_MCLK2_ENA_SHIFT 0 + +/* (0x000F) EXT_AIF_CTRL */ +#define LOCHNAGAR1_SPDIF_AIF_LRCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_SPDIF_AIF_LRCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_SPDIF_AIF_BCLK_DIR_MASK 0x10 +#define LOCHNAGAR1_SPDIF_AIF_BCLK_DIR_SHIFT 4 +#define LOCHNAGAR1_SPDIF_AIF_ENA_MASK 0x08 +#define LOCHNAGAR1_SPDIF_AIF_ENA_SHIFT 3 + +/* (0x0013) DSP_AIF */ +#define LOCHNAGAR1_DSP_AIF2_LRCLK_DIR_MASK 0x40 +#define LOCHNAGAR1_DSP_AIF2_LRCLK_DIR_SHIFT 6 +#define LOCHNAGAR1_DSP_AIF2_BCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_DSP_AIF2_BCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_DSP_AIF2_ENA_MASK 0x10 +#define LOCHNAGAR1_DSP_AIF2_ENA_SHIFT 4 +#define LOCHNAGAR1_DSP_CLKIN_ENA_MASK 0x08 +#define LOCHNAGAR1_DSP_CLKIN_ENA_SHIFT 3 +#define LOCHNAGAR1_DSP_AIF1_LRCLK_DIR_MASK 0x04 +#define LOCHNAGAR1_DSP_AIF1_LRCLK_DIR_SHIFT 2 +#define LOCHNAGAR1_DSP_AIF1_BCLK_DIR_MASK 0x02 +#define LOCHNAGAR1_DSP_AIF1_BCLK_DIR_SHIFT 1 +#define LOCHNAGAR1_DSP_AIF1_ENA_MASK 0x01 +#define LOCHNAGAR1_DSP_AIF1_ENA_SHIFT 0 + +/* (0x0014) GF_AIF1 */ +#define LOCHNAGAR1_GF_CLKOUT1_ENA_MASK 0x40 +#define LOCHNAGAR1_GF_CLKOUT1_ENA_SHIFT 6 +#define LOCHNAGAR1_GF_AIF3_LRCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_GF_AIF3_LRCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_GF_AIF3_BCLK_DIR_MASK 0x10 +#define LOCHNAGAR1_GF_AIF3_BCLK_DIR_SHIFT 4 +#define LOCHNAGAR1_GF_AIF3_ENA_MASK 0x08 +#define LOCHNAGAR1_GF_AIF3_ENA_SHIFT 3 +#define LOCHNAGAR1_GF_AIF1_LRCLK_DIR_MASK 0x04 +#define LOCHNAGAR1_GF_AIF1_LRCLK_DIR_SHIFT 2 +#define LOCHNAGAR1_GF_AIF1_BCLK_DIR_MASK 0x02 +#define LOCHNAGAR1_GF_AIF1_BCLK_DIR_SHIFT 1 +#define LOCHNAGAR1_GF_AIF1_ENA_MASK 0x01 +#define LOCHNAGAR1_GF_AIF1_ENA_SHIFT 0 + +/* (0x0015) GF_AIF2 */ +#define LOCHNAGAR1_GF_AIF4_LRCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_GF_AIF4_LRCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_GF_AIF4_BCLK_DIR_MASK 0x10 +#define LOCHNAGAR1_GF_AIF4_BCLK_DIR_SHIFT 4 +#define LOCHNAGAR1_GF_AIF4_ENA_MASK 0x08 +#define LOCHNAGAR1_GF_AIF4_ENA_SHIFT 3 +#define LOCHNAGAR1_GF_AIF2_LRCLK_DIR_MASK 0x04 +#define LOCHNAGAR1_GF_AIF2_LRCLK_DIR_SHIFT 2 +#define LOCHNAGAR1_GF_AIF2_BCLK_DIR_MASK 0x02 +#define LOCHNAGAR1_GF_AIF2_BCLK_DIR_SHIFT 1 +#define LOCHNAGAR1_GF_AIF2_ENA_MASK 0x01 +#define LOCHNAGAR1_GF_AIF2_ENA_SHIFT 0 + +/* (0x0016) PSIA_AIF */ +#define LOCHNAGAR1_PSIA2_LRCLK_DIR_MASK 0x40 +#define LOCHNAGAR1_PSIA2_LRCLK_DIR_SHIFT 6 +#define LOCHNAGAR1_PSIA2_BCLK_DIR_MASK 0x20 +#define LOCHNAGAR1_PSIA2_BCLK_DIR_SHIFT 5 +#define LOCHNAGAR1_PSIA2_ENA_MASK 0x10 +#define LOCHNAGAR1_PSIA2_ENA_SHIFT 4 +#define LOCHNAGAR1_PSIA1_LRCLK_DIR_MASK 0x04 +#define LOCHNAGAR1_PSIA1_LRCLK_DIR_SHIFT 2 +#define LOCHNAGAR1_PSIA1_BCLK_DIR_MASK 0x02 +#define LOCHNAGAR1_PSIA1_BCLK_DIR_SHIFT 1 +#define LOCHNAGAR1_PSIA1_ENA_MASK 0x01 +#define LOCHNAGAR1_PSIA1_ENA_SHIFT 0 + +/* (0x0029) RST */ +#define LOCHNAGAR1_DSP_RESET_MASK 0x02 +#define LOCHNAGAR1_DSP_RESET_SHIFT 1 +#define LOCHNAGAR1_CDC_RESET_MASK 0x01 +#define LOCHNAGAR1_CDC_RESET_SHIFT 0 + +/* (0x0046) I2C_CTRL */ +#define LOCHNAGAR1_CDC_CIF_MODE_MASK 0x01 +#define LOCHNAGAR1_CDC_CIF_MODE_SHIFT 0 + +#endif diff --git a/include/linux/mfd/lochnagar2_regs.h b/include/linux/mfd/lochnagar2_regs.h new file mode 100644 index 000000000000..419b25a332fd --- /dev/null +++ b/include/linux/mfd/lochnagar2_regs.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Lochnagar2 register definitions + * + * Copyright (c) 2017-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#ifndef LOCHNAGAR2_REGISTERS_H +#define LOCHNAGAR2_REGISTERS_H + +/* Register Addresses */ +#define LOCHNAGAR2_CDC_AIF1_CTRL 0x000D +#define LOCHNAGAR2_CDC_AIF2_CTRL 0x000E +#define LOCHNAGAR2_CDC_AIF3_CTRL 0x000F +#define LOCHNAGAR2_DSP_AIF1_CTRL 0x0010 +#define LOCHNAGAR2_DSP_AIF2_CTRL 0x0011 +#define LOCHNAGAR2_PSIA1_CTRL 0x0012 +#define LOCHNAGAR2_PSIA2_CTRL 0x0013 +#define LOCHNAGAR2_GF_AIF3_CTRL 0x0014 +#define LOCHNAGAR2_GF_AIF4_CTRL 0x0015 +#define LOCHNAGAR2_GF_AIF1_CTRL 0x0016 +#define LOCHNAGAR2_GF_AIF2_CTRL 0x0017 +#define LOCHNAGAR2_SPDIF_AIF_CTRL 0x0018 +#define LOCHNAGAR2_USB_AIF1_CTRL 0x0019 +#define LOCHNAGAR2_USB_AIF2_CTRL 0x001A +#define LOCHNAGAR2_ADAT_AIF_CTRL 0x001B +#define LOCHNAGAR2_CDC_MCLK1_CTRL 0x001E +#define LOCHNAGAR2_CDC_MCLK2_CTRL 0x001F +#define LOCHNAGAR2_DSP_CLKIN_CTRL 0x0020 +#define LOCHNAGAR2_PSIA1_MCLK_CTRL 0x0021 +#define LOCHNAGAR2_PSIA2_MCLK_CTRL 0x0022 +#define LOCHNAGAR2_SPDIF_MCLK_CTRL 0x0023 +#define LOCHNAGAR2_GF_CLKOUT1_CTRL 0x0024 +#define LOCHNAGAR2_GF_CLKOUT2_CTRL 0x0025 +#define LOCHNAGAR2_ADAT_MCLK_CTRL 0x0026 +#define LOCHNAGAR2_SOUNDCARD_MCLK_CTRL 0x0027 +#define LOCHNAGAR2_GPIO_FPGA_GPIO1 0x0031 +#define LOCHNAGAR2_GPIO_FPGA_GPIO2 0x0032 +#define LOCHNAGAR2_GPIO_FPGA_GPIO3 0x0033 +#define LOCHNAGAR2_GPIO_FPGA_GPIO4 0x0034 +#define LOCHNAGAR2_GPIO_FPGA_GPIO5 0x0035 +#define LOCHNAGAR2_GPIO_FPGA_GPIO6 0x0036 +#define LOCHNAGAR2_GPIO_CDC_GPIO1 0x0037 +#define LOCHNAGAR2_GPIO_CDC_GPIO2 0x0038 +#define LOCHNAGAR2_GPIO_CDC_GPIO3 0x0039 +#define LOCHNAGAR2_GPIO_CDC_GPIO4 0x003A +#define LOCHNAGAR2_GPIO_CDC_GPIO5 0x003B +#define LOCHNAGAR2_GPIO_CDC_GPIO6 0x003C +#define LOCHNAGAR2_GPIO_CDC_GPIO7 0x003D +#define LOCHNAGAR2_GPIO_CDC_GPIO8 0x003E +#define LOCHNAGAR2_GPIO_DSP_GPIO1 0x003F +#define LOCHNAGAR2_GPIO_DSP_GPIO2 0x0040 +#define LOCHNAGAR2_GPIO_DSP_GPIO3 0x0041 +#define LOCHNAGAR2_GPIO_DSP_GPIO4 0x0042 +#define LOCHNAGAR2_GPIO_DSP_GPIO5 0x0043 +#define LOCHNAGAR2_GPIO_DSP_GPIO6 0x0044 +#define LOCHNAGAR2_GPIO_GF_GPIO2 0x0045 +#define LOCHNAGAR2_GPIO_GF_GPIO3 0x0046 +#define LOCHNAGAR2_GPIO_GF_GPIO7 0x0047 +#define LOCHNAGAR2_GPIO_CDC_AIF1_BCLK 0x0048 +#define LOCHNAGAR2_GPIO_CDC_AIF1_RXDAT 0x0049 +#define LOCHNAGAR2_GPIO_CDC_AIF1_LRCLK 0x004A +#define LOCHNAGAR2_GPIO_CDC_AIF1_TXDAT 0x004B +#define LOCHNAGAR2_GPIO_CDC_AIF2_BCLK 0x004C +#define LOCHNAGAR2_GPIO_CDC_AIF2_RXDAT 0x004D +#define LOCHNAGAR2_GPIO_CDC_AIF2_LRCLK 0x004E +#define LOCHNAGAR2_GPIO_CDC_AIF2_TXDAT 0x004F +#define LOCHNAGAR2_GPIO_CDC_AIF3_BCLK 0x0050 +#define LOCHNAGAR2_GPIO_CDC_AIF3_RXDAT 0x0051 +#define LOCHNAGAR2_GPIO_CDC_AIF3_LRCLK 0x0052 +#define LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT 0x0053 +#define LOCHNAGAR2_GPIO_DSP_AIF1_BCLK 0x0054 +#define LOCHNAGAR2_GPIO_DSP_AIF1_RXDAT 0x0055 +#define LOCHNAGAR2_GPIO_DSP_AIF1_LRCLK 0x0056 +#define LOCHNAGAR2_GPIO_DSP_AIF1_TXDAT 0x0057 +#define LOCHNAGAR2_GPIO_DSP_AIF2_BCLK 0x0058 +#define LOCHNAGAR2_GPIO_DSP_AIF2_RXDAT 0x0059 +#define LOCHNAGAR2_GPIO_DSP_AIF2_LRCLK 0x005A +#define LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT 0x005B +#define LOCHNAGAR2_GPIO_PSIA1_BCLK 0x005C +#define LOCHNAGAR2_GPIO_PSIA1_RXDAT 0x005D +#define LOCHNAGAR2_GPIO_PSIA1_LRCLK 0x005E +#define LOCHNAGAR2_GPIO_PSIA1_TXDAT 0x005F +#define LOCHNAGAR2_GPIO_PSIA2_BCLK 0x0060 +#define LOCHNAGAR2_GPIO_PSIA2_RXDAT 0x0061 +#define LOCHNAGAR2_GPIO_PSIA2_LRCLK 0x0062 +#define LOCHNAGAR2_GPIO_PSIA2_TXDAT 0x0063 +#define LOCHNAGAR2_GPIO_GF_AIF3_BCLK 0x0064 +#define LOCHNAGAR2_GPIO_GF_AIF3_RXDAT 0x0065 +#define LOCHNAGAR2_GPIO_GF_AIF3_LRCLK 0x0066 +#define LOCHNAGAR2_GPIO_GF_AIF3_TXDAT 0x0067 +#define LOCHNAGAR2_GPIO_GF_AIF4_BCLK 0x0068 +#define LOCHNAGAR2_GPIO_GF_AIF4_RXDAT 0x0069 +#define LOCHNAGAR2_GPIO_GF_AIF4_LRCLK 0x006A +#define LOCHNAGAR2_GPIO_GF_AIF4_TXDAT 0x006B +#define LOCHNAGAR2_GPIO_GF_AIF1_BCLK 0x006C +#define LOCHNAGAR2_GPIO_GF_AIF1_RXDAT 0x006D +#define LOCHNAGAR2_GPIO_GF_AIF1_LRCLK 0x006E +#define LOCHNAGAR2_GPIO_GF_AIF1_TXDAT 0x006F +#define LOCHNAGAR2_GPIO_GF_AIF2_BCLK 0x0070 +#define LOCHNAGAR2_GPIO_GF_AIF2_RXDAT 0x0071 +#define LOCHNAGAR2_GPIO_GF_AIF2_LRCLK 0x0072 +#define LOCHNAGAR2_GPIO_GF_AIF2_TXDAT 0x0073 +#define LOCHNAGAR2_GPIO_DSP_UART1_RX 0x0074 +#define LOCHNAGAR2_GPIO_DSP_UART1_TX 0x0075 +#define LOCHNAGAR2_GPIO_DSP_UART2_RX 0x0076 +#define LOCHNAGAR2_GPIO_DSP_UART2_TX 0x0077 +#define LOCHNAGAR2_GPIO_GF_UART2_RX 0x0078 +#define LOCHNAGAR2_GPIO_GF_UART2_TX 0x0079 +#define LOCHNAGAR2_GPIO_USB_UART_RX 0x007A +#define LOCHNAGAR2_GPIO_CDC_PDMCLK1 0x007C +#define LOCHNAGAR2_GPIO_CDC_PDMDAT1 0x007D +#define LOCHNAGAR2_GPIO_CDC_PDMCLK2 0x007E +#define LOCHNAGAR2_GPIO_CDC_PDMDAT2 0x007F +#define LOCHNAGAR2_GPIO_CDC_DMICCLK1 0x0080 +#define LOCHNAGAR2_GPIO_CDC_DMICDAT1 0x0081 +#define LOCHNAGAR2_GPIO_CDC_DMICCLK2 0x0082 +#define LOCHNAGAR2_GPIO_CDC_DMICDAT2 0x0083 +#define LOCHNAGAR2_GPIO_CDC_DMICCLK3 0x0084 +#define LOCHNAGAR2_GPIO_CDC_DMICDAT3 0x0085 +#define LOCHNAGAR2_GPIO_CDC_DMICCLK4 0x0086 +#define LOCHNAGAR2_GPIO_CDC_DMICDAT4 0x0087 +#define LOCHNAGAR2_GPIO_DSP_DMICCLK1 0x0088 +#define LOCHNAGAR2_GPIO_DSP_DMICDAT1 0x0089 +#define LOCHNAGAR2_GPIO_DSP_DMICCLK2 0x008A +#define LOCHNAGAR2_GPIO_DSP_DMICDAT2 0x008B +#define LOCHNAGAR2_GPIO_I2C2_SCL 0x008C +#define LOCHNAGAR2_GPIO_I2C2_SDA 0x008D +#define LOCHNAGAR2_GPIO_I2C3_SCL 0x008E +#define LOCHNAGAR2_GPIO_I2C3_SDA 0x008F +#define LOCHNAGAR2_GPIO_I2C4_SCL 0x0090 +#define LOCHNAGAR2_GPIO_I2C4_SDA 0x0091 +#define LOCHNAGAR2_GPIO_DSP_STANDBY 0x0092 +#define LOCHNAGAR2_GPIO_CDC_MCLK1 0x0093 +#define LOCHNAGAR2_GPIO_CDC_MCLK2 0x0094 +#define LOCHNAGAR2_GPIO_DSP_CLKIN 0x0095 +#define LOCHNAGAR2_GPIO_PSIA1_MCLK 0x0096 +#define LOCHNAGAR2_GPIO_PSIA2_MCLK 0x0097 +#define LOCHNAGAR2_GPIO_GF_GPIO1 0x0098 +#define LOCHNAGAR2_GPIO_GF_GPIO5 0x0099 +#define LOCHNAGAR2_GPIO_DSP_GPIO20 0x009A +#define LOCHNAGAR2_GPIO_CHANNEL1 0x00B9 +#define LOCHNAGAR2_GPIO_CHANNEL2 0x00BA +#define LOCHNAGAR2_GPIO_CHANNEL3 0x00BB +#define LOCHNAGAR2_GPIO_CHANNEL4 0x00BC +#define LOCHNAGAR2_GPIO_CHANNEL5 0x00BD +#define LOCHNAGAR2_GPIO_CHANNEL6 0x00BE +#define LOCHNAGAR2_GPIO_CHANNEL7 0x00BF +#define LOCHNAGAR2_GPIO_CHANNEL8 0x00C0 +#define LOCHNAGAR2_GPIO_CHANNEL9 0x00C1 +#define LOCHNAGAR2_GPIO_CHANNEL10 0x00C2 +#define LOCHNAGAR2_GPIO_CHANNEL11 0x00C3 +#define LOCHNAGAR2_GPIO_CHANNEL12 0x00C4 +#define LOCHNAGAR2_GPIO_CHANNEL13 0x00C5 +#define LOCHNAGAR2_GPIO_CHANNEL14 0x00C6 +#define LOCHNAGAR2_GPIO_CHANNEL15 0x00C7 +#define LOCHNAGAR2_GPIO_CHANNEL16 0x00C8 +#define LOCHNAGAR2_MINICARD_RESETS 0x00DF +#define LOCHNAGAR2_ANALOGUE_PATH_CTRL1 0x00E3 +#define LOCHNAGAR2_ANALOGUE_PATH_CTRL2 0x00E4 +#define LOCHNAGAR2_COMMS_CTRL4 0x00F0 +#define LOCHNAGAR2_SPDIF_CTRL 0x00FE +#define LOCHNAGAR2_IMON_CTRL1 0x0108 +#define LOCHNAGAR2_IMON_CTRL2 0x0109 +#define LOCHNAGAR2_IMON_CTRL3 0x010A +#define LOCHNAGAR2_IMON_CTRL4 0x010B +#define LOCHNAGAR2_IMON_DATA1 0x010C +#define LOCHNAGAR2_IMON_DATA2 0x010D +#define LOCHNAGAR2_POWER_CTRL 0x0116 +#define LOCHNAGAR2_MICVDD_CTRL1 0x0119 +#define LOCHNAGAR2_MICVDD_CTRL2 0x011B +#define LOCHNAGAR2_VDDCORE_CDC_CTRL1 0x011E +#define LOCHNAGAR2_VDDCORE_CDC_CTRL2 0x0120 +#define LOCHNAGAR2_SOUNDCARD_AIF_CTRL 0x0180 + +/* (0x000D-0x001B, 0x0180) CDC_AIF1_CTRL - SOUNCARD_AIF_CTRL */ +#define LOCHNAGAR2_AIF_ENA_MASK 0x8000 +#define LOCHNAGAR2_AIF_ENA_SHIFT 15 +#define LOCHNAGAR2_AIF_LRCLK_DIR_MASK 0x4000 +#define LOCHNAGAR2_AIF_LRCLK_DIR_SHIFT 14 +#define LOCHNAGAR2_AIF_BCLK_DIR_MASK 0x2000 +#define LOCHNAGAR2_AIF_BCLK_DIR_SHIFT 13 +#define LOCHNAGAR2_AIF_SRC_MASK 0x00FF +#define LOCHNAGAR2_AIF_SRC_SHIFT 0 + +/* (0x001E - 0x0027) CDC_MCLK1_CTRL - SOUNDCARD_MCLK_CTRL */ +#define LOCHNAGAR2_CLK_ENA_MASK 0x8000 +#define LOCHNAGAR2_CLK_ENA_SHIFT 15 +#define LOCHNAGAR2_CLK_SRC_MASK 0x00FF +#define LOCHNAGAR2_CLK_SRC_SHIFT 0 + +/* (0x0031 - 0x009A) GPIO_FPGA_GPIO1 - GPIO_DSP_GPIO20 */ +#define LOCHNAGAR2_GPIO_SRC_MASK 0x00FF +#define LOCHNAGAR2_GPIO_SRC_SHIFT 0 + +/* (0x00B9 - 0x00C8) GPIO_CHANNEL1 - GPIO_CHANNEL16 */ +#define LOCHNAGAR2_GPIO_CHANNEL_STS_MASK 0x8000 +#define LOCHNAGAR2_GPIO_CHANNEL_STS_SHIFT 15 +#define LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK 0x00FF +#define LOCHNAGAR2_GPIO_CHANNEL_SRC_SHIFT 0 + +/* (0x00DF) MINICARD_RESETS */ +#define LOCHNAGAR2_DSP_RESET_MASK 0x0002 +#define LOCHNAGAR2_DSP_RESET_SHIFT 1 +#define LOCHNAGAR2_CDC_RESET_MASK 0x0001 +#define LOCHNAGAR2_CDC_RESET_SHIFT 0 + +/* (0x00E3) ANALOGUE_PATH_CTRL1 */ +#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK 0x8000 +#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_SHIFT 15 +#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK 0x4000 +#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_SHIFT 14 + +/* (0x00E4) ANALOGUE_PATH_CTRL2 */ +#define LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK 0x0080 +#define LOCHNAGAR2_P2_INPUT_BIAS_ENA_SHIFT 7 +#define LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK 0x0040 +#define LOCHNAGAR2_P1_INPUT_BIAS_ENA_SHIFT 6 +#define LOCHNAGAR2_P2_MICBIAS_SRC_MASK 0x0038 +#define LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT 3 +#define LOCHNAGAR2_P1_MICBIAS_SRC_MASK 0x0007 +#define LOCHNAGAR2_P1_MICBIAS_SRC_SHIFT 0 + +/* (0x00F0) COMMS_CTRL4 */ +#define LOCHNAGAR2_CDC_CIF1MODE_MASK 0x0001 +#define LOCHNAGAR2_CDC_CIF1MODE_SHIFT 0 + +/* (0x00FE) SPDIF_CTRL */ +#define LOCHNAGAR2_SPDIF_HWMODE_MASK 0x0008 +#define LOCHNAGAR2_SPDIF_HWMODE_SHIFT 3 +#define LOCHNAGAR2_SPDIF_RESET_MASK 0x0001 +#define LOCHNAGAR2_SPDIF_RESET_SHIFT 0 + +/* (0x0108) IMON_CTRL1 */ +#define LOCHNAGAR2_IMON_ENA_MASK 0x8000 +#define LOCHNAGAR2_IMON_ENA_SHIFT 15 +#define LOCHNAGAR2_IMON_MEASURED_CHANNELS_MASK 0x03FC +#define LOCHNAGAR2_IMON_MEASURED_CHANNELS_SHIFT 2 +#define LOCHNAGAR2_IMON_MODE_SEL_MASK 0x0003 +#define LOCHNAGAR2_IMON_MODE_SEL_SHIFT 0 + +/* (0x0109) IMON_CTRL2 */ +#define LOCHNAGAR2_IMON_FSR_MASK 0x03FF +#define LOCHNAGAR2_IMON_FSR_SHIFT 0 + +/* (0x010A) IMON_CTRL3 */ +#define LOCHNAGAR2_IMON_DONE_MASK 0x0004 +#define LOCHNAGAR2_IMON_DONE_SHIFT 2 +#define LOCHNAGAR2_IMON_CONFIGURE_MASK 0x0002 +#define LOCHNAGAR2_IMON_CONFIGURE_SHIFT 1 +#define LOCHNAGAR2_IMON_MEASURE_MASK 0x0001 +#define LOCHNAGAR2_IMON_MEASURE_SHIFT 0 + +/* (0x010B) IMON_CTRL4 */ +#define LOCHNAGAR2_IMON_DATA_REQ_MASK 0x0080 +#define LOCHNAGAR2_IMON_DATA_REQ_SHIFT 7 +#define LOCHNAGAR2_IMON_CH_SEL_MASK 0x0070 +#define LOCHNAGAR2_IMON_CH_SEL_SHIFT 4 +#define LOCHNAGAR2_IMON_DATA_RDY_MASK 0x0008 +#define LOCHNAGAR2_IMON_DATA_RDY_SHIFT 3 +#define LOCHNAGAR2_IMON_CH_SRC_MASK 0x0007 +#define LOCHNAGAR2_IMON_CH_SRC_SHIFT 0 + +/* (0x010C, 0x010D) IMON_DATA1, IMON_DATA2 */ +#define LOCHNAGAR2_IMON_DATA_MASK 0xFFFF +#define LOCHNAGAR2_IMON_DATA_SHIFT 0 + +/* (0x0116) POWER_CTRL */ +#define LOCHNAGAR2_PWR_ENA_MASK 0x0001 +#define LOCHNAGAR2_PWR_ENA_SHIFT 0 + +/* (0x0119) MICVDD_CTRL1 */ +#define LOCHNAGAR2_MICVDD_REG_ENA_MASK 0x8000 +#define LOCHNAGAR2_MICVDD_REG_ENA_SHIFT 15 + +/* (0x011B) MICVDD_CTRL2 */ +#define LOCHNAGAR2_MICVDD_VSEL_MASK 0x001F +#define LOCHNAGAR2_MICVDD_VSEL_SHIFT 0 + +/* (0x011E) VDDCORE_CDC_CTRL1 */ +#define LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK 0x8000 +#define LOCHNAGAR2_VDDCORE_CDC_REG_ENA_SHIFT 15 + +/* (0x0120) VDDCORE_CDC_CTRL2 */ +#define LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK 0x007F +#define LOCHNAGAR2_VDDCORE_CDC_VSEL_SHIFT 0 + +#endif From 10cffde4ad47cef7e8ab10bbb7a1feb9dc284a7a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 31 Jan 2019 12:32:12 +0800 Subject: [PATCH 1304/4277] mfd: at91-usart: Constify at91_usart_spi_subdev and at91_usart_serial_subdev They are never get changed, make them constant. While at it, fix indent as well. Signed-off-by: Axel Lin Signed-off-by: Lee Jones --- drivers/mfd/at91-usart.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c index d20747f612c1..684d3a8db661 100644 --- a/drivers/mfd/at91-usart.c +++ b/drivers/mfd/at91-usart.c @@ -15,15 +15,15 @@ #include #include -static struct mfd_cell at91_usart_spi_subdev = { - .name = "at91_usart_spi", - .of_compatible = "microchip,at91sam9g45-usart-spi", - }; +static const struct mfd_cell at91_usart_spi_subdev = { + .name = "at91_usart_spi", + .of_compatible = "microchip,at91sam9g45-usart-spi", +}; -static struct mfd_cell at91_usart_serial_subdev = { - .name = "atmel_usart_serial", - .of_compatible = "atmel,at91rm9200-usart-serial", - }; +static const struct mfd_cell at91_usart_serial_subdev = { + .name = "atmel_usart_serial", + .of_compatible = "atmel,at91rm9200-usart-serial", +}; static int at91_usart_mode_probe(struct platform_device *pdev) { From c0056bfe48ab8dfe383c56bc75d1f03260dc9b2a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 31 Jan 2019 12:32:13 +0800 Subject: [PATCH 1305/4277] mfd: at91-usart: No need to copy mfd_cell in probe Use pointer instead. Signed-off-by: Axel Lin Signed-off-by: Lee Jones --- drivers/mfd/at91-usart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c index 684d3a8db661..6a8351a4588e 100644 --- a/drivers/mfd/at91-usart.c +++ b/drivers/mfd/at91-usart.c @@ -27,17 +27,17 @@ static const struct mfd_cell at91_usart_serial_subdev = { static int at91_usart_mode_probe(struct platform_device *pdev) { - struct mfd_cell cell; + const struct mfd_cell *cell; u32 opmode = AT91_USART_MODE_SERIAL; device_property_read_u32(&pdev->dev, "atmel,usart-mode", &opmode); switch (opmode) { case AT91_USART_MODE_SPI: - cell = at91_usart_spi_subdev; + cell = &at91_usart_spi_subdev; break; case AT91_USART_MODE_SERIAL: - cell = at91_usart_serial_subdev; + cell = &at91_usart_serial_subdev; break; default: dev_err(&pdev->dev, "atmel,usart-mode has an invalid value %u\n", @@ -45,7 +45,7 @@ static int at91_usart_mode_probe(struct platform_device *pdev) return -EINVAL; } - return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, &cell, 1, + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, NULL); } From 66265e719b4603ef9a1b8a6c876bcb542c021496 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 6 Feb 2019 14:11:07 -0500 Subject: [PATCH 1306/4277] mfd: tps68470: Drop unused MODULE_DEVICE_TABLE The Kconfig currently controlling compilation of this code is: drivers/mfd/Kconfig:config MFD_TPS68470 drivers/mfd/Kconfig: bool "TI TPS68470 Power Management / LED chips" ...meaning that it currently is not being built as a module by anyone. Hence we remove the MODULE_DEVICE_TABLE since it is a no-op for non-modular code. There is no removal of here because there isn't one. Instead, it is relying on including that implicitly from an ACPI header. In cleaning up the ACPI instance of module.h (which also isn't strictly needed), then this mfd driver breaks when MODULE_DEVICE_TABLE becomes undefined here. The easiest dependency solution is to simply defer the ACPI cleanup until this change is present in mainline. Signed-off-by: Paul Gortmaker Signed-off-by: Lee Jones --- drivers/mfd/tps68470.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/tps68470.c b/drivers/mfd/tps68470.c index a5981a79b29a..4a4df4ffd18c 100644 --- a/drivers/mfd/tps68470.c +++ b/drivers/mfd/tps68470.c @@ -86,7 +86,6 @@ static const struct acpi_device_id tps68470_acpi_ids[] = { {"INT3472"}, {}, }; -MODULE_DEVICE_TABLE(acpi, tps68470_acpi_ids); static struct i2c_driver tps68470_driver = { .driver = { From c1778e5819a9784b5551cf8f7abd66571c1239e2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 6 Feb 2019 18:39:59 +0100 Subject: [PATCH 1307/4277] mfd: cros_ec_dev: Return number of bytes read with CROS_EC_DEV_IOCRDMEM While ioctls normally return a negative error or 0 on success, this is not the case for CROS_EC_DEV_IOCXCMD in the cros_ec driver, which returns the number of bytes read instead. However, CROS_EC_DEV_IOCRDMEM in the same driver does not return the number of byte read on success. This is both inconsistent and problematic, since the amount of returned data can be dynamic. Have it return the number of data bytes for consistency. Signed-off-by: Guenter Roeck Signed-off-by: Enric Balletbo i Serra Reviewed-by: Benson Leung Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index ed809fc97df8..d275deaecb12 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -224,7 +224,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) return -EFAULT; - return 0; + return num; } static long ec_device_ioctl(struct file *filp, unsigned int cmd, From 2e095ce7b6ecce2f3e2ff330527f12056ed1e1a1 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 7 Feb 2019 15:30:05 +0200 Subject: [PATCH 1308/4277] x86/PCI: Fixup RTIT_BAR of Intel Denverton Trace Hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Denverton's integration of the Intel(R) Trace Hub (for a reference and overview see Documentation/trace/intel_th.rst) the reported size of one of its resources (RTIT_BAR) doesn't match its actual size, which leads to overlaps with other devices' resources. In practice, it overlaps with XHCI MMIO space, which results in the xhci driver bailing out after seeing its registers as 0xffffffff, and perceived disappearance of all USB devices: intel_th_pci 0000:00:1f.7: enabling device (0004 -> 0006) xhci_hcd 0000:00:15.0: xHCI host controller not responding, assume dead xhci_hcd 0000:00:15.0: xHC not responding in xhci_irq, assume controller is dead xhci_hcd 0000:00:15.0: HC died; cleaning up usb 1-1: USB disconnect, device number 2 For this reason, we need to resize the RTIT_BAR on Denverton to its actual size, which in this case is 4MB. The corresponding erratum is DNV36 at the link below: DNV36. Processor Host Root Complex May Incorrectly Route Memory Accesses to Intel® Trace Hub Problem: The Intel® Trace Hub RTIT_BAR (B0:D31:F7 offset 20h) is reported as a 2KB memory range. Due to this erratum, the processor Host Root Complex will forward addresses from RTIT_BAR to RTIT_BAR + 4MB -1 to Intel® Trace Hub. Implication: Devices assigned within the RTIT_BAR to RTIT_BAR + 4MB -1 space may not function correctly. Workaround: A BIOS code change has been identified and may be implemented as a workaround for this erratum. Status: No Fix. Note that 5118ccd34780 ("intel_th: pci: Add Denverton SOC support") updates the Trace Hub driver so it claims the Denverton device, but the resource overlap exists regardless of whether that driver is loaded or that commit is included. Link: https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/atom-c3000-family-spec-update.pdf Signed-off-by: Alexander Shishkin [bhelgaas: include erratum text, clarify relationship with 5118ccd34780] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- arch/x86/pci/fixup.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 30a5111ae5fd..527e69b12002 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -635,6 +635,22 @@ static void quirk_no_aersid(struct pci_dev *pdev) DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, 8, quirk_no_aersid); +static void quirk_intel_th_dnv(struct pci_dev *dev) +{ + struct resource *r = &dev->resource[4]; + + /* + * Denverton reports 2k of RTIT_BAR (intel_th resource 4), which + * appears to be 4 MB in reality. + */ + if (r->end == r->start + 0x7ff) { + r->start = 0; + r->end = 0x3fffff; + r->flags |= IORESOURCE_UNSET; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x19e1, quirk_intel_th_dnv); + #ifdef CONFIG_PHYS_ADDR_T_64BIT #define AMD_141b_MMIO_BASE(x) (0x80 + (x) * 0x8) From d88937624351202e5997fd21ae34e35c74a1abf2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Jan 2019 09:06:07 -0500 Subject: [PATCH 1309/4277] media: videobuf2: remove unused variable Commit 2cc1802f62e5 ("media: vb2: Keep dma-buf buffers mapped until they are freed") removed code leaving a local variable unused. Remove it to avoid a compiler warning. Fixes: 2cc1802f62e5 ("media: vb2: Keep dma-buf buffers mapped until they are freed") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index e07b6bdb6982..34cc87ca8d59 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1769,7 +1769,6 @@ EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers); static void __vb2_dqbuf(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; - unsigned int i; /* nothing to do if the buffer is already dequeued */ if (vb->state == VB2_BUF_STATE_DEQUEUED) From 801efd0f7522244b5c09fc4c6630dc55fb37c26c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 28 Jan 2019 06:37:11 -0500 Subject: [PATCH 1310/4277] media: imx-pxp: fix duplicated if condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a copy&paste error to make RGB -> BT.2020 YUV conversion actually selectable. Fixes the following warning: drivers/media/platform/imx-pxp.c:683:24: warning: duplicated ‘if’ condition [-Wduplicated-cond] Fixes: 51abcf7fdb70 ("media: imx-pxp: add i.MX Pixel Pipeline driver") Reported-by: David Binderman Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/imx-pxp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c index c1c255408d16..f087dc4fc729 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/imx-pxp.c @@ -680,7 +680,7 @@ static void pxp_setup_csc(struct pxp_ctx *ctx) csc2_coef = csc2_coef_rec709_full; else csc2_coef = csc2_coef_rec709_lim; - } else if (ycbcr_enc == V4L2_YCBCR_ENC_709) { + } else if (ycbcr_enc == V4L2_YCBCR_ENC_BT2020) { if (quantization == V4L2_QUANTIZATION_FULL_RANGE) csc2_coef = csc2_coef_bt2020_full; else From db9a01b32ca9406394e6e7d19c4cdfcf1cbb5058 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 02:33:41 -0500 Subject: [PATCH 1311/4277] media: vicodec: check type in g/s_selection Check that the selection buf_type is valid before calling get_q_data() to avoid hitting the WARN(1) in that function if the buffer type is not valid. Signed-off-by: Hans Verkuil Reported-by: syzbot+44b24cff6bf96006ecfa@syzkaller.appspotmail.com Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 3703b587e25e..cda348114764 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -953,6 +953,9 @@ static int vidioc_g_selection(struct file *file, void *priv, valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; } + if (s->type != valid_cap_type && s->type != valid_out_type) + return -EINVAL; + q_data = get_q_data(ctx, s->type); if (!q_data) return -EINVAL; @@ -994,12 +997,14 @@ static int vidioc_s_selection(struct file *file, void *priv, if (multiplanar) out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (s->type != out_type) + return -EINVAL; + q_data = get_q_data(ctx, s->type); if (!q_data) return -EINVAL; - if (!ctx->is_enc || s->type != out_type || - s->target != V4L2_SEL_TGT_CROP) + if (!ctx->is_enc || s->target != V4L2_SEL_TGT_CROP) return -EINVAL; s->r.left = 0; From 0247c75b19c0e9554307d842fa1ad914d59a41b1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 08:38:27 -0500 Subject: [PATCH 1312/4277] media: vicodec: fill in bus_info in media_device_info It is good practice to fill in bus_info. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index cda348114764..b5a9dee94c91 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -1669,6 +1669,8 @@ static int vicodec_probe(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; strscpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model)); + strscpy(dev->mdev.bus_info, "platform:vicodec", + sizeof(dev->mdev.bus_info)); media_device_init(&dev->mdev); dev->v4l2_dev.mdev = &dev->mdev; #endif From 281ae39b60b798c12405bfc09c2cb563790ab672 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 08:39:17 -0500 Subject: [PATCH 1313/4277] media: vim2m: fill in bus_info in media_device_info It is good practice to fill in the bus_info. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index e31c14c7d37f..bfa1a2a16009 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1159,6 +1159,8 @@ static int vim2m_probe(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model)); + strscpy(dev->mdev.bus_info, "platform:vim2m", + sizeof(dev->mdev.bus_info)); media_device_init(&dev->mdev); dev->mdev.ops = &m2m_media_ops; dev->v4l2_dev.mdev = &dev->mdev; From 69a9005789ad18806490bd8dc70dd6458319cd28 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Jan 2019 08:21:44 -0500 Subject: [PATCH 1314/4277] media: vicodec: support SOURCE_CHANGE event for decoders only The SOURCE_CHANGE event is decoder specific, so don't allow it for encoders. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index b5a9dee94c91..212da185e4f2 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -1118,9 +1118,14 @@ static int vicodec_enum_framesizes(struct file *file, void *fh, static int vicodec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { + struct vicodec_ctx *ctx = container_of(fh, struct vicodec_ctx, fh); + switch (sub->type) { - case V4L2_EVENT_EOS: case V4L2_EVENT_SOURCE_CHANGE: + if (ctx->is_enc) + return -EINVAL; + /* fall through */ + case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 0, NULL); default: return v4l2_ctrl_subscribe_event(fh, sub); From 47bb117911b051bbc90764a8bff96543cbd2005f Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 18 Dec 2018 20:32:48 -0500 Subject: [PATCH 1315/4277] media: uvcvideo: Fix 'type' check leading to overflow When initially testing the Camera Terminal Descriptor wTerminalType field (buffer[4]), no mask is used. Later in the function, the MSB is overloaded to store the descriptor subtype, and so a mask of 0x7fff is used to check the type. If a descriptor is specially crafted to set this overloaded bit in the original wTerminalType field, the initial type check will fail (falling through, without adjusting the buffer size), but the later type checks will pass, assuming the buffer has been made suitably large, causing an overflow. Avoid this problem by checking for the MSB in the wTerminalType field. If the bit is set, assume the descriptor is bad, and abort parsing it. Originally reported here: https://groups.google.com/forum/#!topic/syzkaller/Ot1fOE6v1d8 A similar (non-compiling) patch was provided at that time. Reported-by: syzbot Signed-off-by: Alistair Strachan Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index b62cbd800111..33a22c016456 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1106,11 +1106,19 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - /* Make sure the terminal type MSB is not null, otherwise it - * could be confused with a unit. + /* + * Reject invalid terminal types that would cause issues: + * + * - The high byte must be non-zero, otherwise it would be + * confused with a unit. + * + * - Bit 15 must be 0, as we use it internally as a terminal + * direction flag. + * + * Other unknown types are accepted. */ type = get_unaligned_le16(&buffer[4]); - if ((type & 0xff00) == 0) { + if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL %d has invalid " "type 0x%04x, skipping\n", udev->devnum, From 9dd0627d8d62a7ddb001a75f63942d92b5336561 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 30 Jan 2019 05:09:41 -0500 Subject: [PATCH 1316/4277] media: uvcvideo: Avoid NULL pointer dereference at the end of streaming The UVC video driver converts the timestamp from hardware specific unit to one known by the kernel at the time when the buffer is dequeued. This is fine in general, but the streamoff operation consists of the following steps (among other things): 1. uvc_video_clock_cleanup --- the hardware clock sample array is released and the pointer to the array is set to NULL, 2. buffers in active state are returned to the user and 3. buf_finish callback is called on buffers that are prepared. buf_finish includes calling uvc_video_clock_update that accesses the hardware clock sample array. The above is serialised by a queue specific mutex. Address the problem by skipping the clock conversion if the hardware clock sample array is already released. Fixes: 9c0863b1cc48 ("[media] vb2: call buf_finish from __queue_cancel") Reported-by: Chiranjeevi Rapolu Tested-by: Chiranjeevi Rapolu Signed-off-by: Sakari Ailus Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_video.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 84525ff04745..e314657a1843 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -676,6 +676,14 @@ void uvc_video_clock_update(struct uvc_streaming *stream, if (!uvc_hw_timestamps_param) return; + /* + * We will get called from __vb2_queue_cancel() if there are buffers + * done but not dequeued by the user, but the sample array has already + * been released at that time. Just bail out in that case. + */ + if (!clock->samples) + return; + spin_lock_irqsave(&clock->lock, flags); if (clock->count < clock->size) From 041bc40662a162eff940676a379cc783cb846f32 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Wed, 16 Jan 2019 12:18:46 -0500 Subject: [PATCH 1317/4277] media: ipu3-imgu: Use MENU type for mode control This addresses the below TODO item. - Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari) Signed-off-by: Yong Zhi Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/TODO | 2 -- drivers/staging/media/ipu3/include/intel-ipu3.h | 6 ------ drivers/staging/media/ipu3/ipu3-v4l2.c | 15 +++++++++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index 905bbb190217..0dc9a2e79978 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -11,8 +11,6 @@ staging directory. - Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to imgu_v4l2_register(). (Sakari) -- Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari) - - IPU3 driver documentation (Laurent) Add diagram in driver rst to describe output capability. Comments on configuring v4l2 subdevs for CIO2 and ImgU. diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h index ec0b74829351..eb6f52aca992 100644 --- a/drivers/staging/media/ipu3/include/intel-ipu3.h +++ b/drivers/staging/media/ipu3/include/intel-ipu3.h @@ -16,12 +16,6 @@ #define V4L2_CID_INTEL_IPU3_BASE (V4L2_CID_USER_BASE + 0x10c0) #define V4L2_CID_INTEL_IPU3_MODE (V4L2_CID_INTEL_IPU3_BASE + 1) -/* custom ctrl to set pipe mode */ -enum ipu3_running_mode { - IPU3_RUNNING_MODE_VIDEO = 0, - IPU3_RUNNING_MODE_STILL = 1, -}; - /******************* ipu3_uapi_stats_3a *******************/ #define IPU3_UAPI_MAX_STRIPES 2 diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index c7936032beb9..e758a650ad2b 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -12,6 +12,9 @@ /******************** v4l2_subdev_ops ********************/ +#define IPU3_RUNNING_MODE_VIDEO 0 +#define IPU3_RUNNING_MODE_STILL 1 + static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imgu_v4l2_subdev *imgu_sd = container_of(sd, @@ -1035,15 +1038,19 @@ static const struct v4l2_ctrl_ops ipu3_subdev_ctrl_ops = { .s_ctrl = ipu3_sd_s_ctrl, }; +static const char * const ipu3_ctrl_mode_strings[] = { + "Video mode", + "Still mode", +}; + static const struct v4l2_ctrl_config ipu3_subdev_ctrl_mode = { .ops = &ipu3_subdev_ctrl_ops, .id = V4L2_CID_INTEL_IPU3_MODE, .name = "IPU3 Pipe Mode", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = IPU3_RUNNING_MODE_VIDEO, - .max = IPU3_RUNNING_MODE_STILL, - .step = 1, + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(ipu3_ctrl_mode_strings) - 1, .def = IPU3_RUNNING_MODE_VIDEO, + .qmenu = ipu3_ctrl_mode_strings, }; /******************** Framework registration ********************/ From 505ecd35182f1ba006ada118c85deca22b88ebcb Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Fri, 1 Feb 2019 12:23:37 -0500 Subject: [PATCH 1318/4277] media: ipu3-imgu: Remove dead code for NULL check Since ipu3_css_buf_dequeue() never returns NULL, remove the dead code to fix static checker warning: drivers/staging/media/ipu3/ipu3.c:493 imgu_isr_threaded() warn: 'b' is an error pointer or valid Reported-by: Dan Carpenter [Bug report: https://lore.kernel.org/linux-media/20190104122856.GA1169@kadam/] Signed-off-by: Yong Zhi Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index d521b3afb8b1..839d9398f8e9 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -489,12 +489,11 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) mutex_unlock(&imgu->lock); } while (PTR_ERR(b) == -EAGAIN); - if (IS_ERR_OR_NULL(b)) { - if (!b || PTR_ERR(b) == -EBUSY) /* All done */ - break; - dev_err(&imgu->pci_dev->dev, - "failed to dequeue buffers (%ld)\n", - PTR_ERR(b)); + if (IS_ERR(b)) { + if (PTR_ERR(b) != -EBUSY) /* All done */ + dev_err(&imgu->pci_dev->dev, + "failed to dequeue buffers (%ld)\n", + PTR_ERR(b)); break; } From 81a43d10b8ed0e165bbd1e134ca7261a9608f389 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Dec 2018 06:49:51 -0500 Subject: [PATCH 1319/4277] media: staging: intel-ipu3: fix unsigned comparison with < 0 The comparison css->pipes[pipe].bindex < 0 is always false because bindex is an unsigned int. Fix this by using a signed integer for the comparison. Detected by CoverityScan, CID#1476023 ("Unsigned compared against 0") Fixes: f5f2e4273518 ("media: staging/intel-ipu3: Add css pipeline programming") Signed-off-by: Colin Ian King Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3-css.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index 44c55639389a..b9354d2bb692 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -1751,7 +1751,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, &q[IPU3_CSS_QUEUE_OUT].fmt.mpix; struct v4l2_pix_format_mplane *const vf = &q[IPU3_CSS_QUEUE_VF].fmt.mpix; - int i, s; + int i, s, ret; /* Adjust all formats, get statistics buffer sizes and formats */ for (i = 0; i < IPU3_CSS_QUEUES; i++) { @@ -1826,12 +1826,12 @@ int ipu3_css_fmt_try(struct ipu3_css *css, s = (bds->height - gdc->height) / 2 - FILTER_SIZE; env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; - css->pipes[pipe].bindex = - ipu3_css_find_binary(css, pipe, q, r); - if (css->pipes[pipe].bindex < 0) { + ret = ipu3_css_find_binary(css, pipe, q, r); + if (ret < 0) { dev_err(css->dev, "failed to find suitable binary\n"); return -EINVAL; } + css->pipes[pipe].bindex = ret; dev_dbg(css->dev, "Binary index %d for pipe %d found.", css->pipes[pipe].bindex, pipe); From 63635b54e07f646aa1d8e54c96b099162a226f7d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:22 -0500 Subject: [PATCH 1320/4277] media: v4l2-event: keep track of the timestamp in ns Internally use ktime_get_ns() to get the timestamp of the event. Only convert to timespec when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-event.c | 19 +++++++++---------- include/media/v4l2-event.h | 2 ++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 481e3c65cf97..c46d14c996fc 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -52,6 +52,7 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) kev->event.pending = fh->navailable; *event = kev->event; + event->timestamp = ns_to_timespec(kev->ts); kev->sev->first = sev_pos(kev->sev, 1); kev->sev->in_use--; @@ -103,8 +104,8 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed( return NULL; } -static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev, - const struct timespec *ts) +static void __v4l2_event_queue_fh(struct v4l2_fh *fh, + const struct v4l2_event *ev, u64 ts) { struct v4l2_subscribed_event *sev; struct v4l2_kevent *kev; @@ -144,7 +145,7 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e if (copy_payload) kev->event.u = ev->u; kev->event.id = ev->id; - kev->event.timestamp = *ts; + kev->ts = ts; kev->event.sequence = fh->sequence; sev->in_use++; list_add_tail(&kev->list, &fh->available); @@ -158,17 +159,17 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) { struct v4l2_fh *fh; unsigned long flags; - struct timespec timestamp; + u64 ts; if (vdev == NULL) return; - ktime_get_ts(×tamp); + ts = ktime_get_ns(); spin_lock_irqsave(&vdev->fh_lock, flags); list_for_each_entry(fh, &vdev->fh_list, list) - __v4l2_event_queue_fh(fh, ev, ×tamp); + __v4l2_event_queue_fh(fh, ev, ts); spin_unlock_irqrestore(&vdev->fh_lock, flags); } @@ -177,12 +178,10 @@ EXPORT_SYMBOL_GPL(v4l2_event_queue); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev) { unsigned long flags; - struct timespec timestamp; - - ktime_get_ts(×tamp); + u64 ts = ktime_get_ns(); spin_lock_irqsave(&fh->vdev->fh_lock, flags); - __v4l2_event_queue_fh(fh, ev, ×tamp); + __v4l2_event_queue_fh(fh, ev, ts); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); } EXPORT_SYMBOL_GPL(v4l2_event_queue_fh); diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 17833e886e11..c2b6cdc714d2 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -34,11 +34,13 @@ struct video_device; * @list: List node for the v4l2_fh->available list. * @sev: Pointer to parent v4l2_subscribed_event. * @event: The event itself. + * @ts: The timestamp of the event. */ struct v4l2_kevent { struct list_head list; struct v4l2_subscribed_event *sev; struct v4l2_event event; + u64 ts; }; /** From 15a40b27beb0a85d7f11d747bfc587dbeb69a96c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:23 -0500 Subject: [PATCH 1321/4277] media: videobuf: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 8 +++----- drivers/media/pci/cx18/cx18-mailbox.c | 2 +- drivers/media/platform/davinci/vpfe_capture.c | 2 +- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/omap/omap_vout.c | 12 ++++++------ drivers/media/usb/cx231xx/cx231xx-417.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/zr364xx/zr364xx.c | 4 ++-- drivers/media/v4l2-core/videobuf-core.c | 4 ++-- include/media/videobuf-core.h | 2 +- 13 files changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index c790ae264464..be4355a4c126 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, } q->curr->vb.state = state; - v4l2_get_timestamp(&q->curr->vb.ts); + q->curr->vb.ts = ktime_get_ns(); wake_up(&q->curr->vb.done); q->curr = NULL; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index d09785fd37a8..5e769c09dbd0 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3600,9 +3600,7 @@ static void bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, struct bttv_buffer_set *curr, unsigned int state) { - struct timeval ts; - - v4l2_get_timestamp(&ts); + u64 ts = ktime_get_ns(); if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { @@ -3643,7 +3641,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, if (NULL == wakeup) return; - v4l2_get_timestamp(&wakeup->vb.ts); + wakeup->vb.ts = ktime_get_ns(); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; wake_up(&wakeup->vb.done); @@ -3713,7 +3711,7 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - v4l2_get_timestamp(&wakeup->vb.ts); + wakeup->vb.ts = ktime_get_ns(); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = VIDEOBUF_DONE; wake_up(&wakeup->vb.done); diff --git a/drivers/media/pci/cx18/cx18-mailbox.c b/drivers/media/pci/cx18/cx18-mailbox.c index f66dd63e1994..0ffd2196a980 100644 --- a/drivers/media/pci/cx18/cx18-mailbox.c +++ b/drivers/media/pci/cx18/cx18-mailbox.c @@ -197,7 +197,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, } if (dispatch) { - v4l2_get_timestamp(&vb_buf->vb.ts); + vb_buf->vb.ts = ktime_get_ns(); list_del(&vb_buf->vb.queue); vb_buf->vb.state = VIDEOBUF_DONE; wake_up(&vb_buf->vb.done); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 9996bab98fe3..26dadbba930f 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -518,7 +518,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) { - v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); + vpfe_dev->cur_frm->ts = ktime_get_ns(); vpfe_dev->cur_frm->state = VIDEOBUF_DONE; vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; wake_up_interruptible(&vpfe_dev->cur_frm->done); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index ca6d0317ab42..cffebcaacb90 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1090,7 +1090,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; wake_up(&buf->vb.done); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index f447ae3bb465..ff3de2dce5a2 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -513,7 +513,7 @@ static int omapvid_apply_changes(struct omap_vout_device *vout) } static int omapvid_handle_interlace_display(struct omap_vout_device *vout, - unsigned int irqstatus, struct timeval timevalue) + unsigned int irqstatus, u64 ts) { u32 fid; @@ -537,7 +537,7 @@ static int omapvid_handle_interlace_display(struct omap_vout_device *vout, if (vout->cur_frm == vout->next_frm) goto err; - vout->cur_frm->ts = timevalue; + vout->cur_frm->ts = ts; vout->cur_frm->state = VIDEOBUF_DONE; wake_up_interruptible(&vout->cur_frm->done); vout->cur_frm = vout->next_frm; @@ -557,7 +557,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) int ret, fid, mgr_id; u32 addr, irq; struct omap_overlay *ovl; - struct timeval timevalue; + u64 ts; struct omapvideo_info *ovid; struct omap_dss_device *cur_display; struct omap_vout_device *vout = (struct omap_vout_device *)arg; @@ -577,7 +577,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) return; spin_lock(&vout->vbq_lock); - v4l2_get_timestamp(&timevalue); + ts = ktime_get_ns(); switch (cur_display->type) { case OMAP_DISPLAY_TYPE_DSI: @@ -595,7 +595,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) break; case OMAP_DISPLAY_TYPE_VENC: fid = omapvid_handle_interlace_display(vout, irqstatus, - timevalue); + ts); if (!fid) goto vout_isr_err; break; @@ -608,7 +608,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) } if (!vout->first_int && (vout->cur_frm != vout->next_frm)) { - vout->cur_frm->ts = timevalue; + vout->cur_frm->ts = ts; vout->cur_frm->state = VIDEOBUF_DONE; wake_up_interruptible(&vout->cur_frm->done); vout->cur_frm = vout->next_frm; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 1c48c497bd6a..0f8ae81f4820 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1316,7 +1316,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); list_del(&buf->vb.queue); wake_up(&buf->vb.done); dma_q->mpeg_buffer_completed = 0; @@ -1347,7 +1347,7 @@ static void buffer_filled(char *data, int len, struct urb *urb, memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); list_del(&buf->vb.queue); wake_up(&buf->vb.done); } diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index 10b2eb7338ad..d16b73c04445 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -528,7 +528,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); dev->vbi_mode.bulk_ctl.buf = NULL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 0d451c4ea3b9..aebbaf9d92a6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -182,7 +182,7 @@ static inline void buffer_filled(struct cx231xx *dev, cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index ee7b5318b351..5127be71dd03 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -106,7 +106,7 @@ static inline void buffer_filled(struct tm6000_core *dev, dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index ab35554cbffa..51aad2cf742f 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -521,7 +521,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); buf->vb.state = VIDEOBUF_DONE; } @@ -549,7 +549,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) goto unlock; } list_del(&buf->vb.queue); - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); DBG("[%p/%d] wakeup\n", buf, buf->vb.i); zr364xx_fillbuff(cam, buf, jpgsize); wake_up(&buf->vb.done); diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 7491b337002c..d1bcfa91aaf8 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -367,7 +367,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, } b->field = vb->field; - b->timestamp = vb->ts; + b->timestamp = ns_to_timeval(vb->ts); b->bytesused = vb->size; b->sequence = vb->field_count >> 1; } @@ -581,7 +581,7 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) || q->type == V4L2_BUF_TYPE_SDR_OUTPUT) { buf->size = b->bytesused; buf->field = b->field; - buf->ts = b->timestamp; + buf->ts = v4l2_timeval_to_ns(&b->timestamp); } break; case V4L2_MEMORY_USERPTR: diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h index 60a664febba0..5684dc6f0d0d 100644 --- a/include/media/videobuf-core.h +++ b/include/media/videobuf-core.h @@ -80,7 +80,7 @@ struct videobuf_buffer { struct list_head queue; wait_queue_head_t done; unsigned int field_count; - struct timeval ts; + u64 ts; /* Memory type */ enum v4l2_memory memory; From cb7130ea67c4319bbf2d810f245ee6f89bbf6ae7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:24 -0500 Subject: [PATCH 1322/4277] media: meye: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 8 ++++---- drivers/media/pci/meye/meye.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index bd870e60c32b..896d2d856795 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -805,7 +805,7 @@ again: mchip_hsize() * mchip_vsize() * 2); meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); + meye.grab_buffer[reqnr].ts = ktime_get_ns(); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -826,7 +826,7 @@ again: size); meye.grab_buffer[reqnr].size = size; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); + meye.grab_buffer[reqnr].ts = ktime_get_ns(); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -1283,7 +1283,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->flags |= V4L2_BUF_FLAG_DONE; buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[index].timestamp; + buf->timestamp = ns_to_timeval(meye.grab_buffer[index].ts); buf->sequence = meye.grab_buffer[index].sequence; buf->memory = V4L2_MEMORY_MMAP; buf->m.offset = index * gbufsize; @@ -1349,7 +1349,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->bytesused = meye.grab_buffer[reqnr].size; buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[reqnr].timestamp; + buf->timestamp = ns_to_timeval(meye.grab_buffer[reqnr].ts); buf->sequence = meye.grab_buffer[reqnr].sequence; buf->memory = V4L2_MEMORY_MMAP; buf->m.offset = reqnr * gbufsize; diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index c4a8a5fe040c..0af868eb6210 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -277,7 +277,7 @@ struct meye_grab_buffer { int state; /* state of buffer */ unsigned long size; /* size of jpg frame */ - struct timeval timestamp; /* timestamp */ + u64 ts; /* timestamp */ unsigned long sequence; /* sequence number */ }; From 597f8e9c13644e4fcf4c1d01c2317c78b2e2df92 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:25 -0500 Subject: [PATCH 1323/4277] media: cpia2: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cpia2/cpia2.h | 2 +- drivers/media/usb/cpia2/cpia2_usb.c | 2 +- drivers/media/usb/cpia2/cpia2_v4l.c | 11 +++-------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/media/usb/cpia2/cpia2.h b/drivers/media/usb/cpia2/cpia2.h index ab238ac8bfc0..d0a464882510 100644 --- a/drivers/media/usb/cpia2/cpia2.h +++ b/drivers/media/usb/cpia2/cpia2.h @@ -350,7 +350,7 @@ struct cpia2_sbuf { }; struct framebuf { - struct timeval timestamp; + u64 ts; unsigned long seq; int num; int length; diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index a771e0a52610..e5d8dee38fe4 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -324,7 +324,7 @@ static void cpia2_usb_complete(struct urb *urb) continue; } DBG("Start of frame pattern found\n"); - v4l2_get_timestamp(&cam->workbuff->timestamp); + cam->workbuff->ts = ktime_get_ns(); cam->workbuff->seq = cam->frame_count++; cam->workbuff->data[0] = 0xFF; cam->workbuff->data[1] = 0xD8; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index 748739c2b8b2..95c0bd4a19dc 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -833,7 +833,7 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) break; case FRAME_READY: buf->bytesused = cam->buffers[buf->index].length; - buf->timestamp = cam->buffers[buf->index].timestamp; + buf->timestamp = ns_to_timeval(cam->buffers[buf->index].ts); buf->sequence = cam->buffers[buf->index].seq; buf->flags = V4L2_BUF_FLAG_DONE; break; @@ -889,12 +889,7 @@ static int find_earliest_filled_buffer(struct camera_data *cam) found = i; } else { /* find which buffer is earlier */ - struct timeval *tv1, *tv2; - tv1 = &cam->buffers[i].timestamp; - tv2 = &cam->buffers[found].timestamp; - if(tv1->tv_sec < tv2->tv_sec || - (tv1->tv_sec == tv2->tv_sec && - tv1->tv_usec < tv2->tv_usec)) + if (cam->buffers[i].ts < cam->buffers[found].ts) found = i; } } @@ -945,7 +940,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; - buf->timestamp = cam->buffers[buf->index].timestamp; + buf->timestamp = ns_to_timeval(cam->buffers[buf->index].ts); buf->sequence = cam->buffers[buf->index].seq; buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; buf->length = cam->frame_size; From bbd217a44a7024018abdd2aee63c991ad3b01e4b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:26 -0500 Subject: [PATCH 1324/4277] media: stkwebcam: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stkwebcam/stk-webcam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index b8ec74d98e8d..03f5e12b13a5 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1144,7 +1144,7 @@ static int stk_vidioc_dqbuf(struct file *filp, sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; sbuf->v4lbuf.sequence = ++dev->sequence; - v4l2_get_timestamp(&sbuf->v4lbuf.timestamp); + sbuf->v4lbuf.timestamp = ns_to_timeval(ktime_get_ns()); *buf = sbuf->v4lbuf; return 0; From 459d2a5d1e696b6fb029df1be041380daff94032 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:27 -0500 Subject: [PATCH 1325/4277] media: usbvision: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-core.c | 2 +- drivers/media/usb/usbvision/usbvision-video.c | 4 ++-- drivers/media/usb/usbvision/usbvision.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 31e0e98d6daf..2b843a7b27a4 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1160,7 +1160,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) if (newstate == parse_state_next_frame) { frame->grabstate = frame_state_done; - v4l2_get_timestamp(&(frame->timestamp)); + frame->ts = ktime_get_ns(); frame->sequence = usbvision->frame_num; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index dd2ff8ed6c6a..e611052ebf59 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -706,7 +706,7 @@ static int vidioc_querybuf(struct file *file, vb->length = usbvision->curwidth * usbvision->curheight * usbvision->palette.bytes_per_pixel; - vb->timestamp = usbvision->frame[vb->index].timestamp; + vb->timestamp = ns_to_timeval(usbvision->frame[vb->index].ts); vb->sequence = usbvision->frame[vb->index].sequence; return 0; } @@ -775,7 +775,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb->index = f->index; vb->sequence = f->sequence; - vb->timestamp = f->timestamp; + vb->timestamp = ns_to_timeval(f->ts); vb->field = V4L2_FIELD_NONE; vb->bytesused = f->scanlength; diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index 017e7baf5747..d55088b4fd63 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -316,7 +316,7 @@ struct usbvision_frame { long bytes_read; /* amount of scanlength that has been read from data */ struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ int v4l2_linesize; /* bytes for one videoline*/ - struct timeval timestamp; + u64 ts; int sequence; /* How many video frames we send to user */ }; From 7b6b9a8c36b9264b8c31059506119a0aac1413ba Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:28 -0500 Subject: [PATCH 1326/4277] media: zoran: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/zoran/zoran.h | 2 +- drivers/staging/media/zoran/zoran_device.c | 4 ++-- drivers/staging/media/zoran/zoran_driver.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h index 9bb3c21aa275..e84fb604a689 100644 --- a/drivers/staging/media/zoran/zoran.h +++ b/drivers/staging/media/zoran/zoran.h @@ -35,7 +35,7 @@ struct zoran_sync { unsigned long frame; /* number of buffer that has been free'd */ unsigned long length; /* number of code bytes in buffer (capture only) */ unsigned long seq; /* frame sequence number */ - struct timeval timestamp; /* timestamp */ + u64 ts; /* timestamp */ }; diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c index 40adceebca7e..d393e7b8aeda 100644 --- a/drivers/staging/media/zoran/zoran_device.c +++ b/drivers/staging/media/zoran/zoran_device.c @@ -1151,7 +1151,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - v4l2_get_timestamp(&buffer->bs.timestamp); + buffer->bs.ts = ktime_get_ns(); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1389,7 +1389,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.ts = ktime_get_ns(); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 27c76e2eeb41..04f88f9d6bb4 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -1354,7 +1354,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, fh->buffers.buffer[num].state == BUZ_STATE_USER) { buf->sequence = fh->buffers.buffer[num].bs.seq; buf->flags |= V4L2_BUF_FLAG_DONE; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts); } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; } @@ -1388,7 +1388,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || fh->buffers.buffer[num].state == BUZ_STATE_USER) { buf->sequence = fh->buffers.buffer[num].bs.seq; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts); buf->bytesused = fh->buffers.buffer[num].bs.length; buf->flags |= V4L2_BUF_FLAG_DONE; } else { From 276c1f066bdaaed6fe82ed231e1eff2f41c34882 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:29 -0500 Subject: [PATCH 1327/4277] media: v4l2-common: drop v4l2_get_timestamp This function is no longer used, so drop it. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 10 ---------- include/media/v4l2-common.h | 9 --------- 2 files changed, 19 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 50763fb42a1b..663730f088cd 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -398,16 +398,6 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, } EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size); -void v4l2_get_timestamp(struct timeval *tv) -{ - struct timespec ts; - - ktime_get_ts(&ts); - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; -} -EXPORT_SYMBOL_GPL(v4l2_get_timestamp); - int v4l2_g_parm_cap(struct video_device *vdev, struct v4l2_subdev *sd, struct v4l2_streamparm *a) { diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 0c511ed8ffb0..2b93cb281fa5 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -361,15 +361,6 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, size_t entry_size, size_t width_offset, size_t height_offset, s32 width, s32 height); -/** - * v4l2_get_timestamp - helper routine to get a timestamp to be used when - * filling streaming metadata. Internally, it uses ktime_get_ts(), - * which is the recommended way to get it. - * - * @tv: pointer to &struct timeval to be filled. - */ -void v4l2_get_timestamp(struct timeval *tv); - /** * v4l2_g_parm_cap - helper routine for vidioc_g_parm to fill this in by * calling the g_frame_interval op of the given subdev. It only works From adc589d2a20808fb99d46a78175cd023f2040338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20A=2E=20M=2E=20Magalh=C3=A3es?= Date: Mon, 21 Jan 2019 20:05:01 -0500 Subject: [PATCH 1328/4277] media: vimc: Add vimc-streamer for stream control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a linear pipeline logic for the stream control. It's created by walking backwards on the entity graph. When the stream starts it will simply loop through the pipeline calling the respective process_frame function of each entity. Fixes: f2fe89061d797 ("vimc: Virtual Media Controller core, capture and sensor") Cc: stable@vger.kernel.org # for v4.20 Signed-off-by: Lucas A. M. Magalhães Acked-by: Helen Koike Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: fixed small space-after-tab issue in the patch] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/Makefile | 3 +- drivers/media/platform/vimc/vimc-capture.c | 18 +- drivers/media/platform/vimc/vimc-common.c | 35 ---- drivers/media/platform/vimc/vimc-common.h | 15 +- drivers/media/platform/vimc/vimc-debayer.c | 26 +-- drivers/media/platform/vimc/vimc-scaler.c | 28 +-- drivers/media/platform/vimc/vimc-sensor.c | 56 ++---- drivers/media/platform/vimc/vimc-streamer.c | 188 ++++++++++++++++++++ drivers/media/platform/vimc/vimc-streamer.h | 38 ++++ 9 files changed, 260 insertions(+), 147 deletions(-) create mode 100644 drivers/media/platform/vimc/vimc-streamer.c create mode 100644 drivers/media/platform/vimc/vimc-streamer.h diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile index 4b2e3de7856e..c4fc8e7d365a 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/platform/vimc/Makefile @@ -5,6 +5,7 @@ vimc_common-objs := vimc-common.o vimc_debayer-objs := vimc-debayer.o vimc_scaler-objs := vimc-scaler.o vimc_sensor-objs := vimc-sensor.o +vimc_streamer-objs := vimc-streamer.o obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \ - vimc_scaler.o vimc_sensor.o + vimc_scaler.o vimc_sensor.o vimc_streamer.o diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index aaeddf24b042..93837d9eecd2 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -24,6 +24,7 @@ #include #include "vimc-common.h" +#include "vimc-streamer.h" #define VIMC_CAP_DRV_NAME "vimc-capture" @@ -44,7 +45,7 @@ struct vimc_cap_device { spinlock_t qlock; struct mutex lock; u32 sequence; - struct media_pipeline pipe; + struct vimc_stream stream; }; static const struct v4l2_pix_format fmt_default = { @@ -248,14 +249,13 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) vcap->sequence = 0; /* Start the media pipeline */ - ret = media_pipeline_start(entity, &vcap->pipe); + ret = media_pipeline_start(entity, &vcap->stream.pipe); if (ret) { vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); return ret; } - /* Enable streaming from the pipe */ - ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1); + ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1); if (ret) { media_pipeline_stop(entity); vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); @@ -273,8 +273,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq) { struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); - /* Disable streaming from the pipe */ - vimc_pipeline_s_stream(&vcap->vdev.entity, 0); + vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0); /* Stop the media pipeline */ media_pipeline_stop(&vcap->vdev.entity); @@ -355,8 +354,8 @@ static void vimc_cap_comp_unbind(struct device *comp, struct device *master, kfree(vcap); } -static void vimc_cap_process_frame(struct vimc_ent_device *ved, - struct media_pad *sink, const void *frame) +static void *vimc_cap_process_frame(struct vimc_ent_device *ved, + const void *frame) { struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, ved); @@ -370,7 +369,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved, typeof(*vimc_buf), list); if (!vimc_buf) { spin_unlock(&vcap->qlock); - return; + return ERR_PTR(-EAGAIN); } /* Remove this entry from the list */ @@ -391,6 +390,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved, vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0, vcap->format.sizeimage); vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); + return NULL; } static int vimc_cap_comp_bind(struct device *comp, struct device *master, diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 867e24dbd6b5..c1a74bb2df58 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -207,41 +207,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) } EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat); -int vimc_propagate_frame(struct media_pad *src, const void *frame) -{ - struct media_link *link; - - if (!(src->flags & MEDIA_PAD_FL_SOURCE)) - return -EINVAL; - - /* Send this frame to all sink pads that are direct linked */ - list_for_each_entry(link, &src->entity->links, list) { - if (link->source == src && - (link->flags & MEDIA_LNK_FL_ENABLED)) { - struct vimc_ent_device *ved = NULL; - struct media_entity *entity = link->sink->entity; - - if (is_media_entity_v4l2_subdev(entity)) { - struct v4l2_subdev *sd = - container_of(entity, struct v4l2_subdev, - entity); - ved = v4l2_get_subdevdata(sd); - } else if (is_media_entity_v4l2_video_device(entity)) { - struct video_device *vdev = - container_of(entity, - struct video_device, - entity); - ved = video_get_drvdata(vdev); - } - if (ved && ved->process_frame) - ved->process_frame(ved, link->sink, frame); - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(vimc_propagate_frame); - /* Helper function to allocate and initialize pads */ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) { diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index f491c33c7c14..84539430b5e7 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -115,23 +115,12 @@ struct vimc_pix_map { struct vimc_ent_device { struct media_entity *ent; struct media_pad *pads; - void (*process_frame)(struct vimc_ent_device *ved, - struct media_pad *sink, const void *frame); + void * (*process_frame)(struct vimc_ent_device *ved, + const void *frame); void (*vdev_get_format)(struct vimc_ent_device *ved, struct v4l2_pix_format *fmt); }; -/** - * vimc_propagate_frame - propagate a frame through the topology - * - * @src: the source pad where the frame is being originated - * @frame: the frame to be propagated - * - * This function will call the process_frame callback from the vimc_ent_device - * struct of the nodes directly connected to the @src pad - */ -int vimc_propagate_frame(struct media_pad *src, const void *frame); - /** * vimc_pads_init - initialize pads * diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 77887f66f323..7d77c63b99d2 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -321,7 +321,6 @@ static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb, static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - int ret; if (enable) { const struct vimc_pix_map *vpix; @@ -351,22 +350,10 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) if (!vdeb->src_frame) return -ENOMEM; - /* Turn the stream on in the subdevices directly connected */ - ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 1); - if (ret) { - vfree(vdeb->src_frame); - vdeb->src_frame = NULL; - return ret; - } } else { if (!vdeb->src_frame) return 0; - /* Disable streaming from the pipe */ - ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 0); - if (ret) - return ret; - vfree(vdeb->src_frame); vdeb->src_frame = NULL; } @@ -480,9 +467,8 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, } } -static void vimc_deb_process_frame(struct vimc_ent_device *ved, - struct media_pad *sink, - const void *sink_frame) +static void *vimc_deb_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, ved); @@ -491,7 +477,7 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved, /* If the stream in this node is not active, just return */ if (!vdeb->src_frame) - return; + return ERR_PTR(-EINVAL); for (i = 0; i < vdeb->sink_fmt.height; i++) for (j = 0; j < vdeb->sink_fmt.width; j++) { @@ -499,12 +485,8 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved, vdeb->set_rgb_src(vdeb, i, j, rgb); } - /* Propagate the frame through all source pads */ - for (i = 1; i < vdeb->sd.entity.num_pads; i++) { - struct media_pad *pad = &vdeb->sd.entity.pads[i]; + return vdeb->src_frame; - vimc_propagate_frame(pad, vdeb->src_frame); - } } static void vimc_deb_comp_unbind(struct device *comp, struct device *master, diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index b0952ee86296..39b2a73dfcc1 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -217,7 +217,6 @@ static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = { static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) { struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - int ret; if (enable) { const struct vimc_pix_map *vpix; @@ -245,22 +244,10 @@ static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) if (!vsca->src_frame) return -ENOMEM; - /* Turn the stream on in the subdevices directly connected */ - ret = vimc_pipeline_s_stream(&vsca->sd.entity, 1); - if (ret) { - vfree(vsca->src_frame); - vsca->src_frame = NULL; - return ret; - } } else { if (!vsca->src_frame) return 0; - /* Disable streaming from the pipe */ - ret = vimc_pipeline_s_stream(&vsca->sd.entity, 0); - if (ret) - return ret; - vfree(vsca->src_frame); vsca->src_frame = NULL; } @@ -346,26 +333,19 @@ static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca, vimc_sca_scale_pix(vsca, i, j, sink_frame); } -static void vimc_sca_process_frame(struct vimc_ent_device *ved, - struct media_pad *sink, - const void *sink_frame) +static void *vimc_sca_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, ved); - unsigned int i; /* If the stream in this node is not active, just return */ if (!vsca->src_frame) - return; + return ERR_PTR(-EINVAL); vimc_sca_fill_src_frame(vsca, sink_frame); - /* Propagate the frame through all source pads */ - for (i = 1; i < vsca->sd.entity.num_pads; i++) { - struct media_pad *pad = &vsca->sd.entity.pads[i]; - - vimc_propagate_frame(pad, vsca->src_frame); - } + return vsca->src_frame; }; static void vimc_sca_comp_unbind(struct device *comp, struct device *master, diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 32ca9c6172b1..93961a1e694f 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -16,8 +16,6 @@ */ #include -#include -#include #include #include #include @@ -201,38 +199,27 @@ static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { .set_fmt = vimc_sen_set_fmt, }; -static int vimc_sen_tpg_thread(void *data) +static void *vimc_sen_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { - struct vimc_sen_device *vsen = data; - unsigned int i; + struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, + ved); + const struct vimc_pix_map *vpix; + unsigned int frame_size; - set_freezable(); - set_current_state(TASK_UNINTERRUPTIBLE); + /* Calculate the frame size */ + vpix = vimc_pix_map_by_code(vsen->mbus_format.code); + frame_size = vsen->mbus_format.width * vpix->bpp * + vsen->mbus_format.height; - for (;;) { - try_to_freeze(); - if (kthread_should_stop()) - break; - - tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); - - /* Send the frame to all source pads */ - for (i = 0; i < vsen->sd.entity.num_pads; i++) - vimc_propagate_frame(&vsen->sd.entity.pads[i], - vsen->frame); - - /* 60 frames per second */ - schedule_timeout(HZ/60); - } - - return 0; + tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); + return vsen->frame; } static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) { struct vimc_sen_device *vsen = container_of(sd, struct vimc_sen_device, sd); - int ret; if (enable) { const struct vimc_pix_map *vpix; @@ -258,26 +245,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) /* configure the test pattern generator */ vimc_sen_tpg_s_format(vsen); - /* Initialize the image generator thread */ - vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen, - "%s-sen", vsen->sd.v4l2_dev->name); - if (IS_ERR(vsen->kthread_sen)) { - dev_err(vsen->dev, "%s: kernel_thread() failed\n", - vsen->sd.name); - vfree(vsen->frame); - vsen->frame = NULL; - return PTR_ERR(vsen->kthread_sen); - } } else { - if (!vsen->kthread_sen) - return 0; - /* Stop image generator */ - ret = kthread_stop(vsen->kthread_sen); - if (ret) - return ret; - - vsen->kthread_sen = NULL; vfree(vsen->frame); vsen->frame = NULL; return 0; @@ -413,6 +382,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master, if (ret) goto err_free_hdl; + vsen->ved.process_frame = vimc_sen_process_frame; dev_set_drvdata(comp, &vsen->ved); vsen->dev = comp; diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c new file mode 100644 index 000000000000..fcc897fb247b --- /dev/null +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vimc-streamer.c Virtual Media Controller Driver + * + * Copyright (C) 2018 Lucas A. M. Magalhães + * + */ + +#include +#include +#include +#include + +#include "vimc-streamer.h" + +/** + * vimc_get_source_entity - get the entity connected with the first sink pad + * + * @ent: reference media_entity + * + * Helper function that returns the media entity containing the source pad + * linked with the first sink pad from the given media entity pad list. + */ +static struct media_entity *vimc_get_source_entity(struct media_entity *ent) +{ + struct media_pad *pad; + int i; + + for (i = 0; i < ent->num_pads; i++) { + if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) + continue; + pad = media_entity_remote_pad(&ent->pads[i]); + return pad ? pad->entity : NULL; + } + return NULL; +} + +/* + * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream + * + * @stream: the pointer to the stream structure with the pipeline to be + * disabled. + * + * Calls s_stream to disable the stream in each entity of the pipeline + * + */ +static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream) +{ + struct media_entity *entity; + struct v4l2_subdev *sd; + + while (stream->pipe_size) { + stream->pipe_size--; + entity = stream->ved_pipeline[stream->pipe_size]->ent; + entity = vimc_get_source_entity(entity); + stream->ved_pipeline[stream->pipe_size] = NULL; + + if (!is_media_entity_v4l2_subdev(entity)) + continue; + + sd = media_entity_to_v4l2_subdev(entity); + v4l2_subdev_call(sd, video, s_stream, 0); + } +} + +/* + * vimc_streamer_pipeline_init - initializes the stream structure + * + * @stream: the pointer to the stream structure to be initialized + * @ved: the pointer to the vimc entity initializing the stream + * + * Initializes the stream structure. Walks through the entity graph to + * construct the pipeline used later on the streamer thread. + * Calls s_stream to enable stream in all entities of the pipeline. + */ +static int vimc_streamer_pipeline_init(struct vimc_stream *stream, + struct vimc_ent_device *ved) +{ + struct media_entity *entity; + struct video_device *vdev; + struct v4l2_subdev *sd; + int ret = 0; + + stream->pipe_size = 0; + while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) { + if (!ved) { + vimc_streamer_pipeline_terminate(stream); + return -EINVAL; + } + stream->ved_pipeline[stream->pipe_size++] = ved; + + entity = vimc_get_source_entity(ved->ent); + /* Check if the end of the pipeline was reached*/ + if (!entity) + return 0; + + if (is_media_entity_v4l2_subdev(entity)) { + sd = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + vimc_streamer_pipeline_terminate(stream); + return ret; + } + ved = v4l2_get_subdevdata(sd); + } else { + vdev = container_of(entity, + struct video_device, + entity); + ved = video_get_drvdata(vdev); + } + } + + vimc_streamer_pipeline_terminate(stream); + return -EINVAL; +} + +static int vimc_streamer_thread(void *data) +{ + struct vimc_stream *stream = data; + int i; + + set_freezable(); + set_current_state(TASK_UNINTERRUPTIBLE); + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + for (i = stream->pipe_size - 1; i >= 0; i--) { + stream->frame = stream->ved_pipeline[i]->process_frame( + stream->ved_pipeline[i], + stream->frame); + if (!stream->frame) + break; + if (IS_ERR(stream->frame)) + break; + } + //wait for 60hz + schedule_timeout(HZ / 60); + } + + return 0; +} + +int vimc_streamer_s_stream(struct vimc_stream *stream, + struct vimc_ent_device *ved, + int enable) +{ + int ret; + + if (!stream || !ved) + return -EINVAL; + + if (enable) { + if (stream->kthread) + return 0; + + ret = vimc_streamer_pipeline_init(stream, ved); + if (ret) + return ret; + + stream->kthread = kthread_run(vimc_streamer_thread, stream, + "vimc-streamer thread"); + + if (IS_ERR(stream->kthread)) + return PTR_ERR(stream->kthread); + + } else { + if (!stream->kthread) + return 0; + + ret = kthread_stop(stream->kthread); + if (ret) + return ret; + + stream->kthread = NULL; + + vimc_streamer_pipeline_terminate(stream); + } + + return 0; +} +EXPORT_SYMBOL_GPL(vimc_streamer_s_stream); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Streamer"); +MODULE_AUTHOR("Lucas A. M. Magalhães "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h new file mode 100644 index 000000000000..752af2e2d5a2 --- /dev/null +++ b/drivers/media/platform/vimc/vimc-streamer.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vimc-streamer.h Virtual Media Controller Driver + * + * Copyright (C) 2018 Lucas A. M. Magalhães + * + */ + +#ifndef _VIMC_STREAMER_H_ +#define _VIMC_STREAMER_H_ + +#include + +#include "vimc-common.h" + +#define VIMC_STREAMER_PIPELINE_MAX_SIZE 16 + +struct vimc_stream { + struct media_pipeline pipe; + struct vimc_ent_device *ved_pipeline[VIMC_STREAMER_PIPELINE_MAX_SIZE]; + unsigned int pipe_size; + u8 *frame; + struct task_struct *kthread; +}; + +/** + * vimc_streamer_s_streamer - start/stop the stream + * + * @stream: the pointer to the stream to start or stop + * @ved: The last entity of the streamer pipeline + * @enable: any non-zero number start the stream, zero stop + * + */ +int vimc_streamer_s_stream(struct vimc_stream *stream, + struct vimc_ent_device *ved, + int enable); + +#endif //_VIMC_STREAMER_H_ From a442940534b77f3892dd629c12b0d01e65d4f314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Almeida?= Date: Mon, 4 Feb 2019 12:37:33 -0500 Subject: [PATCH 1329/4277] media: vivid: add vertical down sampling to imagesize calc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To correctly set the size of the image in a plane, it's needed to divide the height of image by the vertical down sampling factor. This was only happening in vivid_try_fmt_vid_cap(), but now it applied in others sizeimage calculations as well. Signed-off-by: André Almeida Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-cap.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index c059fc12668a..52eeda624d7e 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -124,7 +124,8 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, } } else { for (p = 0; p < buffers; p++) - sizes[p] = tpg_g_line_width(&dev->tpg, p) * h + + sizes[p] = (tpg_g_line_width(&dev->tpg, p) * h) / + dev->fmt_cap->vdownsampling[p] + dev->fmt_cap->data_offset[p]; } @@ -161,7 +162,9 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } for (p = 0; p < buffers; p++) { - size = tpg_g_line_width(&dev->tpg, p) * dev->fmt_cap_rect.height + + size = (tpg_g_line_width(&dev->tpg, p) * + dev->fmt_cap_rect.height) / + dev->fmt_cap->vdownsampling[p] + dev->fmt_cap->data_offset[p]; if (vb2_plane_size(vb, p) < size) { @@ -545,7 +548,8 @@ int vivid_g_fmt_vid_cap(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p); mp->plane_fmt[p].sizeimage = - tpg_g_line_width(&dev->tpg, p) * mp->height + + (tpg_g_line_width(&dev->tpg, p) * mp->height) / + dev->fmt_cap->vdownsampling[p] + dev->fmt_cap->data_offset[p]; } return 0; From 22f05d646df9fdb4f1ff19582f17d350665c2ea9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 7 Feb 2019 12:28:19 -0500 Subject: [PATCH 1330/4277] media: vim2m: don't use curr_ctx->dev before checking It seems that it is possible that dev to be null, as there's a warning printing: "Instance released before the end of transaction" Solves this warning: drivers/media/platform/vim2m.c: drivers/media/platform/vim2m.c:525 device_work() warn: variable dereferenced before check 'curr_ctx' (see line 523) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index bfa1a2a16009..bd125ad34343 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -520,13 +520,14 @@ static void device_work(struct work_struct *w) unsigned long flags; curr_ctx = container_of(w, struct vim2m_ctx, work_run.work); - vim2m_dev = curr_ctx->dev; if (NULL == curr_ctx) { pr_err("Instance released before the end of transaction\n"); return; } + vim2m_dev = curr_ctx->dev; + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); From 9bcb830bfceaac456ff8206e3ed74210e133613e Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Sun, 20 Jan 2019 14:13:31 -0500 Subject: [PATCH 1331/4277] media: imx: Validate frame intervals before setting In the .s_frame_interval() subdev op, don't accept or set a frame interval with a zero numerator or denominator. This fixes a v4l2-compliance failure: fail: v4l2-test-formats.cpp(1146): cap->timeperframe.numerator == 0 || cap->timeperframe.denominator == 0 test VIDIOC_G/S_PARM: FAIL Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prp.c | 9 +++++++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++++++-- drivers/staging/media/imx/imx-media-csi.c | 5 ++++- drivers/staging/media/imx/imx-media-vdic.c | 5 ++++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 98923fc844ce..a2bb5c702d74 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -422,9 +422,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRP_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 3637693c2bc8..376b504e8a42 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -1222,9 +1222,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRPENCVF_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index d851ca2497b4..fb5307e2ca43 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -926,7 +926,10 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case CSI_SINK_PAD: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = *input_fi; /* Reset output intervals and frame skipping ratio to 1:1 */ priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval; priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval; diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 297951d98ab5..2808662e2597 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -818,7 +818,10 @@ static int vdic_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case VDIC_SINK_PAD_DIRECT: case VDIC_SINK_PAD_IDMAC: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = priv->frame_interval[fi->pad]; /* Reset output interval */ *output_fi = fi->interval; if (priv->csi_direct) From c2d88e7d66f9d327593e0d0f9d7f94755b637e5c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 5 Feb 2019 16:11:15 -0500 Subject: [PATCH 1332/4277] media: vb2: Fix buf_out_validate documentation The .buf_out_validate callback is mandatory for OUTPUT queues. Mark it as such in the callback's doc. Fixes: 28d77c21cb ("media: vb2: add buf_out_validate callback") Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4849b865b908..06142c1469cc 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -345,7 +345,8 @@ struct vb2_buffer { * waiting for a new buffer to arrive. * @buf_out_validate: called when the output buffer is prepared or queued * to a request; drivers can use this to validate - * userspace-provided information; optional. + * userspace-provided information; this is required only + * for OUTPUT queues. * @buf_init: called once after allocating a buffer (in MMAP case) * or after acquiring a new USERPTR buffer; drivers may * perform additional buffer-related initialization; From a4d3d61254d3645d8de738102c3c473b176180a5 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 5 Feb 2019 16:20:33 -0500 Subject: [PATCH 1333/4277] media: v4l2-mem2mem: Rename v4l2_m2m_buf_copy_data to v4l2_m2m_buf_copy_metadata The v4l2_m2m_buf_copy_data helper is used to copy the buffer metadata, such as its timestamp and its flags. Therefore, the v4l2_m2m_buf_copy_metadata name is more clear and avoids confusion with a payload data copy. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: also fix cedrus_dec.c] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 4 ++-- drivers/media/platform/vim2m.c | 2 +- drivers/media/v4l2-core/v4l2-mem2mem.c | 8 ++++---- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 2 +- include/media/v4l2-mem2mem.h | 14 +++++++------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 212da185e4f2..9d739ea5542d 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -199,7 +199,7 @@ static int device_process(struct vicodec_ctx *ctx, dst_vb->sequence = q_dst->sequence++; dst_vb->flags &= ~V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_data(src_vb, dst_vb, !ctx->is_enc); + v4l2_m2m_buf_copy_metadata(src_vb, dst_vb, !ctx->is_enc); return 0; } @@ -414,7 +414,7 @@ static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf, vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); dst_buf->sequence = q_dst->sequence++; - v4l2_m2m_buf_copy_data(src_buf, dst_buf, !ctx->is_enc); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc); dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); } diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index bd125ad34343..98be92a4e0b0 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -432,7 +432,7 @@ static int device_process(struct vim2m_ctx *ctx, out_vb->sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; in_vb->sequence = q_data_in->sequence++; - v4l2_m2m_buf_copy_data(in_vb, out_vb, true); + v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true); if (ctx->mode & MEM2MEM_VFLIP) { start = height - 1; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 631f4e2aa942..1494d0d5951a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -975,9 +975,9 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); -void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, - struct vb2_v4l2_buffer *cap_vb, - bool copy_frame_flags) +void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags) { u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK; @@ -993,7 +993,7 @@ void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, cap_vb->flags &= ~mask; cap_vb->flags |= out_vb->flags & mask; } -EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_data); +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_metadata); void v4l2_m2m_request_queue(struct media_request *req) { diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 443fb037e1cf..4d6d602cdde6 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -50,7 +50,7 @@ void cedrus_device_run(void *priv) break; } - v4l2_m2m_buf_copy_data(run.src, run.dst, true); + v4l2_m2m_buf_copy_metadata(run.src, run.dst, true); dev->dec_ops[ctx->current_codec]->setup(ctx, &run); diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 43e447dcf69d..47c6d9aa0bf4 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -623,11 +623,11 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) } /** - * v4l2_m2m_buf_copy_data() - copy buffer data from the output buffer to the - * capture buffer + * v4l2_m2m_buf_copy_metadata() - copy buffer metadata from + * the output buffer to the capture buffer * - * @out_vb: the output buffer that is the source of the data. - * @cap_vb: the capture buffer that will receive the data. + * @out_vb: the output buffer that is the source of the metadata. + * @cap_vb: the capture buffer that will receive the metadata. * @copy_frame_flags: copy the KEY/B/PFRAME flags as well. * * This helper function copies the timestamp, timecode (if the TIMECODE @@ -638,9 +638,9 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) * flags are not copied. This is typically needed for encoders that * set this bits explicitly. */ -void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, - struct vb2_v4l2_buffer *cap_vb, - bool copy_frame_flags); +void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags); /* v4l2 request helper */ From d75e77ed14f86034ace17e65ab6a6261dd0858c2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:33 -0500 Subject: [PATCH 1334/4277] media: hdpvr: fix smatch warning drivers/media/usb/hdpvr/hdpvr-i2c.c: drivers/media/usb/hdpvr/hdpvr-i2c.c:78 hdpvr_i2c_read() warn: 'dev->i2c_buf' 4216624615462223872 can't fit into 127 '*data' dev->i2c_buf is a char array, so you can just use dev->i2c_buf to get the start address, no need to do &dev->i2c_buf, even though it is the same address in C. It only confuses smatch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 5a3cb614a211..d76173f1ced1 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -61,10 +61,10 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, return -EINVAL; if (wlen) { - memcpy(&dev->i2c_buf, wdata, wlen); + memcpy(dev->i2c_buf, wdata, wlen); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, + (bus << 8) | addr, 0, dev->i2c_buf, wlen, 1000); if (ret < 0) return ret; @@ -72,10 +72,10 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_READ, CTRL_READ_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); + (bus << 8) | addr, 0, dev->i2c_buf, len, 1000); if (ret == len) { - memcpy(data, &dev->i2c_buf, len); + memcpy(data, dev->i2c_buf, len); ret = 0; } else if (ret >= 0) ret = -EIO; @@ -91,17 +91,17 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, if (len > sizeof(dev->i2c_buf)) return -EINVAL; - memcpy(&dev->i2c_buf, data, len); + memcpy(dev->i2c_buf, data, len); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); + (bus << 8) | addr, 0, dev->i2c_buf, len, 1000); if (ret < 0) return ret; ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, - 0, 0, &dev->i2c_buf, 2, 1000); + 0, 0, dev->i2c_buf, 2, 1000); if ((ret == 2) && (dev->i2c_buf[1] == (len - 1))) ret = 0; From db89a47fb9c71e769b817fa669631b04c2aba440 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:38 -0500 Subject: [PATCH 1335/4277] media: pxa_camera: fix smatch warning drivers/media/platform/pxa_camera.c:2400 pxa_camera_probe() error: we previously assumed 'pcdev->pdata' could be null (see line 2397) First check if platform data is provided, then check if DT data is provided, and if neither is provided just return with -ENODEV. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 5f930560eb30..3cf3c6390cc8 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2394,15 +2394,17 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->res = res; pcdev->pdata = pdev->dev.platform_data; - if (pdev->dev.of_node && !pcdev->pdata) { - err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); - } else { + if (pcdev->pdata) { pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; pcdev->asd.match.i2c.adapter_id = pcdev->pdata->sensor_i2c_adapter_id; pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address; + } else if (pdev->dev.of_node) { + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); + } else { + return -ENODEV; } if (err < 0) return err; From 6fd369dd1cb65a032f1ab9227033ecb7b759656d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 08:43:51 -0500 Subject: [PATCH 1336/4277] media: vimc: fill in bus_info in media_device_info It is good practice to fill in bus_info. Also just use 'platform:vimc' when filling in the bus_info in querycap: the bus_info has nothing to do with the video device name. Signed-off-by: Hans Verkuil Acked-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 4 +--- drivers/media/platform/vimc/vimc-core.c | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 93837d9eecd2..a6f8715d2b44 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -70,12 +70,10 @@ struct vimc_cap_buffer { static int vimc_cap_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct vimc_cap_device *vcap = video_drvdata(file); - strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", vcap->vdev.v4l2_dev->name); + "platform:%s", VIMC_PDEV_NAME); return 0; } diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index bf19f1f9795e..c2fdf3ea67ed 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -318,6 +318,8 @@ static int vimc_probe(struct platform_device *pdev) /* Initialize media device */ strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME, sizeof(vimc->mdev.model)); + snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info), + "platform:%s", VIMC_PDEV_NAME); vimc->mdev.dev = &pdev->dev; media_device_init(&vimc->mdev); From 13a929f38ad742200270389cedc78c1859726bd8 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 22 Jan 2019 03:29:24 +0000 Subject: [PATCH 1337/4277] rtc: imx-sc: add rtc set time support This patch adds i.MX system controller RTC set time support, the RTC set time is implemented via SIP(silicon provider) runtime service call and ARM-Trusted-Firmware will communicate with system controller via MU(message unit) IPC to set RTC time. Signed-off-by: Anson Huang Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-imx-sc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/rtc/rtc-imx-sc.c b/drivers/rtc/rtc-imx-sc.c index 7ff08544532a..60570a278bb0 100644 --- a/drivers/rtc/rtc-imx-sc.c +++ b/drivers/rtc/rtc-imx-sc.c @@ -3,6 +3,7 @@ * Copyright 2018 NXP. */ +#include #include #include #include @@ -12,6 +13,9 @@ #define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9 #define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6 +#define IMX_SIP_SRTC 0xC2000002 +#define IMX_SIP_SRTC_SET_TIME 0x0 + static struct imx_sc_ipc *rtc_ipc_handle; static struct rtc_device *imx_sc_rtc; @@ -42,8 +46,23 @@ static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm) return 0; } +static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct arm_smccc_res res; + + /* pack 2 time parameters into 1 register, 16 bits for each */ + arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME, + ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1), + (tm->tm_mday << 16) | tm->tm_hour, + (tm->tm_min << 16) | tm->tm_sec, + 0, 0, 0, &res); + + return res.a0; +} + static const struct rtc_class_ops imx_sc_rtc_ops = { .read_time = imx_sc_rtc_read_time, + .set_time = imx_sc_rtc_set_time, }; static int imx_sc_rtc_probe(struct platform_device *pdev) From 70f8a3ca68d3e1f3344d959981ca55d5f6ec77f7 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 09:59:15 -0800 Subject: [PATCH 1338/4277] mm: make mm->pinned_vm an atomic64 counter Taking a sleeping lock to _only_ increment a variable is quite the overkill, and pretty much all users do this. Furthermore, some drivers (ie: infiniband and scif) that need pinned semantics can go to quite some trouble to actually delay via workqueue (un)accounting for pinned pages when not possible to acquire it. By making the counter atomic we no longer need to hold the mmap_sem and can simply some code around it for pinned_vm users. The counter is 64-bit such that we need not worry about overflows such as rdma user input controlled from userspace. Reviewed-by: Ira Weiny Reviewed-by: Christoph Lameter Reviewed-by: Daniel Jordan Reviewed-by: Jan Kara Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/umem.c | 12 ++++++------ drivers/infiniband/hw/hfi1/user_pages.c | 6 +++--- drivers/infiniband/hw/qib/qib_user_pages.c | 4 ++-- drivers/infiniband/hw/usnic/usnic_uiom.c | 8 ++++---- drivers/misc/mic/scif/scif_rma.c | 6 +++--- fs/proc/task_mmu.c | 2 +- include/linux/mm_types.h | 2 +- kernel/events/core.c | 8 ++++---- kernel/fork.c | 2 +- mm/debug.c | 5 +++-- 10 files changed, 28 insertions(+), 27 deletions(-) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 1efe0a74e06b..678abe1afcba 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -166,13 +166,13 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; down_write(&mm->mmap_sem); - if (check_add_overflow(mm->pinned_vm, npages, &new_pinned) || - (new_pinned > lock_limit && !capable(CAP_IPC_LOCK))) { + new_pinned = atomic64_read(&mm->pinned_vm) + npages; + if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) { up_write(&mm->mmap_sem); ret = -ENOMEM; goto out; } - mm->pinned_vm = new_pinned; + atomic64_set(&mm->pinned_vm, new_pinned); up_write(&mm->mmap_sem); cur_base = addr & PAGE_MASK; @@ -234,7 +234,7 @@ umem_release: __ib_umem_release(context->device, umem, 0); vma: down_write(&mm->mmap_sem); - mm->pinned_vm -= ib_umem_num_pages(umem); + atomic64_sub(ib_umem_num_pages(umem), &mm->pinned_vm); up_write(&mm->mmap_sem); out: if (vma_list) @@ -263,7 +263,7 @@ static void ib_umem_release_defer(struct work_struct *work) struct ib_umem *umem = container_of(work, struct ib_umem, work); down_write(&umem->owning_mm->mmap_sem); - umem->owning_mm->pinned_vm -= ib_umem_num_pages(umem); + atomic64_sub(ib_umem_num_pages(umem), &umem->owning_mm->pinned_vm); up_write(&umem->owning_mm->mmap_sem); __ib_umem_release_tail(umem); @@ -302,7 +302,7 @@ void ib_umem_release(struct ib_umem *umem) } else { down_write(&umem->owning_mm->mmap_sem); } - umem->owning_mm->pinned_vm -= ib_umem_num_pages(umem); + atomic64_sub(ib_umem_num_pages(umem), &umem->owning_mm->pinned_vm); up_write(&umem->owning_mm->mmap_sem); __ib_umem_release_tail(umem); diff --git a/drivers/infiniband/hw/hfi1/user_pages.c b/drivers/infiniband/hw/hfi1/user_pages.c index e341e6dcc388..40a6e434190f 100644 --- a/drivers/infiniband/hw/hfi1/user_pages.c +++ b/drivers/infiniband/hw/hfi1/user_pages.c @@ -92,7 +92,7 @@ bool hfi1_can_pin_pages(struct hfi1_devdata *dd, struct mm_struct *mm, size = DIV_ROUND_UP(size, PAGE_SIZE); down_read(&mm->mmap_sem); - pinned = mm->pinned_vm; + pinned = atomic64_read(&mm->pinned_vm); up_read(&mm->mmap_sem); /* First, check the absolute limit against all pinned pages. */ @@ -112,7 +112,7 @@ int hfi1_acquire_user_pages(struct mm_struct *mm, unsigned long vaddr, size_t np return ret; down_write(&mm->mmap_sem); - mm->pinned_vm += ret; + atomic64_add(ret, &mm->pinned_vm); up_write(&mm->mmap_sem); return ret; @@ -131,7 +131,7 @@ void hfi1_release_user_pages(struct mm_struct *mm, struct page **p, if (mm) { /* during close after signal, mm can be NULL */ down_write(&mm->mmap_sem); - mm->pinned_vm -= npages; + atomic64_sub(npages, &mm->pinned_vm); up_write(&mm->mmap_sem); } } diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c index 075f09fb7ce3..c6c81022d313 100644 --- a/drivers/infiniband/hw/qib/qib_user_pages.c +++ b/drivers/infiniband/hw/qib/qib_user_pages.c @@ -75,7 +75,7 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages, goto bail_release; } - current->mm->pinned_vm += num_pages; + atomic64_add(num_pages, ¤t->mm->pinned_vm); ret = 0; goto bail; @@ -156,7 +156,7 @@ void qib_release_user_pages(struct page **p, size_t num_pages) __qib_release_user_pages(p, num_pages, 1); if (current->mm) { - current->mm->pinned_vm -= num_pages; + atomic64_sub(num_pages, ¤t->mm->pinned_vm); up_write(¤t->mm->mmap_sem); } } diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index ce01a59fccc4..854436a2b437 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -129,7 +129,7 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, uiomr->owning_mm = mm = current->mm; down_write(&mm->mmap_sem); - locked = npages + current->mm->pinned_vm; + locked = npages + atomic64_read(¤t->mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { @@ -187,7 +187,7 @@ out: if (ret < 0) usnic_uiom_put_pages(chunk_list, 0); else { - mm->pinned_vm = locked; + atomic64_set(&mm->pinned_vm, locked); mmgrab(uiomr->owning_mm); } @@ -441,7 +441,7 @@ static void usnic_uiom_release_defer(struct work_struct *work) container_of(work, struct usnic_uiom_reg, work); down_write(&uiomr->owning_mm->mmap_sem); - uiomr->owning_mm->pinned_vm -= usnic_uiom_num_pages(uiomr); + atomic64_sub(usnic_uiom_num_pages(uiomr), &uiomr->owning_mm->pinned_vm); up_write(&uiomr->owning_mm->mmap_sem); __usnic_uiom_release_tail(uiomr); @@ -469,7 +469,7 @@ void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr, } else { down_write(&uiomr->owning_mm->mmap_sem); } - uiomr->owning_mm->pinned_vm -= usnic_uiom_num_pages(uiomr); + atomic64_sub(usnic_uiom_num_pages(uiomr), &uiomr->owning_mm->pinned_vm); up_write(&uiomr->owning_mm->mmap_sem); __usnic_uiom_release_tail(uiomr); diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index 749321eb91ae..2448368f181e 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -285,7 +285,7 @@ __scif_dec_pinned_vm_lock(struct mm_struct *mm, } else { down_write(&mm->mmap_sem); } - mm->pinned_vm -= nr_pages; + atomic64_sub(nr_pages, &mm->pinned_vm); up_write(&mm->mmap_sem); return 0; } @@ -299,7 +299,7 @@ static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm, return 0; locked = nr_pages; - locked += mm->pinned_vm; + locked += atomic64_read(&mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { dev_err(scif_info.mdev.this_device, @@ -307,7 +307,7 @@ static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm, locked, lock_limit); return -ENOMEM; } - mm->pinned_vm = locked; + atomic64_set(&mm->pinned_vm, locked); return 0; } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index f0ec9edab2f3..d2902962244d 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -59,7 +59,7 @@ void task_mem(struct seq_file *m, struct mm_struct *mm) SEQ_PUT_DEC("VmPeak:\t", hiwater_vm); SEQ_PUT_DEC(" kB\nVmSize:\t", total_vm); SEQ_PUT_DEC(" kB\nVmLck:\t", mm->locked_vm); - SEQ_PUT_DEC(" kB\nVmPin:\t", mm->pinned_vm); + SEQ_PUT_DEC(" kB\nVmPin:\t", atomic64_read(&mm->pinned_vm)); SEQ_PUT_DEC(" kB\nVmHWM:\t", hiwater_rss); SEQ_PUT_DEC(" kB\nVmRSS:\t", total_rss); SEQ_PUT_DEC(" kB\nRssAnon:\t", anon); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 2c471a2c43fa..acea2ea2d6c4 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -405,7 +405,7 @@ struct mm_struct { unsigned long total_vm; /* Total pages mapped */ unsigned long locked_vm; /* Pages that have PG_mlocked set */ - unsigned long pinned_vm; /* Refcount permanently increased */ + atomic64_t pinned_vm; /* Refcount permanently increased */ unsigned long data_vm; /* VM_WRITE & ~VM_SHARED & ~VM_STACK */ unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE & ~VM_STACK */ unsigned long stack_vm; /* VM_STACK */ diff --git a/kernel/events/core.c b/kernel/events/core.c index e5ede6918050..29e9f2473656 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5459,7 +5459,7 @@ static void perf_mmap_close(struct vm_area_struct *vma) /* now it's safe to free the pages */ atomic_long_sub(rb->aux_nr_pages, &mmap_user->locked_vm); - vma->vm_mm->pinned_vm -= rb->aux_mmap_locked; + atomic64_sub(rb->aux_mmap_locked, &vma->vm_mm->pinned_vm); /* this has to be the last one */ rb_free_aux(rb); @@ -5532,7 +5532,7 @@ again: */ atomic_long_sub((size >> PAGE_SHIFT) + 1, &mmap_user->locked_vm); - vma->vm_mm->pinned_vm -= mmap_locked; + atomic64_sub(mmap_locked, &vma->vm_mm->pinned_vm); free_uid(mmap_user); out_put: @@ -5680,7 +5680,7 @@ accounting: lock_limit = rlimit(RLIMIT_MEMLOCK); lock_limit >>= PAGE_SHIFT; - locked = vma->vm_mm->pinned_vm + extra; + locked = atomic64_read(&vma->vm_mm->pinned_vm) + extra; if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() && !capable(CAP_IPC_LOCK)) { @@ -5721,7 +5721,7 @@ accounting: unlock: if (!ret) { atomic_long_add(user_extra, &user->locked_vm); - vma->vm_mm->pinned_vm += extra; + atomic64_add(extra, &vma->vm_mm->pinned_vm); atomic_inc(&event->mmap_count); } else if (rb) { diff --git a/kernel/fork.c b/kernel/fork.c index b69248e6f0e0..85e08c379a9e 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -981,7 +981,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, mm_pgtables_bytes_init(mm); mm->map_count = 0; mm->locked_vm = 0; - mm->pinned_vm = 0; + atomic64_set(&mm->pinned_vm, 0); memset(&mm->rss_stat, 0, sizeof(mm->rss_stat)); spin_lock_init(&mm->page_table_lock); spin_lock_init(&mm->arg_lock); diff --git a/mm/debug.c b/mm/debug.c index 0abb987dad9b..7d13941a72f9 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -135,7 +135,7 @@ void dump_mm(const struct mm_struct *mm) "mmap_base %lu mmap_legacy_base %lu highest_vm_end %lu\n" "pgd %px mm_users %d mm_count %d pgtables_bytes %lu map_count %d\n" "hiwater_rss %lx hiwater_vm %lx total_vm %lx locked_vm %lx\n" - "pinned_vm %lx data_vm %lx exec_vm %lx stack_vm %lx\n" + "pinned_vm %llx data_vm %lx exec_vm %lx stack_vm %lx\n" "start_code %lx end_code %lx start_data %lx end_data %lx\n" "start_brk %lx brk %lx start_stack %lx\n" "arg_start %lx arg_end %lx env_start %lx env_end %lx\n" @@ -166,7 +166,8 @@ void dump_mm(const struct mm_struct *mm) mm_pgtables_bytes(mm), mm->map_count, mm->hiwater_rss, mm->hiwater_vm, mm->total_vm, mm->locked_vm, - mm->pinned_vm, mm->data_vm, mm->exec_vm, mm->stack_vm, + atomic64_read(&mm->pinned_vm), + mm->data_vm, mm->exec_vm, mm->stack_vm, mm->start_code, mm->end_code, mm->start_data, mm->end_data, mm->start_brk, mm->brk, mm->start_stack, mm->arg_start, mm->arg_end, mm->env_start, mm->env_end, From 4f564ff3d4dd887955b25b83d06989610769bf32 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 09:59:16 -0800 Subject: [PATCH 1339/4277] drivers/mic/scif: do not use mmap_sem The driver uses mmap_sem for both pinned_vm accounting and get_user_pages(). By using gup_fast() and letting the mm handle the lock if needed, we can no longer rely on the semaphore and simplify the whole thing. Reviewed-by: Ira Weiny Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- drivers/misc/mic/scif/scif_rma.c | 36 ++++++++++---------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index 2448368f181e..263b8ad507ea 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -272,21 +272,12 @@ static inline void __scif_release_mm(struct mm_struct *mm) static inline int __scif_dec_pinned_vm_lock(struct mm_struct *mm, - int nr_pages, bool try_lock) + int nr_pages) { if (!mm || !nr_pages || !scif_ulimit_check) return 0; - if (try_lock) { - if (!down_write_trylock(&mm->mmap_sem)) { - dev_err(scif_info.mdev.this_device, - "%s %d err\n", __func__, __LINE__); - return -1; - } - } else { - down_write(&mm->mmap_sem); - } + atomic64_sub(nr_pages, &mm->pinned_vm); - up_write(&mm->mmap_sem); return 0; } @@ -298,16 +289,16 @@ static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm, if (!mm || !nr_pages || !scif_ulimit_check) return 0; - locked = nr_pages; - locked += atomic64_read(&mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + locked = atomic64_add_return(nr_pages, &mm->pinned_vm); + if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { + atomic64_sub(nr_pages, &mm->pinned_vm); dev_err(scif_info.mdev.this_device, "locked(%lu) > lock_limit(%lu)\n", locked, lock_limit); return -ENOMEM; } - atomic64_set(&mm->pinned_vm, locked); return 0; } @@ -326,7 +317,7 @@ int scif_destroy_window(struct scif_endpt *ep, struct scif_window *window) might_sleep(); if (!window->temp && window->mm) { - __scif_dec_pinned_vm_lock(window->mm, window->nr_pages, 0); + __scif_dec_pinned_vm_lock(window->mm, window->nr_pages); __scif_release_mm(window->mm); window->mm = NULL; } @@ -737,7 +728,7 @@ done: ep->rma_info.dma_chan); } else { if (!__scif_dec_pinned_vm_lock(window->mm, - window->nr_pages, 1)) { + window->nr_pages)) { __scif_release_mm(window->mm); window->mm = NULL; } @@ -1385,28 +1376,23 @@ int __scif_pin_pages(void *addr, size_t len, int *out_prot, prot |= SCIF_PROT_WRITE; retry: mm = current->mm; - down_write(&mm->mmap_sem); if (ulimit) { err = __scif_check_inc_pinned_vm(mm, nr_pages); if (err) { - up_write(&mm->mmap_sem); pinned_pages->nr_pages = 0; goto error_unmap; } } - pinned_pages->nr_pages = get_user_pages( + pinned_pages->nr_pages = get_user_pages_fast( (u64)addr, nr_pages, (prot & SCIF_PROT_WRITE) ? FOLL_WRITE : 0, - pinned_pages->pages, - NULL); - up_write(&mm->mmap_sem); + pinned_pages->pages); if (nr_pages != pinned_pages->nr_pages) { if (try_upgrade) { if (ulimit) - __scif_dec_pinned_vm_lock(mm, - nr_pages, 0); + __scif_dec_pinned_vm_lock(mm, nr_pages); /* Roll back any pinned pages */ for (i = 0; i < pinned_pages->nr_pages; i++) { if (pinned_pages->pages[i]) @@ -1433,7 +1419,7 @@ retry: return err; dec_pinned: if (ulimit) - __scif_dec_pinned_vm_lock(mm, nr_pages, 0); + __scif_dec_pinned_vm_lock(mm, nr_pages); /* Something went wrong! Rollback */ error_unmap: pinned_pages->nr_pages = nr_pages; From 3a2a1e90564e3ad215aa5c6ddc0e741cd6208a93 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 09:59:17 -0800 Subject: [PATCH 1340/4277] drivers/IB,qib: optimize mmap_sem usage The driver uses mmap_sem for both pinned_vm accounting and get_user_pages(). Because rdma drivers might want to use gup_longterm() in the future we still need some sort of mmap_sem serialization (as opposed to removing it entirely by using gup_fast()). Now that pinned_vm is atomic the writer lock can therefore be converted to reader. This also fixes a bug that __qib_get_user_pages was not taking into account the current value of pinned_vm. Reviewed-by: Ira Weiny Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qib/qib_user_pages.c | 73 ++++++++-------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c index c6c81022d313..ef8bcf366ddc 100644 --- a/drivers/infiniband/hw/qib/qib_user_pages.c +++ b/drivers/infiniband/hw/qib/qib_user_pages.c @@ -49,43 +49,6 @@ static void __qib_release_user_pages(struct page **p, size_t num_pages, } } -/* - * Call with current->mm->mmap_sem held. - */ -static int __qib_get_user_pages(unsigned long start_page, size_t num_pages, - struct page **p) -{ - unsigned long lock_limit; - size_t got; - int ret; - - lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - - if (num_pages > lock_limit && !capable(CAP_IPC_LOCK)) { - ret = -ENOMEM; - goto bail; - } - - for (got = 0; got < num_pages; got += ret) { - ret = get_user_pages_longterm(start_page + got * PAGE_SIZE, - num_pages - got, - FOLL_WRITE | FOLL_FORCE, - p + got, NULL); - if (ret < 0) - goto bail_release; - } - - atomic64_add(num_pages, ¤t->mm->pinned_vm); - - ret = 0; - goto bail; - -bail_release: - __qib_release_user_pages(p, got, 0); -bail: - return ret; -} - /** * qib_map_page - a safety wrapper around pci_map_page() * @@ -137,26 +100,44 @@ int qib_map_page(struct pci_dev *hwdev, struct page *page, dma_addr_t *daddr) int qib_get_user_pages(unsigned long start_page, size_t num_pages, struct page **p) { + unsigned long locked, lock_limit; + size_t got; int ret; - down_write(¤t->mm->mmap_sem); + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + locked = atomic64_add_return(num_pages, ¤t->mm->pinned_vm); - ret = __qib_get_user_pages(start_page, num_pages, p); + if (num_pages > lock_limit && !capable(CAP_IPC_LOCK)) { + ret = -ENOMEM; + goto bail; + } - up_write(¤t->mm->mmap_sem); + down_read(¤t->mm->mmap_sem); + for (got = 0; got < num_pages; got += ret) { + ret = get_user_pages_longterm(start_page + got * PAGE_SIZE, + num_pages - got, + FOLL_WRITE | FOLL_FORCE, + p + got, NULL); + if (ret < 0) { + up_read(¤t->mm->mmap_sem); + goto bail_release; + } + } + up_read(¤t->mm->mmap_sem); + return 0; +bail_release: + __qib_release_user_pages(p, got, 0); +bail: + atomic64_sub(num_pages, ¤t->mm->pinned_vm); return ret; } void qib_release_user_pages(struct page **p, size_t num_pages) { - if (current->mm) /* during close after signal, mm can be NULL */ - down_write(¤t->mm->mmap_sem); - __qib_release_user_pages(p, num_pages, 1); - if (current->mm) { + /* during close after signal, mm can be NULL */ + if (current->mm) atomic64_sub(num_pages, ¤t->mm->pinned_vm); - up_write(¤t->mm->mmap_sem); - } } From 0e15c253363e9a8824e6b9c9fc17468eb924cebd Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 09:59:18 -0800 Subject: [PATCH 1341/4277] drivers/IB,hfi1: do not se mmap_sem This driver already uses gup_fast() and thus we can just drop the mmap_sem protection around the pinned_vm counter. Note that the window between when hfi1_can_pin_pages() is called and the actual counter is incremented remains the same as mmap_sem was _only_ used for when ->pinned_vm was touched. Reviewed-by: Ira Weiny Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hfi1/user_pages.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/user_pages.c b/drivers/infiniband/hw/hfi1/user_pages.c index 40a6e434190f..24b592c6522e 100644 --- a/drivers/infiniband/hw/hfi1/user_pages.c +++ b/drivers/infiniband/hw/hfi1/user_pages.c @@ -91,9 +91,7 @@ bool hfi1_can_pin_pages(struct hfi1_devdata *dd, struct mm_struct *mm, /* Convert to number of pages */ size = DIV_ROUND_UP(size, PAGE_SIZE); - down_read(&mm->mmap_sem); pinned = atomic64_read(&mm->pinned_vm); - up_read(&mm->mmap_sem); /* First, check the absolute limit against all pinned pages. */ if (pinned + npages >= ulimit && !can_lock) @@ -111,9 +109,7 @@ int hfi1_acquire_user_pages(struct mm_struct *mm, unsigned long vaddr, size_t np if (ret < 0) return ret; - down_write(&mm->mmap_sem); atomic64_add(ret, &mm->pinned_vm); - up_write(&mm->mmap_sem); return ret; } @@ -130,8 +126,6 @@ void hfi1_release_user_pages(struct mm_struct *mm, struct page **p, } if (mm) { /* during close after signal, mm can be NULL */ - down_write(&mm->mmap_sem); atomic64_sub(npages, &mm->pinned_vm); - up_write(&mm->mmap_sem); } } From 8ea1f989aa07a095cbf4aabbf9be7c3a18ce31bf Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 09:59:19 -0800 Subject: [PATCH 1342/4277] drivers/IB,usnic: reduce scope of mmap_sem usnic_uiom_get_pages() uses gup_longterm() so we cannot really get rid of mmap_sem altogether in the driver, but we can get rid of some complexity that mmap_sem brings with only pinned_vm. We can get rid of the wq altogether as we no longer need to defer work to unpin pages as the counter is now atomic. We also share the lock. Acked-by: Parvi Kaustubhi Reviewed-by: Ira Weiny Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/usnic/usnic_ib_main.c | 2 - drivers/infiniband/hw/usnic/usnic_uiom.c | 58 +++------------------ drivers/infiniband/hw/usnic/usnic_uiom.h | 1 - 3 files changed, 6 insertions(+), 55 deletions(-) diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index c4a4cfe4b567..577d9301251a 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -684,7 +684,6 @@ out_unreg_netdev_notifier: out_pci_unreg: pci_unregister_driver(&usnic_ib_pci_driver); out_umem_fini: - usnic_uiom_fini(); return err; } @@ -697,7 +696,6 @@ static void __exit usnic_ib_destroy(void) unregister_inetaddr_notifier(&usnic_ib_inetaddr_notifier); unregister_netdevice_notifier(&usnic_ib_netdevice_notifier); pci_unregister_driver(&usnic_ib_pci_driver); - usnic_uiom_fini(); } MODULE_DESCRIPTION("Cisco VIC (usNIC) Verbs Driver"); diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index 854436a2b437..06862a6af185 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -47,8 +47,6 @@ #include "usnic_uiom.h" #include "usnic_uiom_interval_tree.h" -static struct workqueue_struct *usnic_uiom_wq; - #define USNIC_UIOM_PAGE_CHUNK \ ((PAGE_SIZE - offsetof(struct usnic_uiom_chunk, page_list)) /\ ((void *) &((struct usnic_uiom_chunk *) 0)->page_list[1] - \ @@ -127,9 +125,9 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, npages = PAGE_ALIGN(size + (addr & ~PAGE_MASK)) >> PAGE_SHIFT; uiomr->owning_mm = mm = current->mm; - down_write(&mm->mmap_sem); + down_read(&mm->mmap_sem); - locked = npages + atomic64_read(¤t->mm->pinned_vm); + locked = atomic64_add_return(npages, ¤t->mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { @@ -184,14 +182,13 @@ static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, } out: - if (ret < 0) + if (ret < 0) { usnic_uiom_put_pages(chunk_list, 0); - else { - atomic64_set(&mm->pinned_vm, locked); + atomic64_sub(npages, ¤t->mm->pinned_vm); + } else mmgrab(uiomr->owning_mm); - } - up_write(&mm->mmap_sem); + up_read(&mm->mmap_sem); free_page((unsigned long) page_list); return ret; } @@ -435,43 +432,12 @@ static inline size_t usnic_uiom_num_pages(struct usnic_uiom_reg *uiomr) return PAGE_ALIGN(uiomr->length + uiomr->offset) >> PAGE_SHIFT; } -static void usnic_uiom_release_defer(struct work_struct *work) -{ - struct usnic_uiom_reg *uiomr = - container_of(work, struct usnic_uiom_reg, work); - - down_write(&uiomr->owning_mm->mmap_sem); - atomic64_sub(usnic_uiom_num_pages(uiomr), &uiomr->owning_mm->pinned_vm); - up_write(&uiomr->owning_mm->mmap_sem); - - __usnic_uiom_release_tail(uiomr); -} - void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr, struct ib_ucontext *context) { __usnic_uiom_reg_release(uiomr->pd, uiomr, 1); - /* - * We may be called with the mm's mmap_sem already held. This - * can happen when a userspace munmap() is the call that drops - * the last reference to our file and calls our release - * method. If there are memory regions to destroy, we'll end - * up here and not be able to take the mmap_sem. In that case - * we defer the vm_locked accounting to a workqueue. - */ - if (context->closing) { - if (!down_write_trylock(&uiomr->owning_mm->mmap_sem)) { - INIT_WORK(&uiomr->work, usnic_uiom_release_defer); - queue_work(usnic_uiom_wq, &uiomr->work); - return; - } - } else { - down_write(&uiomr->owning_mm->mmap_sem); - } atomic64_sub(usnic_uiom_num_pages(uiomr), &uiomr->owning_mm->pinned_vm); - up_write(&uiomr->owning_mm->mmap_sem); - __usnic_uiom_release_tail(uiomr); } @@ -600,17 +566,5 @@ int usnic_uiom_init(char *drv_name) return -EPERM; } - usnic_uiom_wq = create_workqueue(drv_name); - if (!usnic_uiom_wq) { - usnic_err("Unable to alloc wq for drv %s\n", drv_name); - return -ENOMEM; - } - return 0; } - -void usnic_uiom_fini(void) -{ - flush_workqueue(usnic_uiom_wq); - destroy_workqueue(usnic_uiom_wq); -} diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.h b/drivers/infiniband/hw/usnic/usnic_uiom.h index b86a9731071b..c88cfa087e3a 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.h +++ b/drivers/infiniband/hw/usnic/usnic_uiom.h @@ -93,5 +93,4 @@ struct usnic_uiom_reg *usnic_uiom_reg_get(struct usnic_uiom_pd *pd, void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr, struct ib_ucontext *ucontext); int usnic_uiom_init(char *drv_name); -void usnic_uiom_fini(void); #endif /* USNIC_UIOM_H_ */ From b95df5e3e45914c679fa5d4ca08abdd1c98b9f50 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 09:59:20 -0800 Subject: [PATCH 1343/4277] drivers/IB,core: reduce scope of mmap_sem ib_umem_get() uses gup_longterm() and relies on the lock to stabilze the vma_list, so we cannot really get rid of mmap_sem altogether, but now that the counter is atomic, we can get of some complexity that mmap_sem brings with only pinned_vm. Reviewed-by: Ira Weiny Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/umem.c | 41 ++-------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 678abe1afcba..b69d3efa8712 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -165,15 +165,12 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - down_write(&mm->mmap_sem); - new_pinned = atomic64_read(&mm->pinned_vm) + npages; + new_pinned = atomic64_add_return(npages, &mm->pinned_vm); if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) { - up_write(&mm->mmap_sem); + atomic64_sub(npages, &mm->pinned_vm); ret = -ENOMEM; goto out; } - atomic64_set(&mm->pinned_vm, new_pinned); - up_write(&mm->mmap_sem); cur_base = addr & PAGE_MASK; @@ -233,9 +230,7 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, umem_release: __ib_umem_release(context->device, umem, 0); vma: - down_write(&mm->mmap_sem); atomic64_sub(ib_umem_num_pages(umem), &mm->pinned_vm); - up_write(&mm->mmap_sem); out: if (vma_list) free_page((unsigned long) vma_list); @@ -258,25 +253,12 @@ static void __ib_umem_release_tail(struct ib_umem *umem) kfree(umem); } -static void ib_umem_release_defer(struct work_struct *work) -{ - struct ib_umem *umem = container_of(work, struct ib_umem, work); - - down_write(&umem->owning_mm->mmap_sem); - atomic64_sub(ib_umem_num_pages(umem), &umem->owning_mm->pinned_vm); - up_write(&umem->owning_mm->mmap_sem); - - __ib_umem_release_tail(umem); -} - /** * ib_umem_release - release memory pinned with ib_umem_get * @umem: umem struct to release */ void ib_umem_release(struct ib_umem *umem) { - struct ib_ucontext *context = umem->context; - if (umem->is_odp) { ib_umem_odp_release(to_ib_umem_odp(umem)); __ib_umem_release_tail(umem); @@ -285,26 +267,7 @@ void ib_umem_release(struct ib_umem *umem) __ib_umem_release(umem->context->device, umem, 1); - /* - * We may be called with the mm's mmap_sem already held. This - * can happen when a userspace munmap() is the call that drops - * the last reference to our file and calls our release - * method. If there are memory regions to destroy, we'll end - * up here and not be able to take the mmap_sem. In that case - * we defer the vm_locked accounting a workqueue. - */ - if (context->closing) { - if (!down_write_trylock(&umem->owning_mm->mmap_sem)) { - INIT_WORK(&umem->work, ib_umem_release_defer); - queue_work(ib_wq, &umem->work); - return; - } - } else { - down_write(&umem->owning_mm->mmap_sem); - } atomic64_sub(ib_umem_num_pages(umem), &umem->owning_mm->pinned_vm); - up_write(&umem->owning_mm->mmap_sem); - __ib_umem_release_tail(umem); } EXPORT_SYMBOL(ib_umem_release); From 92cbf12fc200595611511fb3c5ab17d2627fa43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 25 Jan 2019 15:35:54 +0100 Subject: [PATCH 1344/4277] rtc: ds1307: Move register definitions to start of file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to use the register offsets in all functions which is needed in one of the next patches. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 74b31dce484f..f7a47ee5cdf6 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -114,6 +114,32 @@ enum ds_type { # define RX8025_BIT_VDET 0x40 # define RX8025_BIT_XST 0x20 +#define RX8130_REG_ALARM_MIN 0x07 +#define RX8130_REG_ALARM_HOUR 0x08 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 +#define RX8130_REG_EXTENSION 0x0c +#define RX8130_REG_EXTENSION_WADA BIT(3) +#define RX8130_REG_FLAG 0x0d +#define RX8130_REG_FLAG_AF BIT(3) +#define RX8130_REG_CONTROL0 0x0e +#define RX8130_REG_CONTROL0_AIE BIT(3) + +#define MCP794XX_REG_CONTROL 0x07 +# define MCP794XX_BIT_ALM0_EN 0x10 +# define MCP794XX_BIT_ALM1_EN 0x20 +#define MCP794XX_REG_ALARM0_BASE 0x0a +#define MCP794XX_REG_ALARM0_CTRL 0x0d +#define MCP794XX_REG_ALARM1_BASE 0x11 +#define MCP794XX_REG_ALARM1_CTRL 0x14 +# define MCP794XX_BIT_ALMX_IF BIT(3) +# define MCP794XX_BIT_ALMX_C0 BIT(4) +# define MCP794XX_BIT_ALMX_C1 BIT(5) +# define MCP794XX_BIT_ALMX_C2 BIT(6) +# define MCP794XX_BIT_ALMX_POL BIT(7) +# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ + MCP794XX_BIT_ALMX_C1 | \ + MCP794XX_BIT_ALMX_C2) + #define M41TXX_REG_CONTROL 0x07 # define M41TXX_BIT_OUT BIT(7) # define M41TXX_BIT_FT BIT(6) @@ -680,16 +706,6 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { * Alarm support for rx8130 devices. */ -#define RX8130_REG_ALARM_MIN 0x07 -#define RX8130_REG_ALARM_HOUR 0x08 -#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 -#define RX8130_REG_EXTENSION 0x0c -#define RX8130_REG_EXTENSION_WADA BIT(3) -#define RX8130_REG_FLAG 0x0d -#define RX8130_REG_FLAG_AF BIT(3) -#define RX8130_REG_CONTROL0 0x0e -#define RX8130_REG_CONTROL0_AIE BIT(3) - static irqreturn_t rx8130_irq(int irq, void *dev_id) { struct ds1307 *ds1307 = dev_id; @@ -839,22 +855,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) * Alarm support for mcp794xx devices. */ -#define MCP794XX_REG_CONTROL 0x07 -# define MCP794XX_BIT_ALM0_EN 0x10 -# define MCP794XX_BIT_ALM1_EN 0x20 -#define MCP794XX_REG_ALARM0_BASE 0x0a -#define MCP794XX_REG_ALARM0_CTRL 0x0d -#define MCP794XX_REG_ALARM1_BASE 0x11 -#define MCP794XX_REG_ALARM1_CTRL 0x14 -# define MCP794XX_BIT_ALMX_IF BIT(3) -# define MCP794XX_BIT_ALMX_C0 BIT(4) -# define MCP794XX_BIT_ALMX_C1 BIT(5) -# define MCP794XX_BIT_ALMX_C2 BIT(6) -# define MCP794XX_BIT_ALMX_POL BIT(7) -# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ - MCP794XX_BIT_ALMX_C1 | \ - MCP794XX_BIT_ALMX_C2) - static irqreturn_t mcp794xx_irq(int irq, void *dev_id) { struct ds1307 *ds1307 = dev_id; From d0e3f61b08e222d3c6895085487cbb8b54448abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 25 Jan 2019 15:35:55 +0100 Subject: [PATCH 1345/4277] rtc: ds1307: forward declare chips array instead of a bunch of functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There used to be 16 declarations for static functions. By just adding a declaration for the chips array and reordering the functions the 16 function declarations can be dropped. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 1200 +++++++++++++++++++------------------- 1 file changed, 586 insertions(+), 614 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index f7a47ee5cdf6..a4883d59d88d 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -184,22 +184,592 @@ struct chip_desc { bool); }; -static int ds1307_get_time(struct device *dev, struct rtc_time *t); -static int ds1307_set_time(struct device *dev, struct rtc_time *t); -static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t); -static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t); -static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled); -static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode); -static irqreturn_t rx8130_irq(int irq, void *dev_id); -static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t); -static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t); -static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled); -static irqreturn_t mcp794xx_irq(int irq, void *dev_id); -static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t); -static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t); -static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled); -static int m41txx_rtc_read_offset(struct device *dev, long *offset); -static int m41txx_rtc_set_offset(struct device *dev, long offset); +static const struct chip_desc chips[last_ds_type]; + +static int ds1307_get_time(struct device *dev, struct rtc_time *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int tmp, ret; + const struct chip_desc *chip = &chips[ds1307->type]; + u8 regs[7]; + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; + } + + dev_dbg(dev, "%s: %7ph\n", "read", regs); + + /* if oscillator fail bit is set, no data can be trusted */ + if (ds1307->type == m41t0 && + regs[DS1307_REG_MIN] & M41T0_BIT_OF) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } + + t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f); + t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f); + tmp = regs[DS1307_REG_HOUR] & 0x3f; + t->tm_hour = bcd2bin(tmp); + t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; + t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); + tmp = regs[DS1307_REG_MONTH] & 0x1f; + t->tm_mon = bcd2bin(tmp) - 1; + t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100; + + if (regs[chip->century_reg] & chip->century_bit && + IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) + t->tm_year += 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); + + return 0; +} + +static int ds1307_set_time(struct device *dev, struct rtc_time *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + const struct chip_desc *chip = &chips[ds1307->type]; + int result; + int tmp; + u8 regs[7]; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); + + if (t->tm_year < 100) + return -EINVAL; + +#ifdef CONFIG_RTC_DRV_DS1307_CENTURY + if (t->tm_year > (chip->century_bit ? 299 : 199)) + return -EINVAL; +#else + if (t->tm_year > 199) + return -EINVAL; +#endif + + regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec); + regs[DS1307_REG_MIN] = bin2bcd(t->tm_min); + regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); + regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); + regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); + regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); + + /* assume 20YY not 19YY */ + tmp = t->tm_year - 100; + regs[DS1307_REG_YEAR] = bin2bcd(tmp); + + if (chip->century_enable_bit) + regs[chip->century_reg] |= chip->century_enable_bit; + if (t->tm_year > 199 && chip->century_bit) + regs[chip->century_reg] |= chip->century_bit; + + if (ds1307->type == mcp794xx) { + /* + * these bits were cleared when preparing the date/time + * values and need to be set again before writing the + * regsfer out to the device. + */ + regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST; + regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; + } + + dev_dbg(dev, "%s: %7ph\n", "write", regs); + + result = regmap_bulk_write(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (result) { + dev_err(dev, "%s error %d\n", "write", result); + return result; + } + return 0; +} + +static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret; + u8 regs[9]; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* read all ALARM1, ALARM2, and status registers at once */ + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, + regs, sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "alarm read", ret); + return ret; + } + + dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", + ®s[0], ®s[4], ®s[7]); + + /* + * report alarm time (ALARM1); assume 24 hour and day-of-month modes, + * and that all four fields are checked matches + */ + t->time.tm_sec = bcd2bin(regs[0] & 0x7f); + t->time.tm_min = bcd2bin(regs[1] & 0x7f); + t->time.tm_hour = bcd2bin(regs[2] & 0x3f); + t->time.tm_mday = bcd2bin(regs[3] & 0x3f); + + /* ... and status */ + t->enabled = !!(regs[7] & DS1337_BIT_A1IE); + t->pending = !!(regs[8] & DS1337_BIT_A1I); + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, enabled=%d, pending=%d\n", + "alarm read", t->time.tm_sec, t->time.tm_min, + t->time.tm_hour, t->time.tm_mday, + t->enabled, t->pending); + + return 0; +} + +static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[9]; + u8 control, status; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, enabled=%d, pending=%d\n", + "alarm set", t->time.tm_sec, t->time.tm_min, + t->time.tm_hour, t->time.tm_mday, + t->enabled, t->pending); + + /* read current status of both alarms and the chip */ + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "alarm write", ret); + return ret; + } + control = regs[7]; + status = regs[8]; + + dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)", + ®s[0], ®s[4], control, status); + + /* set ALARM1, using 24 hour and day-of-month modes */ + regs[0] = bin2bcd(t->time.tm_sec); + regs[1] = bin2bcd(t->time.tm_min); + regs[2] = bin2bcd(t->time.tm_hour); + regs[3] = bin2bcd(t->time.tm_mday); + + /* set ALARM2 to non-garbage */ + regs[4] = 0; + regs[5] = 0; + regs[6] = 0; + + /* disable alarms */ + regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); + regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); + + ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "can't set alarm time\n"); + return ret; + } + + /* optionally enable ALARM1 */ + if (t->enabled) { + dev_dbg(dev, "alarm IRQ armed\n"); + regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]); + } + + return 0; +} + +static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -ENOTTY; + + return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, + enabled ? DS1337_BIT_A1IE : 0); +} + +static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode) +{ + u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : + DS1307_TRICKLE_CHARGER_NO_DIODE; + + switch (ohms) { + case 250: + setup |= DS1307_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + setup |= DS1307_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + setup |= DS1307_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(ds1307->dev, + "Unsupported ohm value %u in dt\n", ohms); + return 0; + } + return setup; +} + +static irqreturn_t rx8130_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + u8 ctl[3]; + int ret; + + mutex_lock(lock); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + goto out; + if (!(ctl[1] & RX8130_REG_FLAG_AF)) + goto out; + ctl[1] &= ~RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read alarm registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); + if (ret < 0) + return ret; + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE); + t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = -1; + t->time.tm_min = bcd2bin(ald[0] & 0x7f); + t->time.tm_hour = bcd2bin(ald[1] & 0x7f); + t->time.tm_wday = -1; + t->time.tm_mday = bcd2bin(ald[2] & 0x7f); + t->time.tm_mon = -1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%d\n", + __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled); + + return 0; +} + +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + ctl[0] &= ~RX8130_REG_EXTENSION_WADA; + ctl[1] |= RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + /* Hardware alarm precision is 1 minute! */ + ald[0] = bin2bcd(t->time.tm_min); + ald[1] = bin2bcd(t->time.tm_hour); + ald[2] = bin2bcd(t->time.tm_mday); + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); + if (ret < 0) + return ret; + + if (!t->enabled) + return 0; + + ctl[2] |= RX8130_REG_CONTROL0_AIE; + + return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); +} + +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret, reg; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); + if (ret < 0) + return ret; + + if (enabled) + reg |= RX8130_REG_CONTROL0_AIE; + else + reg &= ~RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); +} + +static irqreturn_t mcp794xx_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int reg, ret; + + mutex_lock(lock); + + /* Check and clear alarm 0 interrupt flag. */ + ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); + if (ret) + goto out; + if (!(reg & MCP794XX_BIT_ALMX_IF)) + goto out; + reg &= ~MCP794XX_BIT_ALMX_IF; + ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); + if (ret) + goto out; + + /* Disable alarm 0. */ + ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 regs[10]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = bcd2bin(regs[3] & 0x7f); + t->time.tm_min = bcd2bin(regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, + !!(regs[6] & MCP794XX_BIT_ALMX_POL), + !!(regs[6] & MCP794XX_BIT_ALMX_IF), + (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); + + return 0; +} + +/* + * We may have a random RTC weekday, therefore calculate alarm weekday based + * on current weekday we read from the RTC timekeeping regs + */ +static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) +{ + struct rtc_time tm_now; + int days_now, days_alarm, ret; + + ret = ds1307_get_time(dev, &tm_now); + if (ret) + return ret; + + days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); + days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); + + return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; +} + +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[10]; + int wday, ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + wday = mcp794xx_alm_weekday(dev, &t->time); + if (wday < 0) + return wday; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + /* Set alarm 0, using 24-hour and day-of-month modes. */ + regs[3] = bin2bcd(t->time.tm_sec); + regs[4] = bin2bcd(t->time.tm_min); + regs[5] = bin2bcd(t->time.tm_hour); + regs[6] = wday; + regs[7] = bin2bcd(t->time.tm_mday); + regs[8] = bin2bcd(t->time.tm_mon + 1); + + /* Clear the alarm 0 interrupt flag. */ + regs[6] &= ~MCP794XX_BIT_ALMX_IF; + /* Set alarm match: second, minute, hour, day, date, month. */ + regs[6] |= MCP794XX_MSK_ALMX_MATCH; + /* Disable interrupt. We will not enable until completely programmed */ + regs[0] &= ~MCP794XX_BIT_ALM0_EN; + + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + if (!t->enabled) + return 0; + regs[0] |= MCP794XX_BIT_ALM0_EN; + return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); +} + +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, + enabled ? MCP794XX_BIT_ALM0_EN : 0); +} + +static int m41txx_rtc_read_offset(struct device *dev, long *offset) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned int ctrl_reg; + u8 val; + + regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg); + + val = ctrl_reg & M41TXX_M_CALIBRATION; + + /* check if positive */ + if (ctrl_reg & M41TXX_BIT_CALIB_SIGN) + *offset = (val * M41TXX_POS_OFFSET_STEP_PPB); + else + *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB); + + return 0; +} + +static int m41txx_rtc_set_offset(struct device *dev, long offset) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned int ctrl_reg; + + if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET)) + return -ERANGE; + + if (offset >= 0) { + ctrl_reg = DIV_ROUND_CLOSEST(offset, + M41TXX_POS_OFFSET_STEP_PPB); + ctrl_reg |= M41TXX_BIT_CALIB_SIGN; + } else { + ctrl_reg = DIV_ROUND_CLOSEST(abs(offset), + M41TXX_NEG_OFFSET_STEP_PPB); + } + + return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, + M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN, + ctrl_reg); +} static const struct rtc_class_ops rx8130_rtc_ops = { .read_time = ds1307_get_time, @@ -468,230 +1038,6 @@ out: /*----------------------------------------------------------------------*/ -static int ds1307_get_time(struct device *dev, struct rtc_time *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - int tmp, ret; - const struct chip_desc *chip = &chips[ds1307->type]; - u8 regs[7]; - - /* read the RTC date and time registers all at once */ - ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, - sizeof(regs)); - if (ret) { - dev_err(dev, "%s error %d\n", "read", ret); - return ret; - } - - dev_dbg(dev, "%s: %7ph\n", "read", regs); - - /* if oscillator fail bit is set, no data can be trusted */ - if (ds1307->type == m41t0 && - regs[DS1307_REG_MIN] & M41T0_BIT_OF) { - dev_warn_once(dev, "oscillator failed, set time!\n"); - return -EINVAL; - } - - t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f); - t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f); - tmp = regs[DS1307_REG_HOUR] & 0x3f; - t->tm_hour = bcd2bin(tmp); - t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; - t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); - tmp = regs[DS1307_REG_MONTH] & 0x1f; - t->tm_mon = bcd2bin(tmp) - 1; - t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100; - - if (regs[chip->century_reg] & chip->century_bit && - IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) - t->tm_year += 100; - - dev_dbg(dev, "%s secs=%d, mins=%d, " - "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", - "read", t->tm_sec, t->tm_min, - t->tm_hour, t->tm_mday, - t->tm_mon, t->tm_year, t->tm_wday); - - return 0; -} - -static int ds1307_set_time(struct device *dev, struct rtc_time *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - const struct chip_desc *chip = &chips[ds1307->type]; - int result; - int tmp; - u8 regs[7]; - - dev_dbg(dev, "%s secs=%d, mins=%d, " - "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", - "write", t->tm_sec, t->tm_min, - t->tm_hour, t->tm_mday, - t->tm_mon, t->tm_year, t->tm_wday); - - if (t->tm_year < 100) - return -EINVAL; - -#ifdef CONFIG_RTC_DRV_DS1307_CENTURY - if (t->tm_year > (chip->century_bit ? 299 : 199)) - return -EINVAL; -#else - if (t->tm_year > 199) - return -EINVAL; -#endif - - regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec); - regs[DS1307_REG_MIN] = bin2bcd(t->tm_min); - regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); - regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); - regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); - regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); - - /* assume 20YY not 19YY */ - tmp = t->tm_year - 100; - regs[DS1307_REG_YEAR] = bin2bcd(tmp); - - if (chip->century_enable_bit) - regs[chip->century_reg] |= chip->century_enable_bit; - if (t->tm_year > 199 && chip->century_bit) - regs[chip->century_reg] |= chip->century_bit; - - if (ds1307->type == mcp794xx) { - /* - * these bits were cleared when preparing the date/time - * values and need to be set again before writing the - * regsfer out to the device. - */ - regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST; - regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; - } - - dev_dbg(dev, "%s: %7ph\n", "write", regs); - - result = regmap_bulk_write(ds1307->regmap, chip->offset, regs, - sizeof(regs)); - if (result) { - dev_err(dev, "%s error %d\n", "write", result); - return result; - } - return 0; -} - -static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - int ret; - u8 regs[9]; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - /* read all ALARM1, ALARM2, and status registers at once */ - ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, - regs, sizeof(regs)); - if (ret) { - dev_err(dev, "%s error %d\n", "alarm read", ret); - return ret; - } - - dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", - ®s[0], ®s[4], ®s[7]); - - /* - * report alarm time (ALARM1); assume 24 hour and day-of-month modes, - * and that all four fields are checked matches - */ - t->time.tm_sec = bcd2bin(regs[0] & 0x7f); - t->time.tm_min = bcd2bin(regs[1] & 0x7f); - t->time.tm_hour = bcd2bin(regs[2] & 0x3f); - t->time.tm_mday = bcd2bin(regs[3] & 0x3f); - - /* ... and status */ - t->enabled = !!(regs[7] & DS1337_BIT_A1IE); - t->pending = !!(regs[8] & DS1337_BIT_A1I); - - dev_dbg(dev, "%s secs=%d, mins=%d, " - "hours=%d, mday=%d, enabled=%d, pending=%d\n", - "alarm read", t->time.tm_sec, t->time.tm_min, - t->time.tm_hour, t->time.tm_mday, - t->enabled, t->pending); - - return 0; -} - -static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char regs[9]; - u8 control, status; - int ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - dev_dbg(dev, "%s secs=%d, mins=%d, " - "hours=%d, mday=%d, enabled=%d, pending=%d\n", - "alarm set", t->time.tm_sec, t->time.tm_min, - t->time.tm_hour, t->time.tm_mday, - t->enabled, t->pending); - - /* read current status of both alarms and the chip */ - ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, - sizeof(regs)); - if (ret) { - dev_err(dev, "%s error %d\n", "alarm write", ret); - return ret; - } - control = regs[7]; - status = regs[8]; - - dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)", - ®s[0], ®s[4], control, status); - - /* set ALARM1, using 24 hour and day-of-month modes */ - regs[0] = bin2bcd(t->time.tm_sec); - regs[1] = bin2bcd(t->time.tm_min); - regs[2] = bin2bcd(t->time.tm_hour); - regs[3] = bin2bcd(t->time.tm_mday); - - /* set ALARM2 to non-garbage */ - regs[4] = 0; - regs[5] = 0; - regs[6] = 0; - - /* disable alarms */ - regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); - regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - - ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, - sizeof(regs)); - if (ret) { - dev_err(dev, "can't set alarm time\n"); - return ret; - } - - /* optionally enable ALARM1 */ - if (t->enabled) { - dev_dbg(dev, "alarm IRQ armed\n"); - regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ - regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]); - } - - return 0; -} - -static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -ENOTTY; - - return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, - DS1337_BIT_A1IE, - enabled ? DS1337_BIT_A1IE : 0); -} - static const struct rtc_class_ops ds13xx_rtc_ops = { .read_time = ds1307_get_time, .set_time = ds1307_set_time, @@ -700,356 +1046,6 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { .alarm_irq_enable = ds1307_alarm_irq_enable, }; -/*----------------------------------------------------------------------*/ - -/* - * Alarm support for rx8130 devices. - */ - -static irqreturn_t rx8130_irq(int irq, void *dev_id) -{ - struct ds1307 *ds1307 = dev_id; - struct mutex *lock = &ds1307->rtc->ops_lock; - u8 ctl[3]; - int ret; - - mutex_lock(lock); - - /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); - if (ret < 0) - goto out; - if (!(ctl[1] & RX8130_REG_FLAG_AF)) - goto out; - ctl[1] &= ~RX8130_REG_FLAG_AF; - ctl[2] &= ~RX8130_REG_CONTROL0_AIE; - - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); - if (ret < 0) - goto out; - - rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); - -out: - mutex_unlock(lock); - - return IRQ_HANDLED; -} - -static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - u8 ald[3], ctl[3]; - int ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - /* Read alarm registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, - sizeof(ald)); - if (ret < 0) - return ret; - - /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); - if (ret < 0) - return ret; - - t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE); - t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF); - - /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ - t->time.tm_sec = -1; - t->time.tm_min = bcd2bin(ald[0] & 0x7f); - t->time.tm_hour = bcd2bin(ald[1] & 0x7f); - t->time.tm_wday = -1; - t->time.tm_mday = bcd2bin(ald[2] & 0x7f); - t->time.tm_mon = -1; - t->time.tm_year = -1; - t->time.tm_yday = -1; - t->time.tm_isdst = -1; - - dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%d\n", - __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, - t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled); - - return 0; -} - -static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - u8 ald[3], ctl[3]; - int ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d pending=%d\n", __func__, - t->time.tm_sec, t->time.tm_min, t->time.tm_hour, - t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, - t->enabled, t->pending); - - /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); - if (ret < 0) - return ret; - - ctl[0] &= ~RX8130_REG_EXTENSION_WADA; - ctl[1] |= RX8130_REG_FLAG_AF; - ctl[2] &= ~RX8130_REG_CONTROL0_AIE; - - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); - if (ret < 0) - return ret; - - /* Hardware alarm precision is 1 minute! */ - ald[0] = bin2bcd(t->time.tm_min); - ald[1] = bin2bcd(t->time.tm_hour); - ald[2] = bin2bcd(t->time.tm_mday); - - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, - sizeof(ald)); - if (ret < 0) - return ret; - - if (!t->enabled) - return 0; - - ctl[2] |= RX8130_REG_CONTROL0_AIE; - - return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); -} - -static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - int ret, reg; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); - if (ret < 0) - return ret; - - if (enabled) - reg |= RX8130_REG_CONTROL0_AIE; - else - reg &= ~RX8130_REG_CONTROL0_AIE; - - return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); -} - -/*----------------------------------------------------------------------*/ - -/* - * Alarm support for mcp794xx devices. - */ - -static irqreturn_t mcp794xx_irq(int irq, void *dev_id) -{ - struct ds1307 *ds1307 = dev_id; - struct mutex *lock = &ds1307->rtc->ops_lock; - int reg, ret; - - mutex_lock(lock); - - /* Check and clear alarm 0 interrupt flag. */ - ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); - if (ret) - goto out; - if (!(reg & MCP794XX_BIT_ALMX_IF)) - goto out; - reg &= ~MCP794XX_BIT_ALMX_IF; - ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); - if (ret) - goto out; - - /* Disable alarm 0. */ - ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, - MCP794XX_BIT_ALM0_EN, 0); - if (ret) - goto out; - - rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); - -out: - mutex_unlock(lock); - - return IRQ_HANDLED; -} - -static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - u8 regs[10]; - int ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, - sizeof(regs)); - if (ret) - return ret; - - t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); - - /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ - t->time.tm_sec = bcd2bin(regs[3] & 0x7f); - t->time.tm_min = bcd2bin(regs[4] & 0x7f); - t->time.tm_hour = bcd2bin(regs[5] & 0x3f); - t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; - t->time.tm_mday = bcd2bin(regs[7] & 0x3f); - t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; - t->time.tm_year = -1; - t->time.tm_yday = -1; - t->time.tm_isdst = -1; - - dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, - t->time.tm_sec, t->time.tm_min, t->time.tm_hour, - t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, - !!(regs[6] & MCP794XX_BIT_ALMX_POL), - !!(regs[6] & MCP794XX_BIT_ALMX_IF), - (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); - - return 0; -} - -/* - * We may have a random RTC weekday, therefore calculate alarm weekday based - * on current weekday we read from the RTC timekeeping regs - */ -static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) -{ - struct rtc_time tm_now; - int days_now, days_alarm, ret; - - ret = ds1307_get_time(dev, &tm_now); - if (ret) - return ret; - - days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); - days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); - - return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; -} - -static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char regs[10]; - int wday, ret; - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - wday = mcp794xx_alm_weekday(dev, &t->time); - if (wday < 0) - return wday; - - dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d pending=%d\n", __func__, - t->time.tm_sec, t->time.tm_min, t->time.tm_hour, - t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, - t->enabled, t->pending); - - /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, - sizeof(regs)); - if (ret) - return ret; - - /* Set alarm 0, using 24-hour and day-of-month modes. */ - regs[3] = bin2bcd(t->time.tm_sec); - regs[4] = bin2bcd(t->time.tm_min); - regs[5] = bin2bcd(t->time.tm_hour); - regs[6] = wday; - regs[7] = bin2bcd(t->time.tm_mday); - regs[8] = bin2bcd(t->time.tm_mon + 1); - - /* Clear the alarm 0 interrupt flag. */ - regs[6] &= ~MCP794XX_BIT_ALMX_IF; - /* Set alarm match: second, minute, hour, day, date, month. */ - regs[6] |= MCP794XX_MSK_ALMX_MATCH; - /* Disable interrupt. We will not enable until completely programmed */ - regs[0] &= ~MCP794XX_BIT_ALM0_EN; - - ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, - sizeof(regs)); - if (ret) - return ret; - - if (!t->enabled) - return 0; - regs[0] |= MCP794XX_BIT_ALM0_EN; - return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); -} - -static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - - if (!test_bit(HAS_ALARM, &ds1307->flags)) - return -EINVAL; - - return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, - MCP794XX_BIT_ALM0_EN, - enabled ? MCP794XX_BIT_ALM0_EN : 0); -} - -static int m41txx_rtc_read_offset(struct device *dev, long *offset) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned int ctrl_reg; - u8 val; - - regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg); - - val = ctrl_reg & M41TXX_M_CALIBRATION; - - /* check if positive */ - if (ctrl_reg & M41TXX_BIT_CALIB_SIGN) - *offset = (val * M41TXX_POS_OFFSET_STEP_PPB); - else - *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB); - - return 0; -} - -static int m41txx_rtc_set_offset(struct device *dev, long offset) -{ - struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned int ctrl_reg; - - if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET)) - return -ERANGE; - - if (offset >= 0) { - ctrl_reg = DIV_ROUND_CLOSEST(offset, - M41TXX_POS_OFFSET_STEP_PPB); - ctrl_reg |= M41TXX_BIT_CALIB_SIGN; - } else { - ctrl_reg = DIV_ROUND_CLOSEST(abs(offset), - M41TXX_NEG_OFFSET_STEP_PPB); - } - - return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, - M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN, - ctrl_reg); -} - static ssize_t frequency_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1137,30 +1133,6 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, /*----------------------------------------------------------------------*/ -static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, - u32 ohms, bool diode) -{ - u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : - DS1307_TRICKLE_CHARGER_NO_DIODE; - - switch (ohms) { - case 250: - setup |= DS1307_TRICKLE_CHARGER_250_OHM; - break; - case 2000: - setup |= DS1307_TRICKLE_CHARGER_2K_OHM; - break; - case 4000: - setup |= DS1307_TRICKLE_CHARGER_4K_OHM; - break; - default: - dev_warn(ds1307->dev, - "Unsupported ohm value %u in dt\n", ohms); - return 0; - } - return setup; -} - static u8 ds1307_trickle_init(struct ds1307 *ds1307, const struct chip_desc *chip) { From 3ffd4a2fcf52c1170654c68459fff40773a729ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 25 Jan 2019 15:35:56 +0100 Subject: [PATCH 1346/4277] rtc: ds1307: correct register offset for rx8130 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While rx8130 has a register offset of 0x10 in its chip_desc, this isn't used when regmap accesses are done. So add 0x10 to access the right locations. Fixes: ee0981be7704 ("rtc: ds1307: Add support for Epson RX8130CE") Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index a4883d59d88d..81e20653700d 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -114,14 +114,15 @@ enum ds_type { # define RX8025_BIT_VDET 0x40 # define RX8025_BIT_XST 0x20 -#define RX8130_REG_ALARM_MIN 0x07 -#define RX8130_REG_ALARM_HOUR 0x08 -#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 -#define RX8130_REG_EXTENSION 0x0c +#define RX8130_REG_ALARM_MIN 0x17 +#define RX8130_REG_ALARM_HOUR 0x18 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x19 +#define RX8130_REG_EXTENSION 0x1c #define RX8130_REG_EXTENSION_WADA BIT(3) -#define RX8130_REG_FLAG 0x0d +#define RX8130_REG_FLAG 0x1d +#define RX8130_REG_FLAG_VLF BIT(1) #define RX8130_REG_FLAG_AF BIT(3) -#define RX8130_REG_CONTROL0 0x0e +#define RX8130_REG_CONTROL0 0x1e #define RX8130_REG_CONTROL0_AIE BIT(3) #define MCP794XX_REG_CONTROL 0x07 From 501f98266caddd2b76cdf10093105aa2318d1421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 25 Jan 2019 15:35:57 +0100 Subject: [PATCH 1347/4277] rtc: ds1307: rx8130: honor Voltage Loss Flag when reading the time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When voltage dropped since the RTC was last set the reported time is not reliable. In this case return an error indicator instead of a bogus time. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 81e20653700d..974eafda9de4 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -194,6 +194,20 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) const struct chip_desc *chip = &chips[ds1307->type]; u8 regs[7]; + if (ds1307->type == rx_8130) { + unsigned int regflag; + ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, ®flag); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; + } + + if (regflag & RX8130_REG_FLAG_VLF) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } + } + /* read the RTC date and time registers all at once */ ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, sizeof(regs)); @@ -293,6 +307,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) dev_err(dev, "%s error %d\n", "write", result); return result; } + + if (ds1307->type == rx_8130) { + /* clear Voltage Loss Flag as data is available now */ + result = regmap_write(ds1307->regmap, RX8130_REG_FLAG, + ~(u8)RX8130_REG_FLAG_VLF); + if (result) { + dev_err(dev, "%s error %d\n", "write", result); + return result; + } + } + return 0; } From 3f929cad943380370b6db31fcb7a38d898d91089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 25 Jan 2019 15:35:58 +0100 Subject: [PATCH 1348/4277] rtc: ds1307: rx8130: Fix alarm handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the EXTENSION.WADA bit is set, register 0x19 contains a bitmap of week days, not a day of month. As Linux only handles a single alarm without repetition using day of month is more flexible, so clear this bit. (Otherwise a value depending on time.tm_wday would have to be written to register 0x19.) Also optimize setting the AIE bit to use a single register write instead of a bulk write of three registers. Fixes: ee0981be7704 ("rtc: ds1307: Add support for Epson RX8130CE") Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 974eafda9de4..07530fe1da2a 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -554,8 +554,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) if (ret < 0) return ret; - ctl[0] &= ~RX8130_REG_EXTENSION_WADA; - ctl[1] |= RX8130_REG_FLAG_AF; + ctl[0] &= RX8130_REG_EXTENSION_WADA; + ctl[1] &= ~RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, @@ -578,8 +578,7 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[2] |= RX8130_REG_CONTROL0_AIE; - return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]); } static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) From 1a7a05e88fa0e4e168f83585d1bb1937197a9745 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 6 Feb 2019 17:31:55 -0800 Subject: [PATCH 1349/4277] Documentation/infiniband: update from locked to pinned_vm We are really talking about pinned_vm here. Signed-off-by: Davidlohr Bueso Signed-off-by: Jason Gunthorpe --- Documentation/infiniband/user_verbs.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/infiniband/user_verbs.txt b/Documentation/infiniband/user_verbs.txt index df049b9f5b6e..47ebf2f80b2b 100644 --- a/Documentation/infiniband/user_verbs.txt +++ b/Documentation/infiniband/user_verbs.txt @@ -46,11 +46,11 @@ Memory pinning I/O targets be kept resident at the same physical address. The ib_uverbs module manages pinning and unpinning memory regions via get_user_pages() and put_page() calls. It also accounts for the - amount of memory pinned in the process's locked_vm, and checks that + amount of memory pinned in the process's pinned_vm, and checks that unprivileged processes do not exceed their RLIMIT_MEMLOCK limit. Pages that are pinned multiple times are counted each time they are - pinned, so the value of locked_vm may be an overestimate of the + pinned, so the value of pinned_vm may be an overestimate of the number of pages pinned by a process. /dev files From af8b38ed0b9d34db940a52418e7849904336ace5 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Wed, 6 Feb 2019 15:45:35 +0200 Subject: [PATCH 1350/4277] IB/mlx5: Simplify WQE count power of two check Use is_power_of_2() instead of hard coding it in the driver. While at it, fix the meaningless error print. Signed-off-by: Gal Pressman Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/qp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 664d5c6e72e4..4f2bc101b061 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -533,9 +533,9 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev, return -EINVAL; } - if (ucmd->sq_wqe_count && ((1 << ilog2(ucmd->sq_wqe_count)) != ucmd->sq_wqe_count)) { - mlx5_ib_warn(dev, "sq_wqe_count %d, sq_wqe_count %d\n", - ucmd->sq_wqe_count, ucmd->sq_wqe_count); + if (ucmd->sq_wqe_count && !is_power_of_2(ucmd->sq_wqe_count)) { + mlx5_ib_warn(dev, "sq_wqe_count %d is not a power of two\n", + ucmd->sq_wqe_count); return -EINVAL; } From ae8637e13185ab05b45bcf35f769f7f627c6714c Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:22 -0500 Subject: [PATCH 1351/4277] RDMA/bnxt_re: Add chip context to identify 57500 series Adding setup and destroy routines for chip-context. The chip context would be used frequently in control and data path to take execution flow depending on the chip type. chip context structure pointer is added to the relevant data structures. Signed-off-by: Selvin Xavier Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/bnxt_re.h | 1 + drivers/infiniband/hw/bnxt_re/main.c | 34 ++++++++++++++++++++++ drivers/infiniband/hw/bnxt_re/qplib_fp.h | 1 + drivers/infiniband/hw/bnxt_re/qplib_rcfw.h | 1 + drivers/infiniband/hw/bnxt_re/qplib_res.h | 15 +++++++++- 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h index 31baa8939a4f..e55a1666c0cd 100644 --- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -124,6 +124,7 @@ struct bnxt_re_dev { #define BNXT_RE_FLAG_ISSUE_ROCE_STATS 29 struct net_device *netdev; unsigned int version, major, minor; + struct bnxt_qplib_chip_ctx chip_ctx; struct bnxt_en_dev *en_dev; struct bnxt_msix_entry msix_entries[BNXT_RE_MAX_MSIX]; int num_msix; diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 08777506d0b1..60b5c4876354 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -80,6 +80,29 @@ static DEFINE_MUTEX(bnxt_re_dev_lock); static struct workqueue_struct *bnxt_re_wq; static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev); +static void bnxt_re_destroy_chip_ctx(struct bnxt_re_dev *rdev) +{ + rdev->rcfw.res = NULL; + rdev->qplib_res.cctx = NULL; +} + +static int bnxt_re_setup_chip_ctx(struct bnxt_re_dev *rdev) +{ + struct bnxt_en_dev *en_dev; + struct bnxt *bp; + + en_dev = rdev->en_dev; + bp = netdev_priv(en_dev->net); + + rdev->chip_ctx.chip_num = bp->chip_num; + /* rest members to follow eventually */ + + rdev->qplib_res.cctx = &rdev->chip_ctx; + rdev->rcfw.res = &rdev->qplib_res; + + return 0; +} + /* SR-IOV helper functions */ static void bnxt_re_get_sriov_func_type(struct bnxt_re_dev *rdev) @@ -278,6 +301,7 @@ static int bnxt_re_register_netdev(struct bnxt_re_dev *rdev) rc = en_dev->en_ops->bnxt_register_device(en_dev, BNXT_ROCE_ULP, &bnxt_re_ulp_ops, rdev); + rdev->qplib_res.pdev = rdev->en_dev->pdev; return rc; } @@ -905,6 +929,7 @@ static void bnxt_re_free_nq_res(struct bnxt_re_dev *rdev) int i; for (i = 0; i < rdev->num_msix - 1; i++) { + rdev->nq[i].res = NULL; bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id); bnxt_qplib_free_nq(&rdev->nq[i]); } @@ -949,6 +974,7 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) goto dealloc_res; for (i = 0; i < rdev->num_msix - 1; i++) { + rdev->nq[i].res = &rdev->qplib_res; rdev->nq[i].hwq.max_elements = BNXT_RE_MAX_CQ_COUNT + BNXT_RE_MAX_SRQC_COUNT + 2; rc = bnxt_qplib_alloc_nq(rdev->en_dev->pdev, &rdev->nq[i]); @@ -1262,6 +1288,8 @@ static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev) dev_warn(rdev_to_dev(rdev), "Failed to free MSI-X vectors: %#x", rc); } + + bnxt_re_destroy_chip_ctx(rdev); if (test_and_clear_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags)) { rc = bnxt_re_unregister_netdev(rdev); if (rc) @@ -1299,6 +1327,12 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev) } set_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags); + rc = bnxt_re_setup_chip_ctx(rdev); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to get chip context\n"); + return -EINVAL; + } + /* Check whether VF or PF */ bnxt_re_get_sriov_func_type(rdev); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index 72352ca80ace..f317bae36dd0 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -439,6 +439,7 @@ struct bnxt_qplib_cq { struct bnxt_qplib_nq { struct pci_dev *pdev; + struct bnxt_qplib_res *res; int vector; cpumask_t mask; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h index be0ef0e8c53e..8011d674391e 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -187,6 +187,7 @@ struct bnxt_qplib_qp_node { /* RCFW Communication Channels */ struct bnxt_qplib_rcfw { struct pci_dev *pdev; + struct bnxt_qplib_res *res; int vector; struct tasklet_struct worker; bool requested; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h index 1e80aa7bbcce..6a0c45827053 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -180,12 +180,20 @@ struct bnxt_qplib_ctx { u64 hwrm_intf_ver; }; +struct bnxt_qplib_chip_ctx { + u16 chip_num; + u8 chip_rev; + u8 chip_metal; +}; + +#define CHIP_NUM_57500 0x1750 + struct bnxt_qplib_res { struct pci_dev *pdev; + struct bnxt_qplib_chip_ctx *cctx; struct net_device *netdev; struct bnxt_qplib_rcfw *rcfw; - struct bnxt_qplib_pd_tbl pd_tbl; struct bnxt_qplib_sgid_tbl sgid_tbl; struct bnxt_qplib_pkey_tbl pkey_tbl; @@ -193,6 +201,11 @@ struct bnxt_qplib_res { bool prio; }; +static inline bool bnxt_qplib_is_chip_gen_p5(struct bnxt_qplib_chip_ctx *cctx) +{ + return (cctx->chip_num == CHIP_NUM_57500); +} + #define to_bnxt_qplib(ptr, type, member) \ container_of(ptr, type, member) From b353ce556d521351eb7daf609e446f3595a6fad6 Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:23 -0500 Subject: [PATCH 1352/4277] RDMA/bnxt_re: Add 64bit doorbells for 57500 series The new chip series has 64 bit doorbell for notification queues. Thus, both control and data path event queues need new routines to write 64 bit doorbell. Adding the same. There is new doorbell interface between the chip and driver. Changing the chip specific data structure definitions. Additional significant changes are listed below - bnxt_re_net_ring_free/alloc takes a new argument - bnxt_qplib_enable_nq and enable_rcfw uses new doorbell offset for new chip. - DB mapping for NQ and CREQ now maps 8 bytes. - DBR_DBR_* macros renames to DBC_DBC_* - store nq_db_offset in a 32bit data type. - got rid of __iowrite64_copy, used writeq instead. - changed the DB header initialization to simpler scheme. Signed-off-by: Selvin Xavier Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/Kconfig | 1 + drivers/infiniband/hw/bnxt_re/ib_verbs.c | 4 +- drivers/infiniband/hw/bnxt_re/main.c | 85 ++++++++++------ drivers/infiniband/hw/bnxt_re/qplib_fp.c | 112 ++++++++++----------- drivers/infiniband/hw/bnxt_re/qplib_fp.h | 43 +++++++- drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 31 ++++-- drivers/infiniband/hw/bnxt_re/qplib_rcfw.h | 44 +++++++- drivers/infiniband/hw/bnxt_re/qplib_res.h | 13 +++ drivers/infiniband/hw/bnxt_re/roce_hsi.h | 92 +++++++++-------- 9 files changed, 274 insertions(+), 151 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/Kconfig b/drivers/infiniband/hw/bnxt_re/Kconfig index 18f5ed082f41..be2fdad248eb 100644 --- a/drivers/infiniband/hw/bnxt_re/Kconfig +++ b/drivers/infiniband/hw/bnxt_re/Kconfig @@ -1,5 +1,6 @@ config INFINIBAND_BNXT_RE tristate "Broadcom Netxtreme HCA support" + depends on 64BIT depends on ETHERNET && NETDEVICES && PCI && INET && DCB depends on MAY_USE_DEVLINK select NET_VENDOR_BROADCOM diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 9bc637e49faa..3e3e481b2fb8 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -3299,10 +3299,10 @@ int bnxt_re_req_notify_cq(struct ib_cq *ib_cq, spin_lock_irqsave(&cq->cq_lock, flags); /* Trigger on the very next completion */ if (ib_cqn_flags & IB_CQ_NEXT_COMP) - type = DBR_DBR_TYPE_CQ_ARMALL; + type = DBC_DBC_TYPE_CQ_ARMALL; /* Trigger on the next solicited completion */ else if (ib_cqn_flags & IB_CQ_SOLICITED) - type = DBR_DBR_TYPE_CQ_ARMSE; + type = DBC_DBC_TYPE_CQ_ARMSE; /* Poll to see if there are missed events */ if ((ib_cqn_flags & IB_CQ_REPORT_MISSED_EVENTS) && diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 60b5c4876354..0df180850a75 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -369,7 +369,8 @@ static void bnxt_re_fill_fw_msg(struct bnxt_fw_msg *fw_msg, void *msg, fw_msg->timeout = timeout; } -static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id) +static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, + u16 fw_ring_id, int type) { struct bnxt_en_dev *en_dev = rdev->en_dev; struct hwrm_ring_free_input req = {0}; @@ -383,7 +384,7 @@ static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id) memset(&fw_msg, 0, sizeof(fw_msg)); bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_RING_FREE, -1, -1); - req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL; + req.ring_type = type; req.ring_id = cpu_to_le16(fw_ring_id); bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); @@ -420,7 +421,7 @@ static int bnxt_re_net_ring_alloc(struct bnxt_re_dev *rdev, dma_addr_t *dma_arr, /* Association of ring index with doorbell index and MSIX number */ req.logical_id = cpu_to_le16(map_index); req.length = cpu_to_le32(ring_mask + 1); - req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL; + req.ring_type = type; req.int_mode = RING_ALLOC_REQ_INT_MODE_MSIX; bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); @@ -884,6 +885,12 @@ static int bnxt_re_cqn_handler(struct bnxt_qplib_nq *nq, return 0; } +static u32 bnxt_re_get_nqdb_offset(struct bnxt_re_dev *rdev, u16 indx) +{ + return bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx) ? + 0x10000 : rdev->msix_entries[indx].db_offset; +} + static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev) { int i; @@ -897,18 +904,18 @@ static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev) static int bnxt_re_init_res(struct bnxt_re_dev *rdev) { - int rc = 0, i; int num_vec_enabled = 0; + int rc = 0, i; + u32 db_offt; bnxt_qplib_init_res(&rdev->qplib_res); for (i = 1; i < rdev->num_msix ; i++) { + db_offt = bnxt_re_get_nqdb_offset(rdev, i); rc = bnxt_qplib_enable_nq(rdev->en_dev->pdev, &rdev->nq[i - 1], i - 1, rdev->msix_entries[i].vector, - rdev->msix_entries[i].db_offset, - &bnxt_re_cqn_handler, + db_offt, &bnxt_re_cqn_handler, &bnxt_re_srqn_handler); - if (rc) { dev_err(rdev_to_dev(rdev), "Failed to enable NQ with rc = 0x%x", rc); @@ -920,17 +927,18 @@ static int bnxt_re_init_res(struct bnxt_re_dev *rdev) fail: for (i = num_vec_enabled; i >= 0; i--) bnxt_qplib_disable_nq(&rdev->nq[i]); - return rc; } static void bnxt_re_free_nq_res(struct bnxt_re_dev *rdev) { + u8 type; int i; for (i = 0; i < rdev->num_msix - 1; i++) { + type = bnxt_qplib_get_ring_type(&rdev->chip_ctx); + bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id, type); rdev->nq[i].res = NULL; - bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id); bnxt_qplib_free_nq(&rdev->nq[i]); } } @@ -952,8 +960,11 @@ static void bnxt_re_free_res(struct bnxt_re_dev *rdev) static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) { - int rc = 0, i; int num_vec_created = 0; + dma_addr_t *pg_map; + int rc = 0, i; + int pages; + u8 type; /* Configure and allocate resources for qplib */ rdev->qplib_res.rcfw = &rdev->rcfw; @@ -983,13 +994,13 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) i, rc); goto free_nq; } - rc = bnxt_re_net_ring_alloc - (rdev, rdev->nq[i].hwq.pbl[PBL_LVL_0].pg_map_arr, - rdev->nq[i].hwq.pbl[rdev->nq[i].hwq.level].pg_count, - HWRM_RING_ALLOC_CMPL, - BNXT_QPLIB_NQE_MAX_CNT - 1, - rdev->msix_entries[i + 1].ring_idx, - &rdev->nq[i].ring_id); + type = bnxt_qplib_get_ring_type(&rdev->chip_ctx); + pg_map = rdev->nq[i].hwq.pbl[PBL_LVL_0].pg_map_arr; + pages = rdev->nq[i].hwq.pbl[rdev->nq[i].hwq.level].pg_count; + rc = bnxt_re_net_ring_alloc(rdev, pg_map, pages, type, + BNXT_QPLIB_NQE_MAX_CNT - 1, + rdev->msix_entries[i + 1].ring_idx, + &rdev->nq[i].ring_id); if (rc) { dev_err(rdev_to_dev(rdev), "Failed to allocate NQ fw id with rc = 0x%x", @@ -1002,7 +1013,8 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) return 0; free_nq: for (i = num_vec_created; i >= 0; i--) { - bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id); + type = bnxt_qplib_get_ring_type(&rdev->chip_ctx); + bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id, type); bnxt_qplib_free_nq(&rdev->nq[i]); } bnxt_qplib_dealloc_dpi(&rdev->qplib_res, @@ -1256,6 +1268,7 @@ static void bnxt_re_query_hwrm_intf_version(struct bnxt_re_dev *rdev) static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev) { + u8 type; int rc; if (test_and_clear_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags)) { @@ -1279,7 +1292,8 @@ static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev) bnxt_re_net_stats_ctx_free(rdev, rdev->qplib_ctx.stats.fw_id); bnxt_qplib_free_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx); bnxt_qplib_disable_rcfw_channel(&rdev->rcfw); - bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id); + type = bnxt_qplib_get_ring_type(&rdev->chip_ctx); + bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id, type); bnxt_qplib_free_rcfw_channel(&rdev->rcfw); } if (test_and_clear_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags)) { @@ -1310,9 +1324,12 @@ static void bnxt_re_worker(struct work_struct *work) static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev) { - int rc; - + dma_addr_t *pg_map; + u32 db_offt, ridx; + int pages, vid; bool locked; + u8 type; + int rc; /* Acquire rtnl lock through out this function */ rtnl_lock(); @@ -1356,21 +1373,22 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev) pr_err("Failed to allocate RCFW Channel: %#x\n", rc); goto fail; } - rc = bnxt_re_net_ring_alloc - (rdev, rdev->rcfw.creq.pbl[PBL_LVL_0].pg_map_arr, - rdev->rcfw.creq.pbl[rdev->rcfw.creq.level].pg_count, - HWRM_RING_ALLOC_CMPL, BNXT_QPLIB_CREQE_MAX_CNT - 1, - rdev->msix_entries[BNXT_RE_AEQ_IDX].ring_idx, - &rdev->rcfw.creq_ring_id); + type = bnxt_qplib_get_ring_type(&rdev->chip_ctx); + pg_map = rdev->rcfw.creq.pbl[PBL_LVL_0].pg_map_arr; + pages = rdev->rcfw.creq.pbl[rdev->rcfw.creq.level].pg_count; + ridx = rdev->msix_entries[BNXT_RE_AEQ_IDX].ring_idx; + rc = bnxt_re_net_ring_alloc(rdev, pg_map, pages, type, + BNXT_QPLIB_CREQE_MAX_CNT - 1, + ridx, &rdev->rcfw.creq_ring_id); if (rc) { pr_err("Failed to allocate CREQ: %#x\n", rc); goto free_rcfw; } - rc = bnxt_qplib_enable_rcfw_channel - (rdev->en_dev->pdev, &rdev->rcfw, - rdev->msix_entries[BNXT_RE_AEQ_IDX].vector, - rdev->msix_entries[BNXT_RE_AEQ_IDX].db_offset, - rdev->is_virtfn, &bnxt_re_aeq_handler); + db_offt = bnxt_re_get_nqdb_offset(rdev, BNXT_RE_AEQ_IDX); + vid = rdev->msix_entries[BNXT_RE_AEQ_IDX].vector; + rc = bnxt_qplib_enable_rcfw_channel(rdev->en_dev->pdev, &rdev->rcfw, + vid, db_offt, rdev->is_virtfn, + &bnxt_re_aeq_handler); if (rc) { pr_err("Failed to enable RCFW channel: %#x\n", rc); goto free_ring; @@ -1454,7 +1472,8 @@ free_ctx: disable_rcfw: bnxt_qplib_disable_rcfw_channel(&rdev->rcfw); free_ring: - bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id); + type = bnxt_qplib_get_ring_type(&rdev->chip_ctx); + bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id, type); free_rcfw: bnxt_qplib_free_rcfw_channel(&rdev->rcfw); fail: diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index b98b054148cd..01ea3d010017 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -244,6 +244,7 @@ static void bnxt_qplib_service_nq(unsigned long data) u16 type; int budget = nq->budget; uintptr_t q_handle; + bool gen_p5 = bnxt_qplib_is_chip_gen_p5(nq->res->cctx); /* Service the NQ until empty */ raw_cons = hwq->cons; @@ -290,7 +291,7 @@ static void bnxt_qplib_service_nq(unsigned long data) q_handle |= (u64)le32_to_cpu(nqsrqe->srq_handle_high) << 32; bnxt_qplib_arm_srq((struct bnxt_qplib_srq *)q_handle, - DBR_DBR_TYPE_SRQ_ARMENA); + DBC_DBC_TYPE_SRQ_ARMENA); if (!nq->srqn_handler(nq, (struct bnxt_qplib_srq *)q_handle, nqsrqe->event)) @@ -312,7 +313,9 @@ static void bnxt_qplib_service_nq(unsigned long data) } if (hwq->cons != raw_cons) { hwq->cons = raw_cons; - NQ_DB_REARM(nq->bar_reg_iomem, hwq->cons, hwq->max_elements); + bnxt_qplib_ring_nq_db_rearm(nq->bar_reg_iomem, hwq->cons, + hwq->max_elements, nq->ring_id, + gen_p5); } } @@ -336,9 +339,11 @@ static irqreturn_t bnxt_qplib_nq_irq(int irq, void *dev_instance) void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill) { + bool gen_p5 = bnxt_qplib_is_chip_gen_p5(nq->res->cctx); tasklet_disable(&nq->worker); /* Mask h/w interrupt */ - NQ_DB(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements); + bnxt_qplib_ring_nq_db(nq->bar_reg_iomem, nq->hwq.cons, + nq->hwq.max_elements, nq->ring_id, gen_p5); /* Sync with last running IRQ handler */ synchronize_irq(nq->vector); if (kill) @@ -373,6 +378,7 @@ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq) int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, int msix_vector, bool need_init) { + bool gen_p5 = bnxt_qplib_is_chip_gen_p5(nq->res->cctx); int rc; if (nq->requested) @@ -399,7 +405,8 @@ int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, nq->vector, nq_indx); } nq->requested = true; - NQ_DB_REARM(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements); + bnxt_qplib_ring_nq_db_rearm(nq->bar_reg_iomem, nq->hwq.cons, + nq->hwq.max_elements, nq->ring_id, gen_p5); return rc; } @@ -433,7 +440,8 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, rc = -ENOMEM; goto fail; } - nq->bar_reg_iomem = ioremap_nocache(nq_base + nq->bar_reg_off, 4); + /* Unconditionally map 8 bytes to support 57500 series */ + nq->bar_reg_iomem = ioremap_nocache(nq_base + nq->bar_reg_off, 8); if (!nq->bar_reg_iomem) { rc = -ENOMEM; goto fail; @@ -462,15 +470,17 @@ void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq) int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq) { + u8 hwq_type; + nq->pdev = pdev; if (!nq->hwq.max_elements || nq->hwq.max_elements > BNXT_QPLIB_NQE_MAX_CNT) nq->hwq.max_elements = BNXT_QPLIB_NQE_MAX_CNT; - + hwq_type = bnxt_qplib_get_hwq_type(nq->res); if (bnxt_qplib_alloc_init_hwq(nq->pdev, &nq->hwq, NULL, 0, &nq->hwq.max_elements, BNXT_QPLIB_MAX_NQE_ENTRY_SIZE, 0, - PAGE_SIZE, HWQ_TYPE_L2_CMPL)) + PAGE_SIZE, hwq_type)) return -ENOMEM; nq->budget = 8; @@ -481,21 +491,19 @@ int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq) static void bnxt_qplib_arm_srq(struct bnxt_qplib_srq *srq, u32 arm_type) { struct bnxt_qplib_hwq *srq_hwq = &srq->hwq; - struct dbr_dbr db_msg = { 0 }; void __iomem *db; - u32 sw_prod = 0; + u32 sw_prod; + u64 val = 0; /* Ring DB */ - sw_prod = (arm_type == DBR_DBR_TYPE_SRQ_ARM) ? srq->threshold : - HWQ_CMP(srq_hwq->prod, srq_hwq); - db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) & - DBR_DBR_INDEX_MASK); - db_msg.type_xid = cpu_to_le32(((srq->id << DBR_DBR_XID_SFT) & - DBR_DBR_XID_MASK) | arm_type); - db = (arm_type == DBR_DBR_TYPE_SRQ_ARMENA) ? - srq->dbr_base : srq->dpi->dbr; - wmb(); /* barrier before db ring */ - __iowrite64_copy(db, &db_msg, sizeof(db_msg) / sizeof(u64)); + sw_prod = (arm_type == DBC_DBC_TYPE_SRQ_ARM) ? + srq->threshold : HWQ_CMP(srq_hwq->prod, srq_hwq); + db = (arm_type == DBC_DBC_TYPE_SRQ_ARMENA) ? srq->dbr_base : + srq->dpi->dbr; + val = ((srq->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | arm_type; + val <<= 32; + val |= (sw_prod << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK; + writeq(val, db); } int bnxt_qplib_destroy_srq(struct bnxt_qplib_res *res, @@ -590,7 +598,7 @@ int bnxt_qplib_create_srq(struct bnxt_qplib_res *res, srq->id = le32_to_cpu(resp.xid); srq->dbr_base = res->dpi_tbl.dbr_bar_reg_iomem; if (srq->threshold) - bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ_ARMENA); + bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ_ARMENA); srq->arm_req = false; return 0; @@ -614,7 +622,7 @@ int bnxt_qplib_modify_srq(struct bnxt_qplib_res *res, srq_hwq->max_elements - sw_cons + sw_prod; if (count > srq->threshold) { srq->arm_req = false; - bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ_ARM); + bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ_ARM); } else { /* Deferred arming */ srq->arm_req = true; @@ -702,10 +710,10 @@ int bnxt_qplib_post_srq_recv(struct bnxt_qplib_srq *srq, srq_hwq->max_elements - sw_cons + sw_prod; spin_unlock(&srq_hwq->lock); /* Ring DB */ - bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ); + bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ); if (srq->arm_req == true && count > srq->threshold) { srq->arm_req = false; - bnxt_qplib_arm_srq(srq, DBR_DBR_TYPE_SRQ_ARM); + bnxt_qplib_arm_srq(srq, DBC_DBC_TYPE_SRQ_ARM); } done: return rc; @@ -1494,19 +1502,16 @@ void *bnxt_qplib_get_qp1_rq_buf(struct bnxt_qplib_qp *qp, void bnxt_qplib_post_send_db(struct bnxt_qplib_qp *qp) { struct bnxt_qplib_q *sq = &qp->sq; - struct dbr_dbr db_msg = { 0 }; u32 sw_prod; + u64 val = 0; + val = (((qp->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | + DBC_DBC_TYPE_SQ); + val <<= 32; sw_prod = HWQ_CMP(sq->hwq.prod, &sq->hwq); - - db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) & - DBR_DBR_INDEX_MASK); - db_msg.type_xid = - cpu_to_le32(((qp->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | - DBR_DBR_TYPE_SQ); + val |= (sw_prod << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK; /* Flush all the WQE writes to HW */ - wmb(); - __iowrite64_copy(qp->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64)); + writeq(val, qp->dpi->dbr); } int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp, @@ -1785,19 +1790,16 @@ done: void bnxt_qplib_post_recv_db(struct bnxt_qplib_qp *qp) { struct bnxt_qplib_q *rq = &qp->rq; - struct dbr_dbr db_msg = { 0 }; u32 sw_prod; + u64 val = 0; + val = (((qp->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | + DBC_DBC_TYPE_RQ); + val <<= 32; sw_prod = HWQ_CMP(rq->hwq.prod, &rq->hwq); - db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) & - DBR_DBR_INDEX_MASK); - db_msg.type_xid = - cpu_to_le32(((qp->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | - DBR_DBR_TYPE_RQ); - + val |= (sw_prod << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK; /* Flush the writes to HW Rx WQE before the ringing Rx DB */ - wmb(); - __iowrite64_copy(qp->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64)); + writeq(val, qp->dpi->dbr); } int bnxt_qplib_post_recv(struct bnxt_qplib_qp *qp, @@ -1881,32 +1883,28 @@ done: /* Spinlock must be held */ static void bnxt_qplib_arm_cq_enable(struct bnxt_qplib_cq *cq) { - struct dbr_dbr db_msg = { 0 }; + u64 val = 0; - db_msg.type_xid = - cpu_to_le32(((cq->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | - DBR_DBR_TYPE_CQ_ARMENA); + val = ((cq->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | + DBC_DBC_TYPE_CQ_ARMENA; + val <<= 32; /* Flush memory writes before enabling the CQ */ - wmb(); - __iowrite64_copy(cq->dbr_base, &db_msg, sizeof(db_msg) / sizeof(u64)); + writeq(val, cq->dbr_base); } static void bnxt_qplib_arm_cq(struct bnxt_qplib_cq *cq, u32 arm_type) { struct bnxt_qplib_hwq *cq_hwq = &cq->hwq; - struct dbr_dbr db_msg = { 0 }; u32 sw_cons; + u64 val = 0; /* Ring DB */ + val = ((cq->id << DBC_DBC_XID_SFT) & DBC_DBC_XID_MASK) | arm_type; + val <<= 32; sw_cons = HWQ_CMP(cq_hwq->cons, cq_hwq); - db_msg.index = cpu_to_le32((sw_cons << DBR_DBR_INDEX_SFT) & - DBR_DBR_INDEX_MASK); - db_msg.type_xid = - cpu_to_le32(((cq->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | - arm_type); + val |= (sw_cons << DBC_DBC_INDEX_SFT) & DBC_DBC_INDEX_MASK; /* flush memory writes before arming the CQ */ - wmb(); - __iowrite64_copy(cq->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64)); + writeq(val, cq->dpi->dbr); } int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) @@ -2125,7 +2123,7 @@ static int do_wa9060(struct bnxt_qplib_qp *qp, struct bnxt_qplib_cq *cq, sq->send_phantom = true; /* TODO: Only ARM if the previous SQE is ARMALL */ - bnxt_qplib_arm_cq(cq, DBR_DBR_TYPE_CQ_ARMALL); + bnxt_qplib_arm_cq(cq, DBC_DBC_TYPE_CQ_ARMALL); rc = -EAGAIN; goto out; @@ -2794,7 +2792,7 @@ int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe, } if (cq->hwq.cons != raw_cons) { cq->hwq.cons = raw_cons; - bnxt_qplib_arm_cq(cq, DBR_DBR_TYPE_CQ); + bnxt_qplib_arm_cq(cq, DBC_DBC_TYPE_CQ); } exit: return num_cqes - budget; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index f317bae36dd0..a00f1b019da0 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -432,10 +432,43 @@ struct bnxt_qplib_cq { #define NQ_DB_CP_FLAGS (NQ_DB_KEY_CP | \ NQ_DB_IDX_VALID | \ NQ_DB_IRQ_DIS) -#define NQ_DB_REARM(db, raw_cons, cp_bit) \ - writel(NQ_DB_CP_FLAGS_REARM | ((raw_cons) & ((cp_bit) - 1)), db) -#define NQ_DB(db, raw_cons, cp_bit) \ - writel(NQ_DB_CP_FLAGS | ((raw_cons) & ((cp_bit) - 1)), db) + +static inline void bnxt_qplib_ring_nq_db64(void __iomem *db, u32 index, + u32 xid, bool arm) +{ + u64 val; + + val = xid & DBC_DBC_XID_MASK; + val |= DBC_DBC_PATH_ROCE; + val |= arm ? DBC_DBC_TYPE_NQ_ARM : DBC_DBC_TYPE_NQ; + val <<= 32; + val |= index & DBC_DBC_INDEX_MASK; + writeq(val, db); +} + +static inline void bnxt_qplib_ring_nq_db_rearm(void __iomem *db, u32 raw_cons, + u32 max_elements, u32 xid, + bool gen_p5) +{ + u32 index = raw_cons & (max_elements - 1); + + if (gen_p5) + bnxt_qplib_ring_nq_db64(db, index, xid, true); + else + writel(NQ_DB_CP_FLAGS_REARM | (index & DBC_DBC32_XID_MASK), db); +} + +static inline void bnxt_qplib_ring_nq_db(void __iomem *db, u32 raw_cons, + u32 max_elements, u32 xid, + bool gen_p5) +{ + u32 index = raw_cons & (max_elements - 1); + + if (gen_p5) + bnxt_qplib_ring_nq_db64(db, index, xid, false); + else + writel(NQ_DB_CP_FLAGS | (index & DBC_DBC32_XID_MASK), db); +} struct bnxt_qplib_nq { struct pci_dev *pdev; @@ -449,7 +482,7 @@ struct bnxt_qplib_nq { struct bnxt_qplib_hwq hwq; u16 bar_reg; - u16 bar_reg_off; + u32 bar_reg_off; u16 ring_id; void __iomem *bar_reg_iomem; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 65e17de220f6..9c000245a6de 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -359,11 +359,12 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, static void bnxt_qplib_service_creq(unsigned long data) { struct bnxt_qplib_rcfw *rcfw = (struct bnxt_qplib_rcfw *)data; + bool gen_p5 = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx); struct bnxt_qplib_hwq *creq = &rcfw->creq; + u32 type, budget = CREQ_ENTRY_POLL_BUDGET; struct creq_base *creqe, **creq_ptr; u32 sw_cons, raw_cons; unsigned long flags; - u32 type, budget = CREQ_ENTRY_POLL_BUDGET; /* Service the CREQ until budget is over */ spin_lock_irqsave(&creq->lock, flags); @@ -407,8 +408,9 @@ static void bnxt_qplib_service_creq(unsigned long data) if (creq->cons != raw_cons) { creq->cons = raw_cons; - CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, raw_cons, - creq->max_elements); + bnxt_qplib_ring_creq_db_rearm(rcfw->creq_bar_reg_iomem, + raw_cons, creq->max_elements, + rcfw->creq_ring_id, gen_p5); } spin_unlock_irqrestore(&creq->lock, flags); } @@ -560,12 +562,15 @@ int bnxt_qplib_alloc_rcfw_channel(struct pci_dev *pdev, struct bnxt_qplib_ctx *ctx, int qp_tbl_sz) { + u8 hwq_type; + rcfw->pdev = pdev; rcfw->creq.max_elements = BNXT_QPLIB_CREQE_MAX_CNT; + hwq_type = bnxt_qplib_get_hwq_type(rcfw->res); if (bnxt_qplib_alloc_init_hwq(rcfw->pdev, &rcfw->creq, NULL, 0, &rcfw->creq.max_elements, - BNXT_QPLIB_CREQE_UNITS, 0, PAGE_SIZE, - HWQ_TYPE_L2_CMPL)) { + BNXT_QPLIB_CREQE_UNITS, + 0, PAGE_SIZE, hwq_type)) { dev_err(&rcfw->pdev->dev, "HW channel CREQ allocation failed\n"); goto fail; @@ -607,10 +612,13 @@ fail: void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill) { + bool gen_p5 = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx); + tasklet_disable(&rcfw->worker); /* Mask h/w interrupts */ - CREQ_DB(rcfw->creq_bar_reg_iomem, rcfw->creq.cons, - rcfw->creq.max_elements); + bnxt_qplib_ring_creq_db(rcfw->creq_bar_reg_iomem, rcfw->creq.cons, + rcfw->creq.max_elements, rcfw->creq_ring_id, + gen_p5); /* Sync with last running IRQ-handler */ synchronize_irq(rcfw->vector); if (kill) @@ -647,6 +655,7 @@ void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, bool need_init) { + bool gen_p5 = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx); int rc; if (rcfw->requested) @@ -663,8 +672,9 @@ int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, if (rc) return rc; rcfw->requested = true; - CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, rcfw->creq.cons, - rcfw->creq.max_elements); + bnxt_qplib_ring_creq_db_rearm(rcfw->creq_bar_reg_iomem, + rcfw->creq.cons, rcfw->creq.max_elements, + rcfw->creq_ring_id, gen_p5); return 0; } @@ -717,8 +727,9 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, dev_err(&rcfw->pdev->dev, "CREQ BAR region %d resc start is 0!\n", rcfw->creq_bar_reg); + /* Unconditionally map 8 bytes to support 57500 series */ rcfw->creq_bar_reg_iomem = ioremap_nocache(res_base + cp_bar_reg_off, - 4); + 8); if (!rcfw->creq_bar_reg_iomem) { dev_err(&rcfw->pdev->dev, "CREQ BAR region %d mapping failed\n", rcfw->creq_bar_reg); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h index 8011d674391e..2138533bb642 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -157,10 +157,46 @@ static inline u32 get_creq_idx(u32 val) #define CREQ_DB_CP_FLAGS (CREQ_DB_KEY_CP | \ CREQ_DB_IDX_VALID | \ CREQ_DB_IRQ_DIS) -#define CREQ_DB_REARM(db, raw_cons, cp_bit) \ - writel(CREQ_DB_CP_FLAGS_REARM | ((raw_cons) & ((cp_bit) - 1)), db) -#define CREQ_DB(db, raw_cons, cp_bit) \ - writel(CREQ_DB_CP_FLAGS | ((raw_cons) & ((cp_bit) - 1)), db) + +static inline void bnxt_qplib_ring_creq_db64(void __iomem *db, u32 index, + u32 xid, bool arm) +{ + u64 val = 0; + + val = xid & DBC_DBC_XID_MASK; + val |= DBC_DBC_PATH_ROCE; + val |= arm ? DBC_DBC_TYPE_NQ_ARM : DBC_DBC_TYPE_NQ; + val <<= 32; + val |= index & DBC_DBC_INDEX_MASK; + + writeq(val, db); +} + +static inline void bnxt_qplib_ring_creq_db_rearm(void __iomem *db, u32 raw_cons, + u32 max_elements, u32 xid, + bool gen_p5) +{ + u32 index = raw_cons & (max_elements - 1); + + if (gen_p5) + bnxt_qplib_ring_creq_db64(db, index, xid, true); + else + writel(CREQ_DB_CP_FLAGS_REARM | (index & DBC_DBC32_XID_MASK), + db); +} + +static inline void bnxt_qplib_ring_creq_db(void __iomem *db, u32 raw_cons, + u32 max_elements, u32 xid, + bool gen_p5) +{ + u32 index = raw_cons & (max_elements - 1); + + if (gen_p5) + bnxt_qplib_ring_creq_db64(db, index, xid, true); + else + writel(CREQ_DB_CP_FLAGS | (index & DBC_DBC32_XID_MASK), + db); +} #define CREQ_ENTRY_POLL_BUDGET 0x100 diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h index 6a0c45827053..35d862b44908 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -206,6 +206,19 @@ static inline bool bnxt_qplib_is_chip_gen_p5(struct bnxt_qplib_chip_ctx *cctx) return (cctx->chip_num == CHIP_NUM_57500); } +static inline u8 bnxt_qplib_get_hwq_type(struct bnxt_qplib_res *res) +{ + return bnxt_qplib_is_chip_gen_p5(res->cctx) ? + HWQ_TYPE_QUEUE : HWQ_TYPE_L2_CMPL; +} + +static inline u8 bnxt_qplib_get_ring_type(struct bnxt_qplib_chip_ctx *cctx) +{ + return bnxt_qplib_is_chip_gen_p5(cctx) ? + RING_ALLOC_REQ_RING_TYPE_NQ : + RING_ALLOC_REQ_RING_TYPE_ROCE_CMPL; +} + #define to_bnxt_qplib(ptr, type, member) \ container_of(ptr, type, member) diff --git a/drivers/infiniband/hw/bnxt_re/roce_hsi.h b/drivers/infiniband/hw/bnxt_re/roce_hsi.h index 8a9ead419ac2..73e0982fc48f 100644 --- a/drivers/infiniband/hw/bnxt_re/roce_hsi.h +++ b/drivers/infiniband/hw/bnxt_re/roce_hsi.h @@ -49,11 +49,11 @@ struct cmpl_doorbell { #define CMPL_DOORBELL_IDX_SFT 0 #define CMPL_DOORBELL_RESERVED_MASK 0x3000000UL #define CMPL_DOORBELL_RESERVED_SFT 24 - #define CMPL_DOORBELL_IDX_VALID 0x4000000UL + #define CMPL_DOORBELL_IDX_VALID 0x4000000UL #define CMPL_DOORBELL_MASK 0x8000000UL #define CMPL_DOORBELL_KEY_MASK 0xf0000000UL #define CMPL_DOORBELL_KEY_SFT 28 - #define CMPL_DOORBELL_KEY_CMPL (0x2UL << 28) + #define CMPL_DOORBELL_KEY_CMPL (0x2UL << 28) }; /* Status Door Bell Format (4 bytes) */ @@ -71,46 +71,56 @@ struct status_doorbell { /* RoCE Host Structures */ /* Doorbell Structures */ -/* 64b Doorbell Format (8 bytes) */ -struct dbr_dbr { - __le32 index; - #define DBR_DBR_INDEX_MASK 0xfffffUL - #define DBR_DBR_INDEX_SFT 0 - #define DBR_DBR_RESERVED12_MASK 0xfff00000UL - #define DBR_DBR_RESERVED12_SFT 20 - __le32 type_xid; - #define DBR_DBR_XID_MASK 0xfffffUL - #define DBR_DBR_XID_SFT 0 - #define DBR_DBR_RESERVED8_MASK 0xff00000UL - #define DBR_DBR_RESERVED8_SFT 20 - #define DBR_DBR_TYPE_MASK 0xf0000000UL - #define DBR_DBR_TYPE_SFT 28 - #define DBR_DBR_TYPE_SQ (0x0UL << 28) - #define DBR_DBR_TYPE_RQ (0x1UL << 28) - #define DBR_DBR_TYPE_SRQ (0x2UL << 28) - #define DBR_DBR_TYPE_SRQ_ARM (0x3UL << 28) - #define DBR_DBR_TYPE_CQ (0x4UL << 28) - #define DBR_DBR_TYPE_CQ_ARMSE (0x5UL << 28) - #define DBR_DBR_TYPE_CQ_ARMALL (0x6UL << 28) - #define DBR_DBR_TYPE_CQ_ARMENA (0x7UL << 28) - #define DBR_DBR_TYPE_SRQ_ARMENA (0x8UL << 28) - #define DBR_DBR_TYPE_CQ_CUTOFF_ACK (0x9UL << 28) - #define DBR_DBR_TYPE_NULL (0xfUL << 28) +/* dbc_dbc (size:64b/8B) */ +struct dbc_dbc { + __le32 index; + #define DBC_DBC_INDEX_MASK 0xffffffUL + #define DBC_DBC_INDEX_SFT 0 + __le32 type_path_xid; + #define DBC_DBC_XID_MASK 0xfffffUL + #define DBC_DBC_XID_SFT 0 + #define DBC_DBC_PATH_MASK 0x3000000UL + #define DBC_DBC_PATH_SFT 24 + #define DBC_DBC_PATH_ROCE (0x0UL << 24) + #define DBC_DBC_PATH_L2 (0x1UL << 24) + #define DBC_DBC_PATH_ENGINE (0x2UL << 24) + #define DBC_DBC_PATH_LAST DBC_DBC_PATH_ENGINE + #define DBC_DBC_DEBUG_TRACE 0x8000000UL + #define DBC_DBC_TYPE_MASK 0xf0000000UL + #define DBC_DBC_TYPE_SFT 28 + #define DBC_DBC_TYPE_SQ (0x0UL << 28) + #define DBC_DBC_TYPE_RQ (0x1UL << 28) + #define DBC_DBC_TYPE_SRQ (0x2UL << 28) + #define DBC_DBC_TYPE_SRQ_ARM (0x3UL << 28) + #define DBC_DBC_TYPE_CQ (0x4UL << 28) + #define DBC_DBC_TYPE_CQ_ARMSE (0x5UL << 28) + #define DBC_DBC_TYPE_CQ_ARMALL (0x6UL << 28) + #define DBC_DBC_TYPE_CQ_ARMENA (0x7UL << 28) + #define DBC_DBC_TYPE_SRQ_ARMENA (0x8UL << 28) + #define DBC_DBC_TYPE_CQ_CUTOFF_ACK (0x9UL << 28) + #define DBC_DBC_TYPE_NQ (0xaUL << 28) + #define DBC_DBC_TYPE_NQ_ARM (0xbUL << 28) + #define DBC_DBC_TYPE_NULL (0xfUL << 28) + #define DBC_DBC_TYPE_LAST DBC_DBC_TYPE_NULL }; -/* 32b Doorbell Format (4 bytes) */ -struct dbr_dbr32 { - __le32 type_abs_incr_xid; - #define DBR_DBR32_XID_MASK 0xfffffUL - #define DBR_DBR32_XID_SFT 0 - #define DBR_DBR32_RESERVED4_MASK 0xf00000UL - #define DBR_DBR32_RESERVED4_SFT 20 - #define DBR_DBR32_INCR_MASK 0xf000000UL - #define DBR_DBR32_INCR_SFT 24 - #define DBR_DBR32_ABS 0x10000000UL - #define DBR_DBR32_TYPE_MASK 0xe0000000UL - #define DBR_DBR32_TYPE_SFT 29 - #define DBR_DBR32_TYPE_SQ (0x0UL << 29) +/* dbc_dbc32 (size:32b/4B) */ +struct dbc_dbc32 { + __le32 type_abs_incr_xid; + #define DBC_DBC32_XID_MASK 0xfffffUL + #define DBC_DBC32_XID_SFT 0 + #define DBC_DBC32_PATH_MASK 0xc00000UL + #define DBC_DBC32_PATH_SFT 22 + #define DBC_DBC32_PATH_ROCE (0x0UL << 22) + #define DBC_DBC32_PATH_L2 (0x1UL << 22) + #define DBC_DBC32_PATH_LAST DBC_DBC32_PATH_L2 + #define DBC_DBC32_INCR_MASK 0xf000000UL + #define DBC_DBC32_INCR_SFT 24 + #define DBC_DBC32_ABS 0x10000000UL + #define DBC_DBC32_TYPE_MASK 0xe0000000UL + #define DBC_DBC32_TYPE_SFT 29 + #define DBC_DBC32_TYPE_SQ (0x0UL << 29) + #define DBC_DBC32_TYPE_LAST DBC_DBC32_TYPE_SQ }; /* SQ WQE Structures */ @@ -2719,6 +2729,8 @@ struct creq_query_func_resp_sb { __le16 max_srq; __le32 max_gid; __le32 tqm_alloc_reqs[12]; + __le32 max_dpi; + __le32 reserved_32; }; /* Set resources command response (16 bytes) */ From e0387e1dd4af2681982b6d29a9a05b5c4a75cc26 Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:24 -0500 Subject: [PATCH 1353/4277] RDMA/bnxt_re: Skip backing store allocation for 57500 series The backing store to keep HW context data structures is allocated and initialized by L2 driver. For 57500 chip RoCE driver do not require to allocate and initialize additional memory. Changing to skip duplicate allocation and initialization for 57500 adapters. Driver continues as before for older chips. This patch also takes care of stats context memory alignment to 128 boundary, a requirement for 57500 series of chip. Older chips do not care of alignment, thus the change is unconditional. Signed-off-by: Selvin Xavier Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/main.c | 3 ++- drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 6 ++++-- drivers/infiniband/hw/bnxt_re/qplib_res.c | 10 +++++++--- drivers/infiniband/hw/bnxt_re/qplib_res.h | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 0df180850a75..0d40a930c192 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -1401,7 +1401,8 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev) if (!rdev->is_virtfn) bnxt_re_set_resource_limits(rdev); - rc = bnxt_qplib_alloc_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx, 0); + rc = bnxt_qplib_alloc_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx, 0, + bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx)); if (rc) { pr_err("Failed to allocate QPLIB context: %#x\n", rc); goto disable_rcfw; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 9c000245a6de..c6461e957078 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -482,11 +482,13 @@ int bnxt_qplib_init_rcfw(struct bnxt_qplib_rcfw *rcfw, req.log2_dbr_pg_size = cpu_to_le16(PAGE_SHIFT - RCFW_DBR_BASE_PAGE_SHIFT); /* - * VFs need not setup the HW context area, PF + * Gen P5 devices doesn't require this allocation + * as the L2 driver does the same for RoCE also. + * Also, VFs need not setup the HW context area, PF * shall setup this area for VF. Skipping the * HW programming */ - if (is_virtfn) + if (is_virtfn || bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx)) goto skip_ctx_setup; level = ctx->qpc_tbl.level; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 57d4951679cb..c8502c2844a2 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -330,13 +330,13 @@ void bnxt_qplib_free_ctx(struct pci_dev *pdev, */ int bnxt_qplib_alloc_ctx(struct pci_dev *pdev, struct bnxt_qplib_ctx *ctx, - bool virt_fn) + bool virt_fn, bool is_p5) { int i, j, k, rc = 0; int fnz_idx = -1; __le64 **pbl_ptr; - if (virt_fn) + if (virt_fn || is_p5) goto stats_alloc; /* QPC Tables */ @@ -762,7 +762,11 @@ static int bnxt_qplib_alloc_stats_ctx(struct pci_dev *pdev, { memset(stats, 0, sizeof(*stats)); stats->fw_id = -1; - stats->size = sizeof(struct ctx_hw_stats); + /* 128 byte aligned context memory is required only for 57500. + * However making this unconditional, it does not harm previous + * generation. + */ + stats->size = ALIGN(sizeof(struct ctx_hw_stats), 128); stats->dma = dma_alloc_coherent(&pdev->dev, stats->size, &stats->dma_map, GFP_KERNEL); if (!stats->dma) { diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h index 35d862b44908..32cebd0f1436 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -252,5 +252,5 @@ void bnxt_qplib_free_ctx(struct pci_dev *pdev, struct bnxt_qplib_ctx *ctx); int bnxt_qplib_alloc_ctx(struct pci_dev *pdev, struct bnxt_qplib_ctx *ctx, - bool virt_fn); + bool virt_fn, bool is_p5); #endif /* __BNXT_QPLIB_RES_H__ */ From 374c5285abee0aa1b9e04c6beb86e13b6813db09 Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:25 -0500 Subject: [PATCH 1354/4277] RDMA/bnxt_re: Enable GSI QP support for 57500 series In the new 57500 series of adapters the GSI qp is a UD type QP unlike the previous generation where it was a Raw Eth QP. Changing the control and data path to support the same. Listing all the significant diffs: - AH creation resolve network type unconditionally - Add check at relevant places to distinguish from Raw Eth processing flow. - bnxt_re_process_res_ud_wc report completion with GRH flag when qp is GSI. - Change length, cfa_meta and smac to match new driver/hardware interface. - Add new driver/hardware interface. Signed-off-by: Selvin Xavier Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 100 ++++++++++++++--------- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 10 ++- drivers/infiniband/hw/bnxt_re/qplib_fp.h | 1 + drivers/infiniband/hw/bnxt_re/roce_hsi.h | 45 ++++++---- 4 files changed, 100 insertions(+), 56 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 3e3e481b2fb8..2d5f9c35bfdc 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -663,17 +663,36 @@ int bnxt_re_destroy_ah(struct ib_ah *ib_ah, u32 flags) return 0; } +static u8 bnxt_re_stack_to_dev_nw_type(enum rdma_network_type ntype) +{ + u8 nw_type; + + switch (ntype) { + case RDMA_NETWORK_IPV4: + nw_type = CMDQ_CREATE_AH_TYPE_V2IPV4; + break; + case RDMA_NETWORK_IPV6: + nw_type = CMDQ_CREATE_AH_TYPE_V2IPV6; + break; + default: + nw_type = CMDQ_CREATE_AH_TYPE_V1; + break; + } + return nw_type; +} + struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd, struct rdma_ah_attr *ah_attr, u32 flags, struct ib_udata *udata) { struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); - struct bnxt_re_dev *rdev = pd->rdev; - struct bnxt_re_ah *ah; const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr); - int rc; + struct bnxt_re_dev *rdev = pd->rdev; + const struct ib_gid_attr *sgid_attr; + struct bnxt_re_ah *ah; u8 nw_type; + int rc; if (!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH)) { dev_err(rdev_to_dev(rdev), "Failed to alloc AH: GRH not set"); @@ -700,28 +719,11 @@ struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd, ah->qplib_ah.flow_label = grh->flow_label; ah->qplib_ah.hop_limit = grh->hop_limit; ah->qplib_ah.sl = rdma_ah_get_sl(ah_attr); - if (udata && - !rdma_is_multicast_addr((struct in6_addr *) - grh->dgid.raw) && - !rdma_link_local_addr((struct in6_addr *) - grh->dgid.raw)) { - const struct ib_gid_attr *sgid_attr; - sgid_attr = grh->sgid_attr; - /* Get network header type for this GID */ - nw_type = rdma_gid_attr_network_type(sgid_attr); - switch (nw_type) { - case RDMA_NETWORK_IPV4: - ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V2IPV4; - break; - case RDMA_NETWORK_IPV6: - ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V2IPV6; - break; - default: - ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V1; - break; - } - } + sgid_attr = grh->sgid_attr; + /* Get network header type for this GID */ + nw_type = rdma_gid_attr_network_type(sgid_attr); + ah->qplib_ah.nw_type = bnxt_re_stack_to_dev_nw_type(nw_type); memcpy(ah->qplib_ah.dmac, ah_attr->roce.dmac, ETH_ALEN); rc = bnxt_qplib_create_ah(&rdev->qplib_res, &ah->qplib_ah, @@ -1065,12 +1067,17 @@ struct ib_qp *bnxt_re_create_qp(struct ib_pd *ib_pd, qp->qplib_qp.pd = &pd->qplib_pd; qp->qplib_qp.qp_handle = (u64)(unsigned long)(&qp->qplib_qp); qp->qplib_qp.type = __from_ib_qp_type(qp_init_attr->qp_type); + + if (qp_init_attr->qp_type == IB_QPT_GSI && + bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx)) + qp->qplib_qp.type = CMDQ_CREATE_QP_TYPE_GSI; if (qp->qplib_qp.type == IB_QPT_MAX) { dev_err(rdev_to_dev(rdev), "QP type 0x%x not supported", qp->qplib_qp.type); rc = -EINVAL; goto fail; } + qp->qplib_qp.max_inline_data = qp_init_attr->cap.max_inline_data; qp->qplib_qp.sig_type = ((qp_init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false); @@ -1131,7 +1138,8 @@ struct ib_qp *bnxt_re_create_qp(struct ib_pd *ib_pd, qp->qplib_qp.mtu = ib_mtu_enum_to_int(iboe_get_mtu(rdev->netdev->mtu)); - if (qp_init_attr->qp_type == IB_QPT_GSI) { + if (qp_init_attr->qp_type == IB_QPT_GSI && + !(bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx))) { /* Allocate 1 more than what's provided */ entries = roundup_pow_of_two(qp_init_attr->cap.max_send_wr + 1); qp->qplib_qp.sq.max_wqe = min_t(u32, entries, @@ -2091,7 +2099,8 @@ static int bnxt_re_build_qp1_shadow_qp_recv(struct bnxt_re_qp *qp, static int is_ud_qp(struct bnxt_re_qp *qp) { - return qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_UD; + return (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_UD || + qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_GSI); } static int bnxt_re_build_send_wqe(struct bnxt_re_qp *qp, @@ -2395,7 +2404,7 @@ int bnxt_re_post_send(struct ib_qp *ib_qp, const struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_SEND: case IB_WR_SEND_WITH_IMM: - if (ib_qp->qp_type == IB_QPT_GSI) { + if (qp->qplib_qp.type == CMDQ_CREATE_QP1_TYPE_GSI) { rc = bnxt_re_build_qp1_send_v2(qp, wr, &wqe, payload_sz); if (rc) @@ -2525,7 +2534,8 @@ int bnxt_re_post_recv(struct ib_qp *ib_qp, const struct ib_recv_wr *wr, wqe.wr_id = wr->wr_id; wqe.type = BNXT_QPLIB_SWQE_TYPE_RECV; - if (ib_qp->qp_type == IB_QPT_GSI) + if (ib_qp->qp_type == IB_QPT_GSI && + qp->qplib_qp.type != CMDQ_CREATE_QP_TYPE_GSI) rc = bnxt_re_build_qp1_shadow_qp_recv(qp, wr, &wqe, payload_sz); if (!rc) @@ -3120,19 +3130,33 @@ static void bnxt_re_process_res_shadow_qp_wc(struct bnxt_re_qp *qp, } } -static void bnxt_re_process_res_ud_wc(struct ib_wc *wc, +static void bnxt_re_process_res_ud_wc(struct bnxt_re_qp *qp, + struct ib_wc *wc, struct bnxt_qplib_cqe *cqe) { + u8 nw_type; + wc->opcode = IB_WC_RECV; wc->status = __rc_to_ib_wc_status(cqe->status); - if (cqe->flags & CQ_RES_RC_FLAGS_IMM) + if (cqe->flags & CQ_RES_UD_FLAGS_IMM) wc->wc_flags |= IB_WC_WITH_IMM; - if (cqe->flags & CQ_RES_RC_FLAGS_INV) - wc->wc_flags |= IB_WC_WITH_INVALIDATE; - if ((cqe->flags & (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) == - (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) - wc->opcode = IB_WC_RECV_RDMA_WITH_IMM; + /* report only on GSI QP for Thor */ + if (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_GSI) { + wc->wc_flags |= IB_WC_GRH; + memcpy(wc->smac, cqe->smac, ETH_ALEN); + wc->wc_flags |= IB_WC_WITH_SMAC; + if (cqe->flags & CQ_RES_UD_FLAGS_META_FORMAT_VLAN) { + wc->vlan_id = (cqe->cfa_meta & 0xFFF); + if (wc->vlan_id < 0x1000) + wc->wc_flags |= IB_WC_WITH_VLAN; + } + nw_type = (cqe->flags & CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK) >> + CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT; + wc->network_hdr_type = bnxt_re_to_ib_nw_type(nw_type); + wc->wc_flags |= IB_WC_WITH_NETWORK_HDR_TYPE; + } + } static int send_phantom_wqe(struct bnxt_re_qp *qp) @@ -3224,7 +3248,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc) switch (cqe->opcode) { case CQ_BASE_CQE_TYPE_REQ: - if (qp->qplib_qp.id == + if (qp->rdev->qp1_sqp && qp->qplib_qp.id == qp->rdev->qp1_sqp->qplib_qp.id) { /* Handle this completion with * the stored completion @@ -3259,7 +3283,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc) bnxt_re_process_res_rc_wc(wc, cqe); break; case CQ_BASE_CQE_TYPE_RES_UD: - if (qp->qplib_qp.id == + if (qp->rdev->qp1_sqp && qp->qplib_qp.id == qp->rdev->qp1_sqp->qplib_qp.id) { /* Handle this completion with * the stored completion @@ -3272,7 +3296,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc) break; } } - bnxt_re_process_res_ud_wc(wc, cqe); + bnxt_re_process_res_ud_wc(qp, wc, cqe); break; default: dev_err(rdev_to_dev(cq->rdev), diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 01ea3d010017..117e78643720 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "roce_hsi.h" @@ -1622,7 +1623,8 @@ int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp, ((offsetof(typeof(*sqe), data) + 15) >> 4); sqe->inv_key_or_imm_data = cpu_to_le32( wqe->send.inv_key); - if (qp->type == CMDQ_CREATE_QP_TYPE_UD) { + if (qp->type == CMDQ_CREATE_QP_TYPE_UD || + qp->type == CMDQ_CREATE_QP_TYPE_GSI) { sqe->q_key = cpu_to_le32(wqe->send.q_key); sqe->dst_qp = cpu_to_le32( wqe->send.dst_qp & SQ_SEND_DST_QP_MASK); @@ -2408,12 +2410,14 @@ static int bnxt_qplib_cq_process_res_ud(struct bnxt_qplib_cq *cq, } cqe = *pcqe; cqe->opcode = hwcqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK; - cqe->length = le32_to_cpu(hwcqe->length); + cqe->length = (u32)le16_to_cpu(hwcqe->length); + cqe->cfa_meta = le16_to_cpu(hwcqe->cfa_metadata); cqe->invrkey = le32_to_cpu(hwcqe->imm_data); cqe->flags = le16_to_cpu(hwcqe->flags); cqe->status = hwcqe->status; cqe->qp_handle = (u64)(unsigned long)qp; - memcpy(cqe->smac, hwcqe->src_mac, 6); + /*FIXME: Endianness fix needed for smace */ + memcpy(cqe->smac, hwcqe->src_mac, ETH_ALEN); wr_id_idx = le32_to_cpu(hwcqe->src_qp_high_srq_or_rq_wr_id) & CQ_RES_UD_SRQ_OR_RQ_WR_ID_MASK; cqe->src_qp = le16_to_cpu(hwcqe->src_qp_low) | diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index a00f1b019da0..f9f7cc54d5ce 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -347,6 +347,7 @@ struct bnxt_qplib_cqe { u8 type; u8 opcode; u32 length; + u16 cfa_meta; u64 wr_id; union { __be32 immdata; diff --git a/drivers/infiniband/hw/bnxt_re/roce_hsi.h b/drivers/infiniband/hw/bnxt_re/roce_hsi.h index 73e0982fc48f..43cce22a5bd1 100644 --- a/drivers/infiniband/hw/bnxt_re/roce_hsi.h +++ b/drivers/infiniband/hw/bnxt_re/roce_hsi.h @@ -515,22 +515,24 @@ struct cq_res_rc { /* Responder UD CQE (32 bytes) */ struct cq_res_ud { - __le32 length; + __le16 length; #define CQ_RES_UD_LENGTH_MASK 0x3fffUL #define CQ_RES_UD_LENGTH_SFT 0 - #define CQ_RES_UD_RESERVED18_MASK 0xffffc000UL - #define CQ_RES_UD_RESERVED18_SFT 14 + __le16 cfa_metadata; + #define CQ_RES_UD_CFA_METADATA_VID_MASK 0xfffUL + #define CQ_RES_UD_CFA_METADATA_VID_SFT 0 + #define CQ_RES_UD_CFA_METADATA_DE 0x1000UL + #define CQ_RES_UD_CFA_METADATA_PRI_MASK 0xe000UL + #define CQ_RES_UD_CFA_METADATA_PRI_SFT 13 __le32 imm_data; __le64 qp_handle; __le16 src_mac[3]; __le16 src_qp_low; u8 cqe_type_toggle; - #define CQ_RES_UD_TOGGLE 0x1UL - #define CQ_RES_UD_CQE_TYPE_MASK 0x1eUL - #define CQ_RES_UD_CQE_TYPE_SFT 1 + #define CQ_RES_UD_TOGGLE 0x1UL + #define CQ_RES_UD_CQE_TYPE_MASK 0x1eUL + #define CQ_RES_UD_CQE_TYPE_SFT 1 #define CQ_RES_UD_CQE_TYPE_RES_UD (0x2UL << 1) - #define CQ_RES_UD_RESERVED3_MASK 0xe0UL - #define CQ_RES_UD_RESERVED3_SFT 5 u8 status; #define CQ_RES_UD_STATUS_OK 0x0UL #define CQ_RES_UD_STATUS_LOCAL_ACCESS_ERROR 0x1UL @@ -546,18 +548,30 @@ struct cq_res_ud { #define CQ_RES_UD_FLAGS_SRQ_SRQ (0x1UL << 0) #define CQ_RES_UD_FLAGS_SRQ_LAST CQ_RES_UD_FLAGS_SRQ_SRQ #define CQ_RES_UD_FLAGS_IMM 0x2UL - #define CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK 0xcUL - #define CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT 2 - #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V1 (0x0UL << 2) - #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV4 (0x2UL << 2) - #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 (0x3UL << 2) + #define CQ_RES_UD_FLAGS_UNUSED_MASK 0xcUL + #define CQ_RES_UD_FLAGS_UNUSED_SFT 2 + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK 0x30UL + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT 4 + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V1 (0x0UL << 4) + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV4 (0x2UL << 4) + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 (0x3UL << 4) #define CQ_RES_UD_FLAGS_ROCE_IP_VER_LAST \ CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 + #define CQ_RES_UD_FLAGS_META_FORMAT_MASK 0x3c0UL + #define CQ_RES_UD_FLAGS_META_FORMAT_SFT 6 + #define CQ_RES_UD_FLAGS_META_FORMAT_NONE (0x0UL << 6) + #define CQ_RES_UD_FLAGS_META_FORMAT_VLAN (0x1UL << 6) + #define CQ_RES_UD_FLAGS_META_FORMAT_TUNNEL_ID (0x2UL << 6) + #define CQ_RES_UD_FLAGS_META_FORMAT_CHDR_DATA (0x3UL << 6) + #define CQ_RES_UD_FLAGS_META_FORMAT_HDR_OFFSET (0x4UL << 6) + #define CQ_RES_UD_FLAGS_META_FORMAT_LAST \ + CQ_RES_UD_FLAGS_META_FORMAT_HDR_OFFSET + #define CQ_RES_UD_FLAGS_EXT_META_FORMAT_MASK 0xc00UL + #define CQ_RES_UD_FLAGS_EXT_META_FORMAT_SFT 10 + __le32 src_qp_high_srq_or_rq_wr_id; #define CQ_RES_UD_SRQ_OR_RQ_WR_ID_MASK 0xfffffUL #define CQ_RES_UD_SRQ_OR_RQ_WR_ID_SFT 0 - #define CQ_RES_UD_RESERVED4_MASK 0xf00000UL - #define CQ_RES_UD_RESERVED4_SFT 20 #define CQ_RES_UD_SRC_QP_HIGH_MASK 0xff000000UL #define CQ_RES_UD_SRC_QP_HIGH_SFT 24 }; @@ -993,6 +1007,7 @@ struct cmdq_create_qp { #define CMDQ_CREATE_QP_TYPE_RC 0x2UL #define CMDQ_CREATE_QP_TYPE_UD 0x4UL #define CMDQ_CREATE_QP_TYPE_RAW_ETHERTYPE 0x6UL + #define CMDQ_CREATE_QP_TYPE_GSI 0x7UL u8 sq_pg_size_sq_lvl; #define CMDQ_CREATE_QP_SQ_LVL_MASK 0xfUL #define CMDQ_CREATE_QP_SQ_LVL_SFT 0 From 37f91cff2de0170930a92e091beed42a4708bcd2 Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:26 -0500 Subject: [PATCH 1355/4277] RDMA/bnxt_re: Add extended psn structure for 57500 adapters The new 57500 series of adapter has bigger psn search structure. The size of new structure is 16B. Changing the control path memory allocation and fast path code to accommodate the new psn structure while maintaining the backward compatibility. There are few additional changes listed below: - For 57500 chip max-sge are limited to 6 for now. - For 57500 chip max-receive-sge should be set to 6 for now. - Add driver/hardware interface structure for new chip. Signed-off-by: Selvin Xavier Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 13 ++++-- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 51 ++++++++++++++++++------ drivers/infiniband/hw/bnxt_re/qplib_fp.h | 2 + drivers/infiniband/hw/bnxt_re/qplib_sp.c | 3 +- drivers/infiniband/hw/bnxt_re/roce_hsi.h | 19 ++++++++- 5 files changed, 70 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 2d5f9c35bfdc..08c1725f371a 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -884,7 +884,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, struct bnxt_re_qp_req ureq; struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; struct ib_umem *umem; - int bytes = 0; + int bytes = 0, psn_sz; struct ib_ucontext *context = pd->ib_pd.uobject->context; struct bnxt_re_ucontext *cntx = container_of(context, struct bnxt_re_ucontext, @@ -894,8 +894,12 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, bytes = (qplib_qp->sq.max_wqe * BNXT_QPLIB_MAX_SQE_ENTRY_SIZE); /* Consider mapping PSN search memory only for RC QPs. */ - if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) - bytes += (qplib_qp->sq.max_wqe * sizeof(struct sq_psn_search)); + if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) { + psn_sz = bnxt_qplib_is_chip_gen_p5(&rdev->chip_ctx) ? + sizeof(struct sq_psn_search_ext) : + sizeof(struct sq_psn_search); + bytes += (qplib_qp->sq.max_wqe * psn_sz); + } bytes = PAGE_ALIGN(bytes); umem = ib_umem_get(udata, ureq.qpsva, bytes, IB_ACCESS_LOCAL_WRITE, 1); if (IS_ERR(umem)) @@ -1652,6 +1656,9 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, __from_ib_access_flags(qp_attr->qp_access_flags); /* LOCAL_WRITE access must be set to allow RC receive */ qp->qplib_qp.access |= BNXT_QPLIB_ACCESS_LOCAL_WRITE; + /* Temp: Set all params on QP as of now */ + qp->qplib_qp.access |= CMDQ_MODIFY_QP_ACCESS_REMOTE_WRITE; + qp->qplib_qp.access |= CMDQ_MODIFY_QP_ACCESS_REMOTE_READ; } if (qp_attr_mask & IB_QP_PKEY_INDEX) { qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PKEY; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 117e78643720..77eb3d556006 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -870,10 +870,11 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) unsigned long int psn_search, poff = 0; struct bnxt_qplib_q *sq = &qp->sq; struct bnxt_qplib_q *rq = &qp->rq; + int i, rc, req_size, psn_sz = 0; struct bnxt_qplib_hwq *xrrq; - int i, rc, req_size, psn_sz; u16 cmd_flags = 0, max_ssge; u32 sw_prod, qp_flags = 0; + u16 max_rsge; RCFW_CMD_PREP(req, CREATE_QP, cmd_flags); @@ -883,8 +884,11 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) req.qp_handle = cpu_to_le64(qp->qp_handle); /* SQ */ - psn_sz = (qp->type == CMDQ_CREATE_QP_TYPE_RC) ? - sizeof(struct sq_psn_search) : 0; + if (qp->type == CMDQ_CREATE_QP_TYPE_RC) { + psn_sz = bnxt_qplib_is_chip_gen_p5(res->cctx) ? + sizeof(struct sq_psn_search_ext) : + sizeof(struct sq_psn_search); + } sq->hwq.max_elements = sq->max_wqe; rc = bnxt_qplib_alloc_init_hwq(res->pdev, &sq->hwq, sq->sglist, sq->nmap, &sq->hwq.max_elements, @@ -914,10 +918,16 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) poff = (psn_search & ~PAGE_MASK) / BNXT_QPLIB_MAX_PSNE_ENTRY_SIZE; } - for (i = 0; i < sq->hwq.max_elements; i++) + for (i = 0; i < sq->hwq.max_elements; i++) { sq->swq[i].psn_search = &psn_search_ptr[get_psne_pg(i + poff)] [get_psne_idx(i + poff)]; + /*psns_ext will be used only for P5 chips. */ + sq->swq[i].psn_ext = + (struct sq_psn_search_ext *) + &psn_search_ptr[get_psne_pg(i + poff)] + [get_psne_idx(i + poff)]; + } } pbl = &sq->hwq.pbl[PBL_LVL_0]; req.sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); @@ -1016,8 +1026,9 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) req.sq_fwo_sq_sge = cpu_to_le16( ((max_ssge & CMDQ_CREATE_QP_SQ_SGE_MASK) << CMDQ_CREATE_QP_SQ_SGE_SFT) | 0); + max_rsge = bnxt_qplib_is_chip_gen_p5(res->cctx) ? 6 : rq->max_sge; req.rq_fwo_rq_sge = cpu_to_le16( - ((rq->max_sge & CMDQ_CREATE_QP_RQ_SGE_MASK) + ((max_rsge & CMDQ_CREATE_QP_RQ_SGE_MASK) << CMDQ_CREATE_QP_RQ_SGE_SFT) | 0); /* ORRQ and IRRQ */ if (psn_sz) { @@ -1062,6 +1073,7 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) qp->id = le32_to_cpu(resp.xid); qp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET; + qp->cctx = res->cctx; INIT_LIST_HEAD(&qp->sq_flush); INIT_LIST_HEAD(&qp->rq_flush); rcfw->qp_tbl[qp->id].qp_id = qp->id; @@ -1748,14 +1760,26 @@ int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp, } swq->next_psn = sq->psn & BTH_PSN_MASK; if (swq->psn_search) { - swq->psn_search->opcode_start_psn = cpu_to_le32( - ((swq->start_psn << SQ_PSN_SEARCH_START_PSN_SFT) & - SQ_PSN_SEARCH_START_PSN_MASK) | - ((wqe->type << SQ_PSN_SEARCH_OPCODE_SFT) & - SQ_PSN_SEARCH_OPCODE_MASK)); - swq->psn_search->flags_next_psn = cpu_to_le32( - ((swq->next_psn << SQ_PSN_SEARCH_NEXT_PSN_SFT) & - SQ_PSN_SEARCH_NEXT_PSN_MASK)); + u32 opcd_spsn; + u32 flg_npsn; + + opcd_spsn = ((swq->start_psn << SQ_PSN_SEARCH_START_PSN_SFT) & + SQ_PSN_SEARCH_START_PSN_MASK); + opcd_spsn |= ((wqe->type << SQ_PSN_SEARCH_OPCODE_SFT) & + SQ_PSN_SEARCH_OPCODE_MASK); + flg_npsn = ((swq->next_psn << SQ_PSN_SEARCH_NEXT_PSN_SFT) & + SQ_PSN_SEARCH_NEXT_PSN_MASK); + if (bnxt_qplib_is_chip_gen_p5(qp->cctx)) { + swq->psn_ext->opcode_start_psn = + cpu_to_le32(opcd_spsn); + swq->psn_ext->flags_next_psn = + cpu_to_le32(flg_npsn); + } else { + swq->psn_search->opcode_start_psn = + cpu_to_le32(opcd_spsn); + swq->psn_search->flags_next_psn = + cpu_to_le32(flg_npsn); + } } queue_err: if (sch_handler) { @@ -2053,6 +2077,7 @@ static int __flush_rq(struct bnxt_qplib_q *rq, struct bnxt_qplib_qp *qp, opcode = CQ_BASE_CQE_TYPE_RES_RC; break; case CMDQ_CREATE_QP_TYPE_UD: + case CMDQ_CREATE_QP_TYPE_GSI: opcode = CQ_BASE_CQE_TYPE_RES_UD; break; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index f9f7cc54d5ce..3f618b5f1f06 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -106,6 +106,7 @@ struct bnxt_qplib_swq { u32 start_psn; u32 next_psn; struct sq_psn_search *psn_search; + struct sq_psn_search_ext *psn_ext; }; struct bnxt_qplib_swqe { @@ -254,6 +255,7 @@ struct bnxt_qplib_q { struct bnxt_qplib_qp { struct bnxt_qplib_pd *pd; struct bnxt_qplib_dpi *dpi; + struct bnxt_qplib_chip_ctx *cctx; u64 qp_handle; #define BNXT_QPLIB_QP_ID_INVALID 0xFFFFFFFF u32 id; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c index efa0f2949dc7..e9c53e406404 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -119,7 +119,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, * reporting the max number */ attr->max_qp_wqes -= BNXT_QPLIB_RESERVED_QP_WRS; - attr->max_qp_sges = sb->max_sge; + attr->max_qp_sges = bnxt_qplib_is_chip_gen_p5(rcfw->res->cctx) ? + 6 : sb->max_sge; attr->max_cq = le32_to_cpu(sb->max_cq); attr->max_cq_wqes = le32_to_cpu(sb->max_cqe); attr->max_cq_sges = attr->max_qp_sges; diff --git a/drivers/infiniband/hw/bnxt_re/roce_hsi.h b/drivers/infiniband/hw/bnxt_re/roce_hsi.h index 43cce22a5bd1..e4b09e7c2175 100644 --- a/drivers/infiniband/hw/bnxt_re/roce_hsi.h +++ b/drivers/infiniband/hw/bnxt_re/roce_hsi.h @@ -159,7 +159,24 @@ struct sq_psn_search { #define SQ_PSN_SEARCH_NEXT_PSN_MASK 0xffffffUL #define SQ_PSN_SEARCH_NEXT_PSN_SFT 0 #define SQ_PSN_SEARCH_FLAGS_MASK 0xff000000UL - #define SQ_PSN_SEARCH_FLAGS_SFT 24 + #define SQ_PSN_SEARCH_FLAGS_SFT 24 +}; + +/* sq_psn_search_ext (size:128b/16B) */ +struct sq_psn_search_ext { + __le32 opcode_start_psn; + #define SQ_PSN_SEARCH_EXT_START_PSN_MASK 0xffffffUL + #define SQ_PSN_SEARCH_EXT_START_PSN_SFT 0 + #define SQ_PSN_SEARCH_EXT_OPCODE_MASK 0xff000000UL + #define SQ_PSN_SEARCH_EXT_OPCODE_SFT 24 + __le32 flags_next_psn; + #define SQ_PSN_SEARCH_EXT_NEXT_PSN_MASK 0xffffffUL + #define SQ_PSN_SEARCH_EXT_NEXT_PSN_SFT 0 + #define SQ_PSN_SEARCH_EXT_FLAGS_MASK 0xff000000UL + #define SQ_PSN_SEARCH_EXT_FLAGS_SFT 24 + __le16 start_slot_idx; + __le16 reserved16; + __le32 reserved32; }; /* Send SQ WQE (40 bytes) */ From 95b86d1c91ad3b19f882d9e70aa37c8e99e8dc17 Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:27 -0500 Subject: [PATCH 1356/4277] RDMA/bnxt_re: Update kernel user abi to pass chip context User space verbs provider library would need chip context. Changing the ABI to add chip version details in structure. Furthermore, changing the kernel driver ucontext allocation code to initialize the abi structure with appropriate values. As suggested by community, appended the new fields at the bottom of the ABI structure and retaining to older fields as those were in the older versions. Keeping the ABI version at 1 and adding a new field in the ucontext response structure to hold the component mask. The user space library should check pre-defined flags to figure out if a certain feature is supported on not. Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 17 ++++++++++++++--- include/uapi/rdma/bnxt_re-abi.h | 11 +++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 08c1725f371a..1d7469e23cde 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -3692,9 +3692,10 @@ struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev, struct ib_udata *udata) { struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; struct bnxt_re_uctx_resp resp; struct bnxt_re_ucontext *uctx; - struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + u32 chip_met_rev_num = 0; int rc; dev_dbg(rdev_to_dev(rdev), "ABI version requested %d", @@ -3719,14 +3720,24 @@ struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev, } spin_lock_init(&uctx->sh_lock); - resp.dev_id = rdev->en_dev->pdev->devfn; /*Temp, Use idr_alloc instead*/ + resp.comp_mask |= BNXT_RE_UCNTX_CMASK_HAVE_CCTX; + chip_met_rev_num = rdev->chip_ctx.chip_num; + chip_met_rev_num |= ((u32)rdev->chip_ctx.chip_rev & 0xFF) << + BNXT_RE_CHIP_ID0_CHIP_REV_SFT; + chip_met_rev_num |= ((u32)rdev->chip_ctx.chip_metal & 0xFF) << + BNXT_RE_CHIP_ID0_CHIP_MET_SFT; + resp.chip_id0 = chip_met_rev_num; + /* Future extension of chip info */ + resp.chip_id1 = 0; + /*Temp, Use idr_alloc instead */ + resp.dev_id = rdev->en_dev->pdev->devfn; resp.max_qp = rdev->qplib_ctx.qpc_count; resp.pg_size = PAGE_SIZE; resp.cqe_sz = sizeof(struct cq_base); resp.max_cqd = dev_attr->max_cq_wqes; resp.rsvd = 0; - rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); + rc = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp))); if (rc) { dev_err(rdev_to_dev(rdev), "Failed to copy user context"); rc = -EFAULT; diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index a7a6111e50c7..dc52e3cf574c 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -44,6 +44,14 @@ #define BNXT_RE_ABI_VERSION 1 +#define BNXT_RE_CHIP_ID0_CHIP_NUM_SFT 0x00 +#define BNXT_RE_CHIP_ID0_CHIP_REV_SFT 0x10 +#define BNXT_RE_CHIP_ID0_CHIP_MET_SFT 0x18 + +enum { + BNXT_RE_UCNTX_CMASK_HAVE_CCTX = 0x1ULL +}; + struct bnxt_re_uctx_resp { __u32 dev_id; __u32 max_qp; @@ -51,6 +59,9 @@ struct bnxt_re_uctx_resp { __u32 cqe_sz; __u32 max_cqd; __u32 rsvd; + __aligned_u64 comp_mask; + __u32 chip_id0; + __u32 chip_id1; }; /* From ecb53febfcad565366762b7413b03452874643db Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Thu, 7 Feb 2019 01:31:28 -0500 Subject: [PATCH 1357/4277] RDMA/bnxt_en: Enable RDMA driver support for 57500 chip Re-enabling RDMA driver support on 57500 chips. Removing the forced error code for 57500 chip. Signed-off-by: Michael Chan Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index ea45a9b8179e..cf475873ce81 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -43,9 +43,6 @@ static int bnxt_register_dev(struct bnxt_en_dev *edev, int ulp_id, if (ulp_id == BNXT_ROCE_ULP) { unsigned int max_stat_ctxs; - if (bp->flags & BNXT_FLAG_CHIP_P5) - return -EOPNOTSUPP; - max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp); if (max_stat_ctxs <= BNXT_MIN_ROCE_STAT_CTXS || bp->cp_nr_rings == max_stat_ctxs) From ebe9f6f19d80d8978d16078dff3d5bd93ad8d102 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Feb 2019 14:56:50 -0800 Subject: [PATCH 1358/4277] acpi/nfit: Fix bus command validation Commit 11189c1089da "acpi/nfit: Fix command-supported detection" broke ND_CMD_CALL for bus-level commands. The "func = cmd" assumption is only valid for: ND_CMD_ARS_CAP ND_CMD_ARS_START ND_CMD_ARS_STATUS ND_CMD_CLEAR_ERROR The function number otherwise needs to be pulled from the command payload for: NFIT_CMD_TRANSLATE_SPA NFIT_CMD_ARS_INJECT_SET NFIT_CMD_ARS_INJECT_CLEAR NFIT_CMD_ARS_INJECT_GET Update cmd_to_func() for the bus case and call it in the common path. Fixes: 11189c1089da ("acpi/nfit: Fix command-supported detection") Cc: Reviewed-by: Vishal Verma Reported-by: Grzegorz Burzynski Tested-by: Jeff Moyer Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index e18ade5d74e9..c34c595d6bb0 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -415,7 +415,7 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd, if (call_pkg) { int i; - if (nfit_mem->family != call_pkg->nd_family) + if (nfit_mem && nfit_mem->family != call_pkg->nd_family) return -ENOTTY; for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) @@ -424,6 +424,10 @@ static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd, return call_pkg->nd_command; } + /* In the !call_pkg case, bus commands == bus functions */ + if (!nfit_mem) + return cmd; + /* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */ if (nfit_mem->family == NVDIMM_FAMILY_INTEL) return cmd; @@ -454,17 +458,18 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, if (cmd_rc) *cmd_rc = -EINVAL; + if (cmd == ND_CMD_CALL) + call_pkg = buf; + func = cmd_to_func(nfit_mem, cmd, call_pkg); + if (func < 0) + return func; + if (nvdimm) { struct acpi_device *adev = nfit_mem->adev; if (!adev) return -ENOTTY; - if (cmd == ND_CMD_CALL) - call_pkg = buf; - func = cmd_to_func(nfit_mem, cmd, call_pkg); - if (func < 0) - return func; dimm_name = nvdimm_name(nvdimm); cmd_name = nvdimm_cmd_name(cmd); cmd_mask = nvdimm_cmd_mask(nvdimm); @@ -475,12 +480,9 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, } else { struct acpi_device *adev = to_acpi_dev(acpi_desc); - func = cmd; cmd_name = nvdimm_bus_cmd_name(cmd); cmd_mask = nd_desc->cmd_mask; - dsm_mask = cmd_mask; - if (cmd == ND_CMD_CALL) - dsm_mask = nd_desc->bus_dsm_mask; + dsm_mask = nd_desc->bus_dsm_mask; desc = nd_cmd_bus_desc(cmd); guid = to_nfit_uuid(NFIT_DEV_BUS); handle = adev->handle; From 576057bf75cb3e40ff81bb01f021d76b764fdc92 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Feb 2019 22:19:42 -0800 Subject: [PATCH 1359/4277] Input: ili210x - switch to using devm_device_add_group() By switching to devm_device_add_group() we can complete driver conversion to using managed resources and get rid of ili210x_i2c_remove(). Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 6cfe463ac118..af1dd9cff12a 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -376,7 +376,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, return error; } - error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + error = devm_device_add_group(dev, &ili210x_attr_group); if (error) { dev_err(dev, "Unable to create sysfs attributes, err: %d\n", error); @@ -386,7 +386,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, error = input_register_device(priv->input); if (error) { dev_err(dev, "Cannot register input device, err: %d\n", error); - goto err_remove_sysfs; + return error; } device_init_wakeup(dev, 1); @@ -396,17 +396,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, client->irq, firmware.id, firmware.major, firmware.minor); return 0; - -err_remove_sysfs: - sysfs_remove_group(&dev->kobj, &ili210x_attr_group); - return error; -} - -static int ili210x_i2c_remove(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); - - return 0; } static int __maybe_unused ili210x_i2c_suspend(struct device *dev) @@ -454,7 +443,6 @@ static struct i2c_driver ili210x_ts_driver = { }, .id_table = ili210x_i2c_id, .probe = ili210x_i2c_probe, - .remove = ili210x_i2c_remove, }; module_i2c_driver(ili210x_ts_driver); From 937c4e552fd1174784045684740edfcea536159d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 5 Feb 2019 14:40:40 -0800 Subject: [PATCH 1360/4277] Input: stmfts - acknowledge that setting brightness is a blocking call We need to turn regulators on and off when switching brightness, and that may block, therefore we have to set stmfts_brightness_set() as LED's brightness_set_blocking() method. Fixes: 78bcac7b2ae1 ("Input: add support for the STMicroelectronics FingerTip touchscreen") Acked-by: Andi Shyti Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 704e99046916..b6f95f20f924 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -106,27 +106,29 @@ struct stmfts_data { bool running; }; -static void stmfts_brightness_set(struct led_classdev *led_cdev, +static int stmfts_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); int err; - if (value == sdata->led_status || !sdata->ledvdd) - return; - - if (!value) { - regulator_disable(sdata->ledvdd); - } else { - err = regulator_enable(sdata->ledvdd); - if (err) - dev_warn(&sdata->client->dev, - "failed to disable ledvdd regulator: %d\n", - err); + if (value != sdata->led_status && sdata->ledvdd) { + if (!value) { + regulator_disable(sdata->ledvdd); + } else { + err = regulator_enable(sdata->ledvdd); + if (err) { + dev_warn(&sdata->client->dev, + "failed to disable ledvdd regulator: %d\n", + err); + return err; + } + } + sdata->led_status = value; } - sdata->led_status = value; + return 0; } static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev) @@ -608,7 +610,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata) sdata->led_cdev.name = STMFTS_DEV_NAME; sdata->led_cdev.max_brightness = LED_ON; sdata->led_cdev.brightness = LED_OFF; - sdata->led_cdev.brightness_set = stmfts_brightness_set; + sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set; sdata->led_cdev.brightness_get = stmfts_brightness_get; err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev); From 0ab0e5a383a78a9e3bd9a02a1b06237fe75fd6a1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Feb 2019 10:10:12 -0800 Subject: [PATCH 1361/4277] Input: tm2-touchkey - acknowledge that setting brightness is a blocking call We need to access I2C bus when switching brightness, and that may block, therefore we have to set stmfts_brightness_set() as LED's brightness_set_blocking() method. Fixes: 72d1f2346ded ("Input: tm2-touchkey - add touchkey driver support for TM2") Acked-by: Andi Shyti Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tm2-touchkey.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index 7dbef96559d2..d4455f3a5cf1 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -78,7 +78,7 @@ static struct touchkey_variant aries_touchkey_variant = { .cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF, }; -static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, +static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct tm2_touchkey_data *touchkey = @@ -97,9 +97,8 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, if (!touchkey->variant->fixed_regulator) regulator_set_voltage(touchkey->vdd, volt, volt); - if (touchkey->variant->no_reg) - i2c_smbus_write_byte(touchkey->client, data); - else + return touchkey->variant->no_reg ? + i2c_smbus_write_byte(touchkey->client, data) : i2c_smbus_write_byte_data(touchkey->client, touchkey->variant->base_reg, data); } @@ -270,7 +269,8 @@ static int tm2_touchkey_probe(struct i2c_client *client, touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME; touchkey->led_dev.brightness = LED_ON; touchkey->led_dev.max_brightness = LED_ON; - touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set; + touchkey->led_dev.brightness_set_blocking = + tm2_touchkey_led_brightness_set; error = devm_led_classdev_register(&client->dev, &touchkey->led_dev); if (error) { From dc9e47160626cdb58d5c39a4f43dcfdb27a5c004 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Feb 2019 09:50:53 +0000 Subject: [PATCH 1362/4277] rtc: 88pm860x: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#144925-144928 ("Unintended sign extension") Fixes: 008b30408c40 ("mfd: Add rtc support to 88pm860x") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-88pm860x.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index 01ffc0ef8033..d25282b4a7dd 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -115,11 +115,13 @@ static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -145,7 +147,8 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm) /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; base = ticks - data; dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -170,10 +173,12 @@ static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -198,11 +203,13 @@ static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); From fb0b322537a831b5b0cb948c56f8f958ce493d3a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Feb 2019 10:08:11 +0000 Subject: [PATCH 1363/4277] rtc: 88pm80x: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#714646-714649 ("Unintended sign extension") Fixes: 2985c29c1964 ("rtc: Add rtc support to 88PM80X PMIC") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-88pm80x.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c index cab293cb2bf0..1fc48ebd3cd0 100644 --- a/drivers/rtc/rtc-88pm80x.c +++ b/drivers/rtc/rtc-88pm80x.c @@ -114,12 +114,14 @@ static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm) unsigned char buf[4]; unsigned long ticks, base, data; regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -137,7 +139,8 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm) /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; base = ticks - data; dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -158,11 +161,13 @@ static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) int ret; regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -185,12 +190,14 @@ static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0); regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); From e42280886018c6f77f0a90190f7cba344b0df3e0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Feb 2019 10:31:02 +0000 Subject: [PATCH 1364/4277] rtc: pm8xxx: fix unintended sign extension Shifting a u8 by 24 will cause the value to be promoted to an integer. If the top bit of the u8 is set then the following conversion to an unsigned long will sign extend the value causing the upper 32 bits to be set in the result. Fix this by casting the u8 value to an unsigned long before the shift. Detected by CoverityScan, CID#1309693 ("Unintended sign extension") Fixes: 9a9a54ad7aa2 ("drivers/rtc: add support for Qualcomm PMIC8xxx RTC") Signed-off-by: Colin Ian King Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pm8xxx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 1074e3dbfc1d..cda020700744 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -213,7 +213,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) } } - secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + secs = value[0] | (value[1] << 8) | (value[2] << 16) | + ((unsigned long)value[3] << 24); rtc_time_to_tm(secs, tm); @@ -284,7 +285,8 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) return rc; } - secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + secs = value[0] | (value[1] << 8) | (value[2] << 16) | + ((unsigned long)value[3] << 24); rtc_time_to_tm(secs, &alarm->time); From 7dd63ec076cfc1a7c46742e2d820554a6fb42fc6 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 8 Feb 2019 11:01:03 +0100 Subject: [PATCH 1365/4277] rtc: imx-sc: depend on HAVE_ARM_SMCCC Fix the following build issue: drivers/rtc/rtc-imx-sc.o: In function `imx_sc_rtc_set_time': >> rtc-imx-sc.c:(.text+0x212): undefined reference to `__arm_smccc_smc' Fixes: 13a929f38ad7 ("rtc: imx-sc: add rtc set time support") Reviewed-by: Anson Huang Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index faa9ae1a3062..8f6735e0ff14 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1698,6 +1698,7 @@ config RTC_DRV_SNVS config RTC_DRV_IMX_SC depends on IMX_SCU + depends on HAVE_ARM_SMCCC tristate "NXP i.MX System Controller RTC support" help If you say yes here you get support for the NXP i.MX System From 001aca0dbb1d791c676c50a7a610921b6c9b2180 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 30 Jan 2019 21:20:05 +0100 Subject: [PATCH 1366/4277] dt-bindings: Add a prefix for Gateworks Corporation As I am adding a PLD GPIO driver for Gateworks, we need their prefix among the vendors. Cc: Imre Kaloz Cc: Tim Harvey Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 389508584f48..c9af4e33f00b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -141,6 +141,7 @@ focaltech FocalTech Systems Co.,Ltd friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd fsl Freescale Semiconductor fujitsu Fujitsu Ltd. +gateworks Gateworks Corporation gcw Game Consoles Worldwide ge General Electric Company geekbuying GeekBuying From 35b21b6ef888e69c42d7dc93bc360084d31e95f4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 30 Jan 2019 21:31:55 +0100 Subject: [PATCH 1367/4277] gpio: Add DT bindings for Gateworks PLD GPIO This adds device tree bindings for the Gateworks PLD GPIO chip, a simple I2C GPIO controller. Cc: devicetree@vger.kernel.org Cc: Imre Kaloz Cc: Tim Harvey Signed-off-by: Linus Walleij --- .../bindings/gpio/gateworks,pld-gpio.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt diff --git a/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt b/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt new file mode 100644 index 000000000000..6e81f8b755c5 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt @@ -0,0 +1,20 @@ +Gateworks PLD GPIO controller bindings + +The GPIO controller should be a child node on an I2C bus, +see: i2c/i2c.txt for details. + +Required properties: +- compatible: Should be "gateworks,pld-gpio" +- reg: I2C slave address +- gpio-controller: Marks the device node as a GPIO controller. +- #gpio-cells: Should be <2>. The first cell is the gpio number and + the second cell is used to specify optional parameters. + +Example: + +pld@56 { + compatible = "gateworks,pld-gpio"; + reg = <0x56>; + gpio-controller; + #gpio-cells = <2>; +}; From b67328e1cf9735d2491ef9402fb7439db1d6e2ae Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:24:46 +0000 Subject: [PATCH 1368/4277] pinctrl: at91: add option to use drive strength bits SAM9X60 uses high and low drive strengths. To implement this, in at91_pinctrl_mux_ops::set_drivestrength and at91_pinctrl_mux_ops::get_drivestrength we need bit numbers of drive strengths (1 for low, 2 for high), thus change the code to allow the usage of drive strength bit numbers. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-at91.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 3d49bbbcdbc7..e907093b5b13 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -72,10 +72,15 @@ static int gpio_banks; * DRIVE_STRENGTH_DEFAULT is just a placeholder to avoid changing the drive * strength when there is no dt config for it. */ -#define DRIVE_STRENGTH_DEFAULT (0 << DRIVE_STRENGTH_SHIFT) -#define DRIVE_STRENGTH_LOW (1 << DRIVE_STRENGTH_SHIFT) -#define DRIVE_STRENGTH_MED (2 << DRIVE_STRENGTH_SHIFT) -#define DRIVE_STRENGTH_HI (3 << DRIVE_STRENGTH_SHIFT) +enum drive_strength_bit { + DRIVE_STRENGTH_BIT_DEF, + DRIVE_STRENGTH_BIT_LOW, + DRIVE_STRENGTH_BIT_MED, + DRIVE_STRENGTH_BIT_HI, +}; + +#define DRIVE_STRENGTH_BIT_MSK(name) (DRIVE_STRENGTH_BIT_##name << \ + DRIVE_STRENGTH_SHIFT) /** * struct at91_pmx_func - describes AT91 pinmux functions @@ -551,7 +556,7 @@ static unsigned at91_mux_sama5d3_get_drivestrength(void __iomem *pio, /* SAMA5 strength is 1:1 with our defines, * except 0 is equivalent to low per datasheet */ if (!tmp) - tmp = DRIVE_STRENGTH_LOW; + tmp = DRIVE_STRENGTH_BIT_MSK(LOW); return tmp; } @@ -564,7 +569,7 @@ static unsigned at91_mux_sam9x5_get_drivestrength(void __iomem *pio, /* strength is inverse in SAM9x5s hardware with the pinctrl defines * hardware: 0 = hi, 1 = med, 2 = low, 3 = rsvd */ - tmp = DRIVE_STRENGTH_HI - tmp; + tmp = DRIVE_STRENGTH_BIT_MSK(HI) - tmp; return tmp; } @@ -600,7 +605,7 @@ static void at91_mux_sam9x5_set_drivestrength(void __iomem *pio, unsigned pin, /* strength is inverse on SAM9x5s with our defines * 0 = hi, 1 = med, 2 = low, 3 = rsvd */ - setting = DRIVE_STRENGTH_HI - setting; + setting = DRIVE_STRENGTH_BIT_MSK(HI) - setting; set_drive_strength(pio + at91sam9x5_get_drive_register(pin), pin, setting); @@ -959,11 +964,11 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev, } \ } while (0) -#define DBG_SHOW_FLAG_MASKED(mask,flag) do { \ +#define DBG_SHOW_FLAG_MASKED(mask, flag, name) do { \ if ((config & mask) == flag) { \ if (num_conf) \ seq_puts(s, "|"); \ - seq_puts(s, #flag); \ + seq_puts(s, #name); \ num_conf++; \ } \ } while (0) @@ -981,9 +986,12 @@ static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev, DBG_SHOW_FLAG(PULL_DOWN); DBG_SHOW_FLAG(DIS_SCHMIT); DBG_SHOW_FLAG(DEGLITCH); - DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_LOW); - DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_MED); - DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_HI); + DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(LOW), + DRIVE_STRENGTH_LOW); + DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(MED), + DRIVE_STRENGTH_MED); + DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(HI), + DRIVE_STRENGTH_HI); DBG_SHOW_FLAG(DEBOUNCE); if (config & DEBOUNCE) { val = config >> DEBOUNCE_VAL_SHIFT; From 42ef75576bdd2ca8019c553d25847e124153a7ee Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:24:49 +0000 Subject: [PATCH 1369/4277] pinctrl: at91: add drive strength support for SAM9X60 Add drive strength support for SAM9X60 pin controller. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-at91.c | 52 ++++++++++++++++++++++++++++++++++ drivers/pinctrl/pinctrl-at91.h | 2 ++ 2 files changed, 54 insertions(+) diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index e907093b5b13..6f443c3728cb 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -574,6 +574,17 @@ static unsigned at91_mux_sam9x5_get_drivestrength(void __iomem *pio, return tmp; } +static unsigned at91_mux_sam9x60_get_drivestrength(void __iomem *pio, + unsigned pin) +{ + unsigned tmp = readl_relaxed(pio + SAM9X60_PIO_DRIVER1); + + if (tmp & BIT(pin)) + return DRIVE_STRENGTH_BIT_HI; + + return DRIVE_STRENGTH_BIT_LOW; +} + static void set_drive_strength(void __iomem *reg, unsigned pin, u32 strength) { unsigned tmp = readl_relaxed(reg); @@ -611,6 +622,27 @@ static void at91_mux_sam9x5_set_drivestrength(void __iomem *pio, unsigned pin, setting); } +static void at91_mux_sam9x60_set_drivestrength(void __iomem *pio, unsigned pin, + u32 setting) +{ + unsigned int tmp; + + if (setting <= DRIVE_STRENGTH_BIT_DEF || + setting == DRIVE_STRENGTH_BIT_MED || + setting > DRIVE_STRENGTH_BIT_HI) + return; + + tmp = readl_relaxed(pio + SAM9X60_PIO_DRIVER1); + + /* Strength is 0: low, 1: hi */ + if (setting == DRIVE_STRENGTH_BIT_LOW) + tmp &= ~BIT(pin); + else + tmp |= BIT(pin); + + writel_relaxed(tmp, pio + SAM9X60_PIO_DRIVER1); +} + static struct at91_pinctrl_mux_ops at91rm9200_ops = { .get_periph = at91_mux_get_periph, .mux_A_periph = at91_mux_set_A_periph, @@ -639,6 +671,26 @@ static struct at91_pinctrl_mux_ops at91sam9x5_ops = { .irq_type = alt_gpio_irq_type, }; +static const struct at91_pinctrl_mux_ops sam9x60_ops = { + .get_periph = at91_mux_pio3_get_periph, + .mux_A_periph = at91_mux_pio3_set_A_periph, + .mux_B_periph = at91_mux_pio3_set_B_periph, + .mux_C_periph = at91_mux_pio3_set_C_periph, + .mux_D_periph = at91_mux_pio3_set_D_periph, + .get_deglitch = at91_mux_pio3_get_deglitch, + .set_deglitch = at91_mux_pio3_set_deglitch, + .get_debounce = at91_mux_pio3_get_debounce, + .set_debounce = at91_mux_pio3_set_debounce, + .get_pulldown = at91_mux_pio3_get_pulldown, + .set_pulldown = at91_mux_pio3_set_pulldown, + .get_schmitt_trig = at91_mux_pio3_get_schmitt_trig, + .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig, + .get_drivestrength = at91_mux_sam9x60_get_drivestrength, + .set_drivestrength = at91_mux_sam9x60_set_drivestrength, + .irq_type = alt_gpio_irq_type, + +}; + static struct at91_pinctrl_mux_ops sama5d3_ops = { .get_periph = at91_mux_pio3_get_periph, .mux_A_periph = at91_mux_pio3_set_A_periph, diff --git a/drivers/pinctrl/pinctrl-at91.h b/drivers/pinctrl/pinctrl-at91.h index 79b957f1dfa2..19fc27e66bfd 100644 --- a/drivers/pinctrl/pinctrl-at91.h +++ b/drivers/pinctrl/pinctrl-at91.h @@ -69,4 +69,6 @@ #define AT91SAM9X5_PIO_DRIVER1 0x114 /*PIO Driver 1 register offset*/ #define AT91SAM9X5_PIO_DRIVER2 0x118 /*PIO Driver 2 register offset*/ +#define SAM9X60_PIO_DRIVER1 0x118 /* PIO Driver 1 register offset */ + #endif From a2fcb1ce88409497b6e8b0f45a41052d5f07e508 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:24:53 +0000 Subject: [PATCH 1370/4277] pinctrl: at91: add compatibles for SAM9X60 pin controller Add compatibles for SAM9X60 pin controller. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-at91.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 6f443c3728cb..128003597f43 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1215,6 +1215,7 @@ static const struct of_device_id at91_pinctrl_of_match[] = { { .compatible = "atmel,sama5d3-pinctrl", .data = &sama5d3_ops }, { .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops }, { .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops }, + { .compatible = "microchip,sam9x60-pinctrl", .data = &sam9x60_ops }, { /* sentinel */ } }; @@ -1757,6 +1758,7 @@ static const struct gpio_chip at91_gpio_template = { static const struct of_device_id at91_gpio_of_match[] = { { .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, }, { .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops }, + { .compatible = "microchip,sam9x60-gpio", .data = &sam9x60_ops }, { /* sentinel */ } }; From 1d741f2e977d7783cd706d6759e29cf36be9d412 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:24:57 +0000 Subject: [PATCH 1371/4277] dt-bindings: add documentation for banks Add documentation for at91 pin controller banks. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- .../bindings/pinctrl/atmel,at91-pinctrl.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 3e23fece99da..40e33dfc36fd 100644 --- a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -116,6 +116,18 @@ Some requirements for using atmel,at91rm9200-pinctrl binding: configurations by referring to the phandle of that pin configuration node. 4. The gpio controller must be describe in the pinctrl simple-bus. +For each bank the required properties are: +- compatible: "atmel,at91sam9x5-gpio" or "atmel,at91rm9200-gpio" +- reg: physical base address and length of the controller's registers +- interrupts: interrupt outputs from the controller +- interrupt-controller: marks the device node as an interrupt controller +- #interrupt-cells: should be 2; refer to ../interrupt-controller/interrupts.txt + for more details. +- gpio-controller +- #gpio-cells: should be 2; the first cell is the GPIO number and the second + cell specifies GPIO flags as defined in . +- clocks: bank clock + Examples: pinctrl@fffff400 { @@ -125,6 +137,17 @@ pinctrl@fffff400 { compatible = "atmel,at91rm9200-pinctrl", "simple-bus"; reg = <0xfffff400 0x600>; + pioA: gpio@fffff400 { + compatible = "atmel,at91sam9x5-gpio"; + reg = <0xfffff400 0x200>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH 1>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 2>; + }; + atmel,mux-mask = < /* A B */ 0xffffffff 0xffc00c3b /* pioA */ From fde84f194aaddc3988e5cdc0c7088e5cd9683061 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:25:01 +0000 Subject: [PATCH 1372/4277] dt-bindings: add bindings for SAM9X60 Add device tree binding for SAM9X60 pin controller. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 40e33dfc36fd..470283b4ddbf 100644 --- a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -19,7 +19,7 @@ such as pull-up, multi drive, etc. Required properties for iomux controller: - compatible: "atmel,at91rm9200-pinctrl" or "atmel,at91sam9x5-pinctrl" - or "atmel,sama5d3-pinctrl" + or "atmel,sama5d3-pinctrl" or "microchip,sam9x60-pinctrl" - atmel,mux-mask: array of mask (periph per bank) to describe if a pin can be configured in this periph mode. All the periph and bank need to be describe. @@ -117,7 +117,8 @@ Some requirements for using atmel,at91rm9200-pinctrl binding: 4. The gpio controller must be describe in the pinctrl simple-bus. For each bank the required properties are: -- compatible: "atmel,at91sam9x5-gpio" or "atmel,at91rm9200-gpio" +- compatible: "atmel,at91sam9x5-gpio" or "atmel,at91rm9200-gpio" or + "microchip,sam9x60-gpio" - reg: physical base address and length of the controller's registers - interrupts: interrupt outputs from the controller - interrupt-controller: marks the device node as an interrupt controller From 64e21add8cd9717f042b523f35ea831eab14261b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:25:05 +0000 Subject: [PATCH 1373/4277] pinctrl: at91: add slewrate support for SAM9X60 Add slew rate support for SAM9X60 pin controller. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-at91.c | 48 ++++++++++++++++++++++++++++++ drivers/pinctrl/pinctrl-at91.h | 1 + include/dt-bindings/pinctrl/at91.h | 4 +++ 3 files changed, 53 insertions(+) diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 128003597f43..cb7c432769a5 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -59,6 +59,9 @@ static int gpio_banks; #define OUTPUT (1 << 7) #define OUTPUT_VAL_SHIFT 8 #define OUTPUT_VAL (0x1 << OUTPUT_VAL_SHIFT) +#define SLEWRATE_SHIFT 9 +#define SLEWRATE_MASK 0x1 +#define SLEWRATE (SLEWRATE_MASK << SLEWRATE_SHIFT) #define DEBOUNCE (1 << 16) #define DEBOUNCE_VAL_SHIFT 17 #define DEBOUNCE_VAL (0x3fff << DEBOUNCE_VAL_SHIFT) @@ -82,6 +85,13 @@ enum drive_strength_bit { #define DRIVE_STRENGTH_BIT_MSK(name) (DRIVE_STRENGTH_BIT_##name << \ DRIVE_STRENGTH_SHIFT) +enum slewrate_bit { + SLEWRATE_BIT_DIS, + SLEWRATE_BIT_ENA, +}; + +#define SLEWRATE_BIT_MSK(name) (SLEWRATE_BIT_##name << SLEWRATE_SHIFT) + /** * struct at91_pmx_func - describes AT91 pinmux functions * @name: the name of this specific function @@ -171,6 +181,8 @@ struct at91_pinctrl_mux_ops { unsigned (*get_drivestrength)(void __iomem *pio, unsigned pin); void (*set_drivestrength)(void __iomem *pio, unsigned pin, u32 strength); + unsigned (*get_slewrate)(void __iomem *pio, unsigned pin); + void (*set_slewrate)(void __iomem *pio, unsigned pin, u32 slewrate); /* irq */ int (*irq_type)(struct irq_data *d, unsigned type); }; @@ -585,6 +597,16 @@ static unsigned at91_mux_sam9x60_get_drivestrength(void __iomem *pio, return DRIVE_STRENGTH_BIT_LOW; } +static unsigned at91_mux_sam9x60_get_slewrate(void __iomem *pio, unsigned pin) +{ + unsigned tmp = readl_relaxed(pio + SAM9X60_PIO_SLEWR); + + if ((tmp & BIT(pin))) + return SLEWRATE_BIT_ENA; + + return SLEWRATE_BIT_DIS; +} + static void set_drive_strength(void __iomem *reg, unsigned pin, u32 strength) { unsigned tmp = readl_relaxed(reg); @@ -643,6 +665,24 @@ static void at91_mux_sam9x60_set_drivestrength(void __iomem *pio, unsigned pin, writel_relaxed(tmp, pio + SAM9X60_PIO_DRIVER1); } +static void at91_mux_sam9x60_set_slewrate(void __iomem *pio, unsigned pin, + u32 setting) +{ + unsigned int tmp; + + if (setting < SLEWRATE_BIT_DIS || setting > SLEWRATE_BIT_ENA) + return; + + tmp = readl_relaxed(pio + SAM9X60_PIO_SLEWR); + + if (setting == SLEWRATE_BIT_DIS) + tmp &= ~BIT(pin); + else + tmp |= BIT(pin); + + writel_relaxed(tmp, pio + SAM9X60_PIO_SLEWR); +} + static struct at91_pinctrl_mux_ops at91rm9200_ops = { .get_periph = at91_mux_get_periph, .mux_A_periph = at91_mux_set_A_periph, @@ -687,6 +727,8 @@ static const struct at91_pinctrl_mux_ops sam9x60_ops = { .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig, .get_drivestrength = at91_mux_sam9x60_get_drivestrength, .set_drivestrength = at91_mux_sam9x60_set_drivestrength, + .get_slewrate = at91_mux_sam9x60_get_slewrate, + .set_slewrate = at91_mux_sam9x60_set_slewrate, .irq_type = alt_gpio_irq_type, }; @@ -950,6 +992,8 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev, if (info->ops->get_drivestrength) *config |= (info->ops->get_drivestrength(pio, pin) << DRIVE_STRENGTH_SHIFT); + if (info->ops->get_slewrate) + *config |= (info->ops->get_slewrate(pio, pin) << SLEWRATE_SHIFT); if (at91_mux_get_output(pio, pin, &out)) *config |= OUTPUT | (out << OUTPUT_VAL_SHIFT); @@ -1001,6 +1045,9 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev, info->ops->set_drivestrength(pio, pin, (config & DRIVE_STRENGTH) >> DRIVE_STRENGTH_SHIFT); + if (info->ops->set_slewrate) + info->ops->set_slewrate(pio, pin, + (config & SLEWRATE) >> SLEWRATE_SHIFT); } /* for each config */ @@ -1044,6 +1091,7 @@ static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev, DRIVE_STRENGTH_MED); DBG_SHOW_FLAG_MASKED(DRIVE_STRENGTH, DRIVE_STRENGTH_BIT_MSK(HI), DRIVE_STRENGTH_HI); + DBG_SHOW_FLAG(SLEWRATE); DBG_SHOW_FLAG(DEBOUNCE); if (config & DEBOUNCE) { val = config >> DEBOUNCE_VAL_SHIFT; diff --git a/drivers/pinctrl/pinctrl-at91.h b/drivers/pinctrl/pinctrl-at91.h index 19fc27e66bfd..223620f14b05 100644 --- a/drivers/pinctrl/pinctrl-at91.h +++ b/drivers/pinctrl/pinctrl-at91.h @@ -69,6 +69,7 @@ #define AT91SAM9X5_PIO_DRIVER1 0x114 /*PIO Driver 1 register offset*/ #define AT91SAM9X5_PIO_DRIVER2 0x118 /*PIO Driver 2 register offset*/ +#define SAM9X60_PIO_SLEWR 0x110 /* PIO Slew Rate Control Register */ #define SAM9X60_PIO_DRIVER1 0x118 /* PIO Driver 1 register offset */ #endif diff --git a/include/dt-bindings/pinctrl/at91.h b/include/dt-bindings/pinctrl/at91.h index eb81867eac77..8dc10e00c627 100644 --- a/include/dt-bindings/pinctrl/at91.h +++ b/include/dt-bindings/pinctrl/at91.h @@ -17,6 +17,7 @@ #define AT91_PINCTRL_DIS_SCHMIT (1 << 4) #define AT91_PINCTRL_OUTPUT (1 << 7) #define AT91_PINCTRL_OUTPUT_VAL(x) ((x & 0x1) << 8) +#define AT91_PINCTRL_SLEWRATE (1 << 9) #define AT91_PINCTRL_DEBOUNCE (1 << 16) #define AT91_PINCTRL_DEBOUNCE_VAL(x) (x << 17) @@ -27,6 +28,9 @@ #define AT91_PINCTRL_DRIVE_STRENGTH_MED (0x2 << 5) #define AT91_PINCTRL_DRIVE_STRENGTH_HI (0x3 << 5) +#define AT91_PINCTRL_SLEWRATE_DIS (0x0 << 9) +#define AT91_PINCTRL_SLEWRATE_ENA (0x1 << 9) + #define AT91_PIOA 0 #define AT91_PIOB 1 #define AT91_PIOC 2 From 5e07a820649bc7e29249c832abc33c1bffd0e2db Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Feb 2019 09:25:08 +0000 Subject: [PATCH 1374/4277] dt-bindings: add documentation for slew rate Add documentation for slew rate. Signed-off-by: Claudiu Beznea Acked-by: Ludovic Desroches Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 470283b4ddbf..eb39f5051159 100644 --- a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -100,6 +100,7 @@ DRIVE_STRENGTH (3 << 5): indicate the drive strength of the pin using the 11 - High OUTPUT (1 << 7): indicate this pin need to be configured as an output. OUTPUT_VAL (1 << 8): output val (1 = high, 0 = low) +SLEWRATE (1 << 9): slew rate of the pin: 0 = disable, 1 = enable DEBOUNCE (1 << 16): indicate this pin needs debounce. DEBOUNCE_VAL (0x3fff << 17): debounce value. From f1c894712bbe54b0c1d8a24f1b1bc32e3688d4ff Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 31 Jan 2019 09:38:53 -0800 Subject: [PATCH 1375/4277] pinctrl: qcom: qcs404: Drop unused UFS_RESET macro The UFS_RESET macro serves no purpose on QCS404, remove it. Signed-off-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-qcs404.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index 7aae52a09ff0..0c218f17b1d7 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -95,31 +95,6 @@ enum { .intr_detection_width = -1, \ } -#define UFS_RESET(pg_name, offset) \ - { \ - .name = #pg_name, \ - .pins = pg_name##_pins, \ - .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \ - .ctl_reg = offset, \ - .io_reg = offset + 0x4, \ - .intr_cfg_reg = 0, \ - .intr_status_reg = 0, \ - .intr_target_reg = 0, \ - .tile = NORTH, \ - .mux_bit = -1, \ - .pull_bit = 3, \ - .drv_bit = 0, \ - .oe_bit = -1, \ - .in_bit = -1, \ - .out_bit = 0, \ - .intr_enable_bit = -1, \ - .intr_status_bit = -1, \ - .intr_target_bit = -1, \ - .intr_raw_status_bit = -1, \ - .intr_polarity_bit = -1, \ - .intr_detection_bit = -1, \ - .intr_detection_width = -1, \ - } static const struct pinctrl_pin_desc qcs404_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), From 1cb66f080c27349fbf87fb327d587b4b0b624fa3 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 3 Feb 2019 14:02:33 +0100 Subject: [PATCH 1376/4277] pinctrl: bcm2835: declare pin config as generic Since commit 0de704955ee44 ("pinctrl: bcm2835: Add support for generic pinctrl binding") this driver is capable to use the generic interface. So declare this accordingly. Signed-off-by: Stefan Wahren Signed-off-by: Linus Walleij --- drivers/pinctrl/bcm/pinctrl-bcm2835.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index f180aa44a422..19eb4fd1ed4c 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -969,6 +969,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, } static const struct pinconf_ops bcm2835_pinconf_ops = { + .is_generic = true, .pin_config_get = bcm2835_pinconf_get, .pin_config_set = bcm2835_pinconf_set, }; From b6e5531c0f80de2779c87d0235b4fde5310a83b5 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 3 Feb 2019 14:02:34 +0100 Subject: [PATCH 1377/4277] pinctrl: bcm2835: Direct GPIO config changes to generic pinctrl In order to support GPIO config changes direct these to the generic pinctrl. This also requires an adjust of the return code for unsupported parameter otherwise gpiod_configure_flags wont work as expected. Signed-off-by: Stefan Wahren Signed-off-by: Linus Walleij --- drivers/pinctrl/bcm/pinctrl-bcm2835.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 19eb4fd1ed4c..183d1ffe6a75 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -341,6 +341,7 @@ static const struct gpio_chip bcm2835_gpio_chip = { .get_direction = bcm2835_gpio_get_direction, .get = bcm2835_gpio_get, .set = bcm2835_gpio_set, + .set_config = gpiochip_generic_config, .base = -1, .ngpio = BCM2835_NUM_GPIOS, .can_sleep = false, @@ -960,7 +961,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, break; default: - return -EINVAL; + return -ENOTSUPP; } /* switch param type */ } /* for each config */ From 740c68a0cd42aab21ad9aaae092ff8a2215966b1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 7 Feb 2019 13:20:44 +0100 Subject: [PATCH 1378/4277] ata: add Buddha PATA controller driver Add Buddha PATA controller driver. It enables libata support for the Buddha, Catweasel and X-Surf expansion boards on the Zorro expansion bus. Module removal is currently unsupported (the old IDE's buddha driver also doesn't support it). Tested-by: John Paul Adrian Glaubitz Cc: Michael Schmitz Cc: Geert Uytterhoeven Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Jens Axboe --- drivers/ata/Kconfig | 12 ++ drivers/ata/Makefile | 1 + drivers/ata/pata_buddha.c | 257 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 drivers/ata/pata_buddha.c diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 8218db17ebdb..a6beb2c5a692 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -963,6 +963,18 @@ config PATA_GAYLE If unsure, say N. +config PATA_BUDDHA + tristate "Buddha/Catweasel/X-Surf PATA support" + depends on ZORRO + help + This option enables support for the IDE interfaces + on the Buddha, Catweasel and X-Surf expansion boards + on the Zorro expansion bus. It supports up to two + interfaces on the Buddha, three on the Catweasel and + two on the X-Surf. + + If unsure, say N. + config PATA_ISAPNP tristate "ISA Plug and Play PATA support" depends on ISAPNP diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index d21cdd83f7ab..d8cc2e04a6c7 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o obj-$(CONFIG_PATA_FALCON) += pata_falcon.o obj-$(CONFIG_PATA_GAYLE) += pata_gayle.o +obj-$(CONFIG_PATA_BUDDHA) += pata_buddha.o obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o diff --git a/drivers/ata/pata_buddha.c b/drivers/ata/pata_buddha.c new file mode 100644 index 000000000000..11a8044ff633 --- /dev/null +++ b/drivers/ata/pata_buddha.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Buddha, Catweasel and X-Surf PATA controller driver + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Based on buddha.c: + * + * Copyright (C) 1997, 2001 by Geert Uytterhoeven and others + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DRV_NAME "pata_buddha" +#define DRV_VERSION "0.1.0" + +#define BUDDHA_BASE1 0x800 +#define BUDDHA_BASE2 0xa00 +#define BUDDHA_BASE3 0xc00 +#define XSURF_BASE1 0xb000 /* 2.5" interface */ +#define XSURF_BASE2 0xd000 /* 3.5" interface */ +#define BUDDHA_CONTROL 0x11a +#define BUDDHA_IRQ 0xf00 +#define XSURF_IRQ 0x7e +#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */ + +enum { + BOARD_BUDDHA = 0, + BOARD_CATWEASEL, + BOARD_XSURF +}; + +static unsigned int buddha_bases[3] __initdata = { + BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3 +}; + +static unsigned int xsurf_bases[2] __initdata = { + XSURF_BASE1, XSURF_BASE2 +}; + +static struct scsi_host_template pata_buddha_sht = { + ATA_PIO_SHT(DRV_NAME), +}; + +/* FIXME: is this needed? */ +static unsigned int pata_buddha_data_xfer(struct ata_queued_cmd *qc, + unsigned char *buf, + unsigned int buflen, int rw) +{ + struct ata_device *dev = qc->dev; + struct ata_port *ap = dev->link->ap; + void __iomem *data_addr = ap->ioaddr.data_addr; + unsigned int words = buflen >> 1; + + /* Transfer multiple of 2 bytes */ + if (rw == READ) + raw_insw((u16 *)data_addr, (u16 *)buf, words); + else + raw_outsw((u16 *)data_addr, (u16 *)buf, words); + + /* Transfer trailing byte, if any. */ + if (unlikely(buflen & 0x01)) { + unsigned char pad[2] = { }; + + /* Point buf to the tail of buffer */ + buf += buflen - 1; + + if (rw == READ) { + raw_insw((u16 *)data_addr, (u16 *)pad, 1); + *buf = pad[0]; + } else { + pad[0] = *buf; + raw_outsw((u16 *)data_addr, (u16 *)pad, 1); + } + words++; + } + + return words << 1; +} + +/* + * Provide our own set_mode() as we don't want to change anything that has + * already been configured.. + */ +static int pata_buddha_set_mode(struct ata_link *link, + struct ata_device **unused) +{ + struct ata_device *dev; + + ata_for_each_dev(dev, link, ENABLED) { + /* We don't really care */ + dev->pio_mode = dev->xfer_mode = XFER_PIO_0; + dev->xfer_shift = ATA_SHIFT_PIO; + dev->flags |= ATA_DFLAG_PIO; + ata_dev_info(dev, "configured for PIO\n"); + } + return 0; +} + +static bool pata_buddha_irq_check(struct ata_port *ap) +{ + u8 ch; + + ch = z_readb((unsigned long)ap->private_data); + + return !!(ch & 0x80); +} + +static void pata_xsurf_irq_clear(struct ata_port *ap) +{ + z_writeb(0, (unsigned long)ap->private_data); +} + +static struct ata_port_operations pata_buddha_ops = { + .inherits = &ata_sff_port_ops, + .sff_data_xfer = pata_buddha_data_xfer, + .sff_irq_check = pata_buddha_irq_check, + .cable_detect = ata_cable_unknown, + .set_mode = pata_buddha_set_mode, +}; + +static struct ata_port_operations pata_xsurf_ops = { + .inherits = &ata_sff_port_ops, + .sff_data_xfer = pata_buddha_data_xfer, + .sff_irq_check = pata_buddha_irq_check, + .sff_irq_clear = pata_xsurf_irq_clear, + .cable_detect = ata_cable_unknown, + .set_mode = pata_buddha_set_mode, +}; + +static int __init pata_buddha_init_one(void) +{ + struct zorro_dev *z = NULL; + + while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { + static const char *board_name[] + = { "Buddha", "Catweasel", "X-Surf" }; + struct ata_host *host; + void __iomem *buddha_board; + unsigned long board; + unsigned int type, nr_ports = 2; + int i; + + if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { + type = BOARD_BUDDHA; + } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) { + type = BOARD_CATWEASEL; + nr_ports++; + } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF) { + type = BOARD_XSURF; + } else + continue; + + dev_info(&z->dev, "%s IDE controller\n", board_name[type]); + + board = z->resource.start; + + if (type != BOARD_XSURF) { + if (!devm_request_mem_region(&z->dev, + board + BUDDHA_BASE1, + 0x800, DRV_NAME)) + continue; + } else { + if (!devm_request_mem_region(&z->dev, + board + XSURF_BASE1, + 0x1000, DRV_NAME)) + continue; + if (!devm_request_mem_region(&z->dev, + board + XSURF_BASE2, + 0x1000, DRV_NAME)) + continue; + } + + /* allocate host */ + host = ata_host_alloc(&z->dev, nr_ports); + if (!host) + continue; + + buddha_board = ZTWO_VADDR(board); + + /* enable the board IRQ on Buddha/Catweasel */ + if (type != BOARD_XSURF) + z_writeb(0, buddha_board + BUDDHA_IRQ_MR); + + for (i = 0; i < nr_ports; i++) { + struct ata_port *ap = host->ports[i]; + void __iomem *base, *irqport; + unsigned long ctl = 0; + + if (type != BOARD_XSURF) { + ap->ops = &pata_buddha_ops; + base = buddha_board + buddha_bases[i]; + ctl = BUDDHA_CONTROL; + irqport = buddha_board + BUDDHA_IRQ + i * 0x40; + } else { + ap->ops = &pata_xsurf_ops; + base = buddha_board + xsurf_bases[i]; + /* X-Surf has no CS1* (Control/AltStat) */ + irqport = buddha_board + XSURF_IRQ; + } + + ap->pio_mask = ATA_PIO4; + ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY; + + ap->ioaddr.data_addr = base; + ap->ioaddr.error_addr = base + 2 + 1 * 4; + ap->ioaddr.feature_addr = base + 2 + 1 * 4; + ap->ioaddr.nsect_addr = base + 2 + 2 * 4; + ap->ioaddr.lbal_addr = base + 2 + 3 * 4; + ap->ioaddr.lbam_addr = base + 2 + 4 * 4; + ap->ioaddr.lbah_addr = base + 2 + 5 * 4; + ap->ioaddr.device_addr = base + 2 + 6 * 4; + ap->ioaddr.status_addr = base + 2 + 7 * 4; + ap->ioaddr.command_addr = base + 2 + 7 * 4; + + if (ctl) { + ap->ioaddr.altstatus_addr = base + ctl; + ap->ioaddr.ctl_addr = base + ctl; + } + + ap->private_data = (void *)irqport; + + ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", board, + ctl ? board + buddha_bases[i] + ctl : 0); + } + + ata_host_activate(host, IRQ_AMIGA_PORTS, ata_sff_interrupt, + IRQF_SHARED, &pata_buddha_sht); + + } + + return 0; +} + +module_init(pata_buddha_init_one); + +MODULE_AUTHOR("Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("low-level driver for Buddha/Catweasel/X-Surf PATA"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); From f3d5e4f18dba18d7c2303dda68b9dbcf5ccc05cd Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 19 Jan 2019 07:52:01 +0300 Subject: [PATCH 1379/4277] ata: pata_of_platform: Allow to use 16-bit wide data transfer In some cases, the system bus can be configured for 16-bit mode, in this case using read/write functions for 32-bit values results in two cycles of 16 bits each, which is wrong. This patch adds the devicetree flag to switch the driver to use 16-bit mode for I/O transfers. Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Alexander Shiyan Signed-off-by: Jens Axboe --- drivers/ata/pata_of_platform.c | 6 +++++- drivers/ata/pata_platform.c | 22 ++++++++++++---------- include/linux/ata_platform.h | 3 ++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index 01161c1aef4d..7a0b1759e5f0 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -32,6 +32,7 @@ static int pata_of_platform_probe(struct platform_device *ofdev) unsigned int reg_shift = 0; int pio_mode = 0; int pio_mask; + bool use16bit; ret = of_address_to_resource(dn, 0, &io_res); if (ret) { @@ -60,11 +61,14 @@ static int pata_of_platform_probe(struct platform_device *ofdev) dev_info(&ofdev->dev, "pio-mode unspecified, assuming PIO0\n"); } + use16bit = of_property_read_bool(dn, "ata-generic,use16bit"); + pio_mask = 1 << pio_mode; pio_mask |= (1 << pio_mode) - 1; return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, irq_res, - reg_shift, pio_mask, &pata_platform_sht); + reg_shift, pio_mask, &pata_platform_sht, + use16bit); } static const struct of_device_id pata_of_platform_match[] = { diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index d6f8f5406442..31cd0f39b0a7 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -47,13 +47,6 @@ static struct scsi_host_template pata_platform_sht = { ATA_PIO_SHT(DRV_NAME), }; -static struct ata_port_operations pata_platform_port_ops = { - .inherits = &ata_sff_port_ops, - .sff_data_xfer = ata_sff_data_xfer32, - .cable_detect = ata_cable_unknown, - .set_mode = pata_platform_set_mode, -}; - static void pata_platform_setup_port(struct ata_ioports *ioaddr, unsigned int shift) { @@ -79,6 +72,7 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr, * @ioport_shift: I/O port shift * @__pio_mask: PIO mask * @sht: scsi_host_template to use when registering + * @use16bit: Flag to indicate 16-bit IO instead of 32-bit * * Register a platform bus IDE interface. Such interfaces are PIO and we * assume do not support IRQ sharing. @@ -101,7 +95,7 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr, int __pata_platform_probe(struct device *dev, struct resource *io_res, struct resource *ctl_res, struct resource *irq_res, unsigned int ioport_shift, int __pio_mask, - struct scsi_host_template *sht) + struct scsi_host_template *sht, bool use16bit) { struct ata_host *host; struct ata_port *ap; @@ -131,7 +125,15 @@ int __pata_platform_probe(struct device *dev, struct resource *io_res, return -ENOMEM; ap = host->ports[0]; - ap->ops = &pata_platform_port_ops; + ap->ops = devm_kzalloc(dev, sizeof(*ap->ops), GFP_KERNEL); + ap->ops->inherits = &ata_sff_port_ops; + ap->ops->cable_detect = ata_cable_unknown; + ap->ops->set_mode = pata_platform_set_mode; + if (use16bit) + ap->ops->sff_data_xfer = ata_sff_data_xfer; + else + ap->ops->sff_data_xfer = ata_sff_data_xfer32; + ap->pio_mask = __pio_mask; ap->flags |= ATA_FLAG_SLAVE_POSS; @@ -218,7 +220,7 @@ static int pata_platform_probe(struct platform_device *pdev) return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res, pp_info ? pp_info->ioport_shift : 0, - pio_mask, &pata_platform_sht); + pio_mask, &pata_platform_sht, false); } static struct platform_driver pata_platform_driver = { diff --git a/include/linux/ata_platform.h b/include/linux/ata_platform.h index ff2120215dec..9cafec92282d 100644 --- a/include/linux/ata_platform.h +++ b/include/linux/ata_platform.h @@ -19,7 +19,8 @@ extern int __pata_platform_probe(struct device *dev, struct resource *irq_res, unsigned int ioport_shift, int __pio_mask, - struct scsi_host_template *sht); + struct scsi_host_template *sht, + bool use16bit); /* * Marvell SATA private data From baac9ce1f333260485e9084809e909d3ce6c69dd Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 19 Jan 2019 07:52:02 +0300 Subject: [PATCH 1380/4277] ata: pata_platform: Add IRQF_SHARED to IRQ flags It's quite possible that multiple devices can be hooked up to the same interrupt line with the processor. So add IRQF_SHARED in request irq. Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Alexander Shiyan Signed-off-by: Jens Axboe --- drivers/ata/pata_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 31cd0f39b0a7..5aba691f09af 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -114,7 +114,7 @@ int __pata_platform_probe(struct device *dev, struct resource *io_res, */ if (irq_res && irq_res->start > 0) { irq = irq_res->start; - irq_flags = irq_res->flags & IRQF_TRIGGER_MASK; + irq_flags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; } /* From bdf2466b259fb1676485e587ed286308ab79d970 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Oct 2018 21:59:49 +0200 Subject: [PATCH 1381/4277] ata: pata_samsung_cf: simplify getting .driver_data We should get 'driver_data' from 'struct device' directly. Going via platform_device is an unneeded step back and forth. Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Wolfram Sang Signed-off-by: Jens Axboe --- drivers/ata/pata_samsung_cf.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index f5bd44b8bd63..1dc3361cb5a5 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -609,17 +609,15 @@ static int __exit pata_s3c_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int pata_s3c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct ata_host *host = platform_get_drvdata(pdev); + struct ata_host *host = dev_get_drvdata(dev); return ata_host_suspend(host, PMSG_SUSPEND); } static int pata_s3c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct ata_host *host = platform_get_drvdata(pdev); - struct s3c_ide_platdata *pdata = dev_get_platdata(&pdev->dev); + struct ata_host *host = dev_get_drvdata(dev); + struct s3c_ide_platdata *pdata = dev_get_platdata(dev); struct s3c_ide_info *info = host->private_data; pata_s3c_hwinit(info, pdata); From 6c826b6867ab37689da61b4256705bdd370911da Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:20 -0600 Subject: [PATCH 1382/4277] ata: macio: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. Cc: linux-ide@vger.kernel.org Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Rob Herring Signed-off-by: Jens Axboe --- drivers/ata/pata_macio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 9e7fc302430f..456ae7184f92 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -954,7 +954,7 @@ static void pata_macio_invariants(struct pata_macio_priv *priv) priv->kind = controller_k2_ata6; priv->timings = pata_macio_kauai_timings; } else if (of_device_is_compatible(priv->node, "keylargo-ata")) { - if (strcmp(priv->node->name, "ata-4") == 0) { + if (of_node_name_eq(priv->node, "ata-4")) { priv->kind = controller_kl_ata4; priv->timings = pata_macio_kl66_timings; } else { From f25191bb322dec8fa2979ecb8235643aa42470e1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 6 Feb 2019 21:13:49 -0800 Subject: [PATCH 1383/4277] cdrom: Fix race condition in cdrom_sysctl_register The following traceback is sometimes seen when booting an image in qemu: [ 54.608293] cdrom: Uniform CD-ROM driver Revision: 3.20 [ 54.611085] Fusion MPT base driver 3.04.20 [ 54.611877] Copyright (c) 1999-2008 LSI Corporation [ 54.616234] Fusion MPT SAS Host driver 3.04.20 [ 54.635139] sysctl duplicate entry: /dev/cdrom//info [ 54.639578] CPU: 0 PID: 266 Comm: kworker/u4:5 Not tainted 5.0.0-rc5 #1 [ 54.639578] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 [ 54.641273] Workqueue: events_unbound async_run_entry_fn [ 54.641273] Call Trace: [ 54.641273] dump_stack+0x67/0x90 [ 54.641273] __register_sysctl_table+0x50b/0x570 [ 54.641273] ? rcu_read_lock_sched_held+0x6f/0x80 [ 54.641273] ? kmem_cache_alloc_trace+0x1c7/0x1f0 [ 54.646814] __register_sysctl_paths+0x1c8/0x1f0 [ 54.646814] cdrom_sysctl_register.part.7+0xc/0x5f [ 54.646814] register_cdrom.cold.24+0x2a/0x33 [ 54.646814] sr_probe+0x4bd/0x580 [ 54.646814] ? __driver_attach+0xd0/0xd0 [ 54.646814] really_probe+0xd6/0x260 [ 54.646814] ? __driver_attach+0xd0/0xd0 [ 54.646814] driver_probe_device+0x4a/0xb0 [ 54.646814] ? __driver_attach+0xd0/0xd0 [ 54.646814] bus_for_each_drv+0x73/0xc0 [ 54.646814] __device_attach+0xd6/0x130 [ 54.646814] bus_probe_device+0x9a/0xb0 [ 54.646814] device_add+0x40c/0x670 [ 54.646814] ? __pm_runtime_resume+0x4f/0x80 [ 54.646814] scsi_sysfs_add_sdev+0x81/0x290 [ 54.646814] scsi_probe_and_add_lun+0x888/0xc00 [ 54.646814] ? scsi_autopm_get_host+0x21/0x40 [ 54.646814] __scsi_add_device+0x116/0x130 [ 54.646814] ata_scsi_scan_host+0x93/0x1c0 [ 54.646814] async_run_entry_fn+0x34/0x100 [ 54.646814] process_one_work+0x237/0x5e0 [ 54.646814] worker_thread+0x37/0x380 [ 54.646814] ? rescuer_thread+0x360/0x360 [ 54.646814] kthread+0x118/0x130 [ 54.646814] ? kthread_create_on_node+0x60/0x60 [ 54.646814] ret_from_fork+0x3a/0x50 The only sensible explanation is that cdrom_sysctl_register() is called twice, once from the module init function and once from register_cdrom(). cdrom_sysctl_register() is not mutex protected and may happily execute twice if the second call is made before the first call is complete. Use a static atomic to ensure that the function is executed exactly once. Signed-off-by: Guenter Roeck Signed-off-by: Jens Axboe --- drivers/cdrom/cdrom.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 614ecdbb4ab7..933268b8d6a5 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -265,6 +265,7 @@ /* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */ /* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */ +#include #include #include #include @@ -3692,9 +3693,9 @@ static struct ctl_table_header *cdrom_sysctl_header; static void cdrom_sysctl_register(void) { - static int initialized; + static atomic_t initialized = ATOMIC_INIT(0); - if (initialized == 1) + if (!atomic_add_unless(&initialized, 1, 1)) return; cdrom_sysctl_header = register_sysctl_table(cdrom_root_table); @@ -3705,8 +3706,6 @@ static void cdrom_sysctl_register(void) cdrom_sysctl_settings.debug = debug; cdrom_sysctl_settings.lock = lockdoor; cdrom_sysctl_settings.check = check_media_type; - - initialized = 1; } static void cdrom_sysctl_unregister(void) From b45eb4084bd918ddd62b8090150b8ac1d3aee689 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 Feb 2019 20:59:09 +0100 Subject: [PATCH 1384/4277] pinctrl: samsung: Remove legacy API for handling external wakeup interrupts mask Remove the legacy, ugly API of exposing the static value of external wakeup interrupts mask, because all arch-machine users where converted to use generic implementation from pinctrl driver. Signed-off-by: Krzysztof Kozlowski Cc: Tomasz Figa Cc: Sylwester Nawrocki Acked-by: Tomasz Figa Signed-off-by: Linus Walleij --- drivers/pinctrl/samsung/pinctrl-exynos.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index f49ea3d92aa1..ebc27b06718c 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -325,13 +325,6 @@ err_domains: return ret; } -static u32 exynos_eint_wake_mask = 0xffffffff; - -u32 exynos_get_eint_wake_mask(void) -{ - return exynos_eint_wake_mask; -} - static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) { struct irq_chip *chip = irq_data_get_irq_chip(irqd); @@ -342,10 +335,9 @@ static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq); if (!on) - exynos_eint_wake_mask |= bit; + our_chip->eint_wake_mask_value |= bit; else - exynos_eint_wake_mask &= ~bit; - our_chip->eint_wake_mask_value = exynos_eint_wake_mask; + our_chip->eint_wake_mask_value &= ~bit; return 0; } From fcce88d9ca1ae202801b75554c951a2393473afc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Nov 2018 14:38:55 +0200 Subject: [PATCH 1385/4277] gpio: crystalcove: Use for_each_set_bit() in IRQ handler This simplifies and standardizes the AB IRQ handler by using the for_each_set_bit() library function. Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/gpio/gpio-crystalcove.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 58531d8b8c6e..3c39d1b6c3ad 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -279,8 +279,8 @@ static struct irq_chip crystalcove_irqchip = { static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) { struct crystalcove_gpio *cg = data; + unsigned long pending; unsigned int p0, p1; - int pending; int gpio; unsigned int virq; @@ -293,11 +293,9 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) pending = p0 | p1 << 8; - for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) { - if (pending & BIT(gpio)) { - virq = irq_find_mapping(cg->chip.irq.domain, gpio); - handle_nested_irq(virq); - } + for_each_set_bit(gpio, &pending, CRYSTALCOVE_GPIO_NUM) { + virq = irq_find_mapping(cg->chip.irq.domain, gpio); + handle_nested_irq(virq); } return IRQ_HANDLED; From 282db906eecccb08b8f0adccd082a129ae1d95d8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 29 Jan 2019 20:37:28 +0200 Subject: [PATCH 1386/4277] gpio: wcove: Allow return negative error code from to_reg() The type of to_reg() is unsigned int while it might return a negative error code. Callers are also expecting a signed type. For sake of type consistency replace unsigned int with plain int. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-wcove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index dde7c6aecbb5..4afc8580ecd7 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -105,7 +105,7 @@ struct wcove_gpio { bool set_irq_mask; }; -static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type) +static inline int to_reg(int gpio, enum ctrl_register reg_type) { unsigned int reg; From 1147575447f988e605c182fa940b1cc4d8c59dce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 29 Jan 2019 21:01:30 +0200 Subject: [PATCH 1387/4277] gpio: msic: Remove duplicate check in IRQ handler There is no need to check for non-zero pending since for_each_set_bit() does it on the first iteration. While here, drop redundant assignment. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-msic.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 3b34dbecef99..138569f8f16e 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -237,20 +237,17 @@ static void msic_gpio_irq_handler(struct irq_desc *desc) struct msic_gpio *mg = irq_data_get_irq_handler_data(data); struct irq_chip *chip = irq_data_get_irq_chip(data); struct intel_msic *msic = pdev_to_intel_msic(mg->pdev); + unsigned long pending; int i; int bitnr; u8 pin; - unsigned long pending = 0; for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) { intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin); pending = pin; - if (pending) { - for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) - generic_handle_irq(mg->irq_base + - (i * BITS_PER_BYTE) + bitnr); - } + for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) + generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr); } chip->irq_eoi(data); } From 78207c5bfc2840206878e61a61195af26cb02a64 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Sep 2018 14:26:25 +0300 Subject: [PATCH 1388/4277] gpio: crystalcove: Sort headers alphabetically Sort header block alphabetically for easy maintenance. Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/gpio/gpio-crystalcove.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 3c39d1b6c3ad..c73e7c285636 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -15,14 +15,14 @@ * Author: Yang, Bin */ +#include +#include #include +#include #include #include -#include -#include -#include #include -#include +#include #define CRYSTALCOVE_GPIO_NUM 16 #define CRYSTALCOVE_VGPIO_NUM 95 From 2456c8b82428a366f8658a13f4befa7ecebdecc9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Sep 2018 14:26:25 +0300 Subject: [PATCH 1389/4277] gpio: msic: Sort headers alphabetically Sort header block alphabetically for easy maintenance. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-msic.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 138569f8f16e..4464a2f067ff 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -20,13 +20,13 @@ * */ -#include -#include -#include -#include #include -#include +#include +#include +#include #include +#include +#include /* the offset for the mapping of global gpio pin to irq */ #define MSIC_GPIO_IRQ_OFFSET 0x100 From 39684807d831daafd02fb1f1de215ef0c4571953 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Sep 2018 14:26:25 +0300 Subject: [PATCH 1390/4277] gpio: wcove: Sort headers alphabetically Sort header block alphabetically for easy maintenance. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-wcove.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 4afc8580ecd7..266866c08c71 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -16,10 +16,10 @@ */ #include -#include -#include #include +#include #include +#include #include #include #include From cb19c7f3ece4a5a6cbe0a557da8d52211316e01b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 29 Jan 2019 20:40:58 +0200 Subject: [PATCH 1391/4277] gpio: wcove: Fix indentation In some cases the indentation is inconsistent. Fix it accordingly. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-wcove.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 266866c08c71..6d6204ceed2c 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -26,9 +26,9 @@ /* * Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks: - * Bank 0: Pin 0 - 6 - * Bank 1: Pin 7 - 10 - * Bank 2: Pin 11 -12 + * Bank 0: Pin 0 - 6 + * Bank 1: Pin 7 - 10 + * Bank 2: Pin 11 - 12 * Each pin has one output control register and one input control register. */ #define BANK0_NR_PINS 7 @@ -75,8 +75,8 @@ #define CTLO_RVAL_50KDOWN (2 << 1) #define CTLO_RVAL_50KUP (3 << 1) -#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) -#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) +#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) +#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) enum ctrl_register { CTRL_IN, @@ -203,8 +203,7 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) return val & 0x1; } -static void wcove_gpio_set(struct gpio_chip *chip, - unsigned int gpio, int value) +static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); From e8362430208efac5c75332d89914142a2ce6ec07 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Nov 2018 14:11:42 +0200 Subject: [PATCH 1392/4277] gpio: crystalcove: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/gpio/gpio-crystalcove.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index c73e7c285636..14d1f4c933b6 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * gpio-crystalcove.c - Intel Crystal Cove GPIO Driver + * Intel Crystal Cove GPIO Driver * * Copyright (C) 2012, 2014 Intel Corporation. All rights reserved. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Author: Yang, Bin */ From 8f710b5f8905a2662ed8384159ebc4769fe4e108 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Nov 2018 14:11:42 +0200 Subject: [PATCH 1393/4277] gpio: msic: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-msic.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 4464a2f067ff..7e3c96e4ab2c 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Medfield MSIC GPIO driver> * Copyright (c) 2011, Intel Corporation. * * Author: Mathias Nyman * Based on intel_pmic_gpio.c - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include From fd30b72eab2d7a32c8e279442584e115e2e125f9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Nov 2018 14:11:42 +0200 Subject: [PATCH 1394/4277] gpio: wcove: Convert to use SPDX identifier Reduce size of duplicated comments by switching to use SPDX identifier. No functional change. Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-wcove.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 6d6204ceed2c..444fe9e7f04a 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Whiskey Cove PMIC GPIO Driver * * This driver is written based on gpio-crystalcove.c * * Copyright (C) 2016 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include From a3240f09307ac978270d423b542f229e2ccc07b8 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 7 Feb 2019 10:55:36 -0300 Subject: [PATCH 1395/4277] pinctrl: ingenic: Add LCD pins for the JZ4725B SoC Add the pins and groups for the "lcd" pin function in the JZ4725B SoC. Signed-off-by: Paul Cercueil Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-ingenic.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index 2b3f7e4708b0..bc21ceb15d68 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -233,6 +233,19 @@ static int jz4725b_pwm_pwm2_pins[] = { 0x4c, }; static int jz4725b_pwm_pwm3_pins[] = { 0x4d, }; static int jz4725b_pwm_pwm4_pins[] = { 0x4e, }; static int jz4725b_pwm_pwm5_pins[] = { 0x4f, }; +static int jz4725b_lcd_8bit_pins[] = { + 0x72, 0x73, 0x74, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, +}; +static int jz4725b_lcd_16bit_pins[] = { + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, +}; +static int jz4725b_lcd_18bit_pins[] = { 0x70, 0x71, }; +static int jz4725b_lcd_24bit_pins[] = { 0x76, 0x77, 0x78, 0x79, }; +static int jz4725b_lcd_special_pins[] = { 0x76, 0x77, 0x78, 0x79, }; +static int jz4725b_lcd_generic_pins[] = { 0x75, }; static int jz4725b_mmc0_1bit_funcs[] = { 1, 1, 1, }; static int jz4725b_mmc0_4bit_funcs[] = { 1, 0, 1, }; @@ -251,6 +264,12 @@ static int jz4725b_pwm_pwm2_funcs[] = { 0, }; static int jz4725b_pwm_pwm3_funcs[] = { 0, }; static int jz4725b_pwm_pwm4_funcs[] = { 0, }; static int jz4725b_pwm_pwm5_funcs[] = { 0, }; +static int jz4725b_lcd_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4725b_lcd_16bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static int jz4725b_lcd_18bit_funcs[] = { 0, 0, }; +static int jz4725b_lcd_24bit_funcs[] = { 1, 1, 1, 1, }; +static int jz4725b_lcd_special_funcs[] = { 0, 0, 0, 0, }; +static int jz4725b_lcd_generic_funcs[] = { 0, }; static const struct group_desc jz4725b_groups[] = { INGENIC_PIN_GROUP("mmc0-1bit", jz4725b_mmc0_1bit), @@ -270,6 +289,12 @@ static const struct group_desc jz4725b_groups[] = { INGENIC_PIN_GROUP("pwm3", jz4725b_pwm_pwm3), INGENIC_PIN_GROUP("pwm4", jz4725b_pwm_pwm4), INGENIC_PIN_GROUP("pwm5", jz4725b_pwm_pwm5), + INGENIC_PIN_GROUP("lcd-8bit", jz4725b_lcd_8bit), + INGENIC_PIN_GROUP("lcd-16bit", jz4725b_lcd_16bit), + INGENIC_PIN_GROUP("lcd-18bit", jz4725b_lcd_18bit), + INGENIC_PIN_GROUP("lcd-24bit", jz4725b_lcd_24bit), + INGENIC_PIN_GROUP("lcd-special", jz4725b_lcd_special), + INGENIC_PIN_GROUP("lcd-generic", jz4725b_lcd_generic), }; static const char *jz4725b_mmc0_groups[] = { "mmc0-1bit", "mmc0-4bit", }; @@ -285,6 +310,10 @@ static const char *jz4725b_pwm2_groups[] = { "pwm2", }; static const char *jz4725b_pwm3_groups[] = { "pwm3", }; static const char *jz4725b_pwm4_groups[] = { "pwm4", }; static const char *jz4725b_pwm5_groups[] = { "pwm5", }; +static const char *jz4725b_lcd_groups[] = { + "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-24bit", + "lcd-special", "lcd-generic", +}; static const struct function_desc jz4725b_functions[] = { { "mmc0", jz4725b_mmc0_groups, ARRAY_SIZE(jz4725b_mmc0_groups), }, @@ -297,6 +326,7 @@ static const struct function_desc jz4725b_functions[] = { { "pwm3", jz4725b_pwm3_groups, ARRAY_SIZE(jz4725b_pwm3_groups), }, { "pwm4", jz4725b_pwm4_groups, ARRAY_SIZE(jz4725b_pwm4_groups), }, { "pwm5", jz4725b_pwm5_groups, ARRAY_SIZE(jz4725b_pwm5_groups), }, + { "lcd", jz4725b_lcd_groups, ARRAY_SIZE(jz4725b_lcd_groups), }, }; static const struct ingenic_chip_info jz4725b_chip_info = { From 5766ba31992d39738b5b0b32d38f94abbe862b3e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 8 Feb 2019 19:24:44 +0100 Subject: [PATCH 1396/4277] fbdev/via: fix spelling mistake "Expandsion" -> "Expansion" Trivial fix to spelling mistake in MODULE_PARM_DESC text. Signed-off-by: Colin Ian King Cc: Florian Tobias Schandinat Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/via/viafbdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c index 7bb7e90b8f00..bdf5a0ea876d 100644 --- a/drivers/video/fbdev/via/viafbdev.c +++ b/drivers/video/fbdev/via/viafbdev.c @@ -2110,7 +2110,7 @@ MODULE_PARM_DESC(viafb_lcd_panel_id, module_param(viafb_lcd_dsp_method, int, S_IRUSR); MODULE_PARM_DESC(viafb_lcd_dsp_method, - "Set Flat Panel display scaling method.(Default=Expandsion)"); + "Set Flat Panel display scaling method.(Default=Expansion)"); module_param(viafb_SAMM_ON, int, S_IRUSR); MODULE_PARM_DESC(viafb_SAMM_ON, From 8e71fa5e4d86bedfd26df85381d65d6b4c860020 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 8 Feb 2019 19:24:45 +0100 Subject: [PATCH 1397/4277] fbdev: chipsfb: remove set but not used variable 'size' Fixes gcc '-Wunused-but-set-variable' warning: drivers/video/fbdev/chipsfb.c: In function 'chipsfb_pci_init': drivers/video/fbdev/chipsfb.c:352:22: warning: variable 'size' set but not used [-Wunused-but-set-variable] Fixes: 8c8709334cec ("[PATCH] ppc32: Remove CONFIG_PMAC_PBOOK"). Signed-off-by: YueHaibing Acked-by: Michael Ellerman Cc: Daniel Vetter Cc: Christophe Leroy [b.zolnierkie: minor commit summary and description fixups] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/chipsfb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c index 40182ed85648..ca549e1532e6 100644 --- a/drivers/video/fbdev/chipsfb.c +++ b/drivers/video/fbdev/chipsfb.c @@ -349,7 +349,7 @@ static void init_chips(struct fb_info *p, unsigned long addr) static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { struct fb_info *p; - unsigned long addr, size; + unsigned long addr; unsigned short cmd; int rc = -ENODEV; @@ -361,7 +361,6 @@ static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) goto err_disable; addr = pci_resource_start(dp, 0); - size = pci_resource_len(dp, 0); if (addr == 0) goto err_disable; From 23cd78e28a7b17528f44933edb93e93ba082138c Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Fri, 8 Feb 2019 19:24:45 +0100 Subject: [PATCH 1398/4277] fbcon: use kvmalloc() for scrollback buffer Scrollback frame buffer is rather big - 32K, so it requires 3rd order page, so let's use kvmalloc() instead of ordinary kmalloc() for it. Signed-off-by: Konstantin Khorenko Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/core/fbcon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index bfa1360ec750..fd430e6b4c63 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -999,7 +999,7 @@ static const char *fbcon_startup(void) if (!softback_buf) { softback_buf = (unsigned long) - kmalloc(fbcon_softback_size, + kvmalloc(fbcon_softback_size, GFP_KERNEL); if (!softback_buf) { fbcon_softback_size = 0; @@ -1008,7 +1008,7 @@ static const char *fbcon_startup(void) } } else { if (softback_buf) { - kfree((void *) softback_buf); + kvfree((void *) softback_buf); softback_buf = 0; softback_top = 0; } @@ -3672,7 +3672,7 @@ static void fbcon_exit(void) } #endif - kfree((void *)softback_buf); + kvfree((void *)softback_buf); softback_buf = 0UL; for_each_registered_fb(i) { From a6f13af4d115a1afb97a9215c491f0bbc5362af7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 8 Feb 2019 19:24:45 +0100 Subject: [PATCH 1399/4277] fbdev: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. For instances using of_node_cmp, this has the side effect of now using case sensitive comparisons. This should not matter for any FDT based system which omap is. Signed-off-by: Rob Herring Cc: Benjamin Herrenschmidt Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/aty/radeon_pm.c | 6 +++--- drivers/video/fbdev/cg14.c | 4 ++-- drivers/video/fbdev/cg3.c | 2 +- drivers/video/fbdev/ffb.c | 2 +- drivers/video/fbdev/imsttfb.c | 4 ++-- drivers/video/fbdev/offb.c | 2 +- drivers/video/fbdev/omap2/omapfb/dss/dss-of.c | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c index e695adb0e573..2dc5703eac51 100644 --- a/drivers/video/fbdev/aty/radeon_pm.c +++ b/drivers/video/fbdev/aty/radeon_pm.c @@ -2844,8 +2844,8 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis * in some desktop G4s), Via (M9+ chip on iBook G4) and * Snowy (M11 chip on iBook G4 manufactured after July 2005) */ - if (!strcmp(rinfo->of_node->name, "ATY,JasperParent") || - !strcmp(rinfo->of_node->name, "ATY,SnowyParent")) { + if (of_node_name_eq(rinfo->of_node, "ATY,JasperParent") || + of_node_name_eq(rinfo->of_node, "ATY,SnowyParent")) { rinfo->reinit_func = radeon_reinitialize_M10; rinfo->pm_mode |= radeon_pm_off; } @@ -2855,7 +2855,7 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis rinfo->pm_mode |= radeon_pm_off; } #endif - if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) { + if (of_node_name_eq(rinfo->of_node, "ATY,ViaParent")) { rinfo->reinit_func = radeon_reinitialize_M9P; rinfo->pm_mode |= radeon_pm_off; } diff --git a/drivers/video/fbdev/cg14.c b/drivers/video/fbdev/cg14.c index 9af54c2368fd..a6dce1a78490 100644 --- a/drivers/video/fbdev/cg14.c +++ b/drivers/video/fbdev/cg14.c @@ -486,8 +486,8 @@ static int cg14_probe(struct platform_device *op) info->var.xres); info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); - if (!strcmp(dp->parent->name, "sbus") || - !strcmp(dp->parent->name, "sbi")) { + if (of_node_name_eq(dp->parent, "sbus") || + of_node_name_eq(dp->parent, "sbi")) { info->fix.smem_start = op->resource[0].start; par->iospace = op->resource[0].flags & IORESOURCE_BITS; } else { diff --git a/drivers/video/fbdev/cg3.c b/drivers/video/fbdev/cg3.c index 1bd95b02f3aa..6d42def8436b 100644 --- a/drivers/video/fbdev/cg3.c +++ b/drivers/video/fbdev/cg3.c @@ -369,7 +369,7 @@ static int cg3_probe(struct platform_device *op) info->var.red.length = 8; info->var.green.length = 8; info->var.blue.length = 8; - if (!strcmp(dp->name, "cgRDI")) + if (of_node_name_eq(dp, "cgRDI")) par->flags |= CG3_FLAG_RDI; if (par->flags & CG3_FLAG_RDI) cg3_rdi_maybe_fixup_var(&info->var, dp); diff --git a/drivers/video/fbdev/ffb.c b/drivers/video/fbdev/ffb.c index 6b1915872af1..b7aee0c427a8 100644 --- a/drivers/video/fbdev/ffb.c +++ b/drivers/video/fbdev/ffb.c @@ -944,7 +944,7 @@ static int ffb_probe(struct platform_device *op) info->var.accel_flags = FB_ACCELF_TEXT; - if (!strcmp(dp->name, "SUNW,afb")) + if (of_node_name_eq(dp, "SUNW,afb")) par->flags |= FFB_FLAG_AFB; par->board_type = of_getintprop_default(dp, "board_type", 0); diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c index 901ca4ed10e9..b268570b019d 100644 --- a/drivers/video/fbdev/imsttfb.c +++ b/drivers/video/fbdev/imsttfb.c @@ -1498,8 +1498,8 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) switch (pdev->device) { case PCI_DEVICE_ID_IMS_TT128: /* IMS,tt128mbA */ par->ramdac = IBM; - if (dp && ((strcmp(dp->name, "IMS,tt128mb8") == 0) || - (strcmp(dp->name, "IMS,tt128mb8A") == 0))) + if (of_node_name_eq(dp, "IMS,tt128mb8") || + of_node_name_eq(dp, "IMS,tt128mb8A")) par->ramdac = TVP; break; case PCI_DEVICE_ID_IMS_TT3D: /* IMS,tt3d */ diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index 057d3cdef92e..8f62e174990b 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -646,7 +646,7 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) } #endif /* kludge for valkyrie */ - if (strcmp(dp->name, "valkyrie") == 0) + if (of_node_name_eq(dp, "valkyrie")) address += 0x1000; offb_init_fb(no_real_node ? "bootx" : NULL, width, height, depth, pitch, address, diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c index f1eb8b0f8a2a..5ce893c1923d 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss-of.c @@ -60,7 +60,7 @@ omapdss_of_get_next_port(const struct device_node *parent, return NULL; } prev = port; - } while (of_node_cmp(port->name, "port") != 0); + } while (!of_node_name_eq(port, "port")); of_node_put(ports); } @@ -83,7 +83,7 @@ omapdss_of_get_next_endpoint(const struct device_node *parent, if (!ep) return NULL; prev = ep; - } while (of_node_cmp(ep->name, "endpoint") != 0); + } while (!of_node_name_eq(ep, "endpoint")); return ep; } From f1fbbf5cfaa8f5e651c85a9c504ed65e96b1b51b Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Fri, 8 Feb 2019 19:24:46 +0100 Subject: [PATCH 1400/4277] omapfb: fix typo Fix spelling mistake: "lenght" -> "length" Signed-off-by: Matteo Croce Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c index fa72e735dad2..d146793dd044 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c @@ -712,7 +712,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, else acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; /* - * The I2S input word length is twice the lenght given in the IEC-60958 + * The I2S input word length is twice the length given in the IEC-60958 * status word. If the word size is greater than * 20 bits, increment by one. */ From 89ef5c6a7584f9d7f8f11def67d2e8a2312ec9fe Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Fri, 8 Feb 2019 19:24:46 +0100 Subject: [PATCH 1401/4277] video: offb: annotate implicit fall throughs There is a plan to build the kernel with -Wimplicit-fallthrough and these places in the code produced warnings (W=1). Fix them up. This commit remove the following warnings: drivers/video/fbdev/offb.c:211:5: warning: this statement may fall through [-Wimplicit-fallthrough=] drivers/video/fbdev/offb.c:142:3: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Mathieu Malaterre Acked-by: Gustavo A. R. Silva Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/offb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index 8f62e174990b..fbc6eafb63c7 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -141,6 +141,7 @@ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ out_le32(par->cmap_adr + 0x58, in_le32(par->cmap_adr + 0x58) & ~0x20); + /* fall through */ case cmap_r128: /* Set palette index & data */ out_8(par->cmap_adr + 0xb0, regno); @@ -210,6 +211,7 @@ static int offb_blank(int blank, struct fb_info *info) /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ out_le32(par->cmap_adr + 0x58, in_le32(par->cmap_adr + 0x58) & ~0x20); + /* fall through */ case cmap_r128: /* Set palette index & data */ out_8(par->cmap_adr + 0xb0, i); From 1ea673ade7448db0f6f2fe423e72b84e33f653fe Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Fri, 8 Feb 2019 19:24:46 +0100 Subject: [PATCH 1402/4277] video: fbdev: geode: remove ifdef OLPC noise provides olpc_has_dcon() stub for CONFIG_OLPC=n, compiler should just optimize the unneeded bits away. Signed-off-by: Lubomir Rintel Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/geode/gxfb_core.c | 13 ++----------- drivers/video/fbdev/geode/lxfb_core.c | 13 ++----------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c index f4f76373b2a8..b1906cf5a8f0 100644 --- a/drivers/video/fbdev/geode/gxfb_core.c +++ b/drivers/video/fbdev/geode/gxfb_core.c @@ -33,6 +33,8 @@ #include #include +#include + #include "gxfb.h" static char *mode_option; @@ -107,9 +109,6 @@ static struct fb_videomode gx_modedb[] = { FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, }; -#ifdef CONFIG_OLPC -#include - static struct fb_videomode gx_dcon_modedb[] = { /* The only mode the DCON has is 1200x900 */ { NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3, @@ -128,14 +127,6 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size) } } -#else -static void get_modedb(struct fb_videomode **modedb, unsigned int *size) -{ - *modedb = (struct fb_videomode *) gx_modedb; - *size = ARRAY_SIZE(gx_modedb); -} -#endif - static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { if (var->xres > 1600 || var->yres > 1200) diff --git a/drivers/video/fbdev/geode/lxfb_core.c b/drivers/video/fbdev/geode/lxfb_core.c index 138da6cb6cbc..17ab905811b1 100644 --- a/drivers/video/fbdev/geode/lxfb_core.c +++ b/drivers/video/fbdev/geode/lxfb_core.c @@ -23,6 +23,8 @@ #include #include +#include + #include "lxfb.h" static char *mode_option; @@ -216,9 +218,6 @@ static struct fb_videomode geode_modedb[] = { 0, FB_VMODE_NONINTERLACED, 0 }, }; -#ifdef CONFIG_OLPC -#include - static struct fb_videomode olpc_dcon_modedb[] = { /* The only mode the DCON has is 1200x900 */ { NULL, 50, 1200, 900, 17460, 24, 8, 4, 5, 8, 3, @@ -237,14 +236,6 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size) } } -#else -static void get_modedb(struct fb_videomode **modedb, unsigned int *size) -{ - *modedb = (struct fb_videomode *) geode_modedb; - *size = ARRAY_SIZE(geode_modedb); -} -#endif - static int lxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { if (var->xres > 1920 || var->yres > 1440) From 60d2fa0dad0687b4d227aacc042ccdbea1782d87 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 8 Feb 2019 19:24:47 +0100 Subject: [PATCH 1403/4277] fbdev: omap2: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Signed-off-by: Greg Kroah-Hartman Acked-by: Tony Lindgren Cc: Mauro Carvalho Chehab Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/omap2/omapfb/dss/core.c | 31 ++++----------------- drivers/video/fbdev/omap2/omapfb/dss/dss.h | 2 +- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index b4bcf3a4a647..7e6a3eb266d0 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -110,19 +110,12 @@ DEFINE_SHOW_ATTRIBUTE(dss); static struct dentry *dss_debugfs_dir; -static int dss_initialize_debugfs(void) +static void dss_initialize_debugfs(void) { dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); - if (IS_ERR(dss_debugfs_dir)) { - int err = PTR_ERR(dss_debugfs_dir); - dss_debugfs_dir = NULL; - return err; - } debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, &dss_debug_dump_clocks, &dss_fops); - - return 0; } static void dss_uninitialize_debugfs(void) @@ -130,24 +123,18 @@ static void dss_uninitialize_debugfs(void) debugfs_remove_recursive(dss_debugfs_dir); } -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) { - struct dentry *d; - - d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, - write, &dss_fops); - - return PTR_ERR_OR_ZERO(d); + debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, write, &dss_fops); } #else /* CONFIG_FB_OMAP2_DSS_DEBUGFS */ -static inline int dss_initialize_debugfs(void) +static inline void dss_initialize_debugfs(void) { - return 0; } static inline void dss_uninitialize_debugfs(void) { } -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) { return 0; } @@ -188,9 +175,7 @@ static int __init omap_dss_probe(struct platform_device *pdev) dss_features_init(omapdss_get_version()); - r = dss_initialize_debugfs(); - if (r) - goto err_debugfs; + dss_initialize_debugfs(); if (def_disp_name) core.default_display_name = def_disp_name; @@ -198,10 +183,6 @@ static int __init omap_dss_probe(struct platform_device *pdev) register_pm_notifier(&omap_dss_pm_notif_block); return 0; - -err_debugfs: - - return r; } static int omap_dss_remove(struct platform_device *pdev) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.h b/drivers/video/fbdev/omap2/omapfb/dss/dss.h index a3cc0ca8f9d2..b1a354494144 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.h @@ -214,7 +214,7 @@ struct platform_device *dss_get_core_pdev(void); int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); int dss_set_min_bus_tput(struct device *dev, unsigned long tput); -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); +void dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); /* display */ int dss_suspend_all_devices(void); From 72aed9e31344a62e6cab2c49a27fd0bc740add63 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 8 Feb 2019 19:24:47 +0100 Subject: [PATCH 1404/4277] fbdev: mbx: fix up debugfs file creation There is no need to keep the dentries around for the individual debugfs files, just delete the whole directory all at once at shutdown instead. This also fixes a tiny memory leak where the memory for the pointers to the file dentries was never freed when the device shut down, as well as making the logic of the code a lot simpler. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/mbx/mbxdebugfs.c | 34 ++++++++-------------------- drivers/video/fbdev/mbx/mbxfb.c | 2 +- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/drivers/video/fbdev/mbx/mbxdebugfs.c b/drivers/video/fbdev/mbx/mbxdebugfs.c index 2bd328883178..52cfe0132b25 100644 --- a/drivers/video/fbdev/mbx/mbxdebugfs.c +++ b/drivers/video/fbdev/mbx/mbxdebugfs.c @@ -211,36 +211,22 @@ static const struct file_operations misc_fops = { static void mbxfb_debugfs_init(struct fb_info *fbi) { struct mbxfb_info *mfbi = fbi->par; - struct mbxfb_debugfs_data *dbg; + struct dentry *dir; - dbg = kzalloc(sizeof(struct mbxfb_debugfs_data), GFP_KERNEL); - mfbi->debugfs_data = dbg; + dir = debugfs_create_dir("mbxfb", NULL); + mbfi->debugfs_dir = dir; - dbg->dir = debugfs_create_dir("mbxfb", NULL); - dbg->sysconf = debugfs_create_file("sysconf", 0444, dbg->dir, - fbi, &sysconf_fops); - dbg->clock = debugfs_create_file("clock", 0444, dbg->dir, - fbi, &clock_fops); - dbg->display = debugfs_create_file("display", 0444, dbg->dir, - fbi, &display_fops); - dbg->gsctl = debugfs_create_file("gsctl", 0444, dbg->dir, - fbi, &gsctl_fops); - dbg->sdram = debugfs_create_file("sdram", 0444, dbg->dir, - fbi, &sdram_fops); - dbg->misc = debugfs_create_file("misc", 0444, dbg->dir, - fbi, &misc_fops); + debugfs_create_file("sysconf", 0444, dir, fbi, &sysconf_fops); + debugfs_create_file("clock", 0444, dir, fbi, &clock_fops); + debugfs_create_file("display", 0444, dir, fbi, &display_fops); + debugfs_create_file("gsctl", 0444, dir, fbi, &gsctl_fops); + debugfs_create_file("sdram", 0444, dir, fbi, &sdram_fops); + debugfs_create_file("misc", 0444, dir, fbi, &misc_fops); } static void mbxfb_debugfs_remove(struct fb_info *fbi) { struct mbxfb_info *mfbi = fbi->par; - struct mbxfb_debugfs_data *dbg = mfbi->debugfs_data; - debugfs_remove(dbg->misc); - debugfs_remove(dbg->sdram); - debugfs_remove(dbg->gsctl); - debugfs_remove(dbg->display); - debugfs_remove(dbg->clock); - debugfs_remove(dbg->sysconf); - debugfs_remove(dbg->dir); + debugfs_remove_recursive(mfbi->debugfs_dir); } diff --git a/drivers/video/fbdev/mbx/mbxfb.c b/drivers/video/fbdev/mbx/mbxfb.c index 539b85da0897..6ded480a69b4 100644 --- a/drivers/video/fbdev/mbx/mbxfb.c +++ b/drivers/video/fbdev/mbx/mbxfb.c @@ -74,7 +74,7 @@ struct mbxfb_info { u32 pseudo_palette[MAX_PALETTES]; #ifdef CONFIG_FB_MBX_DEBUG - void *debugfs_data; + struct dentry *debugfs_dir; #endif }; From a41458d74c241f2c39ee23fef88df166c3c26a85 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 8 Feb 2019 19:24:47 +0100 Subject: [PATCH 1405/4277] video/fbdev: refactor video= cmdline parsing Make the video_setup() function slightly easier to read by removing the repeated checks for !global. Remove the misleading return value comment while at it. I'm slightly hesitant to change any of this, but here goes anyway, with hopes that the next person to have to look at this has it a wee bit easier. Signed-off-by: Jani Nikula Reviewed-by: Daniel Vetter Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/core/fb_cmdline.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/video/fbdev/core/fb_cmdline.c b/drivers/video/fbdev/core/fb_cmdline.c index 39509ccd92f1..3b5bd666b952 100644 --- a/drivers/video/fbdev/core/fb_cmdline.c +++ b/drivers/video/fbdev/core/fb_cmdline.c @@ -75,36 +75,33 @@ EXPORT_SYMBOL(fb_get_options); * NOTE: This function is a __setup and __init function. * It only stores the options. Drivers have to call * fb_get_options() as necessary. - * - * Returns zero. - * */ static int __init video_setup(char *options) { - int i, global = 0; - if (!options || !*options) - global = 1; + goto out; - if (!global && !strncmp(options, "ofonly", 6)) { + if (!strncmp(options, "ofonly", 6)) { ofonly = 1; - global = 1; + goto out; } - if (!global && !strchr(options, ':')) { - fb_mode_option = options; - global = 1; - } + if (strchr(options, ':')) { + /* named */ + int i; - if (!global) { for (i = 0; i < FB_MAX; i++) { if (video_options[i] == NULL) { video_options[i] = options; break; } } + } else { + /* global */ + fb_mode_option = options; } +out: return 1; } __setup("video=", video_setup); From a5399db139cb3ad9b8502d8b1bd02da9ce0b9df0 Mon Sep 17 00:00:00 2001 From: Manfred Schlaegl Date: Fri, 8 Feb 2019 19:24:47 +0100 Subject: [PATCH 1406/4277] fbdev: fbmem: fix memory access if logo is bigger than the screen There is no clipping on the x or y axis for logos larger that the framebuffer size. Therefore: a logo bigger than screen size leads to invalid memory access: [ 1.254664] Backtrace: [ 1.254728] [] (cfb_imageblit) from [] (fb_show_logo+0x620/0x684) [ 1.254763] r10:00000003 r9:00027fd8 r8:c6a40000 r7:c6a36e50 r6:00000000 r5:c06b81e4 [ 1.254774] r4:c6a3e800 [ 1.254810] [] (fb_show_logo) from [] (fbcon_switch+0x3fc/0x46c) [ 1.254842] r10:c6a3e824 r9:c6a3e800 r8:00000000 r7:c6a0c000 r6:c070b014 r5:c6a3e800 [ 1.254852] r4:c6808c00 [ 1.254889] [] (fbcon_switch) from [] (redraw_screen+0xf0/0x1e8) [ 1.254918] r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:c070d5a0 r5:00000080 [ 1.254928] r4:c6808c00 [ 1.254961] [] (redraw_screen) from [] (do_bind_con_driver+0x194/0x2e4) [ 1.254991] r9:00000000 r8:00000000 r7:00000014 r6:c070d5a0 r5:c070d5a0 r4:c070d5a0 So prevent displaying a logo bigger than screen size and avoid invalid memory access. Signed-off-by: Manfred Schlaegl Signed-off-by: Martin Kepplinger Cc: Daniel Vetter Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/core/fbmem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index cb43a2258c51..4721491e6c8c 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -431,6 +431,9 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, { unsigned int x; + if (image->width > info->var.xres || image->height > info->var.yres) + return; + if (rotate == FB_ROTATE_UR) { for (x = 0; x < num && image->dx + image->width <= info->var.xres; From 7d7e58d30e046d3471295a256d15e0b6bdd502c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vok=C3=A1=C4=8D?= Date: Fri, 8 Feb 2019 19:24:48 +0100 Subject: [PATCH 1407/4277] dt-bindings: display: ssd1307fb: Remove reset-active-low from examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reset-active-low property has been removed brom the binding a while ago. So remove it from the examples as well. Fixes: 519b4db ("fbdev: ssd1307fb: Remove reset-active-low from the DT binding document") Reviewed-by: Rob Herring Reviewed-by: Alexandre Belloni Cc: Shawn Guo Cc: Fabio Estevam Cc: Maxime Ripard , Signed-off-by: Michal Vokáč Signed-off-by: Bartlomiej Zolnierkiewicz --- Documentation/devicetree/bindings/display/ssd1307fb.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/ssd1307fb.txt b/Documentation/devicetree/bindings/display/ssd1307fb.txt index 209d931ef16c..b67f8caa212c 100644 --- a/Documentation/devicetree/bindings/display/ssd1307fb.txt +++ b/Documentation/devicetree/bindings/display/ssd1307fb.txt @@ -36,7 +36,6 @@ ssd1307: oled@3c { reg = <0x3c>; pwms = <&pwm 4 3000>; reset-gpios = <&gpio2 7>; - reset-active-low; }; ssd1306: oled@3c { @@ -44,7 +43,6 @@ ssd1306: oled@3c { reg = <0x3c>; pwms = <&pwm 4 3000>; reset-gpios = <&gpio2 7>; - reset-active-low; solomon,com-lrremap; solomon,com-invdir; solomon,com-offset = <32>; From af4b3a71a5c8204d00c1124c9297a0c48a53ec65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vok=C3=A1=C4=8D?= Date: Fri, 8 Feb 2019 19:24:48 +0100 Subject: [PATCH 1408/4277] video: ssd1307fb: Do not hard code active-low reset sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SSD130x OLED display reset signal is active low. Now the reset sequence is implemented in such a way that users are forced to define reset-gpios as GPIO_ACTIVE_HIGH in DT to make the reset work. Do not hard code the active-low sequence into the driver but instead allow the user to specify the gpio as GPIO_ACTIVE_LOW to reflect the real world. Reviewed-by: Rob Herring Reviewed-by: Alexandre Belloni Cc: Shawn Guo Cc: Fabio Estevam Cc: Maxime Ripard , Signed-off-by: Michal Vokáč Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/ssd1307fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 4061a20cfe24..3b361bc9feb8 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -667,10 +667,10 @@ static int ssd1307fb_probe(struct i2c_client *client, if (par->reset) { /* Reset the screen */ - gpiod_set_value_cansleep(par->reset, 0); - udelay(4); gpiod_set_value_cansleep(par->reset, 1); udelay(4); + gpiod_set_value_cansleep(par->reset, 0); + udelay(4); } if (par->vbat_reg) { From 1ac1d4845c013af9ca003adda7dfedb25a8e42ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vok=C3=A1=C4=8D?= Date: Fri, 8 Feb 2019 19:24:48 +0100 Subject: [PATCH 1409/4277] ARM: dts: imx28-cfa10036: Fix the reset gpio signal polarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reset signal of the SSD1306 OLED display is actually active-low. Adapt the DT to reflect the real world. Reviewed-by: Rob Herring Acked-by: Alexandre Belloni Cc: Shawn Guo Cc: Fabio Estevam Cc: Maxime Ripard , Signed-off-by: Michal Vokáč Signed-off-by: Bartlomiej Zolnierkiewicz --- arch/arm/boot/dts/imx28-cfa10036.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts index d3e3622979c5..de48b5808ef6 100644 --- a/arch/arm/boot/dts/imx28-cfa10036.dts +++ b/arch/arm/boot/dts/imx28-cfa10036.dts @@ -11,6 +11,7 @@ /dts-v1/; #include "imx28.dtsi" +#include / { model = "Crystalfontz CFA-10036 Board"; @@ -96,7 +97,7 @@ pinctrl-names = "default"; pinctrl-0 = <&ssd1306_cfa10036>; reg = <0x3c>; - reset-gpios = <&gpio2 7 0>; + reset-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>; solomon,height = <32>; solomon,width = <128>; solomon,page-offset = <0>; From a1939185c7a907f91b40da259a610ce2e2da9e18 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Fri, 8 Feb 2019 19:24:49 +0100 Subject: [PATCH 1410/4277] printk: Export console_printk The fbcon can be built as a module and requires console_printk. Export console_printk. Signed-off-by: Prarit Bhargava Reviewed-by: Sergey Senozhatsky Acked-by: Petr Mladek Signed-off-by: Bartlomiej Zolnierkiewicz --- kernel/printk/printk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index d3d170374ceb..8201019d1fff 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -65,6 +65,7 @@ int console_printk[4] = { CONSOLE_LOGLEVEL_MIN, /* minimum_console_loglevel */ CONSOLE_LOGLEVEL_DEFAULT, /* default_console_loglevel */ }; +EXPORT_SYMBOL_GPL(console_printk); atomic_t ignore_console_lock_warning __read_mostly = ATOMIC_INIT(0); EXPORT_SYMBOL(ignore_console_lock_warning); From 10993504d647356196a04b3022d645ec92e00159 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Fri, 8 Feb 2019 19:24:49 +0100 Subject: [PATCH 1411/4277] fbcon: Silence fbcon logo on 'quiet' boots On text-based systems the 'quiet' boot option will show printk levels higher than CONSOLE_LOGLEVEL_QUIET. The displaying of the Tux logo during boot can cause some consoles to lose display data and as a result confuse the end user. Do not display the Tux logo on systems that are in 'quiet' boot. v2: It helps to commit all my changes before sending them. Remove extra bracket. v3: buildbot error fix: fbcon can be built as part of a module so export console_printk v4: move console_printk change to separate patch, and drop logo cleanup v5: Only set FBCON_LOGO_DONTSHOW for console loglevel Signed-off-by: Prarit Bhargava Cc: Hans de Goede Cc: Marko Myllynen Cc: Steven Rostedt (VMware) Cc: Kees Cook Cc: Daniel Vetter Cc: Thierry Reding Cc: Yisheng Xie Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/core/fbcon.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index fd430e6b4c63..cd059a801662 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -656,11 +656,14 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, kfree(save); } + if (logo_shown == FBCON_LOGO_DONTSHOW) + return; + if (logo_lines > vc->vc_bottom) { logo_shown = FBCON_LOGO_CANSHOW; printk(KERN_INFO "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); - } else if (logo_shown != FBCON_LOGO_DONTSHOW) { + } else { logo_shown = FBCON_LOGO_DRAW; vc->vc_top = logo_lines; } @@ -1066,6 +1069,9 @@ static void fbcon_init(struct vc_data *vc, int init) cap = info->flags; + if (console_loglevel <= CONSOLE_LOGLEVEL_QUIET) + logo_shown = FBCON_LOGO_DONTSHOW; + if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || (info->fix.type == FB_TYPE_TEXT)) logo = 0; From f40298444e8ca13789dc002ffb269c343aadfb87 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 8 Feb 2019 19:24:49 +0100 Subject: [PATCH 1412/4277] video: fbdev: Fix potential NULL pointer dereference There is a potential NULL pointer dereference in case fb_create_modedb() fails and returns NULL. Signed-off-by: YueHaibing Cc: Kees Cook Cc: Rob Herring Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/core/fbmon.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index dd3128990776..3558a70a6664 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -978,6 +978,8 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) get_monspecs(edid, specs); specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs); + if (!specs->modedb) + return; /* * Workaround for buggy EDIDs that sets that the first From ddd065e423c1bad6c573297455ef0a9755ef12d4 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Fri, 8 Feb 2019 09:54:38 -0700 Subject: [PATCH 1413/4277] genirq/msi: Clean up usage of __u8/__u16 types The double underscore types are meant for compatibility in userspace headers which does not apply here. Therefore, change to use the standard no-underscore types. The origin of the double underscore types dates back to before the git era so I was not able to find a commit to see the original justification. Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- include/linux/msi.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/msi.h b/include/linux/msi.h index 784fb52b9900..7e9b81c3b50d 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -83,12 +83,12 @@ struct msi_desc { struct { u32 masked; struct { - __u8 is_msix : 1; - __u8 multiple : 3; - __u8 multi_cap : 3; - __u8 maskbit : 1; - __u8 is_64 : 1; - __u16 entry_nr; + u8 is_msix : 1; + u8 multiple : 3; + u8 multi_cap : 3; + u8 maskbit : 1; + u8 is_64 : 1; + u16 entry_nr; unsigned default_irq; } msi_attrib; union { From 20a796a969efe37c2dd38b72538407e544f41de4 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Fri, 8 Feb 2019 09:54:39 -0700 Subject: [PATCH 1414/4277] PCI: Clean up usage of __u32 type The double underscore types are meant for compatibility in userspace headers which does not apply here. Therefore, change to use the standard no-underscore types. The origin of the double underscore types dates back to before the git era so I was not able to find a commit to see the original justification. Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 79b1610a8beb..71853befd435 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -100,7 +100,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, { struct pci_driver *pdrv = to_pci_driver(driver); const struct pci_device_id *ids = pdrv->id_table; - __u32 vendor, device, subvendor = PCI_ANY_ID, + u32 vendor, device, subvendor = PCI_ANY_ID, subdevice = PCI_ANY_ID, class = 0, class_mask = 0; unsigned long driver_data = 0; int fields = 0; @@ -168,7 +168,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, { struct pci_dynid *dynid, *n; struct pci_driver *pdrv = to_pci_driver(driver); - __u32 vendor, device, subvendor = PCI_ANY_ID, + u32 vendor, device, subvendor = PCI_ANY_ID, subdevice = PCI_ANY_ID, class = 0, class_mask = 0; int fields = 0; size_t retval = -ENODEV; From e5fa81408fb43ebabde65938ef8b20ae879017e7 Mon Sep 17 00:00:00 2001 From: Aleksei Zakharov Date: Fri, 8 Feb 2019 19:14:05 +0300 Subject: [PATCH 1415/4277] block: avoid setting nr_requests to current value There's no reason to freeze queue and set nr_requests value if current value is the same. Signed-off-by: Aleksei Zakharov Signed-off-by: Jens Axboe --- block/blk-mq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/blk-mq.c b/block/blk-mq.c index 8a825aebc6b5..44d471ff8754 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -3089,6 +3089,9 @@ int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr) if (!set) return -EINVAL; + if (q->nr_requests == nr) + return 0; + blk_mq_freeze_queue(q); blk_mq_quiesce_queue(q); From 3d7a850fdc1a2e4d2adbc95cc0fc962974725e88 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 4 Feb 2019 15:59:43 +0200 Subject: [PATCH 1416/4277] tpm/tpm_crb: Avoid unaligned reads in crb_recv() The current approach to read first 6 bytes from the response and then tail of the response, can cause the 2nd memcpy_fromio() to do an unaligned read (e.g. read 32-bit word from address aligned to a 16-bits), depending on how memcpy_fromio() is implemented. If this happens, the read will fail and the memory controller will fill the read with 1's. This was triggered by 170d13ca3a2f, which should be probably refined to check and react to the address alignment. Before that commit, on x86 memcpy_fromio() turned out to be memcpy(). By a luck GCC has done the right thing (from tpm_crb's perspective) for us so far, but we should not rely on that. Thus, it makes sense to fix this also in tpm_crb, not least because the fix can be then backported to stable kernels and make them more robust when compiled in differing environments. Cc: stable@vger.kernel.org Cc: James Morris Cc: Tomas Winkler Cc: Jerry Snitselaar Fixes: 30fc8d138e91 ("tpm: TPM 2.0 CRB Interface") Signed-off-by: Jarkko Sakkinen Reviewed-by: Jerry Snitselaar Acked-by: Tomas Winkler --- drivers/char/tpm/tpm_crb.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 36952ef98f90..763fc7e6c005 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -287,19 +287,29 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) struct crb_priv *priv = dev_get_drvdata(&chip->dev); unsigned int expected; - /* sanity check */ - if (count < 6) + /* A sanity check that the upper layer wants to get at least the header + * as that is the minimum size for any TPM response. + */ + if (count < TPM_HEADER_SIZE) return -EIO; + /* If this bit is set, according to the spec, the TPM is in + * unrecoverable condition. + */ if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR) return -EIO; - memcpy_fromio(buf, priv->rsp, 6); - expected = be32_to_cpup((__be32 *) &buf[2]); - if (expected > count || expected < 6) + /* Read the first 8 bytes in order to get the length of the response. + * We read exactly a quad word in order to make sure that the remaining + * reads will be aligned. + */ + memcpy_fromio(buf, priv->rsp, 8); + + expected = be32_to_cpup((__be32 *)&buf[2]); + if (expected > count || expected < TPM_HEADER_SIZE) return -EIO; - memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6); + memcpy_fromio(&buf[8], &priv->rsp[8], expected - 8); return expected; } From 8ab547a2dcfac6ec184a5e036e1093eb3f7a215c Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 5 Feb 2019 11:14:00 +0200 Subject: [PATCH 1417/4277] tpm: Fix some name collisions with drivers/char/tpm.h * Rename TPM_BUFSIZE defined in drivers/char/tpm/st33zp24/st33zp24.h to ST33ZP24_BUFSIZE. * Rename TPM_BUFSIZE defined in drivers/char/tpm/tpm_i2c_infineon.c to TPM_I2C_INFINEON_BUFSIZE. * Rename TPM_RETRY in tpm_i2c_nuvoton to TPM_I2C_RETRIES. * Remove TPM_HEADER_SIZE from tpm_i2c_nuvoton. Cc: stable@vger.kernel.org Fixes: bf38b8710892 ("tpm/tpm_i2c_stm_st33: Split tpm_i2c_tpm_st33 in 2 layers (core + phy)") Fixes: aad628c1d91a ("char/tpm: Add new driver for Infineon I2C TIS TPM") Fixes: 32d33b29ba07 ("TPM: Retry SaveState command in suspend path") Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/st33zp24/i2c.c | 2 +- drivers/char/tpm/st33zp24/spi.c | 2 +- drivers/char/tpm/st33zp24/st33zp24.h | 4 ++-- drivers/char/tpm/tpm_i2c_infineon.c | 15 ++++++++------- drivers/char/tpm/tpm_i2c_nuvoton.c | 16 +++++++--------- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index be5d1abd3e8e..8390c5b54c3b 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -33,7 +33,7 @@ struct st33zp24_i2c_phy { struct i2c_client *client; - u8 buf[TPM_BUFSIZE + 1]; + u8 buf[ST33ZP24_BUFSIZE + 1]; int io_lpcpd; }; diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index d7909ab287a8..ff019a1e3c68 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -63,7 +63,7 @@ * some latency byte before the answer is available (max 15). * We have 2048 + 1024 + 15. */ -#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\ +#define ST33ZP24_SPI_BUFFER_SIZE (ST33ZP24_BUFSIZE + (ST33ZP24_BUFSIZE / 2) +\ MAX_SPI_LATENCY) diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h index 6f4a4198af6a..20da0a84988d 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.h +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -18,8 +18,8 @@ #ifndef __LOCAL_ST33ZP24_H__ #define __LOCAL_ST33ZP24_H__ -#define TPM_WRITE_DIRECTION 0x80 -#define TPM_BUFSIZE 2048 +#define TPM_WRITE_DIRECTION 0x80 +#define ST33ZP24_BUFSIZE 2048 struct st33zp24_dev { struct tpm_chip *chip; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 9086edc9066b..3b490d9d90e7 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -26,8 +26,7 @@ #include #include "tpm.h" -/* max. buffer size supported by our TPM */ -#define TPM_BUFSIZE 1260 +#define TPM_I2C_INFINEON_BUFSIZE 1260 /* max. number of iterations after I2C NAK */ #define MAX_COUNT 3 @@ -63,11 +62,13 @@ enum i2c_chip_type { UNKNOWN, }; -/* Structure to store I2C TPM specific stuff */ struct tpm_inf_dev { struct i2c_client *client; int locality; - u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ + /* In addition to the data itself, the buffer must fit the 7-bit I2C + * address and the direction bit. + */ + u8 buf[TPM_I2C_INFINEON_BUFSIZE + 1]; struct tpm_chip *chip; enum i2c_chip_type chip_type; unsigned int adapterlimit; @@ -219,7 +220,7 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, .buf = tpm_dev.buf }; - if (len > TPM_BUFSIZE) + if (len > TPM_I2C_INFINEON_BUFSIZE) return -EINVAL; if (!tpm_dev.client->adapter->algo->master_xfer) @@ -527,8 +528,8 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) u8 retries = 0; u8 sts = TPM_STS_GO; - if (len > TPM_BUFSIZE) - return -E2BIG; /* command is too long for our tpm, sorry */ + if (len > TPM_I2C_INFINEON_BUFSIZE) + return -E2BIG; if (request_locality(chip, 0) < 0) return -EBUSY; diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 217f7f1cbde8..5700cc2ddee1 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -35,14 +35,12 @@ #include "tpm.h" /* I2C interface offsets */ -#define TPM_STS 0x00 -#define TPM_BURST_COUNT 0x01 -#define TPM_DATA_FIFO_W 0x20 -#define TPM_DATA_FIFO_R 0x40 -#define TPM_VID_DID_RID 0x60 -/* TPM command header size */ -#define TPM_HEADER_SIZE 10 -#define TPM_RETRY 5 +#define TPM_STS 0x00 +#define TPM_BURST_COUNT 0x01 +#define TPM_DATA_FIFO_W 0x20 +#define TPM_DATA_FIFO_R 0x40 +#define TPM_VID_DID_RID 0x60 +#define TPM_I2C_RETRIES 5 /* * I2C bus device maximum buffer size w/o counting I2C address or command * i.e. max size required for I2C write is 34 = addr, command, 32 bytes data @@ -292,7 +290,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) dev_err(dev, "%s() count < header size\n", __func__); return -EIO; } - for (retries = 0; retries < TPM_RETRY; retries++) { + for (retries = 0; retries < TPM_I2C_RETRIES; retries++) { if (retries > 0) { /* if this is not the first trial, set responseRetry */ i2c_nuvoton_write_status(client, From 6ea3dfe1e0732c5bd3be1e073690b06a83c03c25 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 4 Feb 2019 15:16:40 +0200 Subject: [PATCH 1418/4277] selftests: add TPM 2.0 tests Added the tests that I've been using for testing TPM 2.0 functionality for a long time but have been out-of-tree so far, residing in https://github.com/jsakkine-intel/tpm2-scripts Cc: Tadeusz Struk Signed-off-by: Jarkko Sakkinen Acked-by: Joey Pabalinas Reviewed-by: Petr Vorel --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/tpm2/Makefile | 4 + tools/testing/selftests/tpm2/test_smoke.sh | 4 + tools/testing/selftests/tpm2/test_space.sh | 4 + tools/testing/selftests/tpm2/tpm2.py | 696 +++++++++++++++++++++ tools/testing/selftests/tpm2/tpm2_tests.py | 227 +++++++ 6 files changed, 936 insertions(+) create mode 100644 tools/testing/selftests/tpm2/Makefile create mode 100755 tools/testing/selftests/tpm2/test_smoke.sh create mode 100755 tools/testing/selftests/tpm2/test_space.sh create mode 100644 tools/testing/selftests/tpm2/tpm2.py create mode 100644 tools/testing/selftests/tpm2/tpm2_tests.py diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 1a2bd15c5b6e..a74ce2feea29 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -47,6 +47,7 @@ TARGETS += sysctl ifneq (1, $(quicktest)) TARGETS += timers endif +TARGETS += tpm2 TARGETS += user TARGETS += vm TARGETS += x86 diff --git a/tools/testing/selftests/tpm2/Makefile b/tools/testing/selftests/tpm2/Makefile new file mode 100644 index 000000000000..9dd848427a7b --- /dev/null +++ b/tools/testing/selftests/tpm2/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +include ../lib.mk + +TEST_PROGS := test_smoke.sh test_space.sh diff --git a/tools/testing/selftests/tpm2/test_smoke.sh b/tools/testing/selftests/tpm2/test_smoke.sh new file mode 100755 index 000000000000..80521d46220c --- /dev/null +++ b/tools/testing/selftests/tpm2/test_smoke.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +python -m unittest -v tpm2_tests.SmokeTest diff --git a/tools/testing/selftests/tpm2/test_space.sh b/tools/testing/selftests/tpm2/test_space.sh new file mode 100755 index 000000000000..a6f5e346635e --- /dev/null +++ b/tools/testing/selftests/tpm2/test_space.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +python -m unittest -v tpm2_tests.SpaceTest diff --git a/tools/testing/selftests/tpm2/tpm2.py b/tools/testing/selftests/tpm2/tpm2.py new file mode 100644 index 000000000000..40ea95ce2ead --- /dev/null +++ b/tools/testing/selftests/tpm2/tpm2.py @@ -0,0 +1,696 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +import hashlib +import os +import socket +import struct +import sys +import unittest +from fcntl import ioctl + + +TPM2_ST_NO_SESSIONS = 0x8001 +TPM2_ST_SESSIONS = 0x8002 + +TPM2_CC_FIRST = 0x01FF + +TPM2_CC_CREATE_PRIMARY = 0x0131 +TPM2_CC_DICTIONARY_ATTACK_LOCK_RESET = 0x0139 +TPM2_CC_CREATE = 0x0153 +TPM2_CC_LOAD = 0x0157 +TPM2_CC_UNSEAL = 0x015E +TPM2_CC_FLUSH_CONTEXT = 0x0165 +TPM2_CC_START_AUTH_SESSION = 0x0176 +TPM2_CC_GET_CAPABILITY = 0x017A +TPM2_CC_PCR_READ = 0x017E +TPM2_CC_POLICY_PCR = 0x017F +TPM2_CC_PCR_EXTEND = 0x0182 +TPM2_CC_POLICY_PASSWORD = 0x018C +TPM2_CC_POLICY_GET_DIGEST = 0x0189 + +TPM2_SE_POLICY = 0x01 +TPM2_SE_TRIAL = 0x03 + +TPM2_ALG_RSA = 0x0001 +TPM2_ALG_SHA1 = 0x0004 +TPM2_ALG_AES = 0x0006 +TPM2_ALG_KEYEDHASH = 0x0008 +TPM2_ALG_SHA256 = 0x000B +TPM2_ALG_NULL = 0x0010 +TPM2_ALG_CBC = 0x0042 +TPM2_ALG_CFB = 0x0043 + +TPM2_RH_OWNER = 0x40000001 +TPM2_RH_NULL = 0x40000007 +TPM2_RH_LOCKOUT = 0x4000000A +TPM2_RS_PW = 0x40000009 + +TPM2_RC_SIZE = 0x01D5 +TPM2_RC_AUTH_FAIL = 0x098E +TPM2_RC_POLICY_FAIL = 0x099D +TPM2_RC_COMMAND_CODE = 0x0143 + +TSS2_RC_LAYER_SHIFT = 16 +TSS2_RESMGR_TPM_RC_LAYER = (11 << TSS2_RC_LAYER_SHIFT) + +TPM2_CAP_HANDLES = 0x00000001 +TPM2_CAP_COMMANDS = 0x00000002 +TPM2_CAP_TPM_PROPERTIES = 0x00000006 + +TPM2_PT_FIXED = 0x100 +TPM2_PT_TOTAL_COMMANDS = TPM2_PT_FIXED + 41 + +HR_SHIFT = 24 +HR_LOADED_SESSION = 0x02000000 +HR_TRANSIENT = 0x80000000 + +SHA1_DIGEST_SIZE = 20 +SHA256_DIGEST_SIZE = 32 + +TPM2_VER0_ERRORS = { + 0x000: "TPM_RC_SUCCESS", + 0x030: "TPM_RC_BAD_TAG", +} + +TPM2_VER1_ERRORS = { + 0x000: "TPM_RC_FAILURE", + 0x001: "TPM_RC_FAILURE", + 0x003: "TPM_RC_SEQUENCE", + 0x00B: "TPM_RC_PRIVATE", + 0x019: "TPM_RC_HMAC", + 0x020: "TPM_RC_DISABLED", + 0x021: "TPM_RC_EXCLUSIVE", + 0x024: "TPM_RC_AUTH_TYPE", + 0x025: "TPM_RC_AUTH_MISSING", + 0x026: "TPM_RC_POLICY", + 0x027: "TPM_RC_PCR", + 0x028: "TPM_RC_PCR_CHANGED", + 0x02D: "TPM_RC_UPGRADE", + 0x02E: "TPM_RC_TOO_MANY_CONTEXTS", + 0x02F: "TPM_RC_AUTH_UNAVAILABLE", + 0x030: "TPM_RC_REBOOT", + 0x031: "TPM_RC_UNBALANCED", + 0x042: "TPM_RC_COMMAND_SIZE", + 0x043: "TPM_RC_COMMAND_CODE", + 0x044: "TPM_RC_AUTHSIZE", + 0x045: "TPM_RC_AUTH_CONTEXT", + 0x046: "TPM_RC_NV_RANGE", + 0x047: "TPM_RC_NV_SIZE", + 0x048: "TPM_RC_NV_LOCKED", + 0x049: "TPM_RC_NV_AUTHORIZATION", + 0x04A: "TPM_RC_NV_UNINITIALIZED", + 0x04B: "TPM_RC_NV_SPACE", + 0x04C: "TPM_RC_NV_DEFINED", + 0x050: "TPM_RC_BAD_CONTEXT", + 0x051: "TPM_RC_CPHASH", + 0x052: "TPM_RC_PARENT", + 0x053: "TPM_RC_NEEDS_TEST", + 0x054: "TPM_RC_NO_RESULT", + 0x055: "TPM_RC_SENSITIVE", + 0x07F: "RC_MAX_FM0", +} + +TPM2_FMT1_ERRORS = { + 0x001: "TPM_RC_ASYMMETRIC", + 0x002: "TPM_RC_ATTRIBUTES", + 0x003: "TPM_RC_HASH", + 0x004: "TPM_RC_VALUE", + 0x005: "TPM_RC_HIERARCHY", + 0x007: "TPM_RC_KEY_SIZE", + 0x008: "TPM_RC_MGF", + 0x009: "TPM_RC_MODE", + 0x00A: "TPM_RC_TYPE", + 0x00B: "TPM_RC_HANDLE", + 0x00C: "TPM_RC_KDF", + 0x00D: "TPM_RC_RANGE", + 0x00E: "TPM_RC_AUTH_FAIL", + 0x00F: "TPM_RC_NONCE", + 0x010: "TPM_RC_PP", + 0x012: "TPM_RC_SCHEME", + 0x015: "TPM_RC_SIZE", + 0x016: "TPM_RC_SYMMETRIC", + 0x017: "TPM_RC_TAG", + 0x018: "TPM_RC_SELECTOR", + 0x01A: "TPM_RC_INSUFFICIENT", + 0x01B: "TPM_RC_SIGNATURE", + 0x01C: "TPM_RC_KEY", + 0x01D: "TPM_RC_POLICY_FAIL", + 0x01F: "TPM_RC_INTEGRITY", + 0x020: "TPM_RC_TICKET", + 0x021: "TPM_RC_RESERVED_BITS", + 0x022: "TPM_RC_BAD_AUTH", + 0x023: "TPM_RC_EXPIRED", + 0x024: "TPM_RC_POLICY_CC", + 0x025: "TPM_RC_BINDING", + 0x026: "TPM_RC_CURVE", + 0x027: "TPM_RC_ECC_POINT", +} + +TPM2_WARN_ERRORS = { + 0x001: "TPM_RC_CONTEXT_GAP", + 0x002: "TPM_RC_OBJECT_MEMORY", + 0x003: "TPM_RC_SESSION_MEMORY", + 0x004: "TPM_RC_MEMORY", + 0x005: "TPM_RC_SESSION_HANDLES", + 0x006: "TPM_RC_OBJECT_HANDLES", + 0x007: "TPM_RC_LOCALITY", + 0x008: "TPM_RC_YIELDED", + 0x009: "TPM_RC_CANCELED", + 0x00A: "TPM_RC_TESTING", + 0x010: "TPM_RC_REFERENCE_H0", + 0x011: "TPM_RC_REFERENCE_H1", + 0x012: "TPM_RC_REFERENCE_H2", + 0x013: "TPM_RC_REFERENCE_H3", + 0x014: "TPM_RC_REFERENCE_H4", + 0x015: "TPM_RC_REFERENCE_H5", + 0x016: "TPM_RC_REFERENCE_H6", + 0x018: "TPM_RC_REFERENCE_S0", + 0x019: "TPM_RC_REFERENCE_S1", + 0x01A: "TPM_RC_REFERENCE_S2", + 0x01B: "TPM_RC_REFERENCE_S3", + 0x01C: "TPM_RC_REFERENCE_S4", + 0x01D: "TPM_RC_REFERENCE_S5", + 0x01E: "TPM_RC_REFERENCE_S6", + 0x020: "TPM_RC_NV_RATE", + 0x021: "TPM_RC_LOCKOUT", + 0x022: "TPM_RC_RETRY", + 0x023: "TPM_RC_NV_UNAVAILABLE", + 0x7F: "TPM_RC_NOT_USED", +} + +RC_VER1 = 0x100 +RC_FMT1 = 0x080 +RC_WARN = 0x900 + +ALG_DIGEST_SIZE_MAP = { + TPM2_ALG_SHA1: SHA1_DIGEST_SIZE, + TPM2_ALG_SHA256: SHA256_DIGEST_SIZE, +} + +ALG_HASH_FUNCTION_MAP = { + TPM2_ALG_SHA1: hashlib.sha1, + TPM2_ALG_SHA256: hashlib.sha256 +} + +NAME_ALG_MAP = { + "sha1": TPM2_ALG_SHA1, + "sha256": TPM2_ALG_SHA256, +} + + +class UnknownAlgorithmIdError(Exception): + def __init__(self, alg): + self.alg = alg + + def __str__(self): + return '0x%0x' % (alg) + + +class UnknownAlgorithmNameError(Exception): + def __init__(self, name): + self.name = name + + def __str__(self): + return name + + +class UnknownPCRBankError(Exception): + def __init__(self, alg): + self.alg = alg + + def __str__(self): + return '0x%0x' % (alg) + + +class ProtocolError(Exception): + def __init__(self, cc, rc): + self.cc = cc + self.rc = rc + + if (rc & RC_FMT1) == RC_FMT1: + self.name = TPM2_FMT1_ERRORS.get(rc & 0x3f, "TPM_RC_UNKNOWN") + elif (rc & RC_WARN) == RC_WARN: + self.name = TPM2_WARN_ERRORS.get(rc & 0x7f, "TPM_RC_UNKNOWN") + elif (rc & RC_VER1) == RC_VER1: + self.name = TPM2_VER1_ERRORS.get(rc & 0x7f, "TPM_RC_UNKNOWN") + else: + self.name = TPM2_VER0_ERRORS.get(rc & 0x7f, "TPM_RC_UNKNOWN") + + def __str__(self): + if self.cc: + return '%s: cc=0x%08x, rc=0x%08x' % (self.name, self.cc, self.rc) + else: + return '%s: rc=0x%08x' % (self.name, self.rc) + + +class AuthCommand(object): + """TPMS_AUTH_COMMAND""" + + def __init__(self, session_handle=TPM2_RS_PW, nonce='', session_attributes=0, + hmac=''): + self.session_handle = session_handle + self.nonce = nonce + self.session_attributes = session_attributes + self.hmac = hmac + + def __str__(self): + fmt = '>I H%us B H%us' % (len(self.nonce), len(self.hmac)) + return struct.pack(fmt, self.session_handle, len(self.nonce), + self.nonce, self.session_attributes, len(self.hmac), + self.hmac) + + def __len__(self): + fmt = '>I H%us B H%us' % (len(self.nonce), len(self.hmac)) + return struct.calcsize(fmt) + + +class SensitiveCreate(object): + """TPMS_SENSITIVE_CREATE""" + + def __init__(self, user_auth='', data=''): + self.user_auth = user_auth + self.data = data + + def __str__(self): + fmt = '>H%us H%us' % (len(self.user_auth), len(self.data)) + return struct.pack(fmt, len(self.user_auth), self.user_auth, + len(self.data), self.data) + + def __len__(self): + fmt = '>H%us H%us' % (len(self.user_auth), len(self.data)) + return struct.calcsize(fmt) + + +class Public(object): + """TPMT_PUBLIC""" + + FIXED_TPM = (1 << 1) + FIXED_PARENT = (1 << 4) + SENSITIVE_DATA_ORIGIN = (1 << 5) + USER_WITH_AUTH = (1 << 6) + RESTRICTED = (1 << 16) + DECRYPT = (1 << 17) + + def __fmt(self): + return '>HHIH%us%usH%us' % \ + (len(self.auth_policy), len(self.parameters), len(self.unique)) + + def __init__(self, object_type, name_alg, object_attributes, auth_policy='', + parameters='', unique=''): + self.object_type = object_type + self.name_alg = name_alg + self.object_attributes = object_attributes + self.auth_policy = auth_policy + self.parameters = parameters + self.unique = unique + + def __str__(self): + return struct.pack(self.__fmt(), + self.object_type, + self.name_alg, + self.object_attributes, + len(self.auth_policy), + self.auth_policy, + self.parameters, + len(self.unique), + self.unique) + + def __len__(self): + return struct.calcsize(self.__fmt()) + + +def get_digest_size(alg): + ds = ALG_DIGEST_SIZE_MAP.get(alg) + if not ds: + raise UnknownAlgorithmIdError(alg) + return ds + + +def get_hash_function(alg): + f = ALG_HASH_FUNCTION_MAP.get(alg) + if not f: + raise UnknownAlgorithmIdError(alg) + return f + + +def get_algorithm(name): + alg = NAME_ALG_MAP.get(name) + if not alg: + raise UnknownAlgorithmNameError(name) + return alg + + +def hex_dump(d): + d = [format(ord(x), '02x') for x in d] + d = [d[i: i + 16] for i in xrange(0, len(d), 16)] + d = [' '.join(x) for x in d] + d = os.linesep.join(d) + + return d + +class Client: + FLAG_DEBUG = 0x01 + FLAG_SPACE = 0x02 + TPM_IOC_NEW_SPACE = 0xa200 + + def __init__(self, flags = 0): + self.flags = flags + + if (self.flags & Client.FLAG_SPACE) == 0: + self.tpm = open('/dev/tpm0', 'r+b') + else: + self.tpm = open('/dev/tpmrm0', 'r+b') + + def close(self): + self.tpm.close() + + def send_cmd(self, cmd): + self.tpm.write(cmd) + rsp = self.tpm.read() + + if (self.flags & Client.FLAG_DEBUG) != 0: + sys.stderr.write('cmd' + os.linesep) + sys.stderr.write(hex_dump(cmd) + os.linesep) + sys.stderr.write('rsp' + os.linesep) + sys.stderr.write(hex_dump(rsp) + os.linesep) + + rc = struct.unpack('>I', rsp[6:10])[0] + if rc != 0: + cc = struct.unpack('>I', cmd[6:10])[0] + raise ProtocolError(cc, rc) + + return rsp + + def read_pcr(self, i, bank_alg = TPM2_ALG_SHA1): + pcrsel_len = max((i >> 3) + 1, 3) + pcrsel = [0] * pcrsel_len + pcrsel[i >> 3] = 1 << (i & 7) + pcrsel = ''.join(map(chr, pcrsel)) + + fmt = '>HII IHB%us' % (pcrsel_len) + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_PCR_READ, + 1, + bank_alg, + pcrsel_len, pcrsel) + + rsp = self.send_cmd(cmd) + + pcr_update_cnt, pcr_select_cnt = struct.unpack('>II', rsp[10:18]) + assert pcr_select_cnt == 1 + rsp = rsp[18:] + + alg2, pcrsel_len2 = struct.unpack('>HB', rsp[:3]) + assert bank_alg == alg2 and pcrsel_len == pcrsel_len2 + rsp = rsp[3 + pcrsel_len:] + + digest_cnt = struct.unpack('>I', rsp[:4])[0] + if digest_cnt == 0: + return None + rsp = rsp[6:] + + return rsp + + def extend_pcr(self, i, dig, bank_alg = TPM2_ALG_SHA1): + ds = get_digest_size(bank_alg) + assert(ds == len(dig)) + + auth_cmd = AuthCommand() + + fmt = '>HII I I%us IH%us' % (len(auth_cmd), ds) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_PCR_EXTEND, + i, + len(auth_cmd), + str(auth_cmd), + 1, bank_alg, dig) + + self.send_cmd(cmd) + + def start_auth_session(self, session_type, name_alg = TPM2_ALG_SHA1): + fmt = '>HII IIH16sHBHH' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_START_AUTH_SESSION, + TPM2_RH_NULL, + TPM2_RH_NULL, + 16, + '\0' * 16, + 0, + session_type, + TPM2_ALG_NULL, + name_alg) + + return struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] + + def __calc_pcr_digest(self, pcrs, bank_alg = TPM2_ALG_SHA1, + digest_alg = TPM2_ALG_SHA1): + x = [] + f = get_hash_function(digest_alg) + + for i in pcrs: + pcr = self.read_pcr(i, bank_alg) + if pcr == None: + return None + x += pcr + + return f(bytearray(x)).digest() + + def policy_pcr(self, handle, pcrs, bank_alg = TPM2_ALG_SHA1, + name_alg = TPM2_ALG_SHA1): + ds = get_digest_size(name_alg) + dig = self.__calc_pcr_digest(pcrs, bank_alg, name_alg) + if not dig: + raise UnknownPCRBankError(bank_alg) + + pcrsel_len = max((max(pcrs) >> 3) + 1, 3) + pcrsel = [0] * pcrsel_len + for i in pcrs: + pcrsel[i >> 3] |= 1 << (i & 7) + pcrsel = ''.join(map(chr, pcrsel)) + + fmt = '>HII IH%usIHB3s' % ds + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_POLICY_PCR, + handle, + len(dig), str(dig), + 1, + bank_alg, + pcrsel_len, pcrsel) + + self.send_cmd(cmd) + + def policy_password(self, handle): + fmt = '>HII I' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_POLICY_PASSWORD, + handle) + + self.send_cmd(cmd) + + def get_policy_digest(self, handle): + fmt = '>HII I' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_POLICY_GET_DIGEST, + handle) + + return self.send_cmd(cmd)[12:] + + def flush_context(self, handle): + fmt = '>HIII' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_FLUSH_CONTEXT, + handle) + + self.send_cmd(cmd) + + def create_root_key(self, auth_value = ''): + attributes = \ + Public.FIXED_TPM | \ + Public.FIXED_PARENT | \ + Public.SENSITIVE_DATA_ORIGIN | \ + Public.USER_WITH_AUTH | \ + Public.RESTRICTED | \ + Public.DECRYPT + + auth_cmd = AuthCommand() + sensitive = SensitiveCreate(user_auth=auth_value) + + public_parms = struct.pack( + '>HHHHHI', + TPM2_ALG_AES, + 128, + TPM2_ALG_CFB, + TPM2_ALG_NULL, + 2048, + 0) + + public = Public( + object_type=TPM2_ALG_RSA, + name_alg=TPM2_ALG_SHA1, + object_attributes=attributes, + parameters=public_parms) + + fmt = '>HIII I%us H%us H%us HI' % \ + (len(auth_cmd), len(sensitive), len(public)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_CREATE_PRIMARY, + TPM2_RH_OWNER, + len(auth_cmd), + str(auth_cmd), + len(sensitive), + str(sensitive), + len(public), + str(public), + 0, 0) + + return struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] + + def seal(self, parent_key, data, auth_value, policy_dig, + name_alg = TPM2_ALG_SHA1): + ds = get_digest_size(name_alg) + assert(not policy_dig or ds == len(policy_dig)) + + attributes = 0 + if not policy_dig: + attributes |= Public.USER_WITH_AUTH + policy_dig = '' + + auth_cmd = AuthCommand() + sensitive = SensitiveCreate(user_auth=auth_value, data=data) + + public = Public( + object_type=TPM2_ALG_KEYEDHASH, + name_alg=name_alg, + object_attributes=attributes, + auth_policy=policy_dig, + parameters=struct.pack('>H', TPM2_ALG_NULL)) + + fmt = '>HIII I%us H%us H%us HI' % \ + (len(auth_cmd), len(sensitive), len(public)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_CREATE, + parent_key, + len(auth_cmd), + str(auth_cmd), + len(sensitive), + str(sensitive), + len(public), + str(public), + 0, 0) + + rsp = self.send_cmd(cmd) + + return rsp[14:] + + def unseal(self, parent_key, blob, auth_value, policy_handle): + private_len = struct.unpack('>H', blob[0:2])[0] + public_start = private_len + 2 + public_len = struct.unpack('>H', blob[public_start:public_start + 2])[0] + blob = blob[:private_len + public_len + 4] + + auth_cmd = AuthCommand() + + fmt = '>HII I I%us %us' % (len(auth_cmd), len(blob)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_LOAD, + parent_key, + len(auth_cmd), + str(auth_cmd), + blob) + + data_handle = struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] + + if policy_handle: + auth_cmd = AuthCommand(session_handle=policy_handle, hmac=auth_value) + else: + auth_cmd = AuthCommand(hmac=auth_value) + + fmt = '>HII I I%us' % (len(auth_cmd)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_UNSEAL, + data_handle, + len(auth_cmd), + str(auth_cmd)) + + try: + rsp = self.send_cmd(cmd) + finally: + self.flush_context(data_handle) + + data_len = struct.unpack('>I', rsp[10:14])[0] - 2 + + return rsp[16:16 + data_len] + + def reset_da_lock(self): + auth_cmd = AuthCommand() + + fmt = '>HII I I%us' % (len(auth_cmd)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_DICTIONARY_ATTACK_LOCK_RESET, + TPM2_RH_LOCKOUT, + len(auth_cmd), + str(auth_cmd)) + + self.send_cmd(cmd) + + def __get_cap_cnt(self, cap, pt, cnt): + handles = [] + fmt = '>HII III' + + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_GET_CAPABILITY, + cap, pt, cnt) + + rsp = self.send_cmd(cmd)[10:] + more_data, cap, cnt = struct.unpack('>BII', rsp[:9]) + rsp = rsp[9:] + + for i in xrange(0, cnt): + handle = struct.unpack('>I', rsp[:4])[0] + handles.append(handle) + rsp = rsp[4:] + + return handles, more_data + + def get_cap(self, cap, pt): + handles = [] + + more_data = True + while more_data: + next_handles, more_data = self.__get_cap_cnt(cap, pt, 1) + handles += next_handles + pt += 1 + + return handles diff --git a/tools/testing/selftests/tpm2/tpm2_tests.py b/tools/testing/selftests/tpm2/tpm2_tests.py new file mode 100644 index 000000000000..3bb066fea4a0 --- /dev/null +++ b/tools/testing/selftests/tpm2/tpm2_tests.py @@ -0,0 +1,227 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 +from tpm2 import ProtocolError +import unittest +import logging +import struct + +class SmokeTest(unittest.TestCase): + def setUp(self): + self.client = tpm2.Client() + self.root_key = self.client.create_root_key() + + def tearDown(self): + self.client.flush_context(self.root_key) + self.client.close() + + def test_seal_with_auth(self): + data = 'X' * 64 + auth = 'A' * 15 + + blob = self.client.seal(self.root_key, data, auth, None) + result = self.client.unseal(self.root_key, blob, auth, None) + self.assertEqual(data, result) + + def test_seal_with_policy(self): + handle = self.client.start_auth_session(tpm2.TPM2_SE_TRIAL) + + data = 'X' * 64 + auth = 'A' * 15 + pcrs = [16] + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + policy_dig = self.client.get_policy_digest(handle) + finally: + self.client.flush_context(handle) + + blob = self.client.seal(self.root_key, data, auth, policy_dig) + + handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + result = self.client.unseal(self.root_key, blob, auth, handle) + except: + self.client.flush_context(handle) + raise + + self.assertEqual(data, result) + + def test_unseal_with_wrong_auth(self): + data = 'X' * 64 + auth = 'A' * 20 + rc = 0 + + blob = self.client.seal(self.root_key, data, auth, None) + try: + result = self.client.unseal(self.root_key, blob, auth[:-1] + 'B', None) + except ProtocolError, e: + rc = e.rc + + self.assertEqual(rc, tpm2.TPM2_RC_AUTH_FAIL) + + def test_unseal_with_wrong_policy(self): + handle = self.client.start_auth_session(tpm2.TPM2_SE_TRIAL) + + data = 'X' * 64 + auth = 'A' * 17 + pcrs = [16] + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + policy_dig = self.client.get_policy_digest(handle) + finally: + self.client.flush_context(handle) + + blob = self.client.seal(self.root_key, data, auth, policy_dig) + + # Extend first a PCR that is not part of the policy and try to unseal. + # This should succeed. + + ds = tpm2.get_digest_size(tpm2.TPM2_ALG_SHA1) + self.client.extend_pcr(1, 'X' * ds) + + handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + result = self.client.unseal(self.root_key, blob, auth, handle) + except: + self.client.flush_context(handle) + raise + + self.assertEqual(data, result) + + # Then, extend a PCR that is part of the policy and try to unseal. + # This should fail. + self.client.extend_pcr(16, 'X' * ds) + + handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) + + rc = 0 + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + result = self.client.unseal(self.root_key, blob, auth, handle) + except ProtocolError, e: + rc = e.rc + self.client.flush_context(handle) + except: + self.client.flush_context(handle) + raise + + self.assertEqual(rc, tpm2.TPM2_RC_POLICY_FAIL) + + def test_seal_with_too_long_auth(self): + ds = tpm2.get_digest_size(tpm2.TPM2_ALG_SHA1) + data = 'X' * 64 + auth = 'A' * (ds + 1) + + rc = 0 + try: + blob = self.client.seal(self.root_key, data, auth, None) + except ProtocolError, e: + rc = e.rc + + self.assertEqual(rc, tpm2.TPM2_RC_SIZE) + + def test_too_short_cmd(self): + rejected = False + try: + fmt = '>HIII' + cmd = struct.pack(fmt, + tpm2.TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt) + 1, + tpm2.TPM2_CC_FLUSH_CONTEXT, + 0xDEADBEEF) + + self.client.send_cmd(cmd) + except IOError, e: + rejected = True + except: + pass + self.assertEqual(rejected, True) + +class SpaceTest(unittest.TestCase): + def setUp(self): + logging.basicConfig(filename='SpaceTest.log', level=logging.DEBUG) + + def test_make_two_spaces(self): + log = logging.getLogger(__name__) + log.debug("test_make_two_spaces") + + space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) + root1 = space1.create_root_key() + space2 = tpm2.Client(tpm2.Client.FLAG_SPACE) + root2 = space2.create_root_key() + root3 = space2.create_root_key() + + log.debug("%08x" % (root1)) + log.debug("%08x" % (root2)) + log.debug("%08x" % (root3)) + + def test_flush_context(self): + log = logging.getLogger(__name__) + log.debug("test_flush_context") + + space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) + root1 = space1.create_root_key() + log.debug("%08x" % (root1)) + + space1.flush_context(root1) + + def test_get_handles(self): + log = logging.getLogger(__name__) + log.debug("test_get_handles") + + space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) + space1.create_root_key() + space2 = tpm2.Client(tpm2.Client.FLAG_SPACE) + space2.create_root_key() + space2.create_root_key() + + handles = space2.get_cap(tpm2.TPM2_CAP_HANDLES, tpm2.HR_TRANSIENT) + + self.assertEqual(len(handles), 2) + + log.debug("%08x" % (handles[0])) + log.debug("%08x" % (handles[1])) + + def test_invalid_cc(self): + log = logging.getLogger(__name__) + log.debug(sys._getframe().f_code.co_name) + + TPM2_CC_INVALID = tpm2.TPM2_CC_FIRST - 1 + + space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) + root1 = space1.create_root_key() + log.debug("%08x" % (root1)) + + fmt = '>HII' + cmd = struct.pack(fmt, tpm2.TPM2_ST_NO_SESSIONS, struct.calcsize(fmt), + TPM2_CC_INVALID) + + rc = 0 + try: + space1.send_cmd(cmd) + except ProtocolError, e: + rc = e.rc + + self.assertEqual(rc, tpm2.TPM2_RC_COMMAND_CODE | + tpm2.TSS2_RESMGR_TPM_RC_LAYER) From 2e5a662de36a92a95b5939273468b01785dc41ec Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 Feb 2019 08:16:51 +0100 Subject: [PATCH 1419/4277] i2c: cbus-gpio: Switch to use GPIO descriptors This augments the CBUS GPIO I2C driver to use GPIO descriptors for clock, sel and data. We drop the platform data that was only used for carrying GPIO numbers and use machine descriptor tables instead. Signed-off-by: Linus Walleij Tested-by: Aaro Koskinen Acked-by: Tony Lindgren Signed-off-by: Wolfram Sang --- arch/arm/mach-omap1/board-nokia770.c | 18 ++--- drivers/i2c/busses/i2c-cbus-gpio.c | 80 ++++++++------------- include/linux/platform_data/i2c-cbus-gpio.h | 27 ------- 3 files changed, 40 insertions(+), 85 deletions(-) delete mode 100644 include/linux/platform_data/i2c-cbus-gpio.h diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index eb41db78cd47..10848f573d37 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include @@ -217,18 +217,19 @@ static inline void nokia770_mmc_init(void) #endif #if IS_ENABLED(CONFIG_I2C_CBUS_GPIO) -static struct i2c_cbus_platform_data nokia770_cbus_data = { - .clk_gpio = OMAP_MPUIO(9), - .dat_gpio = OMAP_MPUIO(10), - .sel_gpio = OMAP_MPUIO(11), +static struct gpiod_lookup_table nokia770_cbus_gpio_table = { + .dev_id = "i2c-cbus-gpio.2", + .table = { + GPIO_LOOKUP_IDX("mpuio", 9, NULL, 0, 0), /* clk */ + GPIO_LOOKUP_IDX("mpuio", 10, NULL, 1, 0), /* dat */ + GPIO_LOOKUP_IDX("mpuio", 11, NULL, 2, 0), /* sel */ + { }, + }, }; static struct platform_device nokia770_cbus_device = { .name = "i2c-cbus-gpio", .id = 2, - .dev = { - .platform_data = &nokia770_cbus_data, - }, }; static struct i2c_board_info nokia770_i2c_board_info_2[] __initdata = { @@ -257,6 +258,7 @@ static void __init nokia770_cbus_init(void) nokia770_i2c_board_info_2[1].irq = gpio_to_irq(tahvo_irq_gpio); i2c_register_board_info(2, nokia770_i2c_board_info_2, ARRAY_SIZE(nokia770_i2c_board_info_2)); + gpiod_add_lookup_table(&nokia770_cbus_gpio_table); platform_device_register(&nokia770_cbus_device); } #else /* CONFIG_I2C_CBUS_GPIO */ diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c index b4f91e48948a..72df563477b1 100644 --- a/drivers/i2c/busses/i2c-cbus-gpio.c +++ b/drivers/i2c/busses/i2c-cbus-gpio.c @@ -18,16 +18,14 @@ #include #include -#include #include #include #include #include #include -#include +#include #include #include -#include /* * Bit counts are derived from Nokia implementation. These should be checked @@ -39,9 +37,9 @@ struct cbus_host { spinlock_t lock; /* host lock */ struct device *dev; - int clk_gpio; - int dat_gpio; - int sel_gpio; + struct gpio_desc *clk; + struct gpio_desc *dat; + struct gpio_desc *sel; }; /** @@ -51,9 +49,9 @@ struct cbus_host { */ static void cbus_send_bit(struct cbus_host *host, unsigned bit) { - gpio_set_value(host->dat_gpio, bit ? 1 : 0); - gpio_set_value(host->clk_gpio, 1); - gpio_set_value(host->clk_gpio, 0); + gpiod_set_value(host->dat, bit ? 1 : 0); + gpiod_set_value(host->clk, 1); + gpiod_set_value(host->clk, 0); } /** @@ -78,9 +76,9 @@ static int cbus_receive_bit(struct cbus_host *host) { int ret; - gpio_set_value(host->clk_gpio, 1); - ret = gpio_get_value(host->dat_gpio); - gpio_set_value(host->clk_gpio, 0); + gpiod_set_value(host->clk, 1); + ret = gpiod_get_value(host->dat); + gpiod_set_value(host->clk, 0); return ret; } @@ -123,10 +121,10 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, spin_lock_irqsave(&host->lock, flags); /* Reset state and start of transfer, SEL stays down during transfer */ - gpio_set_value(host->sel_gpio, 0); + gpiod_set_value(host->sel, 0); /* Set the DAT pin to output */ - gpio_direction_output(host->dat_gpio, 1); + gpiod_direction_output(host->dat, 1); /* Send the device address */ cbus_send_data(host, dev, CBUS_ADDR_BITS); @@ -141,12 +139,12 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, cbus_send_data(host, data, 16); ret = 0; } else { - ret = gpio_direction_input(host->dat_gpio); + ret = gpiod_direction_input(host->dat); if (ret) { dev_dbg(host->dev, "failed setting direction\n"); goto out; } - gpio_set_value(host->clk_gpio, 1); + gpiod_set_value(host->clk, 1); ret = cbus_receive_word(host); if (ret < 0) { @@ -156,9 +154,9 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, } /* Indicate end of transfer, SEL goes up until next transfer */ - gpio_set_value(host->sel_gpio, 1); - gpio_set_value(host->clk_gpio, 1); - gpio_set_value(host->clk_gpio, 0); + gpiod_set_value(host->sel, 1); + gpiod_set_value(host->clk, 1); + gpiod_set_value(host->clk, 0); out: spin_unlock_irqrestore(&host->lock, flags); @@ -214,7 +212,6 @@ static int cbus_i2c_probe(struct platform_device *pdev) { struct i2c_adapter *adapter; struct cbus_host *chost; - int ret; adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), GFP_KERNEL); @@ -225,22 +222,20 @@ static int cbus_i2c_probe(struct platform_device *pdev) if (!chost) return -ENOMEM; - if (pdev->dev.of_node) { - struct device_node *dnode = pdev->dev.of_node; - if (of_gpio_count(dnode) != 3) - return -ENODEV; - chost->clk_gpio = of_get_gpio(dnode, 0); - chost->dat_gpio = of_get_gpio(dnode, 1); - chost->sel_gpio = of_get_gpio(dnode, 2); - } else if (dev_get_platdata(&pdev->dev)) { - struct i2c_cbus_platform_data *pdata = - dev_get_platdata(&pdev->dev); - chost->clk_gpio = pdata->clk_gpio; - chost->dat_gpio = pdata->dat_gpio; - chost->sel_gpio = pdata->sel_gpio; - } else { + if (gpiod_count(&pdev->dev, NULL) != 3) return -ENODEV; - } + chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW); + if (IS_ERR(chost->clk)) + return PTR_ERR(chost->clk); + chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN); + if (IS_ERR(chost->dat)) + return PTR_ERR(chost->dat); + chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH); + if (IS_ERR(chost->sel)) + return PTR_ERR(chost->sel); + gpiod_set_consumer_name(chost->clk, "CBUS clk"); + gpiod_set_consumer_name(chost->dat, "CBUS dat"); + gpiod_set_consumer_name(chost->sel, "CBUS sel"); adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON; @@ -254,21 +249,6 @@ static int cbus_i2c_probe(struct platform_device *pdev) spin_lock_init(&chost->lock); chost->dev = &pdev->dev; - ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio, - GPIOF_OUT_INIT_LOW, "CBUS clk"); - if (ret) - return ret; - - ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN, - "CBUS data"); - if (ret) - return ret; - - ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio, - GPIOF_OUT_INIT_HIGH, "CBUS sel"); - if (ret) - return ret; - i2c_set_adapdata(adapter, chost); platform_set_drvdata(pdev, adapter); diff --git a/include/linux/platform_data/i2c-cbus-gpio.h b/include/linux/platform_data/i2c-cbus-gpio.h deleted file mode 100644 index 6faa992a9502..000000000000 --- a/include/linux/platform_data/i2c-cbus-gpio.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * i2c-cbus-gpio.h - CBUS I2C platform_data definition - * - * Copyright (C) 2004-2009 Nokia Corporation - * - * Written by Felipe Balbi and Aaro Koskinen. - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of this - * archive for more details. - * - * 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 __INCLUDE_LINUX_I2C_CBUS_GPIO_H -#define __INCLUDE_LINUX_I2C_CBUS_GPIO_H - -struct i2c_cbus_platform_data { - int dat_gpio; - int clk_gpio; - int sel_gpio; -}; - -#endif /* __INCLUDE_LINUX_I2C_CBUS_GPIO_H */ From 43116a0d9a533af24728ea7898c1d984446caa58 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 4 Feb 2019 23:00:09 +0100 Subject: [PATCH 1420/4277] i2c: sh_mobile: sort compatible entries Makes it easier to add new ones. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 554bd79e0891..0e834d97ca51 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -773,11 +773,11 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, }; From e8a27567509b2439b3d8e5937a11483fb25c0f0c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 4 Feb 2019 23:00:10 +0100 Subject: [PATCH 1421/4277] i2c: sh_mobile: use new clock calculation formulas for Gen3 We could finally measure the clock on an Ebisu board. The new formula gives way better results, i.e. 100kHz instead of 106kHz and 400kHz instead of 387kHz. Switch to these formulas for all Gen3 SoCs. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 0e834d97ca51..7ef3b095f7ae 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -773,11 +773,11 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, }; From 8fbd9b08b73e6de17b958a107e69a998faba18fa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Feb 2019 18:00:46 +0100 Subject: [PATCH 1422/4277] i2c: sh_mobile: use new clock calculation formulas for Gen2 We measured the clock on a Lager and an Ebisu board. The new formula gives better results for both. So after Gen3, switch to this formula for all Gen2 SoCs. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 7ef3b095f7ae..8777af4c695e 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -769,14 +769,14 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, { .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config }, - { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config }, - { .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7791", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a7792", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a7793", .data = &v2_freq_calc_dt_config }, + { .compatible = "renesas,iic-r8a7794", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, - { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,rcar-gen2-iic", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, From 60c1d5605b67218cc600588af59260c0d1144495 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Feb 2019 13:58:41 +0100 Subject: [PATCH 1423/4277] i2c: gpio: merge two very similar comments I think it is clear enough if we have the explanation once and make it clear it is applicable for both SCL and SDA. Reword it a little with the help of Simon's native language skills :) Signed-off-by: Simon Horman Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-gpio.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 362d3b9dd3b6..2d532cc042f5 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -286,11 +286,11 @@ static int i2c_gpio_probe(struct platform_device *pdev) /* * First get the GPIO pins; if it fails, we'll defer the probe. - * If the SDA line is marked from platform data or device tree as - * "open drain" it means something outside of our control is making - * this line being handled as open drain, and we should just handle - * it as any other output. Else we enforce open drain as this is - * required for an I2C bus. + * If the SCL/SDA lines are marked "open drain" by platform data or + * device tree then this means that something outside of our control is + * marking these lines to be handled as open drain, and we should just + * handle them as we handle any other output. Else we enforce open + * drain as this is required for an I2C bus. */ if (pdata->sda_is_open_drain) gflags = GPIOD_OUT_HIGH; @@ -300,13 +300,6 @@ static int i2c_gpio_probe(struct platform_device *pdev) if (IS_ERR(priv->sda)) return PTR_ERR(priv->sda); - /* - * If the SCL line is marked from platform data or device tree as - * "open drain" it means something outside of our control is making - * this line being handled as open drain, and we should just handle - * it as any other output. Else we enforce open drain as this is - * required for an I2C bus. - */ if (pdata->scl_is_open_drain) gflags = GPIOD_OUT_HIGH; else From 18769445ca55c2277d29e35ffda7e9a206b1758f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Feb 2019 14:37:25 +0100 Subject: [PATCH 1424/4277] i2c: rcar: refactor TCYC handling The latest documentation made it clear that we need to initialize the TCYC value independently of DMA. The old code used TCYC06 (wrongly) for non-DMA transfers. The new code sets TCYC up independently from DMA. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 498ba4b87833..dd52a068b140 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -39,8 +39,8 @@ #define ICSAR 0x1C /* slave address */ #define ICMAR 0x20 /* master address */ #define ICRXTX 0x24 /* data port */ -#define ICDMAER 0x3c /* DMA enable */ -#define ICFBSCR 0x38 /* first bit setup cycle */ +#define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */ +#define ICDMAER 0x3c /* DMA enable (Gen3) */ /* ICSCR */ #define SDBS (1 << 3) /* slave data buffer select */ @@ -83,7 +83,6 @@ #define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */ /* ICFBSCR */ -#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */ #define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */ @@ -212,6 +211,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) rcar_i2c_write(priv, ICMSR, 0); /* start clock */ rcar_i2c_write(priv, ICCCR, priv->icccr); + + if (priv->devtype == I2C_RCAR_GEN3) + rcar_i2c_write(priv, ICFBSCR, TCYC17); + } static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) @@ -363,9 +366,6 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) /* Disable DMA Master Received/Transmitted */ rcar_i2c_write(priv, ICDMAER, 0); - /* Reset default delay */ - rcar_i2c_write(priv, ICFBSCR, TCYC06); - dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg), sg_dma_len(&priv->sg), priv->dma_direction); @@ -461,9 +461,6 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv) return; } - /* Set delay for DMA operations */ - rcar_i2c_write(priv, ICFBSCR, TCYC17); - /* Enable DMA Master Received/Transmitted */ if (read) rcar_i2c_write(priv, ICDMAER, RMDMAE); From 6f4e626fb0cc93d50b49b79c2ee33bd769ee57f0 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 7 Feb 2019 09:07:20 -0700 Subject: [PATCH 1425/4277] scsi: ata: Use unsigned int for cmd's type in ioctls in scsi_host_template Clang warns several times in the scsi subsystem (trimmed for brevity): drivers/scsi/hpsa.c:6209:7: warning: overflow converting case value to switch condition type (2147762695 to 18446744071562347015) [-Wswitch] case CCISS_GETBUSTYPES: ^ drivers/scsi/hpsa.c:6208:7: warning: overflow converting case value to switch condition type (2147762694 to 18446744071562347014) [-Wswitch] case CCISS_GETHEARTBEAT: ^ The root cause is that the _IOC macro can generate really large numbers, which don't fit into type 'int', which is used for the cmd parameter in the ioctls in scsi_host_template. My research into how GCC and Clang are handling this at a low level didn't prove fruitful. However, looking at the rest of the kernel tree, all ioctls use an 'unsigned int' for the cmd parameter, which will fit all of the _IOC values in the scsi/ata subsystems. Make that change because none of the ioctls expect a negative value for any command, it brings the ioctls inline with the reset of the kernel, and it removes ambiguity, which is never good when dealing with compilers. Link: https://github.com/ClangBuiltLinux/linux/issues/85 Link: https://github.com/ClangBuiltLinux/linux/issues/154 Link: https://github.com/ClangBuiltLinux/linux/issues/157 Signed-off-by: Nathan Chancellor Acked-by: Bradley Grove Acked-by: Don Brace Reviewed-by: Bart Van Assche Tested-by: Nick Desaulniers Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/ata/libata-scsi.c | 5 +++-- drivers/scsi/aacraid/aachba.c | 2 +- drivers/scsi/aacraid/aacraid.h | 4 ++-- drivers/scsi/aacraid/commctrl.c | 2 +- drivers/scsi/aacraid/linit.c | 6 ++++-- drivers/scsi/cxlflash/common.h | 3 ++- drivers/scsi/cxlflash/main.c | 2 +- drivers/scsi/cxlflash/superpipe.c | 12 +++++------- drivers/scsi/esas2r/esas2r.h | 4 ++-- drivers/scsi/esas2r/esas2r_ioctl.c | 16 +++++++--------- drivers/scsi/esas2r/esas2r_main.c | 2 +- drivers/scsi/hpsa.c | 15 +++++++++------ drivers/scsi/ipr.c | 3 ++- drivers/scsi/libsas/sas_scsi_host.c | 2 +- drivers/scsi/scsi_debug.c | 3 ++- drivers/scsi/smartpqi/smartpqi_init.c | 3 ++- include/linux/libata.h | 5 +++-- include/scsi/libsas.h | 3 ++- include/scsi/scsi_host.h | 6 ++++-- 19 files changed, 54 insertions(+), 44 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 3d4887d0e84a..6291f1dbf342 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -778,7 +778,7 @@ static int ata_ioc32(struct ata_port *ap) } int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *scsidev, - int cmd, void __user *arg) + unsigned int cmd, void __user *arg) { unsigned long val; int rc = -EINVAL; @@ -829,7 +829,8 @@ int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *scsidev, } EXPORT_SYMBOL_GPL(ata_sas_scsi_ioctl); -int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg) +int ata_scsi_ioctl(struct scsi_device *scsidev, unsigned int cmd, + void __user *arg) { return ata_sas_scsi_ioctl(ata_shost_to_port(scsidev->host), scsidev, cmd, arg); diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 75ab5ff6b78c..6085aa087a2f 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -3455,7 +3455,7 @@ static int delete_disk(struct aac_dev *dev, void __user *arg) } } -int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg) +int aac_dev_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg) { switch (cmd) { case FSACTL_QUERY_DISK: diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 3291d1c16864..1df5171594b8 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -2706,12 +2706,12 @@ void aac_set_intx_mode(struct aac_dev *dev); int aac_get_config_status(struct aac_dev *dev, int commit_flag); int aac_get_containers(struct aac_dev *dev); int aac_scsi_cmd(struct scsi_cmnd *cmd); -int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg); +int aac_dev_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg); #ifndef shost_to_class #define shost_to_class(shost) &shost->shost_dev #endif ssize_t aac_get_serial_number(struct device *dev, char *buf); -int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg); +int aac_do_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg); int aac_rx_init(struct aac_dev *dev); int aac_rkt_init(struct aac_dev *dev); int aac_nark_init(struct aac_dev *dev); diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index e2899ff7913e..f0ff40332753 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -1060,7 +1060,7 @@ static int aac_send_reset_adapter(struct aac_dev *dev, void __user *arg) return retval; } -int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg) +int aac_do_ioctl(struct aac_dev *dev, unsigned int cmd, void __user *arg) { int status; diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index dd701d5243b1..22ecacffeca6 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -616,7 +616,8 @@ static struct device_attribute *aac_dev_attrs[] = { NULL, }; -static int aac_ioctl(struct scsi_device *sdev, int cmd, void __user * arg) +static int aac_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg) { struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata; if (!capable(CAP_SYS_RAWIO)) @@ -1205,7 +1206,8 @@ static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long return ret; } -static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +static int aac_compat_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg) { struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata; if (!capable(CAP_SYS_RAWIO)) diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 8908a20065c8..4d90106fcb37 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -334,7 +334,8 @@ int cxlflash_afu_sync(struct afu *afu, ctx_hndl_t c, res_hndl_t r, u8 mode); void cxlflash_list_init(void); void cxlflash_term_global_luns(void); void cxlflash_free_errpage(void); -int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg); +int cxlflash_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg); void cxlflash_stop_term_user_contexts(struct cxlflash_cfg *cfg); int cxlflash_mark_contexts_error(struct cxlflash_cfg *cfg); void cxlflash_term_local_luns(struct cxlflash_cfg *cfg); diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index bfa13e3b191c..a0ea2dea7518 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -3282,7 +3282,7 @@ static int cxlflash_chr_open(struct inode *inode, struct file *file) * * Return: A string identifying the decoded host ioctl. */ -static char *decode_hioctl(int cmd) +static char *decode_hioctl(unsigned int cmd) { switch (cmd) { case HT_CXLFLASH_LUN_PROVISION: diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index acac6152f50b..1a94a469051e 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -1924,7 +1924,7 @@ out: * * Return: A string identifying the decoded ioctl. */ -static char *decode_ioctl(int cmd) +static char *decode_ioctl(unsigned int cmd) { switch (cmd) { case DK_CXLFLASH_ATTACH: @@ -2051,7 +2051,7 @@ err1: * * Return: 0 on success, -errno on failure */ -static int ioctl_common(struct scsi_device *sdev, int cmd) +static int ioctl_common(struct scsi_device *sdev, unsigned int cmd) { struct cxlflash_cfg *cfg = shost_priv(sdev->host); struct device *dev = &cfg->dev->dev; @@ -2096,7 +2096,7 @@ out: * * Return: 0 on success, -errno on failure */ -int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +int cxlflash_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg) { typedef int (*sioctl) (struct scsi_device *, void *); @@ -2179,8 +2179,7 @@ int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) } if (unlikely(copy_from_user(&buf, arg, size))) { - dev_err(dev, "%s: copy_from_user() fail " - "size=%lu cmd=%d (%s) arg=%p\n", + dev_err(dev, "%s: copy_from_user() fail size=%lu cmd=%u (%s) arg=%p\n", __func__, size, cmd, decode_ioctl(cmd), arg); rc = -EFAULT; goto cxlflash_ioctl_exit; @@ -2203,8 +2202,7 @@ int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) rc = do_ioctl(sdev, (void *)&buf); if (likely(!rc)) if (unlikely(copy_to_user(arg, &buf, size))) { - dev_err(dev, "%s: copy_to_user() fail " - "size=%lu cmd=%d (%s) arg=%p\n", + dev_err(dev, "%s: copy_to_user() fail size=%lu cmd=%u (%s) arg=%p\n", __func__, size, cmd, decode_ioctl(cmd), arg); rc = -EFAULT; } diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h index 858c3b33db78..7f43b95f4e94 100644 --- a/drivers/scsi/esas2r/esas2r.h +++ b/drivers/scsi/esas2r/esas2r.h @@ -965,8 +965,8 @@ struct esas2r_adapter { const char *esas2r_info(struct Scsi_Host *); int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq, struct esas2r_sas_nvram *data); -int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg); -int esas2r_ioctl(struct scsi_device *dev, int cmd, void __user *arg); +int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg); +int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg); u8 handle_hba_ioctl(struct esas2r_adapter *a, struct atto_ioctl *ioctl_hba); int esas2r_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd); diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c index 34bcc8c04ff4..3d130523c288 100644 --- a/drivers/scsi/esas2r/esas2r_ioctl.c +++ b/drivers/scsi/esas2r/esas2r_ioctl.c @@ -1274,7 +1274,7 @@ int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq, /* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */ -int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg) +int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg) { struct atto_express_ioctl *ioctl = NULL; struct esas2r_adapter *a; @@ -1292,9 +1292,8 @@ int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg) ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl)); if (IS_ERR(ioctl)) { esas2r_log(ESAS2R_LOG_WARN, - "ioctl_handler access_ok failed for cmd %d, " - "address %p", cmd, - arg); + "ioctl_handler access_ok failed for cmd %u, address %p", + cmd, arg); return PTR_ERR(ioctl); } @@ -1493,7 +1492,7 @@ int esas2r_ioctl_handler(void *hostdata, int cmd, void __user *arg) ioctl_done: if (err < 0) { - esas2r_log(ESAS2R_LOG_WARN, "err %d on ioctl cmd %d", err, + esas2r_log(ESAS2R_LOG_WARN, "err %d on ioctl cmd %u", err, cmd); switch (err) { @@ -1518,9 +1517,8 @@ ioctl_done: err = __copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl)); if (err != 0) { esas2r_log(ESAS2R_LOG_WARN, - "ioctl_handler copy_to_user didn't copy " - "everything (err %d, cmd %d)", err, - cmd); + "ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u)", + err, cmd); kfree(ioctl); return -EFAULT; @@ -1531,7 +1529,7 @@ ioctl_done: return 0; } -int esas2r_ioctl(struct scsi_device *sd, int cmd, void __user *arg) +int esas2r_ioctl(struct scsi_device *sd, unsigned int cmd, void __user *arg) { return esas2r_ioctl_handler(sd->host->hostdata, cmd, arg); } diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index 64397d441bae..fdbda5c05aa0 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -623,7 +623,7 @@ static int esas2r_proc_major; long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { return esas2r_ioctl_handler(esas2r_proc_host->hostdata, - (int)cmd, (void __user *)arg); + cmd, (void __user *)arg); } static void __exit esas2r_exit(void) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 5284444fdd10..f044e7d10d63 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -251,10 +251,11 @@ static int number_of_controllers; static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id); static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id); -static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg); +static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd, + void __user *arg); #ifdef CONFIG_COMPAT -static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, +static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg); #endif @@ -6127,7 +6128,7 @@ static void cmd_free(struct ctlr_info *h, struct CommandList *c) #ifdef CONFIG_COMPAT -static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, +static int hpsa_ioctl32_passthru(struct scsi_device *dev, unsigned int cmd, void __user *arg) { IOCTL32_Command_struct __user *arg32 = @@ -6164,7 +6165,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, } static int hpsa_ioctl32_big_passthru(struct scsi_device *dev, - int cmd, void __user *arg) + unsigned int cmd, void __user *arg) { BIG_IOCTL32_Command_struct __user *arg32 = (BIG_IOCTL32_Command_struct __user *) arg; @@ -6201,7 +6202,8 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev, return err; } -static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void __user *arg) +static int hpsa_compat_ioctl(struct scsi_device *dev, unsigned int cmd, + void __user *arg) { switch (cmd) { case CCISS_GETPCIINFO: @@ -6521,7 +6523,8 @@ static void check_ioctl_unit_attention(struct ctlr_info *h, /* * ioctl */ -static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg) +static int hpsa_ioctl(struct scsi_device *dev, unsigned int cmd, + void __user *arg) { struct ctlr_info *h; void __user *argp = (void __user *)arg; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index d1b4025a4503..6d053e220153 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6696,7 +6696,8 @@ err_nodev: * Return value: * 0 on success / other on failure **/ -static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +static int ipr_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg) { struct ipr_resource_entry *res; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index c43a00a9d819..b775445892af 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -799,7 +799,7 @@ out: shost->host_failed, tries); } -int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +int sas_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg) { struct domain_device *dev = sdev_to_domain_dev(sdev); diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 8044bb08455d..c5014e9f4a50 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -832,7 +832,8 @@ static void mk_sense_invalid_opcode(struct scsi_cmnd *scp) mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0); } -static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) +static int scsi_debug_ioctl(struct scsi_device *dev, unsigned int cmd, + void __user *arg) { if (sdebug_verbose) { if (0x1261 == cmd) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index e2fa3f476227..f6eaea2eadd1 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -6043,7 +6043,8 @@ out: return rc; } -static int pqi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg) { int rc; struct pqi_ctrl_info *ctrl_info; diff --git a/include/linux/libata.h b/include/linux/libata.h index 68133842e6d7..c9419c05a90a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1122,10 +1122,11 @@ extern int ata_host_activate(struct ata_host *host, int irq, extern void ata_host_detach(struct ata_host *host); extern void ata_host_init(struct ata_host *, struct device *, struct ata_port_operations *); extern int ata_scsi_detect(struct scsi_host_template *sht); -extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); +extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, + void __user *arg); extern int ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev, - int cmd, void __user *arg); + unsigned int cmd, void __user *arg); extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 857086cf7ebf..56b2dba7d911 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -707,7 +707,8 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd); extern void sas_target_destroy(struct scsi_target *); extern int sas_slave_alloc(struct scsi_device *); -extern int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg); +extern int sas_ioctl(struct scsi_device *sdev, unsigned int cmd, + void __user *arg); extern int sas_drain_work(struct sas_ha_struct *ha); extern void sas_ssp_task_response(struct device *dev, struct sas_task *task, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 6ca954e9f752..4047d68d1b08 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -60,7 +60,8 @@ struct scsi_host_template { * * Status: OPTIONAL */ - int (* ioctl)(struct scsi_device *dev, int cmd, void __user *arg); + int (*ioctl)(struct scsi_device *dev, unsigned int cmd, + void __user *arg); #ifdef CONFIG_COMPAT @@ -70,7 +71,8 @@ struct scsi_host_template { * * Status: OPTIONAL */ - int (* compat_ioctl)(struct scsi_device *dev, int cmd, void __user *arg); + int (*compat_ioctl)(struct scsi_device *dev, unsigned int cmd, + void __user *arg); #endif /* From 1ace9f00eca6d1e40aea01489a8553f0afb362a0 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 12 Oct 2018 14:39:26 -0700 Subject: [PATCH 1426/4277] scsi: dt-bindings: ufs: Fix the compatible string definition If you look at the bindings for the UFS Host Controller it says: - compatible: must contain "jedec,ufs-1.1" or "jedec,ufs-2.0", may also list one or more of the following: "qcom,msm8994-ufshc" "qcom,msm8996-ufshc" "qcom,ufshc" My reading of that is that it's fine to just have either of these: 1. "qcom,msm8996-ufshc", "jedec,ufs-2.0" 2. "qcom,ufshc", "jedec,ufs-2.0" As far as I can tell neither of the above is actually a good idea. For #1 it turns out that the driver currently only keys off the compatible string "qcom,ufshc" so it won't actually probe. For #2 the driver won't probe but it's not a good idea to keep the SoC name out of the compatible string. Let's update the compatible string to make it really explicit. We'll include a nod to the existing driver and the old binding and say that we should always include the "qcom,ufshc" string in addition to the SoC compatible string. While we're at it we'll also include another example SoC known to have UFS: sdm845. Fixes: 47555a5c8a11 ("scsi: ufs: make the UFS variant a platform device") Signed-off-by: Douglas Anderson Reviewed-by: Vivek Gautam Reviewed-by: Rob Herring Signed-off-by: Martin K. Petersen --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 8cf59452c675..5111e9130bc3 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -4,11 +4,14 @@ UFSHC nodes are defined to describe on-chip UFS host controllers. Each UFS controller instance should have its own node. Required properties: -- compatible : must contain "jedec,ufs-1.1" or "jedec,ufs-2.0", may - also list one or more of the following: - "qcom,msm8994-ufshc" - "qcom,msm8996-ufshc" - "qcom,ufshc" +- compatible : must contain "jedec,ufs-1.1" or "jedec,ufs-2.0" + + For Qualcomm SoCs must contain, as below, an + SoC-specific compatible along with "qcom,ufshc" and + the appropriate jedec string: + "qcom,msm8994-ufshc", "qcom,ufshc", "jedec,ufs-2.0" + "qcom,msm8996-ufshc", "qcom,ufshc", "jedec,ufs-2.0" + "qcom,sdm845-ufshc", "qcom,ufshc", "jedec,ufs-2.0" - interrupts : - reg : From b3cce125cb1e2eb2683e1c785fdc409a4148157d Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 6 Feb 2019 18:52:51 +0800 Subject: [PATCH 1427/4277] scsi: hisi_sas: Add support for DIX feature for v3 hw This patch adds support for DIX to v3 hw driver. For this, we build upon support for DIF, most significantly is adding new DMA map and unmap paths. Some pre-existing macro precedence issues are also tidied. They were detected by checkpatch --strict. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 40 +++++++--- drivers/scsi/hisi_sas/hisi_sas_main.c | 100 ++++++++++++++++++++++--- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 80 +++++++++++++++++--- 3 files changed, 186 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 9b35f84b5029..df2a92a0e7a1 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -41,20 +41,25 @@ #define HISI_SAS_COMMAND_TABLE_SZ (sizeof(union hisi_sas_command_table)) #define hisi_sas_status_buf_addr(buf) \ - (buf + offsetof(struct hisi_sas_slot_buf_table, status_buffer)) -#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr(slot->buf) + ((buf) + offsetof(struct hisi_sas_slot_buf_table, status_buffer)) +#define hisi_sas_status_buf_addr_mem(slot) hisi_sas_status_buf_addr((slot)->buf) #define hisi_sas_status_buf_addr_dma(slot) \ - hisi_sas_status_buf_addr(slot->buf_dma) + hisi_sas_status_buf_addr((slot)->buf_dma) #define hisi_sas_cmd_hdr_addr(buf) \ - (buf + offsetof(struct hisi_sas_slot_buf_table, command_header)) -#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr(slot->buf) -#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr(slot->buf_dma) + ((buf) + offsetof(struct hisi_sas_slot_buf_table, command_header)) +#define hisi_sas_cmd_hdr_addr_mem(slot) hisi_sas_cmd_hdr_addr((slot)->buf) +#define hisi_sas_cmd_hdr_addr_dma(slot) hisi_sas_cmd_hdr_addr((slot)->buf_dma) #define hisi_sas_sge_addr(buf) \ - (buf + offsetof(struct hisi_sas_slot_buf_table, sge_page)) -#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr(slot->buf) -#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr(slot->buf_dma) + ((buf) + offsetof(struct hisi_sas_slot_buf_table, sge_page)) +#define hisi_sas_sge_addr_mem(slot) hisi_sas_sge_addr((slot)->buf) +#define hisi_sas_sge_addr_dma(slot) hisi_sas_sge_addr((slot)->buf_dma) + +#define hisi_sas_sge_dif_addr(buf) \ + ((buf) + offsetof(struct hisi_sas_slot_dif_buf_table, sge_dif_page)) +#define hisi_sas_sge_dif_addr_mem(slot) hisi_sas_sge_dif_addr((slot)->buf) +#define hisi_sas_sge_dif_addr_dma(slot) hisi_sas_sge_dif_addr((slot)->buf_dma) #define HISI_SAS_MAX_SSP_RESP_SZ (sizeof(struct ssp_frame_hdr) + 1024) #define HISI_SAS_MAX_SMP_RESP_SZ 1028 @@ -74,7 +79,11 @@ SHOST_DIF_TYPE2_PROTECTION | \ SHOST_DIF_TYPE3_PROTECTION) -#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK) +#define HISI_SAS_DIX_PROT_MASK (SHOST_DIX_TYPE1_PROTECTION | \ + SHOST_DIX_TYPE2_PROTECTION | \ + SHOST_DIX_TYPE3_PROTECTION) + +#define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK | HISI_SAS_DIX_PROT_MASK) #define HISI_SAS_WAIT_PHYUP_TIMEOUT 20 @@ -201,6 +210,7 @@ struct hisi_sas_slot { struct sas_task *task; struct hisi_sas_port *port; u64 n_elem; + u64 n_elem_dif; int dlvry_queue; int dlvry_queue_slot; int cmplt_queue; @@ -464,6 +474,11 @@ struct hisi_sas_sge_page { struct hisi_sas_sge sge[HISI_SAS_SGE_PAGE_CNT]; } __aligned(16); +#define HISI_SAS_SGE_DIF_PAGE_CNT SG_CHUNK_SIZE +struct hisi_sas_sge_dif_page { + struct hisi_sas_sge sge[HISI_SAS_SGE_DIF_PAGE_CNT]; +} __aligned(16); + struct hisi_sas_command_table_ssp { struct ssp_frame_hdr hdr; union { @@ -494,6 +509,11 @@ struct hisi_sas_slot_buf_table { struct hisi_sas_sge_page sge_page; }; +struct hisi_sas_slot_dif_buf_table { + struct hisi_sas_slot_buf_table slot_buf; + struct hisi_sas_sge_dif_page sge_dif_page; +}; + extern struct scsi_transport_template *hisi_sas_stt; extern bool hisi_sas_debugfs_enable; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 2d6b5fe90a9e..1a3d31d4258c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -252,11 +252,19 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, task->lldd_task = NULL; - if (!sas_protocol_ata(task->task_proto)) + if (!sas_protocol_ata(task->task_proto)) { + struct sas_ssp_task *ssp_task = &task->ssp_task; + struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; + if (slot->n_elem) dma_unmap_sg(dev, task->scatter, task->num_scatter, task->data_dir); + if (slot->n_elem_dif) + dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), + task->data_dir); + } } @@ -380,6 +388,59 @@ prep_out: return rc; } +static void hisi_sas_dif_dma_unmap(struct hisi_hba *hisi_hba, + struct sas_task *task, int n_elem_dif) +{ + struct device *dev = hisi_hba->dev; + + if (n_elem_dif) { + struct sas_ssp_task *ssp_task = &task->ssp_task; + struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; + + dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), + task->data_dir); + } +} + +static int hisi_sas_dif_dma_map(struct hisi_hba *hisi_hba, + int *n_elem_dif, struct sas_task *task) +{ + struct device *dev = hisi_hba->dev; + struct sas_ssp_task *ssp_task; + struct scsi_cmnd *scsi_cmnd; + int rc; + + if (task->num_scatter) { + ssp_task = &task->ssp_task; + scsi_cmnd = ssp_task->cmd; + + if (scsi_prot_sg_count(scsi_cmnd)) { + *n_elem_dif = dma_map_sg(dev, + scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), + task->data_dir); + + if (!*n_elem_dif) + return -ENOMEM; + + if (*n_elem_dif > HISI_SAS_SGE_DIF_PAGE_CNT) { + dev_err(dev, "task prep: n_elem_dif(%d) too large\n", + *n_elem_dif); + rc = -EINVAL; + goto err_out_dif_dma_unmap; + } + } + } + + return 0; + +err_out_dif_dma_unmap: + dma_unmap_sg(dev, scsi_prot_sglist(scsi_cmnd), + scsi_prot_sg_count(scsi_cmnd), task->data_dir); + return rc; +} + static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq **dq_pointer, bool is_tmf, struct hisi_sas_tmf_task *tmf, @@ -394,7 +455,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct asd_sas_port *sas_port = device->port; struct device *dev = hisi_hba->dev; int dlvry_queue_slot, dlvry_queue, rc, slot_idx; - int n_elem = 0, n_elem_req = 0, n_elem_resp = 0; + int n_elem = 0, n_elem_dif = 0, n_elem_req = 0, n_elem_resp = 0; struct hisi_sas_dq *dq; unsigned long flags; int wr_q_index; @@ -427,6 +488,12 @@ static int hisi_sas_task_prep(struct sas_task *task, if (rc < 0) goto prep_out; + if (!sas_protocol_ata(task->task_proto)) { + rc = hisi_sas_dif_dma_map(hisi_hba, &n_elem_dif, task); + if (rc < 0) + goto err_out_dma_unmap; + } + if (hisi_hba->hw->slot_index_alloc) rc = hisi_hba->hw->slot_index_alloc(hisi_hba, device); else { @@ -445,7 +512,7 @@ static int hisi_sas_task_prep(struct sas_task *task, rc = hisi_sas_slot_index_alloc(hisi_hba, scsi_cmnd); } if (rc < 0) - goto err_out_dma_unmap; + goto err_out_dif_dma_unmap; slot_idx = rc; slot = &hisi_hba->slot_info[slot_idx]; @@ -466,6 +533,7 @@ static int hisi_sas_task_prep(struct sas_task *task, dlvry_queue_slot = wr_q_index; slot->n_elem = n_elem; + slot->n_elem_dif = n_elem_dif; slot->dlvry_queue = dlvry_queue; slot->dlvry_queue_slot = dlvry_queue_slot; cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue]; @@ -509,6 +577,9 @@ static int hisi_sas_task_prep(struct sas_task *task, err_out_tag: hisi_sas_slot_index_free(hisi_hba, slot_idx); +err_out_dif_dma_unmap: + if (!sas_protocol_ata(task->task_proto)) + hisi_sas_dif_dma_unmap(hisi_hba, task, n_elem_dif); err_out_dma_unmap: hisi_sas_dma_unmap(hisi_hba, task, n_elem, n_elem_req, n_elem_resp); @@ -2174,19 +2245,24 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba) /* roundup to avoid overly large block size */ max_command_entries_ru = roundup(max_command_entries, 64); - sz_slot_buf_ru = roundup(sizeof(struct hisi_sas_slot_buf_table), 64); + if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK) + sz_slot_buf_ru = sizeof(struct hisi_sas_slot_dif_buf_table); + else + sz_slot_buf_ru = sizeof(struct hisi_sas_slot_buf_table); + sz_slot_buf_ru = roundup(sz_slot_buf_ru, 64); s = lcm(max_command_entries_ru, sz_slot_buf_ru); blk_cnt = (max_command_entries_ru * sz_slot_buf_ru) / s; slots_per_blk = s / sz_slot_buf_ru; - for (i = 0; i < blk_cnt; i++) { - struct hisi_sas_slot_buf_table *buf; - dma_addr_t buf_dma; - int slot_index = i * slots_per_blk; - buf = dmam_alloc_coherent(dev, s, &buf_dma, GFP_KERNEL); + for (i = 0; i < blk_cnt; i++) { + int slot_index = i * slots_per_blk; + dma_addr_t buf_dma; + void *buf; + + buf = dmam_alloc_coherent(dev, s, &buf_dma, + GFP_KERNEL | __GFP_ZERO); if (!buf) goto err_out; - memset(buf, 0, s); for (j = 0; j < slots_per_blk; j++, slot_index++) { struct hisi_sas_slot *slot; @@ -2196,8 +2272,8 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba) slot->buf_dma = buf_dma; slot->idx = slot_index; - buf++; - buf_dma += sizeof(*buf); + buf += sz_slot_buf_ru; + buf_dma += sz_slot_buf_ru; } } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 108683b0a049..11fc6501843f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -399,6 +399,8 @@ struct hisi_sas_err_record_v3 { #define USR_DATA_BLOCK_SZ_OFF 20 #define USR_DATA_BLOCK_SZ_MSK (0x3 << USR_DATA_BLOCK_SZ_OFF) #define T10_CHK_MSK_OFF 16 +#define T10_CHK_REF_TAG_MSK (0xf0 << T10_CHK_MSK_OFF) +#define T10_CHK_APP_TAG_MSK (0xc << T10_CHK_MSK_OFF) static bool hisi_sas_intr_conv; MODULE_PARM_DESC(intr_conv, "interrupt converge enable (0-1)"); @@ -969,19 +971,44 @@ static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba, hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot)); - hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF); + hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF); +} + +static void prep_prd_sge_dif_v3_hw(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + struct hisi_sas_cmd_hdr *hdr, + struct scatterlist *scatter, + int n_elem) +{ + struct hisi_sas_sge_dif_page *sge_dif_page; + struct scatterlist *sg; + int i; + + sge_dif_page = hisi_sas_sge_dif_addr_mem(slot); + + for_each_sg(scatter, sg, n_elem, i) { + struct hisi_sas_sge *entry = &sge_dif_page->sge[i]; + + entry->addr = cpu_to_le64(sg_dma_address(sg)); + entry->page_ctrl_0 = 0; + entry->page_ctrl_1 = 0; + entry->data_len = cpu_to_le32(sg_dma_len(sg)); + entry->data_off = 0; + } + + hdr->dif_prd_table_addr = + cpu_to_le64(hisi_sas_sge_dif_addr_dma(slot)); + + hdr->sg_len |= cpu_to_le32(n_elem << CMD_HDR_DIF_SGL_LEN_OFF); } static u32 get_prot_chk_msk_v3_hw(struct scsi_cmnd *scsi_cmnd) { unsigned char prot_flags = scsi_cmnd->prot_flags; - if (prot_flags & SCSI_PROT_TRANSFER_PI) { - if (prot_flags & SCSI_PROT_REF_CHECK) - return 0xc << 16; - return 0xfc << 16; - } - return 0; + if (prot_flags & SCSI_PROT_REF_CHECK) + return T10_CHK_APP_TAG_MSK; + return T10_CHK_REF_TAG_MSK | T10_CHK_APP_TAG_MSK; } static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd, @@ -992,15 +1019,33 @@ static void fill_prot_v3_hw(struct scsi_cmnd *scsi_cmnd, u32 lbrt_chk_val = t10_pi_ref_tag(scsi_cmnd->request); switch (prot_op) { + case SCSI_PROT_READ_INSERT: + prot->dw0 |= T10_INSRT_EN_MSK; + prot->lbrtgv = lbrt_chk_val; + break; case SCSI_PROT_READ_STRIP: prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK); prot->lbrtcv = lbrt_chk_val; prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd); break; + case SCSI_PROT_READ_PASS: + prot->dw0 |= T10_CHK_EN_MSK; + prot->lbrtcv = lbrt_chk_val; + prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd); + break; case SCSI_PROT_WRITE_INSERT: prot->dw0 |= T10_INSRT_EN_MSK; prot->lbrtgv = lbrt_chk_val; break; + case SCSI_PROT_WRITE_STRIP: + prot->dw0 |= (T10_RMV_EN_MSK | T10_CHK_EN_MSK); + prot->lbrtcv = lbrt_chk_val; + break; + case SCSI_PROT_WRITE_PASS: + prot->dw0 |= T10_CHK_EN_MSK; + prot->lbrtcv = lbrt_chk_val; + prot->dw4 |= get_prot_chk_msk_v3_hw(scsi_cmnd); + break; default: WARN(1, "prot_op(0x%x) is not valid\n", prot_op); break; @@ -1077,9 +1122,15 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, hdr->dw2 = cpu_to_le32(dw2); hdr->transfer_tags = cpu_to_le32(slot->idx); - if (has_data) + if (has_data) { prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter, - slot->n_elem); + slot->n_elem); + + if (scsi_prot_sg_count(scsi_cmnd)) + prep_prd_sge_dif_v3_hw(hisi_hba, slot, hdr, + scsi_prot_sglist(scsi_cmnd), + slot->n_elem_dif); + } hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot)); hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); @@ -1120,18 +1171,19 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, fill_prot_v3_hw(scsi_cmnd, &prot); memcpy(buf_cmd_prot, &prot, sizeof(struct hisi_sas_protect_iu_v3_hw)); - /* * For READ, we need length of info read to memory, while for * WRITE we need length of data written to the disk. */ - if (prot_op == SCSI_PROT_WRITE_INSERT) { + if (prot_op == SCSI_PROT_WRITE_INSERT || + prot_op == SCSI_PROT_READ_INSERT || + prot_op == SCSI_PROT_WRITE_PASS || + prot_op == SCSI_PROT_READ_PASS) { unsigned int interval = scsi_prot_interval(scsi_cmnd); unsigned int ilog2_interval = ilog2(interval); len = (task->total_xfer_len >> ilog2_interval) * 8; } - } hdr->dw1 = cpu_to_le32(dw1); @@ -2526,6 +2578,7 @@ static struct scsi_host_template sht_v3_hw = { .bios_param = sas_bios_param, .this_id = -1, .sg_tablesize = HISI_SAS_SGE_PAGE_CNT, + .sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_target_reset_handler = sas_eh_target_reset_handler, @@ -2713,6 +2766,9 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_info(dev, "Registering for DIF/DIX prot_mask=0x%x\n", prot_mask); scsi_host_set_prot(hisi_hba->shost, prot_mask); + if (hisi_hba->prot_mask & HISI_SAS_DIX_PROT_MASK) + scsi_host_set_guard(hisi_hba->shost, + SHOST_DIX_GUARD_CRC); } scsi_scan_host(shost); From 7c5e136363919d8dfa27d4bcd4b3ecedfa234997 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Wed, 6 Feb 2019 18:52:52 +0800 Subject: [PATCH 1428/4277] scsi: hisi_sas: Add manual trigger for debugfs dump Add an interface to manually trigger a debugfs dump. Signed-off-by: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 1a3d31d4258c..35619bdad1d5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2977,6 +2977,36 @@ static void hisi_sas_debugfs_snapshot_regs(struct hisi_hba *hisi_hba) hisi_hba->hw->snapshot_restore(hisi_hba); } +static ssize_t hisi_sas_debugfs_trigger_dump_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hisi_hba *hisi_hba = file->f_inode->i_private; + char buf[8]; + + /* A bit racy, but don't care too much since it's only debugfs */ + if (hisi_hba->debugfs_snapshot) + return -EFAULT; + + if (count > 8) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (buf[0] != '1') + return -EFAULT; + + queue_work(hisi_hba->wq, &hisi_hba->debugfs_work); + + return count; +} + +static const struct file_operations hisi_sas_debugfs_trigger_dump_fops = { + .write = &hisi_sas_debugfs_trigger_dump_write, + .owner = THIS_MODULE, +}; + void hisi_sas_debugfs_work_handler(struct work_struct *work) { struct hisi_hba *hisi_hba = @@ -2999,6 +3029,10 @@ void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba) hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev), hisi_sas_debugfs_dir); + debugfs_create_file("trigger_dump", 0600, + hisi_hba->debugfs_dir, + hisi_hba, + &hisi_sas_debugfs_trigger_dump_fops); /* Alloc buffer for global */ sz = hisi_hba->hw->debugfs_reg_global->count * 4; From 1273d65f29045df915ff1448672cca22f7c078e6 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 6 Feb 2019 18:52:53 +0800 Subject: [PATCH 1429/4277] scsi: hisi_sas: change queue depth from 512 to 4096 If sending IOs to many disks from single queue, it is possible that the queue may be full. To avoid the situation, change queue depth from 512 to 4096 which is the max number of IOs for v3 hw. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index df2a92a0e7a1..ff844d4ba0d3 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -30,7 +30,7 @@ #define HISI_SAS_MAX_PHYS 9 #define HISI_SAS_MAX_QUEUES 32 -#define HISI_SAS_QUEUE_SLOTS 512 +#define HISI_SAS_QUEUE_SLOTS 4096 #define HISI_SAS_MAX_ITCT_ENTRIES 1024 #define HISI_SAS_MAX_DEVICES HISI_SAS_MAX_ITCT_ENTRIES #define HISI_SAS_RESET_BIT 0 From 795f25a31b542df93bff892ce46390f9befa2135 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 6 Feb 2019 18:52:54 +0800 Subject: [PATCH 1430/4277] scsi: hisi_sas: Issue internal abort on all relevant queues To support queue mapped to a CPU, it needs to be ensured that issuing an internal abort is safe, in that it is guaranteed that an internal abort is processed for a single IO or a device after all the relevant command(s) which it is attempting to abort have been processed by the controller. Currently we only deliver commands for any device on a single queue to solve this problem, as we know that commands issued on the same queue will be processed in order, and we will not have a scenario where the internal abort is racing against a command(s) which it is trying to abort. To enqueue commands on queue mapped to a CPU, choosing a queue for an command is based on the associated queue for the current CPU, so this is not safe for internal abort since it would definitely not be guaranteed that commands for the command devices are issued on the same queue. To solve this issue, we take a bludgeoning approach, and issue a separate internal abort on any queue(s) relevant to the command or device, in that we will be guaranteed that at least one of these internal aborts will be received last in the controller. So, for aborting a single command, we can just force the internal abort to be issued on the same queue as the command which we are trying to abort. For aborting all commands associated with a device, we issue a separate internal abort on all relevant queues. Issuing multiple internal aborts in this fashion would have not side affect. Signed-off-by: John Garry Signed-off-by: Xiang Chen Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 + drivers/scsi/hisi_sas/hisi_sas_main.c | 64 ++++++++++++++++++++------ drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 2 + drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 2 + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 8 +++- 5 files changed, 62 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index ff844d4ba0d3..5accad05a86f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -365,6 +365,8 @@ struct hisi_hba { u32 intr_coal_ticks; /* Time of interrupt coalesce in us */ u32 intr_coal_count; /* Interrupt count to coalesce */ + int cq_nvecs; + /* debugfs memories */ u32 *debugfs_global_reg; u32 *debugfs_port_reg[HISI_SAS_MAX_PHYS]; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 35619bdad1d5..40a402f09afb 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1023,7 +1023,7 @@ static void hisi_sas_dev_gone(struct domain_device *device) if (!test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) { hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + HISI_SAS_INT_ABT_DEV, 0); hisi_sas_dereg_device(hisi_hba, device); @@ -1630,7 +1630,8 @@ static int hisi_sas_abort_task(struct sas_task *task) task->task_proto & SAS_PROTOCOL_STP) { if (task->dev->dev_type == SAS_SATA_DEV) { rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + HISI_SAS_INT_ABT_DEV, + 0); if (rc < 0) { dev_err(dev, "abort task: internal abort failed\n"); goto out; @@ -1645,7 +1646,7 @@ static int hisi_sas_abort_task(struct sas_task *task) struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue]; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_CMD, tag); + HISI_SAS_INT_ABT_CMD, tag); if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && task->lldd_task) { /* @@ -1671,7 +1672,7 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) int rc = TMF_RESP_FUNC_FAILED; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + HISI_SAS_INT_ABT_DEV, 0); if (rc < 0) { dev_err(dev, "abort task set: internal abort rc=%d\n", rc); return TMF_RESP_FUNC_FAILED; @@ -1743,7 +1744,7 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) int rc = TMF_RESP_FUNC_FAILED; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + HISI_SAS_INT_ABT_DEV, 0); if (rc < 0) { dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc); return TMF_RESP_FUNC_FAILED; @@ -1788,7 +1789,7 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) struct hisi_sas_tmf_task tmf_task = { .tmf = TMF_LU_RESET }; rc = hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + HISI_SAS_INT_ABT_DEV, 0); if (rc < 0) { dev_err(dev, "lu_reset: internal abort failed\n"); goto out; @@ -1874,7 +1875,7 @@ static int hisi_sas_query_task(struct sas_task *task) static int hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, struct sas_task *task, int abort_flag, - int task_tag) + int task_tag, struct hisi_sas_dq *dq) { struct domain_device *device = task->dev; struct hisi_sas_device *sas_dev = device->lldd_dev; @@ -1883,7 +1884,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, struct hisi_sas_slot *slot; struct asd_sas_port *sas_port = device->port; struct hisi_sas_cmd_hdr *cmd_hdr_base; - struct hisi_sas_dq *dq = sas_dev->dq; int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx; unsigned long flags, flags_dq = 0; int wr_q_index; @@ -1955,18 +1955,19 @@ err_out: } /** - * hisi_sas_internal_task_abort -- execute an internal + * _hisi_sas_internal_task_abort -- execute an internal * abort command for single IO command or a device * @hisi_hba: host controller struct * @device: domain device * @abort_flag: mode of operation, device or single IO * @tag: tag of IO to be aborted (only relevant to single * IO mode) + * @dq: delivery queue for this internal abort command */ static int -hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, - struct domain_device *device, - int abort_flag, int tag) +_hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, int abort_flag, + int tag, struct hisi_sas_dq *dq) { struct sas_task *task; struct hisi_sas_device *sas_dev = device->lldd_dev; @@ -1994,7 +1995,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, add_timer(&task->slow_task->timer); res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id, - task, abort_flag, tag); + task, abort_flag, tag, dq); if (res) { del_timer(&task->slow_task->timer); dev_err(dev, "internal task abort: executing internal task failed: %d\n", @@ -2051,6 +2052,41 @@ exit: return res; } +static int +hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, + int abort_flag, int tag) +{ + struct hisi_sas_slot *slot; + struct device *dev = hisi_hba->dev; + struct hisi_sas_dq *dq; + int i, rc; + + switch (abort_flag) { + case HISI_SAS_INT_ABT_CMD: + slot = &hisi_hba->slot_info[tag]; + dq = &hisi_hba->dq[slot->dlvry_queue]; + return _hisi_sas_internal_task_abort(hisi_hba, device, + abort_flag, tag, dq); + case HISI_SAS_INT_ABT_DEV: + for (i = 0; i < hisi_hba->cq_nvecs; i++) { + dq = &hisi_hba->dq[i]; + rc = _hisi_sas_internal_task_abort(hisi_hba, device, + abort_flag, tag, + dq); + if (rc) + return rc; + } + break; + default: + dev_err(dev, "Unrecognised internal abort flag (%d)\n", + abort_flag); + return -EINVAL; + } + + return 0; +} + static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) { hisi_sas_port_notify_formed(sas_phy); @@ -2117,7 +2153,7 @@ void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba) { int i; - for (i = 0; i < hisi_hba->queue_count; i++) { + for (i = 0; i < hisi_hba->cq_nvecs; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; tasklet_kill(&cq->tasklet); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index c8f808faed95..293807443480 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1749,6 +1749,8 @@ static int interrupt_init_v1_hw(struct hisi_hba *hisi_hba) } } + hisi_hba->cq_nvecs = hisi_hba->queue_count; + return 0; } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index aa90f17a1cac..39233a9cd1f5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3400,6 +3400,8 @@ static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba) tasklet_init(t, cq_tasklet_v2_hw, (unsigned long)cq); } + hisi_hba->cq_nvecs = hisi_hba->queue_count; + return 0; free_cq_int_irqs: diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 11fc6501843f..79762932f5e0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -402,6 +402,8 @@ struct hisi_sas_err_record_v3 { #define T10_CHK_REF_TAG_MSK (0xf0 << T10_CHK_MSK_OFF) #define T10_CHK_APP_TAG_MSK (0xc << T10_CHK_MSK_OFF) +#define BASE_VECTORS_V3_HW 16 + static bool hisi_sas_intr_conv; MODULE_PARM_DESC(intr_conv, "interrupt converge enable (0-1)"); @@ -2050,6 +2052,8 @@ static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba) return -ENOENT; } + hisi_hba->cq_nvecs = vectors - BASE_VECTORS_V3_HW; + rc = devm_request_irq(dev, pci_irq_vector(pdev, 1), int_phy_up_down_bcast_v3_hw, 0, DRV_NAME " phy", hisi_hba); @@ -2078,7 +2082,7 @@ static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba) } /* Init tasklets for cq only */ - for (i = 0; i < hisi_hba->queue_count; i++) { + for (i = 0; i < hisi_hba->cq_nvecs; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; struct tasklet_struct *t = &cq->tasklet; int nr = hisi_sas_intr_conv ? 16 : 16 + i; @@ -2795,7 +2799,7 @@ hisi_sas_v3_destroy_irqs(struct pci_dev *pdev, struct hisi_hba *hisi_hba) free_irq(pci_irq_vector(pdev, 1), hisi_hba); free_irq(pci_irq_vector(pdev, 2), hisi_hba); free_irq(pci_irq_vector(pdev, 11), hisi_hba); - for (i = 0; i < hisi_hba->queue_count; i++) { + for (i = 0; i < hisi_hba->cq_nvecs; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; int nr = hisi_sas_intr_conv ? 16 : 16 + i; From 4fefe5bbf599d6c6bee6b2ee376be789b33ca571 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 6 Feb 2019 18:52:55 +0800 Subject: [PATCH 1431/4277] scsi: hisi_sas: Use pci_irq_get_affinity() for v3 hw as experimental For auto-control irq affinity mode, choose the dq to deliver IO according to the current CPU. Then it decreases the performance regression that fio and CQ interrupts are processed on different node. For user control irq affinity mode, keep it as before. To realize it, also need to distinguish the usage of dq lock and sas_dev lock. We mark as experimental due to ongoing discussion on managed MSI IRQ during hotplug: https://marc.info/?l=linux-scsi&m=154876335707751&w=2 We're almost at the point where we can expose multiple queues to the upper layer for SCSI MQ, but we need to sort out the per-HBA tags performance issue. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 4 ++ drivers/scsi/hisi_sas/hisi_sas_main.c | 33 +++++++++++--- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 1 + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 63 +++++++++++++++++++++++--- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 5accad05a86f..6c87bd34509a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -173,6 +173,7 @@ struct hisi_sas_port { struct hisi_sas_cq { struct hisi_hba *hisi_hba; + const struct cpumask *pci_irq_mask; struct tasklet_struct tasklet; int rd_point; int id; @@ -195,6 +196,7 @@ struct hisi_sas_device { enum sas_device_type dev_type; int device_id; int sata_idx; + spinlock_t lock; /* For protecting slots */ }; struct hisi_sas_tmf_task { @@ -217,6 +219,7 @@ struct hisi_sas_slot { int cmplt_queue_slot; int abort; int ready; + int device_id; void *cmd_hdr; dma_addr_t cmd_hdr_dma; struct timer_list internal_abort_timer; @@ -366,6 +369,7 @@ struct hisi_hba { u32 intr_coal_count; /* Interrupt count to coalesce */ int cq_nvecs; + unsigned int *reply_map; /* debugfs memories */ u32 *debugfs_global_reg; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 40a402f09afb..eff31472b96e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -241,8 +241,9 @@ static void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba) void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, struct hisi_sas_slot *slot) { - struct hisi_sas_dq *dq = &hisi_hba->dq[slot->dlvry_queue]; unsigned long flags; + int device_id = slot->device_id; + struct hisi_sas_device *sas_dev = &hisi_hba->devices[device_id]; if (task) { struct device *dev = hisi_hba->dev; @@ -267,10 +268,9 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, } } - - spin_lock_irqsave(&dq->lock, flags); + spin_lock_irqsave(&sas_dev->lock, flags); list_del_init(&slot->entry); - spin_unlock_irqrestore(&dq->lock, flags); + spin_unlock_irqrestore(&sas_dev->lock, flags); memset(slot, 0, offsetof(struct hisi_sas_slot, buf)); @@ -471,7 +471,14 @@ static int hisi_sas_task_prep(struct sas_task *task, return -ECOMM; } - *dq_pointer = dq = sas_dev->dq; + if (hisi_hba->reply_map) { + int cpu = raw_smp_processor_id(); + unsigned int dq_index = hisi_hba->reply_map[cpu]; + + *dq_pointer = dq = &hisi_hba->dq[dq_index]; + } else { + *dq_pointer = dq = sas_dev->dq; + } port = to_hisi_sas_port(sas_port); if (port && !port->port_attached) { @@ -526,12 +533,15 @@ static int hisi_sas_task_prep(struct sas_task *task, } list_add_tail(&slot->delivery, &dq->list); - list_add_tail(&slot->entry, &sas_dev->list); spin_unlock_irqrestore(&dq->lock, flags); + spin_lock_irqsave(&sas_dev->lock, flags); + list_add_tail(&slot->entry, &sas_dev->list); + spin_unlock_irqrestore(&sas_dev->lock, flags); dlvry_queue = dq->id; dlvry_queue_slot = wr_q_index; + slot->device_id = sas_dev->device_id; slot->n_elem = n_elem; slot->n_elem_dif = n_elem_dif; slot->dlvry_queue = dlvry_queue; @@ -701,6 +711,7 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) sas_dev->hisi_hba = hisi_hba; sas_dev->sas_device = device; sas_dev->dq = dq; + spin_lock_init(&sas_dev->lock); INIT_LIST_HEAD(&hisi_hba->devices[i].list); break; } @@ -1913,10 +1924,14 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, } list_add_tail(&slot->delivery, &dq->list); spin_unlock_irqrestore(&dq->lock, flags_dq); + spin_lock_irqsave(&sas_dev->lock, flags); + list_add_tail(&slot->entry, &sas_dev->list); + spin_unlock_irqrestore(&sas_dev->lock, flags); dlvry_queue = dq->id; dlvry_queue_slot = wr_q_index; + slot->device_id = sas_dev->device_id; slot->n_elem = n_elem; slot->dlvry_queue = dlvry_queue; slot->dlvry_queue_slot = dlvry_queue_slot; @@ -1940,7 +1955,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, WRITE_ONCE(slot->ready, 1); /* send abort command to the chip */ spin_lock_irqsave(&dq->lock, flags); - list_add_tail(&slot->entry, &sas_dev->list); hisi_hba->hw->start_delivery(dq); spin_unlock_irqrestore(&dq->lock, flags); @@ -2070,6 +2084,11 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, abort_flag, tag, dq); case HISI_SAS_INT_ABT_DEV: for (i = 0; i < hisi_hba->cq_nvecs; i++) { + struct hisi_sas_cq *cq = &hisi_hba->cq[i]; + const struct cpumask *mask = cq->pci_irq_mask; + + if (mask && !cpumask_intersects(cpu_online_mask, mask)) + continue; dq = &hisi_hba->dq[i]; rc = _hisi_sas_internal_task_abort(hisi_hba, device, abort_flag, tag, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 39233a9cd1f5..e40cc6b3b67b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -873,6 +873,7 @@ hisi_sas_device *alloc_dev_quirk_v2_hw(struct domain_device *device) sas_dev->sas_device = device; sas_dev->sata_idx = sata_idx; sas_dev->dq = dq; + spin_lock_init(&sas_dev->lock); INIT_LIST_HEAD(&hisi_hba->devices[i].list); break; } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 79762932f5e0..4777c30b0a8e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -403,6 +403,7 @@ struct hisi_sas_err_record_v3 { #define T10_CHK_APP_TAG_MSK (0xc << T10_CHK_MSK_OFF) #define BASE_VECTORS_V3_HW 16 +#define MIN_AFFINE_VECTORS_V3_HW (BASE_VECTORS_V3_HW + 1) static bool hisi_sas_intr_conv; MODULE_PARM_DESC(intr_conv, "interrupt converge enable (0-1)"); @@ -412,6 +413,11 @@ static int prot_mask; module_param(prot_mask, int, 0); MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=0x0 "); +static bool auto_affine_msi_experimental; +module_param(auto_affine_msi_experimental, bool, 0444); +MODULE_PARM_DESC(auto_affine_msi_experimental, "Enable auto-affinity of MSI IRQs as experimental:\n" + "default is off"); + static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off) { void __iomem *regs = hisi_hba->regs + off; @@ -2037,19 +2043,64 @@ static irqreturn_t cq_interrupt_v3_hw(int irq_no, void *p) return IRQ_HANDLED; } +static void setup_reply_map_v3_hw(struct hisi_hba *hisi_hba, int nvecs) +{ + const struct cpumask *mask; + int queue, cpu; + + for (queue = 0; queue < nvecs; queue++) { + struct hisi_sas_cq *cq = &hisi_hba->cq[queue]; + + mask = pci_irq_get_affinity(hisi_hba->pci_dev, queue + + BASE_VECTORS_V3_HW); + if (!mask) + goto fallback; + cq->pci_irq_mask = mask; + for_each_cpu(cpu, mask) + hisi_hba->reply_map[cpu] = queue; + } + return; + +fallback: + for_each_possible_cpu(cpu) + hisi_hba->reply_map[cpu] = cpu % hisi_hba->queue_count; + /* Don't clean all CQ masks */ +} + static int interrupt_init_v3_hw(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; struct pci_dev *pdev = hisi_hba->pci_dev; int vectors, rc; int i, k; - int max_msi = HISI_SAS_MSI_COUNT_V3_HW; + int max_msi = HISI_SAS_MSI_COUNT_V3_HW, min_msi; - vectors = pci_alloc_irq_vectors(hisi_hba->pci_dev, 1, - max_msi, PCI_IRQ_MSI); - if (vectors < max_msi) { - dev_err(dev, "could not allocate all msi (%d)\n", vectors); - return -ENOENT; + if (auto_affine_msi_experimental) { + struct irq_affinity desc = { + .pre_vectors = BASE_VECTORS_V3_HW, + }; + + min_msi = MIN_AFFINE_VECTORS_V3_HW; + + hisi_hba->reply_map = devm_kcalloc(dev, nr_cpu_ids, + sizeof(unsigned int), + GFP_KERNEL); + if (!hisi_hba->reply_map) + return -ENOMEM; + vectors = pci_alloc_irq_vectors_affinity(hisi_hba->pci_dev, + min_msi, max_msi, + PCI_IRQ_MSI | + PCI_IRQ_AFFINITY, + &desc); + if (vectors < 0) + return -ENOENT; + setup_reply_map_v3_hw(hisi_hba, vectors - BASE_VECTORS_V3_HW); + } else { + min_msi = max_msi; + vectors = pci_alloc_irq_vectors(hisi_hba->pci_dev, min_msi, + max_msi, PCI_IRQ_MSI); + if (vectors < 0) + return vectors; } hisi_hba->cq_nvecs = vectors - BASE_VECTORS_V3_HW; From 4a8bec88f71a94f646aeccf26404da710019811d Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 6 Feb 2019 18:52:56 +0800 Subject: [PATCH 1432/4277] scsi: hisi_sas: Do some more tidy-up Do some very minor tidy-up, for things like needlessly initing variable and not leaving whitespace before quote endings. Originally-from: Xiang Chen Originally-from: Luo Jiaxing Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 10 +++++----- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index eff31472b96e..923296653ed7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -144,7 +144,7 @@ EXPORT_SYMBOL_GPL(hisi_sas_get_ncq_tag); */ u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max) { - u16 rate = 0; + u8 rate = 0; int i; max -= SAS_LINK_RATE_1_5_GBPS; @@ -1180,7 +1180,7 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, task->task_done = hisi_sas_task_done; task->slow_task->timer.function = hisi_sas_tmf_timedout; - task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ; + task->slow_task->timer.expires = jiffies + TASK_TIMEOUT * HZ; add_timer(&task->slow_task->timer); res = hisi_sas_task_exec(task, GFP_KERNEL, 1, tmf); @@ -1701,8 +1701,8 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun) { - int rc = TMF_RESP_FUNC_FAILED; struct hisi_sas_tmf_task tmf_task; + int rc; tmf_task.tmf = TMF_CLEAR_ACA; rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); @@ -1752,7 +1752,7 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) { struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; - int rc = TMF_RESP_FUNC_FAILED; + int rc; rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); @@ -2005,7 +2005,7 @@ _hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, task->task_proto = device->tproto; task->task_done = hisi_sas_task_done; task->slow_task->timer.function = hisi_sas_tmf_timedout; - task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT*HZ; + task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT * HZ; add_timer(&task->slow_task->timer); res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 4777c30b0a8e..00738d0673fe 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -11,7 +11,7 @@ #include "hisi_sas.h" #define DRV_NAME "hisi_sas_v3_hw" -/* global registers need init*/ +/* global registers need init */ #define DLVRY_QUEUE_ENABLE 0x0 #define IOST_BASE_ADDR_LO 0x8 #define IOST_BASE_ADDR_HI 0xc @@ -728,7 +728,7 @@ static void clear_itct_v3_hw(struct hisi_hba *hisi_hba, hisi_sas_write32(hisi_hba, ENT_INT_SRC3, ENT_INT_SRC3_ITC_INT_MSK); - /* clear the itct table*/ + /* clear the itct table */ reg_val = ITCT_CLR_EN_MSK | (dev_id & ITCT_DEV_MSK); hisi_sas_write32(hisi_hba, ITCT_CLR, reg_val); From 2c1619edef61a03cb516efaa81750784c3071d10 Mon Sep 17 00:00:00 2001 From: Danit Goldberg Date: Thu, 24 Jan 2019 14:18:15 +0200 Subject: [PATCH 1433/4277] IB/cma: Define option to set ack timeout and pack tos_set Define new option in 'rdma_set_option' to override calculated QP timeout when requested to provide QP attributes to modify a QP. At the same time, pack tos_set to be bitfield. Signed-off-by: Danit Goldberg Reviewed-by: Moni Shoua Signed-off-by: Leon Romanovsky Reviewed-by: Parav Pandit Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 32 ++++++++++++++++++++++++++++++ drivers/infiniband/core/cma_priv.h | 4 +++- drivers/infiniband/core/ucma.c | 7 +++++++ include/rdma/rdma_cm.h | 1 + include/uapi/rdma/rdma_user_cm.h | 4 ++++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index e15546ae4d0f..83aa2ad0c27e 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -888,6 +888,7 @@ struct rdma_cm_id *__rdma_create_id(struct net *net, id_priv->id.ps = ps; id_priv->id.qp_type = qp_type; id_priv->tos_set = false; + id_priv->timeout_set = false; id_priv->gid_type = IB_GID_TYPE_IB; spin_lock_init(&id_priv->lock); mutex_init(&id_priv->qp_mutex); @@ -1130,6 +1131,9 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, } else ret = -ENOSYS; + if ((*qp_attr_mask & IB_QP_TIMEOUT) && id_priv->timeout_set) + qp_attr->timeout = id_priv->timeout; + return ret; } EXPORT_SYMBOL(rdma_init_qp_attr); @@ -2490,6 +2494,34 @@ void rdma_set_service_type(struct rdma_cm_id *id, int tos) } EXPORT_SYMBOL(rdma_set_service_type); +/** + * rdma_set_ack_timeout() - Set the ack timeout of QP associated + * with a connection identifier. + * @id: Communication identifier to associated with service type. + * @timeout: Ack timeout to set a QP, expressed as 4.096 * 2^(timeout) usec. + * + * This function should be called before rdma_connect() on active side, + * and on passive side before rdma_accept(). It is applicable to primary + * path only. The timeout will affect the local side of the QP, it is not + * negotiated with remote side and zero disables the timer. + * + * Return: 0 for success + */ +int rdma_set_ack_timeout(struct rdma_cm_id *id, u8 timeout) +{ + struct rdma_id_private *id_priv; + + if (id->qp_type != IB_QPT_RC) + return -EINVAL; + + id_priv = container_of(id, struct rdma_id_private, id); + id_priv->timeout = timeout; + id_priv->timeout_set = true; + + return 0; +} +EXPORT_SYMBOL(rdma_set_ack_timeout); + static void cma_query_handler(int status, struct sa_path_rec *path_rec, void *context) { diff --git a/drivers/infiniband/core/cma_priv.h b/drivers/infiniband/core/cma_priv.h index cf47c69436a7..ca7307277518 100644 --- a/drivers/infiniband/core/cma_priv.h +++ b/drivers/infiniband/core/cma_priv.h @@ -84,9 +84,11 @@ struct rdma_id_private { u32 options; u8 srq; u8 tos; - bool tos_set; + u8 tos_set:1; + u8 timeout_set:1; u8 reuseaddr; u8 afonly; + u8 timeout; enum ib_gid_type gid_type; /* diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 01d68ed46c1b..7468b26b8a01 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1236,6 +1236,13 @@ static int ucma_set_option_id(struct ucma_context *ctx, int optname, } ret = rdma_set_afonly(ctx->cm_id, *((int *) optval) ? 1 : 0); break; + case RDMA_OPTION_ID_ACK_TIMEOUT: + if (optlen != sizeof(u8)) { + ret = -EINVAL; + break; + } + ret = rdma_set_ack_timeout(ctx->cm_id, *((u8 *)optval)); + break; default: ret = -ENOSYS; } diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index 60987a5903b7..71f48cfdc24c 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h @@ -374,6 +374,7 @@ int rdma_set_reuseaddr(struct rdma_cm_id *id, int reuse); */ int rdma_set_afonly(struct rdma_cm_id *id, int afonly); +int rdma_set_ack_timeout(struct rdma_cm_id *id, u8 timeout); /** * rdma_get_service_id - Return the IB service ID for a specified address. * @id: Communication identifier associated with the address. diff --git a/include/uapi/rdma/rdma_user_cm.h b/include/uapi/rdma/rdma_user_cm.h index 0d1e78ebad05..e42940a215a3 100644 --- a/include/uapi/rdma/rdma_user_cm.h +++ b/include/uapi/rdma/rdma_user_cm.h @@ -300,6 +300,10 @@ enum { RDMA_OPTION_ID_TOS = 0, RDMA_OPTION_ID_REUSEADDR = 1, RDMA_OPTION_ID_AFONLY = 2, + RDMA_OPTION_ID_ACK_TIMEOUT = 3 +}; + +enum { RDMA_OPTION_IB_PATH = 1 }; From 9491128f780e5be382a5b22990439d017f6dfc59 Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Fri, 1 Feb 2019 12:44:27 -0800 Subject: [PATCH 1434/4277] RDMA/cma: listening device cm_ids should inherit tos If a user binds to INADDR_ANY and sets the service id, then the device-specific cm_ids should also use this tos. This allows an app to do: rdma_bind_addr(INADDR_ANY) set_service_type() rdma_listen() And connections setup via this listening endpoint will use the correct tos. Signed-off-by: Steve Wise Reviewed-by: Parav Pandit Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 83aa2ad0c27e..e761ddd09aed 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2466,6 +2466,8 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv, atomic_inc(&id_priv->refcount); dev_id_priv->internal_id = 1; dev_id_priv->afonly = id_priv->afonly; + dev_id_priv->tos_set = id_priv->tos_set; + dev_id_priv->tos = id_priv->tos; ret = rdma_listen(id, id_priv->backlog); if (ret) From 926ba19b3574f6a80823a42484877ed65e91da9c Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Fri, 1 Feb 2019 12:44:32 -0800 Subject: [PATCH 1435/4277] RDMA/iwcm: add tos_set bool to iw_cm struct This allows drivers to know the tos was actively set by the application. Signed-off-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 2 ++ include/rdma/iw_cm.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index e761ddd09aed..c43512752b8a 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2414,6 +2414,7 @@ static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog) return PTR_ERR(id); id->tos = id_priv->tos; + id->tos_set = id_priv->tos_set; id_priv->cm_id.iw = id; memcpy(&id_priv->cm_id.iw->local_addr, cma_src_addr(id_priv), @@ -3843,6 +3844,7 @@ static int cma_connect_iw(struct rdma_id_private *id_priv, return PTR_ERR(cm_id); cm_id->tos = id_priv->tos; + cm_id->tos_set = id_priv->tos_set; id_priv->cm_id.iw = cm_id; memcpy(&cm_id->local_addr, cma_src_addr(id_priv), diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h index 48512abd3162..0e1f02815643 100644 --- a/include/rdma/iw_cm.h +++ b/include/rdma/iw_cm.h @@ -94,7 +94,8 @@ struct iw_cm_id { void (*add_ref)(struct iw_cm_id *); void (*rem_ref)(struct iw_cm_id *); u8 tos; - bool mapped; + bool tos_set:1; + bool mapped:1; }; struct iw_cm_conn_param { From 7235ea227e19100ca748ccd6279322a166a90953 Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Fri, 1 Feb 2019 12:44:37 -0800 Subject: [PATCH 1436/4277] iw_cxgb4: use listening ep tos when accepting new connections If the parent listening endpoint has a service type set, then use that when setting up the connection. This allows server-side applications to mandate the tos for passive side connections via rdma_set_service_type() on the listening endpoints. Signed-off-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/cm.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 59917eb124da..c4e4085430bf 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2476,7 +2476,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) u16 peer_mss = ntohs(req->tcpopt.mss); int iptype; unsigned short hdrs; - u8 tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid)); + u8 tos; parent_ep = (struct c4iw_ep *)get_ep_from_stid(dev, stid); if (!parent_ep) { @@ -2490,6 +2490,11 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) goto reject; } + if (parent_ep->com.cm_id->tos_set) + tos = parent_ep->com.cm_id->tos; + else + tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid)); + cxgb_get_4tuple(req, parent_ep->com.dev->rdev.lldi.adapter_type, &iptype, local_ip, peer_ip, &local_port, &peer_port); @@ -2509,7 +2514,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) ntohs(peer_port), peer_mss); dst = cxgb_find_route6(&dev->rdev.lldi, get_real_dev, local_ip, peer_ip, local_port, peer_port, - PASS_OPEN_TOS_G(ntohl(req->tos_stid)), + tos, ((struct sockaddr_in6 *) &parent_ep->com.local_addr)->sin6_scope_id); } From cb3ba0bde881f0cb7e3945d2a266901e2bd18c92 Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Fri, 1 Feb 2019 12:44:41 -0800 Subject: [PATCH 1437/4277] iw_cxgb4: use tos when importing the endpoint import_ep() is passed the correct tos, but doesn't use it correctly. Fixes: ac8e4c69a021 ("cxgb4/iw_cxgb4: TOS support") Signed-off-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index c4e4085430bf..0259198d594d 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2072,7 +2072,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip, } else { pdev = get_real_dev(n->dev); ep->l2t = cxgb4_l2t_get(cdev->rdev.lldi.l2t, - n, pdev, 0); + n, pdev, rt_tos2priority(tos)); if (!ep->l2t) goto out; ep->mtu = dst_mtu(dst); From c8a7eb554a83214c3d8ee5cb322da8c72810d2dc Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Fri, 1 Feb 2019 12:44:53 -0800 Subject: [PATCH 1438/4277] iw_cxgb4: use tos when finding ipv6 routes When IPv6 support was added, the correct tos was not passed to cxgb_find_route6(). This potentially results in the wrong route entry. Fixes: 830662f6f032 ("RDMA/cxgb4: Add support for active and passive open connection with IPv6 address") Signed-off-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/cm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 0259198d594d..77efd4ae8e10 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2161,7 +2161,8 @@ static int c4iw_reconnect(struct c4iw_ep *ep) laddr6->sin6_addr.s6_addr, raddr6->sin6_addr.s6_addr, laddr6->sin6_port, - raddr6->sin6_port, 0, + raddr6->sin6_port, + ep->com.cm_id->tos, raddr6->sin6_scope_id); iptype = 6; ra = (__u8 *)&raddr6->sin6_addr; @@ -3326,7 +3327,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) laddr6->sin6_addr.s6_addr, raddr6->sin6_addr.s6_addr, laddr6->sin6_port, - raddr6->sin6_port, 0, + raddr6->sin6_port, cm_id->tos, raddr6->sin6_scope_id); } if (!ep->dst) { From 0c236606490b5b356ac3bb885b6417ce09bce63f Mon Sep 17 00:00:00 2001 From: Parvi Kaustubhi Date: Fri, 8 Feb 2019 13:53:43 -0800 Subject: [PATCH 1439/4277] IB/usnic: Fix locking when unregistering Move the call to usnic_ib_device_remove after usnic_ib_ibdev_list_lock has been released. Signed-off-by: Parvi Kaustubhi Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/usnic/usnic_ib_main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index 577d9301251a..1ec155823716 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -470,15 +470,17 @@ static void usnic_ib_undiscover_pf(struct kref *kref) &usnic_ib_ibdev_list, ib_dev_link) { if (us_ibdev->pdev == dev) { list_del(&us_ibdev->ib_dev_link); - usnic_ib_device_remove(us_ibdev); found = true; break; } } - WARN(!found, "Failed to remove PF %s\n", pci_name(dev)); mutex_unlock(&usnic_ib_ibdev_list_lock); + if (found) + usnic_ib_device_remove(us_ibdev); + else + WARN(1, "Failed to remove PF %s\n", pci_name(dev)); } static struct usnic_ib_dev *usnic_ib_discover_pf(struct usnic_vnic *vnic) From d60667fc398ed34b3c7456b020481c55c760e503 Mon Sep 17 00:00:00 2001 From: Daniel Jurgens Date: Sat, 2 Feb 2019 11:09:42 +0200 Subject: [PATCH 1440/4277] IB/core: Unregister notifier before freeing MAD security If the notifier runs after the security context is freed an access of freed memory can occur. Fixes: 47a2b338fe63 ("IB/core: Enforce security on management datagrams") Signed-off-by: Daniel Jurgens Reviewed-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/security.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index 1efadbccf394..402449d4a888 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -727,9 +727,10 @@ void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) if (!rdma_protocol_ib(agent->device, agent->port_num)) return; - security_ib_free_security(agent->security); if (agent->lsm_nb_reg) unregister_lsm_notifier(&agent->lsm_nb); + + security_ib_free_security(agent->security); } int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index) From 6e88e672b69f0e627acdae74a527b730ea224b6b Mon Sep 17 00:00:00 2001 From: Daniel Jurgens Date: Sat, 2 Feb 2019 11:09:43 +0200 Subject: [PATCH 1441/4277] IB/core: Fix potential memory leak while creating MAD agents If the MAD agents isn't allowed to manage the subnet, or fails to register for the LSM notifier, the security context is leaked. Free the context in these cases. Fixes: 47a2b338fe63 ("IB/core: Enforce security on management datagrams") Signed-off-by: Daniel Jurgens Reviewed-by: Parav Pandit Reported-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/security.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index 402449d4a888..7662e9347238 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -710,16 +710,20 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, dev_name(&agent->device->dev), agent->port_num); if (ret) - return ret; + goto free_security; agent->lsm_nb.notifier_call = ib_mad_agent_security_change; ret = register_lsm_notifier(&agent->lsm_nb); if (ret) - return ret; + goto free_security; agent->smp_allowed = true; agent->lsm_nb_reg = true; return 0; + +free_security: + security_ib_free_security(agent->security); + return ret; } void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) From 805b754d492f6227e1646001bdf85ad4bb819e55 Mon Sep 17 00:00:00 2001 From: Daniel Jurgens Date: Sat, 2 Feb 2019 11:09:44 +0200 Subject: [PATCH 1442/4277] IB/core: Eliminate a hole in MAD agent struct Move the security related fields above the u8s to eliminate a hole in the struct. pahole before: struct ib_mad_agent { ... u32 hi_tid; /* 48 4 */ u32 flags; /* 52 4 */ u8 port_num; /* 56 1 */ u8 rmpp_version; /* 57 1 */ /* XXX 6 bytes hole, try to pack */ /* --- cacheline 1 boundary (64 bytes) --- */ void * security; /* 64 8 */ bool smp_allowed; /* 72 1 */ bool lsm_nb_reg; /* 73 1 */ /* XXX 6 bytes hole, try to pack */ struct notifier_block lsm_nb; /* 80 24 */ /* XXX last struct has 4 bytes of padding */ /* size: 104, cachelines: 2, members: 14 */ ... }; pahole after: struct ib_mad_agent { ... u32 hi_tid; /* 48 4 */ u32 flags; /* 52 4 */ void * security; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ struct notifier_block lsm_nb; /* 64 24 */ /* XXX last struct has 4 bytes of padding */ u8 port_num; /* 88 1 */ u8 rmpp_version; /* 89 1 */ bool smp_allowed; /* 90 1 */ bool lsm_nb_reg; /* 91 1 */ /* size: 96, cachelines: 2, members: 14 */ ... }; Signed-off-by: Daniel Jurgens Reviewed-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- include/rdma/ib_mad.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rdma/ib_mad.h b/include/rdma/ib_mad.h index fdef558e3a2d..1c0b914f199d 100644 --- a/include/rdma/ib_mad.h +++ b/include/rdma/ib_mad.h @@ -616,12 +616,12 @@ struct ib_mad_agent { void *context; u32 hi_tid; u32 flags; + void *security; + struct notifier_block lsm_nb; u8 port_num; u8 rmpp_version; - void *security; bool smp_allowed; bool lsm_nb_reg; - struct notifier_block lsm_nb; }; /** From c66f67414c1f88554485bb2a0abf8b5c0d741de7 Mon Sep 17 00:00:00 2001 From: Daniel Jurgens Date: Sat, 2 Feb 2019 11:09:45 +0200 Subject: [PATCH 1443/4277] IB/core: Don't register each MAD agent for LSM notifier When creating many MAD agents in a short period of time, receive packet processing can be delayed long enough to cause timeouts while new agents are being added to the atomic notifier chain with IRQs disabled. Notifier chain registration and unregstration is an O(n) operation. With large numbers of MAD agents being created and destroyed simultaneously the CPUs spend too much time with interrupts disabled. Instead of each MAD agent registering for it's own LSM notification, maintain a list of agents internally and register once, this registration already existed for handling the PKeys. This list is write mostly, so a normal spin lock is used vs a read/write lock. All MAD agents must be checked, so a single list is used instead of breaking them down per device. Notifier calls are done under rcu_read_lock, so there isn't a risk of similar packet timeouts while checking the MAD agents security settings when notified. Signed-off-by: Daniel Jurgens Reviewed-by: Parav Pandit Signed-off-by: Leon Romanovsky Acked-by: Paul Moore Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/core_priv.h | 5 +++ drivers/infiniband/core/device.c | 1 + drivers/infiniband/core/security.c | 48 ++++++++++++++++------------- include/rdma/ib_mad.h | 3 +- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index bcb3e3029a9b..d053110207eb 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -202,6 +202,7 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, enum ib_qp_type qp_type); void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent); int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index); +void ib_mad_agent_security_change(void); #else static inline void ib_security_destroy_port_pkey_list(struct ib_device *device) { @@ -267,6 +268,10 @@ static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map, { return 0; } + +static inline void ib_mad_agent_security_change(void) +{ +} #endif struct ib_device *ib_device_get_by_index(u32 ifindex); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 55221990d946..32cd35c9b21e 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -452,6 +452,7 @@ static int ib_security_change(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; schedule_work(&ib_policy_change_work); + ib_mad_agent_security_change(); return NOTIFY_OK; } diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index 7662e9347238..a70d2ba312ed 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -39,6 +39,10 @@ #include "core_priv.h" #include "mad_priv.h" +static LIST_HEAD(mad_agent_list); +/* Lock to protect mad_agent_list */ +static DEFINE_SPINLOCK(mad_agent_list_lock); + static struct pkey_index_qp_list *get_pkey_idx_qp_list(struct ib_port_pkey *pp) { struct pkey_index_qp_list *pkey = NULL; @@ -676,19 +680,18 @@ static int ib_security_pkey_access(struct ib_device *dev, return security_ib_pkey_access(sec, subnet_prefix, pkey); } -static int ib_mad_agent_security_change(struct notifier_block *nb, - unsigned long event, - void *data) +void ib_mad_agent_security_change(void) { - struct ib_mad_agent *ag = container_of(nb, struct ib_mad_agent, lsm_nb); + struct ib_mad_agent *ag; - if (event != LSM_POLICY_CHANGE) - return NOTIFY_DONE; - - ag->smp_allowed = !security_ib_endport_manage_subnet( - ag->security, dev_name(&ag->device->dev), ag->port_num); - - return NOTIFY_OK; + spin_lock(&mad_agent_list_lock); + list_for_each_entry(ag, + &mad_agent_list, + mad_agent_sec_list) + WRITE_ONCE(ag->smp_allowed, + !security_ib_endport_manage_subnet(ag->security, + dev_name(&ag->device->dev), ag->port_num)); + spin_unlock(&mad_agent_list_lock); } int ib_mad_agent_security_setup(struct ib_mad_agent *agent, @@ -699,6 +702,8 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, if (!rdma_protocol_ib(agent->device, agent->port_num)) return 0; + INIT_LIST_HEAD(&agent->mad_agent_sec_list); + ret = security_ib_alloc_security(&agent->security); if (ret) return ret; @@ -706,22 +711,20 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, if (qp_type != IB_QPT_SMI) return 0; + spin_lock(&mad_agent_list_lock); ret = security_ib_endport_manage_subnet(agent->security, dev_name(&agent->device->dev), agent->port_num); if (ret) goto free_security; - agent->lsm_nb.notifier_call = ib_mad_agent_security_change; - ret = register_lsm_notifier(&agent->lsm_nb); - if (ret) - goto free_security; - - agent->smp_allowed = true; - agent->lsm_nb_reg = true; + WRITE_ONCE(agent->smp_allowed, true); + list_add(&agent->mad_agent_sec_list, &mad_agent_list); + spin_unlock(&mad_agent_list_lock); return 0; free_security: + spin_unlock(&mad_agent_list_lock); security_ib_free_security(agent->security); return ret; } @@ -731,8 +734,11 @@ void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) if (!rdma_protocol_ib(agent->device, agent->port_num)) return; - if (agent->lsm_nb_reg) - unregister_lsm_notifier(&agent->lsm_nb); + if (agent->qp->qp_type == IB_QPT_SMI) { + spin_lock(&mad_agent_list_lock); + list_del(&agent->mad_agent_sec_list); + spin_unlock(&mad_agent_list_lock); + } security_ib_free_security(agent->security); } @@ -743,7 +749,7 @@ int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index) return 0; if (map->agent.qp->qp_type == IB_QPT_SMI) { - if (!map->agent.smp_allowed) + if (!READ_ONCE(map->agent.smp_allowed)) return -EACCES; return 0; } diff --git a/include/rdma/ib_mad.h b/include/rdma/ib_mad.h index 1c0b914f199d..79ba8219e7dc 100644 --- a/include/rdma/ib_mad.h +++ b/include/rdma/ib_mad.h @@ -617,11 +617,10 @@ struct ib_mad_agent { u32 hi_tid; u32 flags; void *security; - struct notifier_block lsm_nb; + struct list_head mad_agent_sec_list; u8 port_num; u8 rmpp_version; bool smp_allowed; - bool lsm_nb_reg; }; /** From 30471d4b20335d9bd9ae9b2382a1e1e97d18d86d Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 3 Feb 2019 14:55:50 +0200 Subject: [PATCH 1444/4277] RDMA/core: Share driver structure size with core Add new macros to be used in drivers while registering ops structure and IB/core while calling allocation routines, so drivers won't need to perform kzalloc/kfree in their paths. The change in allocation stage allows us to initialize common fields prior to calling to drivers (e.g. restrack). Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 2 ++ include/rdma/ib_verbs.h | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 32cd35c9b21e..d806a5c7b202 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1228,6 +1228,8 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) (ptr)->name = ops->name; \ } while (0) +#define SET_OBJ_SIZE(ptr, name) SET_DEVICE_OP(ptr, size_##name) + SET_DEVICE_OP(dev_ops, add_gid); SET_DEVICE_OP(dev_ops, advise_mr); SET_DEVICE_OP(dev_ops, alloc_dm); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 2e1f1e885ee5..e29eae4aec84 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2264,6 +2264,19 @@ struct ib_counters_read_attr { struct uverbs_attr_bundle; +#define INIT_RDMA_OBJ_SIZE(ib_struct, drv_struct, member) \ + .size_##ib_struct = \ + (sizeof(struct drv_struct) + \ + BUILD_BUG_ON_ZERO(offsetof(struct drv_struct, member)) + \ + BUILD_BUG_ON_ZERO( \ + !__same_type(((struct drv_struct *)NULL)->member, \ + struct ib_struct))) + +#define rdma_zalloc_drv_obj(ib_dev, ib_type) \ + ((struct ib_type *)kzalloc(ib_dev->ops.size_##ib_type, GFP_KERNEL)) + +#define DECLARE_RDMA_OBJ_SIZE(ib_struct) size_t size_##ib_struct + /** * struct ib_device_ops - InfiniBand device operations * This structure defines all the InfiniBand device operations, providers will From 21a428a019c9a6d133e745b529b9bf18c1187e70 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 3 Feb 2019 14:55:51 +0200 Subject: [PATCH 1445/4277] RDMA: Handle PD allocations by IB/core The PD allocations in IB/core allows us to simplify drivers and their error flows in their .alloc_pd() paths. The changes in .alloc_pd() go hand in had with relevant update in .dealloc_pd(). We will use this opportunity and convert .dealloc_pd() to don't fail, as it was suggested a long time ago, failures are not happening as we have never seen a WARN_ON print. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 2 + drivers/infiniband/core/uverbs_cmd.c | 15 ++- drivers/infiniband/core/uverbs_std_types.c | 2 +- drivers/infiniband/core/verbs.c | 27 +++--- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 37 +++----- drivers/infiniband/hw/bnxt_re/ib_verbs.h | 9 +- drivers/infiniband/hw/bnxt_re/main.c | 1 + drivers/infiniband/hw/cxgb3/iwch_provider.c | 25 ++--- drivers/infiniband/hw/cxgb4/provider.c | 25 ++--- drivers/infiniband/hw/hns/hns_roce_device.h | 7 +- drivers/infiniband/hw/hns/hns_roce_hw_v1.c | 27 +++--- drivers/infiniband/hw/hns/hns_roce_main.c | 1 + drivers/infiniband/hw/hns/hns_roce_pd.c | 25 ++--- drivers/infiniband/hw/i40iw/i40iw_utils.c | 1 - drivers/infiniband/hw/i40iw/i40iw_verbs.c | 32 +++---- drivers/infiniband/hw/mlx4/main.c | 36 +++----- drivers/infiniband/hw/mlx5/main.c | 48 +++++----- drivers/infiniband/hw/mthca/mthca_provider.c | 29 ++---- drivers/infiniband/hw/nes/nes_verbs.c | 32 +++---- drivers/infiniband/hw/ocrdma/ocrdma_main.c | 1 + drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 92 ++++++++----------- drivers/infiniband/hw/ocrdma/ocrdma_verbs.h | 6 +- drivers/infiniband/hw/qedr/main.c | 1 + drivers/infiniband/hw/qedr/verbs.c | 34 ++----- drivers/infiniband/hw/qedr/verbs.h | 6 +- drivers/infiniband/hw/usnic/usnic_ib_main.c | 1 + drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 26 ++---- drivers/infiniband/hw/usnic/usnic_ib_verbs.h | 7 +- .../infiniband/hw/vmw_pvrdma/pvrdma_main.c | 1 + .../infiniband/hw/vmw_pvrdma/pvrdma_verbs.c | 43 +++------ .../infiniband/hw/vmw_pvrdma/pvrdma_verbs.h | 7 +- drivers/infiniband/sw/rdmavt/pd.c | 29 ++---- drivers/infiniband/sw/rdmavt/pd.h | 7 +- drivers/infiniband/sw/rdmavt/vt.c | 1 + drivers/infiniband/sw/rxe/rxe_pool.c | 60 +++++++++--- drivers/infiniband/sw/rxe/rxe_pool.h | 4 + drivers/infiniband/sw/rxe/rxe_verbs.c | 16 ++-- drivers/infiniband/sw/rxe/rxe_verbs.h | 2 +- include/rdma/ib_verbs.h | 9 +- 39 files changed, 325 insertions(+), 409 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index d806a5c7b202..57e1e177921e 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1319,6 +1319,8 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, set_vf_guid); SET_DEVICE_OP(dev_ops, set_vf_link_state); SET_DEVICE_OP(dev_ops, unmap_fmr); + + SET_OBJ_SIZE(dev_ops, ib_pd); } EXPORT_SYMBOL(ib_set_device_ops); diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index aa260cafbd85..5ac143f22df0 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -407,9 +407,9 @@ static int ib_uverbs_alloc_pd(struct uverbs_attr_bundle *attrs) if (IS_ERR(uobj)) return PTR_ERR(uobj); - pd = ib_dev->ops.alloc_pd(ib_dev, uobj->context, &attrs->driver_udata); - if (IS_ERR(pd)) { - ret = PTR_ERR(pd); + pd = rdma_zalloc_drv_obj(ib_dev, ib_pd); + if (!pd) { + ret = -ENOMEM; goto err; } @@ -417,11 +417,15 @@ static int ib_uverbs_alloc_pd(struct uverbs_attr_bundle *attrs) pd->uobject = uobj; pd->__internal_mr = NULL; atomic_set(&pd->usecnt, 0); + pd->res.type = RDMA_RESTRACK_PD; + + ret = ib_dev->ops.alloc_pd(pd, uobj->context, &attrs->driver_udata); + if (ret) + goto err_alloc; uobj->object = pd; memset(&resp, 0, sizeof resp); resp.pd_handle = uobj->id; - pd->res.type = RDMA_RESTRACK_PD; rdma_restrack_uadd(&pd->res); ret = uverbs_response(attrs, &resp, sizeof(resp)); @@ -432,7 +436,8 @@ static int ib_uverbs_alloc_pd(struct uverbs_attr_bundle *attrs) err_copy: ib_dealloc_pd(pd); - +err_alloc: + kfree(pd); err: uobj_alloc_abort(uobj); return ret; diff --git a/drivers/infiniband/core/uverbs_std_types.c b/drivers/infiniband/core/uverbs_std_types.c index cbc72312eb41..f224cb727224 100644 --- a/drivers/infiniband/core/uverbs_std_types.c +++ b/drivers/infiniband/core/uverbs_std_types.c @@ -188,7 +188,7 @@ static int uverbs_free_pd(struct ib_uobject *uobject, if (ret) return ret; - ib_dealloc_pd((struct ib_pd *)uobject->object); + ib_dealloc_pd(pd); return 0; } diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 3220fb42ecce..de5d895a5054 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -254,10 +254,11 @@ struct ib_pd *__ib_alloc_pd(struct ib_device *device, unsigned int flags, { struct ib_pd *pd; int mr_access_flags = 0; + int ret; - pd = device->ops.alloc_pd(device, NULL, NULL); - if (IS_ERR(pd)) - return pd; + pd = rdma_zalloc_drv_obj(device, ib_pd); + if (!pd) + return ERR_PTR(-ENOMEM); pd->device = device; pd->uobject = NULL; @@ -265,6 +266,16 @@ struct ib_pd *__ib_alloc_pd(struct ib_device *device, unsigned int flags, atomic_set(&pd->usecnt, 0); pd->flags = flags; + pd->res.type = RDMA_RESTRACK_PD; + rdma_restrack_set_task(&pd->res, caller); + + ret = device->ops.alloc_pd(pd, NULL, NULL); + if (ret) { + kfree(pd); + return ERR_PTR(ret); + } + rdma_restrack_kadd(&pd->res); + if (device->attrs.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY) pd->local_dma_lkey = device->local_dma_lkey; else @@ -275,10 +286,6 @@ struct ib_pd *__ib_alloc_pd(struct ib_device *device, unsigned int flags, mr_access_flags |= IB_ACCESS_REMOTE_READ | IB_ACCESS_REMOTE_WRITE; } - pd->res.type = RDMA_RESTRACK_PD; - rdma_restrack_set_task(&pd->res, caller); - rdma_restrack_kadd(&pd->res); - if (mr_access_flags) { struct ib_mr *mr; @@ -329,10 +336,8 @@ void ib_dealloc_pd(struct ib_pd *pd) WARN_ON(atomic_read(&pd->usecnt)); rdma_restrack_del(&pd->res); - /* Making delalloc_pd a void return is a WIP, no driver should return - an error here. */ - ret = pd->device->ops.dealloc_pd(pd); - WARN_ONCE(ret, "Infiniband HW driver failed dealloc_pd"); + pd->device->ops.dealloc_pd(pd); + kfree(pd); } EXPORT_SYMBOL(ib_dealloc_pd); diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 1d7469e23cde..1606571af63d 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -563,41 +563,29 @@ fail: } /* Protection Domains */ -int bnxt_re_dealloc_pd(struct ib_pd *ib_pd) +void bnxt_re_dealloc_pd(struct ib_pd *ib_pd) { struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; - int rc; bnxt_re_destroy_fence_mr(pd); - if (pd->qplib_pd.id) { - rc = bnxt_qplib_dealloc_pd(&rdev->qplib_res, - &rdev->qplib_res.pd_tbl, - &pd->qplib_pd); - if (rc) - dev_err(rdev_to_dev(rdev), "Failed to deallocate HW PD"); - } - - kfree(pd); - return 0; + if (pd->qplib_pd.id) + bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl, + &pd->qplib_pd); } -struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *ucontext, - struct ib_udata *udata) +int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *ucontext, + struct ib_udata *udata) { + struct ib_device *ibdev = ibpd->device; struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); struct bnxt_re_ucontext *ucntx = container_of(ucontext, struct bnxt_re_ucontext, ib_uctx); - struct bnxt_re_pd *pd; + struct bnxt_re_pd *pd = container_of(ibpd, struct bnxt_re_pd, ib_pd); int rc; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - pd->rdev = rdev; if (bnxt_qplib_alloc_pd(&rdev->qplib_res.pd_tbl, &pd->qplib_pd)) { dev_err(rdev_to_dev(rdev), "Failed to allocate HW PD"); @@ -637,13 +625,12 @@ struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev, if (bnxt_re_create_fence_mr(pd)) dev_warn(rdev_to_dev(rdev), "Failed to create Fence-MR\n"); - return &pd->ib_pd; + return 0; dbfail: - (void)bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl, - &pd->qplib_pd); + bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl, + &pd->qplib_pd); fail: - kfree(pd); - return ERR_PTR(rc); + return rc; } /* Address Handles */ diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index c4af72604b4f..c7cca803cfa3 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -56,8 +56,8 @@ struct bnxt_re_fence_data { }; struct bnxt_re_pd { + struct ib_pd ib_pd; struct bnxt_re_dev *rdev; - struct ib_pd ib_pd; struct bnxt_qplib_pd qplib_pd; struct bnxt_re_fence_data fence; }; @@ -163,10 +163,9 @@ int bnxt_re_query_gid(struct ib_device *ibdev, u8 port_num, int index, union ib_gid *gid); enum rdma_link_layer bnxt_re_get_link_layer(struct ib_device *ibdev, u8 port_num); -struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata); -int bnxt_re_dealloc_pd(struct ib_pd *pd); +int bnxt_re_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata); +void bnxt_re_dealloc_pd(struct ib_pd *pd); struct ib_ah *bnxt_re_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr, u32 flags, diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 0d40a930c192..0a89ef6e5754 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -637,6 +637,7 @@ static const struct ib_device_ops bnxt_re_dev_ops = { .query_srq = bnxt_re_query_srq, .reg_user_mr = bnxt_re_reg_user_mr, .req_notify_cq = bnxt_re_req_notify_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, bnxt_re_pd, ib_pd), }; static int bnxt_re_register_ib(struct bnxt_re_dev *rdev) diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 804c1fc7bfc1..4cc9a6ae2139 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -370,7 +370,7 @@ static int iwch_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) return ret; } -static int iwch_deallocate_pd(struct ib_pd *pd) +static void iwch_deallocate_pd(struct ib_pd *pd) { struct iwch_dev *rhp; struct iwch_pd *php; @@ -379,15 +379,13 @@ static int iwch_deallocate_pd(struct ib_pd *pd) rhp = php->rhp; pr_debug("%s ibpd %p pdid 0x%x\n", __func__, pd, php->pdid); cxio_hal_put_pdid(rhp->rdev.rscp, php->pdid); - kfree(php); - return 0; } -static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int iwch_allocate_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct iwch_pd *php; + struct iwch_pd *php = to_iwch_pd(pd); + struct ib_device *ibdev = pd->device; u32 pdid; struct iwch_dev *rhp; @@ -395,12 +393,8 @@ static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev, rhp = (struct iwch_dev *) ibdev; pdid = cxio_hal_get_pdid(rhp->rdev.rscp); if (!pdid) - return ERR_PTR(-EINVAL); - php = kzalloc(sizeof(*php), GFP_KERNEL); - if (!php) { - cxio_hal_put_pdid(rhp->rdev.rscp, pdid); - return ERR_PTR(-ENOMEM); - } + return -EINVAL; + php->pdid = pdid; php->rhp = rhp; if (context) { @@ -408,11 +402,11 @@ static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev, if (ib_copy_to_udata(udata, &resp, sizeof(resp))) { iwch_deallocate_pd(&php->ibpd); - return ERR_PTR(-EFAULT); + return -EFAULT; } } pr_debug("%s pdid 0x%0x ptr 0x%p\n", __func__, pdid, php); - return &php->ibpd; + return 0; } static int iwch_dereg_mr(struct ib_mr *ib_mr) @@ -1350,6 +1344,7 @@ static const struct ib_device_ops iwch_dev_ops = { .reg_user_mr = iwch_reg_user_mr, .req_notify_cq = iwch_arm_cq, .resize_cq = iwch_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, iwch_pd, ibpd), }; int iwch_register_device(struct iwch_dev *dev) diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index f59bf7e5a589..680b5e98491d 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -209,7 +209,7 @@ static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) return ret; } -static int c4iw_deallocate_pd(struct ib_pd *pd) +static void c4iw_deallocate_pd(struct ib_pd *pd) { struct c4iw_dev *rhp; struct c4iw_pd *php; @@ -221,15 +221,13 @@ static int c4iw_deallocate_pd(struct ib_pd *pd) mutex_lock(&rhp->rdev.stats.lock); rhp->rdev.stats.pd.cur--; mutex_unlock(&rhp->rdev.stats.lock); - kfree(php); - return 0; } -static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int c4iw_allocate_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct c4iw_pd *php; + struct c4iw_pd *php = to_c4iw_pd(pd); + struct ib_device *ibdev = pd->device; u32 pdid; struct c4iw_dev *rhp; @@ -237,12 +235,8 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev, rhp = (struct c4iw_dev *) ibdev; pdid = c4iw_get_resource(&rhp->rdev.resource.pdid_table); if (!pdid) - return ERR_PTR(-EINVAL); - php = kzalloc(sizeof(*php), GFP_KERNEL); - if (!php) { - c4iw_put_resource(&rhp->rdev.resource.pdid_table, pdid); - return ERR_PTR(-ENOMEM); - } + return -EINVAL; + php->pdid = pdid; php->rhp = rhp; if (context) { @@ -250,7 +244,7 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev, if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) { c4iw_deallocate_pd(&php->ibpd); - return ERR_PTR(-EFAULT); + return -EFAULT; } } mutex_lock(&rhp->rdev.stats.lock); @@ -259,7 +253,7 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev, rhp->rdev.stats.pd.max = rhp->rdev.stats.pd.cur; mutex_unlock(&rhp->rdev.stats.lock); pr_debug("pdid 0x%0x ptr 0x%p\n", pdid, php); - return &php->ibpd; + return 0; } static int c4iw_query_pkey(struct ib_device *ibdev, u8 port, u16 index, @@ -570,6 +564,7 @@ static const struct ib_device_ops c4iw_dev_ops = { .query_qp = c4iw_ib_query_qp, .reg_user_mr = c4iw_reg_user_mr, .req_notify_cq = c4iw_arm_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, c4iw_pd, ibpd), }; void c4iw_register_device(struct work_struct *work) diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 8ca8d74dfb6a..9ee86daf1700 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -1114,10 +1114,9 @@ struct ib_ah *hns_roce_create_ah(struct ib_pd *pd, int hns_roce_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr); int hns_roce_destroy_ah(struct ib_ah *ah, u32 flags); -struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev, - struct ib_ucontext *context, - struct ib_udata *udata); -int hns_roce_dealloc_pd(struct ib_pd *pd); +int hns_roce_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata); +void hns_roce_dealloc_pd(struct ib_pd *pd); struct ib_mr *hns_roce_get_dma_mr(struct ib_pd *pd, int acc); struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c index fa08c22aad66..a18b88c95995 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c @@ -711,13 +711,14 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev) struct ib_qp_attr attr = { 0 }; struct hns_roce_v1_priv *priv; struct hns_roce_qp *hr_qp; + struct ib_device *ibdev; struct ib_cq *cq; struct ib_pd *pd; union ib_gid dgid; u64 subnet_prefix; int attr_mask = 0; + int ret = -ENOMEM; int i, j; - int ret; u8 queue_en[HNS_ROCE_V1_RESV_QP] = { 0 }; u8 phy_port; u8 port = 0; @@ -742,12 +743,16 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev) free_mr->mr_free_cq->ib_cq.cq_context = NULL; atomic_set(&free_mr->mr_free_cq->ib_cq.usecnt, 0); - pd = hns_roce_alloc_pd(&hr_dev->ib_dev, NULL, NULL); - if (IS_ERR(pd)) { - dev_err(dev, "Create pd for reserved loop qp failed!"); - ret = -ENOMEM; + ibdev = &hr_dev->ib_dev; + pd = rdma_zalloc_drv_obj(ibdev, ib_pd); + if (pd) + goto alloc_mem_failed; + + pd->device = ibdev; + ret = hns_roce_alloc_pd(pd, NULL, NULL); + if (ret) goto alloc_pd_failed; - } + free_mr->mr_free_pd = to_hr_pd(pd); free_mr->mr_free_pd->ibpd.device = &hr_dev->ib_dev; free_mr->mr_free_pd->ibpd.uobject = NULL; @@ -854,10 +859,12 @@ create_lp_qp_failed: dev_err(dev, "Destroy qp %d for mr free failed!\n", i); } - if (hns_roce_dealloc_pd(pd)) - dev_err(dev, "Destroy pd for create_lp_qp failed!\n"); + hns_roce_dealloc_pd(pd); alloc_pd_failed: + kfree(pd); + +alloc_mem_failed: if (hns_roce_ib_destroy_cq(cq)) dev_err(dev, "Destroy cq for create_lp_qp failed!\n"); @@ -891,9 +898,7 @@ static void hns_roce_v1_release_lp_qp(struct hns_roce_dev *hr_dev) if (ret) dev_err(dev, "Destroy cq for mr_free failed(%d)!\n", ret); - ret = hns_roce_dealloc_pd(&free_mr->mr_free_pd->ibpd); - if (ret) - dev_err(dev, "Destroy pd for mr_free failed(%d)!\n", ret); + hns_roce_dealloc_pd(&free_mr->mr_free_pd->ibpd); } static int hns_roce_db_init(struct hns_roce_dev *hr_dev) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 67a8c4333f4f..ccf10622586c 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -472,6 +472,7 @@ static const struct ib_device_ops hns_roce_dev_ops = { .query_pkey = hns_roce_query_pkey, .query_port = hns_roce_query_port, .reg_user_mr = hns_roce_reg_user_mr, + INIT_RDMA_OBJ_SIZE(ib_pd, hns_roce_pd, ibpd), }; static const struct ib_device_ops hns_roce_dev_mr_ops = { diff --git a/drivers/infiniband/hw/hns/hns_roce_pd.c b/drivers/infiniband/hw/hns/hns_roce_pd.c index 4a29b2cb9bab..b9b97c5e97e6 100644 --- a/drivers/infiniband/hw/hns/hns_roce_pd.c +++ b/drivers/infiniband/hw/hns/hns_roce_pd.c @@ -57,24 +57,19 @@ void hns_roce_cleanup_pd_table(struct hns_roce_dev *hr_dev) hns_roce_bitmap_cleanup(&hr_dev->pd_bitmap); } -struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev, - struct ib_ucontext *context, - struct ib_udata *udata) +int hns_roce_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { + struct ib_device *ib_dev = ibpd->device; struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev); struct device *dev = hr_dev->dev; - struct hns_roce_pd *pd; + struct hns_roce_pd *pd = to_hr_pd(ibpd); int ret; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - ret = hns_roce_pd_alloc(to_hr_dev(ib_dev), &pd->pdn); if (ret) { - kfree(pd); dev_err(dev, "[alloc_pd]hns_roce_pd_alloc failed!\n"); - return ERR_PTR(ret); + return ret; } if (context) { @@ -83,21 +78,17 @@ struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev, if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) { hns_roce_pd_free(to_hr_dev(ib_dev), pd->pdn); dev_err(dev, "[alloc_pd]ib_copy_to_udata failed!\n"); - kfree(pd); - return ERR_PTR(-EFAULT); + return -EFAULT; } } - return &pd->ibpd; + return 0; } EXPORT_SYMBOL_GPL(hns_roce_alloc_pd); -int hns_roce_dealloc_pd(struct ib_pd *pd) +void hns_roce_dealloc_pd(struct ib_pd *pd) { hns_roce_pd_free(to_hr_dev(pd->device), to_hr_pd(pd)->pdn); - kfree(to_hr_pd(pd)); - - return 0; } EXPORT_SYMBOL_GPL(hns_roce_dealloc_pd); diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c index 59e978141ad4..c5a881172524 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_utils.c +++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c @@ -601,7 +601,6 @@ void i40iw_rem_pdusecount(struct i40iw_pd *iwpd, struct i40iw_device *iwdev) if (!atomic_dec_and_test(&iwpd->usecount)) return; i40iw_free_resource(iwdev, iwdev->allocated_pds, iwpd->sc_pd.pd_id); - kfree(iwpd); } /** diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index d4ab46dd9e6c..28449ad57b37 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -312,16 +312,15 @@ static void i40iw_dealloc_push_page(struct i40iw_device *iwdev, struct i40iw_sc_ /** * i40iw_alloc_pd - allocate protection domain - * @ibdev: device pointer from stack + * @pd: PD pointer * @context: user context created during alloc * @udata: user data */ -static struct ib_pd *i40iw_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int i40iw_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct i40iw_pd *iwpd; - struct i40iw_device *iwdev = to_iwdev(ibdev); + struct i40iw_pd *iwpd = to_iwpd(pd); + struct i40iw_device *iwdev = to_iwdev(pd->device); struct i40iw_sc_dev *dev = &iwdev->sc_dev; struct i40iw_alloc_pd_resp uresp; struct i40iw_sc_pd *sc_pd; @@ -330,19 +329,13 @@ static struct ib_pd *i40iw_alloc_pd(struct ib_device *ibdev, int err; if (iwdev->closing) - return ERR_PTR(-ENODEV); + return -ENODEV; err = i40iw_alloc_resource(iwdev, iwdev->allocated_pds, iwdev->max_pd, &pd_id, &iwdev->next_pd); if (err) { i40iw_pr_err("alloc resource failed\n"); - return ERR_PTR(err); - } - - iwpd = kzalloc(sizeof(*iwpd), GFP_KERNEL); - if (!iwpd) { - err = -ENOMEM; - goto free_res; + return err; } sc_pd = &iwpd->sc_pd; @@ -361,25 +354,23 @@ static struct ib_pd *i40iw_alloc_pd(struct ib_device *ibdev, } i40iw_add_pdusecount(iwpd); - return &iwpd->ibpd; + return 0; + error: - kfree(iwpd); -free_res: i40iw_free_resource(iwdev, iwdev->allocated_pds, pd_id); - return ERR_PTR(err); + return err; } /** * i40iw_dealloc_pd - deallocate pd * @ibpd: ptr of pd to be deallocated */ -static int i40iw_dealloc_pd(struct ib_pd *ibpd) +static void i40iw_dealloc_pd(struct ib_pd *ibpd) { struct i40iw_pd *iwpd = to_iwpd(ibpd); struct i40iw_device *iwdev = to_iwdev(ibpd->device); i40iw_rem_pdusecount(iwpd, iwdev); - return 0; } /** @@ -2750,6 +2741,7 @@ static const struct ib_device_ops i40iw_dev_ops = { .query_qp = i40iw_query_qp, .reg_user_mr = i40iw_reg_user_mr, .req_notify_cq = i40iw_req_notify_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, i40iw_pd, ibpd), }; /** diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index d66002a31000..c0f6aea7ed7c 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1186,38 +1186,27 @@ static int mlx4_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) } } -static struct ib_pd *mlx4_ib_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int mlx4_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct mlx4_ib_pd *pd; + struct mlx4_ib_pd *pd = to_mpd(ibpd); + struct ib_device *ibdev = ibpd->device; int err; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - err = mlx4_pd_alloc(to_mdev(ibdev)->dev, &pd->pdn); - if (err) { - kfree(pd); - return ERR_PTR(err); - } + if (err) + return err; - if (context) - if (ib_copy_to_udata(udata, &pd->pdn, sizeof (__u32))) { - mlx4_pd_free(to_mdev(ibdev)->dev, pd->pdn); - kfree(pd); - return ERR_PTR(-EFAULT); - } - return &pd->ibpd; + if (context && ib_copy_to_udata(udata, &pd->pdn, sizeof(__u32))) { + mlx4_pd_free(to_mdev(ibdev)->dev, pd->pdn); + return -EFAULT; + } + return 0; } -static int mlx4_ib_dealloc_pd(struct ib_pd *pd) +static void mlx4_ib_dealloc_pd(struct ib_pd *pd) { mlx4_pd_free(to_mdev(pd->device)->dev, to_mpd(pd)->pdn); - kfree(pd); - - return 0; } static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev, @@ -2580,6 +2569,7 @@ static const struct ib_device_ops mlx4_ib_dev_ops = { .req_notify_cq = mlx4_ib_arm_cq, .rereg_user_mr = mlx4_ib_rereg_user_mr, .resize_cq = mlx4_ib_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, mlx4_ib_pd, ibpd), }; static const struct ib_device_ops mlx4_ib_dev_wq_ops = { diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 76d6c2557d0c..f9cddc6f2ab6 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2280,30 +2280,24 @@ int mlx5_ib_dealloc_dm(struct ib_dm *ibdm) return 0; } -static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int mlx5_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { + struct mlx5_ib_pd *pd = to_mpd(ibpd); + struct ib_device *ibdev = ibpd->device; struct mlx5_ib_alloc_pd_resp resp; - struct mlx5_ib_pd *pd; int err; u32 out[MLX5_ST_SZ_DW(alloc_pd_out)] = {}; u32 in[MLX5_ST_SZ_DW(alloc_pd_in)] = {}; u16 uid = 0; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - uid = context ? to_mucontext(context)->devx_uid : 0; MLX5_SET(alloc_pd_in, in, opcode, MLX5_CMD_OP_ALLOC_PD); MLX5_SET(alloc_pd_in, in, uid, uid); err = mlx5_cmd_exec(to_mdev(ibdev)->mdev, in, sizeof(in), out, sizeof(out)); - if (err) { - kfree(pd); - return ERR_PTR(err); - } + if (err) + return err; pd->pdn = MLX5_GET(alloc_pd_out, out, pd); pd->uid = uid; @@ -2311,23 +2305,19 @@ static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, resp.pdn = pd->pdn; if (ib_copy_to_udata(udata, &resp, sizeof(resp))) { mlx5_cmd_dealloc_pd(to_mdev(ibdev)->mdev, pd->pdn, uid); - kfree(pd); - return ERR_PTR(-EFAULT); + return -EFAULT; } } - return &pd->ibpd; + return 0; } -static int mlx5_ib_dealloc_pd(struct ib_pd *pd) +static void mlx5_ib_dealloc_pd(struct ib_pd *pd) { struct mlx5_ib_dev *mdev = to_mdev(pd->device); struct mlx5_ib_pd *mpd = to_mpd(pd); mlx5_cmd_dealloc_pd(mdev->mdev, mpd->pdn, mpd->uid); - kfree(mpd); - - return 0; } enum { @@ -4680,23 +4670,28 @@ static int create_dev_resources(struct mlx5_ib_resources *devr) { struct ib_srq_init_attr attr; struct mlx5_ib_dev *dev; + struct ib_device *ibdev; struct ib_cq_init_attr cq_attr = {.cqe = 1}; int port; int ret = 0; dev = container_of(devr, struct mlx5_ib_dev, devr); + ibdev = &dev->ib_dev; mutex_init(&devr->mutex); - devr->p0 = mlx5_ib_alloc_pd(&dev->ib_dev, NULL, NULL); - if (IS_ERR(devr->p0)) { - ret = PTR_ERR(devr->p0); - goto error0; - } - devr->p0->device = &dev->ib_dev; + devr->p0 = rdma_zalloc_drv_obj(ibdev, ib_pd); + if (!devr->p0) + return -ENOMEM; + + devr->p0->device = ibdev; devr->p0->uobject = NULL; atomic_set(&devr->p0->usecnt, 0); + ret = mlx5_ib_alloc_pd(devr->p0, NULL, NULL); + if (ret) + goto error0; + devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, &cq_attr, NULL, NULL); if (IS_ERR(devr->c0)) { ret = PTR_ERR(devr->c0); @@ -4794,6 +4789,7 @@ error2: error1: mlx5_ib_dealloc_pd(devr->p0); error0: + kfree(devr->p0); return ret; } @@ -4809,6 +4805,7 @@ static void destroy_dev_resources(struct mlx5_ib_resources *devr) mlx5_ib_dealloc_xrcd(devr->x1); mlx5_ib_destroy_cq(devr->c0); mlx5_ib_dealloc_pd(devr->p0); + kfree(devr->p0); /* Make sure no change P_Key work items are still executing */ for (port = 0; port < dev->num_ports; ++port) @@ -5938,6 +5935,7 @@ static const struct ib_device_ops mlx5_ib_dev_ops = { .req_notify_cq = mlx5_ib_arm_cq, .rereg_user_mr = mlx5_ib_rereg_user_mr, .resize_cq = mlx5_ib_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, mlx5_ib_pd, ibpd), }; static const struct ib_device_ops mlx5_ib_dev_flow_ipsec_ops = { diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 1bb67562c8c8..2c754bc226f3 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -374,40 +374,30 @@ static int mthca_mmap_uar(struct ib_ucontext *context, return 0; } -static struct ib_pd *mthca_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int mthca_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct mthca_pd *pd; + struct ib_device *ibdev = ibpd->device; + struct mthca_pd *pd = to_mpd(ibpd); int err; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - err = mthca_pd_alloc(to_mdev(ibdev), !context, pd); - if (err) { - kfree(pd); - return ERR_PTR(err); - } + if (err) + return err; if (context) { if (ib_copy_to_udata(udata, &pd->pd_num, sizeof (__u32))) { mthca_pd_free(to_mdev(ibdev), pd); - kfree(pd); - return ERR_PTR(-EFAULT); + return -EFAULT; } } - return &pd->ibpd; + return 0; } -static int mthca_dealloc_pd(struct ib_pd *pd) +static void mthca_dealloc_pd(struct ib_pd *pd) { mthca_pd_free(to_mdev(pd->device), to_mpd(pd)); - kfree(pd); - - return 0; } static struct ib_ah *mthca_ah_create(struct ib_pd *pd, @@ -1228,6 +1218,7 @@ static const struct ib_device_ops mthca_dev_ops = { .query_qp = mthca_query_qp, .reg_user_mr = mthca_reg_user_mr, .resize_cq = mthca_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, mthca_pd, ibpd), }; static const struct ib_device_ops mthca_dev_arbel_srq_ops = { diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 6eb991d40035..f18b28ae4bd9 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -658,10 +658,11 @@ static int nes_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) /** * nes_alloc_pd */ -static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, struct ib_udata *udata) +static int nes_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct nes_pd *nespd; + struct ib_device *ibdev = pd->device; + struct nes_pd *nespd = to_nespd(pd); struct nes_vnic *nesvnic = to_nesvnic(ibdev); struct nes_device *nesdev = nesvnic->nesdev; struct nes_adapter *nesadapter = nesdev->nesadapter; @@ -676,15 +677,8 @@ static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev, err = nes_alloc_resource(nesadapter, nesadapter->allocated_pds, nesadapter->max_pd, &pd_num, &nesadapter->next_pd, NES_RESOURCE_PD); - if (err) { - return ERR_PTR(err); - } - - nespd = kzalloc(sizeof (struct nes_pd), GFP_KERNEL); - if (!nespd) { - nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num); - return ERR_PTR(-ENOMEM); - } + if (err) + return err; nes_debug(NES_DBG_PD, "Allocating PD (%p) for ib device %s\n", nespd, dev_name(&nesvnic->nesibdev->ibdev.dev)); @@ -700,16 +694,14 @@ static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev, if (nespd->mmap_db_index >= NES_MAX_USER_DB_REGIONS) { nes_debug(NES_DBG_PD, "mmap_db_index > MAX\n"); nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num); - kfree(nespd); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } uresp.pd_id = nespd->pd_id; uresp.mmap_db_index = nespd->mmap_db_index; if (ib_copy_to_udata(udata, &uresp, sizeof (struct nes_alloc_pd_resp))) { nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num); - kfree(nespd); - return ERR_PTR(-EFAULT); + return -EFAULT; } set_bit(nespd->mmap_db_index, nesucontext->allocated_doorbells); @@ -718,14 +710,14 @@ static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev, } nes_debug(NES_DBG_PD, "PD%u structure located @%p.\n", nespd->pd_id, nespd); - return &nespd->ibpd; + return 0; } /** * nes_dealloc_pd */ -static int nes_dealloc_pd(struct ib_pd *ibpd) +static void nes_dealloc_pd(struct ib_pd *ibpd) { struct nes_ucontext *nesucontext; struct nes_pd *nespd = to_nespd(ibpd); @@ -748,9 +740,6 @@ static int nes_dealloc_pd(struct ib_pd *ibpd) nespd->pd_id, nespd); nes_free_resource(nesadapter, nesadapter->allocated_pds, (nespd->pd_id-nesadapter->base_pd)>>(PAGE_SHIFT-12)); - kfree(nespd); - - return 0; } @@ -3658,6 +3647,7 @@ static const struct ib_device_ops nes_dev_ops = { .query_qp = nes_query_qp, .reg_user_mr = nes_reg_user_mr, .req_notify_cq = nes_req_notify_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, nes_pd, ibpd), }; /** diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index 88970a6bb555..0de83c92691f 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -179,6 +179,7 @@ static const struct ib_device_ops ocrdma_dev_ops = { .reg_user_mr = ocrdma_reg_user_mr, .req_notify_cq = ocrdma_arm_cq, .resize_cq = ocrdma_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, ocrdma_pd, ibpd), }; static const struct ib_device_ops ocrdma_dev_srq_ops = { diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 2a62936bef4d..980ba97188ff 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -367,17 +367,12 @@ static int ocrdma_get_pd_num(struct ocrdma_dev *dev, struct ocrdma_pd *pd) return status; } -static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev, - struct ocrdma_ucontext *uctx, - struct ib_udata *udata) +static int _ocrdma_alloc_pd(struct ocrdma_dev *dev, struct ocrdma_pd *pd, + struct ocrdma_ucontext *uctx, + struct ib_udata *udata) { - struct ocrdma_pd *pd = NULL; int status; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - if (udata && uctx && dev->attr.max_dpp_pds) { pd->dpp_enabled = ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R; @@ -386,15 +381,8 @@ static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev, dev->attr.wqe_size) : 0; } - if (dev->pd_mgr->pd_prealloc_valid) { - status = ocrdma_get_pd_num(dev, pd); - if (status == 0) { - return pd; - } else { - kfree(pd); - return ERR_PTR(status); - } - } + if (dev->pd_mgr->pd_prealloc_valid) + return ocrdma_get_pd_num(dev, pd); retry: status = ocrdma_mbx_alloc_pd(dev, pd); @@ -403,13 +391,11 @@ retry: pd->dpp_enabled = false; pd->num_dpp_qp = 0; goto retry; - } else { - kfree(pd); - return ERR_PTR(status); } + return status; } - return pd; + return 0; } static inline int is_ucontext_pd(struct ocrdma_ucontext *uctx, @@ -418,30 +404,33 @@ static inline int is_ucontext_pd(struct ocrdma_ucontext *uctx, return (uctx->cntxt_pd == pd); } -static int _ocrdma_dealloc_pd(struct ocrdma_dev *dev, +static void _ocrdma_dealloc_pd(struct ocrdma_dev *dev, struct ocrdma_pd *pd) { - int status; - if (dev->pd_mgr->pd_prealloc_valid) - status = ocrdma_put_pd_num(dev, pd->id, pd->dpp_enabled); + ocrdma_put_pd_num(dev, pd->id, pd->dpp_enabled); else - status = ocrdma_mbx_dealloc_pd(dev, pd); - - kfree(pd); - return status; + ocrdma_mbx_dealloc_pd(dev, pd); } static int ocrdma_alloc_ucontext_pd(struct ocrdma_dev *dev, struct ocrdma_ucontext *uctx, struct ib_udata *udata) { - int status = 0; + struct ib_device *ibdev = &dev->ibdev; + struct ib_pd *pd; + int status; - uctx->cntxt_pd = _ocrdma_alloc_pd(dev, uctx, udata); - if (IS_ERR(uctx->cntxt_pd)) { - status = PTR_ERR(uctx->cntxt_pd); - uctx->cntxt_pd = NULL; + pd = rdma_zalloc_drv_obj(ibdev, ib_pd); + if (!pd) + return -ENOMEM; + + pd->device = ibdev; + uctx->cntxt_pd = get_ocrdma_pd(pd); + + status = _ocrdma_alloc_pd(dev, uctx->cntxt_pd, uctx, udata); + if (status) { + kfree(uctx->cntxt_pd); goto err; } @@ -460,6 +449,7 @@ static int ocrdma_dealloc_ucontext_pd(struct ocrdma_ucontext *uctx) pr_err("%s(%d) Freeing in use pdid=0x%x.\n", __func__, dev->id, pd->id); } + kfree(uctx->cntxt_pd); uctx->cntxt_pd = NULL; (void)_ocrdma_dealloc_pd(dev, pd); return 0; @@ -537,6 +527,7 @@ struct ib_ucontext *ocrdma_alloc_ucontext(struct ib_device *ibdev, return &ctx->ibucontext; cpy_err: + ocrdma_dealloc_ucontext_pd(ctx); pd_err: ocrdma_del_mmap(ctx, ctx->ah_tbl.pa, ctx->ah_tbl.len); map_err: @@ -658,10 +649,10 @@ dpp_map_err: return status; } -struct ib_pd *ocrdma_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +int ocrdma_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { + struct ib_device *ibdev = ibpd->device; struct ocrdma_dev *dev = get_ocrdma_dev(ibdev); struct ocrdma_pd *pd; struct ocrdma_ucontext *uctx = NULL; @@ -677,11 +668,10 @@ struct ib_pd *ocrdma_alloc_pd(struct ib_device *ibdev, } } - pd = _ocrdma_alloc_pd(dev, uctx, udata); - if (IS_ERR(pd)) { - status = PTR_ERR(pd); + pd = get_ocrdma_pd(ibpd); + status = _ocrdma_alloc_pd(dev, pd, uctx, udata); + if (status) goto exit; - } pd_mapping: if (udata && context) { @@ -689,25 +679,22 @@ pd_mapping: if (status) goto err; } - return &pd->ibpd; + return 0; err: - if (is_uctx_pd) { + if (is_uctx_pd) ocrdma_release_ucontext_pd(uctx); - } else { - if (_ocrdma_dealloc_pd(dev, pd)) - pr_err("%s: _ocrdma_dealloc_pd() failed\n", __func__); - } + else + _ocrdma_dealloc_pd(dev, pd); exit: - return ERR_PTR(status); + return status; } -int ocrdma_dealloc_pd(struct ib_pd *ibpd) +void ocrdma_dealloc_pd(struct ib_pd *ibpd) { struct ocrdma_pd *pd = get_ocrdma_pd(ibpd); struct ocrdma_dev *dev = get_ocrdma_dev(ibpd->device); struct ocrdma_ucontext *uctx = NULL; - int status = 0; u64 usr_db; uctx = pd->uctx; @@ -721,11 +708,10 @@ int ocrdma_dealloc_pd(struct ib_pd *ibpd) if (is_ucontext_pd(uctx, pd)) { ocrdma_release_ucontext_pd(uctx); - return status; + return; } } - status = _ocrdma_dealloc_pd(dev, pd); - return status; + _ocrdma_dealloc_pd(dev, pd); } static int ocrdma_alloc_lkey(struct ocrdma_dev *dev, struct ocrdma_mr *mr, diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h index b69cfdce7970..1fd66721c930 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h @@ -70,9 +70,9 @@ int ocrdma_dealloc_ucontext(struct ib_ucontext *); int ocrdma_mmap(struct ib_ucontext *, struct vm_area_struct *vma); -struct ib_pd *ocrdma_alloc_pd(struct ib_device *, - struct ib_ucontext *, struct ib_udata *); -int ocrdma_dealloc_pd(struct ib_pd *pd); +int ocrdma_alloc_pd(struct ib_pd *pd, struct ib_ucontext *uctx, + struct ib_udata *udata); +void ocrdma_dealloc_pd(struct ib_pd *pd); struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, const struct ib_cq_init_attr *attr, diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index 878e9e23652b..44ce4989dcef 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -239,6 +239,7 @@ static const struct ib_device_ops qedr_dev_ops = { .reg_user_mr = qedr_reg_user_mr, .req_notify_cq = qedr_arm_cq, .resize_cq = qedr_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, qedr_pd, ibpd), }; static int qedr_register_device(struct qedr_dev *dev) diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 989f08633fbe..a06d2258394a 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -450,11 +450,12 @@ int qedr_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) vma->vm_page_prot); } -struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, struct ib_udata *udata) +int qedr_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { + struct ib_device *ibdev = ibpd->device; struct qedr_dev *dev = get_qedr_dev(ibdev); - struct qedr_pd *pd; + struct qedr_pd *pd = get_qedr_pd(ibpd); u16 pd_id; int rc; @@ -463,16 +464,12 @@ struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev, if (!dev->rdma_ctx) { DP_ERR(dev, "invalid RDMA context\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - rc = dev->ops->rdma_alloc_pd(dev->rdma_ctx, &pd_id); if (rc) - goto err; + return rc; pd->pd_id = pd_id; @@ -485,36 +482,23 @@ struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev, if (rc) { DP_ERR(dev, "copy error pd_id=0x%x.\n", pd_id); dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd_id); - goto err; + return rc; } pd->uctx = get_qedr_ucontext(context); pd->uctx->pd = pd; } - return &pd->ibpd; - -err: - kfree(pd); - return ERR_PTR(rc); + return 0; } -int qedr_dealloc_pd(struct ib_pd *ibpd) +void qedr_dealloc_pd(struct ib_pd *ibpd) { struct qedr_dev *dev = get_qedr_dev(ibpd->device); struct qedr_pd *pd = get_qedr_pd(ibpd); - if (!pd) { - pr_err("Invalid PD received in dealloc_pd\n"); - return -EINVAL; - } - DP_DEBUG(dev, QEDR_MSG_INIT, "Deallocating PD %d\n", pd->pd_id); dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd->pd_id); - - kfree(pd); - - return 0; } static void qedr_free_pbl(struct qedr_dev *dev, diff --git a/drivers/infiniband/hw/qedr/verbs.h b/drivers/infiniband/hw/qedr/verbs.h index 1852b7012bf4..97a6ff3f9afb 100644 --- a/drivers/infiniband/hw/qedr/verbs.h +++ b/drivers/infiniband/hw/qedr/verbs.h @@ -47,9 +47,9 @@ struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *, struct ib_udata *); int qedr_dealloc_ucontext(struct ib_ucontext *); int qedr_mmap(struct ib_ucontext *, struct vm_area_struct *vma); -struct ib_pd *qedr_alloc_pd(struct ib_device *, - struct ib_ucontext *, struct ib_udata *); -int qedr_dealloc_pd(struct ib_pd *pd); +int qedr_alloc_pd(struct ib_pd *pd, struct ib_ucontext *uctx, + struct ib_udata *udata); +void qedr_dealloc_pd(struct ib_pd *pd); struct ib_cq *qedr_create_cq(struct ib_device *ibdev, const struct ib_cq_init_attr *attr, diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index 1ec155823716..256ad2f236c8 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -352,6 +352,7 @@ static const struct ib_device_ops usnic_dev_ops = { .query_port = usnic_ib_query_port, .query_qp = usnic_ib_query_qp, .reg_user_mr = usnic_ib_reg_mr, + INIT_RDMA_OBJ_SIZE(ib_pd, usnic_ib_pd, ibpd), }; /* Start of PF discovery section */ diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 9dea18106247..0ced89b51448 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -456,37 +456,23 @@ int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, return 0; } -struct ib_pd *usnic_ib_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +int usnic_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct usnic_ib_pd *pd; + struct usnic_ib_pd *pd = to_upd(ibpd); void *umem_pd; - usnic_dbg("\n"); - - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - umem_pd = pd->umem_pd = usnic_uiom_alloc_pd(); if (IS_ERR_OR_NULL(umem_pd)) { - kfree(pd); - return ERR_PTR(umem_pd ? PTR_ERR(umem_pd) : -ENOMEM); + return umem_pd ? PTR_ERR(umem_pd) : -ENOMEM; } - usnic_info("domain 0x%p allocated for context 0x%p and device %s\n", - pd, context, dev_name(&ibdev->dev)); - return &pd->ibpd; + return 0; } -int usnic_ib_dealloc_pd(struct ib_pd *pd) +void usnic_ib_dealloc_pd(struct ib_pd *pd) { - usnic_info("freeing domain 0x%p\n", pd); - usnic_uiom_dealloc_pd((to_upd(pd))->umem_pd); - kfree(pd); - return 0; } struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd, diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h index 99a6d81c2bcd..44a9d2f82bf5 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h @@ -51,10 +51,9 @@ int usnic_ib_query_gid(struct ib_device *ibdev, u8 port, int index, struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num); int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey); -struct ib_pd *usnic_ib_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata); -int usnic_ib_dealloc_pd(struct ib_pd *pd); +int usnic_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata); +void usnic_ib_dealloc_pd(struct ib_pd *pd); struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *init_attr, struct ib_udata *udata); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index e582beaf9430..47e653d2495c 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -195,6 +195,7 @@ static const struct ib_device_ops pvrdma_dev_ops = { .query_qp = pvrdma_query_qp, .reg_user_mr = pvrdma_reg_user_mr, .req_notify_cq = pvrdma_req_notify_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, pvrdma_pd, ibpd), }; static const struct ib_device_ops pvrdma_dev_srq_ops = { diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index fafb2add3b44..f44220f72e05 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -438,37 +438,29 @@ int pvrdma_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma) /** * pvrdma_alloc_pd - allocate protection domain - * @ibdev: the IB device + * @ibpd: PD pointer * @context: user context * @udata: user data * * @return: the ib_pd protection domain pointer on success, otherwise errno. */ -struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +int pvrdma_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct pvrdma_pd *pd; + struct ib_device *ibdev = ibpd->device; + struct pvrdma_pd *pd = to_vpd(ibpd); struct pvrdma_dev *dev = to_vdev(ibdev); - union pvrdma_cmd_req req; - union pvrdma_cmd_resp rsp; + union pvrdma_cmd_req req = {}; + union pvrdma_cmd_resp rsp = {}; struct pvrdma_cmd_create_pd *cmd = &req.create_pd; struct pvrdma_cmd_create_pd_resp *resp = &rsp.create_pd_resp; struct pvrdma_alloc_pd_resp pd_resp = {0}; int ret; - void *ptr; /* Check allowed max pds */ if (!atomic_add_unless(&dev->num_pds, 1, dev->dsr->caps.max_pd)) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - ptr = ERR_PTR(-ENOMEM); - goto err; - } - - memset(cmd, 0, sizeof(*cmd)); cmd->hdr.cmd = PVRDMA_CMD_CREATE_PD; cmd->ctx_handle = (context) ? to_vucontext(context)->ctx_handle : 0; ret = pvrdma_cmd_post(dev, &req, &rsp, PVRDMA_CMD_CREATE_PD_RESP); @@ -476,8 +468,7 @@ struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev, dev_warn(&dev->pdev->dev, "failed to allocate protection domain, error: %d\n", ret); - ptr = ERR_PTR(ret); - goto freepd; + goto err; } pd->privileged = !context; @@ -490,18 +481,16 @@ struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev, dev_warn(&dev->pdev->dev, "failed to copy back protection domain\n"); pvrdma_dealloc_pd(&pd->ibpd); - return ERR_PTR(-EFAULT); + return -EFAULT; } } /* u32 pd handle */ - return &pd->ibpd; + return 0; -freepd: - kfree(pd); err: atomic_dec(&dev->num_pds); - return ptr; + return ret; } /** @@ -510,14 +499,13 @@ err: * * @return: 0 on success, otherwise errno. */ -int pvrdma_dealloc_pd(struct ib_pd *pd) +void pvrdma_dealloc_pd(struct ib_pd *pd) { struct pvrdma_dev *dev = to_vdev(pd->device); - union pvrdma_cmd_req req; + union pvrdma_cmd_req req = {}; struct pvrdma_cmd_destroy_pd *cmd = &req.destroy_pd; int ret; - memset(cmd, 0, sizeof(*cmd)); cmd->hdr.cmd = PVRDMA_CMD_DESTROY_PD; cmd->pd_handle = to_vpd(pd)->pd_handle; @@ -527,10 +515,7 @@ int pvrdma_dealloc_pd(struct ib_pd *pd) "could not dealloc protection domain, error: %d\n", ret); - kfree(to_vpd(pd)); atomic_dec(&dev->num_pds); - - return 0; } /** diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h index f7f758d60110..ed91baad1ffa 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h @@ -399,10 +399,9 @@ int pvrdma_mmap(struct ib_ucontext *context, struct vm_area_struct *vma); struct ib_ucontext *pvrdma_alloc_ucontext(struct ib_device *ibdev, struct ib_udata *udata); int pvrdma_dealloc_ucontext(struct ib_ucontext *context); -struct ib_pd *pvrdma_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata); -int pvrdma_dealloc_pd(struct ib_pd *ibpd); +int pvrdma_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata); +void pvrdma_dealloc_pd(struct ib_pd *ibpd); struct ib_mr *pvrdma_get_dma_mr(struct ib_pd *pd, int acc); struct ib_mr *pvrdma_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int access_flags, diff --git a/drivers/infiniband/sw/rdmavt/pd.c b/drivers/infiniband/sw/rdmavt/pd.c index dcc1870b8d23..6033054b22fa 100644 --- a/drivers/infiniband/sw/rdmavt/pd.c +++ b/drivers/infiniband/sw/rdmavt/pd.c @@ -50,7 +50,7 @@ /** * rvt_alloc_pd - allocate a protection domain - * @ibdev: ib device + * @ibpd: PD * @context: optional user context * @udata: optional user data * @@ -58,19 +58,14 @@ * * Return: 0 on success */ -struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata) +int rvt_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { + struct ib_device *ibdev = ibpd->device; struct rvt_dev_info *dev = ib_to_rvt(ibdev); - struct rvt_pd *pd; - struct ib_pd *ret; + struct rvt_pd *pd = ibpd_to_rvtpd(ibpd); + int ret = 0; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - ret = ERR_PTR(-ENOMEM); - goto bail; - } /* * While we could continue allocating protecetion domains, being * constrained only by system resources. The IBTA spec defines that @@ -81,8 +76,7 @@ struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev, spin_lock(&dev->n_pds_lock); if (dev->n_pds_allocated == dev->dparms.props.max_pd) { spin_unlock(&dev->n_pds_lock); - kfree(pd); - ret = ERR_PTR(-ENOMEM); + ret = -ENOMEM; goto bail; } @@ -92,8 +86,6 @@ struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev, /* ib_alloc_pd() will initialize pd->ibpd. */ pd->user = !!udata; - ret = &pd->ibpd; - bail: return ret; } @@ -104,16 +96,11 @@ bail: * * Return: always 0 */ -int rvt_dealloc_pd(struct ib_pd *ibpd) +void rvt_dealloc_pd(struct ib_pd *ibpd) { - struct rvt_pd *pd = ibpd_to_rvtpd(ibpd); struct rvt_dev_info *dev = ib_to_rvt(ibpd->device); spin_lock(&dev->n_pds_lock); dev->n_pds_allocated--; spin_unlock(&dev->n_pds_lock); - - kfree(pd); - - return 0; } diff --git a/drivers/infiniband/sw/rdmavt/pd.h b/drivers/infiniband/sw/rdmavt/pd.h index 1892ca4a9746..7a887e4a45e7 100644 --- a/drivers/infiniband/sw/rdmavt/pd.h +++ b/drivers/infiniband/sw/rdmavt/pd.h @@ -50,9 +50,8 @@ #include -struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev, - struct ib_ucontext *context, - struct ib_udata *udata); -int rvt_dealloc_pd(struct ib_pd *ibpd); +int rvt_alloc_pd(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata); +void rvt_dealloc_pd(struct ib_pd *ibpd); #endif /* DEF_RDMAVTPD_H */ diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index b3f0c5578925..a19832c73d5a 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -436,6 +436,7 @@ static const struct ib_device_ops rvt_dev_ops = { .req_notify_cq = rvt_req_notify_cq, .resize_cq = rvt_resize_cq, .unmap_fmr = rvt_unmap_fmr, + INIT_RDMA_OBJ_SIZE(ib_pd, rvt_pd, ibpd), }; static noinline int check_support(struct rvt_dev_info *rdi, int verb) diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c index b5c91df22047..cd3f14629ba8 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.c +++ b/drivers/infiniband/sw/rxe/rxe_pool.c @@ -46,6 +46,7 @@ struct rxe_type_info rxe_type_info[RXE_NUM_TYPES] = { [RXE_TYPE_PD] = { .name = "rxe-pd", .size = sizeof(struct rxe_pd), + .flags = RXE_POOL_NO_ALLOC, }, [RXE_TYPE_AH] = { .name = "rxe-ah", @@ -119,8 +120,10 @@ static void rxe_cache_clean(size_t cnt) for (i = 0; i < cnt; i++) { type = &rxe_type_info[i]; - kmem_cache_destroy(type->cache); - type->cache = NULL; + if (!(type->flags & RXE_POOL_NO_ALLOC)) { + kmem_cache_destroy(type->cache); + type->cache = NULL; + } } } @@ -134,14 +137,17 @@ int rxe_cache_init(void) for (i = 0; i < RXE_NUM_TYPES; i++) { type = &rxe_type_info[i]; size = ALIGN(type->size, RXE_POOL_ALIGN); - type->cache = kmem_cache_create(type->name, size, - RXE_POOL_ALIGN, - RXE_POOL_CACHE_FLAGS, NULL); - if (!type->cache) { - pr_err("Unable to init kmem cache for %s\n", - type->name); - err = -ENOMEM; - goto err1; + if (!(type->flags & RXE_POOL_NO_ALLOC)) { + type->cache = + kmem_cache_create(type->name, size, + RXE_POOL_ALIGN, + RXE_POOL_CACHE_FLAGS, NULL); + if (!type->cache) { + pr_err("Unable to init kmem cache for %s\n", + type->name); + err = -ENOMEM; + goto err1; + } } } @@ -415,6 +421,37 @@ out_put_pool: return NULL; } +int rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_entry *elem) +{ + unsigned long flags; + + might_sleep_if(!(pool->flags & RXE_POOL_ATOMIC)); + + read_lock_irqsave(&pool->pool_lock, flags); + if (pool->state != RXE_POOL_STATE_VALID) { + read_unlock_irqrestore(&pool->pool_lock, flags); + return -EINVAL; + } + kref_get(&pool->ref_cnt); + read_unlock_irqrestore(&pool->pool_lock, flags); + + kref_get(&pool->rxe->ref_cnt); + + if (atomic_inc_return(&pool->num_elem) > pool->max_elem) + goto out_put_pool; + + elem->pool = pool; + kref_init(&elem->ref_cnt); + + return 0; + +out_put_pool: + atomic_dec(&pool->num_elem); + rxe_dev_put(pool->rxe); + rxe_pool_put(pool); + return -EINVAL; +} + void rxe_elem_release(struct kref *kref) { struct rxe_pool_entry *elem = @@ -424,7 +461,8 @@ void rxe_elem_release(struct kref *kref) if (pool->cleanup) pool->cleanup(elem); - kmem_cache_free(pool_cache(pool), elem); + if (!(pool->flags & RXE_POOL_NO_ALLOC)) + kmem_cache_free(pool_cache(pool), elem); atomic_dec(&pool->num_elem); rxe_dev_put(pool->rxe); rxe_pool_put(pool); diff --git a/drivers/infiniband/sw/rxe/rxe_pool.h b/drivers/infiniband/sw/rxe/rxe_pool.h index 72968c29e01f..2f2cff1cbe43 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.h +++ b/drivers/infiniband/sw/rxe/rxe_pool.h @@ -41,6 +41,7 @@ enum rxe_pool_flags { RXE_POOL_ATOMIC = BIT(0), RXE_POOL_INDEX = BIT(1), RXE_POOL_KEY = BIT(2), + RXE_POOL_NO_ALLOC = BIT(4), }; enum rxe_elem_type { @@ -131,6 +132,9 @@ void rxe_pool_cleanup(struct rxe_pool *pool); /* allocate an object from pool */ void *rxe_alloc(struct rxe_pool *pool); +/* connect already allocated object to pool */ +int rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_entry *elem); + /* assign an index to an indexed object and insert object into * pool's rb tree */ diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index cc5a05124ece..051c3930e808 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -191,23 +191,20 @@ static int rxe_port_immutable(struct ib_device *dev, u8 port_num, return 0; } -static struct ib_pd *rxe_alloc_pd(struct ib_device *dev, - struct ib_ucontext *context, - struct ib_udata *udata) +static int rxe_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context, + struct ib_udata *udata) { - struct rxe_dev *rxe = to_rdev(dev); - struct rxe_pd *pd; + struct rxe_dev *rxe = to_rdev(ibpd->device); + struct rxe_pd *pd = to_rpd(ibpd); - pd = rxe_alloc(&rxe->pd_pool); - return pd ? &pd->ibpd : ERR_PTR(-ENOMEM); + return rxe_add_to_pool(&rxe->pd_pool, &pd->pelem); } -static int rxe_dealloc_pd(struct ib_pd *ibpd) +static void rxe_dealloc_pd(struct ib_pd *ibpd) { struct rxe_pd *pd = to_rpd(ibpd); rxe_drop_ref(pd); - return 0; } static struct ib_ah *rxe_create_ah(struct ib_pd *ibpd, @@ -1183,6 +1180,7 @@ static const struct ib_device_ops rxe_dev_ops = { .reg_user_mr = rxe_reg_user_mr, .req_notify_cq = rxe_req_notify_cq, .resize_cq = rxe_resize_cq, + INIT_RDMA_OBJ_SIZE(ib_pd, rxe_pd, ibpd), }; int rxe_register_device(struct rxe_dev *rxe) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h index 74e04801d34d..70839d3f55d9 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.h +++ b/drivers/infiniband/sw/rxe/rxe_verbs.h @@ -66,8 +66,8 @@ struct rxe_ucontext { }; struct rxe_pd { + struct ib_pd ibpd; struct rxe_pool_entry pelem; - struct ib_pd ibpd; }; struct rxe_ah { diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index e29eae4aec84..854d7816787c 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2385,10 +2385,9 @@ struct ib_device_ops { int (*dealloc_ucontext)(struct ib_ucontext *context); int (*mmap)(struct ib_ucontext *context, struct vm_area_struct *vma); void (*disassociate_ucontext)(struct ib_ucontext *ibcontext); - struct ib_pd *(*alloc_pd)(struct ib_device *device, - struct ib_ucontext *context, - struct ib_udata *udata); - int (*dealloc_pd)(struct ib_pd *pd); + int (*alloc_pd)(struct ib_pd *pd, struct ib_ucontext *context, + struct ib_udata *udata); + void (*dealloc_pd)(struct ib_pd *pd); struct ib_ah *(*create_ah)(struct ib_pd *pd, struct rdma_ah_attr *ah_attr, u32 flags, struct ib_udata *udata); @@ -2530,6 +2529,8 @@ struct ib_device_ops { */ int (*fill_res_entry)(struct sk_buff *msg, struct rdma_restrack_entry *entry); + + DECLARE_RDMA_OBJ_SIZE(ib_pd); }; struct ib_device { From e3593b568a68b0e1a434b80fd6eaebfb655e839d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:47 -0700 Subject: [PATCH 1446/4277] RDMA/device: Check that the rename is nop under the lock Since another rename could be running in parallel it is safer to check that the name is not changing inside the lock, where we already know the device name will not change. Fixes: d21943dd19b5 ("RDMA/core: Implement IB device rename function") Signed-off-by: Jason Gunthorpe Reviewed-by: Parav Pandit --- drivers/infiniband/core/device.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 57e1e177921e..60083bde3e39 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -189,12 +189,14 @@ static struct ib_device *__ib_device_get_by_name(const char *name) int ib_device_rename(struct ib_device *ibdev, const char *name) { - int ret = 0; - - if (!strcmp(name, dev_name(&ibdev->dev))) - return ret; + int ret; mutex_lock(&device_mutex); + if (!strcmp(name, dev_name(&ibdev->dev))) { + ret = 0; + goto out; + } + if (__ib_device_get_by_name(name)) { ret = -EEXIST; goto out; From b34b269ad85d7dd4a512487f2395c3be3e40f76a Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:48 -0700 Subject: [PATCH 1447/4277] RDMA/device: Ensure that security memory is always freed Since this only frees memory it should be done during the release callback. Otherwise there are possible error flows where it might not get called if registration aborts. Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/core_priv.h | 4 ++-- drivers/infiniband/core/device.c | 10 +++------- drivers/infiniband/core/security.c | 4 +--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index d053110207eb..a1826f4c2e23 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -181,7 +181,7 @@ int ib_get_cached_subnet_prefix(struct ib_device *device, u64 *sn_pfx); #ifdef CONFIG_SECURITY_INFINIBAND -void ib_security_destroy_port_pkey_list(struct ib_device *device); +void ib_security_release_port_pkey_list(struct ib_device *device); void ib_security_cache_change(struct ib_device *device, u8 port_num, @@ -204,7 +204,7 @@ void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent); int ib_mad_enforce_security(struct ib_mad_agent_private *map, u16 pkey_index); void ib_mad_agent_security_change(void); #else -static inline void ib_security_destroy_port_pkey_list(struct ib_device *device) +static inline void ib_security_release_port_pkey_list(struct ib_device *device) { } diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 60083bde3e39..b997feac2c63 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -253,6 +253,8 @@ static void ib_device_release(struct device *device) ib_cache_release_one(dev); kfree(dev->port_immutable); } + ib_security_release_port_pkey_list(dev); + kfree(dev->port_pkey_list); kfree(dev); } @@ -522,7 +524,6 @@ static void cleanup_device(struct ib_device *device) { ib_cache_cleanup_one(device); ib_cache_release_one(device); - kfree(device->port_pkey_list); kfree(device->port_immutable); } @@ -560,12 +561,10 @@ static int setup_device(struct ib_device *device) if (ret) { dev_warn(&device->dev, "Couldn't set up InfiniBand P_Key/GID cache\n"); - goto pkey_cleanup; + return ret; } return 0; -pkey_cleanup: - kfree(device->port_pkey_list); port_cleanup: kfree(device->port_immutable); return ret; @@ -682,9 +681,6 @@ void ib_unregister_device(struct ib_device *device) ib_cache_cleanup_one(device); - ib_security_destroy_port_pkey_list(device); - kfree(device->port_pkey_list); - down_write(&lists_rwsem); write_lock_irqsave(&device->client_data_lock, flags); list_for_each_entry_safe(context, tmp, &device->client_data_list, diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index a70d2ba312ed..dad6a94a43f3 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -558,13 +558,12 @@ void ib_security_cache_change(struct ib_device *device, } } -void ib_security_destroy_port_pkey_list(struct ib_device *device) +void ib_security_release_port_pkey_list(struct ib_device *device) { struct pkey_index_qp_list *pkey, *tmp_pkey; int i; for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) { - spin_lock(&device->port_pkey_list[i].list_lock); list_for_each_entry_safe(pkey, tmp_pkey, &device->port_pkey_list[i].pkey_list, @@ -572,7 +571,6 @@ void ib_security_destroy_port_pkey_list(struct ib_device *device) list_del(&pkey->pkey_index_list); kfree(pkey); } - spin_unlock(&device->port_pkey_list[i].list_lock); } } From d45f89d59bcd42d6b8575d0af69d7a3a98e73bb6 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:49 -0700 Subject: [PATCH 1448/4277] RDMA/device: Call ib_cache_release_one() only from ib_device_release() Instead of complicated logic about when this memory is freed, always free it during device release(). All the cache pointers start out as NULL, so it is safe to call this before the cache is initialized. This makes for a simpler error unwind flow, and a simpler understanding of the lifetime of the memory allocations inside the struct ib_device. Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cache.c | 3 +++ drivers/infiniband/core/device.c | 41 ++++++++++---------------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 7b04590f307f..2338d0b3a0ca 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -1460,6 +1460,9 @@ void ib_cache_release_one(struct ib_device *device) { int p; + if (!device->cache.ports) + return; + /* * The release function frees all the cache elements. * This function should be called as part of freeing diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index b997feac2c63..872662a84b16 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -244,17 +244,10 @@ static void ib_device_release(struct device *device) struct ib_device *dev = container_of(device, struct ib_device, dev); WARN_ON(dev->reg_state == IB_DEV_REGISTERED); - if (dev->reg_state == IB_DEV_UNREGISTERED) { - /* - * In IB_DEV_UNINITIALIZED state, cache or port table - * is not even created. Free cache and port table only when - * device reaches UNREGISTERED state. - */ - ib_cache_release_one(dev); - kfree(dev->port_immutable); - } + ib_cache_release_one(dev); ib_security_release_port_pkey_list(dev); kfree(dev->port_pkey_list); + kfree(dev->port_immutable); kfree(dev); } @@ -520,13 +513,6 @@ static void setup_dma_device(struct ib_device *device) } } -static void cleanup_device(struct ib_device *device) -{ - ib_cache_cleanup_one(device); - ib_cache_release_one(device); - kfree(device->port_immutable); -} - static int setup_device(struct ib_device *device) { struct ib_udata uhw = {.outlen = 0, .inlen = 0}; @@ -548,26 +534,16 @@ static int setup_device(struct ib_device *device) if (ret) { dev_warn(&device->dev, "Couldn't query the device attributes\n"); - goto port_cleanup; + return ret; } ret = setup_port_pkey_list(device); if (ret) { dev_warn(&device->dev, "Couldn't create per port_pkey_list\n"); - goto port_cleanup; - } - - ret = ib_cache_setup_one(device); - if (ret) { - dev_warn(&device->dev, - "Couldn't set up InfiniBand P_Key/GID cache\n"); return ret; } - return 0; -port_cleanup: - kfree(device->port_immutable); - return ret; + return 0; } /** @@ -607,6 +583,13 @@ int ib_register_device(struct ib_device *device, const char *name) if (ret) goto out; + ret = ib_cache_setup_one(device); + if (ret) { + dev_warn(&device->dev, + "Couldn't set up InfiniBand P_Key/GID cache\n"); + goto out; + } + device->index = __dev_new_index(); ib_device_register_rdmacg(device); @@ -633,7 +616,7 @@ int ib_register_device(struct ib_device *device, const char *name) cg_cleanup: ib_device_unregister_rdmacg(device); - cleanup_device(device); + ib_cache_cleanup_one(device); out: mutex_unlock(&device_mutex); return ret; From 652432f33c01b2edaa5b2550b423cd894b1c7b9a Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:50 -0700 Subject: [PATCH 1449/4277] RDMA/device: Get rid of reg_state This really has no purpose anymore, refcount can be used to tell if the device is still registered. Keeping it around just invites mis-use. Signed-off-by: Jason Gunthorpe Reviewed-by: Parav Pandit --- drivers/infiniband/core/device.c | 8 ++------ include/rdma/ib_verbs.h | 6 ------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 872662a84b16..1c54ded776d0 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -243,7 +243,7 @@ static void ib_device_release(struct device *device) { struct ib_device *dev = container_of(device, struct ib_device, dev); - WARN_ON(dev->reg_state == IB_DEV_REGISTERED); + WARN_ON(refcount_read(&dev->refcount)); ib_cache_release_one(dev); ib_security_release_port_pkey_list(dev); kfree(dev->port_pkey_list); @@ -316,8 +316,7 @@ EXPORT_SYMBOL(_ib_alloc_device); void ib_dealloc_device(struct ib_device *device) { WARN_ON(!list_empty(&device->client_data_list)); - WARN_ON(device->reg_state != IB_DEV_UNREGISTERED && - device->reg_state != IB_DEV_UNINITIALIZED); + WARN_ON(refcount_read(&device->refcount)); rdma_restrack_clean(device); put_device(&device->dev); } @@ -602,7 +601,6 @@ int ib_register_device(struct ib_device *device, const char *name) } refcount_set(&device->refcount, 1); - device->reg_state = IB_DEV_REGISTERED; list_for_each_entry(client, &client_list, list) if (!add_client_context(device, client) && client->add) @@ -673,8 +671,6 @@ void ib_unregister_device(struct ib_device *device) } write_unlock_irqrestore(&device->client_data_lock, flags); up_write(&lists_rwsem); - - device->reg_state = IB_DEV_UNREGISTERED; } EXPORT_SYMBOL(ib_unregister_device); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 854d7816787c..d8ba987e8b29 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2572,12 +2572,6 @@ struct ib_device { struct kobject *ports_kobj; struct list_head port_list; - enum { - IB_DEV_UNINITIALIZED, - IB_DEV_REGISTERED, - IB_DEV_UNREGISTERED - } reg_state; - int uverbs_abi_ver; u64 uverbs_cmd_mask; u64 uverbs_ex_cmd_mask; From 3b88afd38e88d1bb2e900204ff0af7301a379a09 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:51 -0700 Subject: [PATCH 1450/4277] RDMA/device: Use an ida instead of a free page in alloc_name ida is the proper data structure to hold list of clustered small integers and then allocate an unused integer. Get rid of the convoluted and limited open-coded bitmap. Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 1c54ded776d0..3a80f96c2919 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -213,30 +213,36 @@ out: static int alloc_name(struct ib_device *ibdev, const char *name) { - unsigned long *inuse; struct ib_device *device; + struct ida inuse; + int rc; int i; - inuse = (unsigned long *) get_zeroed_page(GFP_KERNEL); - if (!inuse) - return -ENOMEM; - + ida_init(&inuse); list_for_each_entry(device, &device_list, core_list) { char buf[IB_DEVICE_NAME_MAX]; if (sscanf(dev_name(&device->dev), name, &i) != 1) continue; - if (i < 0 || i >= PAGE_SIZE * 8) + if (i < 0 || i >= INT_MAX) continue; snprintf(buf, sizeof buf, name, i); - if (!strcmp(buf, dev_name(&device->dev))) - set_bit(i, inuse); + if (strcmp(buf, dev_name(&device->dev)) != 0) + continue; + + rc = ida_alloc_range(&inuse, i, i, GFP_KERNEL); + if (rc < 0) + goto out; } - i = find_first_zero_bit(inuse, PAGE_SIZE * 8); - free_page((unsigned long) inuse); + rc = ida_alloc(&inuse, GFP_KERNEL); + if (rc < 0) + goto out; - return dev_set_name(&ibdev->dev, name, i); + rc = dev_set_name(&ibdev->dev, name, rc); +out: + ida_destroy(&inuse); + return rc; } static void ib_device_release(struct device *device) From e59178d895afa29b671323f8265a1e50afe989e5 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:52 -0700 Subject: [PATCH 1451/4277] RDMA/devices: Use xarray to store the clients This gives each client a unique ID and will let us move client_data to use xarray, and revise the locking scheme. clients have to be add/removed in strict FIFO/LIFO order as they interdepend. To support this the client_ids are assigned to increase in FIFO order. The existing linked list is kept to support reverse iteration until xarray can get a reverse iteration API. Signed-off-by: Jason Gunthorpe Reviewed-by: Parav Pandit --- drivers/infiniband/core/device.c | 50 ++++++++++++++++++++++++++++---- include/rdma/ib_verbs.h | 3 +- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 3a80f96c2919..f87d85659359 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -65,15 +65,17 @@ struct workqueue_struct *ib_comp_unbound_wq; struct workqueue_struct *ib_wq; EXPORT_SYMBOL_GPL(ib_wq); -/* The device_list and client_list contain devices and clients after their +/* The device_list and clients contain devices and clients after their * registration has completed, and the devices and clients are removed * during unregistration. */ static LIST_HEAD(device_list); static LIST_HEAD(client_list); +#define CLIENT_REGISTERED XA_MARK_1 +static DEFINE_XARRAY_FLAGS(clients, XA_FLAGS_ALLOC); /* * device_mutex and lists_rwsem protect access to both device_list and - * client_list. device_mutex protects writer access by device and client + * clients. device_mutex protects writer access by device and client * registration / de-registration. lists_rwsem protects reader access to * these lists. Iterators of these lists must lock it for read, while updates * to the lists must be done with a write lock. A special case is when the @@ -564,6 +566,7 @@ int ib_register_device(struct ib_device *device, const char *name) { int ret; struct ib_client *client; + unsigned long index; setup_dma_device(device); @@ -608,7 +611,7 @@ int ib_register_device(struct ib_device *device, const char *name) refcount_set(&device->refcount, 1); - list_for_each_entry(client, &client_list, list) + xa_for_each_marked (&clients, index, client, CLIENT_REGISTERED) if (!add_client_context(device, client) && client->add) client->add(device); @@ -680,6 +683,32 @@ void ib_unregister_device(struct ib_device *device) } EXPORT_SYMBOL(ib_unregister_device); +static int assign_client_id(struct ib_client *client) +{ + int ret; + + /* + * The add/remove callbacks must be called in FIFO/LIFO order. To + * achieve this we assign client_ids so they are sorted in + * registration order, and retain a linked list we can reverse iterate + * to get the LIFO order. The extra linked list can go away if xarray + * learns to reverse iterate. + */ + if (list_empty(&client_list)) + client->client_id = 0; + else + client->client_id = + list_last_entry(&client_list, struct ib_client, list) + ->client_id; + ret = xa_alloc(&clients, &client->client_id, INT_MAX, client, + GFP_KERNEL); + if (ret) + goto out; + +out: + return ret; +} + /** * ib_register_client - Register an IB client * @client:Client to register @@ -696,15 +725,21 @@ EXPORT_SYMBOL(ib_unregister_device); int ib_register_client(struct ib_client *client) { struct ib_device *device; + int ret; mutex_lock(&device_mutex); + ret = assign_client_id(client); + if (ret) { + mutex_unlock(&device_mutex); + return ret; + } list_for_each_entry(device, &device_list, core_list) if (!add_client_context(device, client) && client->add) client->add(device); down_write(&lists_rwsem); - list_add_tail(&client->list, &client_list); + xa_set_mark(&clients, client->client_id, CLIENT_REGISTERED); up_write(&lists_rwsem); mutex_unlock(&device_mutex); @@ -729,7 +764,7 @@ void ib_unregister_client(struct ib_client *client) mutex_lock(&device_mutex); down_write(&lists_rwsem); - list_del(&client->list); + xa_clear_mark(&clients, client->client_id, CLIENT_REGISTERED); up_write(&lists_rwsem); list_for_each_entry(device, &device_list, core_list) { @@ -765,6 +800,10 @@ void ib_unregister_client(struct ib_client *client) kfree(found_context); } + down_write(&lists_rwsem); + list_del(&client->list); + xa_erase(&clients, client->client_id); + up_write(&lists_rwsem); mutex_unlock(&device_mutex); } EXPORT_SYMBOL(ib_unregister_client); @@ -1422,6 +1461,7 @@ static void __exit ib_core_cleanup(void) destroy_workqueue(ib_comp_wq); /* Make sure that any pending umem accounting work is done. */ destroy_workqueue(ib_wq); + WARN_ON(!xa_empty(&clients)); } MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_LS, 4); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index d8ba987e8b29..cc15820513cd 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2610,7 +2610,7 @@ struct ib_device { }; struct ib_client { - char *name; + const char *name; void (*add) (struct ib_device *); void (*remove)(struct ib_device *, void *client_data); @@ -2637,6 +2637,7 @@ struct ib_client { const struct sockaddr *addr, void *client_data); struct list_head list; + u32 client_id; /* kverbs are not required by the client */ u8 no_kverbs_req:1; From 0df91bb67334eebaf73d4ba32567e16d55f4f116 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:53 -0700 Subject: [PATCH 1452/4277] RDMA/devices: Use xarray to store the client_data Now that we have a small ID for each client we can use xarray instead of linearly searching linked lists for client data. This will give much faster and scalable client data lookup, and will lets us revise the locking scheme. Since xarray can store 'going_down' using a mark just entirely eliminate the struct ib_client_data and directly store the client_data value in the xarray. However this does require a special iterator as we must still iterate over any NULL client_data values. Also eliminate the client_data_lock in favour of internal xarray locking. Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 348 +++++++++++++++---------------- include/rdma/ib_verbs.h | 23 +- 2 files changed, 186 insertions(+), 185 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index f87d85659359..5096593b99e9 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -51,30 +51,72 @@ MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("core kernel InfiniBand API"); MODULE_LICENSE("Dual BSD/GPL"); -struct ib_client_data { - struct list_head list; - struct ib_client *client; - void * data; - /* The device or client is going down. Do not call client or device - * callbacks other than remove(). */ - bool going_down; -}; - struct workqueue_struct *ib_comp_wq; struct workqueue_struct *ib_comp_unbound_wq; struct workqueue_struct *ib_wq; EXPORT_SYMBOL_GPL(ib_wq); -/* The device_list and clients contain devices and clients after their - * registration has completed, and the devices and clients are removed - * during unregistration. */ -static LIST_HEAD(device_list); +/* + * devices contains devices that have had their names assigned. The + * devices may not be registered. Users that care about the registration + * status need to call ib_device_try_get() on the device to ensure it is + * registered, and keep it registered, for the required duration. + * + */ +static DEFINE_XARRAY_FLAGS(devices, XA_FLAGS_ALLOC); + +/* + * Note that if the *rwsem is held and the *_REGISTERED mark is seen then the + * object is guaranteed to be and remain registered for the duration of the + * lock. + */ +#define DEVICE_REGISTERED XA_MARK_1 + static LIST_HEAD(client_list); #define CLIENT_REGISTERED XA_MARK_1 static DEFINE_XARRAY_FLAGS(clients, XA_FLAGS_ALLOC); /* - * device_mutex and lists_rwsem protect access to both device_list and + * If client_data is registered then the corresponding client must also still + * be registered. + */ +#define CLIENT_DATA_REGISTERED XA_MARK_1 +/* + * xarray has this behavior where it won't iterate over NULL values stored in + * allocated arrays. So we need our own iterator to see all values stored in + * the array. This does the same thing as xa_for_each except that it also + * returns NULL valued entries if the array is allocating. Simplified to only + * work on simple xarrays. + */ +static void *xan_find_marked(struct xarray *xa, unsigned long *indexp, + xa_mark_t filter) +{ + XA_STATE(xas, xa, *indexp); + void *entry; + + rcu_read_lock(); + do { + entry = xas_find_marked(&xas, ULONG_MAX, filter); + if (xa_is_zero(entry)) + break; + } while (xas_retry(&xas, entry)); + rcu_read_unlock(); + + if (entry) { + *indexp = xas.xa_index; + if (xa_is_zero(entry)) + return NULL; + return entry; + } + return XA_ERROR(-ENOENT); +} +#define xan_for_each_marked(xa, index, entry, filter) \ + for (index = 0, entry = xan_find_marked(xa, &(index), filter); \ + !xa_is_err(entry); \ + (index)++, entry = xan_find_marked(xa, &(index), filter)) + +/* + * device_mutex and lists_rwsem protect access to both devices and * clients. device_mutex protects writer access by device and client * registration / de-registration. lists_rwsem protects reader access to * these lists. Iterators of these lists must lock it for read, while updates @@ -135,17 +177,6 @@ static int ib_device_check_mandatory(struct ib_device *device) return 0; } -static struct ib_device *__ib_device_get_by_index(u32 index) -{ - struct ib_device *device; - - list_for_each_entry(device, &device_list, core_list) - if (device->index == index) - return device; - - return NULL; -} - /* * Caller must perform ib_device_put() to return the device reference count * when ib_device_get_by_index() returns valid device pointer. @@ -155,7 +186,7 @@ struct ib_device *ib_device_get_by_index(u32 index) struct ib_device *device; down_read(&lists_rwsem); - device = __ib_device_get_by_index(index); + device = xa_load(&devices, index); if (device) { if (!ib_device_try_get(device)) device = NULL; @@ -181,8 +212,9 @@ EXPORT_SYMBOL(ib_device_put); static struct ib_device *__ib_device_get_by_name(const char *name) { struct ib_device *device; + unsigned long index; - list_for_each_entry(device, &device_list, core_list) + xa_for_each (&devices, index, device) if (!strcmp(name, dev_name(&device->dev))) return device; @@ -216,12 +248,13 @@ out: static int alloc_name(struct ib_device *ibdev, const char *name) { struct ib_device *device; + unsigned long index; struct ida inuse; int rc; int i; ida_init(&inuse); - list_for_each_entry(device, &device_list, core_list) { + xa_for_each (&devices, index, device) { char buf[IB_DEVICE_NAME_MAX]; if (sscanf(dev_name(&device->dev), name, &i) != 1) @@ -256,6 +289,7 @@ static void ib_device_release(struct device *device) ib_security_release_port_pkey_list(dev); kfree(dev->port_pkey_list); kfree(dev->port_immutable); + xa_destroy(&dev->client_data); kfree(dev); } @@ -306,8 +340,11 @@ struct ib_device *_ib_alloc_device(size_t size) INIT_LIST_HEAD(&device->event_handler_list); spin_lock_init(&device->event_handler_lock); - rwlock_init(&device->client_data_lock); - INIT_LIST_HEAD(&device->client_data_list); + /* + * client_data needs to be alloc because we don't want our mark to be + * destroyed if the user stores NULL in the client data. + */ + xa_init_flags(&device->client_data, XA_FLAGS_ALLOC); INIT_LIST_HEAD(&device->port_list); init_completion(&device->unreg_completion); @@ -323,7 +360,7 @@ EXPORT_SYMBOL(_ib_alloc_device); */ void ib_dealloc_device(struct ib_device *device) { - WARN_ON(!list_empty(&device->client_data_list)); + WARN_ON(!xa_empty(&device->client_data)); WARN_ON(refcount_read(&device->refcount)); rdma_restrack_clean(device); put_device(&device->dev); @@ -332,26 +369,20 @@ EXPORT_SYMBOL(ib_dealloc_device); static int add_client_context(struct ib_device *device, struct ib_client *client) { - struct ib_client_data *context; + void *entry; if (!device->kverbs_provider && !client->no_kverbs_req) return -EOPNOTSUPP; - context = kmalloc(sizeof(*context), GFP_KERNEL); - if (!context) - return -ENOMEM; - - context->client = client; - context->data = NULL; - context->going_down = false; - down_write(&lists_rwsem); - write_lock_irq(&device->client_data_lock); - list_add(&context->list, &device->client_data_list); - write_unlock_irq(&device->client_data_lock); + entry = xa_store(&device->client_data, client->client_id, NULL, + GFP_KERNEL); + if (!xa_is_err(entry)) + xa_set_mark(&device->client_data, client->client_id, + CLIENT_DATA_REGISTERED); up_write(&lists_rwsem); - return 0; + return xa_err(entry); } static int verify_immutable(const struct ib_device *dev, u8 port) @@ -428,9 +459,10 @@ static int setup_port_pkey_list(struct ib_device *device) static void ib_policy_change_task(struct work_struct *work) { struct ib_device *dev; + unsigned long index; down_read(&lists_rwsem); - list_for_each_entry(dev, &device_list, core_list) { + xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) { int i; for (i = rdma_start_port(dev); i <= rdma_end_port(dev); i++) { @@ -461,28 +493,48 @@ static int ib_security_change(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } -/** - * __dev_new_index - allocate an device index - * - * Returns a suitable unique value for a new device interface - * number. It assumes that there are less than 2^32-1 ib devices - * will be present in the system. +/* + * Assign the unique string device name and the unique device index. */ -static u32 __dev_new_index(void) +static int assign_name(struct ib_device *device, const char *name) { - /* - * The device index to allow stable naming. - * Similar to struct net -> ifindex. - */ - static u32 index; + static u32 last_id; + int ret; - for (;;) { - if (!(++index)) - index = 1; + /* Assign a unique name to the device */ + if (strchr(name, '%')) + ret = alloc_name(device, name); + else + ret = dev_set_name(&device->dev, name); + if (ret) + goto out; - if (!__ib_device_get_by_index(index)) - return index; + if (__ib_device_get_by_name(dev_name(&device->dev))) { + ret = -ENFILE; + goto out; } + strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX); + + /* Cyclically allocate a user visible ID for the device */ + device->index = last_id; + ret = xa_alloc(&devices, &device->index, INT_MAX, device, GFP_KERNEL); + if (ret == -ENOSPC) { + device->index = 0; + ret = xa_alloc(&devices, &device->index, INT_MAX, device, + GFP_KERNEL); + } + if (ret) + goto out; + last_id = device->index + 1; + + ret = 0; +out: + return ret; +} + +static void release_name(struct ib_device *device) +{ + xa_erase(&devices, device->index); } static void setup_dma_device(struct ib_device *device) @@ -572,34 +624,21 @@ int ib_register_device(struct ib_device *device, const char *name) mutex_lock(&device_mutex); - if (strchr(name, '%')) { - ret = alloc_name(device, name); - if (ret) - goto out; - } else { - ret = dev_set_name(&device->dev, name); - if (ret) - goto out; - } - if (__ib_device_get_by_name(dev_name(&device->dev))) { - ret = -ENFILE; + ret = assign_name(device, name); + if (ret) goto out; - } - strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX); ret = setup_device(device); if (ret) - goto out; + goto out_name; ret = ib_cache_setup_one(device); if (ret) { dev_warn(&device->dev, "Couldn't set up InfiniBand P_Key/GID cache\n"); - goto out; + goto out_name; } - device->index = __dev_new_index(); - ib_device_register_rdmacg(device); ret = ib_device_register_sysfs(device); @@ -616,7 +655,7 @@ int ib_register_device(struct ib_device *device, const char *name) client->add(device); down_write(&lists_rwsem); - list_add_tail(&device->core_list, &device_list); + xa_set_mark(&devices, device->index, DEVICE_REGISTERED); up_write(&lists_rwsem); mutex_unlock(&device_mutex); return 0; @@ -624,6 +663,8 @@ int ib_register_device(struct ib_device *device, const char *name) cg_cleanup: ib_device_unregister_rdmacg(device); ib_cache_cleanup_one(device); +out_name: + release_name(device); out: mutex_unlock(&device_mutex); return ret; @@ -638,8 +679,8 @@ EXPORT_SYMBOL(ib_register_device); */ void ib_unregister_device(struct ib_device *device) { - struct ib_client_data *context, *tmp; - unsigned long flags; + struct ib_client *client; + unsigned long index; /* * Wait for all netlink command callers to finish working on the @@ -651,34 +692,31 @@ void ib_unregister_device(struct ib_device *device) mutex_lock(&device_mutex); down_write(&lists_rwsem); - list_del(&device->core_list); - write_lock_irq(&device->client_data_lock); - list_for_each_entry(context, &device->client_data_list, list) - context->going_down = true; - write_unlock_irq(&device->client_data_lock); + xa_clear_mark(&devices, device->index, DEVICE_REGISTERED); + xa_for_each (&clients, index, client) + xa_clear_mark(&device->client_data, index, + CLIENT_DATA_REGISTERED); downgrade_write(&lists_rwsem); - list_for_each_entry(context, &device->client_data_list, list) { - if (context->client->remove) - context->client->remove(device, context->data); - } + list_for_each_entry_reverse(client, &client_list, list) + if (xa_get_mark(&device->client_data, client->client_id, + CLIENT_DATA_REGISTERED) && + client->remove) + client->remove(device, xa_load(&device->client_data, + client->client_id)); up_read(&lists_rwsem); ib_device_unregister_sysfs(device); ib_device_unregister_rdmacg(device); + release_name(device); + mutex_unlock(&device_mutex); ib_cache_cleanup_one(device); down_write(&lists_rwsem); - write_lock_irqsave(&device->client_data_lock, flags); - list_for_each_entry_safe(context, tmp, &device->client_data_list, - list) { - list_del(&context->list); - kfree(context); - } - write_unlock_irqrestore(&device->client_data_lock, flags); + xa_destroy(&device->client_data); up_write(&lists_rwsem); } EXPORT_SYMBOL(ib_unregister_device); @@ -725,6 +763,7 @@ out: int ib_register_client(struct ib_client *client) { struct ib_device *device; + unsigned long index; int ret; mutex_lock(&device_mutex); @@ -734,7 +773,7 @@ int ib_register_client(struct ib_client *client) return ret; } - list_for_each_entry(device, &device_list, core_list) + xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) if (!add_client_context(device, client) && client->add) client->add(device); @@ -758,8 +797,8 @@ EXPORT_SYMBOL(ib_register_client); */ void ib_unregister_client(struct ib_client *client) { - struct ib_client_data *context; struct ib_device *device; + unsigned long index; mutex_lock(&device_mutex); @@ -767,37 +806,19 @@ void ib_unregister_client(struct ib_client *client) xa_clear_mark(&clients, client->client_id, CLIENT_REGISTERED); up_write(&lists_rwsem); - list_for_each_entry(device, &device_list, core_list) { - struct ib_client_data *found_context = NULL; - + xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) { down_write(&lists_rwsem); - write_lock_irq(&device->client_data_lock); - list_for_each_entry(context, &device->client_data_list, list) - if (context->client == client) { - context->going_down = true; - found_context = context; - break; - } - write_unlock_irq(&device->client_data_lock); + xa_clear_mark(&device->client_data, client->client_id, + CLIENT_DATA_REGISTERED); up_write(&lists_rwsem); if (client->remove) - client->remove(device, found_context ? - found_context->data : NULL); - - if (!found_context) { - dev_warn(&device->dev, - "No client context found for %s\n", - client->name); - continue; - } + client->remove(device, xa_load(&device->client_data, + client->client_id)); down_write(&lists_rwsem); - write_lock_irq(&device->client_data_lock); - list_del(&found_context->list); - write_unlock_irq(&device->client_data_lock); + xa_erase(&device->client_data, client->client_id); up_write(&lists_rwsem); - kfree(found_context); } down_write(&lists_rwsem); @@ -808,59 +829,28 @@ void ib_unregister_client(struct ib_client *client) } EXPORT_SYMBOL(ib_unregister_client); -/** - * ib_get_client_data - Get IB client context - * @device:Device to get context for - * @client:Client to get context for - * - * ib_get_client_data() returns client context set with - * ib_set_client_data(). - */ -void *ib_get_client_data(struct ib_device *device, struct ib_client *client) -{ - struct ib_client_data *context; - void *ret = NULL; - unsigned long flags; - - read_lock_irqsave(&device->client_data_lock, flags); - list_for_each_entry(context, &device->client_data_list, list) - if (context->client == client) { - ret = context->data; - break; - } - read_unlock_irqrestore(&device->client_data_lock, flags); - - return ret; -} -EXPORT_SYMBOL(ib_get_client_data); - /** * ib_set_client_data - Set IB client context * @device:Device to set context for * @client:Client to set context for * @data:Context to set * - * ib_set_client_data() sets client context that can be retrieved with - * ib_get_client_data(). + * ib_set_client_data() sets client context data that can be retrieved with + * ib_get_client_data(). This can only be called while the client is + * registered to the device, once the ib_client remove() callback returns this + * cannot be called. */ void ib_set_client_data(struct ib_device *device, struct ib_client *client, void *data) { - struct ib_client_data *context; - unsigned long flags; + void *rc; - write_lock_irqsave(&device->client_data_lock, flags); - list_for_each_entry(context, &device->client_data_list, list) - if (context->client == client) { - context->data = data; - goto out; - } + if (WARN_ON(IS_ERR(data))) + data = NULL; - dev_warn(&device->dev, "No client context found for %s\n", - client->name); - -out: - write_unlock_irqrestore(&device->client_data_lock, flags); + rc = xa_store(&device->client_data, client->client_id, data, + GFP_KERNEL); + WARN_ON(xa_is_err(rc)); } EXPORT_SYMBOL(ib_set_client_data); @@ -1018,9 +1008,10 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter, void *cookie) { struct ib_device *dev; + unsigned long index; down_read(&lists_rwsem); - list_for_each_entry(dev, &device_list, core_list) + xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) ib_enum_roce_netdev(dev, filter, filter_cookie, cb, cookie); up_read(&lists_rwsem); } @@ -1034,12 +1025,13 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter, int ib_enum_all_devs(nldev_callback nldev_cb, struct sk_buff *skb, struct netlink_callback *cb) { + unsigned long index; struct ib_device *dev; unsigned int idx = 0; int ret = 0; down_read(&lists_rwsem); - list_for_each_entry(dev, &device_list, core_list) { + xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) { ret = nldev_cb(dev, skb, cb, idx); if (ret) break; @@ -1212,26 +1204,25 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, const struct sockaddr *addr) { struct net_device *net_dev = NULL; - struct ib_client_data *context; + unsigned long index; + void *client_data; if (!rdma_protocol_ib(dev, port)) return NULL; down_read(&lists_rwsem); - list_for_each_entry(context, &dev->client_data_list, list) { - struct ib_client *client = context->client; + xan_for_each_marked (&dev->client_data, index, client_data, + CLIENT_DATA_REGISTERED) { + struct ib_client *client = xa_load(&clients, index); - if (context->going_down) + if (!client || !client->get_net_dev_by_params) continue; - if (client->get_net_dev_by_params) { - net_dev = client->get_net_dev_by_params(dev, port, pkey, - gid, addr, - context->data); - if (net_dev) - break; - } + net_dev = client->get_net_dev_by_params(dev, port, pkey, gid, + addr, client_data); + if (net_dev) + break; } up_read(&lists_rwsem); @@ -1462,6 +1453,7 @@ static void __exit ib_core_cleanup(void) /* Make sure that any pending umem accounting work is done. */ destroy_workqueue(ib_wq); WARN_ON(!xa_empty(&clients)); + WARN_ON(!xa_empty(&devices)); } MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_LS, 4); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index cc15820513cd..8558f31ca46f 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2542,12 +2542,7 @@ struct ib_device { struct list_head event_handler_list; spinlock_t event_handler_lock; - rwlock_t client_data_lock; - struct list_head core_list; - /* Access to the client_data_list is protected by the client_data_lock - * rwlock and the lists_rwsem read-write semaphore - */ - struct list_head client_data_list; + struct xarray client_data; struct ib_cache cache; /** @@ -2660,7 +2655,21 @@ void ib_unregister_device(struct ib_device *device); int ib_register_client (struct ib_client *client); void ib_unregister_client(struct ib_client *client); -void *ib_get_client_data(struct ib_device *device, struct ib_client *client); +/** + * ib_get_client_data - Get IB client context + * @device:Device to get context for + * @client:Client to get context for + * + * ib_get_client_data() returns the client context data set with + * ib_set_client_data(). This can only be called while the client is + * registered to the device, once the ib_client remove() callback returns this + * cannot be called. + */ +static inline void *ib_get_client_data(struct ib_device *device, + struct ib_client *client) +{ + return xa_load(&device->client_data, client->client_id); +} void ib_set_client_data(struct ib_device *device, struct ib_client *client, void *data); void ib_set_device_ops(struct ib_device *device, From 921eab1143aadf976a42cac4605b4d35159b355d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 6 Feb 2019 22:41:54 -0700 Subject: [PATCH 1453/4277] RDMA/devices: Re-organize device.c locking The locking here started out with a single lock that covered everything and then has lately veered into crazy town. The fundamental problem is that several places need to iterate over a linked list, but also need to drop their locks to avoid deadlock during client callbacks. xarray's restartable iteration offers a simple solution to the problem. Once all the lists are xarrays we can drop locks in the places that need that and rely on xarray to provide consistency and locking for the data structure. The resulting simplification is that each of the three lists has a dedicated rwsem that must be held when working with the list it covers. One data structure is no longer covered by multiple locks. The sleeping semaphore is selected because the read side generally needs to be held over something sleeping, and using RCU reader locking in those cases is overkill. In the process this simplifies the entire registration/unregistration flow to be the expected list of setups and the reversed list of matching teardowns, and the registration lock 'refcount' can now be revised to be released after the ULPs are removed, providing a very sane semantic for this feature. Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 361 +++++++++++++++++++------------ include/rdma/ib_verbs.h | 1 + 2 files changed, 222 insertions(+), 140 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 5096593b99e9..3325be4f91a5 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -56,6 +55,29 @@ struct workqueue_struct *ib_comp_unbound_wq; struct workqueue_struct *ib_wq; EXPORT_SYMBOL_GPL(ib_wq); +/* + * Each of the three rwsem locks (devices, clients, client_data) protects the + * xarray of the same name. Specifically it allows the caller to assert that + * the MARK will/will not be changing under the lock, and for devices and + * clients, that the value in the xarray is still a valid pointer. Change of + * the MARK is linked to the object state, so holding the lock and testing the + * MARK also asserts that the contained object is in a certain state. + * + * This is used to build a two stage register/unregister flow where objects + * can continue to be in the xarray even though they are still in progress to + * register/unregister. + * + * The xarray itself provides additional locking, and restartable iteration, + * which is also relied on. + * + * Locks should not be nested, with the exception of client_data, which is + * allowed to nest under the read side of the other two locks. + * + * The devices_rwsem also protects the device name list, any change or + * assignment of device name must also hold the write side to guarantee unique + * names. + */ + /* * devices contains devices that have had their names assigned. The * devices may not be registered. Users that care about the registration @@ -64,17 +86,13 @@ EXPORT_SYMBOL_GPL(ib_wq); * */ static DEFINE_XARRAY_FLAGS(devices, XA_FLAGS_ALLOC); - -/* - * Note that if the *rwsem is held and the *_REGISTERED mark is seen then the - * object is guaranteed to be and remain registered for the duration of the - * lock. - */ +static DECLARE_RWSEM(devices_rwsem); #define DEVICE_REGISTERED XA_MARK_1 static LIST_HEAD(client_list); #define CLIENT_REGISTERED XA_MARK_1 static DEFINE_XARRAY_FLAGS(clients, XA_FLAGS_ALLOC); +static DECLARE_RWSEM(clients_rwsem); /* * If client_data is registered then the corresponding client must also still @@ -115,20 +133,6 @@ static void *xan_find_marked(struct xarray *xa, unsigned long *indexp, !xa_is_err(entry); \ (index)++, entry = xan_find_marked(xa, &(index), filter)) -/* - * device_mutex and lists_rwsem protect access to both devices and - * clients. device_mutex protects writer access by device and client - * registration / de-registration. lists_rwsem protects reader access to - * these lists. Iterators of these lists must lock it for read, while updates - * to the lists must be done with a write lock. A special case is when the - * device_mutex is locked. In this case locking the lists for read access is - * not necessary as the device_mutex implies it. - * - * lists_rwsem also protects access to the client data list. - */ -static DEFINE_MUTEX(device_mutex); -static DECLARE_RWSEM(lists_rwsem); - static int ib_security_change(struct notifier_block *nb, unsigned long event, void *lsm_data); static void ib_policy_change_task(struct work_struct *work); @@ -185,13 +189,13 @@ struct ib_device *ib_device_get_by_index(u32 index) { struct ib_device *device; - down_read(&lists_rwsem); + down_read(&devices_rwsem); device = xa_load(&devices, index); if (device) { if (!ib_device_try_get(device)) device = NULL; } - up_read(&lists_rwsem); + up_read(&devices_rwsem); return device; } @@ -225,7 +229,7 @@ int ib_device_rename(struct ib_device *ibdev, const char *name) { int ret; - mutex_lock(&device_mutex); + down_write(&devices_rwsem); if (!strcmp(name, dev_name(&ibdev->dev))) { ret = 0; goto out; @@ -241,7 +245,7 @@ int ib_device_rename(struct ib_device *ibdev, const char *name) goto out; strlcpy(ibdev->name, name, IB_DEVICE_NAME_MAX); out: - mutex_unlock(&device_mutex); + up_write(&devices_rwsem); return ret; } @@ -253,6 +257,7 @@ static int alloc_name(struct ib_device *ibdev, const char *name) int rc; int i; + lockdep_assert_held_exclusive(&devices_rwsem); ida_init(&inuse); xa_for_each (&devices, index, device) { char buf[IB_DEVICE_NAME_MAX]; @@ -345,6 +350,7 @@ struct ib_device *_ib_alloc_device(size_t size) * destroyed if the user stores NULL in the client data. */ xa_init_flags(&device->client_data, XA_FLAGS_ALLOC); + init_rwsem(&device->client_data_rwsem); INIT_LIST_HEAD(&device->port_list); init_completion(&device->unreg_completion); @@ -367,22 +373,86 @@ void ib_dealloc_device(struct ib_device *device) } EXPORT_SYMBOL(ib_dealloc_device); -static int add_client_context(struct ib_device *device, struct ib_client *client) +/* + * add_client_context() and remove_client_context() must be safe against + * parallel calls on the same device - registration/unregistration of both the + * device and client can be occurring in parallel. + * + * The routines need to be a fence, any caller must not return until the add + * or remove is fully completed. + */ +static int add_client_context(struct ib_device *device, + struct ib_client *client) { - void *entry; + int ret = 0; if (!device->kverbs_provider && !client->no_kverbs_req) - return -EOPNOTSUPP; + return 0; - down_write(&lists_rwsem); - entry = xa_store(&device->client_data, client->client_id, NULL, - GFP_KERNEL); - if (!xa_is_err(entry)) - xa_set_mark(&device->client_data, client->client_id, - CLIENT_DATA_REGISTERED); - up_write(&lists_rwsem); + down_write(&device->client_data_rwsem); + /* + * Another caller to add_client_context got here first and has already + * completely initialized context. + */ + if (xa_get_mark(&device->client_data, client->client_id, + CLIENT_DATA_REGISTERED)) + goto out; - return xa_err(entry); + ret = xa_err(xa_store(&device->client_data, client->client_id, NULL, + GFP_KERNEL)); + if (ret) + goto out; + downgrade_write(&device->client_data_rwsem); + if (client->add) + client->add(device); + + /* Readers shall not see a client until add has been completed */ + xa_set_mark(&device->client_data, client->client_id, + CLIENT_DATA_REGISTERED); + up_read(&device->client_data_rwsem); + return 0; + +out: + up_write(&device->client_data_rwsem); + return ret; +} + +static void remove_client_context(struct ib_device *device, + unsigned int client_id) +{ + struct ib_client *client; + void *client_data; + + down_write(&device->client_data_rwsem); + if (!xa_get_mark(&device->client_data, client_id, + CLIENT_DATA_REGISTERED)) { + up_write(&device->client_data_rwsem); + return; + } + client_data = xa_load(&device->client_data, client_id); + xa_clear_mark(&device->client_data, client_id, CLIENT_DATA_REGISTERED); + client = xa_load(&clients, client_id); + downgrade_write(&device->client_data_rwsem); + + /* + * Notice we cannot be holding any exclusive locks when calling the + * remove callback as the remove callback can recurse back into any + * public functions in this module and thus try for any locks those + * functions take. + * + * For this reason clients and drivers should not call the + * unregistration functions will holdling any locks. + * + * It tempting to drop the client_data_rwsem too, but this is required + * to ensure that unregister_client does not return until all clients + * are completely unregistered, which is required to avoid module + * unloading races. + */ + if (client->remove) + client->remove(device, client_data); + + xa_erase(&device->client_data, client_id); + up_read(&device->client_data_rwsem); } static int verify_immutable(const struct ib_device *dev, u8 port) @@ -461,7 +531,7 @@ static void ib_policy_change_task(struct work_struct *work) struct ib_device *dev; unsigned long index; - down_read(&lists_rwsem); + down_read(&devices_rwsem); xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) { int i; @@ -478,7 +548,7 @@ static void ib_policy_change_task(struct work_struct *work) ib_security_cache_change(dev, i, sp); } } - up_read(&lists_rwsem); + up_read(&devices_rwsem); } static int ib_security_change(struct notifier_block *nb, unsigned long event, @@ -501,6 +571,7 @@ static int assign_name(struct ib_device *device, const char *name) static u32 last_id; int ret; + down_write(&devices_rwsem); /* Assign a unique name to the device */ if (strchr(name, '%')) ret = alloc_name(device, name); @@ -528,13 +599,17 @@ static int assign_name(struct ib_device *device, const char *name) last_id = device->index + 1; ret = 0; + out: + up_write(&devices_rwsem); return ret; } static void release_name(struct ib_device *device) { + down_write(&devices_rwsem); xa_erase(&devices, device->index); + up_write(&devices_rwsem); } static void setup_dma_device(struct ib_device *device) @@ -572,11 +647,18 @@ static void setup_dma_device(struct ib_device *device) } } +/* + * setup_device() allocates memory and sets up data that requires calling the + * device ops, this is the only reason these actions are not done during + * ib_alloc_device. It is undone by ib_dealloc_device(). + */ static int setup_device(struct ib_device *device) { struct ib_udata uhw = {.outlen = 0, .inlen = 0}; int ret; + setup_dma_device(device); + ret = ib_device_check_mandatory(device); if (ret) return ret; @@ -605,6 +687,54 @@ static int setup_device(struct ib_device *device) return 0; } +static void disable_device(struct ib_device *device) +{ + struct ib_client *client; + + WARN_ON(!refcount_read(&device->refcount)); + + down_write(&devices_rwsem); + xa_clear_mark(&devices, device->index, DEVICE_REGISTERED); + up_write(&devices_rwsem); + + down_read(&clients_rwsem); + list_for_each_entry_reverse(client, &client_list, list) + remove_client_context(device, client->client_id); + up_read(&clients_rwsem); + + /* Pairs with refcount_set in enable_device */ + ib_device_put(device); + wait_for_completion(&device->unreg_completion); +} + +/* + * An enabled device is visible to all clients and to all the public facing + * APIs that return a device pointer. + */ +static int enable_device(struct ib_device *device) +{ + struct ib_client *client; + unsigned long index; + int ret; + + refcount_set(&device->refcount, 1); + down_write(&devices_rwsem); + xa_set_mark(&devices, device->index, DEVICE_REGISTERED); + up_write(&devices_rwsem); + + down_read(&clients_rwsem); + xa_for_each_marked (&clients, index, client, CLIENT_REGISTERED) { + ret = add_client_context(device, client); + if (ret) { + up_read(&clients_rwsem); + disable_device(device); + return ret; + } + } + up_read(&clients_rwsem); + return 0; +} + /** * ib_register_device - Register an IB device with IB core * @device:Device to register @@ -617,26 +747,20 @@ static int setup_device(struct ib_device *device) int ib_register_device(struct ib_device *device, const char *name) { int ret; - struct ib_client *client; - unsigned long index; - - setup_dma_device(device); - - mutex_lock(&device_mutex); ret = assign_name(device, name); if (ret) - goto out; + return ret; ret = setup_device(device); if (ret) - goto out_name; + goto out; ret = ib_cache_setup_one(device); if (ret) { dev_warn(&device->dev, "Couldn't set up InfiniBand P_Key/GID cache\n"); - goto out_name; + goto out; } ib_device_register_rdmacg(device); @@ -648,25 +772,19 @@ int ib_register_device(struct ib_device *device, const char *name) goto cg_cleanup; } - refcount_set(&device->refcount, 1); + ret = enable_device(device); + if (ret) + goto sysfs_cleanup; - xa_for_each_marked (&clients, index, client, CLIENT_REGISTERED) - if (!add_client_context(device, client) && client->add) - client->add(device); - - down_write(&lists_rwsem); - xa_set_mark(&devices, device->index, DEVICE_REGISTERED); - up_write(&lists_rwsem); - mutex_unlock(&device_mutex); return 0; +sysfs_cleanup: + ib_device_unregister_sysfs(device); cg_cleanup: ib_device_unregister_rdmacg(device); ib_cache_cleanup_one(device); -out_name: - release_name(device); out: - mutex_unlock(&device_mutex); + release_name(device); return ret; } EXPORT_SYMBOL(ib_register_device); @@ -679,45 +797,11 @@ EXPORT_SYMBOL(ib_register_device); */ void ib_unregister_device(struct ib_device *device) { - struct ib_client *client; - unsigned long index; - - /* - * Wait for all netlink command callers to finish working on the - * device. - */ - ib_device_put(device); - wait_for_completion(&device->unreg_completion); - - mutex_lock(&device_mutex); - - down_write(&lists_rwsem); - xa_clear_mark(&devices, device->index, DEVICE_REGISTERED); - xa_for_each (&clients, index, client) - xa_clear_mark(&device->client_data, index, - CLIENT_DATA_REGISTERED); - downgrade_write(&lists_rwsem); - - list_for_each_entry_reverse(client, &client_list, list) - if (xa_get_mark(&device->client_data, client->client_id, - CLIENT_DATA_REGISTERED) && - client->remove) - client->remove(device, xa_load(&device->client_data, - client->client_id)); - up_read(&lists_rwsem); - + disable_device(device); ib_device_unregister_sysfs(device); ib_device_unregister_rdmacg(device); - - release_name(device); - - mutex_unlock(&device_mutex); - ib_cache_cleanup_one(device); - - down_write(&lists_rwsem); - xa_destroy(&device->client_data); - up_write(&lists_rwsem); + release_name(device); } EXPORT_SYMBOL(ib_unregister_device); @@ -725,6 +809,7 @@ static int assign_client_id(struct ib_client *client) { int ret; + down_write(&clients_rwsem); /* * The add/remove callbacks must be called in FIFO/LIFO order. To * achieve this we assign client_ids so they are sorted in @@ -743,7 +828,11 @@ static int assign_client_id(struct ib_client *client) if (ret) goto out; + xa_set_mark(&clients, client->client_id, CLIENT_REGISTERED); + list_add_tail(&client->list, &client_list); + out: + up_write(&clients_rwsem); return ret; } @@ -766,23 +855,20 @@ int ib_register_client(struct ib_client *client) unsigned long index; int ret; - mutex_lock(&device_mutex); ret = assign_client_id(client); - if (ret) { - mutex_unlock(&device_mutex); + if (ret) return ret; + + down_read(&devices_rwsem); + xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) { + ret = add_client_context(device, client); + if (ret) { + up_read(&devices_rwsem); + ib_unregister_client(client); + return ret; + } } - - xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) - if (!add_client_context(device, client) && client->add) - client->add(device); - - down_write(&lists_rwsem); - xa_set_mark(&clients, client->client_id, CLIENT_REGISTERED); - up_write(&lists_rwsem); - - mutex_unlock(&device_mutex); - + up_read(&devices_rwsem); return 0; } EXPORT_SYMBOL(ib_register_client); @@ -794,38 +880,31 @@ EXPORT_SYMBOL(ib_register_client); * Upper level users use ib_unregister_client() to remove their client * registration. When ib_unregister_client() is called, the client * will receive a remove callback for each IB device still registered. + * + * This is a full fence, once it returns no client callbacks will be called, + * or are running in another thread. */ void ib_unregister_client(struct ib_client *client) { struct ib_device *device; unsigned long index; - mutex_lock(&device_mutex); - - down_write(&lists_rwsem); + down_write(&clients_rwsem); xa_clear_mark(&clients, client->client_id, CLIENT_REGISTERED); - up_write(&lists_rwsem); + up_write(&clients_rwsem); + /* + * Every device still known must be serialized to make sure we are + * done with the client callbacks before we return. + */ + down_read(&devices_rwsem); + xa_for_each (&devices, index, device) + remove_client_context(device, client->client_id); + up_read(&devices_rwsem); - xa_for_each_marked (&devices, index, device, DEVICE_REGISTERED) { - down_write(&lists_rwsem); - xa_clear_mark(&device->client_data, client->client_id, - CLIENT_DATA_REGISTERED); - up_write(&lists_rwsem); - - if (client->remove) - client->remove(device, xa_load(&device->client_data, - client->client_id)); - - down_write(&lists_rwsem); - xa_erase(&device->client_data, client->client_id); - up_write(&lists_rwsem); - } - - down_write(&lists_rwsem); + down_write(&clients_rwsem); list_del(&client->list); xa_erase(&clients, client->client_id); - up_write(&lists_rwsem); - mutex_unlock(&device_mutex); + up_write(&clients_rwsem); } EXPORT_SYMBOL(ib_unregister_client); @@ -1010,10 +1089,10 @@ void ib_enum_all_roce_netdevs(roce_netdev_filter filter, struct ib_device *dev; unsigned long index; - down_read(&lists_rwsem); + down_read(&devices_rwsem); xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) ib_enum_roce_netdev(dev, filter, filter_cookie, cb, cookie); - up_read(&lists_rwsem); + up_read(&devices_rwsem); } /** @@ -1030,15 +1109,14 @@ int ib_enum_all_devs(nldev_callback nldev_cb, struct sk_buff *skb, unsigned int idx = 0; int ret = 0; - down_read(&lists_rwsem); + down_read(&devices_rwsem); xa_for_each_marked (&devices, index, dev, DEVICE_REGISTERED) { ret = nldev_cb(dev, skb, cb, idx); if (ret) break; idx++; } - - up_read(&lists_rwsem); + up_read(&devices_rwsem); return ret; } @@ -1196,6 +1274,7 @@ EXPORT_SYMBOL(ib_find_pkey); * @gid: A GID that the net_dev uses to communicate. * @addr: Contains the IP address that the request specified as its * destination. + * */ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, u8 port, @@ -1210,8 +1289,11 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, if (!rdma_protocol_ib(dev, port)) return NULL; - down_read(&lists_rwsem); - + /* + * Holding the read side guarantees that the client will not become + * unregistered while we are calling get_net_dev_by_params() + */ + down_read(&dev->client_data_rwsem); xan_for_each_marked (&dev->client_data, index, client_data, CLIENT_DATA_REGISTERED) { struct ib_client *client = xa_load(&clients, index); @@ -1224,8 +1306,7 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, if (net_dev) break; } - - up_read(&lists_rwsem); + up_read(&dev->client_data_rwsem); return net_dev; } diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 8558f31ca46f..135fab2c016c 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2542,6 +2542,7 @@ struct ib_device { struct list_head event_handler_list; spinlock_t event_handler_lock; + struct rw_semaphore client_data_rwsem; struct xarray client_data; struct ib_cache cache; From e381a1cb650d97e213b5943c81bbcadf8f480962 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Wed, 6 Feb 2019 22:54:42 +0530 Subject: [PATCH 1454/4277] cxgb4: add tcb flags and tcb rpl struct This patch adds the tcb flags and structures needed for querying tcb information. Signed-off-by: Raju Rangoju Signed-off-by: Jason Gunthorpe --- drivers/net/ethernet/chelsio/cxgb4/t4_msg.h | 8 ++++++++ drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index c62a0c830705..38dd41eb959e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -56,6 +56,7 @@ enum { CPL_TX_DATA_ISO = 0x1F, CPL_CLOSE_LISTSRV_RPL = 0x20, + CPL_GET_TCB_RPL = 0x22, CPL_L2T_WRITE_RPL = 0x23, CPL_PASS_OPEN_RPL = 0x24, CPL_ACT_OPEN_RPL = 0x25, @@ -688,6 +689,13 @@ struct cpl_get_tcb { #define NO_REPLY_V(x) ((x) << NO_REPLY_S) #define NO_REPLY_F NO_REPLY_V(1U) +struct cpl_get_tcb_rpl { + union opcode_tid ot; + __u8 cookie; + __u8 status; + __be16 len; +}; + struct cpl_set_tcb_field { WR_HDR; union opcode_tid ot; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h index 3297ce025e8b..1b9afb192f7f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h @@ -41,6 +41,14 @@ #define TCB_SMAC_SEL_V(x) ((x) << TCB_SMAC_SEL_S) #define TCB_T_FLAGS_W 1 +#define TCB_T_FLAGS_S 0 +#define TCB_T_FLAGS_M 0xffffffffffffffffULL +#define TCB_T_FLAGS_V(x) ((__u64)(x) << TCB_T_FLAGS_S) + +#define TCB_RQ_START_W 30 +#define TCB_RQ_START_S 0 +#define TCB_RQ_START_M 0x3ffffffULL +#define TCB_RQ_START_V(x) ((x) << TCB_RQ_START_S) #define TF_CCTRL_ECE_S 60 #define TF_CCTRL_CWR_S 61 @@ -66,4 +74,8 @@ #define TCB_RX_FRAG3_LEN_RAW_W 29 #define TCB_RX_FRAG3_START_IDX_OFFSET_RAW_W 30 #define TCB_PDU_HDR_LEN_W 31 + +#define TF_RX_PDU_OUT_S 49 +#define TF_RX_PDU_OUT_V(x) ((__u64)(x) << TF_RX_PDU_OUT_S) + #endif /* __T4_TCB_H */ From 11a27e2121a544cae2dde62df9218b3d5d888a02 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Wed, 6 Feb 2019 22:54:43 +0530 Subject: [PATCH 1455/4277] iw_cxgb4: complete the cached SRQ buffers If TP fetches an SRQ buffer but ends up not using it before the connection is aborted, then it passes the index of that SRQ buffer to the host in ABORT_REQ_RSS or ABORT_RPL CPL message. But, if the srqidx field is zero in the received ABORT_RPL or ABORT_REQ_RSS CPL, then we need to read the tcb.rq_start field to see if it really did have an RQE cached. This works around a case where HW does not include the srqidx in the ABORT_RPL/ABORT_REQ_RSS CPL. The final value of rq_start is the one present in TCB with the TF_RX_PDU_OUT bit cleared. So, we need to read the TCB, examine the TF_RX_PDU_OUT (bit 49 of t_flags) in order to determine if there's a rx PDU feedback event pending. Signed-off-by: Raju Rangoju Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/cm.c | 161 +++++++++++++++++++++++-- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 3 + drivers/infiniband/hw/cxgb4/t4.h | 1 + 3 files changed, 157 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 77efd4ae8e10..b188d89674f1 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -655,7 +655,33 @@ static int send_halfclose(struct c4iw_ep *ep) return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); } -static int send_abort(struct c4iw_ep *ep) +void read_tcb(struct c4iw_ep *ep) +{ + struct sk_buff *skb; + struct cpl_get_tcb *req; + int wrlen = roundup(sizeof(*req), 16); + + skb = get_skb(NULL, sizeof(*req), GFP_KERNEL); + if (WARN_ON(!skb)) + return; + + set_wr_txq(skb, CPL_PRIORITY_CONTROL, ep->ctrlq_idx); + req = (struct cpl_get_tcb *) skb_put(skb, wrlen); + memset(req, 0, wrlen); + INIT_TP_WR(req, ep->hwtid); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_GET_TCB, ep->hwtid)); + req->reply_ctrl = htons(REPLY_CHAN_V(0) | QUEUENO_V(ep->rss_qid)); + + /* + * keep a ref on the ep so the tcb is not unlocked before this + * cpl completes. The ref is released in read_tcb_rpl(). + */ + c4iw_get_ep(&ep->com); + if (WARN_ON(c4iw_ofld_send(&ep->com.dev->rdev, skb))) + c4iw_put_ep(&ep->com); +} + +static int send_abort_req(struct c4iw_ep *ep) { u32 wrlen = roundup(sizeof(struct cpl_abort_req), 16); struct sk_buff *req_skb = skb_dequeue(&ep->com.ep_skb_list); @@ -670,6 +696,17 @@ static int send_abort(struct c4iw_ep *ep) return c4iw_l2t_send(&ep->com.dev->rdev, req_skb, ep->l2t); } +static int send_abort(struct c4iw_ep *ep) +{ + if (!ep->com.qp || !ep->com.qp->srq) { + send_abort_req(ep); + return 0; + } + set_bit(ABORT_REQ_IN_PROGRESS, &ep->com.flags); + read_tcb(ep); + return 0; +} + static int send_connect(struct c4iw_ep *ep) { struct cpl_act_open_req *req = NULL; @@ -1851,14 +1888,11 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) return 0; } -static void complete_cached_srq_buffers(struct c4iw_ep *ep, - __be32 srqidx_status) +static void complete_cached_srq_buffers(struct c4iw_ep *ep, u32 srqidx) { enum chip_type adapter_type; - u32 srqidx; adapter_type = ep->com.dev->rdev.lldi.adapter_type; - srqidx = ABORT_RSS_SRQIDX_G(be32_to_cpu(srqidx_status)); /* * If this TCB had a srq buffer cached, then we must complete @@ -1876,6 +1910,7 @@ static void complete_cached_srq_buffers(struct c4iw_ep *ep, static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) { + u32 srqidx; struct c4iw_ep *ep; struct cpl_abort_rpl_rss6 *rpl = cplhdr(skb); int release = 0; @@ -1887,7 +1922,10 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) return 0; } - complete_cached_srq_buffers(ep, rpl->srqidx_status); + if (ep->com.qp && ep->com.qp->srq) { + srqidx = ABORT_RSS_SRQIDX_G(be32_to_cpu(rpl->srqidx_status)); + complete_cached_srq_buffers(ep, srqidx ? srqidx : ep->srqe_idx); + } pr_debug("ep %p tid %u\n", ep, ep->hwtid); mutex_lock(&ep->com.mutex); @@ -2746,6 +2784,21 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) return 0; } +static void finish_peer_abort(struct c4iw_dev *dev, struct c4iw_ep *ep) +{ + complete_cached_srq_buffers(ep, ep->srqe_idx); + if (ep->com.cm_id && ep->com.qp) { + struct c4iw_qp_attributes attrs; + + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + peer_abort_upcall(ep); + release_ep_resources(ep); + c4iw_put_ep(&ep->com); +} + static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) { struct cpl_abort_req_rss6 *req = cplhdr(skb); @@ -2756,6 +2809,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) int release = 0; unsigned int tid = GET_TID(req); u8 status; + u32 srqidx; u32 len = roundup(sizeof(struct cpl_abort_rpl), 16); @@ -2775,8 +2829,6 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) goto deref_ep; } - complete_cached_srq_buffers(ep, req->srqidx_status); - pr_debug("ep %p tid %u state %u\n", ep, ep->hwtid, ep->com.state); set_bit(PEER_ABORT, &ep->com.history); @@ -2825,6 +2877,23 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) stop_ep_timer(ep); /*FALLTHROUGH*/ case FPDU_MODE: + if (ep->com.qp && ep->com.qp->srq) { + srqidx = ABORT_RSS_SRQIDX_G( + be32_to_cpu(req->srqidx_status)); + if (srqidx) { + complete_cached_srq_buffers(ep, + req->srqidx_status); + } else { + /* Hold ep ref until finish_peer_abort() */ + c4iw_get_ep(&ep->com); + __state_set(&ep->com, ABORTING); + set_bit(PEER_ABORT_IN_PROGRESS, &ep->com.flags); + read_tcb(ep); + break; + + } + } + if (ep->com.cm_id && ep->com.qp) { attrs.next_state = C4IW_QP_STATE_ERROR; ret = c4iw_modify_qp(ep->com.qp->rhp, @@ -3726,6 +3795,80 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, return; } +static inline u64 t4_tcb_get_field64(__be64 *tcb, u16 word) +{ + u64 tlo = be64_to_cpu(tcb[((31 - word) / 2)]); + u64 thi = be64_to_cpu(tcb[((31 - word) / 2) - 1]); + u64 t; + u32 shift = 32; + + t = (thi << shift) | (tlo >> shift); + + return t; +} + +static inline u32 t4_tcb_get_field32(__be64 *tcb, u16 word, u32 mask, u32 shift) +{ + u32 v; + u64 t = be64_to_cpu(tcb[(31 - word) / 2]); + + if (word & 0x1) + shift += 32; + v = (t >> shift) & mask; + return v; +} + +static int read_tcb_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_get_tcb_rpl *rpl = cplhdr(skb); + __be64 *tcb = (__be64 *)(rpl + 1); + unsigned int tid = GET_TID(rpl); + struct c4iw_ep *ep; + u64 t_flags_64; + u32 rx_pdu_out; + + ep = get_ep_from_tid(dev, tid); + if (!ep) + return 0; + /* Examine the TF_RX_PDU_OUT (bit 49 of the t_flags) in order to + * determine if there's a rx PDU feedback event pending. + * + * If that bit is set, it means we'll need to re-read the TCB's + * rq_start value. The final value is the one present in a TCB + * with the TF_RX_PDU_OUT bit cleared. + */ + + t_flags_64 = t4_tcb_get_field64(tcb, TCB_T_FLAGS_W); + rx_pdu_out = (t_flags_64 & TF_RX_PDU_OUT_V(1)) >> TF_RX_PDU_OUT_S; + + c4iw_put_ep(&ep->com); /* from get_ep_from_tid() */ + c4iw_put_ep(&ep->com); /* from read_tcb() */ + + /* If TF_RX_PDU_OUT bit is set, re-read the TCB */ + if (rx_pdu_out) { + if (++ep->rx_pdu_out_cnt >= 2) { + WARN_ONCE(1, "tcb re-read() reached the guard limit, finishing the cleanup\n"); + goto cleanup; + } + read_tcb(ep); + return 0; + } + + ep->srqe_idx = t4_tcb_get_field32(tcb, TCB_RQ_START_W, TCB_RQ_START_W, + TCB_RQ_START_S); +cleanup: + pr_debug("ep %p tid %u %016x\n", ep, ep->hwtid, ep->srqe_idx); + + if (test_bit(PEER_ABORT_IN_PROGRESS, &ep->com.flags)) + finish_peer_abort(dev, ep); + else if (test_bit(ABORT_REQ_IN_PROGRESS, &ep->com.flags)) + send_abort_req(ep); + else + WARN_ONCE(1, "unexpected state!"); + + return 0; +} + static int deferred_fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb) { struct cpl_fw6_msg *rpl = cplhdr(skb); @@ -4046,6 +4189,7 @@ static c4iw_handler_func work_handlers[NUM_CPL_CMDS + NUM_FAKE_CPLS] = { [CPL_CLOSE_CON_RPL] = close_con_rpl, [CPL_RDMA_TERMINATE] = terminate, [CPL_FW4_ACK] = fw4_ack, + [CPL_GET_TCB_RPL] = read_tcb_rpl, [CPL_FW6_MSG] = deferred_fw6_msg, [CPL_RX_PKT] = rx_pkt, [FAKE_CPL_PUT_EP_SAFE] = _put_ep_safe, @@ -4277,6 +4421,7 @@ c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS] = { [CPL_RDMA_TERMINATE] = sched, [CPL_FW4_ACK] = sched, [CPL_SET_TCB_RPL] = set_tcb_rpl, + [CPL_GET_TCB_RPL] = sched, [CPL_FW6_MSG] = fw6_msg, [CPL_RX_PKT] = sched }; diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index f0fceadd0d12..3a0923f7c60e 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -982,6 +982,9 @@ struct c4iw_ep { int rcv_win; u32 snd_wscale; struct c4iw_ep_stats stats; + u32 srqe_idx; + u32 rx_pdu_out_cnt; + struct sk_buff *peer_abort_skb; }; static inline struct c4iw_ep *to_ep(struct iw_cm_id *cm_id) diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h index fff6d48d262f..b170817b2741 100644 --- a/drivers/infiniband/hw/cxgb4/t4.h +++ b/drivers/infiniband/hw/cxgb4/t4.h @@ -35,6 +35,7 @@ #include "t4_regs.h" #include "t4_values.h" #include "t4_msg.h" +#include "t4_tcb.h" #include "t4fw_ri_api.h" #define T4_MAX_NUM_PD 65536 From f368ff188ae4b3ef6f740a15999ea0373261b619 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Wed, 6 Feb 2019 22:54:44 +0530 Subject: [PATCH 1456/4277] iw_cxgb4: fix srqidx leak during connection abort When an application aborts the connection by moving QP from RTS to ERROR, then iw_cxgb4's modify_rc_qp() RTS->ERROR logic sets the *srqidxp to 0 via t4_set_wq_in_error(&qhp->wq, 0), and aborts the connection by calling c4iw_ep_disconnect(). c4iw_ep_disconnect() does the following: 1. sends up a close_complete_upcall(ep, -ECONNRESET) to libcxgb4. 2. sends abort request CPL to hw. But, since the close_complete_upcall() is sent before sending the ABORT_REQ to hw, libcxgb4 would fail to release the srqidx if the connection holds one. Because, the srqidx is passed up to libcxgb4 only after corresponding ABORT_RPL is processed by kernel in abort_rpl(). This patch handle the corner-case by moving the call to close_complete_upcall() from c4iw_ep_disconnect() to abort_rpl(). So that libcxgb4 is notified about the -ECONNRESET only after abort_rpl(), and libcxgb4 can relinquish the srqidx properly. Signed-off-by: Raju Rangoju Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/cm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index b188d89674f1..ae90b2932bd2 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1941,8 +1941,10 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) } mutex_unlock(&ep->com.mutex); - if (release) + if (release) { + close_complete_upcall(ep, -ECONNRESET); release_ep_resources(ep); + } c4iw_put_ep(&ep->com); return 0; } @@ -3684,7 +3686,6 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) if (close) { if (abrupt) { set_bit(EP_DISC_ABORT, &ep->com.history); - close_complete_upcall(ep, -ECONNRESET); ret = send_abort(ep); } else { set_bit(EP_DISC_CLOSE, &ep->com.history); From f818b82b80164014d7ee3df89bb110808778c796 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 8 Feb 2019 14:02:45 -0500 Subject: [PATCH 1457/4277] XArray: Mark xa_insert and xa_reserve as must_check If the user doesn't care about the return value from xa_insert(), then they should be using xa_store() instead. The point of xa_reserve() is to get the return value early before taking another lock, so this should also be __must_check. Signed-off-by: Matthew Wilcox --- include/linux/xarray.h | 23 ++++++++++++----------- lib/test_xarray.c | 10 +++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 5ed6b462e754..687c150071a5 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -497,12 +497,13 @@ void *__xa_erase(struct xarray *, unsigned long index); void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old, void *entry, gfp_t); -int __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t); +int __must_check __xa_insert(struct xarray *, unsigned long index, + void *entry, gfp_t); int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry, struct xa_limit, gfp_t); int __must_check __xa_alloc_cyclic(struct xarray *, u32 *id, void *entry, struct xa_limit, u32 *next, gfp_t); -int __xa_reserve(struct xarray *, unsigned long index, gfp_t); +int __must_check __xa_reserve(struct xarray *, unsigned long index, gfp_t); void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); @@ -704,8 +705,8 @@ static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index, * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ -static inline int xa_insert(struct xarray *xa, unsigned long index, - void *entry, gfp_t gfp) +static inline int __must_check xa_insert(struct xarray *xa, + unsigned long index, void *entry, gfp_t gfp) { int err; @@ -733,8 +734,8 @@ static inline int xa_insert(struct xarray *xa, unsigned long index, * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ -static inline int xa_insert_bh(struct xarray *xa, unsigned long index, - void *entry, gfp_t gfp) +static inline int __must_check xa_insert_bh(struct xarray *xa, + unsigned long index, void *entry, gfp_t gfp) { int err; @@ -762,8 +763,8 @@ static inline int xa_insert_bh(struct xarray *xa, unsigned long index, * Return: 0 if the store succeeded. -EBUSY if another entry was present. * -ENOMEM if memory could not be allocated. */ -static inline int xa_insert_irq(struct xarray *xa, unsigned long index, - void *entry, gfp_t gfp) +static inline int __must_check xa_insert_irq(struct xarray *xa, + unsigned long index, void *entry, gfp_t gfp) { int err; @@ -978,7 +979,7 @@ static inline int xa_alloc_cyclic_irq(struct xarray *xa, u32 *id, void *entry, * May sleep if the @gfp flags permit. * Return: 0 if the reservation succeeded or -ENOMEM if it failed. */ -static inline +static inline __must_check int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp) { int ret; @@ -1002,7 +1003,7 @@ int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp) * disabling softirqs. * Return: 0 if the reservation succeeded or -ENOMEM if it failed. */ -static inline +static inline __must_check int xa_reserve_bh(struct xarray *xa, unsigned long index, gfp_t gfp) { int ret; @@ -1026,7 +1027,7 @@ int xa_reserve_bh(struct xarray *xa, unsigned long index, gfp_t gfp) * disabling interrupts. * Return: 0 if the reservation succeeded or -ENOMEM if it failed. */ -static inline +static inline __must_check int xa_reserve_irq(struct xarray *xa, unsigned long index, gfp_t gfp) { int ret; diff --git a/lib/test_xarray.c b/lib/test_xarray.c index eaf53f742c72..3eaa40ddc390 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c @@ -364,21 +364,21 @@ static noinline void check_reserve(struct xarray *xa) /* An array with a reserved entry is not empty */ XA_BUG_ON(xa, !xa_empty(xa)); - xa_reserve(xa, 12345678, GFP_KERNEL); + XA_BUG_ON(xa, xa_reserve(xa, 12345678, GFP_KERNEL) != 0); XA_BUG_ON(xa, xa_empty(xa)); XA_BUG_ON(xa, xa_load(xa, 12345678)); xa_release(xa, 12345678); XA_BUG_ON(xa, !xa_empty(xa)); /* Releasing a used entry does nothing */ - xa_reserve(xa, 12345678, GFP_KERNEL); + XA_BUG_ON(xa, xa_reserve(xa, 12345678, GFP_KERNEL) != 0); XA_BUG_ON(xa, xa_store_index(xa, 12345678, GFP_NOWAIT) != NULL); xa_release(xa, 12345678); xa_erase_index(xa, 12345678); XA_BUG_ON(xa, !xa_empty(xa)); /* cmpxchg sees a reserved entry as NULL */ - xa_reserve(xa, 12345678, GFP_KERNEL); + XA_BUG_ON(xa, xa_reserve(xa, 12345678, GFP_KERNEL) != 0); XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, NULL, xa_mk_value(12345678), GFP_NOWAIT) != NULL); xa_release(xa, 12345678); @@ -386,7 +386,7 @@ static noinline void check_reserve(struct xarray *xa) XA_BUG_ON(xa, !xa_empty(xa)); /* But xa_insert does not */ - xa_reserve(xa, 12345678, GFP_KERNEL); + XA_BUG_ON(xa, xa_reserve(xa, 12345678, GFP_KERNEL) != 0); XA_BUG_ON(xa, xa_insert(xa, 12345678, xa_mk_value(12345678), 0) != -EBUSY); XA_BUG_ON(xa, xa_empty(xa)); @@ -395,7 +395,7 @@ static noinline void check_reserve(struct xarray *xa) /* Can iterate through a reserved entry */ xa_store_index(xa, 5, GFP_KERNEL); - xa_reserve(xa, 6, GFP_KERNEL); + XA_BUG_ON(xa, xa_reserve(xa, 6, GFP_KERNEL) != 0); xa_store_index(xa, 7, GFP_KERNEL); xa_for_each(xa, index, entry) { From 2a0b0a57fa86b951ca17cffdb4c118908dcfba65 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 1 Feb 2019 21:41:31 +0100 Subject: [PATCH 1458/4277] gpio: Add a Gateworks PLD GPIO driver This adds a driver for Gateworks PLD GPIO, that exist in two instances on the Gateworks Cambria GW2358-4 router platform at least. Cc: Imre Kaloz Cc: Tim Harvey Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-gw-pld.c | 137 +++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 drivers/gpio/gpio-gw-pld.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7a934b40bdae..486d9de2716a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -831,6 +831,13 @@ config GPIO_ADNP enough to represent all pins, but the driver will assume a register layout for 64 pins (8 registers). +config GPIO_GW_PLD + tristate "Gateworks PLD GPIO Expander" + depends on OF_GPIO + help + Say yes here to provide access to the Gateworks I2C PLD GPIO + Expander. This is used at least on the Cambria GW2358-4. + config GPIO_MAX7300 tristate "Maxim MAX7300 GPIO expander" select GPIO_MAX730X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 89cfad015687..9655927a3dcf 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o +obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c new file mode 100644 index 000000000000..242112ff60ee --- /dev/null +++ b/drivers/gpio/gpio-gw-pld.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Gateworks I2C PLD GPIO expander +// +// Copyright (C) 2019 Linus Walleij +// +// Based on code and know-how from the OpenWrt driver: +// Copyright (C) 2009 Gateworks Corporation +// Authors: Chris Lang, Imre Kaloz + +#include +#include +#include +#include +#include +#include + +/** + * struct gw_pld - State container for Gateworks PLD + * @chip: GPIO chip instance + * @client: I2C client + * @out: shadow register for the output bute + */ +struct gw_pld { + struct gpio_chip chip; + struct i2c_client *client; + u8 out; +}; + +/* + * The Gateworks I2C PLD chip only expose one read and one write register. + * Writing a "one" bit (to match the reset state) lets that pin be used as an + * input. It is an open-drain model. + */ +static int gw_pld_input8(struct gpio_chip *gc, unsigned offset) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + + gw->out |= BIT(offset); + return i2c_smbus_write_byte(gw->client, gw->out); +} + +static int gw_pld_get8(struct gpio_chip *gc, unsigned offset) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + s32 val; + + val = i2c_smbus_read_byte(gw->client); + + return (val < 0) ? 0 : !!(val & BIT(offset)); +} + +static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value) +{ + struct gw_pld *gw = gpiochip_get_data(gc); + + if (value) + gw->out |= BIT(offset); + else + gw->out &= ~BIT(offset); + + return i2c_smbus_write_byte(gw->client, gw->out); +} + +static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value) +{ + gw_pld_output8(gc, offset, value); +} + +static int gw_pld_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct gw_pld *gw; + int ret; + + gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL); + if (!gw) + return -ENOMEM; + + gw->chip.base = -1; + gw->chip.can_sleep = true; + gw->chip.parent = dev; + gw->chip.of_node = np; + gw->chip.owner = THIS_MODULE; + gw->chip.label = dev_name(dev); + gw->chip.ngpio = 8; + gw->chip.direction_input = gw_pld_input8; + gw->chip.get = gw_pld_get8; + gw->chip.direction_output = gw_pld_output8; + gw->chip.set = gw_pld_set8; + gw->client = client; + + /* + * The Gateworks I2C PLD chip does not properly send the acknowledge + * bit at all times, but we can still use the standard i2c_smbus + * functions by simply ignoring this bit. + */ + client->flags |= I2C_M_IGNORE_NAK; + gw->out = 0xFF; + + i2c_set_clientdata(client, gw); + + ret = devm_gpiochip_add_data(dev, &gw->chip, gw); + if (ret) + return ret; + + dev_info(dev, "registered Gateworks PLD GPIO device\n"); + + return 0; +} + +static const struct i2c_device_id gw_pld_id[] = { + { "gw-pld", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, gw_pld_id); + +static const struct of_device_id gw_pld_dt_ids[] = { + { .compatible = "gateworks,pld-gpio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gw_pld_dt_ids); + +static struct i2c_driver gw_pld_driver = { + .driver = { + .name = "gw_pld", + .of_match_table = gw_pld_dt_ids, + }, + .probe = gw_pld_probe, + .id_table = gw_pld_id, +}; +module_i2c_driver(gw_pld_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Linus Walleij "); From 96be65d12d792b3e177505db1225e1ec4f9cf5cd Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 1 Feb 2019 09:18:41 +0000 Subject: [PATCH 1459/4277] gpio: tqmx86: Drop unnecessary gpiochip_remove in tqmx86_gpio_probe() It's not necessary to remove gpio_chip which added with devm_gpiochip_add_data(). Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") Signed-off-by: Wei Yongjun Reviewed-by: Andrew Lunn Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tqmx86.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 6f7250c6f597..33dbe483f92f 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -291,7 +291,7 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) IRQ_TYPE_EDGE_BOTH); if (ret) { dev_err(dev, "Could not add irq chip\n"); - goto out_remove; + goto out_pm_dis; } gpiochip_set_chained_irqchip(chip, irq_chip, @@ -309,8 +309,6 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) return 0; -out_remove: - gpiochip_remove(&gpio->chip); out_pm_dis: pm_runtime_disable(&pdev->dev); From 9f22af1167815ce10909529412de33122be44758 Mon Sep 17 00:00:00 2001 From: Nikolaus Voss Date: Wed, 25 Jul 2018 10:43:57 +0200 Subject: [PATCH 1460/4277] gpio: adp5588: Add device tree support Make platform data optional and add DT id table. Switch to dynamically mapped GPIOs and IRQs if not provided via platform data. Signed-off-by: Nikolaus Voss Acked-by: Michael Hennerich Signed-off-by: Linus Walleij --- drivers/gpio/gpio-adp5588.c | 151 ++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 83 deletions(-) diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index cc33d8986ad3..bb4138d2c33f 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -33,8 +34,6 @@ struct adp5588_gpio { struct mutex lock; /* protect cached dir, dat_out */ /* protect serialized access to the interrupt controller bus */ struct mutex irq_lock; - unsigned gpio_start; - unsigned irq_base; uint8_t dat_out[3]; uint8_t dir[3]; uint8_t int_lvl[3]; @@ -148,16 +147,11 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, } #ifdef CONFIG_GPIO_ADP5588_IRQ -static int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off) -{ - struct adp5588_gpio *dev = gpiochip_get_data(chip); - - return dev->irq_base + off; -} static void adp5588_irq_bus_lock(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); mutex_lock(&dev->irq_lock); } @@ -172,7 +166,8 @@ static void adp5588_irq_bus_lock(struct irq_data *d) static void adp5588_irq_bus_sync_unlock(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); int i; for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { @@ -203,24 +198,25 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d) static void adp5588_irq_mask(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); - dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio); + dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq); } static void adp5588_irq_unmask(struct irq_data *d) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - unsigned gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); - dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio); + dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq); } static int adp5588_irq_set_type(struct irq_data *d, unsigned int type) { - struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d); - uint16_t gpio = d->irq - dev->irq_base; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5588_gpio *dev = gpiochip_get_data(gc); + uint16_t gpio = d->hwirq; unsigned bank, bit; if ((type & IRQ_TYPE_EDGE_BOTH)) { @@ -281,10 +277,11 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) while (pending) { if (pending & (1 << bit)) { - handle_nested_irq(dev->irq_base + - (bank << 3) + bit); + handle_nested_irq( + irq_find_mapping( + dev->gpio_chip.irq.domain, + (bank << 3) + bit)); pending &= ~(1 << bit); - } bit++; } @@ -299,53 +296,43 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) static int adp5588_irq_setup(struct adp5588_gpio *dev) { struct i2c_client *client = dev->client; + int ret; struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); - unsigned gpio; - int ret; + int irq_base = pdata ? pdata->irq_base : 0; adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ - dev->irq_base = pdata->irq_base; mutex_init(&dev->irq_lock); - for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { - int irq = gpio + dev->irq_base; - irq_set_chip_data(irq, dev); - irq_set_chip_and_handler(irq, &adp5588_irq_chip, - handle_level_irq); - irq_set_nested_thread(irq, 1); - irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE); - } - - ret = request_threaded_irq(client->irq, - NULL, - adp5588_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, adp5588_irq_handler, IRQF_ONESHOT + | IRQF_TRIGGER_FALLING | IRQF_SHARED, + dev_name(&client->dev), dev); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", client->irq); - goto out; + return ret; } + ret = gpiochip_irqchip_add_nested(&dev->gpio_chip, + &adp5588_irq_chip, irq_base, + handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&client->dev, + "could not connect irqchip to gpiochip\n"); + return ret; + } + gpiochip_set_nested_irqchip(&dev->gpio_chip, + &adp5588_irq_chip, + client->irq); - dev->gpio_chip.to_irq = adp5588_gpio_to_irq; adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); return 0; - -out: - dev->irq_base = 0; - return ret; -} - -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ - if (dev->irq_base) - free_irq(dev->client->irq, dev); } #else @@ -357,24 +344,16 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) return 0; } -static void adp5588_irq_teardown(struct adp5588_gpio *dev) -{ -} #endif /* CONFIG_GPIO_ADP5588_IRQ */ -static int adp5588_gpio_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adp5588_gpio_probe(struct i2c_client *client) { struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); struct adp5588_gpio *dev; struct gpio_chip *gc; int ret, i, revid; - - if (!pdata) { - dev_err(&client->dev, "missing platform data\n"); - return -ENODEV; - } + unsigned int pullup_dis_mask = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -394,18 +373,24 @@ static int adp5588_gpio_probe(struct i2c_client *client, gc->get = adp5588_gpio_get_value; gc->set = adp5588_gpio_set_value; gc->can_sleep = true; + gc->base = -1; + gc->parent = &client->dev; + + if (pdata) { + gc->base = pdata->gpio_start; + gc->names = pdata->names; + pullup_dis_mask = pdata->pullup_dis_mask; + } - gc->base = pdata->gpio_start; gc->ngpio = ADP5588_MAXGPIO; gc->label = client->name; gc->owner = THIS_MODULE; - gc->names = pdata->names; mutex_init(&dev->lock); ret = adp5588_gpio_read(dev->client, DEV_ID); if (ret < 0) - goto err; + return ret; revid = ret & ADP5588_DEVICE_ID_MASK; @@ -414,30 +399,27 @@ static int adp5588_gpio_probe(struct i2c_client *client, dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, - (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); + (pullup_dis_mask >> (8 * i)) & 0xFF); ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0); if (ret) - goto err; + return ret; } - if (pdata->irq_base) { + if (client->irq) { if (WA_DELAYED_READOUT_REVID(revid)) { dev_warn(&client->dev, "GPIO int not supported\n"); } else { ret = adp5588_irq_setup(dev); if (ret) - goto err; + return ret; } } ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev); if (ret) - goto err_irq; + return ret; - dev_info(&client->dev, "IRQ Base: %d Rev.: %d\n", - pdata->irq_base, revid); - - if (pdata->setup) { + if (pdata && pdata->setup) { ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); if (ret < 0) dev_warn(&client->dev, "setup failed, %d\n", ret); @@ -446,11 +428,6 @@ static int adp5588_gpio_probe(struct i2c_client *client, i2c_set_clientdata(client, dev); return 0; - -err_irq: - adp5588_irq_teardown(dev); -err: - return ret; } static int adp5588_gpio_remove(struct i2c_client *client) @@ -460,7 +437,7 @@ static int adp5588_gpio_remove(struct i2c_client *client) struct adp5588_gpio *dev = i2c_get_clientdata(client); int ret; - if (pdata->teardown) { + if (pdata && pdata->teardown) { ret = pdata->teardown(client, dev->gpio_chip.base, dev->gpio_chip.ngpio, pdata->context); @@ -470,7 +447,7 @@ static int adp5588_gpio_remove(struct i2c_client *client) } } - if (dev->irq_base) + if (dev->client->irq) free_irq(dev->client->irq, dev); return 0; @@ -480,14 +457,22 @@ static const struct i2c_device_id adp5588_gpio_id[] = { {DRV_NAME, 0}, {} }; - MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); +#ifdef CONFIG_OF +static const struct of_device_id adp5588_gpio_of_id[] = { + { .compatible = "adi," DRV_NAME, }, + {}, +}; +MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); +#endif + static struct i2c_driver adp5588_gpio_driver = { .driver = { - .name = DRV_NAME, - }, - .probe = adp5588_gpio_probe, + .name = DRV_NAME, + .of_match_table = of_match_ptr(adp5588_gpio_of_id), + }, + .probe_new = adp5588_gpio_probe, .remove = adp5588_gpio_remove, .id_table = adp5588_gpio_id, }; From 5d643edad73e839575f854c68782eecdfaf34c1e Mon Sep 17 00:00:00 2001 From: Nikolaus Voss Date: Tue, 5 Feb 2019 14:31:11 +0100 Subject: [PATCH 1461/4277] gpio: adp5588.c: Switch to events system Interupts were generated using GPIN interrupts of ADP5588. These interrupts have two important limitations: 1. Interrupts can only be generated for either rising or falling edges but not both. 2. Interrupts are reasserted as long as the interrupt condition persists (i.e. high or low level on that GPIN). This generates lots of interrupts unless the event is very short. To overcome this, ADP5588 provides an event system which queues up to 10 events in a buffer. GPIN events are queued whenever the GPIN is asserted or deasserted. This makes it possible to support generating GPIN interrupts for both edges and to generate only one interrupt per state change. Thus it is possible to chain the gpio-keys driver for some GPIOs. Signed-off-by: Nikolaus Voss Acked-by: Michael Hennerich Signed-off-by: Linus Walleij --- drivers/gpio/gpio-adp5588.c | 85 ++++++++++++++----------------------- 1 file changed, 32 insertions(+), 53 deletions(-) diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index bb4138d2c33f..c4a5b499f53e 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -36,12 +36,11 @@ struct adp5588_gpio { struct mutex irq_lock; uint8_t dat_out[3]; uint8_t dir[3]; - uint8_t int_lvl[3]; + uint8_t int_lvl_low[3]; + uint8_t int_lvl_high[3]; uint8_t int_en[3]; uint8_t irq_mask[3]; - uint8_t irq_stat[3]; uint8_t int_input_en[3]; - uint8_t int_lvl_cached[3]; }; static int adp5588_gpio_read(struct i2c_client *client, u8 reg) @@ -180,15 +179,9 @@ static void adp5588_irq_bus_sync_unlock(struct irq_data *d) mutex_unlock(&dev->lock); } - if (dev->int_lvl_cached[i] != dev->int_lvl[i]) { - dev->int_lvl_cached[i] = dev->int_lvl[i]; - adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + i, - dev->int_lvl[i]); - } - if (dev->int_en[i] ^ dev->irq_mask[i]) { dev->int_en[i] = dev->irq_mask[i]; - adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i, + adp5588_gpio_write(dev->client, GPI_EM1 + i, dev->int_en[i]); } } @@ -219,21 +212,17 @@ static int adp5588_irq_set_type(struct irq_data *d, unsigned int type) uint16_t gpio = d->hwirq; unsigned bank, bit; - if ((type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&dev->client->dev, "irq %d: unsupported type %d\n", - d->irq, type); - return -EINVAL; - } - bank = ADP5588_BANK(gpio); bit = ADP5588_BIT(gpio); - if (type & IRQ_TYPE_LEVEL_HIGH) - dev->int_lvl[bank] |= bit; - else if (type & IRQ_TYPE_LEVEL_LOW) - dev->int_lvl[bank] &= ~bit; - else - return -EINVAL; + dev->int_lvl_low[bank] &= ~bit; + dev->int_lvl_high[bank] &= ~bit; + + if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH) + dev->int_lvl_high[bank] |= bit; + + if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW) + dev->int_lvl_low[bank] |= bit; dev->int_input_en[bank] |= bit; @@ -249,41 +238,32 @@ static struct irq_chip adp5588_irq_chip = { .irq_set_type = adp5588_irq_set_type, }; -static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf) -{ - int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf); - - if (ret < 0) - dev_err(&client->dev, "Read INT_STAT Error\n"); - - return ret; -} - static irqreturn_t adp5588_irq_handler(int irq, void *devid) { struct adp5588_gpio *dev = devid; - unsigned status, bank, bit, pending; - int ret; - status = adp5588_gpio_read(dev->client, INT_STAT); + int status = adp5588_gpio_read(dev->client, INT_STAT); - if (status & ADP5588_GPI_INT) { - ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat); - if (ret < 0) - memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat)); + if (status & ADP5588_KE_INT) { + int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT); - for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO); - bank++, bit = 0) { - pending = dev->irq_stat[bank] & dev->irq_mask[bank]; + if (ev_cnt > 0) { + int i; - while (pending) { - if (pending & (1 << bit)) { - handle_nested_irq( - irq_find_mapping( - dev->gpio_chip.irq.domain, - (bank << 3) + bit)); - pending &= ~(1 << bit); - } - bit++; + for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) { + int key = adp5588_gpio_read(dev->client, + Key_EVENTA + i); + /* GPIN events begin at 97, + * bit 7 indicates logic level + */ + int gpio = (key & 0x7f) - 97; + int lvl = key & (1 << 7); + int bank = ADP5588_BANK(gpio); + int bit = ADP5588_BIT(gpio); + + if ((lvl && dev->int_lvl_high[bank] & bit) || + (!lvl && dev->int_lvl_low[bank] & bit)) + handle_nested_irq(irq_find_mapping( + dev->gpio_chip.irq.domain, gpio)); } } } @@ -303,7 +283,6 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ - adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */ mutex_init(&dev->irq_lock); @@ -330,7 +309,7 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) client->irq); adp5588_gpio_write(client, CFG, - ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT); + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); return 0; } From 25655c7532c73c6e2ea9fa40134129c5d54b71a7 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Fri, 8 Feb 2019 07:36:35 -0500 Subject: [PATCH 1462/4277] spmi: pmic-arb: revert "validate type when mapping IRQ" Validation of the IRQ type was added to spmi pmic-arb, however spmi-mpp in device tree still uses IRQ_TYPE_NONE. This commit caused the spmi-mpp probe to fail since platform_irq_count() would return 0. Correct this by backing out the previous patch. Signed-off-by: Brian Masney Fixes: 135ef21ab064 ("spmi: pmic-arb: validate type when mapping IRQ") Signed-off-by: Linus Walleij --- drivers/spmi/spmi-pmic-arb.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 316e2708f3ba..928759242e42 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -740,9 +740,9 @@ static int qpnpint_irq_domain_translate(struct irq_domain *d, } -static int qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, - struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq, unsigned int type) +static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, + struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq, unsigned int type) { irq_flow_handler_t handler; @@ -751,15 +751,11 @@ static int qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb, if (type & IRQ_TYPE_EDGE_BOTH) handler = handle_edge_irq; - else if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) - handler = handle_level_irq; else - return -EINVAL; + handler = handle_level_irq; irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb, handler, NULL, NULL); - - return 0; } static int qpnpint_irq_domain_alloc(struct irq_domain *domain, @@ -776,12 +772,9 @@ static int qpnpint_irq_domain_alloc(struct irq_domain *domain, if (ret) return ret; - for (i = 0; i < nr_irqs; i++) { - ret = qpnpint_irq_domain_map(pmic_arb, domain, virq + i, - hwirq + i, type); - if (ret) - return ret; - } + for (i = 0; i < nr_irqs; i++) + qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i, + type); return 0; } From 9951379b0ca88c95876ad9778b9099e19a95d566 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Sat, 9 Feb 2019 12:52:53 +0800 Subject: [PATCH 1463/4277] bcache: never writeback a discard operation Some users see panics like the following when performing fstrim on a bcached volume: [ 529.803060] BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 [ 530.183928] #PF error: [normal kernel read fault] [ 530.412392] PGD 8000001f42163067 P4D 8000001f42163067 PUD 1f42168067 PMD 0 [ 530.750887] Oops: 0000 [#1] SMP PTI [ 530.920869] CPU: 10 PID: 4167 Comm: fstrim Kdump: loaded Not tainted 5.0.0-rc1+ #3 [ 531.290204] Hardware name: HP ProLiant DL360 Gen9/ProLiant DL360 Gen9, BIOS P89 12/27/2015 [ 531.693137] RIP: 0010:blk_queue_split+0x148/0x620 [ 531.922205] Code: 60 38 89 55 a0 45 31 db 45 31 f6 45 31 c9 31 ff 89 4d 98 85 db 0f 84 7f 04 00 00 44 8b 6d 98 4c 89 ee 48 c1 e6 04 49 03 70 78 <8b> 46 08 44 8b 56 0c 48 8b 16 44 29 e0 39 d8 48 89 55 a8 0f 47 c3 [ 532.838634] RSP: 0018:ffffb9b708df39b0 EFLAGS: 00010246 [ 533.093571] RAX: 00000000ffffffff RBX: 0000000000046000 RCX: 0000000000000000 [ 533.441865] RDX: 0000000000000200 RSI: 0000000000000000 RDI: 0000000000000000 [ 533.789922] RBP: ffffb9b708df3a48 R08: ffff940d3b3fdd20 R09: 0000000000000000 [ 534.137512] R10: ffffb9b708df3958 R11: 0000000000000000 R12: 0000000000000000 [ 534.485329] R13: 0000000000000000 R14: 0000000000000000 R15: ffff940d39212020 [ 534.833319] FS: 00007efec26e3840(0000) GS:ffff940d1f480000(0000) knlGS:0000000000000000 [ 535.224098] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 535.504318] CR2: 0000000000000008 CR3: 0000001f4e256004 CR4: 00000000001606e0 [ 535.851759] Call Trace: [ 535.970308] ? mempool_alloc_slab+0x15/0x20 [ 536.174152] ? bch_data_insert+0x42/0xd0 [bcache] [ 536.403399] blk_mq_make_request+0x97/0x4f0 [ 536.607036] generic_make_request+0x1e2/0x410 [ 536.819164] submit_bio+0x73/0x150 [ 536.980168] ? submit_bio+0x73/0x150 [ 537.149731] ? bio_associate_blkg_from_css+0x3b/0x60 [ 537.391595] ? _cond_resched+0x1a/0x50 [ 537.573774] submit_bio_wait+0x59/0x90 [ 537.756105] blkdev_issue_discard+0x80/0xd0 [ 537.959590] ext4_trim_fs+0x4a9/0x9e0 [ 538.137636] ? ext4_trim_fs+0x4a9/0x9e0 [ 538.324087] ext4_ioctl+0xea4/0x1530 [ 538.497712] ? _copy_to_user+0x2a/0x40 [ 538.679632] do_vfs_ioctl+0xa6/0x600 [ 538.853127] ? __do_sys_newfstat+0x44/0x70 [ 539.051951] ksys_ioctl+0x6d/0x80 [ 539.212785] __x64_sys_ioctl+0x1a/0x20 [ 539.394918] do_syscall_64+0x5a/0x110 [ 539.568674] entry_SYSCALL_64_after_hwframe+0x44/0xa9 We have observed it where both: 1) LVM/devmapper is involved (bcache backing device is LVM volume) and 2) writeback cache is involved (bcache cache_mode is writeback) On one machine, we can reliably reproduce it with: # echo writeback > /sys/block/bcache0/bcache/cache_mode (not sure whether above line is required) # mount /dev/bcache0 /test # for i in {0..10}; do file="$(mktemp /test/zero.XXX)" dd if=/dev/zero of="$file" bs=1M count=256 sync rm $file done # fstrim -v /test Observing this with tracepoints on, we see the following writes: fstrim-18019 [022] .... 91107.302026: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 4260112 + 196352 hit 0 bypass 1 fstrim-18019 [022] .... 91107.302050: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 4456464 + 262144 hit 0 bypass 1 fstrim-18019 [022] .... 91107.302075: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 4718608 + 81920 hit 0 bypass 1 fstrim-18019 [022] .... 91107.302094: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 5324816 + 180224 hit 0 bypass 1 fstrim-18019 [022] .... 91107.302121: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 5505040 + 262144 hit 0 bypass 1 fstrim-18019 [022] .... 91107.302145: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 5767184 + 81920 hit 0 bypass 1 fstrim-18019 [022] .... 91107.308777: bcache_write: 73f95583-561c-408f-a93a-4cbd2498f5c8 inode 0 DS 6373392 + 180224 hit 1 bypass 0 Note the final one has different hit/bypass flags. This is because in should_writeback(), we were hitting a case where the partial stripe condition was returning true and so should_writeback() was returning true early. If that hadn't been the case, it would have hit the would_skip test, and as would_skip == s->iop.bypass == true, should_writeback() would have returned false. Looking at the git history from 'commit 72c270612bd3 ("bcache: Write out full stripes")', it looks like the idea was to optimise for raid5/6: * If a stripe is already dirty, force writes to that stripe to writeback mode - to help build up full stripes of dirty data To fix this issue, make sure that should_writeback() on a discard op never returns true. More details of debugging: https://www.spinics.net/lists/linux-bcache/msg06996.html Previous reports: - https://bugzilla.kernel.org/show_bug.cgi?id=201051 - https://bugzilla.kernel.org/show_bug.cgi?id=196103 - https://www.spinics.net/lists/linux-bcache/msg06885.html (Coly Li: minor modification to follow maximum 75 chars per line rule) Cc: Kent Overstreet Cc: stable@vger.kernel.org Fixes: 72c270612bd3 ("bcache: Write out full stripes") Signed-off-by: Daniel Axtens Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/writeback.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h index 6a743d3bb338..4e4c6810dc3c 100644 --- a/drivers/md/bcache/writeback.h +++ b/drivers/md/bcache/writeback.h @@ -71,6 +71,9 @@ static inline bool should_writeback(struct cached_dev *dc, struct bio *bio, in_use > bch_cutoff_writeback_sync) return false; + if (bio_op(bio) == REQ_OP_DISCARD) + return false; + if (dc->partial_stripes_expensive && bcache_dev_stripe_dirty(dc, bio->bi_iter.bi_sector, bio_sectors(bio))) From 83ff9318c44babe32da0947d81443bad82da2d44 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:52:54 +0800 Subject: [PATCH 1464/4277] bcache: not use hard coded memset size in bch_cache_accounting_clear() In stats.c:bch_cache_accounting_clear(), a hard coded number '7' is used in memset(). It is because in struct cache_stats, there are 7 atomic_t type members. This is not good when new members added into struct stats, the hard coded number will only clear part of memory. This patch replaces 'sizeof(unsigned long) * 7' by more generic 'sizeof(struct cache_stats))', to avoid potential error if new member added into struct cache_stats. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/bcache/stats.c b/drivers/md/bcache/stats.c index 894410f3f829..ba1c93791d8d 100644 --- a/drivers/md/bcache/stats.c +++ b/drivers/md/bcache/stats.c @@ -111,7 +111,7 @@ void bch_cache_accounting_clear(struct cache_accounting *acc) { memset(&acc->total.cache_hits, 0, - sizeof(unsigned long) * 7); + sizeof(struct cache_stats)); } void bch_cache_accounting_destroy(struct cache_accounting *acc) From 926d19465b66cb6bde4ca28fde16de775af4e357 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:52:55 +0800 Subject: [PATCH 1465/4277] bcache: export backing_dev_name via sysfs This patch export dc->backing_dev_name to sysfs file /sys/block/bcache/bcache/backing_dev_name, then people or user space tools may know the backing device name of this bcache device. Of cause it can be done by parsing sysfs links, but this method can be much simpler to find the link between bcache device and backing device. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 557a8a3270a1..b9166ee027fa 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -67,6 +67,7 @@ read_attribute(written); read_attribute(btree_written); read_attribute(metadata_written); read_attribute(active_journal_entries); +read_attribute(backing_dev_name); sysfs_time_stats_attribute(btree_gc, sec, ms); sysfs_time_stats_attribute(btree_split, sec, us); @@ -243,6 +244,12 @@ SHOW(__bch_cached_dev) return strlen(buf); } + if (attr == &sysfs_backing_dev_name) { + snprintf(buf, BDEVNAME_SIZE + 1, "%s", dc->backing_dev_name); + strcat(buf, "\n"); + return strlen(buf); + } + #undef var return 0; } @@ -452,6 +459,7 @@ static struct attribute *bch_cached_dev_files[] = { &sysfs_verify, &sysfs_bypass_torture_test, #endif + &sysfs_backing_dev_name, NULL }; KTYPE(bch_cached_dev); From d4610456cfa412811b749f6215b9adae976ab4c3 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:52:56 +0800 Subject: [PATCH 1466/4277] bcache: export backing_dev_uuid via sysfs When there are multiple bcache devices, after a reboot the name of bcache devices may change (e.g. current /dev/bcache1 was /dev/bcache0 before reboot). Therefore we need the backing device UUID (sb.uuid) to identify each bcache device. Backing device uuid can be found by program bcache-super-show, but directly exporting backing_dev_uuid by sysfs file /sys/block/bcache/bcache/backing_dev_uuid is a much simpler method. With backing_dev_uuid, and partition uuids from /dev/disk/by-partuuid/, now we can identify each bcache device and its partitions conveniently. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index b9166ee027fa..9be27b26d300 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -68,6 +68,7 @@ read_attribute(btree_written); read_attribute(metadata_written); read_attribute(active_journal_entries); read_attribute(backing_dev_name); +read_attribute(backing_dev_uuid); sysfs_time_stats_attribute(btree_gc, sec, ms); sysfs_time_stats_attribute(btree_split, sec, us); @@ -250,6 +251,13 @@ SHOW(__bch_cached_dev) return strlen(buf); } + if (attr == &sysfs_backing_dev_uuid) { + /* convert binary uuid into 36-byte string plus '\0' */ + snprintf(buf, 36+1, "%pU", dc->sb.uuid); + strcat(buf, "\n"); + return strlen(buf); + } + #undef var return 0; } @@ -460,6 +468,7 @@ static struct attribute *bch_cached_dev_files[] = { &sysfs_bypass_torture_test, #endif &sysfs_backing_dev_name, + &sysfs_backing_dev_uuid, NULL }; KTYPE(bch_cached_dev); From e8cf978dffb2c603340d4615eec2e5358c9df06d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 9 Feb 2019 12:52:57 +0800 Subject: [PATCH 1467/4277] bcache: fix indentation issue, remove tabs on a hunk of code There is a hunk of code that is indented one level too deep, fix this by removing the extra tabs. Signed-off-by: Colin Ian King Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/super.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 4dee119c3664..a697a3a923cd 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1615,21 +1615,21 @@ static void conditional_stop_bcache_device(struct cache_set *c, */ pr_warn("stop_when_cache_set_failed of %s is \"auto\" and cache is dirty, stop it to avoid potential data corruption.", d->disk->disk_name); - /* - * There might be a small time gap that cache set is - * released but bcache device is not. Inside this time - * gap, regular I/O requests will directly go into - * backing device as no cache set attached to. This - * behavior may also introduce potential inconsistence - * data in writeback mode while cache is dirty. - * Therefore before calling bcache_device_stop() due - * to a broken cache device, dc->io_disable should be - * explicitly set to true. - */ - dc->io_disable = true; - /* make others know io_disable is true earlier */ - smp_mb(); - bcache_device_stop(d); + /* + * There might be a small time gap that cache set is + * released but bcache device is not. Inside this time + * gap, regular I/O requests will directly go into + * backing device as no cache set attached to. This + * behavior may also introduce potential inconsistence + * data in writeback mode while cache is dirty. + * Therefore before calling bcache_device_stop() due + * to a broken cache device, dc->io_disable should be + * explicitly set to true. + */ + dc->io_disable = true; + /* make others know io_disable is true earlier */ + smp_mb(); + bcache_device_stop(d); } else { /* * dc->stop_when_cache_set_failed == BCH_CACHED_STOP_AUTO From 58ac323084ebf44f8470eeb8b82660f9d0ee3689 Mon Sep 17 00:00:00 2001 From: Tang Junhui Date: Sat, 9 Feb 2019 12:52:58 +0800 Subject: [PATCH 1468/4277] bcache: treat stale && dirty keys as bad keys Stale && dirty keys can be produced in the follow way: After writeback in write_dirty_finish(), dirty keys k1 will replace by clean keys k2 ==>ret = bch_btree_insert(dc->disk.c, &keys, NULL, &w->key); ==>btree_insert_fn(struct btree_op *b_op, struct btree *b) ==>static int bch_btree_insert_node(struct btree *b, struct btree_op *op, struct keylist *insert_keys, atomic_t *journal_ref, Then two steps: A) update k1 to k2 in btree node memory; bch_btree_insert_keys(b, op, insert_keys, replace_key) B) Write the bset(contains k2) to cache disk by a 30s delay work bch_btree_leaf_dirty(b, journal_ref). But before the 30s delay work write the bset to cache device, these things happened: A) GC works, and reclaim the bucket k2 point to; B) Allocator works, and invalidate the bucket k2 point to, and increase the gen of the bucket, and place it into free_inc fifo; C) Until now, the 30s delay work still does not finish work, so in the disk, the key still is k1, it is dirty and stale (its gen is smaller than the gen of the bucket). and then the machine power off suddenly happens; D) When the machine power on again, after the btree reconstruction, the stale dirty key appear. In bch_extent_bad(), when expensive_debug_checks is off, it would treat the dirty key as good even it is stale keys, and it would cause bellow probelms: A) In read_dirty() it would cause machine crash: BUG_ON(ptr_stale(dc->disk.c, &w->key, 0)); B) It could be worse when reads hits stale dirty keys, it would read old incorrect data. This patch tolerate the existence of these stale && dirty keys, and treat them as bad key in bch_extent_bad(). (Coly Li: fix indent which was modified by sender's email client) Signed-off-by: Tang Junhui Cc: stable@vger.kernel.org Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/extents.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c index 956004366699..886710043025 100644 --- a/drivers/md/bcache/extents.c +++ b/drivers/md/bcache/extents.c @@ -538,6 +538,7 @@ static bool bch_extent_bad(struct btree_keys *bk, const struct bkey *k) { struct btree *b = container_of(bk, struct btree, keys); unsigned int i, stale; + char buf[80]; if (!KEY_PTRS(k) || bch_extent_invalid(bk, k)) @@ -547,19 +548,19 @@ static bool bch_extent_bad(struct btree_keys *bk, const struct bkey *k) if (!ptr_available(b->c, k, i)) return true; - if (!expensive_debug_checks(b->c) && KEY_DIRTY(k)) - return false; - for (i = 0; i < KEY_PTRS(k); i++) { stale = ptr_stale(b->c, k, i); + if (stale && KEY_DIRTY(k)) { + bch_extent_to_text(buf, sizeof(buf), k); + pr_info("stale dirty pointer, stale %u, key: %s", + stale, buf); + } + btree_bug_on(stale > BUCKET_GC_GEN_MAX, b, "key too stale: %i, need_gc %u", stale, b->c->need_gc); - btree_bug_on(stale && KEY_DIRTY(k) && KEY_SIZE(k), - b, "stale dirty pointer"); - if (stale) return true; From 596b5a5dd1bc2fa019fdaaae522ef331deef927f Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:52:59 +0800 Subject: [PATCH 1469/4277] bcache: improve sysfs_strtoul_clamp() Currently sysfs_strtoul_clamp() is defined as, 82 #define sysfs_strtoul_clamp(file, var, min, max) \ 83 do { \ 84 if (attr == &sysfs_ ## file) \ 85 return strtoul_safe_clamp(buf, var, min, max) \ 86 ?: (ssize_t) size; \ 87 } while (0) The problem is, if bit width of var is less then unsigned long, min and max may not protect var from integer overflow, because overflow happens in strtoul_safe_clamp() before checking min and max. To fix such overflow in sysfs_strtoul_clamp(), to make min and max take effect, this patch adds an unsigned long variable, and uses it to macro strtoul_safe_clamp() to convert an unsigned long value in range defined by [min, max]. Then assign this value to var. By this method, if bit width of var is less than unsigned long, integer overflow won't happen before min and max are checking. Now sysfs_strtoul_clamp() can properly handle smaller data type like unsigned int, of cause min and max should be defined in range of unsigned int too. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/md/bcache/sysfs.h b/drivers/md/bcache/sysfs.h index 3fe82425859c..0ad2715a884e 100644 --- a/drivers/md/bcache/sysfs.h +++ b/drivers/md/bcache/sysfs.h @@ -81,9 +81,16 @@ do { \ #define sysfs_strtoul_clamp(file, var, min, max) \ do { \ - if (attr == &sysfs_ ## file) \ - return strtoul_safe_clamp(buf, var, min, max) \ - ?: (ssize_t) size; \ + if (attr == &sysfs_ ## file) { \ + unsigned long v = 0; \ + ssize_t ret; \ + ret = strtoul_safe_clamp(buf, v, min, max); \ + if (!ret) { \ + var = v; \ + return size; \ + } \ + return ret; \ + } \ } while (0) #define strtoul_or_return(cp) \ From f54478c6e226bb1540a3e58366601039dfd778e2 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:00 +0800 Subject: [PATCH 1470/4277] bcache: fix input integer overflow of congested threshold Cache set congested threshold values congested_read_threshold_us and congested_write_threshold_us can be set via sysfs interface. These two values are 'unsigned int' type, but sysfs interface uses strtoul to convert input string. So if people input a large number like 9999999999, the value indeed set is 1410065407, which is not expected behavior. This patch replaces sysfs_strtoul() by sysfs_strtoul_clamp() when convert input string to unsigned int value, and set value range in [0, UINT_MAX], to avoid the above integer overflow errors. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 9be27b26d300..bedd3e68fd29 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -778,10 +778,12 @@ STORE(__bch_cache_set) c->shrink.scan_objects(&c->shrink, &sc); } - sysfs_strtoul(congested_read_threshold_us, - c->congested_read_threshold_us); - sysfs_strtoul(congested_write_threshold_us, - c->congested_write_threshold_us); + sysfs_strtoul_clamp(congested_read_threshold_us, + c->congested_read_threshold_us, + 0, UINT_MAX); + sysfs_strtoul_clamp(congested_write_threshold_us, + c->congested_write_threshold_us, + 0, UINT_MAX); if (attr == &sysfs_errors) { v = __sysfs_match_string(error_actions, -1, buf); From 8c27a3953e92eb0b22dbb03d599f543a05f9574e Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:01 +0800 Subject: [PATCH 1471/4277] bcache: fix input overflow to sequential_cutoff People may set sequential_cutoff of a cached device via sysfs file, but current code does not check input value overflow. E.g. if value 4294967295 (UINT_MAX) is written to file sequential_cutoff, its value is 4GB, but if 4294967296 (UINT_MAX + 1) is written into, its value will be 0. This is an unexpected behavior. This patch replaces d_strtoi_h() by sysfs_strtoul_clamp() to convert input string to unsigned integer value, and limit its range in [0, UINT_MAX]. Then the input overflow can be fixed. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index bedd3e68fd29..96b64893f2cb 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -314,7 +314,9 @@ STORE(__cached_dev) dc->io_disable = v ? 1 : 0; } - d_strtoi_h(sequential_cutoff); + sysfs_strtoul_clamp(sequential_cutoff, + dc->sequential_cutoff, + 0, UINT_MAX); d_strtoi_h(readahead); if (attr == &sysfs_clear_stats) From e4db37fb69d56d9523bb540bd8e07bf221aa9f6d Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:02 +0800 Subject: [PATCH 1472/4277] bcache: add sysfs_strtoul_bool() for setting bit-field variables When setting bool values via sysfs interface, e.g. writeback_metadata, if writing 1 into writeback_metadata file, dc->writeback_metadata is set to 1, but if writing 2 into the file, dc->writeback_metadata is 0. This is misleading, a better result should be 1 for all non-zero input value. It is because dc->writeback_metadata is a bit-field variable, and current code simply use d_strtoul() to convert a string into integer and takes the lowest bit value. To fix such error, we need a routine to convert the input string into unsigned integer, and set target variable to 1 if the converted integer is non-zero. This patch introduces a new macro called sysfs_strtoul_bool(), it can be used to convert input string into bool value, we can use it to set bool value for bit-field vairables. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/md/bcache/sysfs.h b/drivers/md/bcache/sysfs.h index 0ad2715a884e..215df32f567b 100644 --- a/drivers/md/bcache/sysfs.h +++ b/drivers/md/bcache/sysfs.h @@ -79,6 +79,16 @@ do { \ return strtoul_safe(buf, var) ?: (ssize_t) size; \ } while (0) +#define sysfs_strtoul_bool(file, var) \ +do { \ + if (attr == &sysfs_ ## file) { \ + unsigned long v = strtoul_or_return(buf); \ + \ + var = v ? 1 : 0; \ + return size; \ + } \ +} while (0) + #define sysfs_strtoul_clamp(file, var, min, max) \ do { \ if (attr == &sysfs_ ## file) { \ From f5c0b95d2eeb17cf8a81fde0461938d2a79303ab Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:03 +0800 Subject: [PATCH 1473/4277] bcache: use sysfs_strtoul_bool() to set bit-field variables When setting bcache parameters via sysfs, there are some variables are defined as bit-field value. Current bcache code in sysfs.c uses either d_strtoul() or sysfs_strtoul() to convert the input string to unsigned integer value and set it to the corresponded bit-field value. The problem is, the bit-field value only takes the lowest bit of the converted value. If input is 2, the expected value (like bool value) of the bit-field value should be 1, but indeed it is 0. The following sysfs files for bit-field variables have such problem, bypass_torture_test, for dc->bypass_torture_test writeback_metadata, for dc->writeback_metadata writeback_running, for dc->writeback_running verify, for c->verify key_merging_disabled, for c->key_merging_disabled gc_always_rewrite, for c->gc_always_rewrite btree_shrinker_disabled,for c->shrinker_disabled copy_gc_enabled, for c->copy_gc_enabled This patch uses sysfs_strtoul_bool() to set such bit-field variables, then if the converted value is non-zero, the bit-field variables will be set to 1, like setting a bool value like expensive_debug_checks. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 96b64893f2cb..57395e23747a 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -277,9 +277,9 @@ STORE(__cached_dev) sysfs_strtoul(data_csum, dc->disk.data_csum); d_strtoul(verify); - d_strtoul(bypass_torture_test); - d_strtoul(writeback_metadata); - d_strtoul(writeback_running); + sysfs_strtoul_bool(bypass_torture_test, dc->bypass_torture_test); + sysfs_strtoul_bool(writeback_metadata, dc->writeback_metadata); + sysfs_strtoul_bool(writeback_running, dc->writeback_running); d_strtoul(writeback_delay); sysfs_strtoul_clamp(writeback_percent, dc->writeback_percent, @@ -816,12 +816,12 @@ STORE(__bch_cache_set) } sysfs_strtoul(journal_delay_ms, c->journal_delay_ms); - sysfs_strtoul(verify, c->verify); - sysfs_strtoul(key_merging_disabled, c->key_merging_disabled); + sysfs_strtoul_bool(verify, c->verify); + sysfs_strtoul_bool(key_merging_disabled, c->key_merging_disabled); sysfs_strtoul(expensive_debug_checks, c->expensive_debug_checks); - sysfs_strtoul(gc_always_rewrite, c->gc_always_rewrite); - sysfs_strtoul(btree_shrinker_disabled, c->shrinker_disabled); - sysfs_strtoul(copy_gc_enabled, c->copy_gc_enabled); + sysfs_strtoul_bool(gc_always_rewrite, c->gc_always_rewrite); + sysfs_strtoul_bool(btree_shrinker_disabled, c->shrinker_disabled); + sysfs_strtoul_bool(copy_gc_enabled, c->copy_gc_enabled); /* * write gc_after_writeback here may overwrite an already set * BCH_DO_AUTO_GC, it doesn't matter because this flag will be From 369d21a73a241682de019ac5c5209ce3ec627743 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:04 +0800 Subject: [PATCH 1474/4277] bcache: fix input overflow to writeback_delay Sysfs file writeback_delay is used to configure dc->writeback_delay which is type unsigned int. But bcache code uses sysfs_strtoul() to convert the input string, therefore it might be overflowed if the input value is too large. E.g. input value is 4294967296 but indeed 0 is set to dc->writeback_delay. This patch uses sysfs_strtoul_clamp() to convert the input string and set the result value range in [0, UINT_MAX] to avoid such unsigned integer overflow. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 57395e23747a..e4519326594f 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -280,7 +280,7 @@ STORE(__cached_dev) sysfs_strtoul_bool(bypass_torture_test, dc->bypass_torture_test); sysfs_strtoul_bool(writeback_metadata, dc->writeback_metadata); sysfs_strtoul_bool(writeback_running, dc->writeback_running); - d_strtoul(writeback_delay); + sysfs_strtoul_clamp(writeback_delay, dc->writeback_delay, 0, UINT_MAX); sysfs_strtoul_clamp(writeback_percent, dc->writeback_percent, 0, bch_cutoff_writeback); From c3b75a2199cdbfc1c335155fe143d842604b1baa Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:05 +0800 Subject: [PATCH 1475/4277] bcache: fix potential div-zero error of writeback_rate_i_term_inverse dc->writeback_rate_i_term_inverse can be set via sysfs interface. It is in type unsigned int, and convert from input string by d_strtoul(). The problem is d_strtoul() does not check valid range of the input, if 4294967296 is written into sysfs file writeback_rate_i_term_inverse, an overflow of unsigned integer will happen and value 0 is set to dc->writeback_rate_i_term_inverse. In writeback.c:__update_writeback_rate(), there are following lines of code, integral_scaled = div_s64(dc->writeback_rate_integral, dc->writeback_rate_i_term_inverse); If dc->writeback_rate_i_term_inverse is set to 0 via sysfs interface, a div-zero error might be triggered in the above code. Therefore we need to add a range limitation in the sysfs interface, this is what this patch does, use sysfs_stroul_clamp() to replace d_strtoul() and restrict the input range in [1, UINT_MAX]. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index e4519326594f..0fad46d3a8bd 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -302,7 +302,9 @@ STORE(__cached_dev) sysfs_strtoul_clamp(writeback_rate_update_seconds, dc->writeback_rate_update_seconds, 1, WRITEBACK_RATE_UPDATE_SECS_MAX); - d_strtoul(writeback_rate_i_term_inverse); + sysfs_strtoul_clamp(writeback_rate_i_term_inverse, + dc->writeback_rate_i_term_inverse, + 1, UINT_MAX); d_strtoul_nonzero(writeback_rate_p_term_inverse); d_strtoul_nonzero(writeback_rate_minimum); From 5b5fd3c94eef69dcfaa8648198e54c92e5687d6d Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:06 +0800 Subject: [PATCH 1476/4277] bcache: fix potential div-zero error of writeback_rate_p_term_inverse Current code already uses d_strtoul_nonzero() to convert input string to an unsigned integer, to make sure writeback_rate_p_term_inverse won't be zero value. But overflow may happen when converting input string to an unsigned integer value by d_strtoul_nonzero(), then dc->writeback_rate_p_term_inverse can still be set to 0 even if the sysfs file input value is not zero, e.g. 4294967296 (a.k.a UINT_MAX+1). If dc->writeback_rate_p_term_inverse is set to 0, it might cause a dev-zero error in following code from __update_writeback_rate(), int64_t proportional_scaled = div_s64(error, dc->writeback_rate_p_term_inverse); This patch replaces d_strtoul_nonzero() by sysfs_strtoul_clamp() and limit the value range in [1, UINT_MAX]. Then the unsigned integer overflow and dev-zero error can be avoided. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 0fad46d3a8bd..c6677c93e368 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -305,7 +305,9 @@ STORE(__cached_dev) sysfs_strtoul_clamp(writeback_rate_i_term_inverse, dc->writeback_rate_i_term_inverse, 1, UINT_MAX); - d_strtoul_nonzero(writeback_rate_p_term_inverse); + sysfs_strtoul_clamp(writeback_rate_p_term_inverse, + dc->writeback_rate_p_term_inverse, + 1, UINT_MAX); d_strtoul_nonzero(writeback_rate_minimum); sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX); From dab71b2db98dcdd4657d151b01a7be88ce10f9d1 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:07 +0800 Subject: [PATCH 1477/4277] bcache: fix input overflow to writeback_rate_minimum dc->writeback_rate_minimum is type unsigned integer variable, it is set via sysfs interface, and converte from input string to unsigned integer by d_strtoul_nonzero(). When the converted input value is larger than UINT_MAX, overflow to unsigned integer happens. This patch fixes the overflow by using sysfs_strotoul_clamp() to convert input string and limit the value in range [1, UINT_MAX], then the overflow can be avoided. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index c6677c93e368..d292eb757ac4 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -308,7 +308,9 @@ STORE(__cached_dev) sysfs_strtoul_clamp(writeback_rate_p_term_inverse, dc->writeback_rate_p_term_inverse, 1, UINT_MAX); - d_strtoul_nonzero(writeback_rate_minimum); + sysfs_strtoul_clamp(writeback_rate_minimum, + dc->writeback_rate_minimum, + 1, UINT_MAX); sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX); From 453745fbbebecf7e459785db7e29e11563908525 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:08 +0800 Subject: [PATCH 1478/4277] bcache: fix input overflow to journal_delay_ms c->journal_delay_ms is in type unsigned short, it is set via sysfs interface and converted by sysfs_strtoul() from input string to unsigned short value. Therefore overflow to unsigned short might be happen when the converted value exceed USHRT_MAX. e.g. writing 65536 into sysfs file journal_delay_ms, c->journal_delay_ms is set to 0. This patch uses sysfs_strtoul_clamp() to convert the input string and limit value range in [0, USHRT_MAX], to avoid the input overflow. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index d292eb757ac4..201e85bbe3eb 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -821,7 +821,9 @@ STORE(__bch_cache_set) } } - sysfs_strtoul(journal_delay_ms, c->journal_delay_ms); + sysfs_strtoul_clamp(journal_delay_ms, + c->journal_delay_ms, + 0, USHRT_MAX); sysfs_strtoul_bool(verify, c->verify); sysfs_strtoul_bool(key_merging_disabled, c->key_merging_disabled); sysfs_strtoul(expensive_debug_checks, c->expensive_debug_checks); From b15008403b59955c9fa0c8b55cadd6dae991a4e9 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:09 +0800 Subject: [PATCH 1479/4277] bcache: fix input overflow to cache set io_error_limit c->error_limit is in type unsigned int, it is set via cache set sysfs file io_error_limit. Inside the bcache code, input string is converted by strtoul_or_return() and set the converted value to c->error_limit. Because the converted value is unsigned long, and c->error_limit is unsigned int, if the input is large enought, overflow will happen to c->error_limit. This patch uses sysfs_strtoul_clamp() to convert input string, and set the range in [0, UINT_MAX] to avoid the potential overflow. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 201e85bbe3eb..467105614324 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -801,8 +801,7 @@ STORE(__bch_cache_set) c->on_error = v; } - if (attr == &sysfs_io_error_limit) - c->error_limit = strtoul_or_return(buf); + sysfs_strtoul_clamp(io_error_limit, c->error_limit, 0, UINT_MAX); /* See count_io_errors() for why 88 */ if (attr == &sysfs_io_error_halflife) From a91fbda49f746119828f7e8ad0f0aa2ab0578f65 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:10 +0800 Subject: [PATCH 1480/4277] bcache: fix input overflow to cache set sysfs file io_error_halflife Cache set sysfs entry io_error_halflife is used to set c->error_decay. c->error_decay is in type unsigned int, and it is converted by strtoul_or_return(), therefore overflow to c->error_decay is possible for a large input value. This patch fixes the overflow by using strtoul_safe_clamp() to convert input string to an unsigned long value in range [0, UINT_MAX], then divides by 88 and set it to c->error_decay. Signed-off-by: Coly Li Signed-off-by: Jens Axboe --- drivers/md/bcache/sysfs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 467105614324..17bae9c14ca0 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -804,8 +804,17 @@ STORE(__bch_cache_set) sysfs_strtoul_clamp(io_error_limit, c->error_limit, 0, UINT_MAX); /* See count_io_errors() for why 88 */ - if (attr == &sysfs_io_error_halflife) - c->error_decay = strtoul_or_return(buf) / 88; + if (attr == &sysfs_io_error_halflife) { + unsigned long v = 0; + ssize_t ret; + + ret = strtoul_safe_clamp(buf, v, 0, UINT_MAX); + if (!ret) { + c->error_decay = v / 88; + return size; + } + return ret; + } if (attr == &sysfs_io_disable) { v = strtoul_or_return(buf); From dc7292a5bcb4c878b076fca2ac3fc22f81b8f8df Mon Sep 17 00:00:00 2001 From: Coly Li Date: Sat, 9 Feb 2019 12:53:11 +0800 Subject: [PATCH 1481/4277] bcache: use (REQ_META|REQ_PRIO) to indicate bio for metadata In 'commit 752f66a75aba ("bcache: use REQ_PRIO to indicate bio for metadata")' REQ_META is replaced by REQ_PRIO to indicate metadata bio. This assumption is not always correct, e.g. XFS uses REQ_META to mark metadata bio other than REQ_PRIO. This is why Nix noticed that bcache does not cache metadata for XFS after the above commit. Thanks to Dave Chinner, he explains the difference between REQ_META and REQ_PRIO from view of file system developer. Here I quote part of his explanation from mailing list, REQ_META is used for metadata. REQ_PRIO is used to communicate to the lower layers that the submitter considers this IO to be more important that non REQ_PRIO IO and so dispatch should be expedited. IOWs, if the filesystem considers metadata IO to be more important that user data IO, then it will use REQ_PRIO | REQ_META rather than just REQ_META. Then it seems bios with REQ_META or REQ_PRIO should both be cached for performance optimation, because they are all probably low I/O latency demand by upper layer (e.g. file system). So in this patch, when we want to decide whether to bypass the cache, REQ_META and REQ_PRIO are both checked. Then both metadata and high priority I/O requests will be handled properly. Reported-by: Nix Signed-off-by: Coly Li Reviewed-by: Andre Noll Tested-by: Nix Cc: stable@vger.kernel.org Cc: Dave Chinner Cc: Christoph Hellwig Signed-off-by: Jens Axboe --- drivers/md/bcache/request.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 15070412a32e..f101bfe8657a 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -392,10 +392,11 @@ static bool check_should_bypass(struct cached_dev *dc, struct bio *bio) /* * Flag for bypass if the IO is for read-ahead or background, - * unless the read-ahead request is for metadata (eg, for gfs2). + * unless the read-ahead request is for metadata + * (eg, for gfs2 or xfs). */ if (bio->bi_opf & (REQ_RAHEAD|REQ_BACKGROUND) && - !(bio->bi_opf & REQ_PRIO)) + !(bio->bi_opf & (REQ_META|REQ_PRIO))) goto skip; if (bio->bi_iter.bi_sector & (c->sb.block_size - 1) || @@ -877,7 +878,7 @@ static int cached_dev_cache_miss(struct btree *b, struct search *s, } if (!(bio->bi_opf & REQ_RAHEAD) && - !(bio->bi_opf & REQ_PRIO) && + !(bio->bi_opf & (REQ_META|REQ_PRIO)) && s->iop.c->gc_stats.in_use < CUTOFF_CACHE_READA) reada = min_t(sector_t, dc->readahead >> 9, get_capacity(bio->bi_disk) - bio_end_sector(bio)); From 10ecc818ea7319b5d0d2b4e1aa6a77323e776f76 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2019 17:59:07 -0600 Subject: [PATCH 1482/4277] PCI/ASPM: Use LTR if already enabled by platform RussianNeuroMancer reported that the Intel 7265 wifi on a Dell Venue 11 Pro 7140 table stopped working after wakeup from suspend and bisected the problem to 9ab105deb60f ("PCI/ASPM: Disable ASPM L1.2 Substate if we don't have LTR"). David Ward reported the same problem on a Dell Latitude 7350. After af8bb9f89838 ("PCI/ACPI: Request LTR control from platform before using it"), we don't enable LTR unless the platform has granted LTR control to us. In addition, we don't notice if the platform had already enabled LTR itself. After 9ab105deb60f ("PCI/ASPM: Disable ASPM L1.2 Substate if we don't have LTR"), we avoid using LTR if we don't think the path to the device has LTR enabled. The combination means that if the platform itself enables LTR but declines to give the OS control over LTR, we unnecessarily avoided using ASPM L1.2. Link: https://bugzilla.kernel.org/show_bug.cgi?id=201469 Fixes: 9ab105deb60f ("PCI/ASPM: Disable ASPM L1.2 Substate if we don't have LTR") Fixes: af8bb9f89838 ("PCI/ACPI: Request LTR control from platform before using it") Reported-by: RussianNeuroMancer Reported-by: David Ward Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v4.18+ --- drivers/pci/probe.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..c46a3fcb341e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2071,11 +2071,8 @@ static void pci_configure_ltr(struct pci_dev *dev) { #ifdef CONFIG_PCIEASPM struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); - u32 cap; struct pci_dev *bridge; - - if (!host->native_ltr) - return; + u32 cap, ctl; if (!pci_is_pcie(dev)) return; @@ -2084,22 +2081,35 @@ static void pci_configure_ltr(struct pci_dev *dev) if (!(cap & PCI_EXP_DEVCAP2_LTR)) return; - /* - * Software must not enable LTR in an Endpoint unless the Root - * Complex and all intermediate Switches indicate support for LTR. - * PCIe r3.1, sec 6.18. - */ - if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) - dev->ltr_path = 1; - else { + pcie_capability_read_dword(dev, PCI_EXP_DEVCTL2, &ctl); + if (ctl & PCI_EXP_DEVCTL2_LTR_EN) { + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) { + dev->ltr_path = 1; + return; + } + bridge = pci_upstream_bridge(dev); if (bridge && bridge->ltr_path) dev->ltr_path = 1; + + return; } - if (dev->ltr_path) + if (!host->native_ltr) + return; + + /* + * Software must not enable LTR in an Endpoint unless the Root + * Complex and all intermediate Switches indicate support for LTR. + * PCIe r4.0, sec 6.18. + */ + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + ((bridge = pci_upstream_bridge(dev)) && + bridge->ltr_path)) { pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); + dev->ltr_path = 1; + } #endif } From 320f07b4a92216aa313680e3c7c7d325d8642715 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 9 Feb 2019 08:51:17 -0800 Subject: [PATCH 1483/4277] Input: msm-vibrator - tweak an error message The PTR_ERR(NULL) value is zero and it's not useful to print that. Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/misc/msm-vibrator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/msm-vibrator.c b/drivers/input/misc/msm-vibrator.c index c06941021447..69f2579c4ec2 100644 --- a/drivers/input/misc/msm-vibrator.c +++ b/drivers/input/misc/msm-vibrator.c @@ -199,8 +199,7 @@ static int msm_vibrator_probe(struct platform_device *pdev) vibrator->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!vibrator->base) { - dev_err(&pdev->dev, "Failed to iomap resource: %ld\n", - PTR_ERR(vibrator->base)); + dev_err(&pdev->dev, "Failed to iomap resource.\n"); return -ENOMEM; } From f67cc3e927d8414ad3872e046764534ea1f5db0d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 9 Feb 2019 08:49:38 -0800 Subject: [PATCH 1484/4277] Input: ili210x - fetch touchscreen geometry from DT Fetching the geometry from the ILI251x registers seems unreliable and sometimes returns all zeroes. Add support for fetching the geometry and axis inversion from DT instead. Signed-off-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 51 +++++++---------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index af1dd9cff12a..9169aa03958a 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -20,15 +21,6 @@ #define REG_FIRMWARE_VERSION 0x40 #define REG_CALIBRATE 0xcc -struct panel_info { - u8 x_low; - u8 x_high; - u8 y_low; - u8 y_high; - u8 xchannel_num; - u8 ychannel_num; -} __packed; - struct firmware_version { u8 id; u8 major; @@ -46,6 +38,7 @@ struct ili210x { unsigned int poll_period; struct delayed_work dwork; struct gpio_desc *reset_gpio; + struct touchscreen_properties prop; enum ili2xxx_model model; unsigned int max_touches; }; @@ -149,8 +142,6 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) unsigned int x = 0, y = 0; for (i = 0; i < priv->max_touches; i++) { - input_mt_slot(input, i); - if (priv->model == MODEL_ILI210X) { touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); @@ -161,11 +152,12 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) contact = true; } + input_mt_slot(input, i); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (touch) { - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - } + if (!touch) + continue; + touchscreen_report_pos(input, &priv->prop, x, y, + true); } input_mt_report_pointer_emulation(input, false); @@ -274,10 +266,8 @@ static int ili210x_i2c_probe(struct i2c_client *client, struct ili210x *priv; struct gpio_desc *reset_gpio; struct input_dev *input; - struct panel_info panel; struct firmware_version firmware; enum ili2xxx_model model; - int xmax, ymax; int error; model = (enum ili2xxx_model)id->driver_data; @@ -334,35 +324,16 @@ static int ili210x_i2c_probe(struct i2c_client *client, return error; } - /* get panel info */ - error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); - if (error) { - dev_err(dev, "Failed to get panel information, err: %d\n", - error); - return error; - } - - xmax = panel.x_low | (panel.x_high << 8); - ymax = panel.y_low | (panel.y_high << 8); - /* Setup input device */ input->name = "ILI210x Touchscreen"; input->id.bustype = BUS_I2C; input->dev.parent = dev; - __set_bit(EV_SYN, input->evbit); - __set_bit(EV_KEY, input->evbit); - __set_bit(EV_ABS, input->evbit); - __set_bit(BTN_TOUCH, input->keybit); - - /* Single touch */ - input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); - input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); - /* Multi touch */ - input_mt_init_slots(input, priv->max_touches, 0); - input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); + touchscreen_parse_properties(input, true, &priv->prop); + input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); error = devm_add_action(dev, ili210x_cancel_work, priv); if (error) From d11a3998985b351aaab6bbdc23bc884bd5e815c8 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 9 Feb 2019 15:40:24 -0700 Subject: [PATCH 1485/4277] block: kill QUEUE_FLAG_FLUSH_NQ We have various helpers for setting/clearing this flag, and also a helper to check if the queue supports queueable flushes or not. But nobody uses them anymore, kill it with fire. Signed-off-by: Jens Axboe --- block/blk-mq-debugfs.c | 1 - block/blk-settings.c | 9 --------- drivers/ata/libata-scsi.c | 2 -- drivers/block/null_blk_main.c | 1 - include/linux/blkdev.h | 7 ------- 5 files changed, 20 deletions(-) diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index f8120832ca7b..c782e81db627 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -132,7 +132,6 @@ static const char *const blk_queue_flag_name[] = { QUEUE_FLAG_NAME(POLL), QUEUE_FLAG_NAME(WC), QUEUE_FLAG_NAME(FUA), - QUEUE_FLAG_NAME(FLUSH_NQ), QUEUE_FLAG_NAME(DAX), QUEUE_FLAG_NAME(STATS), QUEUE_FLAG_NAME(POLL_STATS), diff --git a/block/blk-settings.c b/block/blk-settings.c index 3e7038e475ee..6375afaedcec 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -799,15 +799,6 @@ void blk_queue_update_dma_alignment(struct request_queue *q, int mask) } EXPORT_SYMBOL(blk_queue_update_dma_alignment); -void blk_queue_flush_queueable(struct request_queue *q, bool queueable) -{ - if (queueable) - blk_queue_flag_clear(QUEUE_FLAG_FLUSH_NQ, q); - else - blk_queue_flag_set(QUEUE_FLAG_FLUSH_NQ, q); -} -EXPORT_SYMBOL_GPL(blk_queue_flush_queueable); - /** * blk_set_queue_depth - tell the block layer about the device queue depth * @q: the request queue for the device diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 3d4887d0e84a..dfe66d00dd5b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1318,8 +1318,6 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, scsi_change_queue_depth(sdev, depth); } - blk_queue_flush_queueable(q, false); - if (dev->flags & ATA_DFLAG_TRUSTED) sdev->security_supported = 1; diff --git a/drivers/block/null_blk_main.c b/drivers/block/null_blk_main.c index 62c9654b9ce8..83c38a6217d7 100644 --- a/drivers/block/null_blk_main.c +++ b/drivers/block/null_blk_main.c @@ -1678,7 +1678,6 @@ static int null_add_dev(struct nullb_device *dev) if (dev->cache_size > 0) { set_bit(NULLB_DEV_FL_CACHE, &nullb->dev->flags); blk_queue_write_cache(nullb->q, true, true); - blk_queue_flush_queueable(nullb->q, true); } if (dev->zoned) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 338604dff7d0..24ccab51085f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -592,7 +592,6 @@ struct request_queue { #define QUEUE_FLAG_POLL 19 /* IO polling enabled if set */ #define QUEUE_FLAG_WC 20 /* Write back caching */ #define QUEUE_FLAG_FUA 21 /* device supports FUA writes */ -#define QUEUE_FLAG_FLUSH_NQ 22 /* flush not queueuable */ #define QUEUE_FLAG_DAX 23 /* device supports DAX */ #define QUEUE_FLAG_STATS 24 /* track IO start and completion times */ #define QUEUE_FLAG_POLL_STATS 25 /* collecting stats for hybrid polling */ @@ -1069,7 +1068,6 @@ extern void blk_queue_virt_boundary(struct request_queue *, unsigned long); extern void blk_queue_dma_alignment(struct request_queue *, int); extern void blk_queue_update_dma_alignment(struct request_queue *, int); extern void blk_queue_rq_timeout(struct request_queue *, unsigned int); -extern void blk_queue_flush_queueable(struct request_queue *q, bool queueable); extern void blk_queue_write_cache(struct request_queue *q, bool enabled, bool fua); /* @@ -1446,11 +1444,6 @@ static inline unsigned int block_size(struct block_device *bdev) return bdev->bd_block_size; } -static inline bool queue_flush_queueable(struct request_queue *q) -{ - return !test_bit(QUEUE_FLAG_FLUSH_NQ, &q->queue_flags); -} - typedef struct {struct page *v;} Sector; unsigned char *read_dev_sector(struct block_device *, sector_t, Sector *); From eca7abf31abba2acac445ec6a1d3f94cf0cab918 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 9 Feb 2019 15:42:07 -0700 Subject: [PATCH 1486/4277] block: queue flag cleanup We have QUEUE_FLAG_DEFAULT defined, but it's not used anymore since the legacy IO stack is gone. Kill it. Sanitize the queue flags in general, they use spaces (for some reason), and the space is pretty sparse. With the flags renumbered, we can more clearly see how many we have available. Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 58 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 24ccab51085f..3603270cb82d 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -572,37 +572,33 @@ struct request_queue { u64 write_hints[BLK_MAX_WRITE_HINTS]; }; -#define QUEUE_FLAG_STOPPED 1 /* queue is stopped */ -#define QUEUE_FLAG_DYING 2 /* queue being torn down */ -#define QUEUE_FLAG_BIDI 4 /* queue supports bidi requests */ -#define QUEUE_FLAG_NOMERGES 5 /* disable merge attempts */ -#define QUEUE_FLAG_SAME_COMP 6 /* complete on same CPU-group */ -#define QUEUE_FLAG_FAIL_IO 7 /* fake timeout */ -#define QUEUE_FLAG_NONROT 9 /* non-rotational device (SSD) */ -#define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */ -#define QUEUE_FLAG_IO_STAT 10 /* do disk/partitions IO accounting */ -#define QUEUE_FLAG_DISCARD 11 /* supports DISCARD */ -#define QUEUE_FLAG_NOXMERGES 12 /* No extended merges */ -#define QUEUE_FLAG_ADD_RANDOM 13 /* Contributes to random pool */ -#define QUEUE_FLAG_SECERASE 14 /* supports secure erase */ -#define QUEUE_FLAG_SAME_FORCE 15 /* force complete on same CPU */ -#define QUEUE_FLAG_DEAD 16 /* queue tear-down finished */ -#define QUEUE_FLAG_INIT_DONE 17 /* queue is initialized */ -#define QUEUE_FLAG_NO_SG_MERGE 18 /* don't attempt to merge SG segments*/ -#define QUEUE_FLAG_POLL 19 /* IO polling enabled if set */ -#define QUEUE_FLAG_WC 20 /* Write back caching */ -#define QUEUE_FLAG_FUA 21 /* device supports FUA writes */ -#define QUEUE_FLAG_DAX 23 /* device supports DAX */ -#define QUEUE_FLAG_STATS 24 /* track IO start and completion times */ -#define QUEUE_FLAG_POLL_STATS 25 /* collecting stats for hybrid polling */ -#define QUEUE_FLAG_REGISTERED 26 /* queue has been registered to a disk */ -#define QUEUE_FLAG_SCSI_PASSTHROUGH 27 /* queue supports SCSI commands */ -#define QUEUE_FLAG_QUIESCED 28 /* queue has been quiesced */ -#define QUEUE_FLAG_PCI_P2PDMA 29 /* device supports PCI p2p requests */ - -#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ - (1 << QUEUE_FLAG_SAME_COMP) | \ - (1 << QUEUE_FLAG_ADD_RANDOM)) +#define QUEUE_FLAG_STOPPED 0 /* queue is stopped */ +#define QUEUE_FLAG_DYING 1 /* queue being torn down */ +#define QUEUE_FLAG_BIDI 2 /* queue supports bidi requests */ +#define QUEUE_FLAG_NOMERGES 3 /* disable merge attempts */ +#define QUEUE_FLAG_SAME_COMP 4 /* complete on same CPU-group */ +#define QUEUE_FLAG_FAIL_IO 5 /* fake timeout */ +#define QUEUE_FLAG_NONROT 6 /* non-rotational device (SSD) */ +#define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */ +#define QUEUE_FLAG_IO_STAT 7 /* do disk/partitions IO accounting */ +#define QUEUE_FLAG_DISCARD 8 /* supports DISCARD */ +#define QUEUE_FLAG_NOXMERGES 9 /* No extended merges */ +#define QUEUE_FLAG_ADD_RANDOM 10 /* Contributes to random pool */ +#define QUEUE_FLAG_SECERASE 11 /* supports secure erase */ +#define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */ +#define QUEUE_FLAG_DEAD 13 /* queue tear-down finished */ +#define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */ +#define QUEUE_FLAG_NO_SG_MERGE 15 /* don't attempt to merge SG segments*/ +#define QUEUE_FLAG_POLL 16 /* IO polling enabled if set */ +#define QUEUE_FLAG_WC 17 /* Write back caching */ +#define QUEUE_FLAG_FUA 18 /* device supports FUA writes */ +#define QUEUE_FLAG_DAX 19 /* device supports DAX */ +#define QUEUE_FLAG_STATS 20 /* track IO start and completion times */ +#define QUEUE_FLAG_POLL_STATS 21 /* collecting stats for hybrid polling */ +#define QUEUE_FLAG_REGISTERED 22 /* queue has been registered to a disk */ +#define QUEUE_FLAG_SCSI_PASSTHROUGH 23 /* queue supports SCSI commands */ +#define QUEUE_FLAG_QUIESCED 24 /* queue has been quiesced */ +#define QUEUE_FLAG_PCI_P2PDMA 25 /* device supports PCI p2p requests */ #define QUEUE_FLAG_MQ_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ (1 << QUEUE_FLAG_SAME_COMP)) From 401e7e88d4ef80188ffa07095ac00456f901b8c4 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 28 Jan 2019 11:08:54 +0800 Subject: [PATCH 1487/4277] ipmi_si: fix use-after-free of resource->name When we excute the following commands, we got oops rmmod ipmi_si cat /proc/ioports [ 1623.482380] Unable to handle kernel paging request at virtual address ffff00000901d478 [ 1623.482382] Mem abort info: [ 1623.482383] ESR = 0x96000007 [ 1623.482385] Exception class = DABT (current EL), IL = 32 bits [ 1623.482386] SET = 0, FnV = 0 [ 1623.482387] EA = 0, S1PTW = 0 [ 1623.482388] Data abort info: [ 1623.482389] ISV = 0, ISS = 0x00000007 [ 1623.482390] CM = 0, WnR = 0 [ 1623.482393] swapper pgtable: 4k pages, 48-bit VAs, pgdp = 00000000d7d94a66 [ 1623.482395] [ffff00000901d478] pgd=000000dffbfff003, pud=000000dffbffe003, pmd=0000003f5d06e003, pte=0000000000000000 [ 1623.482399] Internal error: Oops: 96000007 [#1] SMP [ 1623.487407] Modules linked in: ipmi_si(E) nls_utf8 isofs rpcrdma ib_iser ib_srpt target_core_mod ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_umad rdma_cm ib_cm dm_mirror dm_region_hash dm_log iw_cm dm_mod aes_ce_blk crypto_simd cryptd aes_ce_cipher ses ghash_ce sha2_ce enclosure sha256_arm64 sg sha1_ce hisi_sas_v2_hw hibmc_drm sbsa_gwdt hisi_sas_main ip_tables mlx5_ib ib_uverbs marvell ib_core mlx5_core ixgbe mdio hns_dsaf ipmi_devintf hns_enet_drv ipmi_msghandler hns_mdio [last unloaded: ipmi_si] [ 1623.532410] CPU: 30 PID: 11438 Comm: cat Kdump: loaded Tainted: G E 5.0.0-rc3+ #168 [ 1623.541498] Hardware name: Huawei TaiShan 2280 /BC11SPCD, BIOS 1.37 11/21/2017 [ 1623.548822] pstate: a0000005 (NzCv daif -PAN -UAO) [ 1623.553684] pc : string+0x28/0x98 [ 1623.557040] lr : vsnprintf+0x368/0x5e8 [ 1623.560837] sp : ffff000013213a80 [ 1623.564191] x29: ffff000013213a80 x28: ffff00001138abb5 [ 1623.569577] x27: ffff000013213c18 x26: ffff805f67d06049 [ 1623.574963] x25: 0000000000000000 x24: ffff00001138abb5 [ 1623.580349] x23: 0000000000000fb7 x22: ffff0000117ed000 [ 1623.585734] x21: ffff000011188fd8 x20: ffff805f67d07000 [ 1623.591119] x19: ffff805f67d06061 x18: ffffffffffffffff [ 1623.596505] x17: 0000000000000200 x16: 0000000000000000 [ 1623.601890] x15: ffff0000117ed748 x14: ffff805f67d07000 [ 1623.607276] x13: ffff805f67d0605e x12: 0000000000000000 [ 1623.612661] x11: 0000000000000000 x10: 0000000000000000 [ 1623.618046] x9 : 0000000000000000 x8 : 000000000000000f [ 1623.623432] x7 : ffff805f67d06061 x6 : fffffffffffffffe [ 1623.628817] x5 : 0000000000000012 x4 : ffff00000901d478 [ 1623.634203] x3 : ffff0a00ffffff04 x2 : ffff805f67d07000 [ 1623.639588] x1 : ffff805f67d07000 x0 : ffffffffffffffff [ 1623.644974] Process cat (pid: 11438, stack limit = 0x000000008d4cbc10) [ 1623.651592] Call trace: [ 1623.654068] string+0x28/0x98 [ 1623.657071] vsnprintf+0x368/0x5e8 [ 1623.660517] seq_vprintf+0x70/0x98 [ 1623.668009] seq_printf+0x7c/0xa0 [ 1623.675530] r_show+0xc8/0xf8 [ 1623.682558] seq_read+0x330/0x440 [ 1623.689877] proc_reg_read+0x78/0xd0 [ 1623.697346] __vfs_read+0x60/0x1a0 [ 1623.704564] vfs_read+0x94/0x150 [ 1623.711339] ksys_read+0x6c/0xd8 [ 1623.717939] __arm64_sys_read+0x24/0x30 [ 1623.725077] el0_svc_common+0x120/0x148 [ 1623.732035] el0_svc_handler+0x30/0x40 [ 1623.738757] el0_svc+0x8/0xc [ 1623.744520] Code: d1000406 aa0103e2 54000149 b4000080 (39400085) [ 1623.753441] ---[ end trace f91b6a4937de9835 ]--- [ 1623.760871] Kernel panic - not syncing: Fatal exception [ 1623.768935] SMP: stopping secondary CPUs [ 1623.775718] Kernel Offset: disabled [ 1623.781998] CPU features: 0x002,21006008 [ 1623.788777] Memory Limit: none [ 1623.798329] Starting crashdump kernel... [ 1623.805202] Bye! If io_setup is called successful in try_smi_init() but try_smi_init() goes out_err before calling ipmi_register_smi(), so ipmi_unregister_smi() will not be called while removing module. It leads to the resource that allocated in io_setup() can not be freed, but the name(DEVICE_NAME) of resource is freed while removing the module. It causes use-after-free when cat /proc/ioports. Fix this by calling io_cleanup() while try_smi_init() goes to out_err. and don't call io_cleanup() until io_setup() returns successful to avoid warning prints. Fixes: 93c303d2045b ("ipmi_si: Clean up shutdown a bit") Cc: stable@vger.kernel.org Reported-by: NuoHan Qiao Suggested-by: Corey Minyard Signed-off-by: Yang Yingliang Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_si_intf.c | 5 +++++ drivers/char/ipmi/ipmi_si_mem_io.c | 5 +++-- drivers/char/ipmi/ipmi_si_port_io.c | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index dc8603d34320..f1b9fda6b9df 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2085,6 +2085,11 @@ static int try_smi_init(struct smi_info *new_smi) WARN_ON(new_smi->io.dev->init_name != NULL); out_err: + if (rv && new_smi->io.io_cleanup) { + new_smi->io.io_cleanup(&new_smi->io); + new_smi->io.io_cleanup = NULL; + } + kfree(init_name); return rv; } diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c index fd0ec8d6bf0e..75583612ab10 100644 --- a/drivers/char/ipmi/ipmi_si_mem_io.c +++ b/drivers/char/ipmi/ipmi_si_mem_io.c @@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io) if (!addr) return -ENODEV; - io->io_cleanup = mem_cleanup; - /* * Figure out the actual readb/readw/readl/etc routine to use based * upon the register size. @@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io) mem_region_cleanup(io, io->io_size); return -EIO; } + + io->io_cleanup = mem_cleanup; + return 0; } diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c index ef6dffcea9fa..03924c32b6e9 100644 --- a/drivers/char/ipmi/ipmi_si_port_io.c +++ b/drivers/char/ipmi/ipmi_si_port_io.c @@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io) if (!addr) return -ENODEV; - io->io_cleanup = port_cleanup; - /* * Figure out the actual inb/inw/inl/etc routine to use based * upon the register size. @@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io) return -EIO; } } + + io->io_cleanup = port_cleanup; + return 0; } From a1466ec5b671651b848df17fc9233ecbb7d35f9f Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 22 Oct 2018 15:30:57 -0500 Subject: [PATCH 1488/4277] ipmi: Fix SSIF flag requests Commit 89986496de141 ("ipmi: Turn off all activity on an idle ipmi interface") modified the IPMI code to only request events when the driver had somethine waiting for events. The SSIF code, however, was using the event fetch request to also fetch the flags. Add a timer and the proper handling for the upper layer telling whether flags fetches are required. Reported-by: Kamlakant Patel Signed-off-by: Corey Minyard Tested-by: Kamlakant Patel --- drivers/char/ipmi/ipmi_ssif.c | 64 ++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index b7a1ae2afaea..1aacc1144d2a 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -90,6 +90,12 @@ #define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC) #define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC) +/* + * Timeout for the watch, only used for get flag timer. + */ +#define SSIF_WATCH_TIMEOUT_MSEC 100 +#define SSIF_WATCH_TIMEOUT_JIFFIES msecs_to_jiffies(SSIF_WATCH_TIMEOUT_MSEC) + enum ssif_intf_state { SSIF_NORMAL, SSIF_GETTING_FLAGS, @@ -270,6 +276,9 @@ struct ssif_info { struct timer_list retry_timer; int retries_left; + bool need_watch; /* Need to look for flags? */ + struct timer_list watch_timer; /* Flag fetch timer. */ + /* Info from SSIF cmd */ unsigned char max_xmit_msg_size; unsigned char max_recv_msg_size; @@ -560,6 +569,26 @@ static void retry_timeout(struct timer_list *t) start_get(ssif_info); } +static void watch_timeout(struct timer_list *t) +{ + struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer); + unsigned long oflags, *flags; + + if (ssif_info->stopping) + return; + + flags = ipmi_ssif_lock_cond(ssif_info, &oflags); + if (ssif_info->need_watch) { + mod_timer(&ssif_info->watch_timer, + jiffies + SSIF_WATCH_TIMEOUT_JIFFIES); + if (SSIF_IDLE(ssif_info)) { + start_flag_fetch(ssif_info, flags); /* Releases lock */ + return; + } + ssif_info->req_flags = true; + } + ipmi_ssif_unlock_cond(ssif_info, flags); +} static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type, unsigned int data) @@ -1073,8 +1102,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data) } /* - * Instead of having our own timer to periodically check the message - * flags, we let the message handler drive us. + * Upper layer wants us to request events. */ static void request_events(void *send_info) { @@ -1085,18 +1113,27 @@ static void request_events(void *send_info) return; flags = ipmi_ssif_lock_cond(ssif_info, &oflags); - /* - * Request flags first, not events, because the lower layer - * doesn't have a way to send an attention. But make sure - * event checking still happens. - */ ssif_info->req_events = true; - if (SSIF_IDLE(ssif_info)) - start_flag_fetch(ssif_info, flags); - else { - ssif_info->req_flags = true; - ipmi_ssif_unlock_cond(ssif_info, flags); + ipmi_ssif_unlock_cond(ssif_info, flags); +} + +/* + * Upper layer is changing the flag saying whether we need to request + * flags periodically or not. + */ +static void ssif_set_need_watch(void *send_info, bool enable) +{ + struct ssif_info *ssif_info = (struct ssif_info *) send_info; + unsigned long oflags, *flags; + + flags = ipmi_ssif_lock_cond(ssif_info, &oflags); + if (enable != ssif_info->need_watch) { + ssif_info->need_watch = enable; + if (ssif_info->need_watch) + mod_timer(&ssif_info->watch_timer, + jiffies + SSIF_WATCH_TIMEOUT_JIFFIES); } + ipmi_ssif_unlock_cond(ssif_info, flags); } static int ssif_start_processing(void *send_info, @@ -1223,6 +1260,7 @@ static void shutdown_ssif(void *send_info) schedule_timeout(1); ssif_info->stopping = true; + del_timer_sync(&ssif_info->watch_timer); del_timer_sync(&ssif_info->retry_timer); if (ssif_info->thread) { complete(&ssif_info->wake_thread); @@ -1708,6 +1746,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) spin_lock_init(&ssif_info->lock); ssif_info->ssif_state = SSIF_NORMAL; timer_setup(&ssif_info->retry_timer, retry_timeout, 0); + timer_setup(&ssif_info->watch_timer, watch_timeout, 0); for (i = 0; i < SSIF_NUM_STATS; i++) atomic_set(&ssif_info->stats[i], 0); @@ -1721,6 +1760,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ssif_info->handlers.get_smi_info = get_smi_info; ssif_info->handlers.sender = sender; ssif_info->handlers.request_events = request_events; + ssif_info->handlers.set_need_watch = ssif_set_need_watch; { unsigned int thread_num; From c65ea996595005be470fbfa16711deba414fd33b Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 23 Oct 2018 11:29:02 -0500 Subject: [PATCH 1489/4277] ipmi: Fix how the lower layers are told to watch for messages The IPMI driver has a mechanism to tell the lower layers it needs to watch for messages, commands, and watchdogs (so it doesn't needlessly poll). However, it needed some extensions, it needed a way to tell what is being waited for so it could set the timeout appropriately. The update to the lower layer was also being done once a second at best because it was done in the main timeout handler. However, if a command is sent and a response message is coming back, it needed to be started immediately. So modify the code to update immediately if it needs to be enabled. Disable is still lazy. Signed-off-by: Corey Minyard Tested-by: Kamlakant Patel --- drivers/char/ipmi/ipmi_msghandler.c | 119 ++++++++++++++++++++-------- drivers/char/ipmi/ipmi_si_intf.c | 5 +- drivers/char/ipmi/ipmi_ssif.c | 26 +++--- include/linux/ipmi_smi.h | 36 +++++++-- 4 files changed, 134 insertions(+), 52 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c518659b4d9f..2e008efa735f 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -529,9 +529,22 @@ struct ipmi_smi { unsigned int waiting_events_count; /* How many events in queue? */ char delivering_events; char event_msg_printed; + + /* How many users are waiting for events? */ atomic_t event_waiters; unsigned int ticks_to_req_ev; - int last_needs_timer; + + /* How many users are waiting for commands? */ + atomic_t command_waiters; + + /* How many users are waiting for watchdogs? */ + atomic_t watchdog_waiters; + + /* + * Tells what the lower layer has last been asked to watch for, + * messages and/or watchdogs. Protected by xmit_msgs_lock. + */ + unsigned int last_watch_mask; /* * The event receiver for my BMC, only really used at panic @@ -1078,6 +1091,29 @@ static int intf_err_seq(struct ipmi_smi *intf, return rv; } +/* Must be called with xmit_msgs_lock held. */ +static void smi_tell_to_watch(struct ipmi_smi *intf, + unsigned int flags, + struct ipmi_smi_msg *smi_msg) +{ + if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) { + if (!smi_msg) + return; + + if (!smi_msg->needs_response) + return; + } + + if (!intf->handlers->set_need_watch) + return; + + if ((intf->last_watch_mask & flags) == flags) + return; + + intf->last_watch_mask |= flags; + intf->handlers->set_need_watch(intf->send_info, + intf->last_watch_mask); +} int ipmi_create_user(unsigned int if_num, const struct ipmi_user_hndl *handler, @@ -1141,8 +1177,9 @@ int ipmi_create_user(unsigned int if_num, spin_unlock_irqrestore(&intf->seq_lock, flags); if (handler->ipmi_watchdog_pretimeout) { /* User wants pretimeouts, so make sure to watch for them. */ - if (atomic_inc_return(&intf->event_waiters) == 1) - need_waiter(intf); + if (atomic_inc_return(&intf->watchdog_waiters) == 1) + smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG, + NULL); } srcu_read_unlock(&ipmi_interfaces_srcu, index); *user = new_user; @@ -1214,7 +1251,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) user->handler->shutdown(user->handler_data); if (user->handler->ipmi_watchdog_pretimeout) - atomic_dec(&intf->event_waiters); + atomic_dec(&intf->watchdog_waiters); if (user->gets_events) atomic_dec(&intf->event_waiters); @@ -1569,8 +1606,8 @@ int ipmi_register_for_cmd(struct ipmi_user *user, goto out_unlock; } - if (atomic_inc_return(&intf->event_waiters) == 1) - need_waiter(intf); + if (atomic_inc_return(&intf->command_waiters) == 1) + smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS, NULL); list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); @@ -1620,7 +1657,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, synchronize_rcu(); release_ipmi_user(user, index); while (rcvrs) { - atomic_dec(&intf->event_waiters); + atomic_dec(&intf->command_waiters); rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); @@ -1737,22 +1774,21 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf, return smi_msg; } - static void smi_send(struct ipmi_smi *intf, const struct ipmi_smi_handlers *handlers, struct ipmi_smi_msg *smi_msg, int priority) { int run_to_completion = intf->run_to_completion; + unsigned long flags = 0; - if (run_to_completion) { - smi_msg = smi_add_send_msg(intf, smi_msg, priority); - } else { - unsigned long flags; - + if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); - smi_msg = smi_add_send_msg(intf, smi_msg, priority); + smi_msg = smi_add_send_msg(intf, smi_msg, priority); + + smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES, smi_msg); + + if (!run_to_completion) spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); - } if (smi_msg) handlers->sender(intf->send_info, smi_msg); @@ -1950,6 +1986,9 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf, ipmb_seq, broadcast, source_address, source_lun); + /* We will be getting a response in the BMC message queue. */ + smi_msg->needs_response = true; + /* * Copy the message into the recv message data, so we * can retransmit it later if necessary. @@ -2137,6 +2176,7 @@ static int i_ipmi_request(struct ipmi_user *user, goto out; } } + smi_msg->needs_response = false; rcu_read_lock(); if (intf->in_shutdown) { @@ -3351,6 +3391,8 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, INIT_LIST_HEAD(&intf->hp_xmit_msgs); spin_lock_init(&intf->events_lock); atomic_set(&intf->event_waiters, 0); + atomic_set(&intf->watchdog_waiters, 0); + atomic_set(&intf->command_waiters, 0); intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; @@ -4365,6 +4407,9 @@ static void smi_recv_tasklet(unsigned long val) intf->curr_msg = newmsg; } } + + smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES, newmsg); + if (!run_to_completion) spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); if (newmsg) @@ -4492,7 +4537,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, struct list_head *timeouts, unsigned long timeout_period, int slot, unsigned long *flags, - unsigned int *waiting_msgs) + unsigned int *watch_mask) { struct ipmi_recv_msg *msg; @@ -4504,7 +4549,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, if (timeout_period < ent->timeout) { ent->timeout -= timeout_period; - (*waiting_msgs)++; + *watch_mask |= IPMI_WATCH_MASK_CHECK_MESSAGES; return; } @@ -4523,7 +4568,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, struct ipmi_smi_msg *smi_msg; /* More retries, send again. */ - (*waiting_msgs)++; + *watch_mask |= IPMI_WATCH_MASK_CHECK_MESSAGES; /* * Start with the max timer, set to normal timer after @@ -4575,13 +4620,13 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg, *msg2; unsigned long flags; int i; - unsigned int waiting_msgs = 0; + unsigned int watch_mask = 0; if (!intf->bmc_registered) { kref_get(&intf->refcount); if (!schedule_work(&intf->bmc_reg_work)) { kref_put(&intf->refcount, intf_free); - waiting_msgs++; + watch_mask |= IPMI_WATCH_MASK_INTERNAL; } } @@ -4601,7 +4646,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) check_msg_timeout(intf, &intf->seq_table[i], &timeouts, timeout_period, i, - &flags, &waiting_msgs); + &flags, &watch_mask); spin_unlock_irqrestore(&intf->seq_lock, flags); list_for_each_entry_safe(msg, msg2, &timeouts, link) @@ -4632,7 +4677,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, tasklet_schedule(&intf->recv_tasklet); - return waiting_msgs; + return watch_mask; } static void ipmi_request_event(struct ipmi_smi *intf) @@ -4652,37 +4697,43 @@ static atomic_t stop_operation; static void ipmi_timeout(struct timer_list *unused) { struct ipmi_smi *intf; - int nt = 0, index; + unsigned int watch_mask = 0; + int index; + unsigned long flags; if (atomic_read(&stop_operation)) return; index = srcu_read_lock(&ipmi_interfaces_srcu); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - int lnt = 0; - if (atomic_read(&intf->event_waiters)) { intf->ticks_to_req_ev--; if (intf->ticks_to_req_ev == 0) { ipmi_request_event(intf); intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; } - lnt++; + watch_mask |= IPMI_WATCH_MASK_INTERNAL; } - lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); + if (atomic_read(&intf->watchdog_waiters)) + watch_mask |= IPMI_WATCH_MASK_CHECK_WATCHDOG; - lnt = !!lnt; - if (lnt != intf->last_needs_timer && + if (atomic_read(&intf->command_waiters)) + watch_mask |= IPMI_WATCH_MASK_CHECK_COMMANDS; + + watch_mask |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); + + spin_lock_irqsave(&intf->xmit_msgs_lock, flags); + if (watch_mask != intf->last_watch_mask && intf->handlers->set_need_watch) - intf->handlers->set_need_watch(intf->send_info, lnt); - intf->last_needs_timer = lnt; - - nt += lnt; + intf->handlers->set_need_watch(intf->send_info, + watch_mask); + intf->last_watch_mask = watch_mask; + spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); } srcu_read_unlock(&ipmi_interfaces_srcu, index); - if (nt) + if (watch_mask) mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f1b9fda6b9df..c81c84a723b6 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1060,10 +1060,13 @@ static void request_events(void *send_info) atomic_set(&smi_info->req_events, 1); } -static void set_need_watch(void *send_info, bool enable) +static void set_need_watch(void *send_info, unsigned int watch_mask) { struct smi_info *smi_info = send_info; unsigned long flags; + int enable; + + enable = !!(watch_mask & ~IPMI_WATCH_MASK_INTERNAL); atomic_set(&smi_info->need_watch, enable); spin_lock_irqsave(&smi_info->si_lock, flags); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 1aacc1144d2a..a1219af32105 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -93,8 +93,8 @@ /* * Timeout for the watch, only used for get flag timer. */ -#define SSIF_WATCH_TIMEOUT_MSEC 100 -#define SSIF_WATCH_TIMEOUT_JIFFIES msecs_to_jiffies(SSIF_WATCH_TIMEOUT_MSEC) +#define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10) +#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250) enum ssif_intf_state { SSIF_NORMAL, @@ -276,7 +276,7 @@ struct ssif_info { struct timer_list retry_timer; int retries_left; - bool need_watch; /* Need to look for flags? */ + long watch_timeout; /* Timeout for flags check, 0 if off. */ struct timer_list watch_timer; /* Flag fetch timer. */ /* Info from SSIF cmd */ @@ -578,9 +578,9 @@ static void watch_timeout(struct timer_list *t) return; flags = ipmi_ssif_lock_cond(ssif_info, &oflags); - if (ssif_info->need_watch) { + if (ssif_info->watch_timeout) { mod_timer(&ssif_info->watch_timer, - jiffies + SSIF_WATCH_TIMEOUT_JIFFIES); + jiffies + ssif_info->watch_timeout); if (SSIF_IDLE(ssif_info)) { start_flag_fetch(ssif_info, flags); /* Releases lock */ return; @@ -1121,17 +1121,23 @@ static void request_events(void *send_info) * Upper layer is changing the flag saying whether we need to request * flags periodically or not. */ -static void ssif_set_need_watch(void *send_info, bool enable) +static void ssif_set_need_watch(void *send_info, unsigned int watch_mask) { struct ssif_info *ssif_info = (struct ssif_info *) send_info; unsigned long oflags, *flags; + long timeout = 0; + + if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES) + timeout = SSIF_WATCH_MSG_TIMEOUT; + else if (watch_mask & ~IPMI_WATCH_MASK_INTERNAL) + timeout = SSIF_WATCH_WATCHDOG_TIMEOUT; flags = ipmi_ssif_lock_cond(ssif_info, &oflags); - if (enable != ssif_info->need_watch) { - ssif_info->need_watch = enable; - if (ssif_info->need_watch) + if (timeout != ssif_info->watch_timeout) { + ssif_info->watch_timeout = timeout; + if (ssif_info->watch_timeout) mod_timer(&ssif_info->watch_timer, - jiffies + SSIF_WATCH_TIMEOUT_JIFFIES); + jiffies + ssif_info->watch_timeout); } ipmi_ssif_unlock_cond(ssif_info, flags); } diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 8c4e2ab696c3..da6abb06a5dc 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -30,6 +30,17 @@ struct device; /* Structure for the low-level drivers. */ struct ipmi_smi; +/* + * Flags for set_check_watch() below. Tells if the SMI should be + * waiting for watchdog timeouts, commands and/or messages. There is + * also an internal flag for the message handler, SMIs should ignore + * it. + */ +#define IPMI_WATCH_MASK_INTERNAL (1 << 0) +#define IPMI_WATCH_MASK_CHECK_MESSAGES (1 << 1) +#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 2) +#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 3) + /* * Messages to/from the lower layer. The smi interface will take one * of these to send. After the send has occurred and a response has @@ -55,8 +66,16 @@ struct ipmi_smi_msg { int rsp_size; unsigned char rsp[IPMI_MAX_MSG_LENGTH]; - /* Will be called when the system is done with the message - (presumably to free it). */ + /* + * There should be a response message coming back in the BMC + * message queue. + */ + bool needs_response; + + /* + * Will be called when the system is done with the message + * (presumably to free it). + */ void (*done)(struct ipmi_smi_msg *msg); }; @@ -105,12 +124,15 @@ struct ipmi_smi_handlers { /* * Called by the upper layer when some user requires that the - * interface watch for events, received messages, watchdog - * pretimeouts, or not. Used by the SMI to know if it should - * watch for these. This may be NULL if the SMI does not - * implement it. + * interface watch for received messages and watchdog + * pretimeouts (basically do a "Get Flags", or not. Used by + * the SMI to know if it should watch for these. This may be + * NULL if the SMI does not implement it. watch_mask is from + * IPMI_WATCH_MASK_xxx above. The interface should run slower + * timeouts for just watchdog checking or faster timeouts when + * waiting for the message queue. */ - void (*set_need_watch)(void *send_info, bool enable); + void (*set_need_watch)(void *send_info, unsigned int watch_mask); /* * Called when flushing all pending messages. From e1891cffd4c4896a899337a243273f0e23c028df Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 24 Oct 2018 15:17:04 -0500 Subject: [PATCH 1490/4277] ipmi: Make the smi watcher be disabled immediately when not needed The code to tell the lower layer to enable or disable watching for certain things was lazy in disabling, it waited until a timer tick to see if a disable was necessary. Not a really big deal, but it could be improved. Modify the code to enable and disable watching immediately and don't do it from the background timer any more. Signed-off-by: Corey Minyard Tested-by: Kamlakant Patel --- drivers/char/ipmi/ipmi_msghandler.c | 164 +++++++++++++++------------- drivers/char/ipmi/ipmi_si_intf.c | 2 +- drivers/char/ipmi/ipmi_ssif.c | 2 +- include/linux/ipmi_smi.h | 17 +-- 4 files changed, 96 insertions(+), 89 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 2e008efa735f..5bc84fbbeee5 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -534,15 +534,20 @@ struct ipmi_smi { atomic_t event_waiters; unsigned int ticks_to_req_ev; + spinlock_t watch_lock; /* For dealing with watch stuff below. */ + /* How many users are waiting for commands? */ - atomic_t command_waiters; + unsigned int command_waiters; /* How many users are waiting for watchdogs? */ - atomic_t watchdog_waiters; + unsigned int watchdog_waiters; + + /* How many users are waiting for message responses? */ + unsigned int response_waiters; /* * Tells what the lower layer has last been asked to watch for, - * messages and/or watchdogs. Protected by xmit_msgs_lock. + * messages and/or watchdogs. Protected by watch_lock. */ unsigned int last_watch_mask; @@ -938,6 +943,64 @@ static void deliver_err_response(struct ipmi_smi *intf, deliver_local_response(intf, msg); } +static void smi_add_watch(struct ipmi_smi *intf, unsigned int flags) +{ + unsigned long iflags; + + if (!intf->handlers->set_need_watch) + return; + + spin_lock_irqsave(&intf->watch_lock, iflags); + if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) + intf->response_waiters++; + + if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG) + intf->watchdog_waiters++; + + if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS) + intf->command_waiters++; + + if ((intf->last_watch_mask & flags) != flags) { + intf->last_watch_mask |= flags; + intf->handlers->set_need_watch(intf->send_info, + intf->last_watch_mask); + } + spin_unlock_irqrestore(&intf->watch_lock, iflags); +} + +static void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags) +{ + unsigned long iflags; + + if (!intf->handlers->set_need_watch) + return; + + spin_lock_irqsave(&intf->watch_lock, iflags); + if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) + intf->response_waiters--; + + if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG) + intf->watchdog_waiters--; + + if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS) + intf->command_waiters--; + + flags = 0; + if (intf->response_waiters) + flags |= IPMI_WATCH_MASK_CHECK_MESSAGES; + if (intf->watchdog_waiters) + flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG; + if (intf->command_waiters) + flags |= IPMI_WATCH_MASK_CHECK_COMMANDS; + + if (intf->last_watch_mask != flags) { + intf->last_watch_mask = flags; + intf->handlers->set_need_watch(intf->send_info, + intf->last_watch_mask); + } + spin_unlock_irqrestore(&intf->watch_lock, iflags); +} + /* * Find the next sequence number not being used and add the given * message with the given timeout to the sequence table. This must be @@ -981,6 +1044,7 @@ static int intf_next_seq(struct ipmi_smi *intf, *seq = i; *seqid = intf->seq_table[i].seqid; intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; + smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); need_waiter(intf); } else { rv = -EAGAIN; @@ -1019,6 +1083,7 @@ static int intf_find_seq(struct ipmi_smi *intf, && (ipmi_addr_equal(addr, &msg->addr))) { *recv_msg = msg; intf->seq_table[seq].inuse = 0; + smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); rv = 0; } } @@ -1080,6 +1145,7 @@ static int intf_err_seq(struct ipmi_smi *intf, struct seq_table *ent = &intf->seq_table[seq]; ent->inuse = 0; + smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); msg = ent->recv_msg; rv = 0; } @@ -1091,30 +1157,6 @@ static int intf_err_seq(struct ipmi_smi *intf, return rv; } -/* Must be called with xmit_msgs_lock held. */ -static void smi_tell_to_watch(struct ipmi_smi *intf, - unsigned int flags, - struct ipmi_smi_msg *smi_msg) -{ - if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES) { - if (!smi_msg) - return; - - if (!smi_msg->needs_response) - return; - } - - if (!intf->handlers->set_need_watch) - return; - - if ((intf->last_watch_mask & flags) == flags) - return; - - intf->last_watch_mask |= flags; - intf->handlers->set_need_watch(intf->send_info, - intf->last_watch_mask); -} - int ipmi_create_user(unsigned int if_num, const struct ipmi_user_hndl *handler, void *handler_data, @@ -1175,12 +1217,9 @@ int ipmi_create_user(unsigned int if_num, spin_lock_irqsave(&intf->seq_lock, flags); list_add_rcu(&new_user->link, &intf->users); spin_unlock_irqrestore(&intf->seq_lock, flags); - if (handler->ipmi_watchdog_pretimeout) { + if (handler->ipmi_watchdog_pretimeout) /* User wants pretimeouts, so make sure to watch for them. */ - if (atomic_inc_return(&intf->watchdog_waiters) == 1) - smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG, - NULL); - } + smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); srcu_read_unlock(&ipmi_interfaces_srcu, index); *user = new_user; return 0; @@ -1251,7 +1290,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) user->handler->shutdown(user->handler_data); if (user->handler->ipmi_watchdog_pretimeout) - atomic_dec(&intf->watchdog_waiters); + smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG); if (user->gets_events) atomic_dec(&intf->event_waiters); @@ -1264,6 +1303,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) if (intf->seq_table[i].inuse && (intf->seq_table[i].recv_msg->user == user)) { intf->seq_table[i].inuse = 0; + smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } } @@ -1606,8 +1646,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user, goto out_unlock; } - if (atomic_inc_return(&intf->command_waiters) == 1) - smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS, NULL); + smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); @@ -1657,7 +1696,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, synchronize_rcu(); release_ipmi_user(user, index); while (rcvrs) { - atomic_dec(&intf->command_waiters); + smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS); rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); @@ -1785,8 +1824,6 @@ static void smi_send(struct ipmi_smi *intf, spin_lock_irqsave(&intf->xmit_msgs_lock, flags); smi_msg = smi_add_send_msg(intf, smi_msg, priority); - smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES, smi_msg); - if (!run_to_completion) spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); @@ -1986,9 +2023,6 @@ static int i_ipmi_req_ipmb(struct ipmi_smi *intf, ipmb_seq, broadcast, source_address, source_lun); - /* We will be getting a response in the BMC message queue. */ - smi_msg->needs_response = true; - /* * Copy the message into the recv message data, so we * can retransmit it later if necessary. @@ -2176,7 +2210,6 @@ static int i_ipmi_request(struct ipmi_user *user, goto out; } } - smi_msg->needs_response = false; rcu_read_lock(); if (intf->in_shutdown) { @@ -3390,9 +3423,8 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, INIT_LIST_HEAD(&intf->xmit_msgs); INIT_LIST_HEAD(&intf->hp_xmit_msgs); spin_lock_init(&intf->events_lock); + spin_lock_init(&intf->watch_lock); atomic_set(&intf->event_waiters, 0); - atomic_set(&intf->watchdog_waiters, 0); - atomic_set(&intf->command_waiters, 0); intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; @@ -4408,8 +4440,6 @@ static void smi_recv_tasklet(unsigned long val) } } - smi_tell_to_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES, newmsg); - if (!run_to_completion) spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); if (newmsg) @@ -4537,7 +4567,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, struct list_head *timeouts, unsigned long timeout_period, int slot, unsigned long *flags, - unsigned int *watch_mask) + bool *need_timer) { struct ipmi_recv_msg *msg; @@ -4549,13 +4579,14 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, if (timeout_period < ent->timeout) { ent->timeout -= timeout_period; - *watch_mask |= IPMI_WATCH_MASK_CHECK_MESSAGES; + *need_timer = true; return; } if (ent->retries_left == 0) { /* The message has used all its retries. */ ent->inuse = 0; + smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES); msg = ent->recv_msg; list_add_tail(&msg->link, timeouts); if (ent->broadcast) @@ -4568,7 +4599,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, struct ipmi_smi_msg *smi_msg; /* More retries, send again. */ - *watch_mask |= IPMI_WATCH_MASK_CHECK_MESSAGES; + *need_timer = true; /* * Start with the max timer, set to normal timer after @@ -4613,20 +4644,20 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, } } -static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, - unsigned long timeout_period) +static bool ipmi_timeout_handler(struct ipmi_smi *intf, + unsigned long timeout_period) { struct list_head timeouts; struct ipmi_recv_msg *msg, *msg2; unsigned long flags; int i; - unsigned int watch_mask = 0; + bool need_timer = false; if (!intf->bmc_registered) { kref_get(&intf->refcount); if (!schedule_work(&intf->bmc_reg_work)) { kref_put(&intf->refcount, intf_free); - watch_mask |= IPMI_WATCH_MASK_INTERNAL; + need_timer = true; } } @@ -4646,7 +4677,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) check_msg_timeout(intf, &intf->seq_table[i], &timeouts, timeout_period, i, - &flags, &watch_mask); + &flags, &need_timer); spin_unlock_irqrestore(&intf->seq_lock, flags); list_for_each_entry_safe(msg, msg2, &timeouts, link) @@ -4677,7 +4708,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, tasklet_schedule(&intf->recv_tasklet); - return watch_mask; + return need_timer; } static void ipmi_request_event(struct ipmi_smi *intf) @@ -4697,9 +4728,8 @@ static atomic_t stop_operation; static void ipmi_timeout(struct timer_list *unused) { struct ipmi_smi *intf; - unsigned int watch_mask = 0; + bool need_timer = false; int index; - unsigned long flags; if (atomic_read(&stop_operation)) return; @@ -4712,28 +4742,14 @@ static void ipmi_timeout(struct timer_list *unused) ipmi_request_event(intf); intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; } - watch_mask |= IPMI_WATCH_MASK_INTERNAL; + need_timer = true; } - if (atomic_read(&intf->watchdog_waiters)) - watch_mask |= IPMI_WATCH_MASK_CHECK_WATCHDOG; - - if (atomic_read(&intf->command_waiters)) - watch_mask |= IPMI_WATCH_MASK_CHECK_COMMANDS; - - watch_mask |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); - - spin_lock_irqsave(&intf->xmit_msgs_lock, flags); - if (watch_mask != intf->last_watch_mask && - intf->handlers->set_need_watch) - intf->handlers->set_need_watch(intf->send_info, - watch_mask); - intf->last_watch_mask = watch_mask; - spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); + need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); } srcu_read_unlock(&ipmi_interfaces_srcu, index); - if (watch_mask) + if (need_timer) mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index c81c84a723b6..ae99d6a14789 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1066,7 +1066,7 @@ static void set_need_watch(void *send_info, unsigned int watch_mask) unsigned long flags; int enable; - enable = !!(watch_mask & ~IPMI_WATCH_MASK_INTERNAL); + enable = !!watch_mask; atomic_set(&smi_info->need_watch, enable); spin_lock_irqsave(&smi_info->si_lock, flags); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index a1219af32105..e4abaa8e22bc 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1129,7 +1129,7 @@ static void ssif_set_need_watch(void *send_info, unsigned int watch_mask) if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES) timeout = SSIF_WATCH_MSG_TIMEOUT; - else if (watch_mask & ~IPMI_WATCH_MASK_INTERNAL) + else if (watch_mask) timeout = SSIF_WATCH_WATCHDOG_TIMEOUT; flags = ipmi_ssif_lock_cond(ssif_info, &oflags); diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index da6abb06a5dc..4dc66157d872 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -32,14 +32,11 @@ struct ipmi_smi; /* * Flags for set_check_watch() below. Tells if the SMI should be - * waiting for watchdog timeouts, commands and/or messages. There is - * also an internal flag for the message handler, SMIs should ignore - * it. + * waiting for watchdog timeouts, commands and/or messages. */ -#define IPMI_WATCH_MASK_INTERNAL (1 << 0) -#define IPMI_WATCH_MASK_CHECK_MESSAGES (1 << 1) -#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 2) -#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 3) +#define IPMI_WATCH_MASK_CHECK_MESSAGES (1 << 0) +#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 1) +#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 2) /* * Messages to/from the lower layer. The smi interface will take one @@ -66,12 +63,6 @@ struct ipmi_smi_msg { int rsp_size; unsigned char rsp[IPMI_MAX_MSG_LENGTH]; - /* - * There should be a response message coming back in the BMC - * message queue. - */ - bool needs_response; - /* * Will be called when the system is done with the message * (presumably to free it). From d5a2197be15ef600c4878d7aaaea8bfb5a06f0d9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 30 Oct 2018 11:46:52 +0000 Subject: [PATCH 1491/4277] ipmi: clean an indentation issue, remove extraneous space Trivial fix to clean up an indentation issue, remove space Signed-off-by: Colin Ian King Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_ssif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index e4abaa8e22bc..11ac025b9a94 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1800,7 +1800,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ssif_info, &ssif_info->client->dev, slave_addr); - if (rv) { + if (rv) { pr_err("Unable to register device: error %d\n", rv); goto out_remove_attr; } From 95ac0daa0ca98ad8ac23769021144f8bf04cf406 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 16 Nov 2018 09:55:47 -0600 Subject: [PATCH 1492/4277] ipmi: Fix return value when a message is truncated The user can ask the message to be returned even if it didn't supply enough memory for the data, and it will return an error but still fills in as much data as possible. However, the return value wasn't being set to an error, it was being overwritten. Create a second return value for that case. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_devintf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index effab11887ca..99c9f581a1f3 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv, struct list_head *entry; struct ipmi_recv_msg *msg; unsigned long flags; - int rv = 0; + int rv = 0, rv2 = 0; /* We claim a mutex because we don't want two users getting something from the queue at a time. @@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv, if (msg->msg.data_len > 0) { if (rsp->msg.data_len < msg->msg.data_len) { - rv = -EMSGSIZE; + rv2 = -EMSGSIZE; if (trunc) msg->msg.data_len = rsp->msg.data_len; else @@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv, mutex_unlock(&priv->recv_mutex); ipmi_free_recv_msg(msg); - return 0; + return rv2; recv_putback_on_err: /* If we got an error, put the message back onto From 42c7c6ef1e6fa5fc0425120f06f045190b1dda2d Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Wed, 21 Nov 2018 16:08:25 +0100 Subject: [PATCH 1493/4277] ipmi: kcs_bmc: handle devm_kasprintf() failure case devm_kasprintf() may return NULL if internal allocation failed so this assignment is not safe. Moved the error exit path and added the !NULL which then allows the devres manager to take care of cleanup. Signed-off-by: Nicholas Mc Guire Fixes: cd2315d471f4 ("ipmi: kcs_bmc: don't change device name") Signed-off-by: Corey Minyard Reviewed-by: Haiyue Wang --- drivers/char/ipmi/kcs_bmc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c index e6124bd548df..ed4dc3b1843e 100644 --- a/drivers/char/ipmi/kcs_bmc.c +++ b/drivers/char/ipmi/kcs_bmc.c @@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); - if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer) - return NULL; kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u", DEVICE_NAME, channel); + if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer || + !kcs_bmc->miscdev.name) + return NULL; kcs_bmc->miscdev.fops = &kcs_bmc_fops; return kcs_bmc; From 83af41947c5cbdf33c78f4818bb0cb23da39d132 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 27 Nov 2018 18:56:04 -0600 Subject: [PATCH 1494/4277] ipmi:ssif: Change some pr_xxx to dev_xxx calls Trying to make things more consistent and compliant with what should be in drivers. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_ssif.c | 116 ++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 11ac025b9a94..8b5aec5430f1 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -28,6 +28,7 @@ */ #define pr_fmt(fmt) "ipmi_ssif: " fmt +#define dev_fmt(fmt) "ipmi_ssif: " fmt #if defined(MODVERSIONS) #include @@ -328,7 +329,8 @@ static void deliver_recv_msg(struct ssif_info *ssif_info, { if (msg->rsp_size < 0) { return_hosed_msg(ssif_info, msg); - pr_err("%s: Malformed message: rsp_size = %d\n", + dev_err(&ssif_info->client->dev, + "%s: Malformed message: rsp_size = %d\n", __func__, msg->rsp_size); } else { ipmi_smi_msg_received(ssif_info->intf, msg); @@ -545,7 +547,8 @@ static void start_get(struct ssif_info *ssif_info) if (rv < 0) { /* request failed, just return the error. */ if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Error from i2c_non_blocking_op(5)\n"); + dev_dbg(&ssif_info->client->dev, + "Error from i2c_non_blocking_op(5)\n"); msg_done_handler(ssif_info, -EIO, NULL, 0); } @@ -647,7 +650,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_inc_stat(ssif_info, receive_errors); if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Error in msg_done_handler: %d\n", result); + dev_dbg(&ssif_info->client->dev, + "%s: Error %d\n", __func__, result); len = 0; goto continue_op; } @@ -672,7 +676,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->recv, I2C_SMBUS_BLOCK_DATA); if (rv < 0) { if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Error from i2c_non_blocking_op(1)\n"); + dev_dbg(&ssif_info->client->dev, + "Error from i2c_non_blocking_op(1)\n"); result = -EIO; } else @@ -685,7 +690,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, if (len == 0) { result = -EIO; if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Middle message with no data\n"); + dev_dbg(&ssif_info->client->dev, + "Middle message with no data\n"); goto continue_op; } @@ -698,7 +704,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, /* All blocks but the last must have 31 data bytes. */ result = -EIO; if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Received middle message <31\n"); + dev_dbg(&ssif_info->client->dev, + "Received middle message <31\n"); goto continue_op; } @@ -707,7 +714,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, /* Received message too big, abort the operation. */ result = -E2BIG; if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Received message too big\n"); + dev_dbg(&ssif_info->client->dev, + "Received message too big\n"); goto continue_op; } @@ -738,7 +746,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, I2C_SMBUS_BLOCK_DATA); if (rv < 0) { if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Error from ssif_i2c_send\n"); + dev_dbg(&ssif_info->client->dev, + "Error from ssif_i2c_send\n"); result = -EIO; } else @@ -755,7 +764,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, } if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) - pr_info("DONE 1: state = %d, result=%d\n", + dev_dbg(&ssif_info->client->dev, + "DONE 1: state = %d, result=%d\n", ssif_info->ssif_state, result); flags = ipmi_ssif_lock_cond(ssif_info, &oflags); @@ -789,8 +799,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, */ ssif_info->ssif_state = SSIF_NORMAL; ipmi_ssif_unlock_cond(ssif_info, flags); - pr_warn("Error getting flags: %d %d, %x\n", - result, len, (len >= 3) ? data[2] : 0); + dev_warn(&ssif_info->client->dev, + "Error getting flags: %d %d, %x\n", + result, len, (len >= 3) ? data[2] : 0); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_GET_MSG_FLAGS_CMD) { /* @@ -798,8 +809,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, * response to a previous command. */ ipmi_ssif_unlock_cond(ssif_info, flags); - pr_warn("Invalid response getting flags: %x %x\n", - data[0], data[1]); + dev_warn(&ssif_info->client->dev, + "Invalid response getting flags: %x %x\n", + data[0], data[1]); } else { ssif_inc_stat(ssif_info, flag_fetches); ssif_info->msg_flags = data[3]; @@ -811,12 +823,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, /* We cleared the flags. */ if ((result < 0) || (len < 3) || (data[2] != 0)) { /* Error clearing flags */ - pr_warn("Error clearing flags: %d %d, %x\n", - result, len, (len >= 3) ? data[2] : 0); + dev_warn(&ssif_info->client->dev, + "Error clearing flags: %d %d, %x\n", + result, len, (len >= 3) ? data[2] : 0); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) { - pr_warn("Invalid response clearing flags: %x %x\n", - data[0], data[1]); + dev_warn(&ssif_info->client->dev, + "Invalid response clearing flags: %x %x\n", + data[0], data[1]); } ssif_info->ssif_state = SSIF_NORMAL; ipmi_ssif_unlock_cond(ssif_info, flags); @@ -832,8 +846,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, handle_flags(ssif_info, flags); } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) { - pr_warn("Invalid response getting events: %x %x\n", - msg->rsp[0], msg->rsp[1]); + dev_warn(&ssif_info->client->dev, + "Invalid response getting events: %x %x\n", + msg->rsp[0], msg->rsp[1]); msg->done(msg); /* Take off the event flag. */ ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; @@ -855,8 +870,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, handle_flags(ssif_info, flags); } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || msg->rsp[1] != IPMI_GET_MSG_CMD) { - pr_warn("Invalid response clearing flags: %x %x\n", - msg->rsp[0], msg->rsp[1]); + dev_warn(&ssif_info->client->dev, + "Invalid response clearing flags: %x %x\n", + msg->rsp[0], msg->rsp[1]); msg->done(msg); /* Take off the msg flag. */ @@ -882,7 +898,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ipmi_ssif_unlock_cond(ssif_info, flags); if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) - pr_info("DONE 2: state = %d.\n", ssif_info->ssif_state); + dev_dbg(&ssif_info->client->dev, + "DONE 2: state = %d.\n", ssif_info->ssif_state); } static void msg_written_handler(struct ssif_info *ssif_info, int result, @@ -902,7 +919,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, ssif_inc_stat(ssif_info, send_errors); if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("%s: Out of retries\n", __func__); + dev_dbg(&ssif_info->client->dev, + "%s: Out of retries\n", __func__); msg_done_handler(ssif_info, -EIO, NULL, 0); return; } @@ -914,7 +932,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, * handle it. */ if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Error in msg_written_handler: %d\n", result); + dev_dbg(&ssif_info->client->dev, + "%s: Error %d\n", __func__, result); msg_done_handler(ssif_info, result, NULL, 0); return; @@ -958,7 +977,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, ssif_inc_stat(ssif_info, send_errors); if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) - pr_info("Error from i2c_non_blocking_op(3)\n"); + dev_dbg(&ssif_info->client->dev, + "Error from i2c_non_blocking_op(3)\n"); msg_done_handler(ssif_info, -EIO, NULL, 0); } } else { @@ -1014,7 +1034,8 @@ static int start_resend(struct ssif_info *ssif_info) rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, command, ssif_info->data, I2C_SMBUS_BLOCK_DATA); if (rv && (ssif_info->ssif_debug & SSIF_DEBUG_MSG)) - pr_info("Error from i2c_non_blocking_op(4)\n"); + dev_dbg(&ssif_info->client->dev, + "Error from i2c_non_blocking_op(4)\n"); return rv; } @@ -1083,7 +1104,8 @@ static void sender(void *send_info, struct timespec64 t; ktime_get_real_ts64(&t); - pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n", + dev_dbg(&ssif_info->client->dev, + "**Enqueue %02x %02x: %lld.%6.6ld\n", msg->data[0], msg->data[1], (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC); } @@ -1614,7 +1636,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) slave_addr = find_slave_address(client, slave_addr); - pr_info("Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n", + dev_info(&client->dev, + "Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n", ipmi_addr_src_to_str(ssif_info->addr_source), client->addr, client->adapter->name, slave_addr); @@ -1629,7 +1652,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!rv && (len >= 3) && (resp[2] == 0)) { if (len < 7) { if (ssif_dbg_probe) - pr_info("SSIF info too short: %d\n", len); + dev_dbg(&ssif_info->client->dev, + "SSIF info too short: %d\n", len); goto no_support; } @@ -1666,7 +1690,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) } else { no_support: /* Assume no multi-part or PEC support */ - pr_info("Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n", + dev_info(&ssif_info->client->dev, + "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n", rv, len, resp[2]); ssif_info->max_xmit_msg_size = 32; @@ -1683,16 +1708,18 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) msg[2] = WDT_PRE_TIMEOUT_INT; rv = do_cmd(client, 3, msg, &len, resp); if (rv || (len < 3) || (resp[2] != 0)) - pr_warn("Unable to clear message flags: %d %d %2.2x\n", - rv, len, resp[2]); + dev_warn(&ssif_info->client->dev, + "Unable to clear message flags: %d %d %2.2x\n", + rv, len, resp[2]); /* Attempt to enable the event buffer. */ msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; rv = do_cmd(client, 2, msg, &len, resp); if (rv || (len < 4) || (resp[2] != 0)) { - pr_warn("Error getting global enables: %d %d %2.2x\n", - rv, len, resp[2]); + dev_warn(&ssif_info->client->dev, + "Error getting global enables: %d %d %2.2x\n", + rv, len, resp[2]); rv = 0; /* Not fatal */ goto found; } @@ -1710,8 +1737,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF; rv = do_cmd(client, 3, msg, &len, resp); if (rv || (len < 2)) { - pr_warn("Error setting global enables: %d %d %2.2x\n", - rv, len, resp[2]); + dev_warn(&ssif_info->client->dev, + "Error setting global enables: %d %d %2.2x\n", + rv, len, resp[2]); rv = 0; /* Not fatal */ goto found; } @@ -1731,8 +1759,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR; rv = do_cmd(client, 3, msg, &len, resp); if (rv || (len < 2)) { - pr_warn("Error setting global enables: %d %d %2.2x\n", - rv, len, resp[2]); + dev_warn(&ssif_info->client->dev, + "Error setting global enables: %d %d %2.2x\n", + rv, len, resp[2]); rv = 0; /* Not fatal */ goto found; } @@ -1745,8 +1774,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) found: if (ssif_dbg_probe) { - pr_info("ssif_probe: i2c_probe found device at i2c address %x\n", - client->addr); + dev_dbg(&ssif_info->client->dev, + "%s: i2c_probe found device at i2c address %x\n", + __func__, client->addr); } spin_lock_init(&ssif_info->lock); @@ -1801,7 +1831,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) &ssif_info->client->dev, slave_addr); if (rv) { - pr_err("Unable to register device: error %d\n", rv); + dev_err(&ssif_info->client->dev, + "Unable to register device: error %d\n", rv); goto out_remove_attr; } @@ -1810,7 +1841,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) if (addr_info) addr_info->client = NULL; - dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv); + dev_err(&ssif_info->client->dev, + "Unable to start IPMI SSIF: %d\n", rv); kfree(ssif_info); } kfree(resp); From 16ccdb552e70e7ae231e7934699fc217e8035d6f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Jan 2019 22:22:19 +0200 Subject: [PATCH 1495/4277] ipmi: Use defined constant for UUID representation Instead of magic number use pre-defined constant for UUID binary and string representations. While here, drop the implementation details of guid_t type. Signed-off-by: Andy Shevchenko [Also converted a "17" in the error string to UUID_SIZE + 1] Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 5bc84fbbeee5..79911914c1cb 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2749,7 +2749,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, if (!guid_set) return -ENOENT; - return snprintf(buf, 38, "%pUl\n", guid.b); + return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid); } static DEVICE_ATTR_RO(guid); @@ -3148,11 +3148,11 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) goto out; } - if (msg->msg.data_len < 17) { + if (msg->msg.data_len < UUID_SIZE + 1) { bmc->dyn_guid_set = 0; dev_warn(intf->si_dev, - "The GUID response from the BMC was too short, it was %d but should have been 17. Assuming GUID is not available.\n", - msg->msg.data_len); + "The GUID response from the BMC was too short, it was %d but should have been %d. Assuming GUID is not available.\n", + msg->msg.data_len, UUID_SIZE + 1); goto out; } From f32043901a389ee8b5417c0cec547a84a2f9b38a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Jan 2019 22:22:20 +0200 Subject: [PATCH 1496/4277] ipmi: Use dedicated API for copying a UUID Use guid_copy() instead of memcpy() to hide guid_t implementation details and to show we expect guid_t in a raw buffer. Signed-off-by: Andy Shevchenko Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 79911914c1cb..e8ba67834746 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3156,7 +3156,7 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) goto out; } - memcpy(bmc->fetch_guid.b, msg->msg.data + 1, 16); + guid_copy(&bmc->fetch_guid, (guid_t *)(msg->msg.data + 1)); /* * Make sure the guid data is available before setting * dyn_guid_set. From 8d1e02d11e3d6ebffca26b29588cdbbe10b2ad39 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Sun, 10 Feb 2019 23:19:01 +0800 Subject: [PATCH 1497/4277] libata: fix a typo in comment poitner -> pointer. Signed-off-by: Chengguang Xu Signed-off-by: Jens Axboe --- drivers/ata/libata-scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 3d4887d0e84a..c2adfd8486c4 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2990,7 +2990,7 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) * This inconsistency confuses several controllers which * perform PIO using DMA such as Intel AHCIs and sil3124/32. * These controllers use actual number of transferred bytes to - * update DMA poitner and transfer of 4n+2 bytes make those + * update DMA pointer and transfer of 4n+2 bytes make those * controller push DMA pointer by 4n+4 bytes because SATA data * FISes are aligned to 4 bytes. This causes data corruption * and buffer overrun. From 7585d5082e17a6988a784010c1fbbc4055edcdb7 Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Fri, 25 Jan 2019 00:01:42 -0200 Subject: [PATCH 1498/4277] blk-cgroup: Fix doc related to blkcg_exit_queue Since 4cf6324b17e9, a portion of function blk_cleanup_queue was moved to a newly created function called blk_exit_queue, including the call of blkcg_exit_queue. So, adjust the documenation according. Reviewed-by: Bart Van Assche Signed-off-by: Marcos Paulo de Souza Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 2bed5725aa03..77f37ef8ef06 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1269,7 +1269,7 @@ void blkcg_drain_queue(struct request_queue *q) * blkcg_exit_queue - exit and release blkcg part of request_queue * @q: request_queue being released * - * Called from blk_release_queue(). Responsible for exiting blkcg part. + * Called from blk_exit_queue(). Responsible for exiting blkcg part. */ void blkcg_exit_queue(struct request_queue *q) { From 1e9364283764ac93b012739890a30d73e76396db Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Sun, 10 Feb 2019 15:22:51 -0200 Subject: [PATCH 1499/4277] blk-sysfs: Rework documention of __blk_release_queue The Notes section of the comment was removed, because now blk_release_queue can only be executed from blk_cleanup_queue (being called when the q->kobj reaches zero), and also blk_init_queue was removed in a1ce35fa4985. Signed-off-by: Marcos Paulo de Souza Signed-off-by: Jens Axboe --- block/blk-sysfs.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 590d1ef2f961..94e1b052abbc 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -817,21 +817,16 @@ static void blk_free_queue_rcu(struct rcu_head *rcu_head) } /** - * __blk_release_queue - release a request queue when it is no longer needed + * __blk_release_queue - release a request queue * @work: pointer to the release_work member of the request queue to be released * * Description: - * blk_release_queue is the counterpart of blk_init_queue(). It should be - * called when a request queue is being released; typically when a block - * device is being de-registered. Its primary task it to free the queue - * itself. - * - * Notes: - * The low level driver must have finished any outstanding requests first - * via blk_cleanup_queue(). - * - * Although blk_release_queue() may be called with preemption disabled, - * __blk_release_queue() may sleep. + * This function is called when a block device is being unregistered. The + * process of releasing a request queue starts with blk_cleanup_queue, which + * set the appropriate flags and then calls blk_put_queue, that decrements + * the reference counter of the request queue. Once the reference counter + * of the request queue reaches zero, blk_release_queue is called to release + * all allocated resources of the request queue. */ static void __blk_release_queue(struct work_struct *work) { From 82dd124c40b8cda710878b88fb0182301c040ffe Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Sun, 10 Feb 2019 23:04:16 -0500 Subject: [PATCH 1500/4277] ext4: replace opencoded i_writecount usage with inode_is_open_for_write() There is a function which clearly conveys the objective of checking i_writecount. Additionally the usage in ext4_mb_initialize_context was wrong, since a node would have wrongfully been reported as writable if i_writecount had a negative value (MMAP_DENY_WRITE). Signed-off-by: Nikolay Borisov Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara --- fs/ext4/inode.c | 2 +- fs/ext4/mballoc.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 34d7e0703cc6..213d1857a7cf 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -391,7 +391,7 @@ void ext4_da_update_reserve_space(struct inode *inode, * inode's preallocations. */ if ((ei->i_reserved_data_blocks == 0) && - (atomic_read(&inode->i_writecount) == 0)) + !inode_is_open_for_write(inode)) ext4_discard_preallocations(inode); } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index e2248083cdca..6fb76d408093 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4176,9 +4176,8 @@ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac) isize = (i_size_read(ac->ac_inode) + ac->ac_sb->s_blocksize - 1) >> bsbits; - if ((size == isize) && - !ext4_fs_is_busy(sbi) && - (atomic_read(&ac->ac_inode->i_writecount) == 0)) { + if ((size == isize) && !ext4_fs_is_busy(sbi) && + !inode_is_open_for_write(ac->ac_inode)) { ac->ac_flags |= EXT4_MB_HINT_NOPREALLOC; return; } @@ -4258,7 +4257,7 @@ ext4_mb_initialize_context(struct ext4_allocation_context *ac, (unsigned) ar->goal, ac->ac_flags, ac->ac_2order, (unsigned) ar->lleft, (unsigned) ar->pleft, (unsigned) ar->lright, (unsigned) ar->pright, - atomic_read(&ar->inode->i_writecount) ? "" : "non-"); + inode_is_open_for_write(ar->inode) ? "" : "non-"); return 0; } From 904cdbd41d749a476863a0ca41f6f396774f26e4 Mon Sep 17 00:00:00 2001 From: "zhangyi (F)" Date: Sun, 10 Feb 2019 23:23:04 -0500 Subject: [PATCH 1501/4277] jbd2: clear dirty flag when revoking a buffer from an older transaction Now, we capture a data corruption problem on ext4 while we're truncating an extent index block. Imaging that if we are revoking a buffer which has been journaled by the committing transaction, the buffer's jbddirty flag will not be cleared in jbd2_journal_forget(), so the commit code will set the buffer dirty flag again after refile the buffer. fsx kjournald2 jbd2_journal_commit_transaction jbd2_journal_revoke commit phase 1~5... jbd2_journal_forget belongs to older transaction commit phase 6 jbddirty not clear __jbd2_journal_refile_buffer __jbd2_journal_unfile_buffer test_clear_buffer_jbddirty mark_buffer_dirty Finally, if the freed extent index block was allocated again as data block by some other files, it may corrupt the file data after writing cached pages later, such as during unmount time. (In general, clean_bdev_aliases() related helpers should be invoked after re-allocation to prevent the above corruption, but unfortunately we missed it when zeroout the head of extra extent blocks in ext4_ext_handle_unwritten_extents()). This patch mark buffer as freed and set j_next_transaction to the new transaction when it already belongs to the committing transaction in jbd2_journal_forget(), so that commit code knows it should clear dirty bits when it is done with the buffer. This problem can be reproduced by xfstests generic/455 easily with seeds (3246 3247 3248 3249). Signed-off-by: zhangyi (F) Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara Cc: stable@vger.kernel.org --- fs/jbd2/transaction.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index cc35537232f2..6f4dff182c91 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1609,14 +1609,21 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) /* However, if the buffer is still owned by a prior * (committing) transaction, we can't drop it yet... */ JBUFFER_TRACE(jh, "belongs to older transaction"); - /* ... but we CAN drop it from the new transaction if we - * have also modified it since the original commit. */ + /* ... but we CAN drop it from the new transaction through + * marking the buffer as freed and set j_next_transaction to + * the new transaction, so that not only the commit code + * knows it should clear dirty bits when it is done with the + * buffer, but also the buffer can be checkpointed only + * after the new transaction commits. */ - if (jh->b_next_transaction) { - J_ASSERT(jh->b_next_transaction == transaction); + set_buffer_freed(bh); + + if (!jh->b_next_transaction) { spin_lock(&journal->j_list_lock); - jh->b_next_transaction = NULL; + jh->b_next_transaction = transaction; spin_unlock(&journal->j_list_lock); + } else { + J_ASSERT(jh->b_next_transaction == transaction); /* * only drop a reference if this transaction modified From 597599268e3b91cac71faf48743f4783dec682fc Mon Sep 17 00:00:00 2001 From: "zhangyi (F)" Date: Sun, 10 Feb 2019 23:26:06 -0500 Subject: [PATCH 1502/4277] jbd2: discard dirty data when forgetting an un-journalled buffer We do not unmap and clear dirty flag when forgetting a buffer without journal or does not belongs to any transaction, so the invalid dirty data may still be written to the disk later. It's fine if the corresponding block is never used before the next mount, and it's also fine that we invoke clean_bdev_aliases() related functions to unmap the block device mapping when re-allocating such freed block as data block. But this logic is somewhat fragile and risky that may lead to data corruption if we forget to clean bdev aliases. So, It's better to discard dirty data during forget time. We have been already handled all the cases of forgetting journalled buffer, this patch deal with the remaining two cases. - buffer is not journalled yet, - buffer is journalled but doesn't belongs to any transaction. We invoke __bforget() instead of __brelese() when forgetting an un-journalled buffer in jbd2_journal_forget(). After this patch we can remove all clean_bdev_aliases() related calls in ext4. Suggested-by: Jan Kara Signed-off-by: zhangyi (F) Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara --- fs/jbd2/transaction.c | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 6f4dff182c91..135f0a10f557 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1597,9 +1597,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) __jbd2_journal_unfile_buffer(jh); if (!buffer_jbd(bh)) { spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); - __bforget(bh); - goto drop; + goto not_jbd; } } spin_unlock(&journal->j_list_lock); @@ -1632,9 +1630,40 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) if (was_modified) drop_reserve = 1; } + } else { + /* + * Finally, if the buffer is not belongs to any + * transaction, we can just drop it now if it has no + * checkpoint. + */ + spin_lock(&journal->j_list_lock); + if (!jh->b_cp_transaction) { + JBUFFER_TRACE(jh, "belongs to none transaction"); + spin_unlock(&journal->j_list_lock); + goto not_jbd; + } + + /* + * Otherwise, if the buffer has been written to disk, + * it is safe to remove the checkpoint and drop it. + */ + if (!buffer_dirty(bh)) { + __jbd2_journal_remove_checkpoint(jh); + spin_unlock(&journal->j_list_lock); + goto not_jbd; + } + + /* + * The buffer is still not written to disk, we should + * attach this buffer to current transaction so that the + * buffer can be checkpointed only after the current + * transaction commits. + */ + clear_buffer_dirty(bh); + __jbd2_journal_file_buffer(jh, transaction, BJ_Forget); + spin_unlock(&journal->j_list_lock); } -not_jbd: jbd_unlock_bh_state(bh); __brelse(bh); drop: @@ -1643,6 +1672,11 @@ drop: handle->h_buffer_credits++; } return err; + +not_jbd: + jbd_unlock_bh_state(bh); + __bforget(bh); + goto drop; } /** From 16e08b14a4550bb167c48e918b26ef0d4980e9d1 Mon Sep 17 00:00:00 2001 From: "zhangyi (F)" Date: Sun, 10 Feb 2019 23:32:07 -0500 Subject: [PATCH 1503/4277] ext4: cleanup clean_bdev_aliases() calls Now, we have already handle all cases of forgetting buffer in jbd2_journal_forget(), the buffer should not be mapped to blockdevice when reallocating it. So this patch remove all clean_bdev_aliases() and clean_bdev_bh_alias() calls which were invoked by ext4 explicitly. Suggested-by: Jan Kara Signed-off-by: zhangyi (F) Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara --- fs/ext4/extents.c | 12 +----------- fs/ext4/inode.c | 7 ------- fs/ext4/page-io.c | 4 +--- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 240b6dea5441..82e239a176c7 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4048,18 +4048,8 @@ out: } else allocated = ret; map->m_flags |= EXT4_MAP_NEW; - /* - * if we allocated more blocks than requested - * we need to make sure we unmap the extra block - * allocated. The actual needed block will get - * unmapped later when we find the buffer_head marked - * new. - */ - if (allocated > map->m_len) { - clean_bdev_aliases(inode->i_sb->s_bdev, newblock + map->m_len, - allocated - map->m_len); + if (allocated > map->m_len) allocated = map->m_len; - } map->m_len = allocated; map_out: diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 213d1857a7cf..dd4641f3cdb9 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -678,8 +678,6 @@ found: if (flags & EXT4_GET_BLOCKS_ZERO && map->m_flags & EXT4_MAP_MAPPED && map->m_flags & EXT4_MAP_NEW) { - clean_bdev_aliases(inode->i_sb->s_bdev, map->m_pblk, - map->m_len); ret = ext4_issue_zeroout(inode, map->m_lblk, map->m_pblk, map->m_len); if (ret) { @@ -1194,7 +1192,6 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, if (err) break; if (buffer_new(bh)) { - clean_bdev_bh_alias(bh); if (PageUptodate(page)) { clear_buffer_new(bh); set_buffer_uptodate(bh); @@ -2490,10 +2487,6 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd) } BUG_ON(map->m_len == 0); - if (map->m_flags & EXT4_MAP_NEW) { - clean_bdev_aliases(inode->i_sb->s_bdev, map->m_pblk, - map->m_len); - } return 0; } diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 2aa62d58d8dd..15599466809b 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -467,10 +467,8 @@ int ext4_bio_write_page(struct ext4_io_submit *io, ext4_io_submit(io); continue; } - if (buffer_new(bh)) { + if (buffer_new(bh)) clear_buffer_new(bh); - clean_bdev_bh_alias(bh); - } set_buffer_async_write(bh); nr_to_submit++; } while ((bh = bh->b_this_page) != head); From a297b2fcee461e40df763e179cbbfba5a9e572d2 Mon Sep 17 00:00:00 2001 From: Xiaoguang Wang Date: Sun, 10 Feb 2019 23:53:21 -0500 Subject: [PATCH 1504/4277] ext4: unlock unused_pages timely when doing writeback In mpage_add_bh_to_extent(), when accumulated extents length is greater than MAX_WRITEPAGES_EXTENT_LEN or buffer head's b_stat is not equal, we will not continue to search unmapped area for this page, but note this page is locked, and will only be unlocked in mpage_release_unused_pages() after ext4_io_submit, if io also is throttled by blk-throttle or similar io qos, we will hold this page locked for a while, it's unnecessary. I think the best fix is to refactor mpage_add_bh_to_extent() to let it return some hints whether to unlock this page, but given that we will improve dioread_nolock later, we can let it done later, so currently the simple fix would just call mpage_release_unused_pages() before ext4_io_submit(). Signed-off-by: Xiaoguang Wang Signed-off-by: Theodore Ts'o --- fs/ext4/inode.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index dd4641f3cdb9..9c3402ec85bc 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2829,12 +2829,12 @@ retry: goto unplug; } ret = mpage_prepare_extent_to_map(&mpd); + /* Unlock pages we didn't use */ + mpage_release_unused_pages(&mpd, false); /* Submit prepared bio */ ext4_io_submit(&mpd.io_submit); ext4_put_io_end_defer(mpd.io_submit.io_end); mpd.io_submit.io_end = NULL; - /* Unlock pages we didn't use */ - mpage_release_unused_pages(&mpd, false); if (ret < 0) goto unplug; @@ -2902,10 +2902,11 @@ retry: handle = NULL; mpd.do_map = 0; } - /* Submit prepared bio */ - ext4_io_submit(&mpd.io_submit); /* Unlock pages we didn't use */ mpage_release_unused_pages(&mpd, give_up_on_write); + /* Submit prepared bio */ + ext4_io_submit(&mpd.io_submit); + /* * Drop our io_end reference we got from init. We have * to be careful and use deferred io_end finishing if From 67a11611e1a5211f6569044fbf8150875764d1d0 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Mon, 11 Feb 2019 00:02:05 -0500 Subject: [PATCH 1505/4277] ext4: fix check of inode in swap_inode_boot_loader Before really do swap between inode and boot inode, something need to check to avoid invalid or not permitted operation, like does this inode has inline data. But the condition check should be protected by inode lock to avoid change while swapping. Also some other condition will not change between swapping, but there has no problem to do this under inode lock. Signed-off-by: yangerkun Signed-off-by: Theodore Ts'o Cc: stable@kernel.org --- fs/ext4/ioctl.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index d37dafa1d133..597e8b617f92 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -116,15 +116,6 @@ static long swap_inode_boot_loader(struct super_block *sb, struct inode *inode_bl; struct ext4_inode_info *ei_bl; - if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) || - IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) || - ext4_has_inline_data(inode)) - return -EINVAL; - - if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) || - !inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) - return -EPERM; - inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO, EXT4_IGET_SPECIAL); if (IS_ERR(inode_bl)) return PTR_ERR(inode_bl); @@ -137,6 +128,19 @@ static long swap_inode_boot_loader(struct super_block *sb, * that only 1 swap_inode_boot_loader is running. */ lock_two_nondirectories(inode, inode_bl); + if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) || + IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) || + ext4_has_inline_data(inode)) { + err = -EINVAL; + goto journal_err_out; + } + + if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) || + !inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto journal_err_out; + } + /* Wait for all existing dio workers */ inode_dio_wait(inode); inode_dio_wait(inode_bl); From a46c68a318b08f819047843abf349aeee5d10ac2 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Mon, 11 Feb 2019 00:05:24 -0500 Subject: [PATCH 1506/4277] ext4: cleanup pagecache before swap i_data While do swap, we should make sure there has no new dirty page since we should swap i_data between two inode: 1.We should lock i_mmap_sem with write to avoid new pagecache from mmap read/write; 2.Change filemap_flush to filemap_write_and_wait and move them to the space protected by inode lock to avoid new pagecache from buffer read/write. Signed-off-by: yangerkun Signed-off-by: Theodore Ts'o Cc: stable@kernel.org --- fs/ext4/ioctl.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 597e8b617f92..ea05e8d641e9 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -121,9 +121,6 @@ static long swap_inode_boot_loader(struct super_block *sb, return PTR_ERR(inode_bl); ei_bl = EXT4_I(inode_bl); - filemap_flush(inode->i_mapping); - filemap_flush(inode_bl->i_mapping); - /* Protect orig inodes against a truncate and make sure, * that only 1 swap_inode_boot_loader is running. */ lock_two_nondirectories(inode, inode_bl); @@ -141,6 +138,15 @@ static long swap_inode_boot_loader(struct super_block *sb, goto journal_err_out; } + down_write(&EXT4_I(inode)->i_mmap_sem); + err = filemap_write_and_wait(inode->i_mapping); + if (err) + goto err_out; + + err = filemap_write_and_wait(inode_bl->i_mapping); + if (err) + goto err_out; + /* Wait for all existing dio workers */ inode_dio_wait(inode); inode_dio_wait(inode_bl); @@ -151,7 +157,7 @@ static long swap_inode_boot_loader(struct super_block *sb, handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2); if (IS_ERR(handle)) { err = -EINVAL; - goto journal_err_out; + goto err_out; } /* Protect extent tree against block allocations via delalloc */ @@ -208,6 +214,8 @@ static long swap_inode_boot_loader(struct super_block *sb, ext4_journal_stop(handle); ext4_double_up_write_data_sem(inode, inode_bl); +err_out: + up_write(&EXT4_I(inode)->i_mmap_sem); journal_err_out: unlock_two_nondirectories(inode, inode_bl); iput(inode_bl); From aa507b5faf38784defe49f5e64605ac3c4425e26 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Mon, 11 Feb 2019 00:14:02 -0500 Subject: [PATCH 1507/4277] ext4: update quota information while swapping boot loader inode While do swap between two inode, they swap i_data without update quota information. Also, swap_inode_boot_loader can do "revert" somtimes, so update the quota while all operations has been finished. Signed-off-by: yangerkun Signed-off-by: Theodore Ts'o Cc: stable@kernel.org --- fs/ext4/ioctl.c | 56 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index ea05e8d641e9..eff68358fae7 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -68,8 +68,6 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2) ei2 = EXT4_I(inode2); swap(inode1->i_version, inode2->i_version); - swap(inode1->i_blocks, inode2->i_blocks); - swap(inode1->i_bytes, inode2->i_bytes); swap(inode1->i_atime, inode2->i_atime); swap(inode1->i_mtime, inode2->i_mtime); @@ -115,6 +113,9 @@ static long swap_inode_boot_loader(struct super_block *sb, int err; struct inode *inode_bl; struct ext4_inode_info *ei_bl; + qsize_t size, size_bl, diff; + blkcnt_t blocks; + unsigned short bytes; inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO, EXT4_IGET_SPECIAL); if (IS_ERR(inode_bl)) @@ -180,6 +181,13 @@ static long swap_inode_boot_loader(struct super_block *sb, memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data)); } + err = dquot_initialize(inode); + if (err) + goto err_out1; + + size = (qsize_t)(inode->i_blocks) * (1 << 9) + inode->i_bytes; + size_bl = (qsize_t)(inode_bl->i_blocks) * (1 << 9) + inode_bl->i_bytes; + diff = size - size_bl; swap_inode_data(inode, inode_bl); inode->i_ctime = inode_bl->i_ctime = current_time(inode); @@ -193,24 +201,46 @@ static long swap_inode_boot_loader(struct super_block *sb, err = ext4_mark_inode_dirty(handle, inode); if (err < 0) { + /* No need to update quota information. */ ext4_warning(inode->i_sb, "couldn't mark inode #%lu dirty (err %d)", inode->i_ino, err); /* Revert all changes: */ swap_inode_data(inode, inode_bl); ext4_mark_inode_dirty(handle, inode); - } else { - err = ext4_mark_inode_dirty(handle, inode_bl); - if (err < 0) { - ext4_warning(inode_bl->i_sb, - "couldn't mark inode #%lu dirty (err %d)", - inode_bl->i_ino, err); - /* Revert all changes: */ - swap_inode_data(inode, inode_bl); - ext4_mark_inode_dirty(handle, inode); - ext4_mark_inode_dirty(handle, inode_bl); - } + goto err_out1; } + + blocks = inode_bl->i_blocks; + bytes = inode_bl->i_bytes; + inode_bl->i_blocks = inode->i_blocks; + inode_bl->i_bytes = inode->i_bytes; + err = ext4_mark_inode_dirty(handle, inode_bl); + if (err < 0) { + /* No need to update quota information. */ + ext4_warning(inode_bl->i_sb, + "couldn't mark inode #%lu dirty (err %d)", + inode_bl->i_ino, err); + goto revert; + } + + /* Bootloader inode should not be counted into quota information. */ + if (diff > 0) + dquot_free_space(inode, diff); + else + err = dquot_alloc_space(inode, -1 * diff); + + if (err < 0) { +revert: + /* Revert all changes: */ + inode_bl->i_blocks = blocks; + inode_bl->i_bytes = bytes; + swap_inode_data(inode, inode_bl); + ext4_mark_inode_dirty(handle, inode); + ext4_mark_inode_dirty(handle, inode_bl); + } + +err_out1: ext4_journal_stop(handle); ext4_double_up_write_data_sem(inode, inode_bl); From abdc644e8cbac2e9b19763680e5a7cf9bab2bee7 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Mon, 11 Feb 2019 00:35:06 -0500 Subject: [PATCH 1508/4277] ext4: add mask of ext4 flags to swap The reason is that while swapping two inode, we swap the flags too. Some flags such as EXT4_JOURNAL_DATA_FL can really confuse the things since we're not resetting the address operations structure. The simplest way to keep things sane is to restrict the flags that can be swapped. Signed-off-by: yangerkun Signed-off-by: Theodore Ts'o Cc: stable@vger.kernel.org --- fs/ext4/ext4.h | 3 +++ fs/ext4/ioctl.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 185a05d3257e..508a37ec9271 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -426,6 +426,9 @@ struct flex_groups { /* Flags that are appropriate for non-directories/regular files. */ #define EXT4_OTHER_FLMASK (EXT4_NODUMP_FL | EXT4_NOATIME_FL) +/* The only flags that should be swapped */ +#define EXT4_FL_SHOULD_SWAP (EXT4_HUGE_FILE_FL | EXT4_EXTENTS_FL) + /* Mask out flags that are inappropriate for the given type of inode. */ static inline __u32 ext4_mask_flags(umode_t mode, __u32 flags) { diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index eff68358fae7..2e76fb55d94a 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -63,6 +63,7 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2) loff_t isize; struct ext4_inode_info *ei1; struct ext4_inode_info *ei2; + unsigned long tmp; ei1 = EXT4_I(inode1); ei2 = EXT4_I(inode2); @@ -72,7 +73,10 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2) swap(inode1->i_mtime, inode2->i_mtime); memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data)); - swap(ei1->i_flags, ei2->i_flags); + tmp = ei1->i_flags & EXT4_FL_SHOULD_SWAP; + ei1->i_flags = (ei2->i_flags & EXT4_FL_SHOULD_SWAP) | + (ei1->i_flags & ~EXT4_FL_SHOULD_SWAP); + ei2->i_flags = tmp | (ei2->i_flags & ~EXT4_FL_SHOULD_SWAP); swap(ei1->i_disksize, ei2->i_disksize); ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS); ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS); From 6e589291f4b1b700ca12baec5930592a0d51e63c Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 11 Feb 2019 01:07:10 -0500 Subject: [PATCH 1509/4277] ext4: disallow files with EXT4_JOURNAL_DATA_FL from EXT4_IOC_SWAP_BOOT A malicious/clueless root user can use EXT4_IOC_SWAP_BOOT to force a corner casew which can lead to the file system getting corrupted. There's no usefulness to allowing this, so just prohibit this case. Signed-off-by: Theodore Ts'o --- fs/ext4/ioctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 2e76fb55d94a..eb8ca8d80885 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -132,6 +132,7 @@ static long swap_inode_boot_loader(struct super_block *sb, if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) || IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) || + (EXT4_I(inode)->i_flags & EXT4_JOURNAL_DATA_FL) || ext4_has_inline_data(inode)) { err = -EINVAL; goto journal_err_out; From a44aec0b4a63725c2601db4257d204a757198817 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 10 Feb 2019 22:28:24 -0800 Subject: [PATCH 1510/4277] Documenation: driver-api: fix gpio/board.rst warning Fix markup warning: insert a blank line before the list. Documentation/driver-api/gpio/board.rst:209: WARNING: Unexpected indentation. Signed-off-by: Randy Dunlap Cc: Linus Walleij Cc: Bartosz Golaszewski Cc: linux-gpio@vger.kernel.org Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/board.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index a0f294e2e250..b37f3f7b8926 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -204,6 +204,7 @@ between a caller and a respective .get/set_multiple() callback of a GPIO chip. In order to qualify for fast bitmap processing, the array must meet the following requirements: + - pin hardware number of array member 0 must also be 0, - pin hardware numbers of consecutive array members which belong to the same chip as member 0 does must also match their array indexes. From 402bfb3c135213dc9e6822cbc04d206d2c450894 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 6 Feb 2019 11:32:31 +0800 Subject: [PATCH 1511/4277] pinctrl: sunxi: Support I/O bias voltage setting on A80 The A80 SoC has configuration registers for I/O bias voltage. Incorrect settings would make the affected peripherals inoperable in some cases, such as Ethernet RGMII signals biased at 2.5V with the settings still at 3.3V. However low speed signals such as MDIO on the same group of pins seem to be unaffected. Previously there was no way to know what the actual voltage used was, short of hard-coding a value in the device tree. With the new pin bank regulator supply support in place, the driver can now query the regulator for its voltage, and if it's valid (as opposed to being the dummy regulator), set the bias voltage setting accordingly. Add a quirk to denote the presence of the configuration registers, and a function to set the correct setting based on the voltage read back from the regulator. This is only done when the regulator is first acquired and enabled. While it would be nice to have a notifier on the regulator so that when the voltage changes, the driver can update the setting, in practice no board currently supports dynamic changing of the I/O voltages. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c | 1 + drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c | 1 + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 41 +++++++++++++++++++++ drivers/pinctrl/sunxi/pinctrl-sunxi.h | 12 ++++++ 4 files changed, 55 insertions(+) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c index c63086c98335..e05dd9a5551d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c @@ -153,6 +153,7 @@ static const struct sunxi_pinctrl_desc sun9i_a80_r_pinctrl_data = { .pin_base = PL_BASE, .irq_banks = 2, .disable_strict_mode = true, + .has_io_bias_cfg = true, }; static int sun9i_a80_r_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c index 5553c0eb0f41..da37d594a13d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c @@ -722,6 +722,7 @@ static const struct sunxi_pinctrl_desc sun9i_a80_pinctrl_data = { .npins = ARRAY_SIZE(sun9i_a80_pins), .irq_banks = 5, .disable_strict_mode = true, + .has_io_bias_cfg = true, }; static int sun9i_a80_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 0e7fa69e93df..8dd25caea2cf 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -603,6 +603,45 @@ static const struct pinconf_ops sunxi_pconf_ops = { .pin_config_group_set = sunxi_pconf_group_set, }; +static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, + unsigned pin, + struct regulator *supply) +{ + u32 val, reg; + int uV; + + if (!pctl->desc->has_io_bias_cfg) + return 0; + + uV = regulator_get_voltage(supply); + if (uV < 0) + return uV; + + /* Might be dummy regulator with no voltage set */ + if (uV == 0) + return 0; + + /* Configured value must be equal or greater to actual voltage */ + if (uV <= 1800000) + val = 0x0; /* 1.8V */ + else if (uV <= 2500000) + val = 0x6; /* 2.5V */ + else if (uV <= 2800000) + val = 0x9; /* 2.8V */ + else if (uV <= 3000000) + val = 0xA; /* 3.0V */ + else + val = 0xD; /* 3.3V */ + + pin -= pctl->desc->pin_base; + + reg = readl(pctl->membase + sunxi_grp_config_reg(pin)); + reg &= ~IO_BIAS_MASK; + writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin)); + + return 0; +} + static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -725,6 +764,8 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset) goto out; } + sunxi_pinctrl_set_io_bias_cfg(pctl, offset, reg); + s_reg->regulator = reg; refcount_set(&s_reg->refcount, 1); diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index 034c0317c8d6..ee15ab067b5f 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -79,6 +79,10 @@ #define IRQ_LEVEL_LOW 0x03 #define IRQ_EDGE_BOTH 0x04 +#define GRP_CFG_REG 0x300 + +#define IO_BIAS_MASK GENMASK(3, 0) + #define SUN4I_FUNC_INPUT 0 #define SUN4I_FUNC_IRQ 6 @@ -113,6 +117,7 @@ struct sunxi_pinctrl_desc { const unsigned int *irq_bank_map; bool irq_read_needs_mux; bool disable_strict_mode; + bool has_io_bias_cfg; }; struct sunxi_pinctrl_function { @@ -338,6 +343,13 @@ static inline u32 sunxi_irq_status_offset(u16 irq) return irq_num * IRQ_STATUS_IRQ_BITS; } +static inline u32 sunxi_grp_config_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + + return GRP_CFG_REG + bank * 0x4; +} + int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc, unsigned long variant); From 09832dc0594ee264b658b7239d3d963b111e8faf Mon Sep 17 00:00:00 2001 From: "Oliver.Rohe@wago.com" Date: Fri, 8 Feb 2019 13:20:44 +0000 Subject: [PATCH 1512/4277] rtc: rs5c372: Fix reading from rtc when the oscillator got interrupted. When the oscillator of the rtc gets interrupted, e.g. due to an empty battery, reading from the rtc will now return an error and the oscillator bit will be cleared, once the rtc is successfully reset. Signed-off-by: Oliver Rohe Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rs5c372.c | 54 ++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index ff35dcef338b..66a473a3c3fe 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -52,8 +52,10 @@ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 # define RS5C372_CTRL2_24 (1 << 5) -# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ # define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ +# define R2x2x_CTRL2_VDET (1 << 6) /* only if R2x2x */ +# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ +# define R2x2x_CTRL2_PON (1 << 4) /* only if R2x2x */ # define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ @@ -212,10 +214,27 @@ static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); int status = rs5c_get_regs(rs5c); + unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2]; if (status < 0) return status; + switch (rs5c->type) { + case rtc_r2025sd: + case rtc_r2221tl: + if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) || + (rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) { + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); + return -EINVAL; + } + break; + default: + if (ctrl2 & RS5C_CTRL2_XSTP) { + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); + return -EINVAL; + } + } + tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f); tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f); tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]); @@ -243,6 +262,7 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); unsigned char buf[7]; + unsigned char ctrl2; int addr; dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " @@ -261,7 +281,32 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) buf[6] = bin2bcd(tm->tm_year - 100); if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { - dev_err(&client->dev, "%s: write error\n", __func__); + dev_dbg(&client->dev, "%s: write error in line %i\n", + __func__, __LINE__); + return -EIO; + } + + addr = RS5C_ADDR(RS5C_REG_CTRL2); + ctrl2 = i2c_smbus_read_byte_data(client, addr); + + /* clear rtc warning bits */ + switch (rs5c->type) { + case rtc_r2025sd: + case rtc_r2221tl: + ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON); + if (rs5c->type == rtc_r2025sd) + ctrl2 |= R2x2x_CTRL2_XSTP; + else + ctrl2 &= ~R2x2x_CTRL2_XSTP; + break; + default: + ctrl2 &= ~RS5C_CTRL2_XSTP; + break; + } + + if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) { + dev_dbg(&client->dev, "%s: write error in line %i\n", + __func__, __LINE__); return -EIO; } @@ -523,23 +568,18 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; - /* handle xstp bit */ switch (rs5c372->type) { case rtc_r2025sd: if (buf[1] & R2x2x_CTRL2_XSTP) return ret; - rs5c372->regs[RS5C_REG_CTRL2] |= R2x2x_CTRL2_XSTP; break; case rtc_r2221tl: if (!(buf[1] & R2x2x_CTRL2_XSTP)) return ret; - rs5c372->regs[RS5C_REG_CTRL2] &= ~R2x2x_CTRL2_XSTP; break; - default: if (!(buf[1] & RS5C_CTRL2_XSTP)) return ret; - rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; break; } From 1366e010ade86bcc8226fbdd9c0cb71cfd2aa173 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 9 Feb 2019 01:18:12 +0100 Subject: [PATCH 1513/4277] dt-bindings: rtc: add device-tree bindings for the Amlogic Meson RTC The 32-bit Amlogic Meson SoCs (Meson6, Meson8, Meson8b and Meson8m2) have a built-in RTC block. It has the following inputs: - an 32.768kHz crystal oscillator - an interrupt line - a reset line - 0.9V voltage input Signed-off-by: Ben Dooks [resurrected patches from Ben after 2 years] Signed-off-by: Martin Blumenstingl Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/rtc-meson.txt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/rtc-meson.txt diff --git a/Documentation/devicetree/bindings/rtc/rtc-meson.txt b/Documentation/devicetree/bindings/rtc/rtc-meson.txt new file mode 100644 index 000000000000..e921fe66a362 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-meson.txt @@ -0,0 +1,35 @@ +* Amlogic Meson6, Meson8, Meson8b and Meson8m2 RTC + +Required properties: +- compatible: should be one of the following describing the hardware: + * "amlogic,meson6-rtc" + * "amlogic,meson8-rtc" + * "amlogic,meson8b-rtc" + * "amlogic,meson8m2-rtc" + +- reg: physical register space for the controller's memory mapped registers. +- interrupts: the interrupt line of the RTC block. +- clocks: reference to the external 32.768kHz crystal oscillator. +- vdd-supply: reference to the power supply of the RTC block. +- resets: reset controller reference to allow reset of the controller + +Optional properties for the battery-backed non-volatile memory: +- #address-cells: should be 1 to address the battery-backed non-volatile memory +- #size-cells: should be 1 to reference the battery-backed non-volatile memory + +Optional child nodes: +- see ../nvmem/nvmem.txt + +Example: + + rtc: rtc@740 { + compatible = "amlogic,meson6-rtc"; + reg = <0x740 0x14>; + interrupts = ; + clocks = <&rtc32k_xtal>; + vdd-supply = <&rtc_vdd>; + resets = <&reset RESET_RTC>; + + #address-cells = <1>; + #size-cells = <1>; + }; From d8fe6009aa3ecbeeab3a4ec1a8bce68959a885be Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 9 Feb 2019 01:18:13 +0100 Subject: [PATCH 1514/4277] rtc: support for the Amlogic Meson RTC Add support for the RTC block on the 32-bit Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs. The RTC is split in to two parts, which are both managed by this driver: - the AHB front end - and a simple serial connection to the actual registers The RTC_COUNTER register which holds the time is 32-bits wide. There are four 32-bit wide (in total: 16 bytes) "regmem" registers which are exposed using nvmem. On Amlogic's 3.10 kernel this is used to store data which needs to survive a suspend / resume cycle. Signed-off-by: Ben Dooks [resurrected Ben's patches after 2 years] Signed-off-by: Martin Blumenstingl Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-meson.c | 410 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 422 insertions(+) create mode 100644 drivers/rtc/rtc-meson.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8f6735e0ff14..7d61bb382cf3 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1294,6 +1294,17 @@ config RTC_DRV_IMXDI This driver can also be built as a module, if so, the module will be called "rtc-imxdi". +config RTC_DRV_MESON + tristate "Amlogic Meson RTC" + depends on (ARM && ARCH_MESON) || COMPILE_TEST + select REGMAP_MMIO + help + Support for the RTC block on the Amlogic Meson6, Meson8, Meson8b + and Meson8m2 SoCs. + + This driver can also be built as a module, if so, the module + will be called "rtc-meson". + config RTC_DRV_OMAP tristate "TI OMAP Real Time Clock" depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index faca02109aaa..b994323942bd 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o +obj-$(CONFIG_RTC_DRV_MESON) += rtc-meson.o obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c new file mode 100644 index 000000000000..6fc4b09e7ebd --- /dev/null +++ b/drivers/rtc/rtc-meson.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the interal RTC block in the Amlogic Meson6, Meson8, + * Meson8b and Meson8m2 SoCs. + * + * The RTC is split in to two parts, the AHB front end and a simple serial + * connection to the actual registers. This driver manages both parts. + * + * Copyright (c) 2018 Martin Blumenstingl + * Copyright (c) 2015 Ben Dooks for Codethink Ltd + * Based on origin by Carlo Caione + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers accessed from cpu bus */ +#define RTC_ADDR0 0x00 + #define RTC_ADDR0_LINE_SCLK BIT(0) + #define RTC_ADDR0_LINE_SEN BIT(1) + #define RTC_ADDR0_LINE_SDI BIT(2) + #define RTC_ADDR0_START_SER BIT(17) + #define RTC_ADDR0_WAIT_SER BIT(22) + #define RTC_ADDR0_DATA GENMASK(31, 24) + +#define RTC_ADDR1 0x04 + #define RTC_ADDR1_SDO BIT(0) + #define RTC_ADDR1_S_READY BIT(1) + +#define RTC_ADDR2 0x08 +#define RTC_ADDR3 0x0c + +#define RTC_REG4 0x10 + #define RTC_REG4_STATIC_VALUE GENMASK(7, 0) + +/* rtc registers accessed via rtc-serial interface */ +#define RTC_COUNTER (0) +#define RTC_SEC_ADJ (2) +#define RTC_REGMEM_0 (4) +#define RTC_REGMEM_1 (5) +#define RTC_REGMEM_2 (6) +#define RTC_REGMEM_3 (7) + +#define RTC_ADDR_BITS (3) /* number of address bits to send */ +#define RTC_DATA_BITS (32) /* number of data bits to tx/rx */ + +#define MESON_STATIC_BIAS_CUR (0x5 << 1) +#define MESON_STATIC_VOLTAGE (0x3 << 11) +#define MESON_STATIC_DEFAULT (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE) + +struct meson_rtc { + struct rtc_device *rtc; /* rtc device we created */ + struct device *dev; /* device we bound from */ + struct reset_control *reset; /* reset source */ + struct regulator *vdd; /* voltage input */ + struct regmap *peripheral; /* peripheral registers */ + struct regmap *serial; /* serial registers */ +}; + +static const struct regmap_config meson_rtc_peripheral_regmap_config = { + .name = "peripheral-registers", + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = RTC_REG4, + .fast_io = true, +}; + +/* RTC front-end serialiser controls */ + +static void meson_rtc_sclk_pulse(struct meson_rtc *rtc) +{ + udelay(5); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0); + udelay(5); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, + RTC_ADDR0_LINE_SCLK); +} + +static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit) +{ + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, + bit ? RTC_ADDR0_LINE_SDI : 0); + meson_rtc_sclk_pulse(rtc); +} + +static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data, + unsigned int nr) +{ + u32 bit = 1 << (nr - 1); + + while (bit) { + meson_rtc_send_bit(rtc, data & bit); + bit >>= 1; + } +} + +static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode) +{ + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); + meson_rtc_send_bit(rtc, mode); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); +} + +static u32 meson_rtc_get_data(struct meson_rtc *rtc) +{ + u32 tmp, val = 0; + int bit; + + for (bit = 0; bit < RTC_DATA_BITS; bit++) { + meson_rtc_sclk_pulse(rtc); + val <<= 1; + + regmap_read(rtc->peripheral, RTC_ADDR1, &tmp); + val |= tmp & RTC_ADDR1_SDO; + } + + return val; +} + +static int meson_rtc_get_bus(struct meson_rtc *rtc) +{ + int ret, retries = 3; + u32 val; + + /* prepare bus for transfers, set all lines low */ + val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK; + regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0); + + for (retries = 0; retries < 3; retries++) { + /* wait for the bus to be ready */ + if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val, + val & RTC_ADDR1_S_READY, 10, + 10000)) + return 0; + + dev_warn(rtc->dev, "failed to get bus, resetting RTC\n"); + + ret = reset_control_reset(rtc->reset); + if (ret) + return ret; + } + + dev_err(rtc->dev, "bus is not ready\n"); + return -ETIMEDOUT; +} + +static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg, + unsigned int *data) +{ + struct meson_rtc *rtc = context; + int ret; + + ret = meson_rtc_get_bus(rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, + RTC_ADDR0_LINE_SEN); + meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); + meson_rtc_set_dir(rtc, 0); + *data = meson_rtc_get_data(rtc); + + return 0; +} + +static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg, + unsigned int data) +{ + struct meson_rtc *rtc = context; + int ret; + + ret = meson_rtc_get_bus(rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, + RTC_ADDR0_LINE_SEN); + meson_rtc_send_bits(rtc, data, RTC_DATA_BITS); + meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); + meson_rtc_set_dir(rtc, 1); + + return 0; +} + +static const struct regmap_bus meson_rtc_serial_bus = { + .reg_read = meson_rtc_serial_bus_reg_read, + .reg_write = meson_rtc_serial_bus_reg_write, +}; + +static const struct regmap_config meson_rtc_serial_regmap_config = { + .name = "serial-registers", + .reg_bits = 4, + .reg_stride = 1, + .val_bits = 32, + .max_register = RTC_REGMEM_3, + .fast_io = false, +}; + +static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data) +{ + u32 tmp; + + regmap_write(rtc->peripheral, RTC_REG4, + FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8))); + + /* write the static value and start the auto serializer */ + tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER; + regmap_update_bits(rtc->peripheral, RTC_ADDR0, + RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp); + + /* wait for the auto serializer to complete */ + return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp, + !(tmp & RTC_ADDR0_WAIT_SER), 10, + 10000); +} + +/* RTC interface layer functions */ + +static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct meson_rtc *rtc = dev_get_drvdata(dev); + u32 time; + int ret; + + ret = regmap_read(rtc->serial, RTC_COUNTER, &time); + if (!ret) + rtc_time64_to_tm(time, tm); + + return ret; +} + +static int meson_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct meson_rtc *rtc = dev_get_drvdata(dev); + + return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm)); +} + +static const struct rtc_class_ops meson_rtc_ops = { + .read_time = meson_rtc_gettime, + .set_time = meson_rtc_settime, +}; + +/* NVMEM interface layer functions */ + +static int meson_rtc_regmem_read(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_rtc *rtc = context; + unsigned int read_offset, read_size; + + read_offset = RTC_REGMEM_0 + (offset / 4); + read_size = bytes / 4; + + return regmap_bulk_read(rtc->serial, read_offset, buf, read_size); +} + +static int meson_rtc_regmem_write(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_rtc *rtc = context; + unsigned int write_offset, write_size; + + write_offset = RTC_REGMEM_0 + (offset / 4); + write_size = bytes / 4; + + return regmap_bulk_write(rtc->serial, write_offset, buf, write_size); +} + +static int meson_rtc_probe(struct platform_device *pdev) +{ + struct nvmem_config meson_rtc_nvmem_config = { + .name = "meson-rtc-regmem", + .type = NVMEM_TYPE_BATTERY_BACKED, + .word_size = 4, + .stride = 4, + .size = 4 * 4, + .reg_read = meson_rtc_regmem_read, + .reg_write = meson_rtc_regmem_write, + }; + struct device *dev = &pdev->dev; + struct meson_rtc *rtc; + struct resource *res; + void __iomem *base; + int ret; + u32 tm; + + rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + platform_set_drvdata(pdev, rtc); + + rtc->dev = dev; + + rtc->rtc->ops = &meson_rtc_ops; + rtc->rtc->range_max = U32_MAX; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rtc->peripheral = devm_regmap_init_mmio(dev, base, + &meson_rtc_peripheral_regmap_config); + if (IS_ERR(rtc->peripheral)) { + dev_err(dev, "failed to create peripheral regmap\n"); + return PTR_ERR(rtc->peripheral); + } + + rtc->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(rtc->reset)) { + dev_err(dev, "missing reset line\n"); + return PTR_ERR(rtc->reset); + } + + rtc->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(rtc->vdd)) { + dev_err(dev, "failed to get the vdd-supply\n"); + return PTR_ERR(rtc->vdd); + } + + ret = regulator_enable(rtc->vdd); + if (ret) { + dev_err(dev, "failed to enable vdd-supply\n"); + return ret; + } + + ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT); + if (ret) { + dev_err(dev, "failed to set static values\n"); + goto out_disable_vdd; + } + + rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc, + &meson_rtc_serial_regmap_config); + if (IS_ERR(rtc->serial)) { + dev_err(dev, "failed to create serial regmap\n"); + ret = PTR_ERR(rtc->serial); + goto out_disable_vdd; + } + + /* + * check if we can read RTC counter, if not then the RTC is probably + * not functional. If it isn't probably best to not bind. + */ + ret = regmap_read(rtc->serial, RTC_COUNTER, &tm); + if (ret) { + dev_err(dev, "cannot read RTC counter, RTC not functional\n"); + goto out_disable_vdd; + } + + meson_rtc_nvmem_config.priv = rtc; + ret = rtc_nvmem_register(rtc->rtc, &meson_rtc_nvmem_config); + if (ret) + goto out_disable_vdd; + + ret = rtc_register_device(rtc->rtc); + if (ret) + goto out_unregister_nvmem; + + return 0; + +out_unregister_nvmem: + rtc_nvmem_unregister(rtc->rtc); + +out_disable_vdd: + regulator_disable(rtc->vdd); + return ret; +} + +static const struct of_device_id meson_rtc_dt_match[] = { + { .compatible = "amlogic,meson6-rtc", }, + { .compatible = "amlogic,meson8-rtc", }, + { .compatible = "amlogic,meson8b-rtc", }, + { .compatible = "amlogic,meson8m2-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, meson_rtc_dt_match); + +static struct platform_driver meson_rtc_driver = { + .probe = meson_rtc_probe, + .driver = { + .name = "meson-rtc", + .of_match_table = of_match_ptr(meson_rtc_dt_match), + }, +}; +module_platform_driver(meson_rtc_driver); + +MODULE_DESCRIPTION("Amlogic Meson RTC Driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:meson-rtc"); From 6b7cb2227d4d833aaccb3e98214015f2162d5bb0 Mon Sep 17 00:00:00 2001 From: Nick Crews Date: Fri, 8 Feb 2019 17:37:16 -0700 Subject: [PATCH 1515/4277] platform/chrome: cros_ec: Remove cros_ec dependency in lpc_mec In order to allow this code to be re-used, remove the dependency on the rest of the cros_ec code from the cros_ec_lpc_mec functions. Instead of using a hardcoded register base address of 0x800 have this be passed in to cros_ec_lpc_mec_init(). The existing cros_ec use case now passes in the 0x800 base address this way. There are some error checks that happen in cros_ec_lpc_mec_in_range() that probably shouldn't be there, as they are checking kernel-space callers and not user-space input. However, we'll just do the refactor in this patch, and in a future patch might remove this error checking and fix all the instances of code that calls this. There's a similar problem in cros_ec_lpc_read_bytes(), where we return a checksum, but on error just return 0. This should probably be changed so that it returns int, but we don't want to have to mess with all the calling code for this fix. Maybe we'll come back through later and fix this. Signed-off-by: Duncan Laurie Signed-off-by: Nick Crews Signed-off-by: Enric Balletbo i Serra --- drivers/platform/chrome/cros_ec_lpc_mec.c | 52 +++++++++++++++++++---- drivers/platform/chrome/cros_ec_lpc_mec.h | 43 ++++++++++--------- drivers/platform/chrome/cros_ec_lpc_reg.c | 51 ++++++++-------------- 3 files changed, 85 insertions(+), 61 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c index 296554a82b83..d8890bafb55d 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.c +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -16,6 +15,7 @@ * EC mutex because memmap data may be accessed without it being held. */ static struct mutex io_mutex; +static u16 mec_emi_base, mec_emi_end; /* * cros_ec_lpc_mec_emi_write_address @@ -28,10 +28,37 @@ static struct mutex io_mutex; static void cros_ec_lpc_mec_emi_write_address(u16 addr, enum cros_ec_lpc_mec_emi_access_mode access_type) { - /* Address relative to start of EMI range */ - addr -= MEC_EMI_RANGE_START; - outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0); - outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1); + outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base)); + outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(mec_emi_base)); +} + +/** + * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range. + * + * @offset: Address offset + * @length: Number of bytes to check + * + * Return: 1 if in range, 0 if not, and -EINVAL on failure + * such as the mec range not being initialized + */ +int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length) +{ + if (length == 0) + return -EINVAL; + + if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0)) + return -EINVAL; + + if (offset >= mec_emi_base && offset < mec_emi_end) { + if (WARN_ON(offset + length - 1 >= mec_emi_end)) + return -EINVAL; + return 1; + } + + if (WARN_ON(offset + length > mec_emi_base && offset < mec_emi_end)) + return -EINVAL; + + return 0; } /* @@ -53,6 +80,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, u8 sum = 0; enum cros_ec_lpc_mec_emi_access_mode access, new_access; + /* Return checksum of 0 if window is not initialized */ + WARN_ON(mec_emi_base == 0 || mec_emi_end == 0); + if (mec_emi_base == 0 || mec_emi_end == 0) + return 0; + /* * Long access cannot be used on misaligned data since reading B0 loads * the data register and writing B3 flushes. @@ -68,9 +100,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, cros_ec_lpc_mec_emi_write_address(offset, access); /* Skip bytes in case of misaligned offset */ - io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3); + io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base) + (offset & 0x3); while (i < length) { - while (io_addr <= MEC_EMI_EC_DATA_B3) { + while (io_addr <= MEC_EMI_EC_DATA_B3(mec_emi_base)) { if (io_type == MEC_IO_READ) buf[i] = inb(io_addr++); else @@ -100,7 +132,7 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, } /* Access [B0, B3] on each loop pass */ - io_addr = MEC_EMI_EC_DATA_B0; + io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base); } done: @@ -110,9 +142,11 @@ done: } EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec); -void cros_ec_lpc_mec_init(void) +void cros_ec_lpc_mec_init(unsigned int base, unsigned int end) { mutex_init(&io_mutex); + mec_emi_base = base; + mec_emi_end = end; } EXPORT_SYMBOL(cros_ec_lpc_mec_init); diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h index 68b381f2eb50..aa1018f6b0f2 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.h +++ b/drivers/platform/chrome/cros_ec_lpc_mec.h @@ -8,8 +8,6 @@ #ifndef __CROS_EC_LPC_MEC_H #define __CROS_EC_LPC_MEC_H -#include - enum cros_ec_lpc_mec_emi_access_mode { /* 8-bit access */ ACCESS_TYPE_BYTE = 0x0, @@ -29,27 +27,23 @@ enum cros_ec_lpc_mec_io_type { MEC_IO_WRITE, }; -/* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */ -#define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0 -#define MEC_EMI_RANGE_END (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE) - /* EMI registers are relative to base */ -#define MEC_EMI_BASE 0x800 -#define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0) -#define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1) -#define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2) -#define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3) -#define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4) -#define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5) -#define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6) -#define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7) +#define MEC_EMI_HOST_TO_EC(MEC_EMI_BASE) ((MEC_EMI_BASE) + 0) +#define MEC_EMI_EC_TO_HOST(MEC_EMI_BASE) ((MEC_EMI_BASE) + 1) +#define MEC_EMI_EC_ADDRESS_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 2) +#define MEC_EMI_EC_ADDRESS_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 3) +#define MEC_EMI_EC_DATA_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 4) +#define MEC_EMI_EC_DATA_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 5) +#define MEC_EMI_EC_DATA_B2(MEC_EMI_BASE) ((MEC_EMI_BASE) + 6) +#define MEC_EMI_EC_DATA_B3(MEC_EMI_BASE) ((MEC_EMI_BASE) + 7) -/* - * cros_ec_lpc_mec_init +/** + * cros_ec_lpc_mec_init() - Initialize MEC I/O. * - * Initialize MEC I/O. + * @base: MEC EMI Base address + * @end: MEC EMI End address */ -void cros_ec_lpc_mec_init(void); +void cros_ec_lpc_mec_init(unsigned int base, unsigned int end); /* * cros_ec_lpc_mec_destroy @@ -58,6 +52,17 @@ void cros_ec_lpc_mec_init(void); */ void cros_ec_lpc_mec_destroy(void); +/** + * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range. + * + * @offset: Address offset + * @length: Number of bytes to check + * + * Return: 1 if in range, 0 if not, and -EINVAL on failure + * such as the mec range not being initialized + */ +int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length); + /** * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port * diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c index 72fc3f6bff90..0f5cd0ac8b49 100644 --- a/drivers/platform/chrome/cros_ec_lpc_reg.c +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c @@ -41,51 +41,36 @@ static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) { - if (length == 0) + int in_range = cros_ec_lpc_mec_in_range(offset, length); + + if (in_range < 0) return 0; - /* Access desired range through EMI interface */ - if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) { - /* Ensure we don't straddle EMI region */ - if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END)) - return 0; - - return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length, - dest); - } - - if (WARN_ON(offset + length > MEC_EMI_RANGE_START && - offset < MEC_EMI_RANGE_START)) - return 0; - - return lpc_read_bytes(offset, length, dest); + return in_range ? + cros_ec_lpc_io_bytes_mec(MEC_IO_READ, + offset - EC_HOST_CMD_REGION0, + length, dest) : + lpc_read_bytes(offset, length, dest); } u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) { - if (length == 0) + int in_range = cros_ec_lpc_mec_in_range(offset, length); + + if (in_range < 0) return 0; - /* Access desired range through EMI interface */ - if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) { - /* Ensure we don't straddle EMI region */ - if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END)) - return 0; - - return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length, - msg); - } - - if (WARN_ON(offset + length > MEC_EMI_RANGE_START && - offset < MEC_EMI_RANGE_START)) - return 0; - - return lpc_write_bytes(offset, length, msg); + return in_range ? + cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, + offset - EC_HOST_CMD_REGION0, + length, msg) : + lpc_write_bytes(offset, length, msg); } void cros_ec_lpc_reg_init(void) { - cros_ec_lpc_mec_init(); + cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, + EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); } void cros_ec_lpc_reg_destroy(void) From b77cf11f094136a9d7d0ee6a56cf49db1f412871 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 5 Feb 2019 10:37:31 -0600 Subject: [PATCH 1516/4277] iommu: Allow io-pgtable to be used outside of drivers/iommu/ Move io-pgtable.h to include/linux/ and export alloc_io_pgtable_ops and free_io_pgtable_ops. This enables drivers outside drivers/iommu/ to use the page table library. Specifically, some ARM Mali GPUs use the ARM page table formats. Cc: Will Deacon Cc: Robin Murphy Cc: Joerg Roedel Cc: Matthias Brugger Cc: Rob Clark Cc: linux-arm-kernel@lists.infradead.org Cc: iommu@lists.linux-foundation.org Cc: linux-mediatek@lists.infradead.org Cc: linux-arm-msm@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Joerg Roedel --- drivers/iommu/arm-smmu-v3.c | 3 +-- drivers/iommu/arm-smmu.c | 2 +- drivers/iommu/io-pgtable-arm-v7s.c | 3 +-- drivers/iommu/io-pgtable-arm.c | 3 +-- drivers/iommu/io-pgtable.c | 5 +++-- drivers/iommu/ipmmu-vmsa.c | 3 +-- drivers/iommu/msm_iommu.c | 2 +- drivers/iommu/mtk_iommu.h | 3 +-- drivers/iommu/qcom_iommu.c | 2 +- {drivers/iommu => include/linux}/io-pgtable.h | 0 10 files changed, 11 insertions(+), 15 deletions(-) rename {drivers/iommu => include/linux}/io-pgtable.h (100%) diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 0d284029dc73..d3880010c6cf 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -32,8 +33,6 @@ #include -#include "io-pgtable.h" - /* MMIO registers */ #define ARM_SMMU_IDR0 0x0 #define IDR0_ST_LVL GENMASK(28, 27) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index af18a7e7f917..045d93884164 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,6 @@ #include #include -#include "io-pgtable.h" #include "arm-smmu-regs.h" #define ARM_MMU500_ACTLR_CPRE (1 << 1) diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index cec29bf45c9b..75a8273d1ae9 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -45,8 +46,6 @@ #include -#include "io-pgtable.h" - /* Struct accessors */ #define io_pgtable_to_data(x) \ container_of((x), struct arm_v7s_io_pgtable, iop) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 237cacd4a62b..d3700ec15cbd 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -31,8 +32,6 @@ #include -#include "io-pgtable.h" - #define ARM_LPAE_MAX_ADDR_BITS 52 #define ARM_LPAE_S2_MAX_CONCAT_PAGES 16 #define ARM_LPAE_MAX_LEVELS 4 diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c index 127558d83667..93f2880be6c6 100644 --- a/drivers/iommu/io-pgtable.c +++ b/drivers/iommu/io-pgtable.c @@ -19,11 +19,10 @@ */ #include +#include #include #include -#include "io-pgtable.h" - static const struct io_pgtable_init_fns * io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = { #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE @@ -61,6 +60,7 @@ struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, return &iop->ops; } +EXPORT_SYMBOL_GPL(alloc_io_pgtable_ops); /* * It is the IOMMU driver's responsibility to ensure that the page table @@ -77,3 +77,4 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops) io_pgtable_tlb_flush_all(iop); io_pgtable_init_table[iop->fmt]->free(iop); } +EXPORT_SYMBOL_GPL(free_io_pgtable_ops); diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 7a4529c61c19..9a380c10655e 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -35,8 +36,6 @@ #define arm_iommu_detach_device(...) do {} while (0) #endif -#include "io-pgtable.h" - #define IPMMU_CTX_MAX 8 struct ipmmu_features { diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index fc4270733f11..ef7d1f995d6b 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include "msm_iommu_hw-8xxx.h" #include "msm_iommu.h" -#include "io-pgtable.h" #define MRC(reg, processor, op1, crn, crm, op2) \ __asm__ __volatile__ ( \ diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h index 778498b8633f..62c2c3e8c5df 100644 --- a/drivers/iommu/mtk_iommu.h +++ b/drivers/iommu/mtk_iommu.h @@ -19,13 +19,12 @@ #include #include #include +#include #include #include #include #include -#include "io-pgtable.h" - struct mtk_iommu_suspend_reg { u32 standard_axi_mode; u32 dcm_dis; diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c index d8595f0a987d..8cdd3f059513 100644 --- a/drivers/iommu/qcom_iommu.c +++ b/drivers/iommu/qcom_iommu.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,6 @@ #include #include -#include "io-pgtable.h" #include "arm-smmu-regs.h" #define SMMU_INTR_SEL_NS 0x2000 diff --git a/drivers/iommu/io-pgtable.h b/include/linux/io-pgtable.h similarity index 100% rename from drivers/iommu/io-pgtable.h rename to include/linux/io-pgtable.h From ef092dec043c0166b8aea0e56d1db8757877e40b Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Mon, 15 Oct 2018 14:02:40 +0200 Subject: [PATCH 1517/4277] drm/etnaviv: mmuv2: don't map zero page Keep the page at address 0 as faulting to catch any potential state setup issues early. Signed-off-by: Lucas Stach Reviewed-by: Christian Gmeiner --- drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c index f1c88d8ad5ba..f794e04be9e6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c @@ -320,8 +320,8 @@ etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu) domain = &etnaviv_domain->base; domain->dev = gpu->dev; - domain->base = 0; - domain->size = (u64)SZ_1G * 4; + domain->base = SZ_4K; + domain->size = (u64)SZ_1G * 4 - SZ_4K; domain->ops = &etnaviv_iommuv2_ops; ret = etnaviv_iommuv2_init(etnaviv_domain); From 9e05352340d3a3e68c144136db9810b26ebb88c3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 5 Feb 2019 12:08:19 +0300 Subject: [PATCH 1518/4277] drm/etnaviv: potential NULL dereference The etnaviv_gem_prime_get_sg_table() is supposed to return error pointers. Otherwise it can lead to a NULL dereference when it's called from drm_gem_map_dma_buf(). Fixes: 5f4a4a73f437 ("drm/etnaviv: fix gem_prime_get_sg_table to return new SG table") Signed-off-by: Dan Carpenter Reviewed-by: Christian Gmeiner Signed-off-by: Lucas Stach --- drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 0566171f8df2..f21529e635e3 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -15,7 +15,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) int npages = obj->size >> PAGE_SHIFT; if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ - return NULL; + return ERR_PTR(-EINVAL); return drm_prime_pages_to_sg(etnaviv_obj->pages, npages); } From 780da9e4f5bf35d348b290f0f97de9b55670cb5b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2019 16:05:45 -0600 Subject: [PATCH 1519/4277] iommu: Use dev_printk() when possible Use dev_printk() when possible so the IOMMU messages are more consistent with other messages related to the device. E.g., I think these messages related to surprise hotplug: pciehp 0000:80:10.0:pcie004: Slot(36): Link Down iommu: Removing device 0000:87:00.0 from group 12 pciehp 0000:80:10.0:pcie004: Slot(36): Card present pcieport 0000:80:10.0: Data Link Layer Link Active not set in 1000 msec would be easier to read as these (also requires some PCI changes not included here): pci 0000:80:10.0: Slot(36): Link Down pci 0000:87:00.0: Removing from iommu group 12 pci 0000:80:10.0: Slot(36): Card present pci 0000:80:10.0: Data Link Layer Link Active not set in 1000 msec Signed-off-by: Bjorn Helgaas Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 3ed4db334341..54c9d18fe31d 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -668,7 +668,7 @@ rename: trace_add_device_to_group(group->id, dev); - pr_info("Adding device %s to group %d\n", dev_name(dev), group->id); + dev_info(dev, "Adding to iommu group %d\n", group->id); return 0; @@ -684,7 +684,7 @@ err_remove_link: sysfs_remove_link(&dev->kobj, "iommu_group"); err_free_device: kfree(device); - pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret); + dev_err(dev, "Failed to add to iommu group %d: %d\n", group->id, ret); return ret; } EXPORT_SYMBOL_GPL(iommu_group_add_device); @@ -701,7 +701,7 @@ void iommu_group_remove_device(struct device *dev) struct iommu_group *group = dev->iommu_group; struct group_device *tmp_device, *device = NULL; - pr_info("Removing device %s from group %d\n", dev_name(dev), group->id); + dev_info(dev, "Removing from iommu group %d\n", group->id); /* Pre-notify listeners that a device is being removed. */ blocking_notifier_call_chain(&group->notifier, @@ -1951,7 +1951,7 @@ int iommu_request_dm_for_dev(struct device *dev) iommu_domain_free(group->default_domain); group->default_domain = dm_domain; - pr_info("Using direct mapping for device %s\n", dev_name(dev)); + dev_info(dev, "Using iommu direct mapping\n"); ret = 0; out: From 5f226da1b1d7065bd22605fd889ac96eb5f3a7db Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2019 16:05:53 -0600 Subject: [PATCH 1520/4277] iommu/amd: Use dev_printk() when possible Use dev_printk() when possible so the IOMMU messages are more consistent with other messages related to the device. Signed-off-by: Bjorn Helgaas Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 26 ++++++++++++-------------- drivers/iommu/amd_iommu_init.c | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 675f7027aa04..c0dc7c33335e 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -18,6 +18,7 @@ */ #define pr_fmt(fmt) "AMD-Vi: " fmt +#define dev_fmt(fmt) pr_fmt(fmt) #include #include @@ -279,10 +280,10 @@ static u16 get_alias(struct device *dev) return pci_alias; } - pr_info("Using IVRS reported alias %02x:%02x.%d " - "for device %s[%04x:%04x], kernel reported alias " + pci_info(pdev, "Using IVRS reported alias %02x:%02x.%d " + "for device [%04x:%04x], kernel reported alias " "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias), - PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device, + PCI_FUNC(ivrs_alias), pdev->vendor, pdev->device, PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias), PCI_FUNC(pci_alias)); @@ -293,9 +294,8 @@ static u16 get_alias(struct device *dev) if (pci_alias == devid && PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { pci_add_dma_alias(pdev, ivrs_alias & 0xff); - pr_info("Added PCI DMA alias %02x.%d for %s\n", - PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), - dev_name(dev)); + pci_info(pdev, "Added PCI DMA alias %02x.%d\n", + PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias)); } return ivrs_alias; @@ -545,7 +545,7 @@ static void amd_iommu_report_page_fault(u16 devid, u16 domain_id, dev_data = get_dev_data(&pdev->dev); if (dev_data && __ratelimit(&dev_data->rs)) { - dev_err(&pdev->dev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n", + pci_err(pdev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n", domain_id, address, flags); } else if (printk_ratelimit()) { pr_err("Event logged [IO_PAGE_FAULT device=%02x:%02x.%x domain=0x%04x address=0x%llx flags=0x%04x]\n", @@ -2251,8 +2251,7 @@ static int amd_iommu_add_device(struct device *dev) ret = iommu_init_device(dev); if (ret) { if (ret != -ENOTSUPP) - pr_err("Failed to initialize device %s - trying to proceed anyway\n", - dev_name(dev)); + dev_err(dev, "Failed to initialize - trying to proceed anyway\n"); iommu_ignore_device(dev); dev->dma_ops = NULL; @@ -2605,8 +2604,8 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, return nelems; out_unmap: - pr_err("%s: IOMMU mapping error in %s (io-pages: %d) reason: %d\n", - dev_name(dev), __func__, npages, ret); + dev_err(dev, "IOMMU mapping error in map_sg (io-pages: %d reason: %d)\n", + npages, ret); for_each_sg(sglist, s, nelems, i) { int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE); @@ -2800,7 +2799,7 @@ static int init_reserved_iova_ranges(void) IOVA_PFN(r->start), IOVA_PFN(r->end)); if (!val) { - pr_err("Reserve pci-resource range failed\n"); + pci_err(pdev, "Reserve pci-resource range %pR failed\n", r); return -ENOMEM; } } @@ -3170,8 +3169,7 @@ static void amd_iommu_get_resv_regions(struct device *dev, length, prot, IOMMU_RESV_DIRECT); if (!region) { - pr_err("Out of memory allocating dm-regions for %s\n", - dev_name(dev)); + dev_err(dev, "Out of memory allocating dm-regions\n"); return; } list_add_tail(®ion->list, head); diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 66123b911ec8..f773792d77fd 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -18,6 +18,7 @@ */ #define pr_fmt(fmt) "AMD-Vi: " fmt +#define dev_fmt(fmt) pr_fmt(fmt) #include #include @@ -1457,8 +1458,7 @@ static void amd_iommu_erratum_746_workaround(struct amd_iommu *iommu) pci_write_config_dword(iommu->dev, 0xf0, 0x90 | (1 << 8)); pci_write_config_dword(iommu->dev, 0xf4, value | 0x4); - pr_info("Applying erratum 746 workaround for IOMMU at %s\n", - dev_name(&iommu->dev->dev)); + pci_info(iommu->dev, "Applying erratum 746 workaround\n"); /* Clear the enable writing bit */ pci_write_config_dword(iommu->dev, 0xf0, 0x90); @@ -1488,8 +1488,7 @@ static void amd_iommu_ats_write_check_workaround(struct amd_iommu *iommu) /* Set L2_DEBUG_3[AtsIgnoreIWDis] = 1 */ iommu_write_l2(iommu, 0x47, value | BIT(0)); - pr_info("Applying ATS write check workaround for IOMMU at %s\n", - dev_name(&iommu->dev->dev)); + pci_info(iommu->dev, "Applying ATS write check workaround\n"); } /* @@ -1665,6 +1664,7 @@ static int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, static void init_iommu_perf_ctr(struct amd_iommu *iommu) { + struct pci_dev *pdev = iommu->dev; u64 val = 0xabcd, val2 = 0; if (!iommu_feature(iommu, FEATURE_PC)) @@ -1676,12 +1676,12 @@ static void init_iommu_perf_ctr(struct amd_iommu *iommu) if ((iommu_pc_get_set_reg(iommu, 0, 0, 0, &val, true)) || (iommu_pc_get_set_reg(iommu, 0, 0, 0, &val2, false)) || (val != val2)) { - pr_err("Unable to write to IOMMU perf counter.\n"); + pci_err(pdev, "Unable to write to IOMMU perf counter.\n"); amd_iommu_pc_present = false; return; } - pr_info("IOMMU performance counters supported\n"); + pci_info(pdev, "IOMMU performance counters supported\n"); val = readl(iommu->mmio_base + MMIO_CNTR_CONF_OFFSET); iommu->max_banks = (u8) ((val >> 12) & 0x3f); @@ -1840,14 +1840,14 @@ static void print_iommu_info(void) struct amd_iommu *iommu; for_each_iommu(iommu) { + struct pci_dev *pdev = iommu->dev; int i; - pr_info("Found IOMMU at %s cap 0x%hx\n", - dev_name(&iommu->dev->dev), iommu->cap_ptr); + pci_info(pdev, "Found IOMMU cap 0x%hx\n", iommu->cap_ptr); if (iommu->cap & (1 << IOMMU_CAP_EFR)) { - pr_info("Extended features (%#llx):\n", - iommu->features); + pci_info(pdev, "Extended features (%#llx):\n", + iommu->features); for (i = 0; i < ARRAY_SIZE(feat_str); ++i) { if (iommu_feature(iommu, (1ULL << i))) pr_cont(" %s", feat_str[i]); From 932a6523ce393921f81f09c273aa4adee52391af Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2019 16:06:00 -0600 Subject: [PATCH 1521/4277] iommu/vt-d: Use dev_printk() when possible Use dev_printk() when possible so the IOMMU messages are more consistent with other messages related to the device. Signed-off-by: Bjorn Helgaas Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 54 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 64dab37c0b96..648dc16cca6e 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -19,6 +19,7 @@ */ #define pr_fmt(fmt) "DMAR: " fmt +#define dev_fmt(fmt) pr_fmt(fmt) #include #include @@ -1815,7 +1816,7 @@ static int dmar_init_reserved_ranges(void) IOVA_PFN(r->start), IOVA_PFN(r->end)); if (!iova) { - pr_err("Reserve iova failed\n"); + pci_err(pdev, "Reserve iova for %pR failed\n", r); return -ENODEV; } } @@ -2544,8 +2545,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, if (dev && dev_is_pci(dev) && sm_supported(iommu)) { ret = intel_pasid_alloc_table(dev); if (ret) { - pr_err("PASID table allocation for %s failed\n", - dev_name(dev)); + dev_err(dev, "PASID table allocation failed\n"); dmar_remove_one_dev_info(domain, dev); return NULL; } @@ -2560,15 +2560,14 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, dev, PASID_RID2PASID); spin_unlock(&iommu->lock); if (ret) { - pr_err("Setup RID2PASID for %s failed\n", - dev_name(dev)); + dev_err(dev, "Setup RID2PASID failed\n"); dmar_remove_one_dev_info(domain, dev); return NULL; } } if (dev && domain_context_mapping(domain, dev)) { - pr_err("Domain context map for %s failed\n", dev_name(dev)); + dev_err(dev, "Domain context map failed\n"); dmar_remove_one_dev_info(domain, dev); return NULL; } @@ -2723,13 +2722,12 @@ static int domain_prepare_identity_map(struct device *dev, range which is reserved in E820, so which didn't get set up to start with in si_domain */ if (domain == si_domain && hw_pass_through) { - pr_warn("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n", - dev_name(dev), start, end); + dev_warn(dev, "Ignoring identity map for HW passthrough [0x%Lx - 0x%Lx]\n", + start, end); return 0; } - pr_info("Setting identity map for device %s [0x%Lx - 0x%Lx]\n", - dev_name(dev), start, end); + dev_info(dev, "Setting identity map [0x%Lx - 0x%Lx]\n", start, end); if (end < start) { WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n" @@ -3016,8 +3014,8 @@ static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw ret = domain_add_dev_info(si_domain, dev); if (!ret) - pr_info("%s identity mapping for device %s\n", - hw ? "Hardware" : "Software", dev_name(dev)); + dev_info(dev, "%s identity mapping\n", + hw ? "Hardware" : "Software"); else if (ret == -ENODEV) /* device not associated with an iommu */ ret = 0; @@ -3550,8 +3548,7 @@ static unsigned long intel_alloc_iova(struct device *dev, iova_pfn = alloc_iova_fast(&domain->iovad, nrpages, IOVA_PFN(dma_mask), true); if (unlikely(!iova_pfn)) { - pr_err("Allocating %ld-page iova for %s failed", - nrpages, dev_name(dev)); + dev_err(dev, "Allocating %ld-page iova failed", nrpages); return 0; } @@ -3599,7 +3596,7 @@ struct dmar_domain *get_valid_domain_for_dev(struct device *dev) out: if (!domain) - pr_err("Allocating domain for %s failed\n", dev_name(dev)); + dev_err(dev, "Allocating domain failed\n"); return domain; @@ -3626,8 +3623,7 @@ static int iommu_no_mapping(struct device *dev) * to non-identity mapping. */ dmar_remove_one_dev_info(si_domain, dev); - pr_info("32bit %s uses non-identity mapping\n", - dev_name(dev)); + dev_info(dev, "32bit DMA uses non-identity mapping\n"); return 0; } } else { @@ -3639,8 +3635,7 @@ static int iommu_no_mapping(struct device *dev) int ret; ret = domain_add_dev_info(si_domain, dev); if (!ret) { - pr_info("64bit %s uses identity mapping\n", - dev_name(dev)); + dev_info(dev, "64bit DMA uses identity mapping\n"); return 1; } } @@ -3703,8 +3698,8 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, error: if (iova_pfn) free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(size)); - pr_err("Device %s request: %zx@%llx dir %d --- failed\n", - dev_name(dev), size, (unsigned long long)paddr, dir); + dev_err(dev, "Device request: %zx@%llx dir %d --- failed\n", + size, (unsigned long long)paddr, dir); return DMA_MAPPING_ERROR; } @@ -3747,8 +3742,7 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) start_pfn = mm_to_dma_pfn(iova_pfn); last_pfn = start_pfn + nrpages - 1; - pr_debug("Device %s unmapping: pfn %lx-%lx\n", - dev_name(dev), start_pfn, last_pfn); + dev_dbg(dev, "Device unmapping: pfn %lx-%lx\n", start_pfn, last_pfn); freelist = domain_unmap(domain, start_pfn, last_pfn); @@ -5105,9 +5099,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, addr_width = cap_mgaw(iommu->cap); if (dmar_domain->max_addr > (1LL << addr_width)) { - pr_err("%s: iommu width (%d) is not " - "sufficient for the mapped address (%llx)\n", - __func__, addr_width, dmar_domain->max_addr); + dev_err(dev, "%s: iommu width (%d) is not " + "sufficient for the mapped address (%llx)\n", + __func__, addr_width, dmar_domain->max_addr); return -EFAULT; } dmar_domain->gaw = addr_width; @@ -5408,7 +5402,7 @@ const struct iommu_ops intel_iommu_ops = { static void quirk_iommu_g4x_gfx(struct pci_dev *dev) { /* G4x/GM45 integrated gfx dmar support is totally busted. */ - pr_info("Disabling IOMMU for graphics on this chipset\n"); + pci_info(dev, "Disabling IOMMU for graphics on this chipset\n"); dmar_map_gfx = 0; } @@ -5426,7 +5420,7 @@ static void quirk_iommu_rwbf(struct pci_dev *dev) * Mobile 4 Series Chipset neglects to set RWBF capability, * but needs it. Same seems to hold for the desktop versions. */ - pr_info("Forcing write-buffer flush capability\n"); + pci_info(dev, "Forcing write-buffer flush capability\n"); rwbf_quirk = 1; } @@ -5456,11 +5450,11 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev) return; if (!(ggc & GGC_MEMORY_VT_ENABLED)) { - pr_info("BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n"); + pci_info(dev, "BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n"); dmar_map_gfx = 0; } else if (dmar_map_gfx) { /* we have to ensure the gfx device is idle before we flush */ - pr_info("Disabling batched IOTLB flush on Ironlake\n"); + pci_info(dev, "Disabling batched IOTLB flush on Ironlake\n"); intel_iommu_strict = 1; } } From e083ea5b02114e1a54ebd285ae114d84ff5bea01 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2019 16:06:08 -0600 Subject: [PATCH 1522/4277] iommu/vt-d: Remove unnecessary local variable initializations A local variable initialization is a hint that the variable will be used in an unusual way. If the initialization is unnecessary, that hint becomes a distraction. Remove unnecessary initializations. No functional change intended. Signed-off-by: Bjorn Helgaas Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 648dc16cca6e..a27c0db0b5b1 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -865,7 +865,7 @@ out: static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, unsigned long pfn, int *target_level) { - struct dma_pte *parent, *pte = NULL; + struct dma_pte *parent, *pte; int level = agaw_to_level(domain->agaw); int offset; @@ -922,7 +922,7 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain, unsigned long pfn, int level, int *large_page) { - struct dma_pte *parent, *pte = NULL; + struct dma_pte *parent, *pte; int total = agaw_to_level(domain->agaw); int offset; @@ -954,7 +954,7 @@ static void dma_pte_clear_range(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { - unsigned int large_page = 1; + unsigned int large_page; struct dma_pte *first_pte, *pte; BUG_ON(!domain_pfn_supported(domain, start_pfn)); @@ -1132,7 +1132,7 @@ static struct page *domain_unmap(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { - struct page *freelist = NULL; + struct page *freelist; BUG_ON(!domain_pfn_supported(domain, start_pfn)); BUG_ON(!domain_pfn_supported(domain, last_pfn)); @@ -1763,7 +1763,7 @@ static int domain_attach_iommu(struct dmar_domain *domain, static int domain_detach_iommu(struct dmar_domain *domain, struct intel_iommu *iommu) { - int num, count = INT_MAX; + int num, count; assert_spin_locked(&device_domain_lock); assert_spin_locked(&iommu->lock); @@ -1902,7 +1902,7 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu, static void domain_exit(struct dmar_domain *domain) { - struct page *freelist = NULL; + struct page *freelist; /* Domain 0 is reserved, so dont process it */ if (!domain) @@ -2583,7 +2583,7 @@ static int get_last_alias(struct pci_dev *pdev, u16 alias, void *opaque) static struct dmar_domain *find_or_alloc_domain(struct device *dev, int gaw) { - struct device_domain_info *info = NULL; + struct device_domain_info *info; struct dmar_domain *domain = NULL; struct intel_iommu *iommu; u16 dma_alias; @@ -2807,7 +2807,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width); static int __init si_domain_init(int hw) { - int nid, ret = 0; + int nid, ret; si_domain = alloc_domain(DOMAIN_FLAG_STATIC_IDENTITY); if (!si_domain) @@ -2931,7 +2931,6 @@ static bool device_is_rmrr_locked(struct device *dev) static int iommu_should_identity_map(struct device *dev, int startup) { - if (dev_is_pci(dev)) { struct pci_dev *pdev = to_pci_dev(dev); @@ -3527,7 +3526,7 @@ static unsigned long intel_alloc_iova(struct device *dev, struct dmar_domain *domain, unsigned long nrpages, uint64_t dma_mask) { - unsigned long iova_pfn = 0; + unsigned long iova_pfn; /* Restrict dma_mask to the width that the iommu can handle */ dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask); @@ -4342,7 +4341,7 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg) static int intel_iommu_add(struct dmar_drhd_unit *dmaru) { - int sp, ret = 0; + int sp, ret; struct intel_iommu *iommu = dmaru->iommu; if (g_iommus[iommu->seq_id]) @@ -4506,7 +4505,7 @@ out: int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) { - int ret = 0; + int ret; struct dmar_rmrr_unit *rmrru; struct dmar_atsr_unit *atsru; struct acpi_dmar_atsr *atsr; @@ -4523,7 +4522,7 @@ int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) ((void *)rmrr) + rmrr->header.length, rmrr->segment, rmrru->devices, rmrru->devices_cnt); - if(ret < 0) + if (ret < 0) return ret; } else if (info->event == BUS_NOTIFY_REMOVED_DEVICE) { dmar_remove_dev_scope(info, rmrr->segment, @@ -4543,7 +4542,7 @@ int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) atsru->devices_cnt); if (ret > 0) break; - else if(ret < 0) + else if (ret < 0) return ret; } else if (info->event == BUS_NOTIFY_REMOVED_DEVICE) { if (dmar_remove_dev_scope(info, atsr->segment, From 717532394c712e044195405685f62522c579e22a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2019 16:06:15 -0600 Subject: [PATCH 1523/4277] iommu/vt-d: Remove unused dmar_remove_one_dev_info() argument domain_remove_dev_info() takes a struct dmar_domain * argument, but doesn't use it. Remove it. No functional change intended. The last use of this argument was removed by 127c761598f7 ("iommu/vt-d: Pass device_domain_info to __dmar_remove_one_dev_info"). Signed-off-by: Bjorn Helgaas Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index a27c0db0b5b1..e06913460cc3 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -343,8 +343,7 @@ static int g_num_of_iommus; static void domain_exit(struct dmar_domain *domain); static void domain_remove_dev_info(struct dmar_domain *domain); -static void dmar_remove_one_dev_info(struct dmar_domain *domain, - struct device *dev); +static void dmar_remove_one_dev_info(struct device *dev); static void __dmar_remove_one_dev_info(struct device_domain_info *info); static void domain_context_clear(struct intel_iommu *iommu, struct device *dev); @@ -2546,7 +2545,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, ret = intel_pasid_alloc_table(dev); if (ret) { dev_err(dev, "PASID table allocation failed\n"); - dmar_remove_one_dev_info(domain, dev); + dmar_remove_one_dev_info(dev); return NULL; } @@ -2561,14 +2560,14 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, spin_unlock(&iommu->lock); if (ret) { dev_err(dev, "Setup RID2PASID failed\n"); - dmar_remove_one_dev_info(domain, dev); + dmar_remove_one_dev_info(dev); return NULL; } } if (dev && domain_context_mapping(domain, dev)) { dev_err(dev, "Domain context map failed\n"); - dmar_remove_one_dev_info(domain, dev); + dmar_remove_one_dev_info(dev); return NULL; } @@ -3621,7 +3620,7 @@ static int iommu_no_mapping(struct device *dev) * 32 bit DMA is removed from si_domain and fall back * to non-identity mapping. */ - dmar_remove_one_dev_info(si_domain, dev); + dmar_remove_one_dev_info(dev); dev_info(dev, "32bit DMA uses non-identity mapping\n"); return 0; } @@ -4576,7 +4575,7 @@ static int device_notifier(struct notifier_block *nb, if (!domain) return 0; - dmar_remove_one_dev_info(domain, dev); + dmar_remove_one_dev_info(dev); if (!domain_type_is_vm_or_si(domain) && list_empty(&domain->devices)) domain_exit(domain); @@ -4989,8 +4988,7 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info) free_devinfo_mem(info); } -static void dmar_remove_one_dev_info(struct dmar_domain *domain, - struct device *dev) +static void dmar_remove_one_dev_info(struct device *dev) { struct device_domain_info *info; unsigned long flags; @@ -5079,7 +5077,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, old_domain = find_domain(dev); if (old_domain) { rcu_read_lock(); - dmar_remove_one_dev_info(old_domain, dev); + dmar_remove_one_dev_info(dev); rcu_read_unlock(); if (!domain_type_is_vm_or_si(old_domain) && @@ -5126,7 +5124,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, static void intel_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { - dmar_remove_one_dev_info(to_dmar_domain(domain), dev); + dmar_remove_one_dev_info(dev); } static int intel_iommu_map(struct iommu_domain *domain, From f096d6657a7714e62a4799893ac9bdee75be29c8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2019 16:06:22 -0600 Subject: [PATCH 1524/4277] iommu/vt-d: Remove misleading "domain 0" test from domain_exit() The "Domain 0 is reserved, so dont process it" comment suggests that a NULL pointer corresponds to domain 0. I don't think that's true, and in any case, every caller supplies a non-NULL domain pointer that has already been dereferenced, so the test is unnecessary. Remove the test for a null "domain" pointer. No functional change intended. This null pointer check was added by 5e98c4b1d6e8 ("Allocation and free functions of virtual machine domain"). Signed-off-by: Bjorn Helgaas Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index e06913460cc3..271455e5026e 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1903,10 +1903,6 @@ static void domain_exit(struct dmar_domain *domain) { struct page *freelist; - /* Domain 0 is reserved, so dont process it */ - if (!domain) - return; - /* Remove associated devices and clear attached or cached domains */ rcu_read_lock(); domain_remove_dev_info(domain); From 266d63a7d9d48c6d5dee486378ec0e8c86c4d74a Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 11 Feb 2019 12:23:51 +0100 Subject: [PATCH 1525/4277] x86/cpufeature: Fix various quality problems in the header Thomas noticed that the new arch/x86/include/asm/cpu_device_id.h header is a train-wreck that didn't incorporate review feedback like not using __u8 in kernel-only headers. While at it also fix all the *other* problems this header has: - Use canonical names for the header guards. It's inexplicable why a non-standard guard was used. - Don't define the header guard to 1. Plus annotate the closing #endif as done absolutely every other header. Again, an inexplicable source of noise. - Move the kernel API calls provided by this header next to each other, there's absolutely no reason to have them spread apart in the header. - Align the INTEL_CPU_DESC() macro initializations vertically, this is easier to read and it's also the canonical style. - Actually name the macro arguments properly: instead of 'mod, step, rev', spell out 'model, stepping, revision' - it's not like we have a lack of characters in this header. - Actually make arguments macro-safe - again it's inexplicable why it wasn't done properly to begin with. Quite amazing how many problems a 41 lines header can contain. This kind of code quality is unacceptable, and it slipped through the review net of 2 developers and 2 maintainers, including myself, until Thomas noticed it. :-/ Reported-by: Thomas Gleixner Cc: Andi Kleen Cc: Kan Liang Cc: Peter Zijlstra (Intel) Cc: Borislav Petkov Cc: Linus Torvalds Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar --- arch/x86/include/asm/cpu_device_id.h | 31 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/arch/x86/include/asm/cpu_device_id.h b/arch/x86/include/asm/cpu_device_id.h index 3417110574c1..31c379c1da41 100644 --- a/arch/x86/include/asm/cpu_device_id.h +++ b/arch/x86/include/asm/cpu_device_id.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _CPU_DEVICE_ID -#define _CPU_DEVICE_ID 1 +#ifndef _ASM_X86_CPU_DEVICE_ID +#define _ASM_X86_CPU_DEVICE_ID /* * Declare drivers belonging to specific x86 CPUs @@ -9,8 +9,6 @@ #include -extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match); - /* * Match specific microcode revisions. * @@ -22,21 +20,22 @@ extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match); */ struct x86_cpu_desc { - __u8 x86_family; - __u8 x86_vendor; - __u8 x86_model; - __u8 x86_stepping; - __u32 x86_microcode_rev; + u8 x86_family; + u8 x86_vendor; + u8 x86_model; + u8 x86_stepping; + u32 x86_microcode_rev; }; -#define INTEL_CPU_DESC(mod, step, rev) { \ - .x86_family = 6, \ - .x86_vendor = X86_VENDOR_INTEL, \ - .x86_model = mod, \ - .x86_stepping = step, \ - .x86_microcode_rev = rev, \ +#define INTEL_CPU_DESC(model, stepping, revision) { \ + .x86_family = 6, \ + .x86_vendor = X86_VENDOR_INTEL, \ + .x86_model = (model), \ + .x86_stepping = (stepping), \ + .x86_microcode_rev = (revision), \ } +extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match); extern bool x86_cpu_has_min_microcode_rev(const struct x86_cpu_desc *table); -#endif +#endif /* _ASM_X86_CPU_DEVICE_ID */ From b29715966a2e13cc1dec22d3b50f3135db9591cd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 10 Feb 2019 22:26:53 -0800 Subject: [PATCH 1526/4277] Documentation: dmaengine: fix dmatest.rst warning Fix markup warning: insert a blank line before the hint. Documentation/driver-api/dmaengine/dmatest.rst:63: WARNING: Unexpected indentation. Signed-off-by: Randy Dunlap Acked-by: Andy Shevchenko Signed-off-by: Vinod Koul --- Documentation/driver-api/dmaengine/dmatest.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/driver-api/dmaengine/dmatest.rst b/Documentation/driver-api/dmaengine/dmatest.rst index 8d81f1a7169b..e78d070bb468 100644 --- a/Documentation/driver-api/dmaengine/dmatest.rst +++ b/Documentation/driver-api/dmaengine/dmatest.rst @@ -59,6 +59,7 @@ parameter, that specific channel is requested using the dmaengine and a thread is created with the existing parameters. This thread is set as pending and will be executed once run is set to 1. Any parameters set after the thread is created are not applied. + .. hint:: available channel list could be extracted by running the following command:: From 85ed4f43db866fe1ea90bcca8cd1e5e628f04a6f Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Fri, 8 Feb 2019 16:30:38 +0100 Subject: [PATCH 1527/4277] Documentation :dmaengine: clarify DMA desc. pointer after submission It clarifies that the DMA description pointer returned by `dmaengine_prep_*` function should not be used after submission. Signed-off-by: Federico Vaga Signed-off-by: Vinod Koul --- Documentation/driver-api/dmaengine/client.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst index fbbb2831f29f..45953f171500 100644 --- a/Documentation/driver-api/dmaengine/client.rst +++ b/Documentation/driver-api/dmaengine/client.rst @@ -168,6 +168,13 @@ The details of these operations are: dmaengine_submit() will not start the DMA operation, it merely adds it to the pending queue. For this, see step 5, dma_async_issue_pending. + .. note:: + + After calling ``dmaengine_submit()`` the submitted transfer descriptor + (``struct dma_async_tx_descriptor``) belongs to the DMA engine. + Consequently, the client must consider invalid the pointer to that + descriptor. + 5. Issue pending DMA requests and wait for callback notification The transactions in the pending queue can be activated by calling the From cce8e04cf79e47809455215744685e8eb56f94bb Mon Sep 17 00:00:00 2001 From: Paul Selles Date: Thu, 6 Dec 2018 21:30:50 +0800 Subject: [PATCH 1528/4277] ntb_hw_switchtec: debug print 64bit aligned crosslink BAR Numbers Switchtec NTB crosslink BARs are 64bit addressed but they are printed as 32bit addressed BARs. Fix debug log to increment the BAR numbers by 2 to reflect the 64bit address alignment. Fixes: 017525018202 ("ntb_hw_switchtec: Add initialization code for crosslink") Signed-off-by: Paul Selles Signed-off-by: Wesley Sheng Reviewed-by: Logan Gunthorpe Signed-off-by: Jon Mason --- drivers/ntb/hw/mscc/ntb_hw_switchtec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index 5ee5f40b4dfc..9916bc5b6759 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -1120,7 +1120,7 @@ static int crosslink_enum_partition(struct switchtec_ntb *sndev, dev_dbg(&sndev->stdev->dev, "Crosslink BAR%d addr: %llx\n", - i, bar_addr); + i*2, bar_addr); if (bar_addr != bar_space * i) continue; From d123fab71f63aae129aebe052664fda73131921a Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Thu, 6 Dec 2018 21:30:51 +0800 Subject: [PATCH 1529/4277] ntb_hw_switchtec: NT req id mapping table register entry number should be 512 The number of available NT req id mapping table entries per NTB control register is 512. The driver mistakenly limits the number to 256. Fix the array size of NT req id mapping table. Fixes: c082b04c9d40 ("NTB: switchtec: Add NTB hardware register definitions") Signed-off-by: Wesley Sheng Reviewed-by: Logan Gunthorpe Signed-off-by: Jon Mason --- include/linux/switchtec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index eee0412bdf4b..32b282cd0ead 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -249,8 +249,8 @@ struct ntb_ctrl_regs { u64 xlate_addr; } bar_entry[6]; u32 reserved2[216]; - u32 req_id_table[256]; - u32 reserved3[512]; + u32 req_id_table[512]; + u32 reserved3[256]; u64 lut_entry[512]; } __packed; From a2585cdc9e4cda6afaea5f5687eaabce3bebbb2c Mon Sep 17 00:00:00 2001 From: Paul Selles Date: Thu, 6 Dec 2018 21:30:52 +0800 Subject: [PATCH 1530/4277] ntb_hw_switchtec: Added support of >=4G memory windows Current Switchtec's BAR setup registers are limited to 32bits, corresponding to the maximum MW (memory window) size is <4G. Increase the MW sizes with the addition of the BAR Setup Extension Register for the upper 32bits of a 64bits MW size. This increases the MW range to between 4K and 2^63. Reported-by: Boris Glimcher Signed-off-by: Paul Selles Signed-off-by: Wesley Sheng Reviewed-by: Logan Gunthorpe Signed-off-by: Jon Mason --- drivers/ntb/hw/mscc/ntb_hw_switchtec.c | 9 +++++++-- include/linux/switchtec.h | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index 9916bc5b6759..f6f00354047b 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -264,6 +264,7 @@ static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx) ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN; iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); iowrite32(0, &ctl->bar_entry[bar].win_size); + iowrite32(0, &ctl->bar_ext_entry[bar].win_size); iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr); } @@ -286,7 +287,9 @@ static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx, ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); - iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); + iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000), + &ctl->bar_entry[bar].win_size); + iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size); iowrite64(sndev->self_partition | addr, &ctl->bar_entry[bar].xlate_addr); } @@ -1053,7 +1056,9 @@ static int crosslink_setup_mws(struct switchtec_ntb *sndev, int ntb_lut_idx, ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); - iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); + iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000), + &ctl->bar_entry[bar].win_size); + iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size); iowrite64(sndev->peer_partition | addr, &ctl->bar_entry[bar].xlate_addr); } diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 32b282cd0ead..52a079b3a9a6 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -248,7 +248,11 @@ struct ntb_ctrl_regs { u32 win_size; u64 xlate_addr; } bar_entry[6]; - u32 reserved2[216]; + struct { + u32 win_size; + u32 reserved[3]; + } bar_ext_entry[6]; + u32 reserved2[192]; u32 req_id_table[512]; u32 reserved3[256]; u64 lut_entry[512]; From 1ecd8c9cb899ae277e6986ae134635cb1a50f5de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 24 Jan 2019 13:04:52 +0100 Subject: [PATCH 1531/4277] pinctrl: sh-pfc: emev2: Add missing pinmux functions The err_rst_reqb, ext_clki, lowpwr, and ref_clko pin groups are present, but no pinmux functions refer to them, hence they can not be selected. Fixes: 1e7d5d849cf4f0c5 ("sh-pfc: Add emev2 pinmux support") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-emev2.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/pfc-emev2.c b/drivers/pinctrl/sh-pfc/pfc-emev2.c index dc271c3243df..310c6f3ee7cc 100644 --- a/drivers/pinctrl/sh-pfc/pfc-emev2.c +++ b/drivers/pinctrl/sh-pfc/pfc-emev2.c @@ -1260,6 +1260,14 @@ static const char * const dtv_groups[] = { "dtv_b", }; +static const char * const err_rst_reqb_groups[] = { + "err_rst_reqb", +}; + +static const char * const ext_clki_groups[] = { + "ext_clki", +}; + static const char * const iic0_groups[] = { "iic0", }; @@ -1282,6 +1290,10 @@ static const char * const lcd_groups[] = { "yuv3", }; +static const char * const lowpwr_groups[] = { + "lowpwr", +}; + static const char * const ntsc_groups[] = { "ntsc_clk", "ntsc_data", @@ -1295,6 +1307,10 @@ static const char * const pwm1_groups[] = { "pwm1", }; +static const char * const ref_clko_groups[] = { + "ref_clko", +}; + static const char * const sd_groups[] = { "sd_cki", }; @@ -1388,13 +1404,17 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(cam), SH_PFC_FUNCTION(cf), SH_PFC_FUNCTION(dtv), + SH_PFC_FUNCTION(err_rst_reqb), + SH_PFC_FUNCTION(ext_clki), SH_PFC_FUNCTION(iic0), SH_PFC_FUNCTION(iic1), SH_PFC_FUNCTION(jtag), SH_PFC_FUNCTION(lcd), + SH_PFC_FUNCTION(lowpwr), SH_PFC_FUNCTION(ntsc), SH_PFC_FUNCTION(pwm0), SH_PFC_FUNCTION(pwm1), + SH_PFC_FUNCTION(ref_clko), SH_PFC_FUNCTION(sd), SH_PFC_FUNCTION(sdi0), SH_PFC_FUNCTION(sdi1), From a4b0350047f1b10207e25e72d7cd3f7826e93769 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 23 Jan 2019 17:07:43 +0100 Subject: [PATCH 1532/4277] pinctrl: sh-pfc: r8a7791: Fix scifb2_data_c pin group The entry for "scifb2_data_c" in the SCIFB2 pin group array contains a typo, thus the group cannot be selected. Fixes: 5088451962389924 ("pinctrl: sh-pfc: r8a7791 PFC support") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a7791.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c index bd363f2e196f..d8b13d4e9bbf 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -5236,7 +5236,7 @@ static const char * const scifb2_groups[] = { "scifb2_data_b", "scifb2_clk_b", "scifb2_ctrl_b", - "scifb0_data_c", + "scifb2_data_c", "scifb2_clk_c", "scifb2_data_d", }; From b9fd50488b4939ce5b3a026d29e752e17c2d1800 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 23 Jan 2019 17:14:07 +0100 Subject: [PATCH 1533/4277] pinctrl: sh-pfc: r8a7792: Fix vin1_data18_b pin group The vin1_data18_b pin group itself is present, but it is not listed in the VIN1 pin group array, and thus cannot be selected. Fixes: 7dd74bb1f058786e ("pinctrl: sh-pfc: r8a7792: Add VIN pin groups") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-r8a7792.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c index a623459b234e..d36da5652de6 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c @@ -1913,6 +1913,7 @@ static const char * const vin1_groups[] = { "vin1_data8", "vin1_data24_b", "vin1_data20_b", + "vin1_data18_b", "vin1_data16_b", "vin1_sync", "vin1_field", From 0e6e448bdcf896d001a289a6112a704542d51516 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 23 Jan 2019 16:51:21 +0100 Subject: [PATCH 1534/4277] pinctrl: sh-pfc: sh73a0: Fix fsic_spdif pin groups There are two pin groups for the FSIC SPDIF signal, but the FSIC pin group array lists only one, and it refers to a nonexistent group. Fixes: 2ecd4154c906b7d6 ("sh-pfc: sh73a0: Add FSI pin groups and functions") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/pfc-sh73a0.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c index 085770e66895..ef3da8bf1d87 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c @@ -3354,7 +3354,8 @@ static const char * const fsic_groups[] = { "fsic_sclk_out", "fsic_data_in", "fsic_data_out", - "fsic_spdif", + "fsic_spdif_0", + "fsic_spdif_1", }; static const char * const fsid_groups[] = { From 5e8588c86d71e78de9e97103324d9127063f18d0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Jan 2019 11:56:05 +0100 Subject: [PATCH 1535/4277] pinctrl: sh-pfc: Validate fixed-size field widths at build time Add a build-time check, to ensure the register and field widths in descriptors for config registers with fixed-width fields are sane. This helps catching bugs early. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/pinctrl/sh-pfc/sh_pfc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h index 46d477ff5109..56016cb76769 100644 --- a/drivers/pinctrl/sh-pfc/sh_pfc.h +++ b/drivers/pinctrl/sh-pfc/sh_pfc.h @@ -126,7 +126,8 @@ struct pinmux_cfg_reg { * one for each possible combination of the register field bit values. */ #define PINMUX_CFG_REG(name, r, r_width, f_width) \ - .reg = r, .reg_width = r_width, .field_width = f_width, \ + .reg = r, .reg_width = r_width, \ + .field_width = f_width + BUILD_BUG_ON_ZERO(r_width % f_width), \ .enum_ids = (const u16 [(r_width / f_width) * (1 << f_width)]) /* From 729257d674bc2e68bf051af5c7202d882bccafe0 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Sat, 9 Feb 2019 20:01:52 +0900 Subject: [PATCH 1536/4277] pinctrl: sh-pfc: r8a77965: Add TMU pins, groups and functions This patch adds TMU TCLK{1,2} pins, groups and functions to the R8A77965 SoC. Signed-off-by: Takeshi Kihara Signed-off-by: Yoshihiro Kaneko Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/sh-pfc/pfc-r8a77965.c | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c index d937b1bc396a..7899ffdda748 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c @@ -3760,6 +3760,42 @@ static const unsigned int ssi9_ctrl_b_mux[] = { SSI_SCK9_B_MARK, SSI_WS9_B_MARK, }; +/* - TMU -------------------------------------------------------------------- */ +static const unsigned int tmu_tclk1_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(6, 23), +}; + +static const unsigned int tmu_tclk1_a_mux[] = { + TCLK1_A_MARK, +}; + +static const unsigned int tmu_tclk1_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(5, 19), +}; + +static const unsigned int tmu_tclk1_b_mux[] = { + TCLK1_B_MARK, +}; + +static const unsigned int tmu_tclk2_a_pins[] = { + /* TCLK */ + RCAR_GP_PIN(6, 19), +}; + +static const unsigned int tmu_tclk2_a_mux[] = { + TCLK2_A_MARK, +}; + +static const unsigned int tmu_tclk2_b_pins[] = { + /* TCLK */ + RCAR_GP_PIN(6, 28), +}; + +static const unsigned int tmu_tclk2_b_mux[] = { + TCLK2_B_MARK, +}; /* - USB0 ------------------------------------------------------------------- */ static const unsigned int usb0_pins[] = { @@ -4280,6 +4316,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(ssi9_data_b), SH_PFC_PIN_GROUP(ssi9_ctrl_a), SH_PFC_PIN_GROUP(ssi9_ctrl_b), + SH_PFC_PIN_GROUP(tmu_tclk1_a), + SH_PFC_PIN_GROUP(tmu_tclk1_b), + SH_PFC_PIN_GROUP(tmu_tclk2_a), + SH_PFC_PIN_GROUP(tmu_tclk2_b), SH_PFC_PIN_GROUP(usb0), SH_PFC_PIN_GROUP(usb1), SH_PFC_PIN_GROUP(usb30), @@ -4711,6 +4751,13 @@ static const char * const ssi_groups[] = { "ssi9_ctrl_b", }; +static const char * const tmu_groups[] = { + "tmu_tclk1_a", + "tmu_tclk1_b", + "tmu_tclk2_a", + "tmu_tclk2_b", +}; + static const char * const usb0_groups[] = { "usb0", }; @@ -4797,6 +4844,7 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(sdhi2), SH_PFC_FUNCTION(sdhi3), SH_PFC_FUNCTION(ssi), + SH_PFC_FUNCTION(tmu), SH_PFC_FUNCTION(usb0), SH_PFC_FUNCTION(usb1), SH_PFC_FUNCTION(usb30), From 79dbbdbeccc6784d5e189710adcca750c03d3bd4 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Sat, 9 Feb 2019 20:02:32 +0900 Subject: [PATCH 1537/4277] pinctrl: sh-pfc: r8a77965: Add DRIF pins, groups and functions This patch adds DRIF{0,1,2,3} pins, groups and functions to the R8A77965 SoC. Based on a similar patch of the R8A7796 PFC driver by Ramesh Shanmugasundaram . Signed-off-by: Takeshi Kihara Signed-off-by: Yoshihiro Kaneko Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/sh-pfc/pfc-r8a77965.c | 350 ++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c index 7899ffdda748..14c4b671cddf 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c @@ -1850,6 +1850,280 @@ static const unsigned int canfd1_data_mux[] = { CANFD1_TX_MARK, CANFD1_RX_MARK, }; +/* - DRIF0 --------------------------------------------------------------- */ +static const unsigned int drif0_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9), +}; + +static const unsigned int drif0_ctrl_a_mux[] = { + RIF0_CLK_A_MARK, RIF0_SYNC_A_MARK, +}; + +static const unsigned int drif0_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 10), +}; + +static const unsigned int drif0_data0_a_mux[] = { + RIF0_D0_A_MARK, +}; + +static const unsigned int drif0_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 7), +}; + +static const unsigned int drif0_data1_a_mux[] = { + RIF0_D1_A_MARK, +}; + +static const unsigned int drif0_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 0), RCAR_GP_PIN(5, 4), +}; + +static const unsigned int drif0_ctrl_b_mux[] = { + RIF0_CLK_B_MARK, RIF0_SYNC_B_MARK, +}; + +static const unsigned int drif0_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 1), +}; + +static const unsigned int drif0_data0_b_mux[] = { + RIF0_D0_B_MARK, +}; + +static const unsigned int drif0_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 2), +}; + +static const unsigned int drif0_data1_b_mux[] = { + RIF0_D1_B_MARK, +}; + +static const unsigned int drif0_ctrl_c_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 12), RCAR_GP_PIN(5, 15), +}; + +static const unsigned int drif0_ctrl_c_mux[] = { + RIF0_CLK_C_MARK, RIF0_SYNC_C_MARK, +}; + +static const unsigned int drif0_data0_c_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 13), +}; + +static const unsigned int drif0_data0_c_mux[] = { + RIF0_D0_C_MARK, +}; + +static const unsigned int drif0_data1_c_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 14), +}; + +static const unsigned int drif0_data1_c_mux[] = { + RIF0_D1_C_MARK, +}; + +/* - DRIF1 --------------------------------------------------------------- */ +static const unsigned int drif1_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18), +}; + +static const unsigned int drif1_ctrl_a_mux[] = { + RIF1_CLK_A_MARK, RIF1_SYNC_A_MARK, +}; + +static const unsigned int drif1_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 19), +}; + +static const unsigned int drif1_data0_a_mux[] = { + RIF1_D0_A_MARK, +}; + +static const unsigned int drif1_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 20), +}; + +static const unsigned int drif1_data1_a_mux[] = { + RIF1_D1_A_MARK, +}; + +static const unsigned int drif1_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 3), +}; + +static const unsigned int drif1_ctrl_b_mux[] = { + RIF1_CLK_B_MARK, RIF1_SYNC_B_MARK, +}; + +static const unsigned int drif1_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 7), +}; + +static const unsigned int drif1_data0_b_mux[] = { + RIF1_D0_B_MARK, +}; + +static const unsigned int drif1_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 8), +}; + +static const unsigned int drif1_data1_b_mux[] = { + RIF1_D1_B_MARK, +}; + +static const unsigned int drif1_ctrl_c_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 11), +}; + +static const unsigned int drif1_ctrl_c_mux[] = { + RIF1_CLK_C_MARK, RIF1_SYNC_C_MARK, +}; + +static const unsigned int drif1_data0_c_pins[] = { + /* D0 */ + RCAR_GP_PIN(5, 6), +}; + +static const unsigned int drif1_data0_c_mux[] = { + RIF1_D0_C_MARK, +}; + +static const unsigned int drif1_data1_c_pins[] = { + /* D1 */ + RCAR_GP_PIN(5, 10), +}; + +static const unsigned int drif1_data1_c_mux[] = { + RIF1_D1_C_MARK, +}; + +/* - DRIF2 --------------------------------------------------------------- */ +static const unsigned int drif2_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9), +}; + +static const unsigned int drif2_ctrl_a_mux[] = { + RIF2_CLK_A_MARK, RIF2_SYNC_A_MARK, +}; + +static const unsigned int drif2_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 7), +}; + +static const unsigned int drif2_data0_a_mux[] = { + RIF2_D0_A_MARK, +}; + +static const unsigned int drif2_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 10), +}; + +static const unsigned int drif2_data1_a_mux[] = { + RIF2_D1_A_MARK, +}; + +static const unsigned int drif2_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 26), RCAR_GP_PIN(6, 27), +}; + +static const unsigned int drif2_ctrl_b_mux[] = { + RIF2_CLK_B_MARK, RIF2_SYNC_B_MARK, +}; + +static const unsigned int drif2_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 30), +}; + +static const unsigned int drif2_data0_b_mux[] = { + RIF2_D0_B_MARK, +}; + +static const unsigned int drif2_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 31), +}; + +static const unsigned int drif2_data1_b_mux[] = { + RIF2_D1_B_MARK, +}; + +/* - DRIF3 --------------------------------------------------------------- */ +static const unsigned int drif3_ctrl_a_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18), +}; + +static const unsigned int drif3_ctrl_a_mux[] = { + RIF3_CLK_A_MARK, RIF3_SYNC_A_MARK, +}; + +static const unsigned int drif3_data0_a_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 19), +}; + +static const unsigned int drif3_data0_a_mux[] = { + RIF3_D0_A_MARK, +}; + +static const unsigned int drif3_data1_a_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 20), +}; + +static const unsigned int drif3_data1_a_mux[] = { + RIF3_D1_A_MARK, +}; + +static const unsigned int drif3_ctrl_b_pins[] = { + /* CLK, SYNC */ + RCAR_GP_PIN(6, 24), RCAR_GP_PIN(6, 25), +}; + +static const unsigned int drif3_ctrl_b_mux[] = { + RIF3_CLK_B_MARK, RIF3_SYNC_B_MARK, +}; + +static const unsigned int drif3_data0_b_pins[] = { + /* D0 */ + RCAR_GP_PIN(6, 28), +}; + +static const unsigned int drif3_data0_b_mux[] = { + RIF3_D0_B_MARK, +}; + +static const unsigned int drif3_data1_b_pins[] = { + /* D1 */ + RCAR_GP_PIN(6, 29), +}; + +static const unsigned int drif3_data1_b_mux[] = { + RIF3_D1_B_MARK, +}; + /* - DU --------------------------------------------------------------------- */ static const unsigned int du_rgb666_pins[] = { /* R[7:2], G[7:2], B[7:2] */ @@ -4073,6 +4347,36 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(canfd0_data_a), SH_PFC_PIN_GROUP(canfd0_data_b), SH_PFC_PIN_GROUP(canfd1_data), + SH_PFC_PIN_GROUP(drif0_ctrl_a), + SH_PFC_PIN_GROUP(drif0_data0_a), + SH_PFC_PIN_GROUP(drif0_data1_a), + SH_PFC_PIN_GROUP(drif0_ctrl_b), + SH_PFC_PIN_GROUP(drif0_data0_b), + SH_PFC_PIN_GROUP(drif0_data1_b), + SH_PFC_PIN_GROUP(drif0_ctrl_c), + SH_PFC_PIN_GROUP(drif0_data0_c), + SH_PFC_PIN_GROUP(drif0_data1_c), + SH_PFC_PIN_GROUP(drif1_ctrl_a), + SH_PFC_PIN_GROUP(drif1_data0_a), + SH_PFC_PIN_GROUP(drif1_data1_a), + SH_PFC_PIN_GROUP(drif1_ctrl_b), + SH_PFC_PIN_GROUP(drif1_data0_b), + SH_PFC_PIN_GROUP(drif1_data1_b), + SH_PFC_PIN_GROUP(drif1_ctrl_c), + SH_PFC_PIN_GROUP(drif1_data0_c), + SH_PFC_PIN_GROUP(drif1_data1_c), + SH_PFC_PIN_GROUP(drif2_ctrl_a), + SH_PFC_PIN_GROUP(drif2_data0_a), + SH_PFC_PIN_GROUP(drif2_data1_a), + SH_PFC_PIN_GROUP(drif2_ctrl_b), + SH_PFC_PIN_GROUP(drif2_data0_b), + SH_PFC_PIN_GROUP(drif2_data1_b), + SH_PFC_PIN_GROUP(drif3_ctrl_a), + SH_PFC_PIN_GROUP(drif3_data0_a), + SH_PFC_PIN_GROUP(drif3_data1_a), + SH_PFC_PIN_GROUP(drif3_ctrl_b), + SH_PFC_PIN_GROUP(drif3_data0_b), + SH_PFC_PIN_GROUP(drif3_data1_b), SH_PFC_PIN_GROUP(du_rgb666), SH_PFC_PIN_GROUP(du_rgb888), SH_PFC_PIN_GROUP(du_clk_out_0), @@ -4407,6 +4711,48 @@ static const char * const canfd1_groups[] = { "canfd1_data", }; +static const char * const drif0_groups[] = { + "drif0_ctrl_a", + "drif0_data0_a", + "drif0_data1_a", + "drif0_ctrl_b", + "drif0_data0_b", + "drif0_data1_b", + "drif0_ctrl_c", + "drif0_data0_c", + "drif0_data1_c", +}; + +static const char * const drif1_groups[] = { + "drif1_ctrl_a", + "drif1_data0_a", + "drif1_data1_a", + "drif1_ctrl_b", + "drif1_data0_b", + "drif1_data1_b", + "drif1_ctrl_c", + "drif1_data0_c", + "drif1_data1_c", +}; + +static const char * const drif2_groups[] = { + "drif2_ctrl_a", + "drif2_data0_a", + "drif2_data1_a", + "drif2_ctrl_b", + "drif2_data0_b", + "drif2_data1_b", +}; + +static const char * const drif3_groups[] = { + "drif3_ctrl_a", + "drif3_data0_a", + "drif3_data1_a", + "drif3_ctrl_b", + "drif3_data0_b", + "drif3_data1_b", +}; + static const char * const du_groups[] = { "du_rgb666", "du_rgb888", @@ -4810,6 +5156,10 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(can_clk), SH_PFC_FUNCTION(canfd0), SH_PFC_FUNCTION(canfd1), + SH_PFC_FUNCTION(drif0), + SH_PFC_FUNCTION(drif1), + SH_PFC_FUNCTION(drif2), + SH_PFC_FUNCTION(drif3), SH_PFC_FUNCTION(du), SH_PFC_FUNCTION(hscif0), SH_PFC_FUNCTION(hscif1), From 9143595a7e05a848384c240d34abcc4740a65897 Mon Sep 17 00:00:00 2001 From: Joey Zhang Date: Mon, 7 Jan 2019 11:12:56 +0800 Subject: [PATCH 1538/4277] NTB: ntb_transport: Free MWs in ntb_transport_link_cleanup() If NTB peer host crashes or reboots, the NTB transport link will be down and the MWs of NTB transport will be invalid. But the ntb_transport_link_cleanup() does not free these invalid MWs. When the NTB peer host is recovered later, NTB transport link will be up and the ntb_set_mw() will not reset up MWs. Because the MWs of NTB transport are invalid, the NTB transport will not work. We can fix it by freeing MWs when NTB transport link is down, then the ntb_set_mw() will reset up MWs when NTB transport link is up. Signed-off-by: Joey Zhang Reviewed-by: Logan Gunthorpe Signed-off-by: Jon Mason --- drivers/ntb/ntb_transport.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 3bfdb4562408..6e8902d03a69 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -862,6 +862,9 @@ static void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt) if (!nt->link_is_up) cancel_delayed_work_sync(&nt->link_work); + for (i = 0; i < nt->mw_count; i++) + ntb_free_mw(nt, i); + /* The scratchpad registers keep the values if the remote side * goes down, blast them now to give them a sane value the next * time they are accessed From c59666bb32b91da84b1d3df0e88789de9e827f61 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Fri, 18 Jan 2019 17:10:01 -0700 Subject: [PATCH 1539/4277] NTB: ntb_transport: Ensure the destination buffer is mapped for TX DMA Presently, when ntb_transport is used with DMA and the IOMMU turned on, it fails with errors from the IOMMU such as: DMAR: DRHD: handling fault status reg 202 DMAR: [DMA Write] Request device [00:04.0] fault addr 381fc0340000 [fault reason 05] PTE Write access is not set This is because ntb_transport does not map the BAR space with the IOMMU. To fix this, we map the entire MW region for each QP after we assign the DMA channel. This prevents needing an extra DMA map in the fast path. Link: https://lore.kernel.org/linux-pci/499934e7-3734-1aee-37dd-b42a5d2a2608@intel.com/ Signed-off-by: Logan Gunthorpe Reviewed-by: Dave Jiang Signed-off-by: Jon Mason --- drivers/ntb/ntb_transport.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 6e8902d03a69..d4f39ba1d976 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -144,7 +144,9 @@ struct ntb_transport_qp { struct list_head tx_free_q; spinlock_t ntb_tx_free_q_lock; void __iomem *tx_mw; - dma_addr_t tx_mw_phys; + phys_addr_t tx_mw_phys; + size_t tx_mw_size; + dma_addr_t tx_mw_dma_addr; unsigned int tx_index; unsigned int tx_max_entry; unsigned int tx_max_frame; @@ -1052,6 +1054,7 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, tx_size = (unsigned int)mw_size / num_qps_mw; qp_offset = tx_size * (qp_num / mw_count); + qp->tx_mw_size = tx_size; qp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset; if (!qp->tx_mw) return -EINVAL; @@ -1647,7 +1650,7 @@ static int ntb_async_tx_submit(struct ntb_transport_qp *qp, dma_cookie_t cookie; device = chan->device; - dest = qp->tx_mw_phys + qp->tx_max_frame * entry->tx_index; + dest = qp->tx_mw_dma_addr + qp->tx_max_frame * entry->tx_index; buff_off = (size_t)buf & ~PAGE_MASK; dest_off = (size_t)dest & ~PAGE_MASK; @@ -1866,6 +1869,18 @@ ntb_transport_create_queue(void *data, struct device *client_dev, qp->rx_dma_chan = NULL; } + if (qp->tx_dma_chan) { + qp->tx_mw_dma_addr = + dma_map_resource(qp->tx_dma_chan->device->dev, + qp->tx_mw_phys, qp->tx_mw_size, + DMA_FROM_DEVICE, 0); + if (dma_mapping_error(qp->tx_dma_chan->device->dev, + qp->tx_mw_dma_addr)) { + qp->tx_mw_dma_addr = 0; + goto err1; + } + } + dev_dbg(&pdev->dev, "Using %s memcpy for TX\n", qp->tx_dma_chan ? "DMA" : "CPU"); @@ -1907,6 +1922,10 @@ err1: qp->rx_alloc_entry = 0; while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) kfree(entry); + if (qp->tx_mw_dma_addr) + dma_unmap_resource(qp->tx_dma_chan->device->dev, + qp->tx_mw_dma_addr, qp->tx_mw_size, + DMA_FROM_DEVICE, 0); if (qp->tx_dma_chan) dma_release_channel(qp->tx_dma_chan); if (qp->rx_dma_chan) @@ -1948,6 +1967,11 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp) */ dma_sync_wait(chan, qp->last_cookie); dmaengine_terminate_all(chan); + + dma_unmap_resource(chan->device->dev, + qp->tx_mw_dma_addr, qp->tx_mw_size, + DMA_FROM_DEVICE, 0); + dma_release_channel(chan); } From 85b0cae89d5266e6a7abb2e83c6f716326fc494c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 Jan 2019 19:38:56 +0300 Subject: [PATCH 1540/4277] PCI: Blacklist power management of Gigabyte X299 DESIGNARE EX PCIe ports Gigabyte X299 DESIGNARE EX motherboard has one PCIe root port that is connected to an Alpine Ridge Thunderbolt controller. This port has slot implemented bit set in the config space but other than that it is not hotplug capable in the sense we are expecting in Linux (it has dev->is_hotplug_bridge set to 0): 00:1c.4 PCI bridge: Intel Corporation 200 Series PCH PCI Express Root Port #5 Bus: primary=00, secondary=05, subordinate=46, sec-latency=0 Memory behind bridge: 78000000-8fffffff [size=384M] Prefetchable memory behind bridge: 00003800f8000000-00003800ffffffff [size=128M] ... Capabilities: [40] Express (v2) Root Port (Slot+), MSI 00 ... SltCap: AttnBtn- PwrCtrl- MRL- AttnInd- PwrInd- HotPlug- Surprise- Slot #8, PowerLimit 25.000W; Interlock- NoCompl+ SltCtl: Enable: AttnBtn- PwrFlt- MRL- PresDet- CmdCplt- HPIrq- LinkChg- Control: AttnInd Unknown, PwrInd Unknown, Power- Interlock- SltSta: Status: AttnBtn- PowerFlt- MRL- CmdCplt- PresDet- Interlock- Changed: MRL- PresDet+ LinkState+ This system is using ACPI based hotplug to notify the OS that it needs to rescan the PCI bus (ACPI hotplug). If there is nothing connected in any of the Thunderbolt ports the root port will not have any runtime PM active children and is thus automatically runtime suspended pretty soon after boot by PCI PM core. Now, when a device is connected the BIOS SMI handler responsible for enumerating newly added devices is not able to find anything because the port is in D3. Prevent this from happening by blacklisting PCI power management of this particular Gigabyte system. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202031 Reported-by: Kedar A Dongre Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..0f29cd1d36d3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2501,6 +2501,25 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev) pm_runtime_put_sync(parent); } +static const struct dmi_system_id bridge_d3_blacklist[] = { +#ifdef CONFIG_X86 + { + /* + * Gigabyte X299 root port is not marked as hotplug capable + * which allows Linux to power manage it. However, this + * confuses the BIOS SMI handler so don't power manage root + * ports on that system. + */ + .ident = "X299 DESIGNARE EX-CF", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"), + }, + }, +#endif + { } +}; + /** * pci_bridge_d3_possible - Is it possible to put the bridge into D3 * @bridge: Bridge to check @@ -2546,6 +2565,9 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) if (bridge->is_hotplug_bridge) return false; + if (dmi_check_system(bridge_d3_blacklist)) + return false; + /* * It should be safe to put PCIe ports from 2015 or newer * to D3. From bf7fbeeae6db644ef5995085de2bc5c6121f8c8d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 8 Feb 2019 17:02:56 +0100 Subject: [PATCH 1541/4277] module: Cure the MODULE_LICENSE "GPL" vs. "GPL v2" bogosity The original MODULE_LICENSE string for kernel modules licensed under the GPL v2 (only / or later) was simply "GPL", which was - and still is - completely sufficient for the purpose of module loading and checking whether the module is free software or proprietary. In January 2003 this was changed with commit 3344ea3ad4b7 ("[PATCH] MODULE_LICENSE and EXPORT_SYMBOL_GPL support"). This commit can be found in the history git repository which holds the 1:1 import of Linus' bitkeeper repository: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/?id=3344ea3ad4b7c302c846a680dbaeedf96ed45c02 The main intention of the patch was to refuse linking proprietary modules against symbols exported with EXPORT_SYMBOL_GPL() at module load time. As a completely undocumented side effect it also introduced the distinction between "GPL" and "GPL v2" MODULE_LICENSE() strings: * "GPL" [GNU Public License v2 or later] * "GPL v2" [GNU Public License v2] * "GPL and additional rights" [GNU Public License v2 rights and more] * "Dual BSD/GPL" [GNU Public License v2 * or BSD license choice] * "Dual MPL/GPL" [GNU Public License v2 * or Mozilla license choice] This distinction was and still is wrong in several aspects: 1) It broke all modules which were using the "GPL" string in the MODULE_LICENSE() already and were licensed under GPL v2 only. A quick license scan over the tree at that time shows that at least 480 out of 1484 modules have been affected by this change back then. The number is probably way higher as this was just a quick check for clearly identifiable license information. There was exactly ONE instance of a "GPL v2" module license string in the kernel back then - drivers/net/tulip/xircom_tulip_cb.c which otherwise had no license information at all. There is no indication that the change above is any way related to this driver. The change happend with the 2.4.11 release which was on Oct. 9 2001 - so quite some time before the above commit. Unfortunately there is no trace on the intertubes to any discussion of this. 2) The dual licensed strings became ill defined as well because following the "GPL" vs. "GPL v2" distinction all dual licensed (or additional rights) MODULE_LICENSE strings would either require those dual licensed modules to be licensed under GPL v2 or later or just be unspecified for the dual licensing case. Neither choice is coherent with the GPL distinction. Due to the lack of a proper changelog and no real discussion on the patch submission other than a few implementation details, it's completely unclear why this distinction was introduced at all. Other than the comment in the module header file exists no documentation for this at all. From a license compliance and license scanning POV this distinction is a total nightmare. As of 5.0-rc2 2873 out of 9200 instances of MODULE_LICENSE() strings are conflicting with the actual license in the source code (either SPDX or license boilerplate/reference). A comparison between the scan of the history tree and a scan of current Linus tree shows to the extent that the git rename detection over Linus tree grafted with the history tree is halfways complete that almost none of the files which got broken in 2003 have been cleaned up vs. the MODULE_LICENSE string. So subtracting those 480 known instances from the conflicting 2800 of today more than 25% of the module authors got it wrong and it's a high propability that a large portion of the rest just got it right by chance. There is no value for the module loader to convey the detailed license information as the only decision to be made is whether the module is free software or not. The "and additional rights", "BSD" and "MPL" strings are not conclusive license information either. So there is no point in trying to make the GPL part conclusive and exact. As shown above it's already non conclusive for dual licensing and incoherent with a large portion of the module source. As an unintended side effect this distinction causes a major headache for license compliance, license scanners and the ongoing effort to clean up the license mess of the kernel. Therefore remove the well meant, but ill defined, distinction between "GPL" and "GPL v2" and document that: - "GPL" and "GPL v2" both express that the module is licensed under GPLv2 (without a distinction of 'only' and 'or later') and is therefore kernel license compliant. - None of the MODULE_LICENSE strings can be used for expressing or determining the exact license - Their sole purpose is to decide whether the module is free software or not. Add a MODULE_LICENSE subsection to the license rule documentation as well. Signed-off-by: Thomas Gleixner Reviewed-by: Greg Kroah-Hartman Acked-by: Philippe Ombredanne Acked-by: Joe Perches [jc: Did s/merily/merely/ ] Acked-by: Jessica Yu Signed-off-by: Jonathan Corbet --- Documentation/process/license-rules.rst | 62 +++++++++++++++++++++++++ include/linux/module.h | 18 ++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/Documentation/process/license-rules.rst b/Documentation/process/license-rules.rst index 2bb8c0fc2238..43b6a1ee0193 100644 --- a/Documentation/process/license-rules.rst +++ b/Documentation/process/license-rules.rst @@ -372,3 +372,65 @@ in the LICENSE subdirectories. This is required to allow tool verification (e.g. checkpatch.pl) and to have the licenses ready to read and extract right from the source, which is recommended by various FOSS organizations, e.g. the `FSFE REUSE initiative `_. + +_`MODULE_LICENSE` +----------------- + + Loadable kernel modules also require a MODULE_LICENSE() tag. This tag is + neither a replacement for proper source code license information + (SPDX-License-Identifier) nor in any way relevant for expressing or + determining the exact license under which the source code of the module + is provided. + + The sole purpose of this tag is to provide sufficient information + whether the module is free software or proprietary for the kernel + module loader and for user space tools. + + The valid license strings for MODULE_LICENSE() are: + + ============================= ============================================= + "GPL" Module is licensed under GPL version 2. This + does not express any distinction between + GPL-2.0-only or GPL-2.0-or-later. The exact + license information can only be determined + via the license information in the + corresponding source files. + + "GPL v2" Same as "GPL". It exists for historic + reasons. + + "GPL and additional rights" Historical variant of expressing that the + module source is dual licensed under a + GPL v2 variant and MIT license. Please do + not use in new code. + + "Dual MIT/GPL" The correct way of expressing that the + module is dual licensed under a GPL v2 + variant or MIT license choice. + + "Dual BSD/GPL" The module is dual licensed under a GPL v2 + variant or BSD license choice. The exact + variant of the BSD license can only be + determined via the license information + in the corresponding source files. + + "Dual MPL/GPL" The module is dual licensed under a GPL v2 + variant or Mozilla Public License (MPL) + choice. The exact variant of the MPL + license can only be determined via the + license information in the corresponding + source files. + + "Proprietary" The module is under a proprietary license. + This string is solely for proprietary third + party modules and cannot be used for modules + which have their source code in the kernel + tree. Modules tagged that way are tainting + the kernel with the 'P' flag when loaded and + the kernel module loader refuses to link such + modules against symbols which are exported + with EXPORT_SYMBOL_GPL(). + ============================= ============================================= + + + diff --git a/include/linux/module.h b/include/linux/module.h index 9a21fe3509af..3a2402b8d790 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -172,7 +172,7 @@ extern void cleanup_module(void); * The following license idents are currently accepted as indicating free * software modules * - * "GPL" [GNU Public License v2 or later] + * "GPL" [GNU Public License v2] * "GPL v2" [GNU Public License v2] * "GPL and additional rights" [GNU Public License v2 rights and more] * "Dual BSD/GPL" [GNU Public License v2 @@ -186,6 +186,22 @@ extern void cleanup_module(void); * * "Proprietary" [Non free products] * + * Both "GPL v2" and "GPL" (the latter also in dual licensed strings) are + * merely stating that the module is licensed under the GPL v2, but are not + * telling whether "GPL v2 only" or "GPL v2 or later". The reason why there + * are two variants is a historic and failed attempt to convey more + * information in the MODULE_LICENSE string. For module loading the + * "only/or later" distinction is completely irrelevant and does neither + * replace the proper license identifiers in the corresponding source file + * nor amends them in any way. The sole purpose is to make the + * 'Proprietary' flagging work and to refuse to bind symbols which are + * exported with EXPORT_SYMBOL_GPL when a non free module is loaded. + * + * In the same way "BSD" is not a clear license information. It merely + * states, that the module is licensed under one of the compatible BSD + * license variants. The detailed and correct license information is again + * to be found in the corresponding source files. + * * There are dual licensed components, but when running with Linux it is the * GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL * is a GPL combined work. From f9324980d7300f961e9895ad94d5ea71c0fe187e Mon Sep 17 00:00:00 2001 From: Hans Holmberg Date: Mon, 11 Feb 2019 13:25:02 +0100 Subject: [PATCH 1542/4277] lightnvm: pblk: stop taking the free lock in in pblk_lines_free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pblk_line_meta_free might sleep (it can end up calling vfree, depending on how we allocate lba lists), and this can lead to a BUG() if we wake up on a different cpu and release the lock. As there is no point of grabbing the free lock when pblk has shut down, remove the lock. Signed-off-by: Hans Holmberg Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c index f9a3e47b6a93..eb0135c77805 100644 --- a/drivers/lightnvm/pblk-init.c +++ b/drivers/lightnvm/pblk-init.c @@ -584,14 +584,12 @@ static void pblk_lines_free(struct pblk *pblk) struct pblk_line *line; int i; - spin_lock(&l_mg->free_lock); for (i = 0; i < l_mg->nr_lines; i++) { line = &pblk->lines[i]; pblk_line_free(line); pblk_line_meta_free(l_mg, line); } - spin_unlock(&l_mg->free_lock); pblk_line_mg_free(pblk); From 6916cf5426d08ea8be50ab4ba7ff86ea022cdff3 Mon Sep 17 00:00:00 2001 From: Hans Holmberg Date: Mon, 11 Feb 2019 13:25:03 +0100 Subject: [PATCH 1543/4277] lightnvm: pblk: use vfree to free metadata on error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As chunk metadata is allocated using vmalloc, we need to free it using vfree. Fixes: 090ee26fd512 ("lightnvm: use internal allocation for chunk log page") Signed-off-by: Hans Holmberg Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c index 1ff165351180..1b5ff51faa63 100644 --- a/drivers/lightnvm/pblk-core.c +++ b/drivers/lightnvm/pblk-core.c @@ -141,7 +141,7 @@ struct nvm_chk_meta *pblk_get_chunk_meta(struct pblk *pblk) ret = nvm_get_chunk_meta(dev, ppa, geo->all_chunks, meta); if (ret) { - kfree(meta); + vfree(meta); return ERR_PTR(-EIO); } From e74ecf63ef9c3ff92bda531ed41a43ad4e75682e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 11 Feb 2019 13:25:04 +0100 Subject: [PATCH 1544/4277] lightnvm: Use u64 instead of __le64 for CPU visible side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sparse complains about using strict data types: drivers/lightnvm/pblk-read.c:254:43: warning: incorrect type in assignment (different base types) drivers/lightnvm/pblk-read.c:254:43: expected restricted __le64 drivers/lightnvm/pblk-read.c:254:43: got unsigned long long [unsigned] [usertype] drivers/lightnvm/pblk-read.c:255:29: warning: cast from restricted __le64 drivers/lightnvm/pblk-read.c:268:29: warning: cast from restricted __le64 drivers/lightnvm/pblk-read.c:328:41: warning: incorrect type in assignment (different base types) drivers/lightnvm/pblk-read.c:328:41: expected restricted __le64 drivers/lightnvm/pblk-read.c:328:41: got unsigned long long [unsigned] [usertype] In the code it seems explicit that lba_list_mem and lba_list_media members of struct pblk_pr_ctx are used on CPU side, which means they should not be of strict types. Change types of lba_list_mem and lba_list_media members to be u64. Signed-off-by: Andy Shevchenko Reviewed-by: Javier González Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index 85e38ed62f85..0dd697ea201e 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h @@ -131,8 +131,8 @@ struct pblk_pr_ctx { unsigned int bio_init_idx; void *ppa_ptr; dma_addr_t dma_ppa_list; - __le64 lba_list_mem[NVM_MAX_VLBA]; - __le64 lba_list_media[NVM_MAX_VLBA]; + u64 lba_list_mem[NVM_MAX_VLBA]; + u64 lba_list_media[NVM_MAX_VLBA]; }; /* Pad context */ From 7e0a0847ed7ea02f03dd1442136a0cd684d91218 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 11 Feb 2019 13:25:05 +0100 Subject: [PATCH 1545/4277] lightnvm: pblk: Switch to use new generic UUID API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are new types and helpers that are supposed to be used in new code. As a preparation to get rid of legacy types and API functions do the conversion here. Signed-off-by: Andy Shevchenko Reviewed-by: Javier González Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-core.c | 5 +++-- drivers/lightnvm/pblk-init.c | 2 +- drivers/lightnvm/pblk-recovery.c | 8 +++++--- drivers/lightnvm/pblk.h | 10 +--------- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c index 1b5ff51faa63..2a9e9facf44f 100644 --- a/drivers/lightnvm/pblk-core.c +++ b/drivers/lightnvm/pblk-core.c @@ -1065,7 +1065,7 @@ static int pblk_line_init_metadata(struct pblk *pblk, struct pblk_line *line, bitmap_set(line->lun_bitmap, 0, lm->lun_bitmap_len); smeta_buf->header.identifier = cpu_to_le32(PBLK_MAGIC); - memcpy(smeta_buf->header.uuid, pblk->instance_uuid, 16); + guid_copy((guid_t *)&smeta_buf->header.uuid, &pblk->instance_uuid); smeta_buf->header.id = cpu_to_le32(line->id); smeta_buf->header.type = cpu_to_le16(line->type); smeta_buf->header.version_major = SMETA_VERSION_MAJOR; @@ -1874,7 +1874,8 @@ void pblk_line_close_meta(struct pblk *pblk, struct pblk_line *line) if (le32_to_cpu(emeta_buf->header.identifier) != PBLK_MAGIC) { emeta_buf->header.identifier = cpu_to_le32(PBLK_MAGIC); - memcpy(emeta_buf->header.uuid, pblk->instance_uuid, 16); + guid_copy((guid_t *)&emeta_buf->header.uuid, + &pblk->instance_uuid); emeta_buf->header.id = cpu_to_le32(line->id); emeta_buf->header.type = cpu_to_le16(line->type); emeta_buf->header.version_major = EMETA_VERSION_MAJOR; diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c index eb0135c77805..8b643d0bffae 100644 --- a/drivers/lightnvm/pblk-init.c +++ b/drivers/lightnvm/pblk-init.c @@ -130,7 +130,7 @@ static int pblk_l2p_recover(struct pblk *pblk, bool factory_init) struct pblk_line *line = NULL; if (factory_init) { - pblk_setup_uuid(pblk); + guid_gen(&pblk->instance_uuid); } else { line = pblk_recov_l2p(pblk); if (IS_ERR(line)) { diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c index 5ee20da7bdb3..6761d2afa4d0 100644 --- a/drivers/lightnvm/pblk-recovery.c +++ b/drivers/lightnvm/pblk-recovery.c @@ -703,11 +703,13 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk) /* The first valid instance uuid is used for initialization */ if (!valid_uuid) { - memcpy(pblk->instance_uuid, smeta_buf->header.uuid, 16); + guid_copy(&pblk->instance_uuid, + (guid_t *)&smeta_buf->header.uuid); valid_uuid = 1; } - if (memcmp(pblk->instance_uuid, smeta_buf->header.uuid, 16)) { + if (!guid_equal(&pblk->instance_uuid, + (guid_t *)&smeta_buf->header.uuid)) { pblk_debug(pblk, "ignore line %u due to uuid mismatch\n", i); continue; @@ -737,7 +739,7 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk) } if (!found_lines) { - pblk_setup_uuid(pblk); + guid_gen(&pblk->instance_uuid); spin_lock(&l_mg->free_lock); WARN_ON_ONCE(!test_and_clear_bit(meta_line, diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index 0dd697ea201e..72ae8755764e 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h @@ -646,7 +646,7 @@ struct pblk { int sec_per_write; - unsigned char instance_uuid[16]; + guid_t instance_uuid; /* Persistent write amplification counters, 4kb sector I/Os */ atomic64_t user_wa; /* Sectors written by user */ @@ -1360,14 +1360,6 @@ static inline unsigned int pblk_get_secs(struct bio *bio) return bio->bi_iter.bi_size / PBLK_EXPOSED_PAGE_SIZE; } -static inline void pblk_setup_uuid(struct pblk *pblk) -{ - uuid_le uuid; - - uuid_le_gen(&uuid); - memcpy(pblk->instance_uuid, uuid.b, 16); -} - static inline char *pblk_disk_name(struct pblk *pblk) { struct gendisk *disk = pblk->disk; From b7fce8f79d947abbe84e577d89ded3c3d5345c23 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 Feb 2019 13:25:06 +0100 Subject: [PATCH 1546/4277] lightnvm: pblk: fix TRACE_INCLUDE_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the comment block in include/trace/define_trace.h says, TRACE_INCLUDE_PATH should be a relative path to the define_trace.h ../../drivers/lightnvm is the correct relative path. ../../../drivers/lightnvm is working by coincidence because the top Makefile adds -I$(srctree)/arch/$(SRCARCH)/include as a header search path, but we should not rely on it. Signed-off-by: Masahiro Yamada Reviewed-by: Hans Holmberg Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/lightnvm/pblk-trace.h b/drivers/lightnvm/pblk-trace.h index 679e5c458ca6..9534503b69d9 100644 --- a/drivers/lightnvm/pblk-trace.h +++ b/drivers/lightnvm/pblk-trace.h @@ -139,7 +139,7 @@ TRACE_EVENT(pblk_state, /* This part must be outside protection */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../../../drivers/lightnvm +#define TRACE_INCLUDE_PATH ../../drivers/lightnvm #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE pblk-trace #include From aa8759d80a755cb32f1707b360a265f3695770ef Mon Sep 17 00:00:00 2001 From: Hans Holmberg Date: Mon, 11 Feb 2019 13:25:07 +0100 Subject: [PATCH 1547/4277] lightnvm: pblk: extend line wp balance check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pblk stripes writes of minimal write size across all non-offline chunks in a line, which means that the maximum write pointer delta should not exceed the minimal write size. Extend the line write pointer balance check to cover this case, and ignore the offline chunk wps. This will render us a warning during recovery if something unexpected has happened to the chunk write pointers (i.e. powerloss, a spurious chunk reset, ..). Reported-by: Zhoujie Wu Tested-by: Zhoujie Wu Reviewed-by: Javier González Signed-off-by: Hans Holmberg Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-recovery.c | 56 ++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c index 6761d2afa4d0..d86f580036d3 100644 --- a/drivers/lightnvm/pblk-recovery.c +++ b/drivers/lightnvm/pblk-recovery.c @@ -302,35 +302,55 @@ static int pblk_pad_distance(struct pblk *pblk, struct pblk_line *line) return (distance > line->left_msecs) ? line->left_msecs : distance; } -static int pblk_line_wp_is_unbalanced(struct pblk *pblk, - struct pblk_line *line) +/* Return a chunk belonging to a line by stripe(write order) index */ +static struct nvm_chk_meta *pblk_get_stripe_chunk(struct pblk *pblk, + struct pblk_line *line, + int index) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; - struct pblk_line_meta *lm = &pblk->lm; struct pblk_lun *rlun; - struct nvm_chk_meta *chunk; struct ppa_addr ppa; - u64 line_wp; - int pos, i; + int pos; - rlun = &pblk->luns[0]; + rlun = &pblk->luns[index]; ppa = rlun->bppa; pos = pblk_ppa_to_pos(geo, ppa); - chunk = &line->chks[pos]; - line_wp = chunk->wp; + return &line->chks[pos]; +} - for (i = 1; i < lm->blk_per_line; i++) { - rlun = &pblk->luns[i]; - ppa = rlun->bppa; - pos = pblk_ppa_to_pos(geo, ppa); - chunk = &line->chks[pos]; +static int pblk_line_wps_are_unbalanced(struct pblk *pblk, + struct pblk_line *line) +{ + struct pblk_line_meta *lm = &pblk->lm; + int blk_in_line = lm->blk_per_line; + struct nvm_chk_meta *chunk; + u64 max_wp, min_wp; + int i; - if (chunk->wp > line_wp) + i = find_first_zero_bit(line->blk_bitmap, blk_in_line); + + /* If there is one or zero good chunks in the line, + * the write pointers can't be unbalanced. + */ + if (i >= (blk_in_line - 1)) + return 0; + + chunk = pblk_get_stripe_chunk(pblk, line, i); + max_wp = chunk->wp; + if (max_wp > pblk->max_write_pgs) + min_wp = max_wp - pblk->max_write_pgs; + else + min_wp = 0; + + i = find_next_zero_bit(line->blk_bitmap, blk_in_line, i + 1); + while (i < blk_in_line) { + chunk = pblk_get_stripe_chunk(pblk, line, i); + if (chunk->wp > max_wp || chunk->wp < min_wp) return 1; - else if (chunk->wp < line_wp) - line_wp = chunk->wp; + + i = find_next_zero_bit(line->blk_bitmap, blk_in_line, i + 1); } return 0; @@ -356,7 +376,7 @@ static int pblk_recov_scan_oob(struct pblk *pblk, struct pblk_line *line, int ret; u64 left_ppas = pblk_sec_in_open_line(pblk, line) - lm->smeta_sec; - if (pblk_line_wp_is_unbalanced(pblk, line)) + if (pblk_line_wps_are_unbalanced(pblk, line)) pblk_warn(pblk, "recovering unbalanced line (%d)\n", line->id); ppa_list = p.ppa_list; From b4cdc4260edf7e03d8b6fabb88aec2cdda95917e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Gonz=C3=A1lez?= Date: Mon, 11 Feb 2019 13:25:08 +0100 Subject: [PATCH 1548/4277] lightnvm: pblk: prevent stall due to wb threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to respect mw_cuinits, pblk's write buffer maintains a backpointer to protect data not yet persisted; when writing to the write buffer, this backpointer defines a threshold that pblk's rate-limiter enforces. On small PU configurations, the following scenarios might take place: (i) the threshold is larger than the write buffer and (ii) the threshold is smaller than the write buffer, but larger than the maximun allowed split bio - 256KB at this moment (Note that writes are not always split - we only do this when we the size of the buffer is smaller than the buffer). In both cases, pblk's rate-limiter prevents the I/O to be written to the buffer, thus stalling. This patch fixes the original backpointer implementation by considering the threshold both on buffer creation and on the rate-limiters path, when bio_split is triggered (case (ii) above). Fixes: 766c8ceb16fc ("lightnvm: pblk: guarantee that backpointer is respected on writer stall") Signed-off-by: Javier González Reviewed-by: Hans Holmberg Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-rb.c | 25 +++++++++++++++++++------ drivers/lightnvm/pblk-rl.c | 5 ++--- drivers/lightnvm/pblk.h | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c index d4ca8c64ee0f..a6133b50ed9c 100644 --- a/drivers/lightnvm/pblk-rb.c +++ b/drivers/lightnvm/pblk-rb.c @@ -45,10 +45,23 @@ void pblk_rb_free(struct pblk_rb *rb) /* * pblk_rb_calculate_size -- calculate the size of the write buffer */ -static unsigned int pblk_rb_calculate_size(unsigned int nr_entries) +static unsigned int pblk_rb_calculate_size(unsigned int nr_entries, + unsigned int threshold) { - /* Alloc a write buffer that can at least fit 128 entries */ - return (1 << max(get_count_order(nr_entries), 7)); + unsigned int thr_sz = 1 << (get_count_order(threshold + NVM_MAX_VLBA)); + unsigned int max_sz = max(thr_sz, nr_entries); + unsigned int max_io; + + /* Alloc a write buffer that can (i) fit at least two split bios + * (considering max I/O size NVM_MAX_VLBA, and (ii) guarantee that the + * threshold will be respected + */ + max_io = (1 << max((int)(get_count_order(max_sz)), + (int)(get_count_order(NVM_MAX_VLBA << 1)))); + if ((threshold + NVM_MAX_VLBA) >= max_io) + max_io <<= 1; + + return max_io; } /* @@ -67,12 +80,12 @@ int pblk_rb_init(struct pblk_rb *rb, unsigned int size, unsigned int threshold, unsigned int alloc_order, order, iter; unsigned int nr_entries; - nr_entries = pblk_rb_calculate_size(size); + nr_entries = pblk_rb_calculate_size(size, threshold); entries = vzalloc(array_size(nr_entries, sizeof(struct pblk_rb_entry))); if (!entries) return -ENOMEM; - power_size = get_count_order(size); + power_size = get_count_order(nr_entries); power_seg_sz = get_count_order(seg_size); down_write(&pblk_rb_lock); @@ -149,7 +162,7 @@ int pblk_rb_init(struct pblk_rb *rb, unsigned int size, unsigned int threshold, * Initialize rate-limiter, which controls access to the write buffer * by user and GC I/O */ - pblk_rl_init(&pblk->rl, rb->nr_entries); + pblk_rl_init(&pblk->rl, rb->nr_entries, threshold); return 0; } diff --git a/drivers/lightnvm/pblk-rl.c b/drivers/lightnvm/pblk-rl.c index 76116d5f78e4..b014957dde0b 100644 --- a/drivers/lightnvm/pblk-rl.c +++ b/drivers/lightnvm/pblk-rl.c @@ -207,7 +207,7 @@ void pblk_rl_free(struct pblk_rl *rl) del_timer(&rl->u_timer); } -void pblk_rl_init(struct pblk_rl *rl, int budget) +void pblk_rl_init(struct pblk_rl *rl, int budget, int threshold) { struct pblk *pblk = container_of(rl, struct pblk, rl); struct nvm_tgt_dev *dev = pblk->dev; @@ -217,7 +217,6 @@ void pblk_rl_init(struct pblk_rl *rl, int budget) int sec_meta, blk_meta; unsigned int rb_windows; - /* Consider sectors used for metadata */ sec_meta = (lm->smeta_sec + lm->emeta_sec[0]) * l_mg->nr_free_lines; blk_meta = DIV_ROUND_UP(sec_meta, geo->clba); @@ -234,7 +233,7 @@ void pblk_rl_init(struct pblk_rl *rl, int budget) /* To start with, all buffer is available to user I/O writers */ rl->rb_budget = budget; rl->rb_user_max = budget; - rl->rb_max_io = budget >> 1; + rl->rb_max_io = threshold ? (budget - threshold) : (budget - 1); rl->rb_gc_max = 0; rl->rb_state = PBLK_RL_HIGH; diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index 72ae8755764e..a6386d5acd73 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h @@ -924,7 +924,7 @@ int pblk_gc_sysfs_force(struct pblk *pblk, int force); /* * pblk rate limiter */ -void pblk_rl_init(struct pblk_rl *rl, int budget); +void pblk_rl_init(struct pblk_rl *rl, int budget, int threshold); void pblk_rl_free(struct pblk_rl *rl); void pblk_rl_update_rates(struct pblk_rl *rl); int pblk_rl_high_thrs(struct pblk_rl *rl); From 0586942f03b71bc95b0ee356ff6b09d53acbad06 Mon Sep 17 00:00:00 2001 From: Heiner Litz Date: Mon, 11 Feb 2019 13:25:09 +0100 Subject: [PATCH 1549/4277] lightnvm: pblk: fix race condition on GC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes a race condition where a write is mapped to the last sectors of a line. The write is synced to the device but the L2P is not updated yet. When the line is garbage collected before the L2P update is performed, the sectors are ignored by the GC logic and the line is freed before all sectors are moved. When the L2P is finally updated, it contains a mapping to a freed line, subsequent reads of the corresponding LBAs fail. This patch introduces a per line counter specifying the number of sectors that are synced to the device but have not been updated in the L2P. Lines with a counter of greater than zero will not be selected for GC. Signed-off-by: Heiner Litz Reviewed-by: Hans Holmberg Reviewed-by: Javier González Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/pblk-core.c | 1 + drivers/lightnvm/pblk-gc.c | 20 +++++++++++++------- drivers/lightnvm/pblk-map.c | 1 + drivers/lightnvm/pblk-rb.c | 1 + drivers/lightnvm/pblk-write.c | 1 + drivers/lightnvm/pblk.h | 1 + 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c index 2a9e9facf44f..6ca868868fee 100644 --- a/drivers/lightnvm/pblk-core.c +++ b/drivers/lightnvm/pblk-core.c @@ -1278,6 +1278,7 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line) spin_unlock(&line->lock); kref_init(&line->ref); + atomic_set(&line->sec_to_update, 0); return 0; } diff --git a/drivers/lightnvm/pblk-gc.c b/drivers/lightnvm/pblk-gc.c index 2fa118c8eb71..26a52ea7ec45 100644 --- a/drivers/lightnvm/pblk-gc.c +++ b/drivers/lightnvm/pblk-gc.c @@ -365,16 +365,22 @@ static struct pblk_line *pblk_gc_get_victim_line(struct pblk *pblk, struct list_head *group_list) { struct pblk_line *line, *victim; - int line_vsc, victim_vsc; + unsigned int line_vsc = ~0x0L, victim_vsc = ~0x0L; victim = list_first_entry(group_list, struct pblk_line, list); + list_for_each_entry(line, group_list, list) { - line_vsc = le32_to_cpu(*line->vsc); - victim_vsc = le32_to_cpu(*victim->vsc); - if (line_vsc < victim_vsc) + if (!atomic_read(&line->sec_to_update)) + line_vsc = le32_to_cpu(*line->vsc); + if (line_vsc < victim_vsc) { victim = line; + victim_vsc = le32_to_cpu(*victim->vsc); + } } + if (victim_vsc == ~0x0) + return NULL; + return victim; } @@ -448,13 +454,13 @@ next_gc_group: do { spin_lock(&l_mg->gc_lock); - if (list_empty(group_list)) { + + line = pblk_gc_get_victim_line(pblk, group_list); + if (!line) { spin_unlock(&l_mg->gc_lock); break; } - line = pblk_gc_get_victim_line(pblk, group_list); - spin_lock(&line->lock); WARN_ON(line->state != PBLK_LINESTATE_CLOSED); line->state = PBLK_LINESTATE_GC; diff --git a/drivers/lightnvm/pblk-map.c b/drivers/lightnvm/pblk-map.c index 79df583ea709..7fbc99b60cac 100644 --- a/drivers/lightnvm/pblk-map.c +++ b/drivers/lightnvm/pblk-map.c @@ -73,6 +73,7 @@ static int pblk_map_page_data(struct pblk *pblk, unsigned int sentry, */ if (i < valid_secs) { kref_get(&line->ref); + atomic_inc(&line->sec_to_update); w_ctx = pblk_rb_w_ctx(&pblk->rwb, sentry + i); w_ctx->ppa = ppa_list[i]; meta->lba = cpu_to_le64(w_ctx->lba); diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c index a6133b50ed9c..03c241b340ea 100644 --- a/drivers/lightnvm/pblk-rb.c +++ b/drivers/lightnvm/pblk-rb.c @@ -260,6 +260,7 @@ static int __pblk_rb_update_l2p(struct pblk_rb *rb, unsigned int to_update) entry->cacheline); line = pblk_ppa_to_line(pblk, w_ctx->ppa); + atomic_dec(&line->sec_to_update); kref_put(&line->ref, pblk_line_put); clean_wctx(w_ctx); rb->l2p_update = pblk_rb_ptr_wrap(rb, rb->l2p_update, 1); diff --git a/drivers/lightnvm/pblk-write.c b/drivers/lightnvm/pblk-write.c index 06d56deb645d..6593deab52da 100644 --- a/drivers/lightnvm/pblk-write.c +++ b/drivers/lightnvm/pblk-write.c @@ -177,6 +177,7 @@ static void pblk_prepare_resubmit(struct pblk *pblk, unsigned int sentry, * re-map these entries */ line = pblk_ppa_to_line(pblk, w_ctx->ppa); + atomic_dec(&line->sec_to_update); kref_put(&line->ref, pblk_line_put); } spin_unlock(&pblk->trans_lock); diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index a6386d5acd73..ac3ab778e976 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h @@ -487,6 +487,7 @@ struct pblk_line { __le32 *vsc; /* Valid sector count in line */ struct kref ref; /* Write buffer L2P references */ + atomic_t sec_to_update; /* Outstanding L2P updates to ppa */ struct pblk_w_err_gc *w_err_gc; /* Write error gc recovery metadata */ From b7143fe67bfc3b83a9e11371da659e1e70a1bbf3 Mon Sep 17 00:00:00 2001 From: Aleksei Zakharov Date: Mon, 11 Feb 2019 13:10:34 +0300 Subject: [PATCH 1550/4277] block: avoid setting wbt_lat_usec to current value There's no reason to set wbt min lat and freeze request queue if current value is the same. Reviewed-by: Johannes Thumshirn Signed-off-by: Aleksei Zakharov Signed-off-by: Jens Axboe --- block/blk-sysfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 94e1b052abbc..59685918167e 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -468,6 +468,9 @@ static ssize_t queue_wb_lat_store(struct request_queue *q, const char *page, else if (val >= 0) val *= 1000ULL; + if (wbt_get_min_lat(q) == val) + return count; + /* * Ensure that the queue is idled, in case the latency update * ends up either enabling or disabling wbt completely. We can't From fbd72127c975dc8e532ecc73d52f3b1b00935bec Mon Sep 17 00:00:00 2001 From: Aleksei Zakharov Date: Mon, 11 Feb 2019 13:50:37 +0300 Subject: [PATCH 1551/4277] block: avoid setting none scheduler if it's already none There's no reason to freeze queue and remove scheduler if there's no scheduler already. Signed-off-by: Aleksei Zakharov Signed-off-by: Jens Axboe --- block/elevator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/elevator.c b/block/elevator.c index f05e90d4e695..d6d835a08de6 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -667,8 +667,11 @@ static int __elevator_change(struct request_queue *q, const char *name) /* * Special case for mq, turn off scheduling */ - if (!strncmp(name, "none", 4)) + if (!strncmp(name, "none", 4)) { + if (!q->elevator) + return 0; return elevator_switch(q, NULL); + } strlcpy(elevator_name, name, sizeof(elevator_name)); e = elevator_get(q, strstrip(elevator_name), true); From 9a065fa8f76f1b025c6b595d781050ad32db76bf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Feb 2019 14:43:23 +0100 Subject: [PATCH 1552/4277] Documentation/DMA-ISA-LPC: fix an incorrect reference AFAIK we never had a isa_virt_to_phys, it always was isa_virt_to_bus. Signed-off-by: Christoph Hellwig Signed-off-by: Jonathan Corbet --- Documentation/DMA-ISA-LPC.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/DMA-ISA-LPC.txt b/Documentation/DMA-ISA-LPC.txt index 8c2b8be6e45b..b1ec7b16c21f 100644 --- a/Documentation/DMA-ISA-LPC.txt +++ b/Documentation/DMA-ISA-LPC.txt @@ -52,8 +52,8 @@ Address translation ------------------- To translate the virtual address to a bus address, use the normal DMA -API. Do _not_ use isa_virt_to_phys() even though it does the same -thing. The reason for this is that the function isa_virt_to_phys() +API. Do _not_ use isa_virt_to_bus() even though it does the same +thing. The reason for this is that the function isa_virt_to_bus() will require a Kconfig dependency to ISA, not just ISA_DMA_API which is really all you need. Remember that even though the DMA controller has its origins in ISA it is used elsewhere. From c9389ad814cd0dd32e7d922bc33d5cc4b04784c1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 10 Feb 2019 22:30:49 -0800 Subject: [PATCH 1553/4277] Documentation: fix lg-laptop.rst warnings Fix markup warnings by inserting blank lines. Also correct one typo. Documentation/laptops/lg-laptop.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent. Documentation/laptops/lg-laptop.rst:16: WARNING: Unexpected indentation. Documentation/laptops/lg-laptop.rst:17: WARNING: Block quote ends without a blank line; unexpected unindent. Signed-off-by: Randy Dunlap Cc: Matan Ziv-Av Acked-by: Andy Shevchenko Signed-off-by: Jonathan Corbet --- Documentation/laptops/lg-laptop.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/laptops/lg-laptop.rst b/Documentation/laptops/lg-laptop.rst index e486fe7ddc35..aa503ee9b3bc 100644 --- a/Documentation/laptops/lg-laptop.rst +++ b/Documentation/laptops/lg-laptop.rst @@ -1,4 +1,5 @@ .. SPDX-License-Identifier: GPL-2.0+ + LG Gram laptop extra features ============================= @@ -9,6 +10,7 @@ Hotkeys ------- The following FN keys are ignored by the kernel without this driver: + - FN-F1 (LG control panel) - Generates F15 - FN-F5 (Touchpad toggle) - Generates F13 - FN-F6 (Airplane mode) - Generates RFKILL @@ -16,7 +18,7 @@ The following FN keys are ignored by the kernel without this driver: This key also changes keyboard backlight mode. - FN-F9 (Reader mode) - Generates F14 -The rest of the FN key work without a need for a special driver. +The rest of the FN keys work without a need for a special driver. Reader mode From 2c71d305caf9cd5076873c6a57d22ab8cb7c906c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 10 Feb 2019 18:12:59 +0100 Subject: [PATCH 1554/4277] docs: process: Remove outdated info about -git patches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As can be seen by clicking around the timeline on web.archive.org[1], there were no -git patches/tarballs on kernel.org since release 3.1. [1]: https://web.archive.org/web/20111103073843/http://www.kernel.org/ Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- Documentation/process/howto.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Documentation/process/howto.rst b/Documentation/process/howto.rst index bdd2eda81a1c..64deab5cfb3b 100644 --- a/Documentation/process/howto.rst +++ b/Documentation/process/howto.rst @@ -302,15 +302,6 @@ The file :ref:`Documentation/process/stable-kernel-rules.rst Date: Fri, 8 Feb 2019 16:30:38 +0100 Subject: [PATCH 1555/4277] doc:dmaengine: clarify DMA desc. pointer after submission It clarifies that the DMA description pointer returned by `dmaengine_prep_*` function should not be used after submission. Signed-off-by: Federico Vaga Signed-off-by: Jonathan Corbet --- Documentation/driver-api/dmaengine/client.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst index fbbb2831f29f..d728e50105eb 100644 --- a/Documentation/driver-api/dmaengine/client.rst +++ b/Documentation/driver-api/dmaengine/client.rst @@ -168,6 +168,13 @@ The details of these operations are: dmaengine_submit() will not start the DMA operation, it merely adds it to the pending queue. For this, see step 5, dma_async_issue_pending. + .. note:: + + After calling ``dmaengine_submit()`` the submitted transfer descriptor + (``struct dma_async_tx_descriptor``) belongs to the DMA engine. + Consequentially, the client must consider invalid the pointer to that + descriptor. + 5. Issue pending DMA requests and wait for callback notification The transactions in the pending queue can be activated by calling the From f96c3ac8dfc24b4e38fc4c2eba5fea2107b929d1 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 11 Feb 2019 13:30:32 -0500 Subject: [PATCH 1556/4277] ext4: fix crash during online resizing When computing maximum size of filesystem possible with given number of group descriptor blocks, we forget to include s_first_data_block into the number of blocks. Thus for filesystems with non-zero s_first_data_block it can happen that computed maximum filesystem size is actually lower than current filesystem size which confuses the code and eventually leads to a BUG_ON in ext4_alloc_group_tables() hitting on flex_gd->count == 0. The problem can be reproduced like: truncate -s 100g /tmp/image mkfs.ext4 -b 1024 -E resize=262144 /tmp/image 32768 mount -t ext4 -o loop /tmp/image /mnt resize2fs /dev/loop0 262145 resize2fs /dev/loop0 300000 Fix the problem by properly including s_first_data_block into the computed number of filesystem blocks. Fixes: 1c6bd7173d66 "ext4: convert file system to meta_bg if needed..." Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o Cc: stable@vger.kernel.org --- fs/ext4/resize.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 48421de803b7..3d9b18505c0c 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1960,7 +1960,8 @@ retry: le16_to_cpu(es->s_reserved_gdt_blocks); n_group = n_desc_blocks * EXT4_DESC_PER_BLOCK(sb); n_blocks_count = (ext4_fsblk_t)n_group * - EXT4_BLOCKS_PER_GROUP(sb); + EXT4_BLOCKS_PER_GROUP(sb) + + le32_to_cpu(es->s_first_data_block); n_group--; /* set to last group number */ } From 85acbb21b9310043692cf18b1fd14067b5a25ccd Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Sun, 10 Feb 2019 00:19:19 +0800 Subject: [PATCH 1557/4277] tracing: Change the function format to display function names by perf Here is an example for this change. $ sudo perf record -e 'ftrace:function' --filter='ip==schedule' $ sudo perf report The output of perf before this patch: \# Samples: 100 of event 'ftrace:function' \# Event count (approx.): 100 \# \# Overhead Trace output \# ........ ...................................... \# 51.00% ffffffff81f6aaa0 <-- ffffffff81158e8d 29.00% ffffffff81f6aaa0 <-- ffffffff8116ccb2 8.00% ffffffff81f6aaa0 <-- ffffffff81f6f2ed 4.00% ffffffff81f6aaa0 <-- ffffffff811628db 4.00% ffffffff81f6aaa0 <-- ffffffff81f6ec5b 2.00% ffffffff81f6aaa0 <-- ffffffff81f6f21a 1.00% ffffffff81f6aaa0 <-- ffffffff811b04af 1.00% ffffffff81f6aaa0 <-- ffffffff8143ce17 After this patch: \# Samples: 36 of event 'ftrace:function' \# Event count (approx.): 36 \# \# Overhead Trace output \# ........ ............................................ \# 38.89% schedule <-- schedule_hrtimeout_range_clock 27.78% schedule <-- worker_thread 13.89% schedule <-- schedule_timeout 11.11% schedule <-- smpboot_thread_fn 5.56% schedule <-- rcu_gp_kthread 2.78% schedule <-- exit_to_usermode_loop Link: http://lkml.kernel.org/r/20190209161919.32350-1-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_entries.h | 41 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 06bb2fd9a56c..fc8e97328e54 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -65,7 +65,8 @@ FTRACE_ENTRY_REG(function, ftrace_entry, __field( unsigned long, parent_ip ) ), - F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip), + F_printk(" %ps <-- %ps", + (void *)__entry->ip, (void *)__entry->parent_ip), FILTER_TRACE_FN, @@ -83,7 +84,7 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry, __field_desc( int, graph_ent, depth ) ), - F_printk("--> %lx (%d)", __entry->func, __entry->depth), + F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth), FILTER_OTHER ); @@ -102,8 +103,8 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry, __field_desc( int, ret, depth ) ), - F_printk("<-- %lx (%d) (start: %llx end: %llx) over: %d", - __entry->func, __entry->depth, + F_printk("<-- %ps (%d) (start: %llx end: %llx) over: %d", + (void *)__entry->func, __entry->depth, __entry->calltime, __entry->rettime, __entry->depth), @@ -167,12 +168,6 @@ FTRACE_ENTRY_DUP(wakeup, ctx_switch_entry, #define FTRACE_STACK_ENTRIES 8 -#ifndef CONFIG_64BIT -# define IP_FMT "%08lx" -#else -# define IP_FMT "%016lx" -#endif - FTRACE_ENTRY(kernel_stack, stack_entry, TRACE_STACK, @@ -182,12 +177,13 @@ FTRACE_ENTRY(kernel_stack, stack_entry, __dynamic_array(unsigned long, caller ) ), - F_printk("\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" - "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" - "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n", - __entry->caller[0], __entry->caller[1], __entry->caller[2], - __entry->caller[3], __entry->caller[4], __entry->caller[5], - __entry->caller[6], __entry->caller[7]), + F_printk("\t=> %ps\n\t=> %ps\n\t=> %ps\n" + "\t=> %ps\n\t=> %ps\n\t=> %ps\n" + "\t=> %ps\n\t=> %ps\n", + (void *)__entry->caller[0], (void *)__entry->caller[1], + (void *)__entry->caller[2], (void *)__entry->caller[3], + (void *)__entry->caller[4], (void *)__entry->caller[5], + (void *)__entry->caller[6], (void *)__entry->caller[7]), FILTER_OTHER ); @@ -201,12 +197,13 @@ FTRACE_ENTRY(user_stack, userstack_entry, __array( unsigned long, caller, FTRACE_STACK_ENTRIES ) ), - F_printk("\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" - "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n" - "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n", - __entry->caller[0], __entry->caller[1], __entry->caller[2], - __entry->caller[3], __entry->caller[4], __entry->caller[5], - __entry->caller[6], __entry->caller[7]), + F_printk("\t=> %ps\n\t=> %ps\n\t=> %ps\n" + "\t=> %ps\n\t=> %ps\n\t=> %ps\n" + "\t=> %ps\n\t=> %ps\n", + (void *)__entry->caller[0], (void *)__entry->caller[1], + (void *)__entry->caller[2], (void *)__entry->caller[3], + (void *)__entry->caller[4], (void *)__entry->caller[5], + (void *)__entry->caller[6], (void *)__entry->caller[7]), FILTER_OTHER ); From d901b2760dc6cd5fbbf2eac31d71d94baa6c4aef Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 4 Jan 2019 11:40:21 -0700 Subject: [PATCH 1558/4277] lib/scatterlist: Provide a DMA page iterator Commit 2db76d7c3c6d ("lib/scatterlist: sg_page_iter: support sg lists w/o backing pages") introduced the sg_page_iter_dma_address() function without providing a way to use it in the general case. If the sg_dma_len() is not equal to the sg length callers cannot safely use the for_each_sg_page/sg_page_iter_dma_address combination. Resolve this API mistake by providing a DMA specific iterator, for_each_sg_dma_page(), that uses the right length so sg_page_iter_dma_address() works as expected with all sglists. A new iterator type is introduced to provide compile-time safety against wrongly mixing accessors and iterators. Acked-by: Christoph Hellwig (for scatterlist) Acked-by: Thomas Hellstrom Acked-by: Sakari Ailus (ipu3-cio2) Signed-off-by: Jason Gunthorpe --- .clang-format | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 8 +++- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 4 +- include/linux/scatterlist.h | 49 ++++++++++++++++++---- lib/scatterlist.c | 26 ++++++++++++ 5 files changed, 76 insertions(+), 12 deletions(-) diff --git a/.clang-format b/.clang-format index bc2ffb2a0b53..335ce29ab813 100644 --- a/.clang-format +++ b/.clang-format @@ -240,6 +240,7 @@ ForEachMacros: - 'for_each_set_bit' - 'for_each_set_bit_from' - 'for_each_sg' + - 'for_each_sg_dma_page' - 'for_each_sg_page' - 'for_each_sibling_event' - '__for_each_thread' diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index 31786b200afc..a3357ff7540d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -311,7 +311,13 @@ static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter) { - return sg_page_iter_dma_address(&viter->iter); + /* + * FIXME: This driver wrongly mixes DMA and CPU SG list iteration and + * needs revision. See + * https://lore.kernel.org/lkml/20190104223531.GA1705@ziepe.ca/ + */ + return sg_page_iter_dma_address( + container_of(&viter->iter, struct sg_dma_page_iter, base)); } diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index cdb79ae2d8dc..9fbfbda74171 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -846,7 +846,7 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb) unsigned int pages = DIV_ROUND_UP(vb->planes[0].length, CIO2_PAGE_SIZE); unsigned int lops = DIV_ROUND_UP(pages + 1, entries_per_page); struct sg_table *sg; - struct sg_page_iter sg_iter; + struct sg_dma_page_iter sg_iter; int i, j; if (lops <= 0 || lops > CIO2_MAX_LOPS) { @@ -873,7 +873,7 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb) b->offset = sg->sgl->offset; i = j = 0; - for_each_sg_page(sg->sgl, &sg_iter, sg->nents, 0) { + for_each_sg_dma_page (sg->sgl, &sg_iter, sg->nents, 0) { if (!pages--) break; b->lop[i][j] = sg_page_iter_dma_address(&sg_iter) >> PAGE_SHIFT; diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index b96f0d0b5b8f..b4be960c7e5d 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -339,12 +339,12 @@ int sg_alloc_table_chained(struct sg_table *table, int nents, /* * sg page iterator * - * Iterates over sg entries page-by-page. On each successful iteration, - * you can call sg_page_iter_page(@piter) and sg_page_iter_dma_address(@piter) - * to get the current page and its dma address. @piter->sg will point to the - * sg holding this page and @piter->sg_pgoffset to the page's page offset - * within the sg. The iteration will stop either when a maximum number of sg - * entries was reached or a terminating sg (sg_last(sg) == true) was reached. + * Iterates over sg entries page-by-page. On each successful iteration, you + * can call sg_page_iter_page(@piter) to get the current page and its dma + * address. @piter->sg will point to the sg holding this page and + * @piter->sg_pgoffset to the page's page offset within the sg. The iteration + * will stop either when a maximum number of sg entries was reached or a + * terminating sg (sg_last(sg) == true) was reached. */ struct sg_page_iter { struct scatterlist *sg; /* sg holding the page */ @@ -356,7 +356,19 @@ struct sg_page_iter { * next step */ }; +/* + * sg page iterator for DMA addresses + * + * This is the same as sg_page_iter however you can call + * sg_page_iter_dma_address(@dma_iter) to get the page's DMA + * address. sg_page_iter_page() cannot be called on this iterator. + */ +struct sg_dma_page_iter { + struct sg_page_iter base; +}; + bool __sg_page_iter_next(struct sg_page_iter *piter); +bool __sg_page_iter_dma_next(struct sg_dma_page_iter *dma_iter); void __sg_page_iter_start(struct sg_page_iter *piter, struct scatterlist *sglist, unsigned int nents, unsigned long pgoffset); @@ -372,11 +384,13 @@ static inline struct page *sg_page_iter_page(struct sg_page_iter *piter) /** * sg_page_iter_dma_address - get the dma address of the current page held by * the page iterator. - * @piter: page iterator holding the page + * @dma_iter: page iterator holding the page */ -static inline dma_addr_t sg_page_iter_dma_address(struct sg_page_iter *piter) +static inline dma_addr_t +sg_page_iter_dma_address(struct sg_dma_page_iter *dma_iter) { - return sg_dma_address(piter->sg) + (piter->sg_pgoffset << PAGE_SHIFT); + return sg_dma_address(dma_iter->base.sg) + + (dma_iter->base.sg_pgoffset << PAGE_SHIFT); } /** @@ -385,11 +399,28 @@ static inline dma_addr_t sg_page_iter_dma_address(struct sg_page_iter *piter) * @piter: page iterator to hold current page, sg, sg_pgoffset * @nents: maximum number of sg entries to iterate over * @pgoffset: starting page offset + * + * Callers may use sg_page_iter_page() to get each page pointer. */ #define for_each_sg_page(sglist, piter, nents, pgoffset) \ for (__sg_page_iter_start((piter), (sglist), (nents), (pgoffset)); \ __sg_page_iter_next(piter);) +/** + * for_each_sg_dma_page - iterate over the pages of the given sg list + * @sglist: sglist to iterate over + * @dma_iter: page iterator to hold current page + * @dma_nents: maximum number of sg entries to iterate over, this is the value + * returned from dma_map_sg + * @pgoffset: starting page offset + * + * Callers may use sg_page_iter_dma_address() to get each page's DMA address. + */ +#define for_each_sg_dma_page(sglist, dma_iter, dma_nents, pgoffset) \ + for (__sg_page_iter_start(&(dma_iter)->base, sglist, dma_nents, \ + pgoffset); \ + __sg_page_iter_dma_next(dma_iter);) + /* * Mapping sg iterator * diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 9ba349e775ef..739dc9fe2c55 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -625,6 +625,32 @@ bool __sg_page_iter_next(struct sg_page_iter *piter) } EXPORT_SYMBOL(__sg_page_iter_next); +static int sg_dma_page_count(struct scatterlist *sg) +{ + return PAGE_ALIGN(sg->offset + sg_dma_len(sg)) >> PAGE_SHIFT; +} + +bool __sg_page_iter_dma_next(struct sg_dma_page_iter *dma_iter) +{ + struct sg_page_iter *piter = &dma_iter->base; + + if (!piter->__nents || !piter->sg) + return false; + + piter->sg_pgoffset += piter->__pg_advance; + piter->__pg_advance = 1; + + while (piter->sg_pgoffset >= sg_dma_page_count(piter->sg)) { + piter->sg_pgoffset -= sg_dma_page_count(piter->sg); + piter->sg = sg_next(piter->sg); + if (!--piter->__nents || !piter->sg) + return false; + } + + return true; +} +EXPORT_SYMBOL(__sg_page_iter_dma_next); + /** * sg_miter_start - start mapping iteration over a sg list * @miter: sg mapping iter to be started From 161ebe2498d4108c5e80dc3b47736ecda32cd816 Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:24:57 -0600 Subject: [PATCH 1559/4277] RDMA/bnxt_re: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 21 ++++++++------------- drivers/infiniband/hw/bnxt_re/qplib_res.c | 9 +++++---- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 1606571af63d..bff9320a968e 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -3553,19 +3553,14 @@ static int fill_umem_pbl_tbl(struct ib_umem *umem, u64 *pbl_tbl_orig, u64 *pbl_tbl = pbl_tbl_orig; u64 paddr; u64 page_mask = (1ULL << page_shift) - 1; - int i, pages; - struct scatterlist *sg; - int entry; + struct sg_dma_page_iter sg_iter; - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - pages = sg_dma_len(sg) >> PAGE_SHIFT; - for (i = 0; i < pages; i++) { - paddr = sg_dma_address(sg) + (i << PAGE_SHIFT); - if (pbl_tbl == pbl_tbl_orig) - *pbl_tbl++ = paddr & ~page_mask; - else if ((paddr & page_mask) == 0) - *pbl_tbl++ = paddr; - } + for_each_sg_dma_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + paddr = sg_page_iter_dma_address(&sg_iter); + if (pbl_tbl == pbl_tbl_orig) + *pbl_tbl++ = paddr & ~page_mask; + else if ((paddr & page_mask) == 0) + *pbl_tbl++ = paddr; } return pbl_tbl - pbl_tbl_orig; } @@ -3628,7 +3623,7 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, goto free_umem; } - page_shift = umem->page_shift; + page_shift = PAGE_SHIFT; if (!bnxt_re_page_size_ok(page_shift)) { dev_err(rdev_to_dev(rdev), "umem page size unsupported!"); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index c8502c2844a2..d08b9d9948fd 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -85,7 +85,7 @@ static void __free_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl, static int __alloc_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl, struct scatterlist *sghead, u32 pages, u32 pg_size) { - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; bool is_umem = false; int i; @@ -116,12 +116,13 @@ static int __alloc_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl, } else { i = 0; is_umem = true; - for_each_sg(sghead, sg, pages, i) { - pbl->pg_map_arr[i] = sg_dma_address(sg); - pbl->pg_arr[i] = sg_virt(sg); + for_each_sg_dma_page (sghead, &sg_iter, pages, 0) { + pbl->pg_map_arr[i] = sg_page_iter_dma_address(&sg_iter); + pbl->pg_arr[i] = NULL; if (!pbl->pg_arr[i]) goto fail; + i++; pbl->pg_count++; } } From 8d249af3e6d70db08b61449fe6ad1da000d625d1 Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:24:58 -0600 Subject: [PATCH 1560/4277] RDMA/mthca: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mthca/mthca_provider.c | 36 +++++++++----------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 2c754bc226f3..516c8cf9c0fd 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -897,12 +897,11 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, int acc, struct ib_udata *udata) { struct mthca_dev *dev = to_mdev(pd->device); - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; struct mthca_mr *mr; struct mthca_reg_mr ucmd; u64 *pages; - int shift, n, len; - int i, k, entry; + int n, i; int err = 0; int write_mtt_size; @@ -929,7 +928,6 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, goto err; } - shift = mr->umem->page_shift; n = mr->umem->nmap; mr->mtt = mthca_alloc_mtt(dev, n); @@ -948,21 +946,19 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, write_mtt_size = min(mthca_write_mtt_size(dev), (int) (PAGE_SIZE / sizeof *pages)); - for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) { - len = sg_dma_len(sg) >> shift; - for (k = 0; k < len; ++k) { - pages[i++] = sg_dma_address(sg) + (k << shift); - /* - * Be friendly to write_mtt and pass it chunks - * of appropriate size. - */ - if (i == write_mtt_size) { - err = mthca_write_mtt(dev, mr->mtt, n, pages, i); - if (err) - goto mtt_done; - n += i; - i = 0; - } + for_each_sg_dma_page(mr->umem->sg_head.sgl, &sg_iter, mr->umem->nmap, 0) { + pages[i++] = sg_page_iter_dma_address(&sg_iter); + + /* + * Be friendly to write_mtt and pass it chunks + * of appropriate size. + */ + if (i == write_mtt_size) { + err = mthca_write_mtt(dev, mr->mtt, n, pages, i); + if (err) + goto mtt_done; + n += i; + i = 0; } } @@ -973,7 +969,7 @@ mtt_done: if (err) goto err_mtt; - err = mthca_mr_alloc(dev, to_mpd(pd)->pd_num, shift, virt, length, + err = mthca_mr_alloc(dev, to_mpd(pd)->pd_num, PAGE_SHIFT, virt, length, convert_access(acc), mr); if (err) From 43fae91276a543e430649bc88284bd7be475dfba Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:24:59 -0600 Subject: [PATCH 1561/4277] RDMA/i40iw: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/i40iw/i40iw_verbs.c | 33 +++++++++++------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 28449ad57b37..d5fb2b927587 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -1360,32 +1360,29 @@ static void i40iw_copy_user_pgaddrs(struct i40iw_mr *iwmr, { struct ib_umem *region = iwmr->region; struct i40iw_pbl *iwpbl = &iwmr->iwpbl; - int chunk_pages, entry, i; struct i40iw_pble_alloc *palloc = &iwpbl->pble_alloc; struct i40iw_pble_info *pinfo; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; u64 pg_addr = 0; u32 idx = 0; + bool first_pg = true; pinfo = (level == I40IW_LEVEL_1) ? NULL : palloc->level2.leaf; - for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) { - chunk_pages = sg_dma_len(sg) >> region->page_shift; - if ((iwmr->type == IW_MEMREG_TYPE_QP) && - !iwpbl->qp_mr.sq_page) - iwpbl->qp_mr.sq_page = sg_page(sg); - for (i = 0; i < chunk_pages; i++) { - pg_addr = sg_dma_address(sg) + - (i << region->page_shift); + if (iwmr->type == IW_MEMREG_TYPE_QP) + iwpbl->qp_mr.sq_page = sg_page(region->sg_head.sgl); - if ((entry + i) == 0) - *pbl = cpu_to_le64(pg_addr & iwmr->page_msk); - else if (!(pg_addr & ~iwmr->page_msk)) - *pbl = cpu_to_le64(pg_addr); - else - continue; - pbl = i40iw_next_pbl_addr(pbl, &pinfo, &idx); - } + for_each_sg_dma_page (region->sg_head.sgl, &sg_iter, region->nmap, 0) { + pg_addr = sg_page_iter_dma_address(&sg_iter); + if (first_pg) + *pbl = cpu_to_le64(pg_addr & iwmr->page_msk); + else if (!(pg_addr & ~iwmr->page_msk)) + *pbl = cpu_to_le64(pg_addr); + else + continue; + + first_pg = false; + pbl = i40iw_next_pbl_addr(pbl, &pinfo, &idx); } } From 3856ec55270099494afa0cabba020365a38430a2 Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:00 -0600 Subject: [PATCH 1562/4277] RDMA/hns: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v1.c | 7 +- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 25 +++---- drivers/infiniband/hw/hns/hns_roce_mr.c | 86 ++++++++++------------ drivers/infiniband/hw/hns/hns_roce_qp.c | 10 +-- 4 files changed, 55 insertions(+), 73 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c index a18b88c95995..c8c90072badd 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c @@ -1871,9 +1871,8 @@ static int hns_roce_v1_write_mtpt(void *mb_buf, struct hns_roce_mr *mr, unsigned long mtpt_idx) { struct hns_roce_v1_mpt_entry *mpt_entry; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; u64 *pages; - int entry; int i; /* MPT filled into mailbox buf */ @@ -1928,8 +1927,8 @@ static int hns_roce_v1_write_mtpt(void *mb_buf, struct hns_roce_mr *mr, return -ENOMEM; i = 0; - for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) { - pages[i] = ((u64)sg_dma_address(sg)) >> 12; + for_each_sg_dma_page(mr->umem->sg_head.sgl, &sg_iter, mr->umem->nmap, 0) { + pages[i] = ((u64)sg_page_iter_dma_address(&sg_iter)) >> 12; /* Directly record to MTPT table firstly 7 entry */ if (i >= HNS_ROCE_MAX_INNER_MTPT_NUM) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 19fefff4f699..c648ee825852 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -2084,12 +2084,10 @@ static int hns_roce_v2_set_mac(struct hns_roce_dev *hr_dev, u8 phy_port, static int set_mtpt_pbl(struct hns_roce_v2_mpt_entry *mpt_entry, struct hns_roce_mr *mr) { - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; u64 page_addr; u64 *pages; - int i, j; - int len; - int entry; + int i; mpt_entry->pbl_size = cpu_to_le32(mr->pbl_size); mpt_entry->pbl_ba_l = cpu_to_le32(lower_32_bits(mr->pbl_ba >> 3)); @@ -2102,17 +2100,14 @@ static int set_mtpt_pbl(struct hns_roce_v2_mpt_entry *mpt_entry, return -ENOMEM; i = 0; - for_each_sg(mr->umem->sg_head.sgl, sg, mr->umem->nmap, entry) { - len = sg_dma_len(sg) >> PAGE_SHIFT; - for (j = 0; j < len; ++j) { - page_addr = sg_dma_address(sg) + - (j << mr->umem->page_shift); - pages[i] = page_addr >> 6; - /* Record the first 2 entry directly to MTPT table */ - if (i >= HNS_ROCE_V2_MAX_INNER_MTPT_NUM - 1) - goto found; - i++; - } + for_each_sg_dma_page(mr->umem->sg_head.sgl, &sg_iter, mr->umem->nmap, 0) { + page_addr = sg_page_iter_dma_address(&sg_iter); + pages[i] = page_addr >> 6; + + /* Record the first 2 entry directly to MTPT table */ + if (i >= HNS_ROCE_V2_MAX_INNER_MTPT_NUM - 1) + goto found; + i++; } found: mpt_entry->pa0_l = cpu_to_le32(lower_32_bits(pages[0])); diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index da4fffedb879..b09f1cde2ff5 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -976,12 +976,11 @@ int hns_roce_ib_umem_write_mtt(struct hns_roce_dev *hr_dev, struct hns_roce_mtt *mtt, struct ib_umem *umem) { struct device *dev = hr_dev->dev; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; unsigned int order; - int i, k, entry; int npage = 0; int ret = 0; - int len; + int i; u64 page_addr; u64 *pages; u32 bt_page_size; @@ -1014,29 +1013,25 @@ int hns_roce_ib_umem_write_mtt(struct hns_roce_dev *hr_dev, i = n = 0; - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - len = sg_dma_len(sg) >> PAGE_SHIFT; - for (k = 0; k < len; ++k) { - page_addr = - sg_dma_address(sg) + (k << umem->page_shift); - if (!(npage % (1 << (mtt->page_shift - PAGE_SHIFT)))) { - if (page_addr & ((1 << mtt->page_shift) - 1)) { - dev_err(dev, "page_addr 0x%llx is not page_shift %d alignment!\n", - page_addr, mtt->page_shift); - ret = -EINVAL; - goto out; - } - pages[i++] = page_addr; - } - npage++; - if (i == bt_page_size / sizeof(u64)) { - ret = hns_roce_write_mtt(hr_dev, mtt, n, i, - pages); - if (ret) - goto out; - n += i; - i = 0; + for_each_sg_dma_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + page_addr = sg_page_iter_dma_address(&sg_iter); + if (!(npage % (1 << (mtt->page_shift - PAGE_SHIFT)))) { + if (page_addr & ((1 << mtt->page_shift) - 1)) { + dev_err(dev, + "page_addr 0x%llx is not page_shift %d alignment!\n", + page_addr, mtt->page_shift); + ret = -EINVAL; + goto out; } + pages[i++] = page_addr; + } + npage++; + if (i == bt_page_size / sizeof(u64)) { + ret = hns_roce_write_mtt(hr_dev, mtt, n, i, pages); + if (ret) + goto out; + n += i; + i = 0; } } @@ -1052,10 +1047,8 @@ static int hns_roce_ib_umem_write_mr(struct hns_roce_dev *hr_dev, struct hns_roce_mr *mr, struct ib_umem *umem) { - struct scatterlist *sg; - int i = 0, j = 0, k; - int entry; - int len; + struct sg_dma_page_iter sg_iter; + int i = 0, j = 0; u64 page_addr; u32 pbl_bt_sz; @@ -1063,27 +1056,22 @@ static int hns_roce_ib_umem_write_mr(struct hns_roce_dev *hr_dev, return 0; pbl_bt_sz = 1 << (hr_dev->caps.pbl_ba_pg_sz + PAGE_SHIFT); - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - len = sg_dma_len(sg) >> PAGE_SHIFT; - for (k = 0; k < len; ++k) { - page_addr = sg_dma_address(sg) + - (k << umem->page_shift); + for_each_sg_dma_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + page_addr = sg_page_iter_dma_address(&sg_iter); + if (!hr_dev->caps.pbl_hop_num) { + mr->pbl_buf[i++] = page_addr >> 12; + } else if (hr_dev->caps.pbl_hop_num == 1) { + mr->pbl_buf[i++] = page_addr; + } else { + if (hr_dev->caps.pbl_hop_num == 2) + mr->pbl_bt_l1[i][j] = page_addr; + else if (hr_dev->caps.pbl_hop_num == 3) + mr->pbl_bt_l2[i][j] = page_addr; - if (!hr_dev->caps.pbl_hop_num) { - mr->pbl_buf[i++] = page_addr >> 12; - } else if (hr_dev->caps.pbl_hop_num == 1) { - mr->pbl_buf[i++] = page_addr; - } else { - if (hr_dev->caps.pbl_hop_num == 2) - mr->pbl_bt_l1[i][j] = page_addr; - else if (hr_dev->caps.pbl_hop_num == 3) - mr->pbl_bt_l2[i][j] = page_addr; - - j++; - if (j >= (pbl_bt_sz / 8)) { - i++; - j = 0; - } + j++; + if (j >= (pbl_bt_sz / 8)) { + i++; + j = 0; } } } diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 73066bf38e47..51ca22b9f960 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -640,19 +640,19 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, } hr_qp->mtt.mtt_type = MTT_TYPE_WQE; + page_shift = PAGE_SHIFT; if (hr_dev->caps.mtt_buf_pg_sz) { npages = (ib_umem_page_count(hr_qp->umem) + (1 << hr_dev->caps.mtt_buf_pg_sz) - 1) / - (1 << hr_dev->caps.mtt_buf_pg_sz); - page_shift = PAGE_SHIFT + hr_dev->caps.mtt_buf_pg_sz; + (1 << hr_dev->caps.mtt_buf_pg_sz); + page_shift += hr_dev->caps.mtt_buf_pg_sz; ret = hns_roce_mtt_init(hr_dev, npages, page_shift, &hr_qp->mtt); } else { ret = hns_roce_mtt_init(hr_dev, - ib_umem_page_count(hr_qp->umem), - hr_qp->umem->page_shift, - &hr_qp->mtt); + ib_umem_page_count(hr_qp->umem), + page_shift, &hr_qp->mtt); } if (ret) { dev_err(dev, "hns_roce_mtt_init error for create qp\n"); From 48b586ac36fc4922331a50043058464cc1306aed Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:01 -0600 Subject: [PATCH 1563/4277] RDMA/cxgb4: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Acked-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/mem.c | 32 +++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index 96760a36b9fc..8d1ab8273c2b 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -502,10 +502,9 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, int acc, struct ib_udata *udata) { __be64 *pages; - int shift, n, len; - int i, k, entry; + int shift, n, i; int err = -ENOMEM; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; struct c4iw_dev *rhp; struct c4iw_pd *php; struct c4iw_mr *mhp; @@ -541,7 +540,7 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (IS_ERR(mhp->umem)) goto err_free_skb; - shift = mhp->umem->page_shift; + shift = PAGE_SHIFT; n = mhp->umem->nmap; err = alloc_pbl(mhp, n); @@ -556,21 +555,16 @@ struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, i = n = 0; - for_each_sg(mhp->umem->sg_head.sgl, sg, mhp->umem->nmap, entry) { - len = sg_dma_len(sg) >> shift; - for (k = 0; k < len; ++k) { - pages[i++] = cpu_to_be64(sg_dma_address(sg) + - (k << shift)); - if (i == PAGE_SIZE / sizeof *pages) { - err = write_pbl(&mhp->rhp->rdev, - pages, - mhp->attr.pbl_addr + (n << 3), i, - mhp->wr_waitp); - if (err) - goto pbl_done; - n += i; - i = 0; - } + for_each_sg_dma_page(mhp->umem->sg_head.sgl, &sg_iter, mhp->umem->nmap, 0) { + pages[i++] = cpu_to_be64(sg_page_iter_dma_address(&sg_iter)); + if (i == PAGE_SIZE / sizeof(*pages)) { + err = write_pbl(&mhp->rhp->rdev, pages, + mhp->attr.pbl_addr + (n << 3), i, + mhp->wr_waitp); + if (err) + goto pbl_done; + n += i; + i = 0; } } From b44e47eb065b65aa7cb2bd9c8d8d5c0009cd1393 Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:02 -0600 Subject: [PATCH 1564/4277] RDMA/cxgb3: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Acked-by: Steve Wise Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb3/iwch_provider.c | 29 +++++++++------------ 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 4cc9a6ae2139..80dff6804e48 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -516,14 +516,13 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, int acc, struct ib_udata *udata) { __be64 *pages; - int shift, n, len; - int i, k, entry; + int shift, n, i; int err = 0; struct iwch_dev *rhp; struct iwch_pd *php; struct iwch_mr *mhp; struct iwch_reg_user_mr_resp uresp; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; pr_debug("%s ib_pd %p\n", __func__, pd); php = to_iwch_pd(pd); @@ -541,7 +540,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, return ERR_PTR(err); } - shift = mhp->umem->page_shift; + shift = PAGE_SHIFT; n = mhp->umem->nmap; @@ -557,19 +556,15 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, i = n = 0; - for_each_sg(mhp->umem->sg_head.sgl, sg, mhp->umem->nmap, entry) { - len = sg_dma_len(sg) >> shift; - for (k = 0; k < len; ++k) { - pages[i++] = cpu_to_be64(sg_dma_address(sg) + - (k << shift)); - if (i == PAGE_SIZE / sizeof *pages) { - err = iwch_write_pbl(mhp, pages, i, n); - if (err) - goto pbl_done; - n += i; - i = 0; - } - } + for_each_sg_dma_page(mhp->umem->sg_head.sgl, &sg_iter, mhp->umem->nmap, 0) { + pages[i++] = cpu_to_be64(sg_page_iter_dma_address(&sg_iter)); + if (i == PAGE_SIZE / sizeof *pages) { + err = iwch_write_pbl(mhp, pages, i, n); + if (err) + goto pbl_done; + n += i; + i = 0; + } } if (i) From f3e6d3117939e982462fe1539f1ae44b1ed57f09 Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:03 -0600 Subject: [PATCH 1565/4277] RDMA/vmw_pvrdma: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- .../infiniband/hw/vmw_pvrdma/pvrdma_misc.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c index fb0c5c0976b3..7944c58ded0e 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c @@ -183,25 +183,20 @@ int pvrdma_page_dir_insert_umem(struct pvrdma_page_dir *pdir, struct ib_umem *umem, u64 offset) { u64 i = offset; - int j, entry; - int ret = 0, len = 0; - struct scatterlist *sg; + int ret = 0; + struct sg_dma_page_iter sg_iter; if (offset >= pdir->npages) return -EINVAL; - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - len = sg_dma_len(sg) >> PAGE_SHIFT; - for (j = 0; j < len; j++) { - dma_addr_t addr = sg_dma_address(sg) + - (j << umem->page_shift); + for_each_sg_dma_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + dma_addr_t addr = sg_page_iter_dma_address(&sg_iter); - ret = pvrdma_page_dir_insert_dma(pdir, i, addr); - if (ret) - goto exit; + ret = pvrdma_page_dir_insert_dma(pdir, i, addr); + if (ret) + goto exit; - i++; - } + i++; } exit: From 95ad233ffbed0c87925483bd902b5ec5fa8ed4cd Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:04 -0600 Subject: [PATCH 1566/4277] RDMA/qedr: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Acked-by: Michal Kalderon Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qedr/verbs.c | 61 +++++++++++++----------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index a06d2258394a..a613ebde322f 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -620,13 +620,12 @@ static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem, struct qedr_pbl *pbl, struct qedr_pbl_info *pbl_info, u32 pg_shift) { - int shift, pg_cnt, pages, pbe_cnt, total_num_pbes = 0; + int pbe_cnt, total_num_pbes = 0; u32 fw_pg_cnt, fw_pg_per_umem_pg; struct qedr_pbl *pbl_tbl; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; struct regpair *pbe; u64 pg_addr; - int entry; if (!pbl_info->num_pbes) return; @@ -647,38 +646,32 @@ static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem, pbe_cnt = 0; - shift = umem->page_shift; + fw_pg_per_umem_pg = BIT(PAGE_SHIFT - pg_shift); - fw_pg_per_umem_pg = BIT(umem->page_shift - pg_shift); + for_each_sg_dma_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + pg_addr = sg_page_iter_dma_address(&sg_iter); + for (fw_pg_cnt = 0; fw_pg_cnt < fw_pg_per_umem_pg;) { + pbe->lo = cpu_to_le32(pg_addr); + pbe->hi = cpu_to_le32(upper_32_bits(pg_addr)); - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - pages = sg_dma_len(sg) >> shift; - pg_addr = sg_dma_address(sg); - for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) { - for (fw_pg_cnt = 0; fw_pg_cnt < fw_pg_per_umem_pg;) { - pbe->lo = cpu_to_le32(pg_addr); - pbe->hi = cpu_to_le32(upper_32_bits(pg_addr)); + pg_addr += BIT(pg_shift); + pbe_cnt++; + total_num_pbes++; + pbe++; - pg_addr += BIT(pg_shift); - pbe_cnt++; - total_num_pbes++; - pbe++; + if (total_num_pbes == pbl_info->num_pbes) + return; - if (total_num_pbes == pbl_info->num_pbes) - return; - - /* If the given pbl is full storing the pbes, - * move to next pbl. - */ - if (pbe_cnt == - (pbl_info->pbl_size / sizeof(u64))) { - pbl_tbl++; - pbe = (struct regpair *)pbl_tbl->va; - pbe_cnt = 0; - } - - fw_pg_cnt++; + /* If the given pbl is full storing the pbes, + * move to next pbl. + */ + if (pbe_cnt == (pbl_info->pbl_size / sizeof(u64))) { + pbl_tbl++; + pbe = (struct regpair *)pbl_tbl->va; + pbe_cnt = 0; } + + fw_pg_cnt++; } } } @@ -739,7 +732,7 @@ static inline int qedr_init_user_queue(struct ib_udata *udata, } fw_pages = ib_umem_page_count(q->umem) << - (q->umem->page_shift - FW_PAGE_SHIFT); + (PAGE_SHIFT - FW_PAGE_SHIFT); rc = qedr_prepare_pbl_tbl(dev, &q->pbl_info, fw_pages, 0); if (rc) @@ -1455,7 +1448,7 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd, page_cnt = srq->usrq.pbl_info.num_pbes; pbl_base_addr = srq->usrq.pbl_tbl->pa; phy_prod_pair_addr = hw_srq->phy_prod_pair_addr; - page_size = BIT(srq->usrq.umem->page_shift); + page_size = PAGE_SIZE; } else { struct qed_chain *pbl; @@ -2707,7 +2700,7 @@ struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, goto err1; qedr_populate_pbls(dev, mr->umem, mr->info.pbl_table, - &mr->info.pbl_info, mr->umem->page_shift); + &mr->info.pbl_info, PAGE_SHIFT); rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid); if (rc) { @@ -2728,7 +2721,7 @@ struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, mr->hw_mr.pbl_ptr = mr->info.pbl_table[0].pa; mr->hw_mr.pbl_two_level = mr->info.pbl_info.two_layered; mr->hw_mr.pbl_page_size_log = ilog2(mr->info.pbl_info.pbl_size); - mr->hw_mr.page_size_log = mr->umem->page_shift; + mr->hw_mr.page_size_log = PAGE_SHIFT; mr->hw_mr.fbo = ib_umem_offset(mr->umem); mr->hw_mr.length = len; mr->hw_mr.vaddr = usr_addr; From be8c456abfbd3ae753521e438dc2319fb1dbb8a3 Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:05 -0600 Subject: [PATCH 1567/4277] RDMA/ocrdma: Use for_each_sg_dma_page iterator on umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 53 +++++++++------------ 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 980ba97188ff..ed5da67b693d 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -840,10 +840,11 @@ static void build_user_pbes(struct ocrdma_dev *dev, struct ocrdma_mr *mr, u32 num_pbes) { struct ocrdma_pbe *pbe; - struct scatterlist *sg; + struct sg_dma_page_iter sg_iter; struct ocrdma_pbl *pbl_tbl = mr->hwmr.pbl_table; struct ib_umem *umem = mr->umem; - int shift, pg_cnt, pages, pbe_cnt, entry, total_num_pbes = 0; + int pbe_cnt, total_num_pbes = 0; + u64 pg_addr; if (!mr->hwmr.num_pbes) return; @@ -851,36 +852,26 @@ static void build_user_pbes(struct ocrdma_dev *dev, struct ocrdma_mr *mr, pbe = (struct ocrdma_pbe *)pbl_tbl->va; pbe_cnt = 0; - shift = umem->page_shift; + for_each_sg_dma_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + /* store the page address in pbe */ + pg_addr = sg_page_iter_dma_address(&sg_iter); + pbe->pa_lo = cpu_to_le32(pg_addr); + pbe->pa_hi = cpu_to_le32(upper_32_bits(pg_addr)); + pbe_cnt += 1; + total_num_pbes += 1; + pbe++; - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - pages = sg_dma_len(sg) >> shift; - for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) { - /* store the page address in pbe */ - pbe->pa_lo = - cpu_to_le32(sg_dma_address(sg) + - (pg_cnt << shift)); - pbe->pa_hi = - cpu_to_le32(upper_32_bits(sg_dma_address(sg) + - (pg_cnt << shift))); - pbe_cnt += 1; - total_num_pbes += 1; - pbe++; - - /* if done building pbes, issue the mbx cmd. */ - if (total_num_pbes == num_pbes) - return; - - /* if the given pbl is full storing the pbes, - * move to next pbl. - */ - if (pbe_cnt == - (mr->hwmr.pbl_size / sizeof(u64))) { - pbl_tbl++; - pbe = (struct ocrdma_pbe *)pbl_tbl->va; - pbe_cnt = 0; - } + /* if done building pbes, issue the mbx cmd. */ + if (total_num_pbes == num_pbes) + return; + /* if the given pbl is full storing the pbes, + * move to next pbl. + */ + if (pbe_cnt == (mr->hwmr.pbl_size / sizeof(u64))) { + pbl_tbl++; + pbe = (struct ocrdma_pbe *)pbl_tbl->va; + pbe_cnt = 0; } } } @@ -912,7 +903,7 @@ struct ib_mr *ocrdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, if (status) goto umem_err; - mr->hwmr.pbe_size = BIT(mr->umem->page_shift); + mr->hwmr.pbe_size = PAGE_SIZE; mr->hwmr.fbo = ib_umem_offset(mr->umem); mr->hwmr.va = usr_addr; mr->hwmr.len = len; From 8317d6cdc1c7b8add6f2e5b915b5b5f80fdbfa5b Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Mon, 11 Feb 2019 09:25:07 -0600 Subject: [PATCH 1568/4277] RDMA/rxe: Use for_each_sg_page iterator on umem SGL The driver walks the umem SGL assuming a 1:1 mapping between SGE and system page. Update to use the for_each_sg_page iterator to get individual pages contained in the SGEs. This is a pre-requisite before adding page combining into SGEs while building the scatter table in IB core. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_mr.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c index 2438093776a0..42f0f25e396c 100644 --- a/drivers/infiniband/sw/rxe/rxe_mr.c +++ b/drivers/infiniband/sw/rxe/rxe_mr.c @@ -162,11 +162,10 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start, u64 length, u64 iova, int access, struct ib_udata *udata, struct rxe_mem *mem) { - int entry; struct rxe_map **map; struct rxe_phys_buf *buf = NULL; struct ib_umem *umem; - struct scatterlist *sg; + struct sg_page_iter sg_iter; int num_buf; void *vaddr; int err; @@ -191,16 +190,16 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start, goto err1; } - mem->page_shift = umem->page_shift; - mem->page_mask = BIT(umem->page_shift) - 1; + mem->page_shift = PAGE_SHIFT; + mem->page_mask = PAGE_SIZE - 1; num_buf = 0; map = mem->map; if (length > 0) { buf = map[0]->buf; - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { - vaddr = page_address(sg_page(sg)); + for_each_sg_page(umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { + vaddr = page_address(sg_page_iter_page(&sg_iter)); if (!vaddr) { pr_warn("null vaddr\n"); err = -ENOMEM; @@ -208,7 +207,7 @@ int rxe_mem_init_user(struct rxe_pd *pd, u64 start, } buf->addr = (uintptr_t)vaddr; - buf->size = BIT(umem->page_shift); + buf->size = PAGE_SIZE; num_buf++; buf++; From 0da4d48d99dfdb2a69172079f6a56e22689d16ce Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Mon, 11 Feb 2019 17:40:53 +0200 Subject: [PATCH 1569/4277] IB/mlx5: Fix bad flow upon DEVX mkey creation Fix bad flow upon DEVX mkey creation to prevent deleting the indirect mkey from the radix tree in case there was a previous failure to insert it. Fixes: 534fd7aac56a ("IB/mlx5: Manage indirection mkey upon DEVX flow for ODP") Signed-off-by: Yishai Hadas Reviewed-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/devx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index cd43e39ced87..8e6d23d6859f 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1204,14 +1204,15 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( err = uverbs_copy_to(attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_CMD_OUT, cmd_out, cmd_out_len); if (err) - goto obj_destroy; + goto err_copy; obj->obj_id = get_enc_obj_id(opcode, obj_id); return 0; -obj_destroy: +err_copy: if (obj->flags & DEVX_OBJ_FLAGS_INDIRECT_MKEY) devx_cleanup_mkey(obj); +obj_destroy: mlx5_cmd_exec(obj->mdev, obj->dinbox, obj->dinlen, out, sizeof(out)); obj_free: kfree(obj); From fc9e4477f924e84d7798f7a1d41401d699de1219 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Mon, 11 Feb 2019 17:40:54 +0200 Subject: [PATCH 1570/4277] RDMA/mlx5: Fix memory leak in case we fail to add an IB device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure the IB device is freed on failure. Fixes: b5ca15ad7e61 ("IB/mlx5: Add proper representors support") Signed-off-by: Mark Bloch Reviewed-by: Bodong Wang Reviewed-by: Håkon Bugge Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/ib_rep.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c index 6d7b8bad4b61..95ac97af6166 100644 --- a/drivers/infiniband/hw/mlx5/ib_rep.c +++ b/drivers/infiniband/hw/mlx5/ib_rep.c @@ -78,8 +78,10 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) ibdev->mdev = dev; ibdev->num_ports = max(MLX5_CAP_GEN(dev, num_ports), MLX5_CAP_GEN(dev, num_vhca_ports)); - if (!__mlx5_ib_add(ibdev, &rep_profile)) + if (!__mlx5_ib_add(ibdev, &rep_profile)) { + ib_dealloc_device(&ibdev->ib_dev); return -EINVAL; + } rep->rep_if[REP_IB].priv = ibdev; From a87145957eb9c474559b3acd2cfc6e8914b0e08f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 11 Feb 2019 13:34:15 +0000 Subject: [PATCH 1571/4277] RDMA/bnxt_re: fix or'ing of data into an uninitialized struct member The struct member comp_mask has not been initialized however a bit pattern is being bitwise or'd into the member and hence other bit fields in comp_mask may contain any garbage from the stack. Fix this by making the bitwise or into an assignment. Fixes: 95b86d1c91ad ("RDMA/bnxt_re: Update kernel user abi to pass chip context") Signed-off-by: Colin Ian King Acked-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index bff9320a968e..2ed778683c6b 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -3702,7 +3702,7 @@ struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev, } spin_lock_init(&uctx->sh_lock); - resp.comp_mask |= BNXT_RE_UCNTX_CMASK_HAVE_CCTX; + resp.comp_mask = BNXT_RE_UCNTX_CMASK_HAVE_CCTX; chip_met_rev_num = rdev->chip_ctx.chip_num; chip_met_rev_num |= ((u32)rdev->chip_ctx.chip_rev & 0xFF) << BNXT_RE_CHIP_ID0_CHIP_REV_SFT; From dbbfadf2319005cf528b0f15f12a05d4e4644303 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 9 Jan 2019 08:22:08 -0600 Subject: [PATCH 1572/4277] PCI/ASPM: Save LTR Capability for suspend/resume Latency Tolerance Reporting (LTR) allows Endpoints and Switch Upstream Ports to report their latency requirements to upstream components. If ASPM L1 PM substates are enabled, the LTR information helps determine when a Link enters L1.2 [1]. Software must set the maximum latency values in the LTR Capability based on characteristics of the platform, then set LTR Mechanism Enable in the Device Control 2 register in the PCIe Capability. The device can then use LTR to report its latency tolerance. If the device reports a maximum latency value of zero, that means the device requires the highest possible performance and the ASPM L1.2 substate is effectively disabled. We put devices in D3 for suspend, and we assume their internal state is lost. On resume, previously we did not restore the LTR Capability, but we did restore the LTR Mechanism Enable bit, so devices would request the highest possible performance and ASPM L1.2 wouldn't be used. [1] PCIe r4.0, sec 5.5.1 Link: https://bugzilla.kernel.org/show_bug.cgi?id=201469 Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9d8e3c837de..13d65991c77b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1233,7 +1233,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev) pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]); } - static int pci_save_pcix_state(struct pci_dev *dev) { int pos; @@ -1270,6 +1269,45 @@ static void pci_restore_pcix_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]); } +static void pci_save_ltr_state(struct pci_dev *dev) +{ + int ltr; + struct pci_cap_saved_state *save_state; + u16 *cap; + + if (!pci_is_pcie(dev)) + return; + + ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); + if (!ltr) + return; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR); + if (!save_state) { + pci_err(dev, "no suspend buffer for LTR; ASPM issues possible after resume\n"); + return; + } + + cap = (u16 *)&save_state->cap.data[0]; + pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap++); + pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, cap++); +} + +static void pci_restore_ltr_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state; + int ltr; + u16 *cap; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR); + ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); + if (!save_state || !ltr) + return; + + cap = (u16 *)&save_state->cap.data[0]; + pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap++); + pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, *cap++); +} /** * pci_save_state - save the PCI configuration space of a device before suspending @@ -1291,6 +1329,7 @@ int pci_save_state(struct pci_dev *dev) if (i != 0) return i; + pci_save_ltr_state(dev); pci_save_dpc_state(dev); return pci_save_vc_state(dev); } @@ -1390,7 +1429,12 @@ void pci_restore_state(struct pci_dev *dev) if (!dev->state_saved) return; - /* PCI Express register must be restored first */ + /* + * Restore max latencies (in the LTR capability) before enabling + * LTR itself (in the PCIe capability). + */ + pci_restore_ltr_state(dev); + pci_restore_pcie_state(dev); pci_restore_pasid_state(dev); pci_restore_pri_state(dev); @@ -2998,6 +3042,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) if (error) pci_err(dev, "unable to preallocate PCI-X save buffer\n"); + error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_LTR, + 2 * sizeof(u16)); + if (error) + pci_err(dev, "unable to allocate suspend buffer for LTR\n"); + pci_allocate_vc_save_buffers(dev); } From a80554fc36ba41d96af8e72fb54cd5d490e06c54 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Fri, 8 Feb 2019 09:36:48 +0000 Subject: [PATCH 1573/4277] arm64: irqflags: Fix clang build warnings Clang complains when passing asm operands that are smaller than the registers they are mapped to: arch/arm64/include/asm/irqflags.h:50:10: warning: value size does not match register size specified by the constraint and modifier [-Wasm-operand-widths] : "r" (GIC_PRIO_IRQON) Fix it by casting the affected input operands to a type of the correct size. Reported-by: Nathan Chancellor Tested-by: Nathan Chancellor Signed-off-by: Julien Thierry Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/irqflags.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/irqflags.h b/arch/arm64/include/asm/irqflags.h index d4597b2c5729..43d8366c1e87 100644 --- a/arch/arm64/include/asm/irqflags.h +++ b/arch/arm64/include/asm/irqflags.h @@ -47,7 +47,7 @@ static inline void arch_local_irq_enable(void) "dsb sy", ARM64_HAS_IRQ_PRIO_MASKING) : - : "r" (GIC_PRIO_IRQON) + : "r" ((unsigned long) GIC_PRIO_IRQON) : "memory"); } @@ -58,7 +58,7 @@ static inline void arch_local_irq_disable(void) "msr_s " __stringify(SYS_ICC_PMR_EL1) ", %0", ARM64_HAS_IRQ_PRIO_MASKING) : - : "r" (GIC_PRIO_IRQOFF) + : "r" ((unsigned long) GIC_PRIO_IRQOFF) : "memory"); } @@ -91,7 +91,7 @@ static inline unsigned long arch_local_save_flags(void) "csel %0, %0, %2, eq", ARM64_HAS_IRQ_PRIO_MASKING) : "=&r" (flags), "+r" (daif_bits) - : "r" (GIC_PRIO_IRQOFF) + : "r" ((unsigned long) GIC_PRIO_IRQOFF) : "memory"); return flags; From 65ab26e397555d340290a99e6c736e291526ecc0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Jan 2019 23:49:21 +0100 Subject: [PATCH 1574/4277] selftests: kvm: add selftest for releasing VM file descriptor while in L2 This adds a test for the previous bug. Signed-off-by: Paolo Bonzini --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../kvm/x86_64/vmx_close_while_nested_test.c | 95 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 6210ba41c29e..2689d1ea6d7a 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -3,6 +3,7 @@ /x86_64/platform_info_test /x86_64/set_sregs_test /x86_64/sync_regs_test +/x86_64/vmx_close_while_nested_test /x86_64/vmx_tsc_adjust_test /x86_64/state_test /dirty_log_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index f9a0e9938480..3c1f4bdf9000 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -16,6 +16,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/cr4_cpuid_sync_test TEST_GEN_PROGS_x86_64 += x86_64/state_test TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid +TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test TEST_GEN_PROGS_x86_64 += dirty_log_test TEST_GEN_PROGS_x86_64 += clear_dirty_log_test diff --git a/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c b/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c new file mode 100644 index 000000000000..6edec6fd790b --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c @@ -0,0 +1,95 @@ +/* + * vmx_close_while_nested + * + * Copyright (C) 2019, Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2. + * + * Verify that nothing bad happens if a KVM user exits with open + * file descriptors while executing a nested guest. + */ + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "vmx.h" + +#include +#include + +#include "kselftest.h" + +#define VCPU_ID 5 + +enum { + PORT_L0_EXIT = 0x2000, +}; + +/* The virtual machine object. */ +static struct kvm_vm *vm; + +static void l2_guest_code(void) +{ + /* Exit to L0 */ + asm volatile("inb %%dx, %%al" + : : [port] "d" (PORT_L0_EXIT) : "rax"); +} + +static void l1_guest_code(struct vmx_pages *vmx_pages) +{ +#define L2_GUEST_STACK_SIZE 64 + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + uint32_t control; + uintptr_t save_cr3; + + GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); + GUEST_ASSERT(load_vmcs(vmx_pages)); + + /* Prepare the VMCS for L2 execution. */ + prepare_vmcs(vmx_pages, l2_guest_code, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + GUEST_ASSERT(!vmlaunch()); + GUEST_ASSERT(0); +} + +int main(int argc, char *argv[]) +{ + struct vmx_pages *vmx_pages; + vm_vaddr_t vmx_pages_gva; + struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1); + + if (!(entry->ecx & CPUID_VMX)) { + fprintf(stderr, "nested VMX not enabled, skipping test\n"); + exit(KSFT_SKIP); + } + + vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + + /* Allocate VMX pages and shared descriptors (vmx_pages). */ + vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva); + vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + + for (;;) { + volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct ucall uc; + + vcpu_run(vm, VCPU_ID); + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", + run->exit_reason, + exit_reason_str(run->exit_reason)); + + if (run->io.port == PORT_L0_EXIT) + break; + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_ABORT: + TEST_ASSERT(false, "%s", (const char *)uc.args[0]); + /* NOT REACHED */ + default: + TEST_ASSERT(false, "Unknown ucall 0x%x.", uc.cmd); + } + } +} From 61c08aa9606d4e48a8a50639c956448a720174c3 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:48 -0800 Subject: [PATCH 1575/4277] KVM: VMX: Compare only a single byte for VMCS' "launched" in vCPU-run The vCPU-run asm blob does a manual comparison of a VMCS' launched status to execute the correct VM-Enter instruction, i.e. VMLAUNCH vs. VMRESUME. The launched flag is a bool, which is a typedef of _Bool. C99 does not define an exact size for _Bool, stating only that is must be large enough to hold '0' and '1'. Most, if not all, compilers use a single byte for _Bool, including gcc[1]. Originally, 'launched' was of type 'int' and so the asm blob used 'cmpl' to check the launch status. When 'launched' was moved to be stored on a per-VMCS basis, struct vcpu_vmx's "temporary" __launched flag was added in order to avoid having to pass the current VMCS into the asm blob. The new '__launched' was defined as a 'bool' and not an 'int', but the 'cmp' instruction was not updated. This has not caused any known problems, likely due to compilers aligning variables to 4-byte or 8-byte boundaries and KVM zeroing out struct vcpu_vmx during allocation. I.e. vCPU-run accesses "junk" data, it just happens to always be zero and so doesn't affect the result. [1] https://gcc.gnu.org/ml/gcc-patches/2000-10/msg01127.html Fixes: d462b8192368 ("KVM: VMX: Keep list of loaded VMCSs, instead of vcpus") Cc: Reviewed-by: Jim Mattson Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 95d618045001..fdb6305cc971 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6402,7 +6402,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "mov %%" _ASM_AX", %%cr2 \n\t" "3: \n\t" /* Check if vmlaunch or vmresume is needed */ - "cmpl $0, %c[launched](%%" _ASM_CX ") \n\t" + "cmpb $0, %c[launched](%%" _ASM_CX ") \n\t" /* Load guest registers. Don't clobber flags. */ "mov %c[rax](%%" _ASM_CX "), %%" _ASM_AX " \n\t" "mov %c[rbx](%%" _ASM_CX "), %%" _ASM_BX " \n\t" From 1ce072cbfd8dba46f117804850398e0b3040a541 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:49 -0800 Subject: [PATCH 1576/4277] KVM: nVMX: Check a single byte for VMCS "launched" in nested early checks Nested early checks does a manual comparison of a VMCS' launched status in its asm blob to execute the correct VM-Enter instruction, i.e. VMLAUNCH vs. VMRESUME. The launched flag is a bool, which is a typedef of _Bool. C99 does not define an exact size for _Bool, stating only that is must be large enough to hold '0' and '1'. Most, if not all, compilers use a single byte for _Bool, including gcc[1]. The use of 'cmpl' instead of 'cmpb' was not deliberate, but rather the result of a copy-paste as the asm blob was directly derived from the asm blob for vCPU-run. This has not caused any known problems, likely due to compilers aligning variables to 4-byte or 8-byte boundaries and KVM zeroing out struct vcpu_vmx during allocation. I.e. vCPU-run accesses "junk" data, it just happens to always be zero and so doesn't affect the result. [1] https://gcc.gnu.org/ml/gcc-patches/2000-10/msg01127.html Fixes: 52017608da33 ("KVM: nVMX: add option to perform early consistency checks via H/W") Cc: Reviewed-by: Jim Mattson Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index d8ea4ebd79e7..50c9e04910ea 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2761,7 +2761,7 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ /* Check if vmlaunch or vmresume is needed */ - "cmpl $0, %c[launched](%% " _ASM_CX")\n\t" + "cmpb $0, %c[launched](%% " _ASM_CX")\n\t" "call vmx_vmenter\n\t" From 0e0ab73c9a0243736bcd779b30b717e23ba9a56d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:50 -0800 Subject: [PATCH 1577/4277] KVM: VMX: Zero out *all* general purpose registers after VM-Exit ...except RSP, which is restored by hardware as part of VM-Exit. Paolo theorized that restoring registers from the stack after a VM-Exit in lieu of zeroing them could lead to speculative execution with the guest's values, e.g. if the stack accesses miss the L1 cache[1]. Zeroing XORs are dirt cheap, so just be ultra-paranoid. Note that the scratch register (currently RCX) used to save/restore the guest state is also zeroed as its host-defined value is loaded via the stack, just with a MOV instead of a POP. [1] https://patchwork.kernel.org/patch/10771539/#22441255 Fixes: 0cb5b30698fd ("kvm: vmx: Scrub hardware GPRs at VM-exit") Cc: Cc: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index fdb6305cc971..10fee67a6dcd 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6452,10 +6452,15 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "mov %%r13, %c[r13](%%" _ASM_CX ") \n\t" "mov %%r14, %c[r14](%%" _ASM_CX ") \n\t" "mov %%r15, %c[r15](%%" _ASM_CX ") \n\t" + /* - * Clear host registers marked as clobbered to prevent - * speculative use. - */ + * Clear all general purpose registers (except RSP, which is loaded by + * the CPU during VM-Exit) to prevent speculative use of the guest's + * values, even those that are saved/loaded via the stack. In theory, + * an L1 cache miss when restoring registers could lead to speculative + * execution with the guest's values. Zeroing XORs are dirt cheap, + * i.e. the extra paranoia is essentially free. + */ "xor %%r8d, %%r8d \n\t" "xor %%r9d, %%r9d \n\t" "xor %%r10d, %%r10d \n\t" @@ -6470,8 +6475,11 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%eax, %%eax \n\t" "xor %%ebx, %%ebx \n\t" + "xor %%ecx, %%ecx \n\t" + "xor %%edx, %%edx \n\t" "xor %%esi, %%esi \n\t" "xor %%edi, %%edi \n\t" + "xor %%ebp, %%ebp \n\t" "pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" : ASM_CALL_CONSTRAINT : "c"(vmx), "d"((unsigned long)HOST_RSP), "S"(evmcs_rsp), From 831a3011294d9aa284afae9194619893d454d708 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:51 -0800 Subject: [PATCH 1578/4277] KVM: VMX: Modify only RSP when creating a placeholder for guest's RCX In the vCPU-run asm blob, the guest's RCX is temporarily saved onto the stack after VM-Exit as the exit flow must first load a register with a pointer to the vCPU's save area in order to save the guest's registers. RCX is arbitrarily designated as the scratch register. Since the stack usage is to (1)save host, (2)save guest, (3)load host and (4)load guest, the code can't conform to the stack's natural FIFO semantics, i.e. it can't simply do PUSH/POP. Regardless of whether it is done for the host's value or guest's value, at some point the code needs to access the stack using a non-traditional method, e.g. MOV instead of POP. vCPU-run opts to create a placeholder on the stack for guest's RCX (by adjusting RSP) and saves RCX to its place immediately after VM-Exit (via MOV). In other words, the purpose of the first 'PUSH RCX' at the start of the vCPU-run asm blob is to adjust RSP down, i.e. there's no need to actually access memory. Use 'SUB $wordsize, RSP' instead of 'PUSH RCX' to make it more obvious that the intent is simply to create a gap on the stack for the guest's RCX. Reviewed-by: Jim Mattson Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 10fee67a6dcd..cb0dd17c31c6 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6378,7 +6378,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) asm( /* Store host registers */ "push %%" _ASM_DX "; push %%" _ASM_BP ";" - "push %%" _ASM_CX " \n\t" /* placeholder for guest rcx */ + "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* placeholder for guest RCX */ "push %%" _ASM_CX " \n\t" "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ "cmp %%" _ASM_SP ", %c[host_rsp](%%" _ASM_CX ") \n\t" From f3689e3f17f064fd4cd5f0cb01ae2395c94f39d9 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:52 -0800 Subject: [PATCH 1579/4277] KVM: VMX: Save RSI to an unused output in the vCPU-run asm blob RSI is clobbered by the vCPU-run asm blob, but it's not marked as such, probably because GCC doesn't let you mark inputs as clobbered. "Save" RSI to a dummy output so that GCC recognizes it as being clobbered. Fixes: 773e8a0425c9 ("x86/kvm: use Enlightened VMCS when running on Hyper-V") Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index cb0dd17c31c6..3ae51a09f420 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6481,7 +6481,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%edi, %%edi \n\t" "xor %%ebp, %%ebp \n\t" "pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" - : ASM_CALL_CONSTRAINT + : ASM_CALL_CONSTRAINT, "=S"((int){0}) : "c"(vmx), "d"((unsigned long)HOST_RSP), "S"(evmcs_rsp), [launched]"i"(offsetof(struct vcpu_vmx, __launched)), [fail]"i"(offsetof(struct vcpu_vmx, fail)), From ccf447434ee624b64a1c215d88453f1921aaa88a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:53 -0800 Subject: [PATCH 1580/4277] KVM: VMX: Manually load RDX in vCPU-run asm blob Load RDX with the VMCS.HOST_RSP field encoding on-demand instead of delegating to the compiler via an input constraint. In addition to saving one whole MOV instruction, this allows RDX to be properly clobbered (in a future patch) instead of being saved/loaded to/from the stack. Despite nested_vmx_check_vmentry_hw() having similar code, leave it alone, for now. In that case, RDX is unconditionally used and isn't clobbered, i.e. sending in HOST_RSP as an input is simpler. Note that because HOST_RSP is an enum and not a define, it must be redefined as an immediate instead of using __stringify(HOST_RSP). The naming "conflict" between host_rsp and HOST_RSP is slightly confusing, but the former will be removed in a future patch, at which point HOST_RSP is absolutely what is desired. Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 3ae51a09f420..b935ba3306aa 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6390,6 +6390,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "mov %%" _ASM_SP ", (%%" _ASM_SI ") \n\t" "jmp 1f \n\t" "2: \n\t" + "mov $%c[HOST_RSP], %%" _ASM_DX " \n\t" __ex("vmwrite %%" _ASM_SP ", %%" _ASM_DX) "\n\t" "1: \n\t" "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ @@ -6482,10 +6483,11 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%ebp, %%ebp \n\t" "pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" : ASM_CALL_CONSTRAINT, "=S"((int){0}) - : "c"(vmx), "d"((unsigned long)HOST_RSP), "S"(evmcs_rsp), + : "c"(vmx), "S"(evmcs_rsp), [launched]"i"(offsetof(struct vcpu_vmx, __launched)), [fail]"i"(offsetof(struct vcpu_vmx, fail)), [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), + [HOST_RSP]"i"(HOST_RSP), [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])), [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])), [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])), From 6f7c6d23b71af2e65056e777eeb2f1e6e0195f14 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:54 -0800 Subject: [PATCH 1581/4277] KVM: VMX: Let the compiler save/load RDX during vCPU-run Per commit c20363006af6 ("KVM: VMX: Let gcc to choose which registers to save (x86_64)"), the only reason RDX is saved/loaded to/from the stack is because it was specified as an input, i.e. couldn't be marked as clobbered (ignoring the fact that "saving" it to a dummy output would indirectly mark it as clobbered). Now that RDX is no longer an input, clobber it. Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b935ba3306aa..e9d81cd8421f 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6377,7 +6377,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) asm( /* Store host registers */ - "push %%" _ASM_DX "; push %%" _ASM_BP ";" + "push %%" _ASM_BP " \n\t" "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* placeholder for guest RCX */ "push %%" _ASM_CX " \n\t" "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ @@ -6481,7 +6481,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%esi, %%esi \n\t" "xor %%edi, %%edi \n\t" "xor %%ebp, %%ebp \n\t" - "pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" + "pop %%" _ASM_BP " \n\t" : ASM_CALL_CONSTRAINT, "=S"((int){0}) : "c"(vmx), "S"(evmcs_rsp), [launched]"i"(offsetof(struct vcpu_vmx, __launched)), @@ -6509,10 +6509,10 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) [wordsize]"i"(sizeof(ulong)) : "cc", "memory" #ifdef CONFIG_X86_64 - , "rax", "rbx", "rdi" + , "rax", "rbx", "rdx", "rdi" , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" #else - , "eax", "ebx", "edi" + , "eax", "ebx", "edx", "edi" #endif ); } From 9ce0a07a6f49822238fd4357c02e0dba060a43cc Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:55 -0800 Subject: [PATCH 1582/4277] KVM: nVMX: Remove a rogue "rax" clobber from nested_vmx_check_vmentry_hw() RAX is not touched by nested_vmx_check_vmentry_hw(), directly or indirectly (e.g. vmx_vmenter()). Remove it from the clobber list. Fixes: 52017608da33 ("KVM: nVMX: add option to perform early consistency checks via H/W") Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 50c9e04910ea..bb85e3a05424 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2773,7 +2773,7 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) [fail]"i"(offsetof(struct vcpu_vmx, fail)), [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), [wordsize]"i"(sizeof(ulong)) - : "rax", "cc", "memory" + : "cc", "memory" ); preempt_enable(); From 98ff2acc91d836277d34558fbbba7678e04281ed Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:56 -0800 Subject: [PATCH 1583/4277] KVM: nVMX: Drop STACK_FRAME_NON_STANDARD from nested_vmx_check_vmentry_hw() ...as it doesn't technically actually do anything non-standard with the stack even though it modifies RSP in a weird way. E.g. RSP is loaded with VMCS.HOST_RSP if the VM-Enter gets far enough to trigger VM-Exit, but it's simply reloaded with the current value. Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index bb85e3a05424..e40df725b952 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2809,8 +2809,6 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) return 0; } -STACK_FRAME_NON_STANDARD(nested_vmx_check_vmentry_hw); - static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12); From 6c1e7e5b40f23b9e754a47852924115febba35df Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:57 -0800 Subject: [PATCH 1584/4277] KVM: nVMX: Explicitly reference the scratch reg in nested early checks Using %1 to reference RCX, i.e. the 'vmx' pointer', is obtuse and fragile, e.g. it results in cryptic and infurating compile errors if the output constraints are touched by anything more than a gentle breeze. Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index e40df725b952..a562ecabc118 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2757,7 +2757,7 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) /* Set HOST_RSP */ "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ __ex("vmwrite %%" _ASM_SP ", %%" _ASM_DX) "\n\t" - "mov %%" _ASM_SP ", %c[host_rsp](%1)\n\t" + "mov %%" _ASM_SP ", %c[host_rsp](%% " _ASM_CX ")\n\t" "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ /* Check if vmlaunch or vmresume is needed */ From f1727b4954772a778df0b73a93c4b646fd3c21f6 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:58 -0800 Subject: [PATCH 1585/4277] KVM: nVMX: Capture VM-Fail to a local var in nested_vmx_check_vmentry_hw() Unlike the primary vCPU-run flow, the nested early checks code doesn't actually want to propagate VM-Fail back to 'vmx'. Yay copy+paste. In additional to eliminating the need to clear vmx->fail before returning, using a local boolean also drops a reference to 'vmx' in the asm blob. Dropping the reference to 'vmx' will save a register in the long run as future patches will shift all pointer references from 'vmx' to 'vmx->loaded_vmcs'. Fixes: 52017608da33 ("KVM: nVMX: add option to perform early consistency checks via H/W") Signed-off-by: Sean Christopherson Reviewed-by: Jim Mattson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index a562ecabc118..bfacf9029466 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2718,6 +2718,7 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long cr3, cr4; + bool vm_fail; if (!nested_early_check) return 0; @@ -2763,14 +2764,18 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) /* Check if vmlaunch or vmresume is needed */ "cmpb $0, %c[launched](%% " _ASM_CX")\n\t" + /* + * VMLAUNCH and VMRESUME clear RFLAGS.{CF,ZF} on VM-Exit, set + * RFLAGS.CF on VM-Fail Invalid and set RFLAGS.ZF on VM-Fail + * Valid. vmx_vmenter() directly "returns" RFLAGS, and so the + * results of VM-Enter is captured via SETBE to vm_fail. + */ "call vmx_vmenter\n\t" - /* Set vmx->fail accordingly */ - "setbe %c[fail](%% " _ASM_CX")\n\t" - : ASM_CALL_CONSTRAINT + "setbe %[fail]\n\t" + : ASM_CALL_CONSTRAINT, [fail]"=qm"(vm_fail) : "c"(vmx), "d"((unsigned long)HOST_RSP), [launched]"i"(offsetof(struct vcpu_vmx, __launched)), - [fail]"i"(offsetof(struct vcpu_vmx, fail)), [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), [wordsize]"i"(sizeof(ulong)) : "cc", "memory" @@ -2783,10 +2788,9 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) if (vmx->msr_autoload.guest.nr) vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr); - if (vmx->fail) { + if (vm_fail) { WARN_ON_ONCE(vmcs_read32(VM_INSTRUCTION_ERROR) != VMXERR_ENTRY_INVALID_CONTROL_FIELD); - vmx->fail = 0; return 1; } From bbc0b8239257d91cd43fe219de5552dee8bb9123 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:40:59 -0800 Subject: [PATCH 1586/4277] KVM: nVMX: Capture VM-Fail via CC_{SET,OUT} in nested early checks ...to take advantage of __GCC_ASM_FLAG_OUTPUTS__ when possible. Reviewed-by: Jim Mattson Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index bfacf9029466..67f8fdf568eb 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2768,12 +2768,12 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) * VMLAUNCH and VMRESUME clear RFLAGS.{CF,ZF} on VM-Exit, set * RFLAGS.CF on VM-Fail Invalid and set RFLAGS.ZF on VM-Fail * Valid. vmx_vmenter() directly "returns" RFLAGS, and so the - * results of VM-Enter is captured via SETBE to vm_fail. + * results of VM-Enter is captured via CC_{SET,OUT} to vm_fail. */ "call vmx_vmenter\n\t" - "setbe %[fail]\n\t" - : ASM_CALL_CONSTRAINT, [fail]"=qm"(vm_fail) + CC_SET(be) + : ASM_CALL_CONSTRAINT, CC_OUT(be) (vm_fail) : "c"(vmx), "d"((unsigned long)HOST_RSP), [launched]"i"(offsetof(struct vcpu_vmx, __launched)), [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), From 74dfa2784e961fae66143b811f45105b43c63046 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:00 -0800 Subject: [PATCH 1587/4277] KVM: nVMX: Reference vmx->loaded_vmcs->launched directly Temporarily propagating vmx->loaded_vmcs->launched to vmx->__launched is not functionally necessary, but rather was done historically to avoid passing both 'vmx' and 'loaded_vmcs' to the vCPU-run asm blob. Nested early checks inherited this behavior by virtue of copy+paste. A future patch will move HOST_RSP caching to be per-VMCS, i.e. store 'host_rsp' in loaded VMCS. Now that the reference to 'vmx->fail' is also gone from nested early checks, referencing 'loaded_vmcs' directly means we can drop the 'vmx' reference when introducing per-VMCS RSP caching. And it means __launched can be dropped from struct vcpu_vmx if/when vCPU-run receives similar treatment. Note the use of a named register constraint for 'loaded_vmcs'. Using RCX to hold 'vmx' was inherited from vCPU-run. In the vCPU-run case, the scratch register needs to be explicitly defined as it is crushed when loading guest state, i.e. deferring to the compiler would corrupt the pointer. Since nested early checks never loads guests state, it's a-ok to let the compiler pick any register. Naming the constraint avoids the fragility of referencing constraints via %1, %2, etc.., which breaks horribly when modifying constraints, and generally makes the asm blob more readable. Signed-off-by: Sean Christopherson Reviewed-by: Jim Mattson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 67f8fdf568eb..47299e10522f 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2752,8 +2752,6 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) vmx->loaded_vmcs->host_state.cr4 = cr4; } - vmx->__launched = vmx->loaded_vmcs->launched; - asm( /* Set HOST_RSP */ "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ @@ -2762,7 +2760,7 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ /* Check if vmlaunch or vmresume is needed */ - "cmpb $0, %c[launched](%% " _ASM_CX")\n\t" + "cmpb $0, %c[launched](%[loaded_vmcs])\n\t" /* * VMLAUNCH and VMRESUME clear RFLAGS.{CF,ZF} on VM-Exit, set @@ -2775,7 +2773,8 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) CC_SET(be) : ASM_CALL_CONSTRAINT, CC_OUT(be) (vm_fail) : "c"(vmx), "d"((unsigned long)HOST_RSP), - [launched]"i"(offsetof(struct vcpu_vmx, __launched)), + [loaded_vmcs]"r"(vmx->loaded_vmcs), + [launched]"i"(offsetof(struct loaded_vmcs, launched)), [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), [wordsize]"i"(sizeof(ulong)) : "cc", "memory" From fbda0fd31a6d683637f848ba17956048dd0c7e48 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:01 -0800 Subject: [PATCH 1588/4277] KVM: nVMX: Let the compiler select the reg for holding HOST_RSP ...and provide an explicit name for the constraint. Naming the input constraint makes the code self-documenting and also avoids the fragility of numerically referring to constraints, e.g. %4 breaks badly whenever the constraints are modified. Explicitly using RDX was inherited from vCPU-run, i.e. completely arbitrary. Even vCPU-run doesn't truly need to explicitly use RDX, but doing so is more robust as vCPU-run needs tight control over its register usage. Note that while the naming "conflict" between host_rsp and HOST_RSP is slightly confusing, the former will be renamed slightly in a future patch, at which point HOST_RSP is absolutely what is desired. Signed-off-by: Sean Christopherson Reviewed-by: Jim Mattson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 47299e10522f..23a2c1b91389 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2753,9 +2753,8 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) } asm( - /* Set HOST_RSP */ "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ - __ex("vmwrite %%" _ASM_SP ", %%" _ASM_DX) "\n\t" + __ex("vmwrite %%" _ASM_SP ", %[HOST_RSP]") "\n\t" "mov %%" _ASM_SP ", %c[host_rsp](%% " _ASM_CX ")\n\t" "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ @@ -2772,7 +2771,8 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) CC_SET(be) : ASM_CALL_CONSTRAINT, CC_OUT(be) (vm_fail) - : "c"(vmx), "d"((unsigned long)HOST_RSP), + : "c"(vmx), + [HOST_RSP]"r"((unsigned long)HOST_RSP), [loaded_vmcs]"r"(vmx->loaded_vmcs), [launched]"i"(offsetof(struct loaded_vmcs, launched)), [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), From 5a8781607e677eda60b20e0a4c91d2a5f12f9244 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:02 -0800 Subject: [PATCH 1589/4277] KVM: nVMX: Cache host_rsp on a per-VMCS basis Currently, host_rsp is cached on a per-vCPU basis, i.e. it's stored in struct vcpu_vmx. In non-nested usage the caching is for all intents and purposes 100% effective, e.g. only the first VMLAUNCH needs to synchronize VMCS.HOST_RSP since the call stack to vmx_vcpu_run() is identical each and every time. But when running a nested guest, KVM must invalidate the cache when switching the current VMCS as it can't guarantee the new VMCS has the same HOST_RSP as the previous VMCS. In other words, the cache loses almost all of its efficacy when running a nested VM. Move host_rsp to struct vmcs_host_state, which is per-VMCS, so that it is cached on a per-VMCS basis and restores its 100% hit rate when nested VMs are in play. Note that the host_rsp cache for vmcs02 essentially "breaks" when nested early checks are enabled as nested_vmx_check_vmentry_hw() will see a different RSP at the time of its VM-Enter. While it's possible to avoid even that VMCS.HOST_RSP synchronization, e.g. by employing a dedicated VM-Exit stack, there is little motivation for doing so as the overhead of two VMWRITEs (~55 cycles) is dwarfed by the overhead of the extra VMX transition (600+ cycles) and is a proverbial drop in the ocean relative to the total cost of a nested transtion (10s of thousands of cycles). Signed-off-by: Sean Christopherson Reviewed-by: Jim Mattson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 24 ++++++------------------ arch/x86/kvm/vmx/vmcs.h | 1 + arch/x86/kvm/vmx/vmx.c | 13 ++++++------- arch/x86/kvm/vmx/vmx.h | 1 - 4 files changed, 13 insertions(+), 26 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 23a2c1b91389..0e67649e39ce 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -1979,17 +1979,6 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) if (vmx->nested.dirty_vmcs12 || vmx->nested.hv_evmcs) prepare_vmcs02_early_full(vmx, vmcs12); - /* - * HOST_RSP is normally set correctly in vmx_vcpu_run() just before - * entry, but only if the current (host) sp changed from the value - * we wrote last (vmx->host_rsp). This cache is no longer relevant - * if we switch vmcs, and rather than hold a separate cache per vmcs, - * here we just force the write to happen on entry. host_rsp will - * also be written unconditionally by nested_vmx_check_vmentry_hw() - * if we are doing early consistency checks via hardware. - */ - vmx->host_rsp = 0; - /* * PIN CONTROLS */ @@ -2754,8 +2743,11 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) asm( "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ + "cmp %%" _ASM_SP ", %c[host_state_rsp](%[loaded_vmcs]) \n\t" + "je 1f \n\t" __ex("vmwrite %%" _ASM_SP ", %[HOST_RSP]") "\n\t" - "mov %%" _ASM_SP ", %c[host_rsp](%% " _ASM_CX ")\n\t" + "mov %%" _ASM_SP ", %c[host_state_rsp](%[loaded_vmcs]) \n\t" + "1: \n\t" "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ /* Check if vmlaunch or vmresume is needed */ @@ -2771,11 +2763,10 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu) CC_SET(be) : ASM_CALL_CONSTRAINT, CC_OUT(be) (vm_fail) - : "c"(vmx), - [HOST_RSP]"r"((unsigned long)HOST_RSP), + : [HOST_RSP]"r"((unsigned long)HOST_RSP), [loaded_vmcs]"r"(vmx->loaded_vmcs), [launched]"i"(offsetof(struct loaded_vmcs, launched)), - [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), + [host_state_rsp]"i"(offsetof(struct loaded_vmcs, host_state.rsp)), [wordsize]"i"(sizeof(ulong)) : "cc", "memory" ); @@ -3912,9 +3903,6 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, vmx_flush_tlb(vcpu, true); } - /* This is needed for same reason as it was needed in prepare_vmcs02 */ - vmx->host_rsp = 0; - /* Unpin physical memory we referred to in vmcs02 */ if (vmx->nested.apic_access_page) { kvm_release_page_dirty(vmx->nested.apic_access_page); diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h index 6def3ba88e3b..cb6079f8a227 100644 --- a/arch/x86/kvm/vmx/vmcs.h +++ b/arch/x86/kvm/vmx/vmcs.h @@ -34,6 +34,7 @@ struct vmcs_host_state { unsigned long cr4; /* May not match real cr4 */ unsigned long gs_base; unsigned long fs_base; + unsigned long rsp; u16 fs_sel, gs_sel, ldt_sel; #ifdef CONFIG_X86_64 diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index e9d81cd8421f..12bb61e7aca6 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6381,9 +6381,9 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* placeholder for guest RCX */ "push %%" _ASM_CX " \n\t" "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ - "cmp %%" _ASM_SP ", %c[host_rsp](%%" _ASM_CX ") \n\t" + "cmp %%" _ASM_SP ", (%%" _ASM_DI ") \n\t" "je 1f \n\t" - "mov %%" _ASM_SP ", %c[host_rsp](%%" _ASM_CX ") \n\t" + "mov %%" _ASM_SP ", (%%" _ASM_DI ") \n\t" /* Avoid VMWRITE when Enlightened VMCS is in use */ "test %%" _ASM_SI ", %%" _ASM_SI " \n\t" "jz 2f \n\t" @@ -6482,11 +6482,10 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%edi, %%edi \n\t" "xor %%ebp, %%ebp \n\t" "pop %%" _ASM_BP " \n\t" - : ASM_CALL_CONSTRAINT, "=S"((int){0}) - : "c"(vmx), "S"(evmcs_rsp), + : ASM_CALL_CONSTRAINT, "=D"((int){0}), "=S"((int){0}) + : "c"(vmx), "D"(&vmx->loaded_vmcs->host_state.rsp), "S"(evmcs_rsp), [launched]"i"(offsetof(struct vcpu_vmx, __launched)), [fail]"i"(offsetof(struct vcpu_vmx, fail)), - [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)), [HOST_RSP]"i"(HOST_RSP), [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])), [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])), @@ -6509,10 +6508,10 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) [wordsize]"i"(sizeof(ulong)) : "cc", "memory" #ifdef CONFIG_X86_64 - , "rax", "rbx", "rdx", "rdi" + , "rax", "rbx", "rdx" , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" #else - , "eax", "ebx", "edx", "edi" + , "eax", "ebx", "edx" #endif ); } diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 99328954c2fc..8e203b725928 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -175,7 +175,6 @@ struct nested_vmx { struct vcpu_vmx { struct kvm_vcpu vcpu; - unsigned long host_rsp; u8 fail; u8 msr_bitmap_mode; u32 exit_intr_info; From 47e97c099bbcb3211b22456679991095c0578da2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:03 -0800 Subject: [PATCH 1590/4277] KVM: VMX: Load/save guest CR2 via C code in __vmx_vcpu_run() ...to eliminate its parameter and struct vcpu_vmx offset definition from the assembly blob. Accessing CR2 from C versus assembly doesn't change the likelihood of taking a page fault (and modifying CR2) while it's loaded with the guest's value, so long as we don't do anything silly between accessing CR2 and VM-Enter/VM-Exit. Signed-off-by: Sean Christopherson Reviewed-by: Jim Mattson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 12bb61e7aca6..5e43999ece1d 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6375,6 +6375,9 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) if (static_branch_unlikely(&vmx_l1d_should_flush)) vmx_l1d_flush(vcpu); + if (vcpu->arch.cr2 != read_cr2()) + write_cr2(vcpu->arch.cr2); + asm( /* Store host registers */ "push %%" _ASM_BP " \n\t" @@ -6395,13 +6398,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "1: \n\t" "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ - /* Reload cr2 if changed */ - "mov %c[cr2](%%" _ASM_CX "), %%" _ASM_AX " \n\t" - "mov %%cr2, %%" _ASM_DX " \n\t" - "cmp %%" _ASM_AX ", %%" _ASM_DX " \n\t" - "je 3f \n\t" - "mov %%" _ASM_AX", %%cr2 \n\t" - "3: \n\t" /* Check if vmlaunch or vmresume is needed */ "cmpb $0, %c[launched](%%" _ASM_CX ") \n\t" /* Load guest registers. Don't clobber flags. */ @@ -6471,9 +6467,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%r14d, %%r14d \n\t" "xor %%r15d, %%r15d \n\t" #endif - "mov %%cr2, %%" _ASM_AX " \n\t" - "mov %%" _ASM_AX ", %c[cr2](%%" _ASM_CX ") \n\t" - "xor %%eax, %%eax \n\t" "xor %%ebx, %%ebx \n\t" "xor %%ecx, %%ecx \n\t" @@ -6504,7 +6497,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) [r14]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R14])), [r15]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R15])), #endif - [cr2]"i"(offsetof(struct vcpu_vmx, vcpu.arch.cr2)), [wordsize]"i"(sizeof(ulong)) : "cc", "memory" #ifdef CONFIG_X86_64 @@ -6514,6 +6506,8 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) , "eax", "ebx", "edx" #endif ); + + vcpu->arch.cr2 = read_cr2(); } STACK_FRAME_NON_STANDARD(__vmx_vcpu_run); From c09b03eb7f964370149577fdba425623b7f5b0b5 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:04 -0800 Subject: [PATCH 1591/4277] KVM: VMX: Update VMCS.HOST_RSP via helper C function Providing a helper function to update HOST_RSP is visibly easier to read, and more importantly (for the future) eliminates two arguments to the VM-Enter assembly blob. Reducing the number of arguments to the asm blob is for all intents and purposes a prerequisite to moving the code to a proper assembly routine. It's not truly mandatory, but it greatly simplifies the future code, and the cost of the extra CALL+RET is negligible in the grand scheme. Note that although _ASM_ARG[1-3] can be used in the inline asm itself, the intput/output constraints need to be manually defined. gcc will actually compile with _ASM_ARG[1-3] specified as constraints, but what it actually ends up doing with the bogus constraint is unknown. Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 51 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 5e43999ece1d..4b8a94fedb76 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6363,15 +6363,18 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu) vmx->loaded_vmcs->hv_timer_armed = false; } +void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp) +{ + if (unlikely(host_rsp != vmx->loaded_vmcs->host_state.rsp)) { + vmx->loaded_vmcs->host_state.rsp = host_rsp; + vmcs_writel(HOST_RSP, host_rsp); + } +} + static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) { - unsigned long evmcs_rsp; - vmx->__launched = vmx->loaded_vmcs->launched; - evmcs_rsp = static_branch_unlikely(&enable_evmcs) ? - (unsigned long)¤t_evmcs->host_rsp : 0; - if (static_branch_unlikely(&vmx_l1d_should_flush)) vmx_l1d_flush(vcpu); @@ -6382,21 +6385,14 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) /* Store host registers */ "push %%" _ASM_BP " \n\t" "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* placeholder for guest RCX */ - "push %%" _ASM_CX " \n\t" - "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* temporarily adjust RSP for CALL */ - "cmp %%" _ASM_SP ", (%%" _ASM_DI ") \n\t" - "je 1f \n\t" - "mov %%" _ASM_SP ", (%%" _ASM_DI ") \n\t" - /* Avoid VMWRITE when Enlightened VMCS is in use */ - "test %%" _ASM_SI ", %%" _ASM_SI " \n\t" - "jz 2f \n\t" - "mov %%" _ASM_SP ", (%%" _ASM_SI ") \n\t" - "jmp 1f \n\t" - "2: \n\t" - "mov $%c[HOST_RSP], %%" _ASM_DX " \n\t" - __ex("vmwrite %%" _ASM_SP ", %%" _ASM_DX) "\n\t" - "1: \n\t" - "add $%c[wordsize], %%" _ASM_SP "\n\t" /* un-adjust RSP */ + "push %%" _ASM_ARG1 " \n\t" + + /* Adjust RSP to account for the CALL to vmx_vmenter(). */ + "lea -%c[wordsize](%%" _ASM_SP "), %%" _ASM_ARG2 " \n\t" + "call vmx_update_host_rsp \n\t" + + /* Load the vcpu_vmx pointer to RCX. */ + "mov (%%" _ASM_SP "), %%" _ASM_CX " \n\t" /* Check if vmlaunch or vmresume is needed */ "cmpb $0, %c[launched](%%" _ASM_CX ") \n\t" @@ -6475,11 +6471,16 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%edi, %%edi \n\t" "xor %%ebp, %%ebp \n\t" "pop %%" _ASM_BP " \n\t" - : ASM_CALL_CONSTRAINT, "=D"((int){0}), "=S"((int){0}) - : "c"(vmx), "D"(&vmx->loaded_vmcs->host_state.rsp), "S"(evmcs_rsp), + : ASM_CALL_CONSTRAINT, +#ifdef CONFIG_X86_64 + "=D"((int){0}) + : "D"(vmx), +#else + "=a"((int){0}) + : "a"(vmx), +#endif [launched]"i"(offsetof(struct vcpu_vmx, __launched)), [fail]"i"(offsetof(struct vcpu_vmx, fail)), - [HOST_RSP]"i"(HOST_RSP), [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])), [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])), [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])), @@ -6500,10 +6501,10 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) [wordsize]"i"(sizeof(ulong)) : "cc", "memory" #ifdef CONFIG_X86_64 - , "rax", "rbx", "rdx" + , "rax", "rbx", "rcx", "rdx", "rsi" , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" #else - , "eax", "ebx", "edx" + , "ebx", "ecx", "edx", "edi", "esi" #endif ); From c9afc58cc368623aaa34a62e321eea2a59240f6f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:05 -0800 Subject: [PATCH 1592/4277] KVM: VMX: Pass "launched" directly to the vCPU-run asm blob ...and remove struct vcpu_vmx's temporary __launched variable. Eliminating __launched is a bonus, the real motivation is to get to the point where the only reference to struct vcpu_vmx in the asm code is to vcpu.arch.regs, which will simplify moving the blob to a proper asm file. Note that also means this approach is deliberately different than what is used in nested_vmx_check_vmentry_hw(). Use BL as it is a callee-save register in both 32-bit and 64-bit ABIs, i.e. it can't be modified by vmx_update_host_rsp(), to avoid having to temporarily save/restore the launched flag. Signed-off-by: Sean Christopherson Reviewed-by: Jim Mattson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 13 ++++++------- arch/x86/kvm/vmx/vmx.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 4b8a94fedb76..996a13ea86cc 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6373,8 +6373,6 @@ void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp) static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) { - vmx->__launched = vmx->loaded_vmcs->launched; - if (static_branch_unlikely(&vmx_l1d_should_flush)) vmx_l1d_flush(vcpu); @@ -6395,7 +6393,8 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "mov (%%" _ASM_SP "), %%" _ASM_CX " \n\t" /* Check if vmlaunch or vmresume is needed */ - "cmpb $0, %c[launched](%%" _ASM_CX ") \n\t" + "cmpb $0, %%bl \n\t" + /* Load guest registers. Don't clobber flags. */ "mov %c[rax](%%" _ASM_CX "), %%" _ASM_AX " \n\t" "mov %c[rbx](%%" _ASM_CX "), %%" _ASM_BX " \n\t" @@ -6471,7 +6470,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%edi, %%edi \n\t" "xor %%ebp, %%ebp \n\t" "pop %%" _ASM_BP " \n\t" - : ASM_CALL_CONSTRAINT, + : ASM_CALL_CONSTRAINT, "=b"((int){0}), #ifdef CONFIG_X86_64 "=D"((int){0}) : "D"(vmx), @@ -6479,7 +6478,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "=a"((int){0}) : "a"(vmx), #endif - [launched]"i"(offsetof(struct vcpu_vmx, __launched)), + "b"(vmx->loaded_vmcs->launched), [fail]"i"(offsetof(struct vcpu_vmx, fail)), [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])), [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])), @@ -6501,10 +6500,10 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) [wordsize]"i"(sizeof(ulong)) : "cc", "memory" #ifdef CONFIG_X86_64 - , "rax", "rbx", "rcx", "rdx", "rsi" + , "rax", "rcx", "rdx", "rsi" , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" #else - , "ebx", "ecx", "edx", "edi", "esi" + , "ecx", "edx", "edi", "esi" #endif ); diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 8e203b725928..6ee6a492efaf 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -208,7 +208,7 @@ struct vcpu_vmx { struct loaded_vmcs vmcs01; struct loaded_vmcs *loaded_vmcs; struct loaded_vmcs *loaded_cpu_state; - bool __launched; /* temporary, used in vmx_vcpu_run */ + struct msr_autoload { struct vmx_msrs guest; struct vmx_msrs host; From 217aaff53c25f03b1d2fc23eff9dc2bae34f690e Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:06 -0800 Subject: [PATCH 1593/4277] KVM: VMX: Invert the ordering of saving guest/host scratch reg at VM-Enter Switching the ordering allows for an out-of-line path for VM-Fail that elides saving guest state but still shares the register clearing with the VM-Exit path. Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 996a13ea86cc..79b42197ed7e 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6382,7 +6382,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) asm( /* Store host registers */ "push %%" _ASM_BP " \n\t" - "sub $%c[wordsize], %%" _ASM_SP "\n\t" /* placeholder for guest RCX */ "push %%" _ASM_ARG1 " \n\t" /* Adjust RSP to account for the CALL to vmx_vmenter(). */ @@ -6418,11 +6417,11 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) /* Enter guest mode */ "call vmx_vmenter\n\t" - /* Save guest's RCX to the stack placeholder (see above) */ - "mov %%" _ASM_CX ", %c[wordsize](%%" _ASM_SP ") \n\t" + /* Temporarily save guest's RCX. */ + "push %%" _ASM_CX " \n\t" - /* Load host's RCX, i.e. the vmx_vcpu pointer */ - "pop %%" _ASM_CX " \n\t" + /* Reload the vcpu_vmx pointer to RCX. */ + "mov %c[wordsize](%%" _ASM_SP "), %%" _ASM_CX " \n\t" /* Set vmx->fail based on EFLAGS.{CF,ZF} */ "setbe %c[fail](%%" _ASM_CX ")\n\t" @@ -6469,6 +6468,9 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%esi, %%esi \n\t" "xor %%edi, %%edi \n\t" "xor %%ebp, %%ebp \n\t" + + /* "POP" the vcpu_vmx pointer. */ + "add $%c[wordsize], %%" _ASM_SP " \n\t" "pop %%" _ASM_BP " \n\t" : ASM_CALL_CONSTRAINT, "=b"((int){0}), #ifdef CONFIG_X86_64 From f78d0971b7bd5bf4373a1fac27f176af5d5594ed Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:07 -0800 Subject: [PATCH 1594/4277] KVM: VMX: Don't save guest registers after VM-Fail A failed VM-Enter (obviously) didn't succeed, meaning the CPU never executed an instrunction in guest mode and so can't have changed the general purpose registers. In addition to saving some instructions in the VM-Fail case, this also provides a separate path entirely and thus an opportunity to propagate the fail condition to vmx->fail via register without introducing undue pain. Using a register, as opposed to directly referencing vmx->fail, eliminates the need to pass the offset of 'fail', which will simplify moving the code to proper assembly in future patches. Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 79b42197ed7e..1dcd3f157a70 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6416,6 +6416,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) /* Enter guest mode */ "call vmx_vmenter\n\t" + "jbe 2f \n\t" /* Temporarily save guest's RCX. */ "push %%" _ASM_CX " \n\t" @@ -6423,9 +6424,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) /* Reload the vcpu_vmx pointer to RCX. */ "mov %c[wordsize](%%" _ASM_SP "), %%" _ASM_CX " \n\t" - /* Set vmx->fail based on EFLAGS.{CF,ZF} */ - "setbe %c[fail](%%" _ASM_CX ")\n\t" - /* Save all guest registers, including RCX from the stack */ "mov %%" _ASM_AX ", %c[rax](%%" _ASM_CX ") \n\t" "mov %%" _ASM_BX ", %c[rbx](%%" _ASM_CX ") \n\t" @@ -6443,15 +6441,22 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "mov %%r13, %c[r13](%%" _ASM_CX ") \n\t" "mov %%r14, %c[r14](%%" _ASM_CX ") \n\t" "mov %%r15, %c[r15](%%" _ASM_CX ") \n\t" +#endif + + /* Clear EBX to indicate VM-Exit (as opposed to VM-Fail). */ + "xor %%ebx, %%ebx \n\t" /* - * Clear all general purpose registers (except RSP, which is loaded by - * the CPU during VM-Exit) to prevent speculative use of the guest's - * values, even those that are saved/loaded via the stack. In theory, - * an L1 cache miss when restoring registers could lead to speculative - * execution with the guest's values. Zeroing XORs are dirt cheap, - * i.e. the extra paranoia is essentially free. + * Clear all general purpose registers except RSP and RBX to prevent + * speculative use of the guest's values, even those that are reloaded + * via the stack. In theory, an L1 cache miss when restoring registers + * could lead to speculative execution with the guest's values. + * Zeroing XORs are dirt cheap, i.e. the extra paranoia is essentially + * free. RSP and RBX are exempt as RSP is restored by hardware during + * VM-Exit and RBX is explicitly loaded with 0 or 1 to "return" VM-Fail. */ + "1: \n\t" +#ifdef CONFIG_X86_64 "xor %%r8d, %%r8d \n\t" "xor %%r9d, %%r9d \n\t" "xor %%r10d, %%r10d \n\t" @@ -6462,7 +6467,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) "xor %%r15d, %%r15d \n\t" #endif "xor %%eax, %%eax \n\t" - "xor %%ebx, %%ebx \n\t" "xor %%ecx, %%ecx \n\t" "xor %%edx, %%edx \n\t" "xor %%esi, %%esi \n\t" @@ -6472,7 +6476,15 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) /* "POP" the vcpu_vmx pointer. */ "add $%c[wordsize], %%" _ASM_SP " \n\t" "pop %%" _ASM_BP " \n\t" - : ASM_CALL_CONSTRAINT, "=b"((int){0}), + "jmp 3f \n\t" + + /* VM-Fail. Out-of-line to avoid a taken Jcc after VM-Exit. */ + "2: \n\t" + "mov $1, %%ebx \n\t" + "jmp 1b \n\t" + "3: \n\t" + + : ASM_CALL_CONSTRAINT, "=b"(vmx->fail), #ifdef CONFIG_X86_64 "=D"((int){0}) : "D"(vmx), @@ -6481,7 +6493,6 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) : "a"(vmx), #endif "b"(vmx->loaded_vmcs->launched), - [fail]"i"(offsetof(struct vcpu_vmx, fail)), [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])), [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])), [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])), From d55892049171872a8f1aba542aa0691efe3da52d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 25 Jan 2019 07:41:08 -0800 Subject: [PATCH 1595/4277] KVM: VMX: Use vcpu->arch.regs directly when saving/loading guest state ...now that all other references to struct vcpu_vmx have been removed. Note that 'vmx' still needs to be passed into the asm blob in _ASM_ARG1 as it is consumed by vmx_update_host_rsp(). And similar to that code, use _ASM_ARG2 in the assembly code to prepare for moving to proper asm, while explicitly referencing the exact registers in the clobber list for clarity in the short term and to avoid additional precompiler games. Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 53 +++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 1dcd3f157a70..cd92568bed26 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6382,13 +6382,18 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) asm( /* Store host registers */ "push %%" _ASM_BP " \n\t" - "push %%" _ASM_ARG1 " \n\t" + + /* + * Save @regs, _ASM_ARG2 may be modified by vmx_update_host_rsp() and + * @regs is needed after VM-Exit to save the guest's register values. + */ + "push %%" _ASM_ARG2 " \n\t" /* Adjust RSP to account for the CALL to vmx_vmenter(). */ "lea -%c[wordsize](%%" _ASM_SP "), %%" _ASM_ARG2 " \n\t" "call vmx_update_host_rsp \n\t" - /* Load the vcpu_vmx pointer to RCX. */ + /* Load RCX with @regs. */ "mov (%%" _ASM_SP "), %%" _ASM_CX " \n\t" /* Check if vmlaunch or vmresume is needed */ @@ -6421,7 +6426,7 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) /* Temporarily save guest's RCX. */ "push %%" _ASM_CX " \n\t" - /* Reload the vcpu_vmx pointer to RCX. */ + /* Reload RCX with @regs. */ "mov %c[wordsize](%%" _ASM_SP "), %%" _ASM_CX " \n\t" /* Save all guest registers, including RCX from the stack */ @@ -6486,37 +6491,37 @@ static void __vmx_vcpu_run(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) : ASM_CALL_CONSTRAINT, "=b"(vmx->fail), #ifdef CONFIG_X86_64 - "=D"((int){0}) - : "D"(vmx), + "=D"((int){0}), "=S"((int){0}) + : "D"(vmx), "S"(&vcpu->arch.regs), #else - "=a"((int){0}) - : "a"(vmx), + "=a"((int){0}), "=d"((int){0}) + : "a"(vmx), "d"(&vcpu->arch.regs), #endif "b"(vmx->loaded_vmcs->launched), - [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])), - [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])), - [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])), - [rdx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDX])), - [rsi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RSI])), - [rdi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDI])), - [rbp]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBP])), + [rax]"i"(VCPU_REGS_RAX * sizeof(ulong)), + [rbx]"i"(VCPU_REGS_RBX * sizeof(ulong)), + [rcx]"i"(VCPU_REGS_RCX * sizeof(ulong)), + [rdx]"i"(VCPU_REGS_RDX * sizeof(ulong)), + [rsi]"i"(VCPU_REGS_RSI * sizeof(ulong)), + [rdi]"i"(VCPU_REGS_RDI * sizeof(ulong)), + [rbp]"i"(VCPU_REGS_RBP * sizeof(ulong)), #ifdef CONFIG_X86_64 - [r8]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R8])), - [r9]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R9])), - [r10]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R10])), - [r11]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R11])), - [r12]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R12])), - [r13]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R13])), - [r14]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R14])), - [r15]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R15])), + [r8]"i"(VCPU_REGS_R8 * sizeof(ulong)), + [r9]"i"(VCPU_REGS_R9 * sizeof(ulong)), + [r10]"i"(VCPU_REGS_R10 * sizeof(ulong)), + [r11]"i"(VCPU_REGS_R11 * sizeof(ulong)), + [r12]"i"(VCPU_REGS_R12 * sizeof(ulong)), + [r13]"i"(VCPU_REGS_R13 * sizeof(ulong)), + [r14]"i"(VCPU_REGS_R14 * sizeof(ulong)), + [r15]"i"(VCPU_REGS_R15 * sizeof(ulong)), #endif [wordsize]"i"(sizeof(ulong)) : "cc", "memory" #ifdef CONFIG_X86_64 - , "rax", "rcx", "rdx", "rsi" + , "rax", "rcx", "rdx" , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" #else - , "ecx", "edx", "edi", "esi" + , "ecx", "edi", "esi" #endif ); From de9c0d49d85dc563549972edc5589d195cd5e859 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 2 Feb 2019 03:34:36 +0100 Subject: [PATCH 1596/4277] ARM: 8833/1: Ensure that NEON code always compiles with Clang While building arm32 allyesconfig, I ran into the following errors: arch/arm/lib/xor-neon.c:17:2: error: You should compile this file with '-mfloat-abi=softfp -mfpu=neon' In file included from lib/raid6/neon1.c:27: /home/nathan/cbl/prebuilt/lib/clang/8.0.0/include/arm_neon.h:28:2: error: "NEON support not enabled" Building V=1 showed NEON_FLAGS getting passed along to Clang but __ARM_NEON__ was not getting defined. Ultimately, it boils down to Clang only defining __ARM_NEON__ when targeting armv7, rather than armv6k, which is the '-march' value for allyesconfig. >From lib/Basic/Targets/ARM.cpp in the Clang source: // This only gets set when Neon instructions are actually available, unlike // the VFP define, hence the soft float and arch check. This is subtly // different from gcc, we follow the intent which was that it should be set // when Neon instructions are actually available. if ((FPU & NeonFPU) && !SoftFloat && ArchVersion >= 7) { Builder.defineMacro("__ARM_NEON", "1"); Builder.defineMacro("__ARM_NEON__"); // current AArch32 NEON implementations do not support double-precision // floating-point even when it is present in VFP. Builder.defineMacro("__ARM_NEON_FP", "0x" + Twine::utohexstr(HW_FP & ~HW_FP_DP)); } Ard Biesheuvel recommended explicitly adding '-march=armv7-a' at the beginning of the NEON_FLAGS definitions so that __ARM_NEON__ always gets definined by Clang. This doesn't functionally change anything because that code will only run where NEON is supported, which is implicitly armv7. Link: https://github.com/ClangBuiltLinux/linux/issues/287 Suggested-by: Ard Biesheuvel Signed-off-by: Nathan Chancellor Acked-by: Nicolas Pitre Reviewed-by: Nick Desaulniers Reviewed-by: Stefan Agner Signed-off-by: Russell King --- Documentation/arm/kernel_mode_neon.txt | 4 ++-- arch/arm/lib/Makefile | 2 +- arch/arm/lib/xor-neon.c | 2 +- lib/raid6/Makefile | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/arm/kernel_mode_neon.txt b/Documentation/arm/kernel_mode_neon.txt index 525452726d31..b9e060c5b61e 100644 --- a/Documentation/arm/kernel_mode_neon.txt +++ b/Documentation/arm/kernel_mode_neon.txt @@ -6,7 +6,7 @@ TL;DR summary * Use only NEON instructions, or VFP instructions that don't rely on support code * Isolate your NEON code in a separate compilation unit, and compile it with - '-mfpu=neon -mfloat-abi=softfp' + '-march=armv7-a -mfpu=neon -mfloat-abi=softfp' * Put kernel_neon_begin() and kernel_neon_end() calls around the calls into your NEON code * Don't sleep in your NEON code, and be aware that it will be executed with @@ -87,7 +87,7 @@ instructions appearing in unexpected places if no special care is taken. Therefore, the recommended and only supported way of using NEON/VFP in the kernel is by adhering to the following rules: * isolate the NEON code in a separate compilation unit and compile it with - '-mfpu=neon -mfloat-abi=softfp'; + '-march=armv7-a -mfpu=neon -mfloat-abi=softfp'; * issue the calls to kernel_neon_begin(), kernel_neon_end() as well as the calls into the unit containing the NEON code from a compilation unit which is *not* built with the GCC flag '-mfpu=neon' set. diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index ad25fd1872c7..0bff0176db2c 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -39,7 +39,7 @@ $(obj)/csumpartialcopy.o: $(obj)/csumpartialcopygeneric.S $(obj)/csumpartialcopyuser.o: $(obj)/csumpartialcopygeneric.S ifeq ($(CONFIG_KERNEL_MODE_NEON),y) - NEON_FLAGS := -mfloat-abi=softfp -mfpu=neon + NEON_FLAGS := -march=armv7-a -mfloat-abi=softfp -mfpu=neon CFLAGS_xor-neon.o += $(NEON_FLAGS) obj-$(CONFIG_XOR_BLOCKS) += xor-neon.o endif diff --git a/arch/arm/lib/xor-neon.c b/arch/arm/lib/xor-neon.c index 2c40aeab3eaa..c691b901092f 100644 --- a/arch/arm/lib/xor-neon.c +++ b/arch/arm/lib/xor-neon.c @@ -14,7 +14,7 @@ MODULE_LICENSE("GPL"); #ifndef __ARM_NEON__ -#error You should compile this file with '-mfloat-abi=softfp -mfpu=neon' +#error You should compile this file with '-march=armv7-a -mfloat-abi=softfp -mfpu=neon' #endif /* diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index 4e90d443d1b0..e723eacf7868 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -39,7 +39,7 @@ endif ifeq ($(CONFIG_KERNEL_MODE_NEON),y) NEON_FLAGS := -ffreestanding ifeq ($(ARCH),arm) -NEON_FLAGS += -mfloat-abi=softfp -mfpu=neon +NEON_FLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=neon endif CFLAGS_recov_neon_inner.o += $(NEON_FLAGS) ifeq ($(ARCH),arm64) From c208556ab3627b5f53144e9cbf739d6371d80543 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 8 Feb 2019 13:21:27 -0800 Subject: [PATCH 1597/4277] scsi: scsi_debug: Fix a recently introduced regression A recent commit removed an element from opcode_info_arr[] but did not modify opcode_ind_arr[] nor was SDEB_I_XDWRITEREAD removed. Remove SDEB_I_XDWRITEREAD and bring the two arrays again in sync. This patch avoids that the following is reported: BUG: KASAN: null-ptr-deref in scsi_debug_queuecommand+0x60f/0xc90 [scsi_debug] Read of size 1 at addr 0000000000000001 by task iscsi-test-cu/683 CPU: 3 PID: 683 Comm: iscsi-test-cu Not tainted 5.0.0-rc5-dbg+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 Call Trace: dump_stack+0x86/0xca kasan_report.cold.3+0x5/0x3e __asan_load1+0x47/0x50 scsi_debug_queuecommand+0x60f/0xc90 [scsi_debug] scsi_queue_rq+0xc17/0x12e0 blk_mq_dispatch_rq_list+0x5fc/0xb10 blk_mq_sched_dispatch_requests+0x2f7/0x300 __blk_mq_run_hw_queue+0xd6/0x180 __blk_mq_delay_run_hw_queue+0x25c/0x290 blk_mq_run_hw_queue+0x119/0x1b0 blk_mq_sched_insert_request+0x274/0x350 blk_execute_rq_nowait+0x78/0x90 blk_execute_rq+0xcc/0x140 sg_io+0x30f/0x700 scsi_cmd_ioctl+0x4d4/0x540 scsi_cmd_blk_ioctl+0x7b/0x8b sd_ioctl+0xba/0x150 blkdev_ioctl+0x6e1/0xea0 block_ioctl+0x79/0x90 do_vfs_ioctl+0x12b/0x9b0 ksys_ioctl+0x41/0x80 __x64_sys_ioctl+0x43/0x50 do_syscall_64+0x71/0x210 entry_SYSCALL_64_after_hwframe+0x49/0xbe Cc: Christoph Hellwig Cc: Douglas Gilbert Fixes: ae3d56d81507 ("scsi: remove bidirectional command support") Signed-off-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index c5014e9f4a50..90b1b94a7772 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -351,12 +351,11 @@ enum sdeb_opcode_index { SDEB_I_ATA_PT = 22, /* 12, 16 */ SDEB_I_SEND_DIAG = 23, SDEB_I_UNMAP = 24, - SDEB_I_XDWRITEREAD = 25, /* 10 only */ - SDEB_I_WRITE_BUFFER = 26, - SDEB_I_WRITE_SAME = 27, /* 10, 16 */ - SDEB_I_SYNC_CACHE = 28, /* 10, 16 */ - SDEB_I_COMP_WRITE = 29, - SDEB_I_LAST_ELEMENT = 30, /* keep this last (previous + 1) */ + SDEB_I_WRITE_BUFFER = 25, + SDEB_I_WRITE_SAME = 26, /* 10, 16 */ + SDEB_I_SYNC_CACHE = 27, /* 10, 16 */ + SDEB_I_COMP_WRITE = 28, + SDEB_I_LAST_ELEMENT = 29, /* keep this last (previous + 1) */ }; @@ -377,7 +376,7 @@ static const unsigned char opcode_ind_arr[256] = { /* 0x40; 0x40->0x5f: 10 byte cdbs */ 0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0, - 0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, + 0, 0, 0, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE, 0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0, /* 0x60; 0x60->0x7d are reserved, 0x7e is "extended cdb" */ @@ -614,7 +613,7 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0x3f, 0xc7} }, /* COMPARE AND WRITE */ -/* 30 */ +/* 29 */ {0xff, 0, 0, 0, NULL, NULL, /* terminating element */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, }; From 960bf87a4fbe46bec1e69fcb6868f43c3c3c9456 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 8 Feb 2019 13:24:59 -0800 Subject: [PATCH 1598/4277] scsi: libiscsi: Use scsi_[gs]et_resid() where appropriate This patch does not change any functionality. Cc: Lee Duncan Cc: Chris Leech Signed-off-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index bca3a8636c27..dd314d2b1111 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -915,7 +915,7 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (res_count > 0 && (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || res_count <= sc->sdb.length)) - sc->sdb.resid = res_count; + scsi_set_resid(sc, res_count); else sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; } From 42d387be5bdaa29f416e0ff210616538e92d6b94 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 8 Feb 2019 13:25:00 -0800 Subject: [PATCH 1599/4277] scsi: scsi_debug: Use scsi_[gs]et_resid() where appropriate This patch does not change any functionality. Cc: Douglas Gilbert Signed-off-by: Bart Van Assche Acked-by: Douglas Gilbert Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 90b1b94a7772..4c9339ea455d 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1015,7 +1015,7 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len); - sdb->resid = scsi_bufflen(scp) - act_len; + scsi_set_resid(scp, scsi_bufflen(scp) - act_len); return 0; } @@ -1040,9 +1040,10 @@ static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr, act_len = sg_pcopy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len, skip); pr_debug("%s: off_dst=%u, scsi_bufflen=%u, act_len=%u, resid=%d\n", - __func__, off_dst, scsi_bufflen(scp), act_len, sdb->resid); + __func__, off_dst, scsi_bufflen(scp), act_len, + scsi_get_resid(scp)); n = (int)scsi_bufflen(scp) - ((int)off_dst + act_len); - sdb->resid = min(sdb->resid, n); + scsi_set_resid(scp, min(scsi_get_resid(scp), n)); return 0; } @@ -2768,7 +2769,7 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) if (unlikely(ret == -1)) return DID_ERROR << 16; - scp->sdb.resid = scsi_bufflen(scp) - ret; + scsi_set_resid(scp, scsi_bufflen(scp) - ret); if (unlikely(sqcp)) { if (sqcp->inj_recovered) { From 229531be6918906e92da5647c755ee8205fc1d7f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 8 Feb 2019 13:25:01 -0800 Subject: [PATCH 1600/4277] scsi: uas: Use scsi_[gs]et_resid() where appropriate This patch does not change any functionality. Cc: Oliver Neukum Signed-off-by: Bart Van Assche Acked-by: Oliver Neukum Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/usb/storage/uas.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 95f2142093d5..a6d68191c861 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -395,9 +395,9 @@ static void uas_data_cmplt(struct urb *urb) if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) uas_log_cmd_state(cmnd, "data cmplt err", status); /* error: no data transfered */ - sdb->resid = sdb->length; + scsi_set_resid(cmnd, sdb->length); } else { - sdb->resid = sdb->length - urb->actual_length; + scsi_set_resid(cmnd, sdb->length - urb->actual_length); } uas_try_complete(cmnd, __func__); out: From 80f82c169b68b2dcd5adfc4625edd93a2ab954cf Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 8 Feb 2019 13:25:02 -0800 Subject: [PATCH 1601/4277] scsi: sd: Remove superfluous residual assignments Since commit 26e85fcd15f6 ("[SCSI] sd: Permit merged discard requests"; kernel v3.10) sd_done() sets the residual not only for failed special requests but also for special requests that succeeded. Hence remove the code from functions called by sd_init_command() that sets the residual. This patch does not change any functionality. Signed-off-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c124459041dc..cc175b78b366 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -843,7 +843,6 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) cmd->allowed = SD_MAX_RETRIES; cmd->transfersize = data_len; rq->timeout = SD_TIMEOUT; - scsi_req(rq)->resid_len = data_len; return scsi_init_io(cmd); } @@ -875,7 +874,6 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, cmd->allowed = SD_MAX_RETRIES; cmd->transfersize = data_len; rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT; - scsi_req(rq)->resid_len = data_len; return scsi_init_io(cmd); } @@ -907,7 +905,6 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, cmd->allowed = SD_MAX_RETRIES; cmd->transfersize = data_len; rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT; - scsi_req(rq)->resid_len = data_len; return scsi_init_io(cmd); } From 9fa505adf9f2914637e4e81620f62dcdaa575f71 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 8 Feb 2019 13:25:03 -0800 Subject: [PATCH 1602/4277] scsi: core: Move resid from scsi_data_buffer to scsi_cmnd This patch does not change any functionality but reduces the size of struct scsi_cmnd. Cc: Douglas Gilbert Cc: Hannes Reinecke Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 1 - include/scsi/scsi_cmnd.h | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d25101c5e3b8..c77cb0d31dbc 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -938,7 +938,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) * scsi_result_to_blk_status may have reset the host_byte */ scsi_req(req)->result = cmd->result; - scsi_req(req)->resid_len = scsi_get_resid(cmd); } /* diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index af6ae3e1eba6..6c7793c2ad01 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -35,7 +35,6 @@ struct scsi_driver; struct scsi_data_buffer { struct sg_table table; unsigned length; - int resid; }; /* embedded in scsi_cmnd */ @@ -202,12 +201,12 @@ static inline unsigned scsi_bufflen(struct scsi_cmnd *cmd) static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid) { - cmd->sdb.resid = resid; + cmd->req.resid_len = resid; } static inline int scsi_get_resid(struct scsi_cmnd *cmd) { - return cmd->sdb.resid; + return cmd->req.resid_len; } #define scsi_for_each_sg(cmd, sg, nseg, __i) \ From 9447b6ce944b294fa44c9de77b6a4a9fc33aa313 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 8 Feb 2019 18:37:25 -0500 Subject: [PATCH 1603/4277] scsi: scsi_debug: Implement support for write protect Teach scsi_debug to honor SWP in the Control Mode Page and report the resulting WP state in the Device-Specific Parameter field. In check_device_access_params() verify that commands that will write the medium are permitted to do so. Signed-off-by: Martin K. Petersen Acked-by: Douglas Gilbert --- drivers/scsi/scsi_debug.c | 76 ++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 4c9339ea455d..2c255071d4c0 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -76,6 +76,7 @@ static const char *sdebug_version_date = "20180128"; #define LBA_OUT_OF_RANGE 0x21 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define WRITE_PROTECTED 0x27 #define UA_RESET_ASC 0x29 #define UA_CHANGED_ASC 0x2a #define TARGET_CHANGED_ASC 0x3f @@ -668,6 +669,7 @@ static bool sdebug_verbose; static bool have_dif_prot; static bool write_since_sync; static bool sdebug_statistics = DEF_STATISTICS; +static bool sdebug_wp; static unsigned int sdebug_store_sectors; static sector_t sdebug_capacity; /* in sectors */ @@ -2143,9 +2145,11 @@ static int resp_mode_sense(struct scsi_cmnd *scp, target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) + (devip->target * 1000) - 3; /* for disks set DPOFUA bit and clear write protect (WP) bit */ - if (is_disk) + if (is_disk) { dev_spec = 0x10; /* =0x90 if WP=1 implies read-only */ - else + if (sdebug_wp) + dev_spec |= 0x80; + } else dev_spec = 0x0; if (msense_6) { arr[2] = dev_spec; @@ -2328,6 +2332,10 @@ static int resp_mode_select(struct scsi_cmnd *scp, if (ctrl_m_pg[1] == arr[off + 1]) { memcpy(ctrl_m_pg + 2, arr + off + 2, sizeof(ctrl_m_pg) - 2); + if (ctrl_m_pg[4] & 0x8) + sdebug_wp = true; + else + sdebug_wp = false; sdebug_dsense = !!(ctrl_m_pg[2] & 0x4); goto set_mode_changed_ua; } @@ -2452,8 +2460,8 @@ static int resp_log_sense(struct scsi_cmnd *scp, min(len, SDEBUG_MAX_INQ_ARR_SZ)); } -static int check_device_access_params(struct scsi_cmnd *scp, - unsigned long long lba, unsigned int num) +static inline int check_device_access_params(struct scsi_cmnd *scp, + unsigned long long lba, unsigned int num, bool write) { if (lba + num > sdebug_capacity) { mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); @@ -2465,6 +2473,10 @@ static int check_device_access_params(struct scsi_cmnd *scp, mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } + if (write && unlikely(sdebug_wp)) { + mk_sense_buffer(scp, DATA_PROTECT, WRITE_PROTECTED, 0x2); + return check_condition_result; + } return 0; } @@ -2723,18 +2735,9 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) } else sqcp = NULL; - /* inline check_device_access_params() */ - if (unlikely(lba + num > sdebug_capacity)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); - return check_condition_result; - } - /* transfer length excessive (tie in to block limits VPD page) */ - if (unlikely(num > sdebug_store_sectors)) { - /* needs work to find which cdb byte 'num' comes from */ - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); - return check_condition_result; - } - + ret = check_device_access_params(scp, lba, num, false); + if (ret) + return ret; if (unlikely((SDEBUG_OPT_MEDIUM_ERR & sdebug_opts) && (lba <= (sdebug_medium_error_start + sdebug_medium_error_count - 1)) && ((lba + num) > sdebug_medium_error_start))) { @@ -3026,19 +3029,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); } - - /* inline check_device_access_params() */ - if (unlikely(lba + num > sdebug_capacity)) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); - return check_condition_result; - } - /* transfer length excessive (tie in to block limits VPD page) */ - if (unlikely(num > sdebug_store_sectors)) { - /* needs work to find which cdb byte 'num' comes from */ - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); - return check_condition_result; - } - + ret = check_device_access_params(scp, lba, num, true); + if (ret) + return ret; write_lock_irqsave(&atomic_rw, iflags); /* DIX + T10 DIF */ @@ -3177,7 +3170,7 @@ static int resp_write_scat(struct scsi_cmnd *scp, my_name, __func__, k, lba, num, sg_off); if (num == 0) continue; - ret = check_device_access_params(scp, lba, num); + ret = check_device_access_params(scp, lba, num, true); if (ret) goto err_out_unlock; num_by = num * lb_size; @@ -3261,7 +3254,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, int ret; u64 lba_off; - ret = check_device_access_params(scp, lba, num); + ret = check_device_access_params(scp, lba, num, true); if (ret) return ret; @@ -3434,18 +3427,9 @@ static int resp_comp_write(struct scsi_cmnd *scp, (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); - - /* inline check_device_access_params() */ - if (lba + num > sdebug_capacity) { - mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); - return check_condition_result; - } - /* transfer length excessive (tie in to block limits VPD page) */ - if (num > sdebug_store_sectors) { - /* needs work to find which cdb byte 'num' comes from */ - mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); - return check_condition_result; - } + ret = check_device_access_params(scp, lba, num, false); + if (ret) + return ret; dnum = 2 * num; arr = kcalloc(lb_size, dnum, GFP_ATOMIC); if (NULL == arr) { @@ -3528,7 +3512,7 @@ static int resp_unmap(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) unsigned long long lba = get_unaligned_be64(&desc[i].lba); unsigned int num = get_unaligned_be32(&desc[i].blocks); - ret = check_device_access_params(scp, lba, num); + ret = check_device_access_params(scp, lba, num, true); if (ret) goto out; @@ -3561,7 +3545,7 @@ static int resp_get_lba_status(struct scsi_cmnd *scp, if (alloc_len < 24) return 0; - ret = check_device_access_params(scp, lba, 1); + ret = check_device_access_params(scp, lba, 1, false); if (ret) return ret; @@ -4485,6 +4469,7 @@ module_param_named(virtual_gb, sdebug_virtual_gb, int, S_IRUGO | S_IWUSR); module_param_named(uuid_ctl, sdebug_uuid_ctl, int, S_IRUGO); module_param_named(vpd_use_hostno, sdebug_vpd_use_hostno, int, S_IRUGO | S_IWUSR); +module_param_named(wp, sdebug_wp, bool, S_IRUGO | S_IWUSR); module_param_named(write_same_length, sdebug_write_same_length, int, S_IRUGO | S_IWUSR); @@ -4544,6 +4529,7 @@ MODULE_PARM_DESC(uuid_ctl, "1->use uuid for lu name, 0->don't, 2->all use same (def=0)"); MODULE_PARM_DESC(virtual_gb, "virtual gigabyte (GiB) size (def=0 -> use dev_size_mb)"); MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); +MODULE_PARM_DESC(wp, "Write Protect (def=0)"); MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)"); #define SDEBUG_INFO_LEN 256 From 6442ca2abf882d9d838fb844d852ba6acd1db7f4 Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Fri, 18 Jan 2019 15:10:26 +0800 Subject: [PATCH 1604/4277] swiotlb: fix comment on swiotlb_bounce() Fix the comment as swiotlb_bounce() is used to copy from original dma location to swiotlb buffer during swiotlb_tbl_map_single(), while to copy from swiotlb buffer to original dma location during swiotlb_tbl_unmap_single(). Signed-off-by: Dongli Zhang Signed-off-by: Konrad Rzeszutek Wilk --- kernel/dma/swiotlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 1fb6fd68b9c7..1d8b37747bf0 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -385,7 +385,7 @@ void __init swiotlb_exit(void) } /* - * Bounce: copy the swiotlb buffer back to the original dma location + * Bounce: copy the swiotlb buffer from or back to the original dma location */ static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr, size_t size, enum dma_data_direction dir) From 71602fe6d4e9291af105adfef8e893b57c735906 Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Fri, 18 Jan 2019 15:10:27 +0800 Subject: [PATCH 1605/4277] swiotlb: add debugfs to track swiotlb buffer usage The device driver will not be able to do dma operations once swiotlb buffer is full, either because the driver is using so many IO TLB blocks inflight, or because there is memory leak issue in device driver. To export the swiotlb buffer usage via debugfs would help the user estimate the size of swiotlb buffer to pre-allocate or analyze device driver memory leak issue. Signed-off-by: Dongli Zhang Signed-off-by: Konrad Rzeszutek Wilk --- kernel/dma/swiotlb.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 1d8b37747bf0..bedc9f945836 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -34,6 +34,9 @@ #include #include #include +#ifdef CONFIG_DEBUG_FS +#include +#endif #include #include @@ -72,6 +75,11 @@ phys_addr_t io_tlb_start, io_tlb_end; */ static unsigned long io_tlb_nslabs; +/* + * The number of used IO TLB block + */ +static unsigned long io_tlb_used; + /* * This is a free list describing the number of free entries available from * each index @@ -524,6 +532,7 @@ not_found: dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes)\n", size); return DMA_MAPPING_ERROR; found: + io_tlb_used += nslots; spin_unlock_irqrestore(&io_tlb_lock, flags); /* @@ -584,6 +593,8 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr, */ for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE -1) && io_tlb_list[i]; i--) io_tlb_list[i] = ++count; + + io_tlb_used -= nslots; } spin_unlock_irqrestore(&io_tlb_lock, flags); } @@ -662,3 +673,36 @@ swiotlb_dma_supported(struct device *hwdev, u64 mask) { return __phys_to_dma(hwdev, io_tlb_end - 1) <= mask; } + +#ifdef CONFIG_DEBUG_FS + +static int __init swiotlb_create_debugfs(void) +{ + static struct dentry *d_swiotlb_usage; + struct dentry *ent; + + d_swiotlb_usage = debugfs_create_dir("swiotlb", NULL); + + if (!d_swiotlb_usage) + return -ENOMEM; + + ent = debugfs_create_ulong("io_tlb_nslabs", 0400, + d_swiotlb_usage, &io_tlb_nslabs); + if (!ent) + goto fail; + + ent = debugfs_create_ulong("io_tlb_used", 0400, + d_swiotlb_usage, &io_tlb_used); + if (!ent) + goto fail; + + return 0; + +fail: + debugfs_remove_recursive(d_swiotlb_usage); + return -ENOMEM; +} + +late_initcall(swiotlb_create_debugfs); + +#endif From 60513ed06a41049768a6875229b025b6e726e148 Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Fri, 18 Jan 2019 15:10:28 +0800 Subject: [PATCH 1606/4277] swiotlb: checking whether swiotlb buffer is full with io_tlb_used This patch uses io_tlb_used to help check whether swiotlb buffer is full. io_tlb_used is no longer used for only debugfs. It is also used to help optimize swiotlb_tbl_map_single(). Suggested-by: Joe Jin Signed-off-by: Dongli Zhang Signed-off-by: Konrad Rzeszutek Wilk --- kernel/dma/swiotlb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index bedc9f945836..a01b83e95a2a 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -483,6 +483,10 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev, * request and allocate a buffer from that IO TLB pool. */ spin_lock_irqsave(&io_tlb_lock, flags); + + if (unlikely(nslots > io_tlb_nslabs - io_tlb_used)) + goto not_found; + index = ALIGN(io_tlb_index, stride); if (index >= io_tlb_nslabs) index = 0; From df997abeebadaa4824271009e2d2b526a70a11cb Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Feb 2019 12:43:23 -0600 Subject: [PATCH 1607/4277] iscsi_ibft: Fix missing break in switch statement Add missing break statement in order to prevent the code from falling through to case ISCSI_BOOT_TGT_NAME, which is unnecessary. This bug was found thanks to the ongoing efforts to enable -Wimplicit-fallthrough. Fixes: b33a84a38477 ("ibft: convert iscsi_ibft module to iscsi boot lib") Cc: stable@vger.kernel.org Signed-off-by: Gustavo A. R. Silva Signed-off-by: Konrad Rzeszutek Wilk --- drivers/firmware/iscsi_ibft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index 6bc8e6640d71..c51462f5aa1e 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c @@ -542,6 +542,7 @@ static umode_t __init ibft_check_tgt_for(void *data, int type) case ISCSI_BOOT_TGT_NIC_ASSOC: case ISCSI_BOOT_TGT_CHAP_TYPE: rc = S_IRUGO; + break; case ISCSI_BOOT_TGT_NAME: if (tgt->tgt_name_len) rc = S_IRUGO; From 8bd04c57fda62e3599f654aa43b521b326a95805 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Feb 2019 14:46:42 +0100 Subject: [PATCH 1608/4277] iscsi_ibft: use virt_to_phys instead of isa_virt_to_bus As far as I can tell IBFT is a firmware table and has nothing to do with the good old ISA bus. And even if it the two would be the same on x86 anyway. So remove the isa_virt_to_bus call in preparation of eventually removing that API entirely. Signed-off-by: Christoph Hellwig Signed-off-by: Konrad Rzeszutek Wilk --- drivers/firmware/iscsi_ibft_find.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c index 72d9ea18270b..85c656d04bb0 100644 --- a/drivers/firmware/iscsi_ibft_find.c +++ b/drivers/firmware/iscsi_ibft_find.c @@ -104,7 +104,7 @@ unsigned long __init find_ibft_region(unsigned long *sizep) if (ibft_addr) { *sizep = PAGE_ALIGN(ibft_addr->header.length); - return (u64)isa_virt_to_bus(ibft_addr); + return (u64)virt_to_phys(ibft_addr); } *sizep = 0; From f101ada7da6551127d192c2f1742c1e9e0f62799 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Tue, 22 Jan 2019 10:48:09 +0800 Subject: [PATCH 1609/4277] libnvdimm, pfn: Fix over-trim in trim_pfn_device() When trying to see whether current nd_region intersects with others, trim_pfn_device() has already calculated the *size* to be expanded to SECTION size. Do not double append 'adjust' to 'size' when calculating whether the end of a region collides with the next pmem region. Fixes: ae86cbfef381 "libnvdimm, pfn: Pad pfn namespaces relative to other regions" Cc: Signed-off-by: Wei Yang Signed-off-by: Dan Williams --- drivers/nvdimm/pfn_devs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 6f22272e8d80..040bbd9c367e 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -678,7 +678,7 @@ static void trim_pfn_device(struct nd_pfn *nd_pfn, u32 *start_pad, u32 *end_trun if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_MIXED || !IS_ALIGNED(end, nd_pfn->align) - || nd_region_conflict(nd_region, start, size + adjust)) + || nd_region_conflict(nd_region, start, size)) *end_trunc = end - phys_pmem_align_down(nd_pfn, end); } From 07464e88365e9236febaca9ed1a2e2006d8bc952 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 6 Feb 2019 13:04:53 +1100 Subject: [PATCH 1610/4277] libnvdimm: Fix altmap reservation size calculation Libnvdimm reserves the first 8K of pfn and devicedax namespaces to store a superblock describing the namespace. This 8K reservation is contained within the altmap area which the kernel uses for the vmemmap backing for the pages within the namespace. The altmap allows for some pages at the start of the altmap area to be reserved and that mechanism is used to protect the superblock from being re-used as vmemmap backing. The number of PFNs to reserve is calculated using: PHYS_PFN(SZ_8K) Which is implemented as: #define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT)) So on systems where PAGE_SIZE is greater than 8K the reservation size is truncated to zero and the superblock area is re-used as vmemmap backing. As a result all the namespace information stored in the superblock (i.e. if it's a PFN or DAX namespace) is lost and the namespace needs to be re-created to get access to the contents. This patch fixes this by using PFN_UP() rather than PHYS_PFN() to ensure that at least one page is reserved. On systems with a 4K pages size this patch should have no effect. Cc: stable@vger.kernel.org Cc: Dan Williams Fixes: ac515c084be9 ("libnvdimm, pmem, pfn: move pfn setup to the core") Signed-off-by: Oliver O'Halloran Reviewed-by: Vishal Verma Signed-off-by: Dan Williams --- drivers/nvdimm/pfn_devs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 040bbd9c367e..7760c1b91853 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -593,7 +593,7 @@ static unsigned long init_altmap_base(resource_size_t base) static unsigned long init_altmap_reserve(resource_size_t base) { - unsigned long reserve = PHYS_PFN(SZ_8K); + unsigned long reserve = PFN_UP(SZ_8K); unsigned long base_pfn = PHYS_PFN(base); reserve += base_pfn - PFN_SECTION_ALIGN_DOWN(base_pfn); From 11a358109e0c48fa74f27e750dde4d25de4d5fc3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Feb 2019 13:04:53 +1100 Subject: [PATCH 1611/4277] libnvdimm/pfn: Account for PAGE_SIZE > info-block-size in nd_pfn_init() Similar to "libnvdimm: Fix altmap reservation size calculation" provide for a reservation of a full page worth of info block space at info-block establishment time. Typically there is already slack in the padding from honoring the default 2MB alignment, but provide for a reservation for corner case configurations that would otherwise fit. Cc: Oliver O'Halloran Signed-off-by: Dan Williams --- drivers/nvdimm/pfn_devs.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 7760c1b91853..ba74a341da5d 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -580,6 +580,11 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) } EXPORT_SYMBOL(nd_pfn_probe); +static u32 info_block_reserve(void) +{ + return ALIGN(SZ_8K, PAGE_SIZE); +} + /* * We hotplug memory at section granularity, pad the reserved area from * the previous section base to the namespace base address. @@ -593,7 +598,7 @@ static unsigned long init_altmap_base(resource_size_t base) static unsigned long init_altmap_reserve(resource_size_t base) { - unsigned long reserve = PFN_UP(SZ_8K); + unsigned long reserve = info_block_reserve() >> PAGE_SHIFT; unsigned long base_pfn = PHYS_PFN(base); reserve += base_pfn - PFN_SECTION_ALIGN_DOWN(base_pfn); @@ -608,6 +613,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) u64 offset = le64_to_cpu(pfn_sb->dataoff); u32 start_pad = __le32_to_cpu(pfn_sb->start_pad); u32 end_trunc = __le32_to_cpu(pfn_sb->end_trunc); + u32 reserve = info_block_reserve(); struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); resource_size_t base = nsio->res.start + start_pad; @@ -621,7 +627,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) res->end -= end_trunc; if (nd_pfn->mode == PFN_MODE_RAM) { - if (offset < SZ_8K) + if (offset < reserve) return -EINVAL; nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns); pgmap->altmap_valid = false; @@ -634,7 +640,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) le64_to_cpu(nd_pfn->pfn_sb->npfns), nd_pfn->npfns); memcpy(altmap, &__altmap, sizeof(*altmap)); - altmap->free = PHYS_PFN(offset - SZ_8K); + altmap->free = PHYS_PFN(offset - reserve); altmap->alloc = 0; pgmap->altmap_valid = true; } else @@ -687,9 +693,9 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0; struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); + u32 start_pad, end_trunc, reserve = info_block_reserve(); resource_size_t start, size; struct nd_region *nd_region; - u32 start_pad, end_trunc; struct nd_pfn_sb *pfn_sb; unsigned long npfns; phys_addr_t offset; @@ -734,7 +740,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) */ start = nsio->res.start + start_pad; size = resource_size(&nsio->res); - npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - SZ_8K) + npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - reserve) / PAGE_SIZE); if (nd_pfn->mode == PFN_MODE_PMEM) { /* @@ -742,10 +748,10 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) * when populating the vmemmap. This *should* be equal to * PMD_SIZE for most architectures. */ - offset = ALIGN(start + SZ_8K + 64 * npfns + dax_label_reserve, + offset = ALIGN(start + reserve + 64 * npfns + dax_label_reserve, max(nd_pfn->align, PMD_SIZE)) - start; } else if (nd_pfn->mode == PFN_MODE_RAM) - offset = ALIGN(start + SZ_8K + dax_label_reserve, + offset = ALIGN(start + reserve + dax_label_reserve, nd_pfn->align) - start; else return -ENXIO; From fa7d2e639cd90442d868dfc6ca1d4cc9d8bf206e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 24 Jan 2019 17:33:06 -0800 Subject: [PATCH 1612/4277] libnvdimm/pmem: Honor force_raw for legacy pmem regions For recovery, where non-dax access is needed to a given physical address range, and testing, allow the 'force_raw' attribute to override the default establishment of a dev_pagemap. Otherwise without this capability it is possible to end up with a namespace that can not be activated due to corrupted info-block, and one that can not be repaired due to a section collision. Cc: Fixes: 004f1afbe199 ("libnvdimm, pmem: direct map legacy pmem by default") Signed-off-by: Dan Williams --- drivers/nvdimm/namespace_devs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 4b077555ac70..33a3b23b3db7 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -138,6 +138,7 @@ bool nd_is_uuid_unique(struct device *dev, u8 *uuid) bool pmem_should_map_pages(struct device *dev) { struct nd_region *nd_region = to_nd_region(dev->parent); + struct nd_namespace_common *ndns = to_ndns(dev); struct nd_namespace_io *nsio; if (!IS_ENABLED(CONFIG_ZONE_DEVICE)) @@ -149,6 +150,9 @@ bool pmem_should_map_pages(struct device *dev) if (is_nd_pfn(dev) || is_nd_btt(dev)) return false; + if (ndns->force_raw) + return false; + nsio = to_nd_namespace_io(dev); if (region_intersects(nsio->res.start, resource_size(&nsio->res), IORESOURCE_SYSTEM_RAM, From 0380cf843c54d9ee506f5dadc00dd4fe524c99a6 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Fri, 7 Dec 2018 18:24:37 +0100 Subject: [PATCH 1613/4277] PCI: dwc: Print debug error message when MSI-X entry control mask bit is set Add debug error message when MSI-X entry control mask bit is set, to help debug the reason why a MSI-X interrupt is not being triggered. Signed-off-by: Gustavo Pimentel Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Bjorn Helgaas Cc: Joao Pinto --- drivers/pci/controller/dwc/pcie-designware-ep.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index a543c45c7224..5e766af04b66 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -465,8 +465,10 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, iounmap(msix_tbl); - if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) + if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) { + dev_dbg(pci->dev, "MSI-X entry ctrl set\n"); return -EPERM; + } ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, epc->mem->page_size); From b7e312dea1d8378b8f311ce9096759b5accb1939 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 5 Feb 2019 00:17:39 +0000 Subject: [PATCH 1614/4277] dt-bindings: imx6q-pcie: Add description of imx7d pcie phy There is a separate PHY device with its own registers on imx7d. It's currently unused, but a PCIe erratum on imx7d will require it for the workaround. Signed-off-by: Trent Piepho Signed-off-by: Lorenzo Pieralisi Acked-by: Lucas Stach --- .../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt index 920ca93870a8..954f435dca8c 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt @@ -54,6 +54,7 @@ Additional required properties for imx7d-pcie and imx8mq-pcie: - "pciephy" - "apps" - "turnoff" +- fsl,imx7d-pcie-phy: A phandle to an fsl,imx7d-pcie-phy node. Example: @@ -80,3 +81,13 @@ Example: clocks = <&clks 144>, <&clks 206>, <&clks 189>; clock-names = "pcie", "pcie_bus", "pcie_phy"; }; + +* Freescale i.MX7d PCIe PHY + +This is the PHY associated with the IMX7d PCIe controller. It's used by the +PCI-e controller via the fsl,imx7d-pcie-phy phandle. + +Required properties: +- compatible: + - "fsl,imx7d-pcie-phy" +- reg: base address and length of the PCIe PHY controller From 27f7717e2344a087e396796ed6b10d15722c9e43 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 5 Feb 2019 00:17:40 +0000 Subject: [PATCH 1615/4277] ARM: dts: imx7d: Add node for PCIe PHY This adds the PHY as a new node. The PCI-e controller node gains a phandle property that points to it. There isn't yet any code in the kernel that uses this device's registers, but it will be added for a PCIe PLL erratum workaround. Signed-off-by: Trent Piepho Signed-off-by: Lorenzo Pieralisi --- arch/arm/boot/dts/imx7d.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/imx7d.dtsi b/arch/arm/boot/dts/imx7d.dtsi index 6b298e388f4b..6eb98e7c568d 100644 --- a/arch/arm/boot/dts/imx7d.dtsi +++ b/arch/arm/boot/dts/imx7d.dtsi @@ -96,6 +96,14 @@ }; }; +&aips2 { + pcie_phy: pcie-phy@306d0000 { + compatible = "fsl,imx7d-pcie-phy"; + reg = <0x306d0000 0x10000>; + status = "disabled"; + }; +}; + &aips3 { usbotg2: usb@30b20000 { compatible = "fsl,imx7d-usb", "fsl,imx27-usb"; @@ -173,6 +181,7 @@ <&src IMX7_RESET_PCIE_CTRL_APPS_EN>, <&src IMX7_RESET_PCIE_CTRL_APPS_TURNOFF>; reset-names = "pciephy", "apps", "turnoff"; + fsl,imx7d-pcie-phy = <&pcie_phy>; status = "disabled"; }; }; From 1df82ec4660099fdae42545051cbbeacb5666b50 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 5 Feb 2019 00:17:41 +0000 Subject: [PATCH 1616/4277] PCI: imx: Add workaround for e10728, IMX7d PCIe PLL failure This implements the workound described in the NXP IMX7d erratum e10728. Initial VCO oscillation may fail under corner conditions such as cold temperature. It causes PCIe PLL to fail to lock in the initialization phase, which results in the PCIe link failing to come up. The workaround is to disable Duty-Cycle Corrector (DCC) calibration after G_RST. To do this it is necessary to gain access to the undocumented and currently unused PCIe PHY register bank. A new device tree node of type "fsl,imx7d-pcie-phy" is created for the PHY block and the existing PCIe device uses a phandle named "fsl,imx7d-pcie-phy" to point to it. Signed-off-by: Trent Piepho [lorenzo.pieralisi@arm.com: updated log string, commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Lucas Stach --- drivers/pci/controller/dwc/pci-imx6.c | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index c1d434ba3642..64c74334f7d0 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,7 @@ struct imx6_pcie { u32 tx_swing_low; int link_gen; struct regulator *vpcie; + void __iomem *phy_base; /* power domain for pcie */ struct device *pd_pcie; @@ -134,6 +136,23 @@ struct imx6_pcie { #define PCIE_PHY_RX_ASIC_OUT 0x100D #define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0) +/* iMX7 PCIe PHY registers */ +#define PCIE_PHY_CMN_REG4 0x14 +/* These are probably the bits that *aren't* DCC_FB_EN */ +#define PCIE_PHY_CMN_REG4_DCC_FB_EN 0x29 + +#define PCIE_PHY_CMN_REG15 0x54 +#define PCIE_PHY_CMN_REG15_DLY_4 BIT(2) +#define PCIE_PHY_CMN_REG15_PLL_PD BIT(5) +#define PCIE_PHY_CMN_REG15_OVRD_PLL_PD BIT(7) + +#define PCIE_PHY_CMN_REG24 0x90 +#define PCIE_PHY_CMN_REG24_RX_EQ BIT(6) +#define PCIE_PHY_CMN_REG24_RX_EQ_SEL BIT(3) + +#define PCIE_PHY_CMN_REG26 0x98 +#define PCIE_PHY_CMN_REG26_ATT_MODE 0xBC + #define PHY_RX_OVRD_IN_LO 0x1005 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) @@ -533,6 +552,26 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) break; case IMX7D: reset_control_deassert(imx6_pcie->pciephy_reset); + + /* Workaround for ERR010728, failure of PCI-e PLL VCO to + * oscillate, especially when cold. This turns off "Duty-cycle + * Corrector" and other mysterious undocumented things. + */ + if (likely(imx6_pcie->phy_base)) { + /* De-assert DCC_FB_EN */ + writel(PCIE_PHY_CMN_REG4_DCC_FB_EN, + imx6_pcie->phy_base + PCIE_PHY_CMN_REG4); + /* Assert RX_EQS and RX_EQS_SEL */ + writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL + | PCIE_PHY_CMN_REG24_RX_EQ, + imx6_pcie->phy_base + PCIE_PHY_CMN_REG24); + /* Assert ATT_MODE */ + writel(PCIE_PHY_CMN_REG26_ATT_MODE, + imx6_pcie->phy_base + PCIE_PHY_CMN_REG26); + } else { + dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n"); + } + imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); break; case IMX6SX: @@ -994,6 +1033,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dw_pcie *pci; struct imx6_pcie *imx6_pcie; + struct device_node *np; struct resource *dbi_base; struct device_node *node = dev->of_node; int ret; @@ -1013,6 +1053,23 @@ static int imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->pci = pci; imx6_pcie->drvdata = of_device_get_match_data(dev); + /* Find the PHY if one is defined, only imx7d uses it */ + np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); + if (np) { + struct resource res; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(dev, "Unable to map PCIe PHY\n"); + return ret; + } + imx6_pcie->phy_base = devm_ioremap_resource(dev, &res); + if (IS_ERR(imx6_pcie->phy_base)) { + dev_err(dev, "Unable to map PCIe PHY\n"); + return PTR_ERR(imx6_pcie->phy_base); + } + } + dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); pci->dbi_base = devm_ioremap_resource(dev, dbi_base); if (IS_ERR(pci->dbi_base)) From 4c6c4d34536744f2c9e171ef5bb548a06a525501 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Thu, 31 Jan 2019 18:12:46 +0800 Subject: [PATCH 1617/4277] pstore: Avoid writing records with zero size Sometimes pstore_console_write() will write records with zero size to persistent ram zone, which is unnecessary. It will only increase resource consumption. Also adjust ramoops_write_kmsg_hdr() to have same logic if memory allocation fails. Signed-off-by: Yue Hu Signed-off-by: Kees Cook --- fs/pstore/platform.c | 3 +++ fs/pstore/ram.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 2d1066ed3c28..75887a269b64 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -501,6 +501,9 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) { struct pstore_record record; + if (!c) + return; + pstore_record_init(&record, psinfo); record.type = PSTORE_TYPE_CONSOLE; diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 1adb5e35c004..b2471d430048 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -352,8 +352,10 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, (time64_t)record->time.tv_sec, record->time.tv_nsec / 1000, record->compressed ? 'C' : 'D'); - WARN_ON_ONCE(!hdr); - len = hdr ? strlen(hdr) : 0; + if (WARN_ON_ONCE(!hdr)) + return 0; + + len = strlen(hdr); persistent_ram_write(prz, hdr, len); kfree(hdr); From 1e0f67a96aedd0b5fbd3ae743e68a26606ea98e8 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Wed, 23 Jan 2019 16:56:48 +0800 Subject: [PATCH 1618/4277] pstore/ram: Move initialization earlier Since only one single ramoops area allowed at a time, other probes (like device tree) are meaningless, as it will waste CPU resources. So let's check for being already initialized first. Signed-off-by: Yue Hu Signed-off-by: Kees Cook --- fs/pstore/ram.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index b2471d430048..d06e2f3e3bec 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -717,15 +717,6 @@ static int ramoops_probe(struct platform_device *pdev) phys_addr_t paddr; int err = -EINVAL; - if (dev_of_node(dev) && !pdata) { - pdata = &pdata_local; - memset(pdata, 0, sizeof(*pdata)); - - err = ramoops_parse_dt(pdev, pdata); - if (err < 0) - goto fail_out; - } - /* * Only a single ramoops area allowed at a time, so fail extra * probes. @@ -735,6 +726,15 @@ static int ramoops_probe(struct platform_device *pdev) goto fail_out; } + if (dev_of_node(dev) && !pdata) { + pdata = &pdata_local; + memset(pdata, 0, sizeof(*pdata)); + + err = ramoops_parse_dt(pdev, pdata); + if (err < 0) + goto fail_out; + } + /* Make sure we didn't get bogus platform data pointer. */ if (!pdata) { pr_err("NULL platform data\n"); From 47afd7ae65c737bfef67e9eb1424723d13b06787 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Fri, 1 Feb 2019 11:27:32 +0800 Subject: [PATCH 1619/4277] pstore/ram: Add kmsg hlen zero check to ramoops_pstore_write() If zero-length header happened in ramoops_write_kmsg_hdr(), that means we will not be able to read back dmesg record later, since it will be treated as invalid header in ramoops_pstore_read(). So we should not execute the following code but return the error. Signed-off-by: Yue Hu Signed-off-by: Kees Cook --- fs/pstore/ram.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index d06e2f3e3bec..ec0e1da0596a 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -425,6 +425,9 @@ static int notrace ramoops_pstore_write(struct pstore_record *record) /* Build header and append record contents. */ hlen = ramoops_write_kmsg_hdr(prz, record); + if (!hlen) + return -ENOMEM; + size = record->size; if (size + hlen > prz->buffer_size) size = prz->buffer_size - hlen; From 93ee4b7d9f0632690713aee604c49e298e634094 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 12 Feb 2019 13:09:36 -0800 Subject: [PATCH 1620/4277] pstore/ram: Avoid needless alloc during header write Since the header is a fixed small maximum size, just use a stack variable to avoid memory allocation in the write path. Signed-off-by: Kees Cook --- fs/pstore/ram.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index ec0e1da0596a..c5c685589e36 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -345,19 +345,15 @@ out: static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, struct pstore_record *record) { - char *hdr; + char hdr[36]; /* "===="(4), %lld(20), "."(1), %06lu(6), "-%c\n"(3) */ size_t len; - hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lld.%06lu-%c\n", + len = scnprintf(hdr, sizeof(hdr), + RAMOOPS_KERNMSG_HDR "%lld.%06lu-%c\n", (time64_t)record->time.tv_sec, record->time.tv_nsec / 1000, record->compressed ? 'C' : 'D'); - if (WARN_ON_ONCE(!hdr)) - return 0; - - len = strlen(hdr); persistent_ram_write(prz, hdr, len); - kfree(hdr); return len; } From 74f0aaba7c7161f2f777015f6c2e6d8c2c44aa8f Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Tue, 12 Feb 2019 11:21:49 -0500 Subject: [PATCH 1621/4277] power: supply: ds2782: fix possible use-after-free on remove In remove(), use cancel_delayed_work_sync() to cancel the delayed work. Otherwise there's a chance that this work will continue to run until after the device has been removed. While we're here, fix the deallocation order in remove(), to correspond to the inverse of the probe() allocation order. This guarantees that any remaining work can run to completion with all driver structures still intact. This issue was detected with the help of Coccinelle. Signed-off-by: Sven Van Asbroeck Signed-off-by: Sebastian Reichel --- drivers/power/supply/ds2782_battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/ds2782_battery.c b/drivers/power/supply/ds2782_battery.c index 019c58493e3d..04b0fe7d7d62 100644 --- a/drivers/power/supply/ds2782_battery.c +++ b/drivers/power/supply/ds2782_battery.c @@ -319,17 +319,17 @@ static void ds278x_power_supply_init(struct power_supply_desc *battery) static int ds278x_battery_remove(struct i2c_client *client) { struct ds278x_info *info = i2c_get_clientdata(client); + int id = info->id; power_supply_unregister(info->battery); + cancel_delayed_work_sync(&info->bat_work); kfree(info->battery_desc.name); + kfree(info); mutex_lock(&battery_lock); - idr_remove(&battery_id, info->id); + idr_remove(&battery_id, id); mutex_unlock(&battery_lock); - cancel_delayed_work(&info->bat_work); - - kfree(info); return 0; } From 2174b18513d1064d2706cd6d6b878139eb050277 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Sun, 10 Feb 2019 23:20:45 +0800 Subject: [PATCH 1622/4277] scsi: ufs: fix a typo in comment poitner -> pointer. Signed-off-by: Chengguang Xu Reviewed-by: Pedro Sousa Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd-dwc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-dwc.c b/drivers/scsi/ufs/ufshcd-dwc.c index 5fd16c72207f..977b21871a5d 100644 --- a/drivers/scsi/ufs/ufshcd-dwc.c +++ b/drivers/scsi/ufs/ufshcd-dwc.c @@ -50,7 +50,7 @@ static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val) /** * ufshcd_dwc_link_is_up() * Check if link is up - * @hba: private structure poitner + * @hba: private structure pointer * * Returns 0 on success, non-zero value on failure */ @@ -110,7 +110,7 @@ static int ufshcd_dwc_connection_setup(struct ufs_hba *hba) /** * ufshcd_dwc_link_startup_notify() * UFS Host DWC specific link startup sequence - * @hba: private structure poitner + * @hba: private structure pointer * @status: Callback notify status * * Returns 0 on success, non-zero value on failure From fad28e3d9aa14522e33694b63ccccdf28e64a8e0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 11 Feb 2019 21:43:00 +0300 Subject: [PATCH 1623/4277] scsi: lpfc: Fix error code if kcalloc() fails This should return -ENOMEM if kcalloc() fails, but it accidentally returns success instead. Fixes: 6a828b0f6192 ("scsi: lpfc: Support non-uniform allocation of MSIX vectors to hardware queues") Signed-off-by: Dan Carpenter Reviewed-by: Ewan D. Milne Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index d24af076afa8..eef6b929c327 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -9638,6 +9638,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0549 Failed setup of CQ Lookup table: " "size 0x%x\n", phba->sli4_hba.cq_max); + rc = -ENOMEM; goto out_destroy; } lpfc_setup_cq_lookup(phba); From a3742d68484083ce55de8fec492be2db2fff2cfa Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Fri, 8 Feb 2019 00:22:46 -0800 Subject: [PATCH 1624/4277] scsi: megaraid_sas: Update structures for HOST_DEVICE_LIST DCMD Add padding to make the structure variables in MR_HOST_DEVICE_LIST_ENTRY 64-bit aligned. Also, add reserved fields to MR_HOST_DEVICE_LIST for future firmware usage. Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index fe1173f02c54..eb48ca17dca0 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -807,13 +807,14 @@ struct MR_HOST_DEVICE_LIST_ENTRY { } flags; u8 scsi_type; __le16 target_id; - u8 reserved[2]; + u8 reserved[4]; __le64 sas_addr[2]; } __packed; struct MR_HOST_DEVICE_LIST { __le32 size; __le32 count; + __le32 reserved[2]; struct MR_HOST_DEVICE_LIST_ENTRY host_device_list[1]; } __packed; From 0de05405122406bcada51cdfd8531dc279deb8b2 Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Fri, 8 Feb 2019 00:22:47 -0800 Subject: [PATCH 1625/4277] scsi: megaraid_sas: driver version update Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index eb48ca17dca0..6fd57f7f0b1e 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -33,8 +33,8 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "07.707.50.00-rc1" -#define MEGASAS_RELDATE "December 18, 2018" +#define MEGASAS_VERSION "07.707.51.00-rc1" +#define MEGASAS_RELDATE "February 7, 2019" /* * Device IDs From df46cac3f71c57e0b23f6865651629aaa54f8ca9 Mon Sep 17 00:00:00 2001 From: Dietmar Hahn Date: Tue, 5 Feb 2019 11:10:48 +0100 Subject: [PATCH 1626/4277] scsi: sd: Fix typo in sd_first_printk() Commit b2bff6ceb61a9 ("[SCSI] sd: Quiesce mode sense error messages") added the macro sd_first_printk(). The macro takes "sdsk" as argument but dereferences "sdkp". This hasn't caused any real issues since all callers of sd_first_printk() have an sdkp. But fix the typo. [mkp: Turned this into a real patch and tweaked commit description] Signed-off-by: Dietmar Hahn Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 1080c85d97f8..5796ace76225 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -132,7 +132,7 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk) #define sd_first_printk(prefix, sdsk, fmt, a...) \ do { \ - if ((sdkp)->first_scan) \ + if ((sdsk)->first_scan) \ sd_printk(prefix, sdsk, fmt, ##a); \ } while (0) From 0171b6b78131110a6870d4b7296bc9dfc392116a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 3 Feb 2019 11:17:27 -0800 Subject: [PATCH 1627/4277] acpi/nfit: Require opt-in for read-only label configurations Recent fixes to command handling enabled Linux to read label configurations that it could not before. Unfortunately that means that configurations that were operating in label-less mode will be broken as the kernel ignores the existing namespace configuration and tries to honor the new found labels. Fortunately this seems limited to a case where Linux can quirk the behavior and maintain the existing label-less semantics by default. When the platform does not emit an _LSW method, disable all label access methods. Provide a 'force_labels' module parameter to allow read-only label operation. Fixes: 11189c1089da ("acpi/nfit: Fix command-supported detection") Reported-by: Dexuan Cui Reviewed-by: Dexuan Cui Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 811c399a3a76..5b5e802de7b8 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -56,6 +56,10 @@ static bool no_init_ars; module_param(no_init_ars, bool, 0644); MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time"); +static bool force_labels; +module_param(force_labels, bool, 0444); +MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods"); + LIST_HEAD(acpi_descs); DEFINE_MUTEX(acpi_desc_lock); @@ -1916,6 +1920,19 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); set_bit(NFIT_MEM_LSW, &nfit_mem->flags); } + + /* + * Quirk read-only label configurations to preserve + * access to label-less namespaces by default. + */ + if (!test_bit(NFIT_MEM_LSW, &nfit_mem->flags) + && !force_labels) { + dev_dbg(dev, "%s: No _LSW, disable labels\n", + dev_name(&adev_dimm->dev)); + clear_bit(NFIT_MEM_LSR, &nfit_mem->flags); + } else + dev_dbg(dev, "%s: Force enable labels\n", + dev_name(&adev_dimm->dev)); } populate_shutdown_status(nfit_mem); From c9aed74e6a2762c32e9b84fcd5e1b9bc451ffdfd Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sat, 5 Jan 2019 00:54:11 +0530 Subject: [PATCH 1628/4277] fs/dax: Convert to use vmf_error() This code is converted to use vmf_error(). Signed-off-by: Souptick Joarder Reviewed-by: Jan Kara Signed-off-by: Dan Williams --- fs/dax.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/dax.c b/fs/dax.c index 6959837cc465..7865d9080085 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1220,9 +1220,7 @@ static vm_fault_t dax_fault_return(int error) { if (error == 0) return VM_FAULT_NOPAGE; - if (error == -ENOMEM) - return VM_FAULT_OOM; - return VM_FAULT_SIGBUS; + return vmf_error(error); } /* From 0cefc36b32b3a9ee9da9bd055810bf331e4243dd Mon Sep 17 00:00:00 2001 From: Ira Weiny Date: Thu, 17 Jan 2019 18:20:42 -0500 Subject: [PATCH 1629/4277] fs/dax: NIT fix comment regarding start/end vs range Fixes: ac46d4f3c432 ("mm/mmu_notifier: use structure for invalidate_range_start/end calls v2") Signed-off-by: Ira Weiny Signed-off-by: Dan Williams --- fs/dax.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dax.c b/fs/dax.c index 7865d9080085..c55afa4261d1 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -788,7 +788,7 @@ static void dax_entry_mkclean(struct address_space *mapping, pgoff_t index, address = pgoff_address(index, vma); /* - * Note because we provide start/end to follow_pte_pmd it will + * Note because we provide range to follow_pte_pmd it will * call mmu_notifier_invalidate_range_start() on our behalf * before taking any lock. */ From f5595f5baa30e009bf54d0d7653a9a0cc465be60 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 8 Feb 2019 18:30:58 +0200 Subject: [PATCH 1630/4277] tpm: Unify the send callback behaviour The send() callback should never return length as it does not in every driver except tpm_crb in the success case. The reason is that the main transmit functionality only cares about whether the transmit was successful or not and ignores the count completely. Suggested-by: Stefan Berger Cc: stable@vger.kernel.org Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Reviewed-by: Jerry Snitselaar Tested-by: Alexander Steffen --- drivers/char/tpm/st33zp24/st33zp24.c | 2 +- drivers/char/tpm/tpm-interface.c | 11 ++++++++++- drivers/char/tpm/tpm_atmel.c | 2 +- drivers/char/tpm/tpm_i2c_atmel.c | 6 +++++- drivers/char/tpm/tpm_i2c_infineon.c | 2 +- drivers/char/tpm/tpm_i2c_nuvoton.c | 2 +- drivers/char/tpm/tpm_ibmvtpm.c | 8 ++++---- drivers/char/tpm/tpm_infineon.c | 2 +- drivers/char/tpm/tpm_nsc.c | 2 +- drivers/char/tpm/tpm_tis_core.c | 2 +- drivers/char/tpm/tpm_vtpm_proxy.c | 3 +-- drivers/char/tpm/xen-tpmfront.c | 2 +- 12 files changed, 28 insertions(+), 16 deletions(-) diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 64dc560859f2..13dc614b7ebc 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -436,7 +436,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, goto out_err; } - return len; + return 0; out_err: st33zp24_cancel(chip); release_locality(chip); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index d9439f9abe78..88d2e01a651d 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -230,10 +230,19 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, - "%s: tpm_send: error %d\n", __func__, rc); + "%s: send(): error %d\n", __func__, rc); goto out; } + /* A sanity check. send() should just return zero on success e.g. + * not the command length. + */ + if (rc > 0) { + dev_warn(&chip->dev, + "%s: send(): invalid value %d\n", __func__, rc); + rc = 0; + } + if (chip->flags & TPM_CHIP_FLAG_IRQ) goto out_recv; diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 66a14526aaf4..a290b30a0c35 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -105,7 +105,7 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) iowrite8(buf[i], priv->iobase); } - return count; + return 0; } static void tpm_atml_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 95ce2e9ccdc6..32a8e27c5382 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -65,7 +65,11 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) dev_dbg(&chip->dev, "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__, (int)min_t(size_t, 64, len), buf, len, status); - return status; + + if (status < 0) + return status; + + return 0; } static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 3b490d9d90e7..3b4e9672ff6c 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -588,7 +588,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) /* go and do it */ iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1); - return len; + return 0; out_err: tpm_tis_i2c_ready(chip); /* The TPM needs some time to clean up here, diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 5700cc2ddee1..315a3b4548f7 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -465,7 +465,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) } dev_dbg(dev, "%s() -> %zd\n", __func__, len); - return len; + return 0; } static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status) diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 07b5a487d0c8..757ca45b39b8 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -139,14 +139,14 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) } /** - * tpm_ibmvtpm_send - Send tpm request - * + * tpm_ibmvtpm_send() - Send a TPM command * @chip: tpm chip struct * @buf: buffer contains data to send * @count: size of buffer * * Return: - * Number of bytes sent or < 0 on error. + * 0 on success, + * -errno on error */ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { @@ -192,7 +192,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) rc = 0; ibmvtpm->tpm_processing_cmd = false; } else - rc = count; + rc = 0; spin_unlock(&ibmvtpm->rtce_lock); return rc; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index d8f10047fbba..97f6d4fe0aee 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -354,7 +354,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) for (i = 0; i < count; i++) { wait_and_send(chip, buf[i]); } - return count; + return 0; } static void tpm_inf_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 5d6cce74cd3f..9bee3c5eb4bf 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -226,7 +226,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) } outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND); - return count; + return 0; } static void tpm_nsc_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index bf7e49cfa643..bb0c2e160562 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -481,7 +481,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) goto out_err; } } - return len; + return 0; out_err: tpm_tis_ready(chip); return rc; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 87a0ce47f201..ecbb63f8d231 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -335,7 +335,6 @@ static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) { struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); - int rc = 0; if (count > sizeof(proxy_dev->buffer)) { dev_err(&chip->dev, @@ -366,7 +365,7 @@ static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) wake_up_interruptible(&proxy_dev->wq); - return rc; + return 0; } static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip) diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index b150f87f38f5..5a327eb7f63a 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -173,7 +173,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) return -ETIME; } - return count; + return 0; } static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) From 442601e87a4769a8daba4976ec3afa5222ca211d Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 8 Feb 2019 18:30:59 +0200 Subject: [PATCH 1631/4277] tpm/tpm_i2c_atmel: Return -E2BIG when the transfer is incomplete Return -E2BIG when the transfer is incomplete. The upper layer does not retry, so not doing that is incorrect behaviour. Cc: stable@vger.kernel.org Fixes: a2871c62e186 ("tpm: Add support for Atmel I2C TPMs") Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Reviewed-by: Jerry Snitselaar --- drivers/char/tpm/tpm_i2c_atmel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 32a8e27c5382..cc4e642d3180 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -69,6 +69,10 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) if (status < 0) return status; + /* The upper layer does not support incomplete sends. */ + if (status != len) + return -E2BIG; + return 0; } From bb3b6b0fc57182b568ded61c55eff8a02fcfe27b Mon Sep 17 00:00:00 2001 From: Jia Zhang Date: Fri, 11 Jan 2019 16:59:32 +0800 Subject: [PATCH 1632/4277] tpm: Simplify the measurements loop The responsibility of tpm1_bios_measurements_start() is to walk over the first *pos measurements, ensuring the skipped and to-be-read measurements are not out-of-boundary. This commit simplifies the loop by employing a do-while loop with the necessary sanity check. Signed-off-by: Jia Zhang Reviewd-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/eventlog/tpm1.c | 37 ++++++++++++-------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index 58c84784ba25..4cf830333684 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -74,7 +74,7 @@ static const char* tcpa_pc_event_id_strings[] = { /* returns pointer to start of pos. entry of tcg log */ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos) { - loff_t i; + loff_t i = 0; struct tpm_chip *chip = m->private; struct tpm_bios_log *log = &chip->log; void *addr = log->bios_event_log; @@ -83,38 +83,29 @@ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos) u32 converted_event_size; u32 converted_event_type; - /* read over *pos measurements */ - for (i = 0; i < *pos; i++) { + do { event = addr; + /* check if current entry is valid */ + if (addr + sizeof(struct tcpa_event) >= limit) + return NULL; + converted_event_size = do_endian_conversion(event->event_size); converted_event_type = do_endian_conversion(event->event_type); - if ((addr + sizeof(struct tcpa_event)) < limit) { - if ((converted_event_type == 0) && - (converted_event_size == 0)) - return NULL; - addr += (sizeof(struct tcpa_event) + - converted_event_size); - } - } + if (((converted_event_type == 0) && (converted_event_size == 0)) + || ((addr + sizeof(struct tcpa_event) + converted_event_size) + >= limit)) + return NULL; - /* now check if current entry is valid */ - if ((addr + sizeof(struct tcpa_event)) >= limit) - return NULL; + if (i++ == *pos) + break; - event = addr; - - converted_event_size = do_endian_conversion(event->event_size); - converted_event_type = do_endian_conversion(event->event_type); - - if (((converted_event_type == 0) && (converted_event_size == 0)) - || ((addr + sizeof(struct tcpa_event) + converted_event_size) - >= limit)) - return NULL; + addr += (sizeof(struct tcpa_event) + converted_event_size); + } while (1); return addr; } From 64494d39ff630a63b5308042b20132b491e3706b Mon Sep 17 00:00:00 2001 From: Jia Zhang Date: Fri, 11 Jan 2019 16:59:33 +0800 Subject: [PATCH 1633/4277] tpm: Fix off-by-one when reading binary_bios_measurements It is unable to read the entry when it is the only one in binary_bios_measurements: 00000000 00 00 00 00 08 00 00 00 c4 2f ed ad 26 82 00 cb 00000010 1d 15 f9 78 41 c3 44 e7 9d ae 33 20 00 00 00 00 00000020 This is obviously a firmware problem on my linux machine: Manufacturer: Inspur Product Name: SA5212M4 Version: 01 However, binary_bios_measurements should return it any way, rather than nothing, after all its content is completely valid. Fixes: 55a82ab3181b ("tpm: add bios measurement log") Signed-off-by: Jia Zhang Reviewd-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/eventlog/tpm1.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index 4cf830333684..bfdff9271be0 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -88,7 +88,7 @@ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos) event = addr; /* check if current entry is valid */ - if (addr + sizeof(struct tcpa_event) >= limit) + if (addr + sizeof(struct tcpa_event) > limit) return NULL; converted_event_size = @@ -98,7 +98,7 @@ static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos) if (((converted_event_type == 0) && (converted_event_size == 0)) || ((addr + sizeof(struct tcpa_event) + converted_event_size) - >= limit)) + > limit)) return NULL; if (i++ == *pos) @@ -125,7 +125,7 @@ static void *tpm1_bios_measurements_next(struct seq_file *m, void *v, v += sizeof(struct tcpa_event) + converted_event_size; /* now check if current entry is valid */ - if ((v + sizeof(struct tcpa_event)) >= limit) + if ((v + sizeof(struct tcpa_event)) > limit) return NULL; event = v; @@ -134,7 +134,7 @@ static void *tpm1_bios_measurements_next(struct seq_file *m, void *v, converted_event_type = do_endian_conversion(event->event_type); if (((converted_event_type == 0) && (converted_event_size == 0)) || - ((v + sizeof(struct tcpa_event) + converted_event_size) >= limit)) + ((v + sizeof(struct tcpa_event) + converted_event_size) > limit)) return NULL; (*pos)++; From c8faabfc6f48009fb0d9ad4203aecfa569e5ff8d Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 24 Jan 2019 16:49:05 +0100 Subject: [PATCH 1634/4277] tpm: add _head suffix to tcg_efi_specid_event and tcg_pcr_event2 TCG defines two structures, TCG_EfiSpecIDEventStruct and TCG_PCR_EVENT2, which contain variable-sized arrays in the middle of the definition. Since these structures are not suitable for type casting, this patch removes structure members after the variable-sized arrays and adds the _head suffix to the structure name, to indicate that the renamed structures do not contain all fields defined by TCG. Lastly, given that variable-sized arrays are now in the last position, and given that the size of the arrays cannot be determined in advance, this patch also sets the size of those arrays to zero and removes the definition of TPM2_ACTIVE_PCR_BANKS. Signed-off-by: Roberto Sassu Reviewed-by: Jarkko Sakkinen Tested-by: Nayna Jain Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/eventlog/tpm2.c | 12 ++++++------ include/linux/tpm_eventlog.h | 12 ++++-------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c index 1b8fa9de2cac..d8b77133a83a 100644 --- a/drivers/char/tpm/eventlog/tpm2.c +++ b/drivers/char/tpm/eventlog/tpm2.c @@ -37,10 +37,10 @@ * * Returns size of the event. If it is an invalid event, returns 0. */ -static int calc_tpm2_event_size(struct tcg_pcr_event2 *event, +static int calc_tpm2_event_size(struct tcg_pcr_event2_head *event, struct tcg_pcr_event *event_header) { - struct tcg_efi_specid_event *efispecid; + struct tcg_efi_specid_event_head *efispecid; struct tcg_event_field *event_field; void *marker; void *marker_start; @@ -55,7 +55,7 @@ static int calc_tpm2_event_size(struct tcg_pcr_event2 *event, marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type) + sizeof(event->count); - efispecid = (struct tcg_efi_specid_event *)event_header->event; + efispecid = (struct tcg_efi_specid_event_head *)event_header->event; /* Check if event is malformed. */ if (event->count > efispecid->num_algs) @@ -95,7 +95,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos) void *addr = log->bios_event_log; void *limit = log->bios_event_log_end; struct tcg_pcr_event *event_header; - struct tcg_pcr_event2 *event; + struct tcg_pcr_event2_head *event; size_t size; int i; @@ -136,7 +136,7 @@ static void *tpm2_bios_measurements_next(struct seq_file *m, void *v, loff_t *pos) { struct tcg_pcr_event *event_header; - struct tcg_pcr_event2 *event; + struct tcg_pcr_event2_head *event; struct tpm_chip *chip = m->private; struct tpm_bios_log *log = &chip->log; void *limit = log->bios_event_log_end; @@ -180,7 +180,7 @@ static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v) struct tpm_chip *chip = m->private; struct tpm_bios_log *log = &chip->log; struct tcg_pcr_event *event_header = log->bios_event_log; - struct tcg_pcr_event2 *event = v; + struct tcg_pcr_event2_head *event = v; void *temp_ptr; size_t size; diff --git a/include/linux/tpm_eventlog.h b/include/linux/tpm_eventlog.h index 20d9da77fc11..f47342361e87 100644 --- a/include/linux/tpm_eventlog.h +++ b/include/linux/tpm_eventlog.h @@ -8,7 +8,6 @@ #define TCG_EVENT_NAME_LEN_MAX 255 #define MAX_TEXT_EVENT 1000 /* Max event string length */ #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ -#define TPM2_ACTIVE_PCR_BANKS 3 #define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x1 #define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x2 @@ -82,7 +81,7 @@ struct tcg_efi_specid_event_algs { u16 digest_size; } __packed; -struct tcg_efi_specid_event { +struct tcg_efi_specid_event_head { u8 signature[16]; u32 platform_class; u8 spec_version_minor; @@ -90,9 +89,7 @@ struct tcg_efi_specid_event { u8 spec_errata; u8 uintnsize; u32 num_algs; - struct tcg_efi_specid_event_algs digest_sizes[TPM2_ACTIVE_PCR_BANKS]; - u8 vendor_info_size; - u8 vendor_info[0]; + struct tcg_efi_specid_event_algs digest_sizes[]; } __packed; struct tcg_pcr_event { @@ -113,12 +110,11 @@ struct tpm2_digest { u8 digest[SHA512_DIGEST_SIZE]; } __packed; -struct tcg_pcr_event2 { +struct tcg_pcr_event2_head { u32 pcr_idx; u32 event_type; u32 count; - struct tpm2_digest digests[TPM2_ACTIVE_PCR_BANKS]; - struct tcg_event_field event; + struct tpm2_digest digests[]; } __packed; #endif From 08a8112ad9c17ec3d78363bf7123df6598f09bc2 Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Tue, 29 Jan 2019 11:59:11 -0700 Subject: [PATCH 1635/4277] tpm: don't print error message in tpm_transmit_cmd when tpm still testing Currently tpm_transmit_cmd will print an error message if the tpm returns something other than TPM2_RC_SUCCESS. This means that if the tpm returns that it is testing an error message will be printed, and this can cause confusion for the end user. So avoid printing the error message if TPM2_RC_TESTING is the return code. Cc: Peter Huewe Cc: Jarkko Sakkinen Cc: Jason Gunthorpe Signed-off-by: Jerry Snitselaar Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 88d2e01a651d..624075e767b8 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -406,7 +406,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, err = be32_to_cpu(header->return_code); if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED - && desc) + && err != TPM2_RC_TESTING && desc) dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); if (err) From 36ce089758b1b55df5854d6b6d74713f609e125d Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Wed, 30 Jan 2019 15:06:58 -0700 Subject: [PATCH 1636/4277] tpm: don't return bool from update_timeouts Set tpm_chip->timeouts_adjusted directly in the update_timeouts code instead of returning bool. In case of tpm read failing print warning that the read failed and continue on. Signed-off-by: Jerry Snitselaar Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm1-cmd.c | 3 +-- drivers/char/tpm/tpm_tis_core.c | 15 +++++++++------ include/linux/tpm.h | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 6f306338953b..bda9a16b44f6 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -380,8 +380,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip) * of misreporting. */ if (chip->ops->update_timeouts) - chip->timeout_adjusted = - chip->ops->update_timeouts(chip, timeout_eff); + chip->ops->update_timeouts(chip, timeout_eff); if (!chip->timeout_adjusted) { /* Restore default if chip reported 0 */ diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index bb0c2e160562..c6b0c6d541a5 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -521,35 +521,38 @@ static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, }; -static bool tpm_tis_update_timeouts(struct tpm_chip *chip, +static void tpm_tis_update_timeouts(struct tpm_chip *chip, unsigned long *timeout_cap) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int i, rc; u32 did_vid; + chip->timeout_adjusted = false; + if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, true); rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid); - if (rc < 0) + if (rc < 0) { + dev_warn(&chip->dev, "%s: failed to read did_vid: %d\n", + __func__, rc); goto out; + } for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { if (vendor_timeout_overrides[i].did_vid != did_vid) continue; memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, sizeof(vendor_timeout_overrides[i].timeout_us)); - rc = true; + chip->timeout_adjusted = true; } - rc = false; - out: if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); - return rc; + return; } /* diff --git a/include/linux/tpm.h b/include/linux/tpm.h index b49a55cf775f..13563b8c0c3a 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -41,7 +41,7 @@ struct tpm_class_ops { int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); void (*cancel) (struct tpm_chip *chip); u8 (*status) (struct tpm_chip *chip); - bool (*update_timeouts)(struct tpm_chip *chip, + void (*update_timeouts)(struct tpm_chip *chip, unsigned long *timeout_cap); int (*go_idle)(struct tpm_chip *chip); int (*cmd_ready)(struct tpm_chip *chip); From 412eb585587a1dc43c9622db79de9663b6c4c238 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 26 Oct 2018 14:34:22 +0100 Subject: [PATCH 1637/4277] tpm: use tpm_buf in tpm_transmit_cmd() as the IO parameter Since we pass an initialized struct tpm_buf instance in every call site now, it is cleaner to pass that directly to the tpm_transmit_cmd() as the TPM command/response buffer. Fine-tune a little bit tpm_transmit() and tpm_transmit_cmd() comments while doing this. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-interface.c | 67 +++++++++++++++++-------------- drivers/char/tpm/tpm-sysfs.c | 2 +- drivers/char/tpm/tpm.h | 5 +-- drivers/char/tpm/tpm1-cmd.c | 26 ++++-------- drivers/char/tpm/tpm2-cmd.c | 37 +++++++---------- drivers/char/tpm/tpm2-space.c | 4 +- drivers/char/tpm/tpm_vtpm_proxy.c | 3 +- 7 files changed, 64 insertions(+), 80 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 624075e767b8..ec54f1677505 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -307,23 +307,22 @@ out_locality: /** * tpm_transmit - Internal kernel interface to transmit TPM commands. + * @chip: a TPM chip to use + * @space: a TPM space + * @buf: a TPM command buffer + * @bufsiz: length of the TPM command buffer + * @flags: TPM transmit flags * - * @chip: TPM chip to use - * @space: tpm space - * @buf: TPM command buffer - * @bufsiz: length of the TPM command buffer - * @flags: tpm transmit flags - bitmap + * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from + * the TPM and retransmits the command after a delay up to a maximum wait of + * TPM2_DURATION_LONG. * - * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY - * returns from the TPM and retransmits the command after a delay up - * to a maximum wait of TPM2_DURATION_LONG. - * - * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2 - * only + * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0 + * only. * * Return: - * the length of the return when the operation is successful. - * A negative number for system errors (errno). + * * The response length - OK + * * -errno - A system error */ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz, unsigned int flags) @@ -374,33 +373,31 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, } return ret; } + /** * tpm_transmit_cmd - send a tpm command to the device - * The function extracts tpm out header return code - * - * @chip: TPM chip to use - * @space: tpm space - * @buf: TPM command buffer - * @bufsiz: length of the buffer - * @min_rsp_body_length: minimum expected length of response body - * @flags: tpm transmit flags - bitmap - * @desc: command description used in the error message + * @chip: a TPM chip to use + * @space: a TPM space + * @buf: a TPM command buffer + * @min_rsp_body_length: minimum expected length of response body + * @flags: TPM transmit flags + * @desc: command description used in the error message * * Return: - * 0 when the operation is successful. - * A negative number for system errors (errno). - * A positive number for a TPM error. + * * 0 - OK + * * -errno - A system error + * * TPM_RC - A TPM error */ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - void *buf, size_t bufsiz, - size_t min_rsp_body_length, unsigned int flags, - const char *desc) + struct tpm_buf *buf, size_t min_rsp_body_length, + unsigned int flags, const char *desc) { - const struct tpm_output_header *header = buf; + const struct tpm_output_header *header = + (struct tpm_output_header *)buf->data; int err; ssize_t len; - len = tpm_transmit(chip, space, buf, bufsiz, flags); + len = tpm_transmit(chip, space, buf->data, PAGE_SIZE, flags); if (len < 0) return len; @@ -537,14 +534,22 @@ EXPORT_SYMBOL_GPL(tpm_pcr_extend); */ int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { + struct tpm_buf buf; int rc; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; - rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0, + rc = tpm_buf_init(&buf, 0, 0); + if (rc) + goto out; + + memcpy(buf.data, cmd, buflen); + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "attempting to a send a command"); + tpm_buf_destroy(&buf); +out: tpm_put_ops(chip); return rc; } diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index b88e08ec2c59..c2769e55cb6c 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -53,7 +53,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); - rc = tpm_transmit_cmd(chip, NULL, tpm_buf.data, PAGE_SIZE, + rc = tpm_transmit_cmd(chip, NULL, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, "attempting to read the PUBEK"); if (rc) { diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f27d1f38a93d..49bca4d1e786 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -503,9 +503,8 @@ enum tpm_transmit_flags { ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz, unsigned int flags); ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - void *buf, size_t bufsiz, - size_t min_rsp_body_length, unsigned int flags, - const char *desc); + struct tpm_buf *buf, size_t min_rsp_body_length, + unsigned int flags, const char *desc); int tpm_get_timeouts(struct tpm_chip *); int tpm_auto_startup(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index bda9a16b44f6..10a0b7683b4b 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -334,11 +334,9 @@ static int tpm1_startup(struct tpm_chip *chip) tpm_buf_append_u16(&buf, TPM_ST_CLEAR); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "attempting to start the TPM"); - tpm_buf_destroy(&buf); - return rc; } @@ -461,9 +459,7 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash, tpm_buf_append_u32(&buf, pcr_idx); tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - TPM_DIGEST_SIZE, 0, log_msg); - + rc = tpm_transmit_cmd(chip, NULL, &buf, TPM_DIGEST_SIZE, 0, log_msg); tpm_buf_destroy(&buf); return rc; } @@ -493,11 +489,9 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, tpm_buf_append_u32(&buf, 4); tpm_buf_append_u32(&buf, subcap_id); } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - min_cap_length, 0, desc); + rc = tpm_transmit_cmd(chip, NULL, &buf, min_cap_length, 0, desc); if (!rc) *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4]; - tpm_buf_destroy(&buf); return rc; } @@ -536,7 +530,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_append_u32(&buf, num_bytes); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, + rc = tpm_transmit_cmd(chip, NULL, &buf, sizeof(out->rng_data_len), 0, "attempting get random"); if (rc) @@ -582,8 +576,7 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append_u32(&buf, pcr_idx); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - TPM_DIGEST_SIZE, 0, + rc = tpm_transmit_cmd(chip, NULL, &buf, TPM_DIGEST_SIZE, 0, "attempting to read a pcr value"); if (rc) goto out; @@ -617,11 +610,8 @@ static int tpm1_continue_selftest(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - 0, 0, "continue selftest"); - + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "continue selftest"); tpm_buf_destroy(&buf); - return rc; } @@ -746,9 +736,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr) return rc; /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - 0, 0, NULL); - + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); /* * If the TPM indicates that it is too busy to respond to * this command then retry before giving up. It can take diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index a6bec13afa69..2bcf470c8e5d 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -197,8 +197,8 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append(&buf, (const unsigned char *)pcr_select, sizeof(pcr_select)); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - res_buf ? "attempting to read a pcr value" : NULL); + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, res_buf ? + "attempting to read a pcr value" : NULL); if (rc == 0 && res_buf) { out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE); @@ -264,7 +264,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, } } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "attempting extend a PCR value"); tpm_buf_destroy(&buf); @@ -309,7 +309,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM); tpm_buf_append_u16(&buf, num_bytes); - err = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, + err = tpm_transmit_cmd(chip, NULL, &buf, offsetof(struct tpm2_get_random_out, buffer), 0, "attempting get random"); @@ -362,9 +362,7 @@ void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, tpm_buf_append_u32(&buf, handle); - (void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags, - "flushing context"); - + tpm_transmit_cmd(chip, NULL, &buf, 0, flags, "flushing context"); tpm_buf_destroy(&buf); } @@ -478,8 +476,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0, - "sealing data"); + rc = tpm_transmit_cmd(chip, NULL, &buf, 4, 0, "sealing data"); if (rc) goto out; @@ -561,8 +558,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags, - "loading blob"); + rc = tpm_transmit_cmd(chip, NULL, &buf, 4, flags, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -612,8 +608,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags, - "unsealing"); + rc = tpm_transmit_cmd(chip, NULL, &buf, 6, flags, "unsealing"); if (rc > 0) rc = -EPERM; @@ -703,7 +698,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, property_id); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); if (!rc) { out = (struct tpm2_get_cap_out *) &buf.data[TPM_HEADER_SIZE]; @@ -733,8 +728,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) if (rc) return; tpm_buf_append_u16(&buf, shutdown_type); - tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, - "stopping the TPM"); + tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "stopping the TPM"); tpm_buf_destroy(&buf); } @@ -763,7 +757,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip) return rc; tpm_buf_append_u8(&buf, full); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "attempting the self test"); tpm_buf_destroy(&buf); @@ -800,7 +794,7 @@ int tpm2_probe(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); /* We ignore TPM return codes on purpose. */ if (rc >= 0) { out = (struct tpm_output_header *)buf.data; @@ -839,7 +833,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) tpm_buf_append_u32(&buf, 0); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0, + rc = tpm_transmit_cmd(chip, NULL, &buf, 9, 0, "get tpm pcr allocation"); if (rc) goto out; @@ -911,8 +905,7 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CC_FIRST); tpm_buf_append_u32(&buf, nr_commands); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, - 9 + 4 * nr_commands, 0, NULL); + rc = tpm_transmit_cmd(chip, NULL, &buf, 9 + 4 * nr_commands, 0, NULL); if (rc) { tpm_buf_destroy(&buf); goto out; @@ -969,7 +962,7 @@ static int tpm2_startup(struct tpm_chip *chip) return rc; tpm_buf_append_u16(&buf, TPM2_SU_CLEAR); - rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index dcdfde3c253e..1131a8e7b79b 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -83,7 +83,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); tpm_buf_append(&tbuf, &buf[*offset], body_size); - rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4, + rc = tpm_transmit_cmd(chip, NULL, &tbuf, 4, TPM_TRANSMIT_NESTED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", @@ -132,7 +132,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); - rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0, + rc = tpm_transmit_cmd(chip, NULL, &tbuf, 0, TPM_TRANSMIT_NESTED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index ecbb63f8d231..ed9a163caaa4 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -416,8 +416,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) proxy_dev->state |= STATE_DRIVER_COMMAND; - rc = tpm_transmit_cmd(chip, NULL, buf.data, tpm_buf_length(&buf), 0, - TPM_TRANSMIT_NESTED, + rc = tpm_transmit_cmd(chip, NULL, &buf, 0, TPM_TRANSMIT_NESTED, "attempting to set locality"); proxy_dev->state &= ~STATE_DRIVER_COMMAND; From c628610087d4dd45015dde53e13397fcad4ad95a Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 4 Nov 2018 12:39:02 +0200 Subject: [PATCH 1638/4277] tpm: fix invalid return value in pubek_show() Return zero when tpm_buf_init() fails as we do for other functions in tpm-sysfs.c. Fixes: da379f3c1db0c ("tpm: migrate pubek_show to struct tpm_buf") Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-sysfs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index c2769e55cb6c..7ed7eb6f906a 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -47,9 +47,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, memset(&anti_replay, 0, sizeof(anti_replay)); - rc = tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK); - if (rc) - return rc; + if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK)) + return 0; tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); From 41484674da31cb94ee35da5e5bf7038f48b8eb9d Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 4 Nov 2018 14:42:29 +0200 Subject: [PATCH 1639/4277] tpm: return 0 from pcrs_show() when tpm1_pcr_read() fails Do not print partial list of PCRs when tpm1_pcr_read() fails but instead return 0 from pcrs_show(). This is consistent behavior with other sysfs functions. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-sysfs.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 7ed7eb6f906a..928d4e839bb7 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -100,22 +100,21 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, { cap_t cap; u8 digest[TPM_DIGEST_SIZE]; - ssize_t rc; u32 i, j, num_pcrs; char *str = buf; struct tpm_chip *chip = to_tpm_chip(dev); - rc = tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap, - "attempting to determine the number of PCRS", - sizeof(cap.num_pcrs)); - if (rc) + if (tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap, + "attempting to determine the number of PCRS", + sizeof(cap.num_pcrs))) return 0; num_pcrs = be32_to_cpu(cap.num_pcrs); for (i = 0; i < num_pcrs; i++) { - rc = tpm1_pcr_read(chip, i, digest); - if (rc) + if (tpm1_pcr_read(chip, i, digest)) { + str = buf; break; + } str += sprintf(str, "PCR-%02d: ", i); for (j = 0; j < TPM_DIGEST_SIZE; j++) str += sprintf(str, "%02X ", digest[j]); From aff0c20bfe59e0895c75631e2639bc54efae8007 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 3 Nov 2018 18:36:38 +0200 Subject: [PATCH 1640/4277] tpm: print tpm2_commit_space() error inside tpm2_commit_space() The error logging for tpm2_commit_space() is in a wrong place. This commit moves it inside that function. Cc: James Bottomley Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-interface.c | 2 -- drivers/char/tpm/tpm2-space.c | 9 ++++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index ec54f1677505..e48ed28562f4 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -286,8 +286,6 @@ out_recv: } rc = tpm2_commit_space(chip, space, ordinal, buf, &len); - if (rc) - dev_err(&chip->dev, "tpm2_commit_space: error %d\n", rc); out: /* may fail but do not override previous error value in rc */ diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 1131a8e7b79b..5ecc73988f7c 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -501,19 +501,19 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, rc = tpm2_map_response_header(chip, cc, buf, *bufsiz); if (rc) { tpm2_flush_space(chip); - return rc; + goto out; } rc = tpm2_map_response_body(chip, cc, buf, *bufsiz); if (rc) { tpm2_flush_space(chip); - return rc; + goto out; } rc = tpm2_save_space(chip); if (rc) { tpm2_flush_space(chip); - return rc; + goto out; } *bufsiz = be32_to_cpu(header->length); @@ -526,4 +526,7 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE); return 0; +out: + dev_err(&chip->dev, "%s: error %d\n", __func__, rc); + return rc; } From b34b77a99b1a4ccccb54f2c4c6ef982d6b008c15 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 6 Nov 2018 19:04:30 +0200 Subject: [PATCH 1641/4277] tpm: declare struct tpm_header Declare struct tpm_header that replaces struct tpm_input_header and struct tpm_output_header. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-interface.c | 21 ++++++++------------- drivers/char/tpm/tpm.h | 29 +++++++++++++---------------- drivers/char/tpm/tpm2-cmd.c | 4 ++-- drivers/char/tpm/tpm2-space.c | 8 ++++---- drivers/char/tpm/tpm_i2c_atmel.c | 5 ++--- drivers/char/tpm/tpm_vtpm_proxy.c | 8 ++++---- drivers/char/tpm/xen-tpmfront.c | 2 +- 7 files changed, 34 insertions(+), 43 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index e48ed28562f4..e0ed31cbc7e9 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -62,12 +62,10 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -static int tpm_validate_command(struct tpm_chip *chip, - struct tpm_space *space, - const u8 *cmd, - size_t len) +static int tpm_validate_command(struct tpm_chip *chip, struct tpm_space *space, + const void *cmd, size_t len) { - const struct tpm_input_header *header = (const void *)cmd; + const struct tpm_header *header = cmd; int i; u32 cc; u32 attrs; @@ -161,12 +159,10 @@ static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) return chip->ops->go_idle(chip); } -static ssize_t tpm_try_transmit(struct tpm_chip *chip, - struct tpm_space *space, - u8 *buf, size_t bufsiz, - unsigned int flags) +static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, + void *buf, size_t bufsiz, unsigned int flags) { - struct tpm_output_header *header = (void *)buf; + struct tpm_header *header = buf; int rc; ssize_t len = 0; u32 count, ordinal; @@ -325,7 +321,7 @@ out_locality: ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz, unsigned int flags) { - struct tpm_output_header *header = (struct tpm_output_header *)buf; + struct tpm_header *header = (struct tpm_header *)buf; /* space for header and handles */ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; unsigned int delay_msec = TPM2_DURATION_SHORT; @@ -390,8 +386,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, struct tpm_buf *buf, size_t min_rsp_body_length, unsigned int flags, const char *desc) { - const struct tpm_output_header *header = - (struct tpm_output_header *)buf->data; + const struct tpm_header *header = (struct tpm_header *)buf->data; int err; ssize_t len; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 49bca4d1e786..1454ef19d2f4 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -273,16 +273,13 @@ struct tpm_chip { #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) -struct tpm_input_header { - __be16 tag; - __be32 length; - __be32 ordinal; -} __packed; - -struct tpm_output_header { - __be16 tag; - __be32 length; - __be32 return_code; +struct tpm_header { + __be16 tag; + __be32 length; + union { + __be32 ordinal; + __be32 return_code; + }; } __packed; #define TPM_TAG_RQU_COMMAND 193 @@ -401,8 +398,8 @@ struct tpm_buf { static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal) { - struct tpm_input_header *head; - head = (struct tpm_input_header *)buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; + head->tag = cpu_to_be16(tag); head->length = cpu_to_be32(sizeof(*head)); head->ordinal = cpu_to_be32(ordinal); @@ -428,14 +425,14 @@ static inline void tpm_buf_destroy(struct tpm_buf *buf) static inline u32 tpm_buf_length(struct tpm_buf *buf) { - struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; return be32_to_cpu(head->length); } static inline u16 tpm_buf_tag(struct tpm_buf *buf) { - struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; return be16_to_cpu(head->tag); } @@ -444,7 +441,7 @@ static inline void tpm_buf_append(struct tpm_buf *buf, const unsigned char *new_data, unsigned int new_len) { - struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + struct tpm_header *head = (struct tpm_header *)buf->data; u32 len = tpm_buf_length(buf); /* Return silently if overflow has already happened. */ @@ -582,7 +579,7 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space); int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, u8 *cmd); int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, - u32 cc, u8 *buf, size_t *bufsiz); + u32 cc, void *buf, size_t *bufsiz); int tpm_bios_log_setup(struct tpm_chip *chip); void tpm_bios_log_teardown(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 2bcf470c8e5d..ab03f8600f89 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -784,7 +784,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip) */ int tpm2_probe(struct tpm_chip *chip) { - struct tpm_output_header *out; + struct tpm_header *out; struct tpm_buf buf; int rc; @@ -797,7 +797,7 @@ int tpm2_probe(struct tpm_chip *chip) rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); /* We ignore TPM return codes on purpose. */ if (rc >= 0) { - out = (struct tpm_output_header *)buf.data; + out = (struct tpm_header *)buf.data; if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS) chip->flags |= TPM_CHIP_FLAG_TPM2; } diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 5ecc73988f7c..39cb3915771e 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -334,7 +334,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) { struct tpm_space *space = &chip->work_space; - struct tpm_output_header *header = (void *)rsp; + struct tpm_header *header = (struct tpm_header *)rsp; u32 phandle; u32 phandle_type; u32 vhandle; @@ -394,7 +394,7 @@ static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len) { struct tpm_space *space = &chip->work_space; - struct tpm_output_header *header = (void *)rsp; + struct tpm_header *header = (struct tpm_header *)rsp; struct tpm2_cap_handles *data; u32 phandle; u32 phandle_type; @@ -490,9 +490,9 @@ static int tpm2_save_space(struct tpm_chip *chip) } int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, - u32 cc, u8 *buf, size_t *bufsiz) + u32 cc, void *buf, size_t *bufsiz) { - struct tpm_output_header *header = (void *)buf; + struct tpm_header *header = buf; int rc; if (!space) diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index cc4e642d3180..8a7e80923091 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -46,7 +46,7 @@ struct priv_data { /* This is the amount we read on the first try. 25 was chosen to fit a * fair number of read responses in the buffer so a 2nd retry can be * avoided in small message cases. */ - u8 buffer[sizeof(struct tpm_output_header) + 25]; + u8 buffer[sizeof(struct tpm_header) + 25]; }; static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) @@ -80,8 +80,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) { struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); - struct tpm_output_header *hdr = - (struct tpm_output_header *)priv->buffer; + struct tpm_header *hdr = (struct tpm_header *)priv->buffer; u32 expected_len; int rc; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index ed9a163caaa4..986d7e8147b8 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -303,9 +303,9 @@ out: static int vtpm_proxy_is_driver_command(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_input_header *hdr = (struct tpm_input_header *)buf; + struct tpm_header *hdr = (struct tpm_header *)buf; - if (count < sizeof(struct tpm_input_header)) + if (count < sizeof(struct tpm_header)) return 0; if (chip->flags & TPM_CHIP_FLAG_TPM2) { @@ -401,7 +401,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) { struct tpm_buf buf; int rc; - const struct tpm_output_header *header; + const struct tpm_header *header; struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -426,7 +426,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) goto out; } - header = (const struct tpm_output_header *)buf.data; + header = (const struct tpm_header *)buf.data; rc = be32_to_cpu(header->return_code); if (rc) locality = -1; diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 5a327eb7f63a..4e2d00cb0d81 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -163,7 +163,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) wmb(); notify_remote_via_evtchn(priv->evtchn); - ordinal = be32_to_cpu(((struct tpm_input_header*)buf)->ordinal); + ordinal = be32_to_cpu(((struct tpm_header *)buf)->ordinal); duration = tpm_calc_ordinal_duration(chip, ordinal); if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration, From 720b071102d8fab3f0a2808db3ba76c5ce880b7a Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 3 Nov 2018 02:31:07 +0200 Subject: [PATCH 1642/4277] tpm: access command header through struct in tpm_try_transmit() Instead of accessing fields of the command header through offsets to the raw buffer, it is a better idea to use the header struct pointer that is already used elsewhere in the function. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index e0ed31cbc7e9..7870e3178ada 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -187,8 +187,8 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, if (bufsiz > TPM_BUFSIZE) bufsiz = TPM_BUFSIZE; - count = be32_to_cpu(*((__be32 *) (buf + 2))); - ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + count = be32_to_cpu(header->length); + ordinal = be32_to_cpu(header->ordinal); if (count == 0) return -ENODATA; if (count > bufsiz) { From c4df71d43a5beaab5fe13a8d8974f77596bb2a22 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 3 Nov 2018 04:46:50 +0200 Subject: [PATCH 1643/4277] tpm: encapsulate tpm_dev_transmit() Encapsulate tpm_transmit() call pattern to tpm_dev_transmit() because it is identically used from two places. Use unlocked version of tpm_transmit() so that we are able to move the calls to tpm2_prepare_space() and tpm2_commit_space() later on to this new function. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-dev-common.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 5eecad233ea1..759796953d84 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -27,7 +27,19 @@ static struct workqueue_struct *tpm_dev_wq; static DEFINE_MUTEX(tpm_dev_wq_lock); -static void tpm_async_work(struct work_struct *work) +static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, + u8 *buf, size_t bufsiz) +{ + ssize_t ret; + + mutex_lock(&chip->tpm_mutex); + ret = tpm_transmit(chip, space, buf, bufsiz, TPM_TRANSMIT_UNLOCKED); + mutex_unlock(&chip->tpm_mutex); + + return ret; +} + +static void tpm_dev_async_work(struct work_struct *work) { struct file_priv *priv = container_of(work, struct file_priv, async_work); @@ -35,9 +47,8 @@ static void tpm_async_work(struct work_struct *work) mutex_lock(&priv->buffer_mutex); priv->command_enqueued = false; - ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, - sizeof(priv->data_buffer), 0); - + ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, + sizeof(priv->data_buffer)); tpm_put_ops(priv->chip); if (ret > 0) { priv->response_length = ret; @@ -80,7 +91,7 @@ void tpm_common_open(struct file *file, struct tpm_chip *chip, mutex_init(&priv->buffer_mutex); timer_setup(&priv->user_read_timer, user_reader_timeout, 0); INIT_WORK(&priv->timeout_work, tpm_timeout_work); - INIT_WORK(&priv->async_work, tpm_async_work); + INIT_WORK(&priv->async_work, tpm_dev_async_work); init_waitqueue_head(&priv->async_wait); file->private_data = priv; } @@ -183,8 +194,8 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, return size; } - ret = tpm_transmit(priv->chip, priv->space, priv->data_buffer, - sizeof(priv->data_buffer), 0); + ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, + sizeof(priv->data_buffer)); tpm_put_ops(priv->chip); if (ret > 0) { From 304ff672537c25339578273487a1d84e4936e006 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 4 Nov 2018 16:15:49 +0200 Subject: [PATCH 1644/4277] tpm: clean up tpm_try_transmit() error handling flow Move locking, locality handling and power management to tpm_transmit() in order to simplify the flow. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-interface.c | 94 +++++++++++++++----------------- drivers/char/tpm/tpm.h | 1 + drivers/char/tpm/tpm2-space.c | 2 +- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 7870e3178ada..ce06e66b4400 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -167,7 +167,6 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, ssize_t len = 0; u32 count, ordinal; unsigned long stop; - bool need_locality; rc = tpm_validate_command(chip, space, buf, bufsiz); if (rc == -EINVAL) @@ -197,37 +196,16 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, return -E2BIG; } - if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED)) - mutex_lock(&chip->tpm_mutex); - - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, true); - - /* Store the decision as chip->locality will be changed. */ - need_locality = chip->locality == -1; - - if (need_locality) { - rc = tpm_request_locality(chip, flags); - if (rc < 0) { - need_locality = false; - goto out_locality; - } - } - - rc = tpm_cmd_ready(chip, flags); - if (rc) - goto out_locality; - rc = tpm2_prepare_space(chip, space, ordinal, buf); if (rc) - goto out; + return rc; rc = chip->ops->send(chip, buf, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, "%s: send(): error %d\n", __func__, rc); - goto out; + goto out_rc; } /* A sanity check. send() should just return zero on success e.g. @@ -252,7 +230,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, if (chip->ops->req_canceled(chip, status)) { dev_err(&chip->dev, "Operation Canceled\n"); rc = -ECANCELED; - goto out; + goto out_rc; } tpm_msleep(TPM_TIMEOUT_POLL); @@ -262,40 +240,20 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, chip->ops->cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); rc = -ETIME; - goto out; + goto out_rc; out_recv: len = chip->ops->recv(chip, buf, bufsiz); if (len < 0) { rc = len; - dev_err(&chip->dev, - "tpm_transmit: tpm_recv: error %d\n", rc); - goto out; - } else if (len < TPM_HEADER_SIZE) { + dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc); + } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; - goto out; - } - if (len != be32_to_cpu(header->length)) { - rc = -EFAULT; - goto out; - } +out_rc: + if (!rc) + rc = tpm2_commit_space(chip, space, ordinal, buf, &len); - rc = tpm2_commit_space(chip, space, ordinal, buf, &len); - -out: - /* may fail but do not override previous error value in rc */ - tpm_go_idle(chip, flags); - -out_locality: - if (need_locality) - tpm_relinquish_locality(chip, flags); - - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, false); - - if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED)) - mutex_unlock(&chip->tpm_mutex); return rc ? rc : len; } @@ -325,6 +283,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, /* space for header and handles */ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; unsigned int delay_msec = TPM2_DURATION_SHORT; + bool has_locality = false; u32 rc = 0; ssize_t ret; const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE, @@ -340,7 +299,40 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, memcpy(save, buf, save_size); for (;;) { + if (!(flags & TPM_TRANSMIT_UNLOCKED) && + !(flags & TPM_TRANSMIT_NESTED)) + mutex_lock(&chip->tpm_mutex); + + if (chip->ops->clk_enable != NULL) + chip->ops->clk_enable(chip, true); + + if (chip->locality == -1) { + ret = tpm_request_locality(chip, flags); + if (ret) + goto out_locality; + has_locality = true; + } + + ret = tpm_cmd_ready(chip, flags); + if (ret) + goto out_locality; + ret = tpm_try_transmit(chip, space, buf, bufsiz, flags); + + /* This may fail but do not override ret. */ + tpm_go_idle(chip, flags); + +out_locality: + if (has_locality) + tpm_relinquish_locality(chip, flags); + + if (chip->ops->clk_enable != NULL) + chip->ops->clk_enable(chip, false); + + if (!(flags & TPM_TRANSMIT_UNLOCKED) && + !(flags & TPM_TRANSMIT_NESTED)) + mutex_unlock(&chip->tpm_mutex); + if (ret < 0) break; rc = be32_to_cpu(header->return_code); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 1454ef19d2f4..6eb67ccad2a3 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -576,6 +576,7 @@ int tpm2_probe(struct tpm_chip *chip); int tpm2_find_cc(struct tpm_chip *chip, u32 cc); int tpm2_init_space(struct tpm_space *space); void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space); +void tpm2_flush_space(struct tpm_chip *chip); int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, u8 *cmd); int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 39cb3915771e..5d6487575074 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -162,7 +162,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, return 0; } -static void tpm2_flush_space(struct tpm_chip *chip) +void tpm2_flush_space(struct tpm_chip *chip) { struct tpm_space *space = &chip->work_space; int i; From c3465a370fb371136f6ed7f1d15bcae1037270d3 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 3 Nov 2018 03:04:56 +0200 Subject: [PATCH 1645/4277] tpm: move tpm_validate_commmand() to tpm2-space.c Move tpm_validate_command() to tpm2-space.c and make it part of the tpm2_prepare_space() flow. Make cc resolution as part of the TPM space functionality in order to detach it from rest of the tpm_transmit() flow. Cc: James Bottomley Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-interface.c | 70 +++++++------------------------- drivers/char/tpm/tpm.h | 9 ++-- drivers/char/tpm/tpm2-space.c | 51 ++++++++++++++++++++--- 3 files changed, 66 insertions(+), 64 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index ce06e66b4400..88ed6109984e 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -62,45 +62,6 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -static int tpm_validate_command(struct tpm_chip *chip, struct tpm_space *space, - const void *cmd, size_t len) -{ - const struct tpm_header *header = cmd; - int i; - u32 cc; - u32 attrs; - unsigned int nr_handles; - - if (len < TPM_HEADER_SIZE) - return -EINVAL; - - if (!space) - return 0; - - if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) { - cc = be32_to_cpu(header->ordinal); - - i = tpm2_find_cc(chip, cc); - if (i < 0) { - dev_dbg(&chip->dev, "0x%04X is an invalid command\n", - cc); - return -EOPNOTSUPP; - } - - attrs = chip->cc_attrs_tbl[i]; - nr_handles = - 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); - if (len < TPM_HEADER_SIZE + 4 * nr_handles) - goto err_len; - } - - return 0; -err_len: - dev_dbg(&chip->dev, - "%s: insufficient command length %zu", __func__, len); - return -EINVAL; -} - static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) { int rc; @@ -168,20 +129,8 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, u32 count, ordinal; unsigned long stop; - rc = tpm_validate_command(chip, space, buf, bufsiz); - if (rc == -EINVAL) - return rc; - /* - * If the command is not implemented by the TPM, synthesize a - * response with a TPM2_RC_COMMAND_CODE return for user-space. - */ - if (rc == -EOPNOTSUPP) { - header->length = cpu_to_be32(sizeof(*header)); - header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); - header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | - TSS2_RESMGR_TPM_RC_LAYER); - return sizeof(*header); - } + if (bufsiz < TPM_HEADER_SIZE) + return -EINVAL; if (bufsiz > TPM_BUFSIZE) bufsiz = TPM_BUFSIZE; @@ -196,7 +145,18 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, return -E2BIG; } - rc = tpm2_prepare_space(chip, space, ordinal, buf); + rc = tpm2_prepare_space(chip, space, buf, bufsiz); + /* + * If the command is not implemented by the TPM, synthesize a + * response with a TPM2_RC_COMMAND_CODE return for user-space. + */ + if (rc == -EOPNOTSUPP) { + header->length = cpu_to_be32(sizeof(*header)); + header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | + TSS2_RESMGR_TPM_RC_LAYER); + return sizeof(*header); + } if (rc) return rc; @@ -252,7 +212,7 @@ out_recv: out_rc: if (!rc) - rc = tpm2_commit_space(chip, space, ordinal, buf, &len); + rc = tpm2_commit_space(chip, space, buf, &len); return rc ? rc : len; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 6eb67ccad2a3..e84333259e28 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -264,6 +264,7 @@ struct tpm_chip { #endif /* CONFIG_ACPI */ struct tpm_space work_space; + u32 last_cc; u32 nr_commands; u32 *cc_attrs_tbl; @@ -577,10 +578,10 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc); int tpm2_init_space(struct tpm_space *space); void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space); void tpm2_flush_space(struct tpm_chip *chip); -int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, - u8 *cmd); -int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, - u32 cc, void *buf, size_t *bufsiz); +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd, + size_t cmdsiz); +int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, void *buf, + size_t *bufsiz); int tpm_bios_log_setup(struct tpm_chip *chip); void tpm_bios_log_teardown(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 5d6487575074..92aea758d9c6 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -264,14 +264,54 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd) return 0; } -int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, - u8 *cmd) +static int tpm_find_and_validate_cc(struct tpm_chip *chip, + struct tpm_space *space, + const void *cmd, size_t len) +{ + const struct tpm_header *header = (const void *)cmd; + int i; + u32 cc; + u32 attrs; + unsigned int nr_handles; + + if (len < TPM_HEADER_SIZE || !chip->nr_commands) + return -EINVAL; + + cc = be32_to_cpu(header->ordinal); + + i = tpm2_find_cc(chip, cc); + if (i < 0) { + dev_dbg(&chip->dev, "0x%04X is an invalid command\n", + cc); + return -EOPNOTSUPP; + } + + attrs = chip->cc_attrs_tbl[i]; + nr_handles = + 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); + if (len < TPM_HEADER_SIZE + 4 * nr_handles) + goto err_len; + + return cc; +err_len: + dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__, + len); + return -EINVAL; +} + +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd, + size_t cmdsiz) { int rc; + int cc; if (!space) return 0; + cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz); + if (cc < 0) + return cc; + memcpy(&chip->work_space.context_tbl, &space->context_tbl, sizeof(space->context_tbl)); memcpy(&chip->work_space.session_tbl, &space->session_tbl, @@ -291,6 +331,7 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, return rc; } + chip->last_cc = cc; return 0; } @@ -490,7 +531,7 @@ static int tpm2_save_space(struct tpm_chip *chip) } int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, - u32 cc, void *buf, size_t *bufsiz) + void *buf, size_t *bufsiz) { struct tpm_header *header = buf; int rc; @@ -498,13 +539,13 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, if (!space) return 0; - rc = tpm2_map_response_header(chip, cc, buf, *bufsiz); + rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz); if (rc) { tpm2_flush_space(chip); goto out; } - rc = tpm2_map_response_body(chip, cc, buf, *bufsiz); + rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz); if (rc) { tpm2_flush_space(chip); goto out; From 29b47ce987591254602cd7b69acd0eecc62f0a70 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 3 Nov 2018 05:22:36 +0200 Subject: [PATCH 1646/4277] tpm: move TPM space code out of tpm_transmit() Prepare and commit TPM space before and after calling tpm_transmit() instead of doing that inside tpm_transmit(). After this change we can remove TPM_TRANSMIT_NESTED flag from tpm2_prepare_space() and tpm2_commit_space() and replace it with TPM_TRANSMIT_UNLOCKED. Cc: James Bottomley Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-dev-common.c | 28 +++++++++++++++++++++++++--- drivers/char/tpm/tpm-interface.c | 27 +++------------------------ drivers/char/tpm/tpm2-space.c | 12 ++++++------ 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 759796953d84..327d1dca92c8 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -30,13 +30,35 @@ static DEFINE_MUTEX(tpm_dev_wq_lock); static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz) { - ssize_t ret; + struct tpm_header *header = (void *)buf; + ssize_t ret, len; mutex_lock(&chip->tpm_mutex); - ret = tpm_transmit(chip, space, buf, bufsiz, TPM_TRANSMIT_UNLOCKED); + ret = tpm2_prepare_space(chip, space, buf, bufsiz); + /* If the command is not implemented by the TPM, synthesize a + * response with a TPM2_RC_COMMAND_CODE return for user-space. + */ + if (ret == -EOPNOTSUPP) { + header->length = cpu_to_be32(sizeof(*header)); + header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | + TSS2_RESMGR_TPM_RC_LAYER); + ret = sizeof(*header); + } + if (ret) + goto out_lock; + + len = tpm_transmit(chip, space, buf, bufsiz, TPM_TRANSMIT_UNLOCKED); + if (len < 0) + ret = len; + + if (!ret) + ret = tpm2_commit_space(chip, space, buf, &len); + +out_lock: mutex_unlock(&chip->tpm_mutex); - return ret; + return ret ? ret : len; } static void tpm_dev_async_work(struct work_struct *work) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 88ed6109984e..138c9c165a9d 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -145,27 +145,12 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, return -E2BIG; } - rc = tpm2_prepare_space(chip, space, buf, bufsiz); - /* - * If the command is not implemented by the TPM, synthesize a - * response with a TPM2_RC_COMMAND_CODE return for user-space. - */ - if (rc == -EOPNOTSUPP) { - header->length = cpu_to_be32(sizeof(*header)); - header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); - header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | - TSS2_RESMGR_TPM_RC_LAYER); - return sizeof(*header); - } - if (rc) - return rc; - rc = chip->ops->send(chip, buf, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, "%s: send(): error %d\n", __func__, rc); - goto out_rc; + return rc; } /* A sanity check. send() should just return zero on success e.g. @@ -189,8 +174,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, if (chip->ops->req_canceled(chip, status)) { dev_err(&chip->dev, "Operation Canceled\n"); - rc = -ECANCELED; - goto out_rc; + return -ECANCELED; } tpm_msleep(TPM_TIMEOUT_POLL); @@ -199,8 +183,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, chip->ops->cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); - rc = -ETIME; - goto out_rc; + return -ETIME; out_recv: len = chip->ops->recv(chip, buf, bufsiz); @@ -210,10 +193,6 @@ out_recv: } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; -out_rc: - if (!rc) - rc = tpm2_commit_space(chip, space, buf, &len); - return rc ? rc : len; } diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 92aea758d9c6..b9ada4abbcad 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -39,7 +39,7 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { if (space->session_tbl[i]) tpm2_flush_context_cmd(chip, space->session_tbl[i], - TPM_TRANSMIT_NESTED); + TPM_TRANSMIT_UNLOCKED); } } @@ -84,7 +84,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, tpm_buf_append(&tbuf, &buf[*offset], body_size); rc = tpm_transmit_cmd(chip, NULL, &tbuf, 4, - TPM_TRANSMIT_NESTED, NULL); + TPM_TRANSMIT_UNLOCKED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -133,7 +133,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); rc = tpm_transmit_cmd(chip, NULL, &tbuf, 0, - TPM_TRANSMIT_NESTED, NULL); + TPM_TRANSMIT_UNLOCKED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -170,7 +170,7 @@ void tpm2_flush_space(struct tpm_chip *chip) for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) if (space->context_tbl[i] && ~space->context_tbl[i]) tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_NESTED); + TPM_TRANSMIT_UNLOCKED); tpm2_flush_sessions(chip, space); } @@ -418,7 +418,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, return 0; out_no_slots: - tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED); + tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED); dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, phandle); return -ENOMEM; @@ -506,7 +506,7 @@ static int tpm2_save_space(struct tpm_chip *chip) return rc; tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_NESTED); + TPM_TRANSMIT_UNLOCKED); space->context_tbl[i] = ~0; } From 5faafbab77e37290daa023ba2002e0d611682397 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 3 Nov 2018 15:15:07 +0200 Subject: [PATCH 1647/4277] tpm: remove @space from tpm_transmit() Remove @space from tpm_transmit() API` in order to completely remove the bound between low-level transmission functionality and TPM spaces. The only real dependency existing is the amount of data saved before trying to send a command to the TPM. It doesn't really matter if we save always a bit more than needed so this commit changes the amount saved always to be the size of the TPM header and three handles. Cc: James Bottomley Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-dev-common.c | 2 +- drivers/char/tpm/tpm-interface.c | 25 +++++++++++-------------- drivers/char/tpm/tpm-sysfs.c | 5 ++--- drivers/char/tpm/tpm.h | 10 +++++----- drivers/char/tpm/tpm1-cmd.c | 16 +++++++--------- drivers/char/tpm/tpm2-cmd.c | 30 ++++++++++++++---------------- drivers/char/tpm/tpm2-space.c | 6 ++---- drivers/char/tpm/tpm_vtpm_proxy.c | 2 +- 8 files changed, 43 insertions(+), 53 deletions(-) diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 327d1dca92c8..95fe652b34ff 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -48,7 +48,7 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, if (ret) goto out_lock; - len = tpm_transmit(chip, space, buf, bufsiz, TPM_TRANSMIT_UNLOCKED); + len = tpm_transmit(chip, buf, bufsiz, TPM_TRANSMIT_UNLOCKED); if (len < 0) ret = len; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 138c9c165a9d..5acbef8cf2ce 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -120,8 +120,8 @@ static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) return chip->ops->go_idle(chip); } -static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, - void *buf, size_t bufsiz, unsigned int flags) +static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz, + unsigned int flags) { struct tpm_header *header = buf; int rc; @@ -199,7 +199,6 @@ out_recv: /** * tpm_transmit - Internal kernel interface to transmit TPM commands. * @chip: a TPM chip to use - * @space: a TPM space * @buf: a TPM command buffer * @bufsiz: length of the TPM command buffer * @flags: TPM transmit flags @@ -215,8 +214,8 @@ out_recv: * * The response length - OK * * -errno - A system error */ -ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, - u8 *buf, size_t bufsiz, unsigned int flags) +ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + unsigned int flags) { struct tpm_header *header = (struct tpm_header *)buf; /* space for header and handles */ @@ -225,8 +224,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, bool has_locality = false; u32 rc = 0; ssize_t ret; - const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE, - bufsiz); + const size_t save_size = min(sizeof(save), bufsiz); /* the command code is where the return code will be */ u32 cc = be32_to_cpu(header->return_code); @@ -256,7 +254,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, if (ret) goto out_locality; - ret = tpm_try_transmit(chip, space, buf, bufsiz, flags); + ret = tpm_try_transmit(chip, buf, bufsiz, flags); /* This may fail but do not override ret. */ tpm_go_idle(chip, flags); @@ -302,7 +300,6 @@ out_locality: /** * tpm_transmit_cmd - send a tpm command to the device * @chip: a TPM chip to use - * @space: a TPM space * @buf: a TPM command buffer * @min_rsp_body_length: minimum expected length of response body * @flags: TPM transmit flags @@ -313,15 +310,15 @@ out_locality: * * -errno - A system error * * TPM_RC - A TPM error */ -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - struct tpm_buf *buf, size_t min_rsp_body_length, - unsigned int flags, const char *desc) +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, + size_t min_rsp_body_length, unsigned int flags, + const char *desc) { const struct tpm_header *header = (struct tpm_header *)buf->data; int err; ssize_t len; - len = tpm_transmit(chip, space, buf->data, PAGE_SIZE, flags); + len = tpm_transmit(chip, buf->data, PAGE_SIZE, flags); if (len < 0) return len; @@ -470,7 +467,7 @@ int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) goto out; memcpy(buf.data, cmd, buflen); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to a send a command"); tpm_buf_destroy(&buf); out: diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 928d4e839bb7..03e704f99ed6 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -52,9 +52,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); - rc = tpm_transmit_cmd(chip, NULL, &tpm_buf, - READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, - "attempting to read the PUBEK"); + rc = tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, + 0, "attempting to read the PUBEK"); if (rc) { tpm_buf_destroy(&tpm_buf); return 0; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index e84333259e28..644f1a5c4fdd 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -498,11 +498,11 @@ enum tpm_transmit_flags { TPM_TRANSMIT_NESTED = BIT(1), }; -ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, - u8 *buf, size_t bufsiz, unsigned int flags); -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - struct tpm_buf *buf, size_t min_rsp_body_length, - unsigned int flags, const char *desc); +ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, + unsigned int flags); +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, + size_t min_rsp_body_length, unsigned int flags, + const char *desc); int tpm_get_timeouts(struct tpm_chip *); int tpm_auto_startup(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 10a0b7683b4b..5b5f8bcc6210 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -334,8 +334,7 @@ static int tpm1_startup(struct tpm_chip *chip) tpm_buf_append_u16(&buf, TPM_ST_CLEAR); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, - "attempting to start the TPM"); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); return rc; } @@ -459,7 +458,7 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash, tpm_buf_append_u32(&buf, pcr_idx); tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, NULL, &buf, TPM_DIGEST_SIZE, 0, log_msg); + rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, 0, log_msg); tpm_buf_destroy(&buf); return rc; } @@ -489,7 +488,7 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, tpm_buf_append_u32(&buf, 4); tpm_buf_append_u32(&buf, subcap_id); } - rc = tpm_transmit_cmd(chip, NULL, &buf, min_cap_length, 0, desc); + rc = tpm_transmit_cmd(chip, &buf, min_cap_length, 0, desc); if (!rc) *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4]; tpm_buf_destroy(&buf); @@ -530,8 +529,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_append_u32(&buf, num_bytes); - rc = tpm_transmit_cmd(chip, NULL, &buf, - sizeof(out->rng_data_len), 0, + rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len), 0, "attempting get random"); if (rc) goto out; @@ -576,7 +574,7 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append_u32(&buf, pcr_idx); - rc = tpm_transmit_cmd(chip, NULL, &buf, TPM_DIGEST_SIZE, 0, + rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, 0, "attempting to read a pcr value"); if (rc) goto out; @@ -610,7 +608,7 @@ static int tpm1_continue_selftest(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "continue selftest"); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "continue selftest"); tpm_buf_destroy(&buf); return rc; } @@ -736,7 +734,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr) return rc; /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, NULL); /* * If the TPM indicates that it is too busy to respond to * this command then retry before giving up. It can take diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index ab03f8600f89..f2b0e5c52a57 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -197,7 +197,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append(&buf, (const unsigned char *)pcr_select, sizeof(pcr_select)); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, res_buf ? + rc = tpm_transmit_cmd(chip, &buf, 0, 0, res_buf ? "attempting to read a pcr value" : NULL); if (rc == 0 && res_buf) { out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; @@ -264,7 +264,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, } } - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting extend a PCR value"); tpm_buf_destroy(&buf); @@ -309,7 +309,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM); tpm_buf_append_u16(&buf, num_bytes); - err = tpm_transmit_cmd(chip, NULL, &buf, + err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer), 0, "attempting get random"); @@ -362,7 +362,7 @@ void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, tpm_buf_append_u32(&buf, handle); - tpm_transmit_cmd(chip, NULL, &buf, 0, flags, "flushing context"); + tpm_transmit_cmd(chip, &buf, 0, flags, "flushing context"); tpm_buf_destroy(&buf); } @@ -476,7 +476,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, NULL, &buf, 4, 0, "sealing data"); + rc = tpm_transmit_cmd(chip, &buf, 4, 0, "sealing data"); if (rc) goto out; @@ -558,7 +558,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, NULL, &buf, 4, flags, "loading blob"); + rc = tpm_transmit_cmd(chip, &buf, 4, flags, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -608,7 +608,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, NULL, &buf, 6, flags, "unsealing"); + rc = tpm_transmit_cmd(chip, &buf, 6, flags, "unsealing"); if (rc > 0) rc = -EPERM; @@ -698,7 +698,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, property_id); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, NULL); if (!rc) { out = (struct tpm2_get_cap_out *) &buf.data[TPM_HEADER_SIZE]; @@ -728,7 +728,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) if (rc) return; tpm_buf_append_u16(&buf, shutdown_type); - tpm_transmit_cmd(chip, NULL, &buf, 0, 0, "stopping the TPM"); + tpm_transmit_cmd(chip, &buf, 0, 0, "stopping the TPM"); tpm_buf_destroy(&buf); } @@ -757,7 +757,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip) return rc; tpm_buf_append_u8(&buf, full); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting the self test"); tpm_buf_destroy(&buf); @@ -794,7 +794,7 @@ int tpm2_probe(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, NULL); /* We ignore TPM return codes on purpose. */ if (rc >= 0) { out = (struct tpm_header *)buf.data; @@ -833,8 +833,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) tpm_buf_append_u32(&buf, 0); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, NULL, &buf, 9, 0, - "get tpm pcr allocation"); + rc = tpm_transmit_cmd(chip, &buf, 9, 0, "get tpm pcr allocation"); if (rc) goto out; @@ -905,7 +904,7 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CC_FIRST); tpm_buf_append_u32(&buf, nr_commands); - rc = tpm_transmit_cmd(chip, NULL, &buf, 9 + 4 * nr_commands, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, 0, NULL); if (rc) { tpm_buf_destroy(&buf); goto out; @@ -962,8 +961,7 @@ static int tpm2_startup(struct tpm_chip *chip) return rc; tpm_buf_append_u16(&buf, TPM2_SU_CLEAR); - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, 0, - "attempting to start the TPM"); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); return rc; diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index b9ada4abbcad..08bbd558dbc1 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -83,8 +83,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); tpm_buf_append(&tbuf, &buf[*offset], body_size); - rc = tpm_transmit_cmd(chip, NULL, &tbuf, 4, - TPM_TRANSMIT_UNLOCKED, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 4, TPM_TRANSMIT_UNLOCKED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -132,8 +131,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); - rc = tpm_transmit_cmd(chip, NULL, &tbuf, 0, - TPM_TRANSMIT_UNLOCKED, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 0, TPM_TRANSMIT_UNLOCKED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 986d7e8147b8..8678c4bba38a 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -416,7 +416,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) proxy_dev->state |= STATE_DRIVER_COMMAND; - rc = tpm_transmit_cmd(chip, NULL, &buf, 0, TPM_TRANSMIT_NESTED, + rc = tpm_transmit_cmd(chip, &buf, 0, TPM_TRANSMIT_NESTED, "attempting to set locality"); proxy_dev->state &= ~STATE_DRIVER_COMMAND; From 2677ca98ae377517930c183248221f69f771c921 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 4 Nov 2018 11:38:27 +0200 Subject: [PATCH 1648/4277] tpm: use tpm_try_get_ops() in tpm-sysfs.c. Use tpm_try_get_ops() in tpm-sysfs.c so that we can consider moving other decorations (locking, localities, power management for example) inside it. This direction can be of course taken only after other call sites for tpm_transmit() have been treated in the same way. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-sysfs.c | 123 ++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 03e704f99ed6..3733491671ca 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -39,7 +39,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, { struct tpm_buf tpm_buf; struct tpm_readpubek_out *out; - ssize_t rc; int i; char *str = buf; struct tpm_chip *chip = to_tpm_chip(dev); @@ -47,17 +46,17 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, memset(&anti_replay, 0, sizeof(anti_replay)); - if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK)) + if (tpm_try_get_ops(chip)) return 0; + if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK)) + goto out_ops; + tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); - rc = tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, - 0, "attempting to read the PUBEK"); - if (rc) { - tpm_buf_destroy(&tpm_buf); - return 0; - } + if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, + 0, "attempting to read the PUBEK")) + goto out_buf; out = (struct tpm_readpubek_out *)&tpm_buf.data[10]; str += @@ -88,9 +87,11 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, str += sprintf(str, "\n"); } - rc = str - buf; +out_buf: tpm_buf_destroy(&tpm_buf); - return rc; +out_ops: + tpm_put_ops(chip); + return str - buf; } static DEVICE_ATTR_RO(pubek); @@ -103,10 +104,15 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, char *str = buf; struct tpm_chip *chip = to_tpm_chip(dev); + if (tpm_try_get_ops(chip)) + return 0; + if (tpm1_getcap(chip, TPM_CAP_PROP_PCR, &cap, "attempting to determine the number of PCRS", - sizeof(cap.num_pcrs))) + sizeof(cap.num_pcrs))) { + tpm_put_ops(chip); return 0; + } num_pcrs = be32_to_cpu(cap.num_pcrs); for (i = 0; i < num_pcrs; i++) { @@ -119,6 +125,7 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, str += sprintf(str, "%02X ", digest[j]); str += sprintf(str, "\n"); } + tpm_put_ops(chip); return str - buf; } static DEVICE_ATTR_RO(pcrs); @@ -126,16 +133,21 @@ static DEVICE_ATTR_RO(pcrs); static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent enabled state", - sizeof(cap.perm_flags)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(chip, TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent enabled state", + sizeof(cap.perm_flags))) + goto out_ops; + rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(enabled); @@ -143,16 +155,21 @@ static DEVICE_ATTR_RO(enabled); static ssize_t active_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent active state", - sizeof(cap.perm_flags)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(chip, TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent active state", + sizeof(cap.perm_flags))) + goto out_ops; + rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(active); @@ -160,16 +177,21 @@ static DEVICE_ATTR_RO(active); static ssize_t owned_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, - "attempting to determine the owner state", - sizeof(cap.owned)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, + "attempting to determine the owner state", + sizeof(cap.owned))) + goto out_ops; + rc = sprintf(buf, "%d\n", cap.owned); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(owned); @@ -177,16 +199,21 @@ static DEVICE_ATTR_RO(owned); static ssize_t temp_deactivated_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); + ssize_t rc = 0; cap_t cap; - ssize_t rc; - rc = tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, - "attempting to determine the temporary state", - sizeof(cap.stclear_flags)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + if (tpm1_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, + "attempting to determine the temporary state", + sizeof(cap.stclear_flags))) + goto out_ops; + rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); +out_ops: + tpm_put_ops(chip); return rc; } static DEVICE_ATTR_RO(temp_deactivated); @@ -195,15 +222,18 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tpm_chip *chip = to_tpm_chip(dev); - cap_t cap; - ssize_t rc; + ssize_t rc = 0; char *str = buf; + cap_t cap; - rc = tpm1_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, - "attempting to determine the manufacturer", - sizeof(cap.manufacturer_id)); - if (rc) + if (tpm_try_get_ops(chip)) return 0; + + if (tpm1_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, + "attempting to determine the manufacturer", + sizeof(cap.manufacturer_id))) + goto out_ops; + str += sprintf(str, "Manufacturer: 0x%x\n", be32_to_cpu(cap.manufacturer_id)); @@ -220,11 +250,10 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, cap.tpm_version_1_2.revMinor); } else { /* Otherwise just use TPM_STRUCT_VER */ - rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, - "attempting to determine the 1.1 version", - sizeof(cap.tpm_version)); - if (rc) - return 0; + if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, + "attempting to determine the 1.1 version", + sizeof(cap.tpm_version))) + goto out_ops; str += sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n", cap.tpm_version.Major, @@ -232,8 +261,10 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, cap.tpm_version.revMajor, cap.tpm_version.revMinor); } - - return str - buf; + rc = str - buf; +out_ops: + tpm_put_ops(chip); + return rc; } static DEVICE_ATTR_RO(caps); @@ -241,10 +272,12 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tpm_chip *chip = to_tpm_chip(dev); - if (chip == NULL) + + if (tpm_try_get_ops(chip)) return 0; chip->ops->cancel(chip); + tpm_put_ops(chip); return count; } static DEVICE_ATTR_WO(cancel); From 2f257402ee981720d65080b1e3ce19f693f5c9c3 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 4 Nov 2018 20:01:42 +0200 Subject: [PATCH 1649/4277] tpm: remove TPM_TRANSMIT_UNLOCKED flag Added locking as part of tpm_try_get_ops() and tpm_put_ops() as they are anyway used in most of the call sites except in tpmrm_release() where we take the locks manually. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-chip.c | 2 ++ drivers/char/tpm/tpm-dev-common.c | 9 +++------ drivers/char/tpm/tpm-interface.c | 8 -------- drivers/char/tpm/tpm.h | 8 ++------ drivers/char/tpm/tpm2-cmd.c | 13 ++++--------- drivers/char/tpm/tpm2-space.c | 15 ++++++--------- 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 32db84683c40..157505b0f755 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -58,6 +58,7 @@ int tpm_try_get_ops(struct tpm_chip *chip) if (!chip->ops) goto out_lock; + mutex_lock(&chip->tpm_mutex); return 0; out_lock: up_read(&chip->ops_sem); @@ -75,6 +76,7 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops); */ void tpm_put_ops(struct tpm_chip *chip) { + mutex_unlock(&chip->tpm_mutex); up_read(&chip->ops_sem); put_device(&chip->dev); } diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 95fe652b34ff..435c09ec9056 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -33,7 +33,6 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, struct tpm_header *header = (void *)buf; ssize_t ret, len; - mutex_lock(&chip->tpm_mutex); ret = tpm2_prepare_space(chip, space, buf, bufsiz); /* If the command is not implemented by the TPM, synthesize a * response with a TPM2_RC_COMMAND_CODE return for user-space. @@ -46,18 +45,16 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, ret = sizeof(*header); } if (ret) - goto out_lock; + goto out_rc; - len = tpm_transmit(chip, buf, bufsiz, TPM_TRANSMIT_UNLOCKED); + len = tpm_transmit(chip, buf, bufsiz, 0); if (len < 0) ret = len; if (!ret) ret = tpm2_commit_space(chip, space, buf, &len); -out_lock: - mutex_unlock(&chip->tpm_mutex); - +out_rc: return ret ? ret : len; } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 5acbef8cf2ce..54a15f59e175 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -236,10 +236,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, memcpy(save, buf, save_size); for (;;) { - if (!(flags & TPM_TRANSMIT_UNLOCKED) && - !(flags & TPM_TRANSMIT_NESTED)) - mutex_lock(&chip->tpm_mutex); - if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, true); @@ -266,10 +262,6 @@ out_locality: if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); - if (!(flags & TPM_TRANSMIT_UNLOCKED) && - !(flags & TPM_TRANSMIT_NESTED)) - mutex_unlock(&chip->tpm_mutex); - if (ret < 0) break; rc = be32_to_cpu(header->return_code); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 644f1a5c4fdd..18ef432a3fde 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -488,14 +488,10 @@ extern struct idr dev_nums_idr; /** * enum tpm_transmit_flags - flags for tpm_transmit() * - * @TPM_TRANSMIT_UNLOCKED: do not lock the chip - * @TPM_TRANSMIT_NESTED: discard setup steps (power management, - * locality) including locking (i.e. implicit - * UNLOCKED) + * %TPM_TRANSMIT_NESTED: discard setup steps (power management, locality) */ enum tpm_transmit_flags { - TPM_TRANSMIT_UNLOCKED = BIT(0), - TPM_TRANSMIT_NESTED = BIT(1), + TPM_TRANSMIT_NESTED = BIT(0), }; ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index f2b0e5c52a57..d6abc964ef66 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -652,17 +652,12 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, u32 blob_handle; int rc; - mutex_lock(&chip->tpm_mutex); - rc = tpm2_load_cmd(chip, payload, options, &blob_handle, - TPM_TRANSMIT_UNLOCKED); + rc = tpm2_load_cmd(chip, payload, options, &blob_handle, 0); if (rc) - goto out; + return rc; - rc = tpm2_unseal_cmd(chip, payload, options, blob_handle, - TPM_TRANSMIT_UNLOCKED); - tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED); -out: - mutex_unlock(&chip->tpm_mutex); + rc = tpm2_unseal_cmd(chip, payload, options, blob_handle, 0); + tpm2_flush_context_cmd(chip, blob_handle, 0); return rc; } diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 08bbd558dbc1..8a8754a0f964 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -38,8 +38,7 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { if (space->session_tbl[i]) - tpm2_flush_context_cmd(chip, space->session_tbl[i], - TPM_TRANSMIT_UNLOCKED); + tpm2_flush_context_cmd(chip, space->session_tbl[i], 0); } } @@ -83,7 +82,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); tpm_buf_append(&tbuf, &buf[*offset], body_size); - rc = tpm_transmit_cmd(chip, &tbuf, 4, TPM_TRANSMIT_UNLOCKED, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 4, 0, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -131,7 +130,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); - rc = tpm_transmit_cmd(chip, &tbuf, 0, TPM_TRANSMIT_UNLOCKED, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 0, 0, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -167,8 +166,7 @@ void tpm2_flush_space(struct tpm_chip *chip) for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) if (space->context_tbl[i] && ~space->context_tbl[i]) - tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_UNLOCKED); + tpm2_flush_context_cmd(chip, space->context_tbl[i], 0); tpm2_flush_sessions(chip, space); } @@ -416,7 +414,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, return 0; out_no_slots: - tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED); + tpm2_flush_context_cmd(chip, phandle, 0); dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, phandle); return -ENOMEM; @@ -503,8 +501,7 @@ static int tpm2_save_space(struct tpm_chip *chip) } else if (rc) return rc; - tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_UNLOCKED); + tpm2_flush_context_cmd(chip, space->context_tbl[i], 0); space->context_tbl[i] = ~0; } From 719b7d81f2048477f89f7303e2b4ddec8197e6e3 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 4 Nov 2018 21:18:46 +0200 Subject: [PATCH 1650/4277] tpm: introduce tpm_chip_start() and tpm_chip_stop() Encapsulate power gating and locality functionality to tpm_chip_start() and tpm_chip_stop() in order to clean up the branching mess in tpm_transmit(). Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-chip.c | 111 +++++++++++++++++++++++++++++++ drivers/char/tpm/tpm-interface.c | 84 +---------------------- drivers/char/tpm/tpm.h | 2 + 3 files changed, 116 insertions(+), 81 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 157505b0f755..ed673c7216a0 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -37,6 +37,117 @@ struct class *tpm_class; struct class *tpmrm_class; dev_t tpm_devt; +static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) +{ + int rc; + + if (flags & TPM_TRANSMIT_NESTED) + return 0; + + if (!chip->ops->request_locality) + return 0; + + rc = chip->ops->request_locality(chip, 0); + if (rc < 0) + return rc; + + chip->locality = rc; + return 0; +} + +static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) +{ + int rc; + + if (flags & TPM_TRANSMIT_NESTED) + return; + + if (!chip->ops->relinquish_locality) + return; + + rc = chip->ops->relinquish_locality(chip, chip->locality); + if (rc) + dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); + + chip->locality = -1; +} + +static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) +{ + if (flags & TPM_TRANSMIT_NESTED) + return 0; + + if (!chip->ops->cmd_ready) + return 0; + + return chip->ops->cmd_ready(chip); +} + +static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) +{ + if (flags & TPM_TRANSMIT_NESTED) + return 0; + + if (!chip->ops->go_idle) + return 0; + + return chip->ops->go_idle(chip); +} + +/** + * tpm_chip_start() - power on the TPM + * @chip: a TPM chip to use + * @flags: TPM transmit flags + * + * Return: + * * The response length - OK + * * -errno - A system error + */ +int tpm_chip_start(struct tpm_chip *chip, unsigned int flags) +{ + int ret; + + if (chip->ops->clk_enable) + chip->ops->clk_enable(chip, true); + + if (chip->locality == -1) { + ret = tpm_request_locality(chip, flags); + if (ret) { + chip->ops->clk_enable(chip, false); + return ret; + } + } + + ret = tpm_cmd_ready(chip, flags); + if (ret) { + tpm_relinquish_locality(chip, flags); + if (chip->ops->clk_enable) + chip->ops->clk_enable(chip, false); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(tpm_chip_start); + +/** + * tpm_chip_stop() - power off the TPM + * @chip: a TPM chip to use + * @flags: TPM transmit flags + * + * Return: + * * The response length - OK + * * -errno - A system error + */ +void tpm_chip_stop(struct tpm_chip *chip, unsigned int flags) +{ + tpm_go_idle(chip, flags); + tpm_relinquish_locality(chip, flags); + if (chip->ops->clk_enable) + chip->ops->clk_enable(chip, false); +} +EXPORT_SYMBOL_GPL(tpm_chip_stop); + /** * tpm_try_get_ops() - Get a ref to the tpm_chip * @chip: Chip to ref diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 54a15f59e175..038a4767d9d6 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -62,64 +62,6 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) -{ - int rc; - - if (flags & TPM_TRANSMIT_NESTED) - return 0; - - if (!chip->ops->request_locality) - return 0; - - rc = chip->ops->request_locality(chip, 0); - if (rc < 0) - return rc; - - chip->locality = rc; - - return 0; -} - -static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) -{ - int rc; - - if (flags & TPM_TRANSMIT_NESTED) - return; - - if (!chip->ops->relinquish_locality) - return; - - rc = chip->ops->relinquish_locality(chip, chip->locality); - if (rc) - dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); - - chip->locality = -1; -} - -static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) -{ - if (flags & TPM_TRANSMIT_NESTED) - return 0; - - if (!chip->ops->cmd_ready) - return 0; - - return chip->ops->cmd_ready(chip); -} - -static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) -{ - if (flags & TPM_TRANSMIT_NESTED) - return 0; - - if (!chip->ops->go_idle) - return 0; - - return chip->ops->go_idle(chip); -} - static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz, unsigned int flags) { @@ -221,7 +163,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, /* space for header and handles */ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; unsigned int delay_msec = TPM2_DURATION_SHORT; - bool has_locality = false; u32 rc = 0; ssize_t ret; const size_t save_size = min(sizeof(save), bufsiz); @@ -236,32 +177,13 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, memcpy(save, buf, save_size); for (;;) { - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, true); - - if (chip->locality == -1) { - ret = tpm_request_locality(chip, flags); - if (ret) - goto out_locality; - has_locality = true; - } - - ret = tpm_cmd_ready(chip, flags); + ret = tpm_chip_start(chip, flags); if (ret) - goto out_locality; + return ret; ret = tpm_try_transmit(chip, buf, bufsiz, flags); - /* This may fail but do not override ret. */ - tpm_go_idle(chip, flags); - -out_locality: - if (has_locality) - tpm_relinquish_locality(chip, flags); - - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, false); - + tpm_chip_stop(chip, flags); if (ret < 0) break; rc = be32_to_cpu(header->return_code); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 18ef432a3fde..2d6d934f1c8b 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -523,6 +523,8 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; +int tpm_chip_start(struct tpm_chip *chip, unsigned int flags); +void tpm_chip_stop(struct tpm_chip *chip, unsigned int flags); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); __must_check int tpm_try_get_ops(struct tpm_chip *chip); void tpm_put_ops(struct tpm_chip *chip); From a3fbfae82b4cb3ff9928e29f34c64d0507cad874 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 5 Nov 2018 02:07:56 +0200 Subject: [PATCH 1651/4277] tpm: take TPM chip power gating out of tpm_transmit() Call tpm_chip_start() and tpm_chip_stop() in * tpm_chip_register() * tpm_class_shutdown() * tpm_del_char_device() * tpm_pm_suspend() * tpm_try_get_ops() and tpm_put_ops() * tpm2_del_space() And remove these calls from tpm_transmit(). The core reason for this change is that in tpm_vtpm_proxy a locality change requires a virtual TPM command (a command made up just for that driver). The consequence of this is that this commit removes the remaining nested calls. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-chip.c | 38 ++++++++++++++++++------------- drivers/char/tpm/tpm-interface.c | 18 +++++++-------- drivers/char/tpm/tpm.h | 9 -------- drivers/char/tpm/tpm2-space.c | 5 +++- drivers/char/tpm/tpm_tis_core.c | 4 ++++ drivers/char/tpm/tpm_vtpm_proxy.c | 3 +-- 6 files changed, 40 insertions(+), 37 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index ed673c7216a0..1dbc7f45e2b4 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -41,9 +41,6 @@ static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) { int rc; - if (flags & TPM_TRANSMIT_NESTED) - return 0; - if (!chip->ops->request_locality) return 0; @@ -59,9 +56,6 @@ static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) { int rc; - if (flags & TPM_TRANSMIT_NESTED) - return; - if (!chip->ops->relinquish_locality) return; @@ -74,9 +68,6 @@ static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) { - if (flags & TPM_TRANSMIT_NESTED) - return 0; - if (!chip->ops->cmd_ready) return 0; @@ -85,9 +76,6 @@ static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) { - if (flags & TPM_TRANSMIT_NESTED) - return 0; - if (!chip->ops->go_idle) return 0; @@ -167,11 +155,17 @@ int tpm_try_get_ops(struct tpm_chip *chip) down_read(&chip->ops_sem); if (!chip->ops) - goto out_lock; + goto out_ops; mutex_lock(&chip->tpm_mutex); + rc = tpm_chip_start(chip, 0); + if (rc) + goto out_lock; + return 0; out_lock: + mutex_unlock(&chip->tpm_mutex); +out_ops: up_read(&chip->ops_sem); put_device(&chip->dev); return rc; @@ -187,6 +181,7 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops); */ void tpm_put_ops(struct tpm_chip *chip) { + tpm_chip_stop(chip, 0); mutex_unlock(&chip->tpm_mutex); up_read(&chip->ops_sem); put_device(&chip->dev); @@ -302,7 +297,10 @@ static int tpm_class_shutdown(struct device *dev) if (chip->flags & TPM_CHIP_FLAG_TPM2) { down_write(&chip->ops_sem); - tpm2_shutdown(chip, TPM2_SU_CLEAR); + if (!tpm_chip_start(chip, 0)) { + tpm2_shutdown(chip, TPM2_SU_CLEAR); + tpm_chip_stop(chip, 0); + } chip->ops = NULL; up_write(&chip->ops_sem); } @@ -481,8 +479,12 @@ static void tpm_del_char_device(struct tpm_chip *chip) /* Make the driver uncallable. */ down_write(&chip->ops_sem); - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_shutdown(chip, TPM2_SU_CLEAR); + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + if (!tpm_chip_start(chip, 0)) { + tpm2_shutdown(chip, TPM2_SU_CLEAR); + tpm_chip_stop(chip, 0); + } + } chip->ops = NULL; up_write(&chip->ops_sem); } @@ -564,7 +566,11 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; + rc = tpm_chip_start(chip, 0); + if (rc) + return rc; rc = tpm_auto_startup(chip); + tpm_chip_stop(chip, 0); if (rc) return rc; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 038a4767d9d6..744f1b96128e 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -177,13 +177,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, memcpy(save, buf, save_size); for (;;) { - ret = tpm_chip_start(chip, flags); - if (ret) - return ret; - ret = tpm_try_transmit(chip, buf, bufsiz, flags); - - tpm_chip_stop(chip, flags); if (ret < 0) break; rc = be32_to_cpu(header->return_code); @@ -420,10 +414,16 @@ int tpm_pm_suspend(struct device *dev) if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) return 0; - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_shutdown(chip, TPM2_SU_STATE); - else + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + mutex_lock(&chip->tpm_mutex); + if (!tpm_chip_start(chip, 0)) { + tpm2_shutdown(chip, TPM2_SU_STATE); + tpm_chip_stop(chip, 0); + } + mutex_unlock(&chip->tpm_mutex); + } else { rc = tpm1_pm_suspend(chip, tpm_suspend_pcr); + } return rc; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 2d6d934f1c8b..53e4208759ee 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -485,15 +485,6 @@ extern const struct file_operations tpm_fops; extern const struct file_operations tpmrm_fops; extern struct idr dev_nums_idr; -/** - * enum tpm_transmit_flags - flags for tpm_transmit() - * - * %TPM_TRANSMIT_NESTED: discard setup steps (power management, locality) - */ -enum tpm_transmit_flags { - TPM_TRANSMIT_NESTED = BIT(0), -}; - ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, unsigned int flags); ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 8a8754a0f964..6cc7bb442162 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -60,7 +60,10 @@ int tpm2_init_space(struct tpm_space *space) void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) { mutex_lock(&chip->tpm_mutex); - tpm2_flush_sessions(chip, space); + if (!tpm_chip_start(chip, 0)) { + tpm2_flush_sessions(chip, space); + tpm_chip_stop(chip, 0); + } mutex_unlock(&chip->tpm_mutex); kfree(space->context_buf); kfree(space->session_buf); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index c6b0c6d541a5..e33a10491f07 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -916,7 +916,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, intmask &= ~TPM_GLOBAL_INT_ENABLE; tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); + rc = tpm_chip_start(chip, 0); + if (rc) + goto out_err; rc = tpm2_probe(chip); + tpm_chip_stop(chip, 0); if (rc) goto out_err; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 8678c4bba38a..9aa6a3baa491 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -416,8 +416,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) proxy_dev->state |= STATE_DRIVER_COMMAND; - rc = tpm_transmit_cmd(chip, &buf, 0, TPM_TRANSMIT_NESTED, - "attempting to set locality"); + rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to set locality"); proxy_dev->state &= ~STATE_DRIVER_COMMAND; From 47a6c28b6861b14a69e36ba974d2ffa1746e8e6f Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 5 Nov 2018 03:02:38 +0200 Subject: [PATCH 1652/4277] tpm: remove @flags from tpm_transmit() Remove @flags from tpm_transmit() API. It is no longer used for anything. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jerry Snitselaar Reviewed-by: James Bottomley Tested-by: Alexander Steffen --- drivers/char/tpm/tpm-chip.c | 40 ++++++++++++-------------- drivers/char/tpm/tpm-dev-common.c | 2 +- drivers/char/tpm/tpm-interface.c | 22 ++++++-------- drivers/char/tpm/tpm-sysfs.c | 2 +- drivers/char/tpm/tpm.h | 13 ++++----- drivers/char/tpm/tpm1-cmd.c | 14 ++++----- drivers/char/tpm/tpm2-cmd.c | 48 ++++++++++++++----------------- drivers/char/tpm/tpm2-space.c | 16 +++++------ drivers/char/tpm/tpm_tis_core.c | 4 +-- drivers/char/tpm/tpm_vtpm_proxy.c | 2 +- 10 files changed, 73 insertions(+), 90 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 1dbc7f45e2b4..4eb48cf6a03a 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -37,7 +37,7 @@ struct class *tpm_class; struct class *tpmrm_class; dev_t tpm_devt; -static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) +static int tpm_request_locality(struct tpm_chip *chip) { int rc; @@ -52,7 +52,7 @@ static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) return 0; } -static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) +static void tpm_relinquish_locality(struct tpm_chip *chip) { int rc; @@ -66,7 +66,7 @@ static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) chip->locality = -1; } -static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) +static int tpm_cmd_ready(struct tpm_chip *chip) { if (!chip->ops->cmd_ready) return 0; @@ -74,7 +74,7 @@ static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) return chip->ops->cmd_ready(chip); } -static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) +static int tpm_go_idle(struct tpm_chip *chip) { if (!chip->ops->go_idle) return 0; @@ -85,13 +85,12 @@ static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) /** * tpm_chip_start() - power on the TPM * @chip: a TPM chip to use - * @flags: TPM transmit flags * * Return: * * The response length - OK * * -errno - A system error */ -int tpm_chip_start(struct tpm_chip *chip, unsigned int flags) +int tpm_chip_start(struct tpm_chip *chip) { int ret; @@ -99,16 +98,16 @@ int tpm_chip_start(struct tpm_chip *chip, unsigned int flags) chip->ops->clk_enable(chip, true); if (chip->locality == -1) { - ret = tpm_request_locality(chip, flags); + ret = tpm_request_locality(chip); if (ret) { chip->ops->clk_enable(chip, false); return ret; } } - ret = tpm_cmd_ready(chip, flags); + ret = tpm_cmd_ready(chip); if (ret) { - tpm_relinquish_locality(chip, flags); + tpm_relinquish_locality(chip); if (chip->ops->clk_enable) chip->ops->clk_enable(chip, false); return ret; @@ -121,16 +120,15 @@ EXPORT_SYMBOL_GPL(tpm_chip_start); /** * tpm_chip_stop() - power off the TPM * @chip: a TPM chip to use - * @flags: TPM transmit flags * * Return: * * The response length - OK * * -errno - A system error */ -void tpm_chip_stop(struct tpm_chip *chip, unsigned int flags) +void tpm_chip_stop(struct tpm_chip *chip) { - tpm_go_idle(chip, flags); - tpm_relinquish_locality(chip, flags); + tpm_go_idle(chip); + tpm_relinquish_locality(chip); if (chip->ops->clk_enable) chip->ops->clk_enable(chip, false); } @@ -158,7 +156,7 @@ int tpm_try_get_ops(struct tpm_chip *chip) goto out_ops; mutex_lock(&chip->tpm_mutex); - rc = tpm_chip_start(chip, 0); + rc = tpm_chip_start(chip); if (rc) goto out_lock; @@ -181,7 +179,7 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops); */ void tpm_put_ops(struct tpm_chip *chip) { - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); mutex_unlock(&chip->tpm_mutex); up_read(&chip->ops_sem); put_device(&chip->dev); @@ -297,9 +295,9 @@ static int tpm_class_shutdown(struct device *dev) if (chip->flags & TPM_CHIP_FLAG_TPM2) { down_write(&chip->ops_sem); - if (!tpm_chip_start(chip, 0)) { + if (!tpm_chip_start(chip)) { tpm2_shutdown(chip, TPM2_SU_CLEAR); - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); } chip->ops = NULL; up_write(&chip->ops_sem); @@ -480,9 +478,9 @@ static void tpm_del_char_device(struct tpm_chip *chip) /* Make the driver uncallable. */ down_write(&chip->ops_sem); if (chip->flags & TPM_CHIP_FLAG_TPM2) { - if (!tpm_chip_start(chip, 0)) { + if (!tpm_chip_start(chip)) { tpm2_shutdown(chip, TPM2_SU_CLEAR); - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); } } chip->ops = NULL; @@ -566,11 +564,11 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; - rc = tpm_chip_start(chip, 0); + rc = tpm_chip_start(chip); if (rc) return rc; rc = tpm_auto_startup(chip); - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); if (rc) return rc; diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 435c09ec9056..8856cce5a23b 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -47,7 +47,7 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, if (ret) goto out_rc; - len = tpm_transmit(chip, buf, bufsiz, 0); + len = tpm_transmit(chip, buf, bufsiz); if (len < 0) ret = len; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 744f1b96128e..498809bf1bf0 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -62,8 +62,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); -static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz, - unsigned int flags) +static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) { struct tpm_header *header = buf; int rc; @@ -143,7 +142,6 @@ out_recv: * @chip: a TPM chip to use * @buf: a TPM command buffer * @bufsiz: length of the TPM command buffer - * @flags: TPM transmit flags * * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from * the TPM and retransmits the command after a delay up to a maximum wait of @@ -156,8 +154,7 @@ out_recv: * * The response length - OK * * -errno - A system error */ -ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, - unsigned int flags) +ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz) { struct tpm_header *header = (struct tpm_header *)buf; /* space for header and handles */ @@ -177,7 +174,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, memcpy(save, buf, save_size); for (;;) { - ret = tpm_try_transmit(chip, buf, bufsiz, flags); + ret = tpm_try_transmit(chip, buf, bufsiz); if (ret < 0) break; rc = be32_to_cpu(header->return_code); @@ -210,7 +207,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, * @chip: a TPM chip to use * @buf: a TPM command buffer * @min_rsp_body_length: minimum expected length of response body - * @flags: TPM transmit flags * @desc: command description used in the error message * * Return: @@ -219,14 +215,13 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, * * TPM_RC - A TPM error */ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, - size_t min_rsp_body_length, unsigned int flags, - const char *desc) + size_t min_rsp_body_length, const char *desc) { const struct tpm_header *header = (struct tpm_header *)buf->data; int err; ssize_t len; - len = tpm_transmit(chip, buf->data, PAGE_SIZE, flags); + len = tpm_transmit(chip, buf->data, PAGE_SIZE); if (len < 0) return len; @@ -375,8 +370,7 @@ int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) goto out; memcpy(buf.data, cmd, buflen); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, - "attempting to a send a command"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command"); tpm_buf_destroy(&buf); out: tpm_put_ops(chip); @@ -416,9 +410,9 @@ int tpm_pm_suspend(struct device *dev) if (chip->flags & TPM_CHIP_FLAG_TPM2) { mutex_lock(&chip->tpm_mutex); - if (!tpm_chip_start(chip, 0)) { + if (!tpm_chip_start(chip)) { tpm2_shutdown(chip, TPM2_SU_STATE); - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); } mutex_unlock(&chip->tpm_mutex); } else { diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 3733491671ca..533a260d744e 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -55,7 +55,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, - 0, "attempting to read the PUBEK")) + "attempting to read the PUBEK")) goto out_buf; out = (struct tpm_readpubek_out *)&tpm_buf.data[10]; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 53e4208759ee..183e2b93e0fe 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -485,11 +485,9 @@ extern const struct file_operations tpm_fops; extern const struct file_operations tpmrm_fops; extern struct idr dev_nums_idr; -ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz, - unsigned int flags); +ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz); ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, - size_t min_rsp_body_length, unsigned int flags, - const char *desc); + size_t min_rsp_body_length, const char *desc); int tpm_get_timeouts(struct tpm_chip *); int tpm_auto_startup(struct tpm_chip *chip); @@ -514,8 +512,8 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; -int tpm_chip_start(struct tpm_chip *chip, unsigned int flags); -void tpm_chip_stop(struct tpm_chip *chip, unsigned int flags); +int tpm_chip_start(struct tpm_chip *chip); +void tpm_chip_stop(struct tpm_chip *chip); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); __must_check int tpm_try_get_ops(struct tpm_chip *chip); void tpm_put_ops(struct tpm_chip *chip); @@ -548,8 +546,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf); int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, struct tpm2_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); -void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, - unsigned int flags); +void tpm2_flush_context(struct tpm_chip *chip, u32 handle); int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 5b5f8bcc6210..ec5f3693c096 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -334,7 +334,7 @@ static int tpm1_startup(struct tpm_chip *chip) tpm_buf_append_u16(&buf, TPM_ST_CLEAR); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to start the TPM"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); return rc; } @@ -458,7 +458,7 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash, tpm_buf_append_u32(&buf, pcr_idx); tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, 0, log_msg); + rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg); tpm_buf_destroy(&buf); return rc; } @@ -488,7 +488,7 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, tpm_buf_append_u32(&buf, 4); tpm_buf_append_u32(&buf, subcap_id); } - rc = tpm_transmit_cmd(chip, &buf, min_cap_length, 0, desc); + rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc); if (!rc) *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4]; tpm_buf_destroy(&buf); @@ -529,7 +529,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) do { tpm_buf_append_u32(&buf, num_bytes); - rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len), 0, + rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len), "attempting get random"); if (rc) goto out; @@ -574,7 +574,7 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append_u32(&buf, pcr_idx); - rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, 0, + rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, "attempting to read a pcr value"); if (rc) goto out; @@ -608,7 +608,7 @@ static int tpm1_continue_selftest(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_transmit_cmd(chip, &buf, 0, 0, "continue selftest"); + rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest"); tpm_buf_destroy(&buf); return rc; } @@ -734,7 +734,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr) return rc; /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { - rc = tpm_transmit_cmd(chip, &buf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, NULL); /* * If the TPM indicates that it is too busy to respond to * this command then retry before giving up. It can take diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index d6abc964ef66..971d46efaca5 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -197,7 +197,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) tpm_buf_append(&buf, (const unsigned char *)pcr_select, sizeof(pcr_select)); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, res_buf ? + rc = tpm_transmit_cmd(chip, &buf, 0, res_buf ? "attempting to read a pcr value" : NULL); if (rc == 0 && res_buf) { out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; @@ -264,8 +264,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, } } - rc = tpm_transmit_cmd(chip, &buf, 0, 0, - "attempting extend a PCR value"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value"); tpm_buf_destroy(&buf); @@ -312,7 +311,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer), - 0, "attempting get random"); + "attempting get random"); if (err) goto out; @@ -341,14 +340,11 @@ out: } /** - * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command + * tpm2_flush_context() - execute a TPM2_FlushContext command * @chip: TPM chip to use * @handle: context handle - * @flags: tpm transmit flags - bitmap - * */ -void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, - unsigned int flags) +void tpm2_flush_context(struct tpm_chip *chip, u32 handle) { struct tpm_buf buf; int rc; @@ -362,7 +358,7 @@ void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, tpm_buf_append_u32(&buf, handle); - tpm_transmit_cmd(chip, &buf, 0, flags, "flushing context"); + tpm_transmit_cmd(chip, &buf, 0, "flushing context"); tpm_buf_destroy(&buf); } @@ -476,7 +472,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, &buf, 4, 0, "sealing data"); + rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); if (rc) goto out; @@ -513,7 +509,6 @@ out: * @payload: the key data in clear and encrypted form * @options: authentication values and other options * @blob_handle: returned blob handle - * @flags: tpm transmit flags * * Return: 0 on success. * -E2BIG on wrong payload size. @@ -523,7 +518,7 @@ out: static int tpm2_load_cmd(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options, - u32 *blob_handle, unsigned int flags) + u32 *blob_handle) { struct tpm_buf buf; unsigned int private_len; @@ -558,7 +553,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, &buf, 4, flags, "loading blob"); + rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -579,7 +574,6 @@ out: * @payload: the key data in clear and encrypted form * @options: authentication values and other options * @blob_handle: blob handle - * @flags: tpm_transmit_cmd flags * * Return: 0 on success * -EPERM on tpm error status @@ -588,7 +582,7 @@ out: static int tpm2_unseal_cmd(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options, - u32 blob_handle, unsigned int flags) + u32 blob_handle) { struct tpm_buf buf; u16 data_len; @@ -608,7 +602,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &buf, 6, flags, "unsealing"); + rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); if (rc > 0) rc = -EPERM; @@ -652,12 +646,12 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, u32 blob_handle; int rc; - rc = tpm2_load_cmd(chip, payload, options, &blob_handle, 0); + rc = tpm2_load_cmd(chip, payload, options, &blob_handle); if (rc) return rc; - rc = tpm2_unseal_cmd(chip, payload, options, blob_handle, 0); - tpm2_flush_context_cmd(chip, blob_handle, 0); + rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); + tpm2_flush_context(chip, blob_handle); return rc; } @@ -693,7 +687,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, property_id); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, NULL); if (!rc) { out = (struct tpm2_get_cap_out *) &buf.data[TPM_HEADER_SIZE]; @@ -723,7 +717,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) if (rc) return; tpm_buf_append_u16(&buf, shutdown_type); - tpm_transmit_cmd(chip, &buf, 0, 0, "stopping the TPM"); + tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM"); tpm_buf_destroy(&buf); } @@ -752,7 +746,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip) return rc; tpm_buf_append_u8(&buf, full); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting the self test"); tpm_buf_destroy(&buf); @@ -789,7 +783,7 @@ int tpm2_probe(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 0, NULL); /* We ignore TPM return codes on purpose. */ if (rc >= 0) { out = (struct tpm_header *)buf.data; @@ -828,7 +822,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) tpm_buf_append_u32(&buf, 0); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, &buf, 9, 0, "get tpm pcr allocation"); + rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation"); if (rc) goto out; @@ -899,7 +893,7 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) tpm_buf_append_u32(&buf, TPM2_CC_FIRST); tpm_buf_append_u32(&buf, nr_commands); - rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, 0, NULL); + rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL); if (rc) { tpm_buf_destroy(&buf); goto out; @@ -956,7 +950,7 @@ static int tpm2_startup(struct tpm_chip *chip) return rc; tpm_buf_append_u16(&buf, TPM2_SU_CLEAR); - rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to start the TPM"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM"); tpm_buf_destroy(&buf); return rc; diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 6cc7bb442162..4a2773c3374f 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -38,7 +38,7 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { if (space->session_tbl[i]) - tpm2_flush_context_cmd(chip, space->session_tbl[i], 0); + tpm2_flush_context(chip, space->session_tbl[i]); } } @@ -60,9 +60,9 @@ int tpm2_init_space(struct tpm_space *space) void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) { mutex_lock(&chip->tpm_mutex); - if (!tpm_chip_start(chip, 0)) { + if (!tpm_chip_start(chip)) { tpm2_flush_sessions(chip, space); - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); } mutex_unlock(&chip->tpm_mutex); kfree(space->context_buf); @@ -85,7 +85,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); tpm_buf_append(&tbuf, &buf[*offset], body_size); - rc = tpm_transmit_cmd(chip, &tbuf, 4, 0, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -133,7 +133,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); - rc = tpm_transmit_cmd(chip, &tbuf, 0, 0, NULL); + rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -169,7 +169,7 @@ void tpm2_flush_space(struct tpm_chip *chip) for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) if (space->context_tbl[i] && ~space->context_tbl[i]) - tpm2_flush_context_cmd(chip, space->context_tbl[i], 0); + tpm2_flush_context(chip, space->context_tbl[i]); tpm2_flush_sessions(chip, space); } @@ -417,7 +417,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, return 0; out_no_slots: - tpm2_flush_context_cmd(chip, phandle, 0); + tpm2_flush_context(chip, phandle); dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, phandle); return -ENOMEM; @@ -504,7 +504,7 @@ static int tpm2_save_space(struct tpm_chip *chip) } else if (rc) return rc; - tpm2_flush_context_cmd(chip, space->context_tbl[i], 0); + tpm2_flush_context(chip, space->context_tbl[i]); space->context_tbl[i] = ~0; } diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index e33a10491f07..b9f64684c3fb 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -916,11 +916,11 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, intmask &= ~TPM_GLOBAL_INT_ENABLE; tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); - rc = tpm_chip_start(chip, 0); + rc = tpm_chip_start(chip); if (rc) goto out_err; rc = tpm2_probe(chip); - tpm_chip_stop(chip, 0); + tpm_chip_stop(chip); if (rc) goto out_err; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 9aa6a3baa491..d74f3de74ae6 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -416,7 +416,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) proxy_dev->state |= STATE_DRIVER_COMMAND; - rc = tpm_transmit_cmd(chip, &buf, 0, 0, "attempting to set locality"); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality"); proxy_dev->state &= ~STATE_DRIVER_COMMAND; From bcfff8384f6c4e6627676ef07ccad9cfacd67849 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 6 Feb 2019 17:24:47 +0100 Subject: [PATCH 1653/4277] tpm: dynamically allocate the allocated_banks array This patch renames active_banks (member of tpm_chip) to allocated_banks, stores the number of allocated PCR banks in nr_allocated_banks (new member of tpm_chip), and replaces the static array with a pointer to a dynamically allocated array. tpm2_get_pcr_allocation() determines if a PCR bank is allocated by checking the mask in the TPML_PCR_SELECTION structure returned by the TPM for TPM2_Get_Capability(). If a bank is not allocated, the TPM returns that bank in TPML_PCR_SELECTION, with all bits in the mask set to zero. In this case, the bank is not included in chip->allocated_banks, to avoid that TPM driver users unnecessarily calculate a digest for that bank. One PCR bank with algorithm set to SHA1 is always allocated for TPM 1.x. As a consequence of the introduction of nr_allocated_banks, tpm_pcr_extend() does not check anymore if the algorithm stored in tpm_chip is equal to zero. Signed-off-by: Roberto Sassu Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-chip.c | 1 + drivers/char/tpm/tpm-interface.c | 18 ++++++++++-------- drivers/char/tpm/tpm.h | 3 ++- drivers/char/tpm/tpm1-cmd.c | 10 ++++++++++ drivers/char/tpm/tpm2-cmd.c | 31 +++++++++++++++++++++---------- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 4eb48cf6a03a..8804c9e916fd 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -266,6 +266,7 @@ static void tpm_dev_release(struct device *dev) kfree(chip->log.bios_event_log); kfree(chip->work_space.context_buf); kfree(chip->work_space.session_buf); + kfree(chip->allocated_banks); kfree(chip); } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 498809bf1bf0..2b31eff06b0e 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -318,8 +318,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) { int rc; - struct tpm2_digest digest_list[ARRAY_SIZE(chip->active_banks)]; - u32 count = 0; + struct tpm2_digest *digest_list; int i; chip = tpm_find_get_ops(chip); @@ -327,16 +326,19 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) { - memset(digest_list, 0, sizeof(digest_list)); + digest_list = kcalloc(chip->nr_allocated_banks, + sizeof(*digest_list), GFP_KERNEL); + if (!digest_list) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(chip->active_banks) && - chip->active_banks[i] != TPM2_ALG_ERROR; i++) { - digest_list[i].alg_id = chip->active_banks[i]; + for (i = 0; i < chip->nr_allocated_banks; i++) { + digest_list[i].alg_id = chip->allocated_banks[i]; memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE); - count++; } - rc = tpm2_pcr_extend(chip, pcr_idx, count, digest_list); + rc = tpm2_pcr_extend(chip, pcr_idx, chip->nr_allocated_banks, + digest_list); + kfree(digest_list); tpm_put_ops(chip); return rc; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 183e2b93e0fe..cd330ace6248 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -257,7 +257,8 @@ struct tpm_chip { const struct attribute_group *groups[3]; unsigned int groups_cnt; - u16 active_banks[7]; + u32 nr_allocated_banks; + u16 *allocated_banks; #ifdef CONFIG_ACPI acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index ec5f3693c096..e7d3228a0f37 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -696,6 +696,16 @@ int tpm1_auto_startup(struct tpm_chip *chip) goto out; } + chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks), + GFP_KERNEL); + if (!chip->allocated_banks) { + rc = -ENOMEM; + goto out; + } + + chip->allocated_banks[0] = TPM2_ALG_SHA1; + chip->nr_allocated_banks = 1; + return rc; out: if (rc > 0) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 971d46efaca5..bd20b9a61fc0 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -234,7 +234,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, int i; int j; - if (count > ARRAY_SIZE(chip->active_banks)) + if (count > chip->nr_allocated_banks) return -EINVAL; rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND); @@ -808,8 +808,10 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) void *marker; void *end; void *pcr_select_offset; - unsigned int count; u32 sizeof_pcr_selection; + u32 nr_possible_banks; + u32 nr_alloc_banks = 0; + u16 hash_alg; u32 rsp_len; int rc; int i = 0; @@ -826,11 +828,14 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) if (rc) goto out; - count = be32_to_cpup( + nr_possible_banks = be32_to_cpup( (__be32 *)&buf.data[TPM_HEADER_SIZE + 5]); - if (count > ARRAY_SIZE(chip->active_banks)) { - rc = -ENODEV; + chip->allocated_banks = kcalloc(nr_possible_banks, + sizeof(*chip->allocated_banks), + GFP_KERNEL); + if (!chip->allocated_banks) { + rc = -ENOMEM; goto out; } @@ -839,7 +844,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) rsp_len = be32_to_cpup((__be32 *)&buf.data[2]); end = &buf.data[rsp_len]; - for (i = 0; i < count; i++) { + for (i = 0; i < nr_possible_banks; i++) { pcr_select_offset = marker + offsetof(struct tpm2_pcr_selection, size_of_select); if (pcr_select_offset >= end) { @@ -848,17 +853,23 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) } memcpy(&pcr_selection, marker, sizeof(pcr_selection)); - chip->active_banks[i] = be16_to_cpu(pcr_selection.hash_alg); + hash_alg = be16_to_cpu(pcr_selection.hash_alg); + + pcr_select_offset = memchr_inv(pcr_selection.pcr_select, 0, + pcr_selection.size_of_select); + if (pcr_select_offset) { + chip->allocated_banks[nr_alloc_banks] = hash_alg; + nr_alloc_banks++; + } + sizeof_pcr_selection = sizeof(pcr_selection.hash_alg) + sizeof(pcr_selection.size_of_select) + pcr_selection.size_of_select; marker = marker + sizeof_pcr_selection; } + chip->nr_allocated_banks = nr_alloc_banks; out: - if (i < ARRAY_SIZE(chip->active_banks)) - chip->active_banks[i] = TPM2_ALG_ERROR; - tpm_buf_destroy(&buf); return rc; From aa042475938f5818b0c1b6203061e85ad2535dbc Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 6 Feb 2019 17:24:48 +0100 Subject: [PATCH 1654/4277] tpm: rename and export tpm2_digest and tpm2_algorithms Rename tpm2_* to tpm_* and move the definitions to include/linux/tpm.h so that these can be used by other kernel subsystems (e.g. IMA). Also, set the length of the digest array in tpm_digest to a new constant named TPM_MAX_DIGEST_SIZE, equal to SHA512_DIGEST_SIZE. Signed-off-by: Roberto Sassu Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Acked-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-interface.c | 2 +- drivers/char/tpm/tpm.h | 13 +------------ drivers/char/tpm/tpm1-cmd.c | 2 +- drivers/char/tpm/tpm2-cmd.c | 18 +++++++++--------- include/linux/tpm.h | 19 +++++++++++++++++++ include/linux/tpm_eventlog.h | 9 ++------- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 2b31eff06b0e..9c6aa77b5dee 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -318,7 +318,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) { int rc; - struct tpm2_digest *digest_list; + struct tpm_digest *digest_list; int i; chip = tpm_find_get_ops(chip); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index cd330ace6248..0e54061d3fd1 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -122,17 +122,6 @@ enum tpm2_return_codes { TPM2_RC_RETRY = 0x0922, }; -enum tpm2_algorithms { - TPM2_ALG_ERROR = 0x0000, - TPM2_ALG_SHA1 = 0x0004, - TPM2_ALG_KEYEDHASH = 0x0008, - TPM2_ALG_SHA256 = 0x000B, - TPM2_ALG_SHA384 = 0x000C, - TPM2_ALG_SHA512 = 0x000D, - TPM2_ALG_NULL = 0x0010, - TPM2_ALG_SM3_256 = 0x0012, -}; - enum tpm2_command_codes { TPM2_CC_FIRST = 0x011F, TPM2_CC_HIERARCHY_CONTROL = 0x0121, @@ -545,7 +534,7 @@ static inline u32 tpm2_rc_value(u32 rc) int tpm2_get_timeouts(struct tpm_chip *chip); int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf); int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, - struct tpm2_digest *digests); + struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); void tpm2_flush_context(struct tpm_chip *chip, u32 handle); int tpm2_seal_trusted(struct tpm_chip *chip, diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index e7d3228a0f37..3eb7e03889a0 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -703,7 +703,7 @@ int tpm1_auto_startup(struct tpm_chip *chip) goto out; } - chip->allocated_banks[0] = TPM2_ALG_SHA1; + chip->allocated_banks[0] = TPM_ALG_SHA1; chip->nr_allocated_banks = 1; return rc; diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index bd20b9a61fc0..440ae6ee29e4 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -33,11 +33,11 @@ struct tpm2_hash { }; static struct tpm2_hash tpm2_hash_map[] = { - {HASH_ALGO_SHA1, TPM2_ALG_SHA1}, - {HASH_ALGO_SHA256, TPM2_ALG_SHA256}, - {HASH_ALGO_SHA384, TPM2_ALG_SHA384}, - {HASH_ALGO_SHA512, TPM2_ALG_SHA512}, - {HASH_ALGO_SM3_256, TPM2_ALG_SM3_256}, + {HASH_ALGO_SHA1, TPM_ALG_SHA1}, + {HASH_ALGO_SHA256, TPM_ALG_SHA256}, + {HASH_ALGO_SHA384, TPM_ALG_SHA384}, + {HASH_ALGO_SHA512, TPM_ALG_SHA512}, + {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, }; int tpm2_get_timeouts(struct tpm_chip *chip) @@ -192,7 +192,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); tpm_buf_append_u32(&buf, 1); - tpm_buf_append_u16(&buf, TPM2_ALG_SHA1); + tpm_buf_append_u16(&buf, TPM_ALG_SHA1); tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN); tpm_buf_append(&buf, (const unsigned char *)pcr_select, sizeof(pcr_select)); @@ -226,7 +226,7 @@ struct tpm2_null_auth_area { * Return: Same as with tpm_transmit_cmd. */ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, - struct tpm2_digest *digests) + struct tpm_digest *digests) { struct tpm_buf buf; struct tpm2_null_auth_area auth_area; @@ -443,7 +443,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, /* public */ tpm_buf_append_u16(&buf, 14 + options->policydigest_len); - tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); + tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); tpm_buf_append_u16(&buf, hash); /* policy */ @@ -458,7 +458,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, } /* public parameters */ - tpm_buf_append_u16(&buf, TPM2_ALG_NULL); + tpm_buf_append_u16(&buf, TPM_ALG_NULL); tpm_buf_append_u16(&buf, 0); /* outside info */ diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 13563b8c0c3a..9fe8c9816cf0 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -22,12 +22,31 @@ #ifndef __LINUX_TPM_H__ #define __LINUX_TPM_H__ +#include + #define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */ +#define TPM_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE struct tpm_chip; struct trusted_key_payload; struct trusted_key_options; +enum tpm_algorithms { + TPM_ALG_ERROR = 0x0000, + TPM_ALG_SHA1 = 0x0004, + TPM_ALG_KEYEDHASH = 0x0008, + TPM_ALG_SHA256 = 0x000B, + TPM_ALG_SHA384 = 0x000C, + TPM_ALG_SHA512 = 0x000D, + TPM_ALG_NULL = 0x0010, + TPM_ALG_SM3_256 = 0x0012, +}; + +struct tpm_digest { + u16 alg_id; + u8 digest[TPM_MAX_DIGEST_SIZE]; +} __packed; + enum TPM_OPS_FLAGS { TPM_OPS_AUTO_STARTUP = BIT(0), }; diff --git a/include/linux/tpm_eventlog.h b/include/linux/tpm_eventlog.h index f47342361e87..81519f163211 100644 --- a/include/linux/tpm_eventlog.h +++ b/include/linux/tpm_eventlog.h @@ -3,7 +3,7 @@ #ifndef __LINUX_TPM_EVENTLOG_H__ #define __LINUX_TPM_EVENTLOG_H__ -#include +#include #define TCG_EVENT_NAME_LEN_MAX 255 #define MAX_TEXT_EVENT 1000 /* Max event string length */ @@ -105,16 +105,11 @@ struct tcg_event_field { u8 event[0]; } __packed; -struct tpm2_digest { - u16 alg_id; - u8 digest[SHA512_DIGEST_SIZE]; -} __packed; - struct tcg_pcr_event2_head { u32 pcr_idx; u32 event_type; u32 count; - struct tpm2_digest digests[]; + struct tpm_digest digests[]; } __packed; #endif From 879b589210a9a0c9f77d301aaf0ddee20f2c5052 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 6 Feb 2019 17:24:49 +0100 Subject: [PATCH 1655/4277] tpm: retrieve digest size of unknown algorithms with PCR read Currently, the TPM driver retrieves the digest size from a table mapping TPM algorithms identifiers to identifiers defined by the crypto subsystem. If the algorithm is not defined by the latter, the digest size can be retrieved from the output of the PCR read command. The patch modifies the definition of tpm_pcr_read() and tpm2_pcr_read() to pass the desired hash algorithm and obtain the digest size at TPM startup. Algorithms and corresponding digest sizes are stored in the new structure tpm_bank_info, member of tpm_chip, so that the information can be used by other kernel subsystems. tpm_bank_info contains: the TPM algorithm identifier, necessary to generate the event log as defined by Trusted Computing Group (TCG); the digest size, to pad/truncate a digest calculated with a different algorithm; the crypto subsystem identifier, to calculate the digest of event data. This patch also protects against data corruption that could happen in the bus, by checking that the digest size returned by the TPM during a PCR read matches the size of the algorithm passed to tpm2_pcr_read(). For the initial PCR read, when digest sizes are not yet available, this patch ensures that the amount of data copied from the output returned by the TPM does not exceed the size of the array data are copied to. Signed-off-by: Roberto Sassu Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Acked-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-interface.c | 16 +++--- drivers/char/tpm/tpm.h | 5 +- drivers/char/tpm/tpm1-cmd.c | 4 +- drivers/char/tpm/tpm2-cmd.c | 85 +++++++++++++++++++++++------ include/linux/tpm.h | 12 +++- security/integrity/ima/ima_crypto.c | 10 ++-- 6 files changed, 96 insertions(+), 36 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 9c6aa77b5dee..1c92dbeef736 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -281,11 +281,12 @@ EXPORT_SYMBOL_GPL(tpm_is_tpm2); * tpm_pcr_read - read a PCR value from SHA1 bank * @chip: a &struct tpm_chip instance, %NULL for the default chip * @pcr_idx: the PCR to be retrieved - * @res_buf: the value of the PCR + * @digest: the PCR bank and buffer current PCR value is written to * * Return: same as with tpm_transmit_cmd() */ -int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) +int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest) { int rc; @@ -294,9 +295,9 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) - rc = tpm2_pcr_read(chip, pcr_idx, res_buf); + rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL); else - rc = tpm1_pcr_read(chip, pcr_idx, res_buf); + rc = tpm1_pcr_read(chip, pcr_idx, digest->digest); tpm_put_ops(chip); return rc; @@ -309,9 +310,8 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * @pcr_idx: the PCR to be retrieved * @hash: the hash value used to extend the PCR value * - * Note: with TPM 2.0 extends also those banks with a known digest size to the - * cryto subsystem in order to prevent malicious use of those PCR banks. In the - * future we should dynamically determine digest sizes. + * Note: with TPM 2.0 extends also those banks for which no digest was + * specified in order to prevent malicious use of those PCR banks. * * Return: same as with tpm_transmit_cmd() */ @@ -332,7 +332,7 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) return -ENOMEM; for (i = 0; i < chip->nr_allocated_banks; i++) { - digest_list[i].alg_id = chip->allocated_banks[i]; + digest_list[i].alg_id = chip->allocated_banks[i].alg_id; memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 0e54061d3fd1..4efa304e9ece 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -247,7 +247,7 @@ struct tpm_chip { unsigned int groups_cnt; u32 nr_allocated_banks; - u16 *allocated_banks; + struct tpm_bank_info *allocated_banks; #ifdef CONFIG_ACPI acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; @@ -532,7 +532,8 @@ static inline u32 tpm2_rc_value(u32 rc) } int tpm2_get_timeouts(struct tpm_chip *chip); -int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf); +int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest, u16 *digest_size_ptr); int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 3eb7e03889a0..85dcf2654d11 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -703,7 +703,9 @@ int tpm1_auto_startup(struct tpm_chip *chip) goto out; } - chip->allocated_banks[0] = TPM_ALG_SHA1; + chip->allocated_banks[0].alg_id = TPM_ALG_SHA1; + chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1]; + chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1; chip->nr_allocated_banks = 1; return rc; diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 440ae6ee29e4..6967f15a6585 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -171,20 +171,36 @@ struct tpm2_pcr_read_out { * tpm2_pcr_read() - read a PCR value * @chip: TPM chip to use. * @pcr_idx: index of the PCR to read. - * @res_buf: buffer to store the resulting hash. + * @digest: PCR bank and buffer current PCR value is written to. + * @digest_size_ptr: pointer to variable that stores the digest size. * * Return: Same as with tpm_transmit_cmd. */ -int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) +int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest, u16 *digest_size_ptr) { + int i; int rc; struct tpm_buf buf; struct tpm2_pcr_read_out *out; u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0}; + u16 digest_size; + u16 expected_digest_size = 0; if (pcr_idx >= TPM2_PLATFORM_PCR) return -EINVAL; + if (!digest_size_ptr) { + for (i = 0; i < chip->nr_allocated_banks && + chip->allocated_banks[i].alg_id != digest->alg_id; i++) + ; + + if (i == chip->nr_allocated_banks) + return -EINVAL; + + expected_digest_size = chip->allocated_banks[i].digest_size; + } + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ); if (rc) return rc; @@ -192,18 +208,28 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); tpm_buf_append_u32(&buf, 1); - tpm_buf_append_u16(&buf, TPM_ALG_SHA1); + tpm_buf_append_u16(&buf, digest->alg_id); tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN); tpm_buf_append(&buf, (const unsigned char *)pcr_select, sizeof(pcr_select)); - rc = tpm_transmit_cmd(chip, &buf, 0, res_buf ? - "attempting to read a pcr value" : NULL); - if (rc == 0 && res_buf) { - out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; - memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE); + rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value"); + if (rc) + goto out; + + out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE]; + digest_size = be16_to_cpu(out->digest_size); + if (digest_size > sizeof(digest->digest) || + (!digest_size_ptr && digest_size != expected_digest_size)) { + rc = -EINVAL; + goto out; } + if (digest_size_ptr) + *digest_size_ptr = digest_size; + + memcpy(digest->digest, out->digest, digest_size); +out: tpm_buf_destroy(&buf); return rc; } @@ -232,7 +258,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, struct tpm2_null_auth_area auth_area; int rc; int i; - int j; if (count > chip->nr_allocated_banks) return -EINVAL; @@ -254,14 +279,9 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, tpm_buf_append_u32(&buf, count); for (i = 0; i < count; i++) { - for (j = 0; j < ARRAY_SIZE(tpm2_hash_map); j++) { - if (digests[i].alg_id != tpm2_hash_map[j].tpm_id) - continue; - tpm_buf_append_u16(&buf, digests[i].alg_id); - tpm_buf_append(&buf, (const unsigned char - *)&digests[i].digest, - hash_digest_size[tpm2_hash_map[j].crypto_id]); - } + tpm_buf_append_u16(&buf, digests[i].alg_id); + tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest, + chip->allocated_banks[i].digest_size); } rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value"); @@ -795,6 +815,30 @@ int tpm2_probe(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm2_probe); +static int tpm2_init_bank_info(struct tpm_chip *chip, u32 bank_index) +{ + struct tpm_bank_info *bank = chip->allocated_banks + bank_index; + struct tpm_digest digest = { .alg_id = bank->alg_id }; + int i; + + /* + * Avoid unnecessary PCR read operations to reduce overhead + * and obtain identifiers of the crypto subsystem. + */ + for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { + enum hash_algo crypto_algo = tpm2_hash_map[i].crypto_id; + + if (bank->alg_id != tpm2_hash_map[i].tpm_id) + continue; + + bank->digest_size = hash_digest_size[crypto_algo]; + bank->crypto_id = crypto_algo; + return 0; + } + + return tpm2_pcr_read(chip, 0, &digest, &bank->digest_size); +} + struct tpm2_pcr_selection { __be16 hash_alg; u8 size_of_select; @@ -858,7 +902,12 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) pcr_select_offset = memchr_inv(pcr_selection.pcr_select, 0, pcr_selection.size_of_select); if (pcr_select_offset) { - chip->allocated_banks[nr_alloc_banks] = hash_alg; + chip->allocated_banks[nr_alloc_banks].alg_id = hash_alg; + + rc = tpm2_init_bank_info(chip, nr_alloc_banks); + if (rc < 0) + break; + nr_alloc_banks++; } diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 9fe8c9816cf0..afd022fc9d3d 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -47,6 +47,12 @@ struct tpm_digest { u8 digest[TPM_MAX_DIGEST_SIZE]; } __packed; +struct tpm_bank_info { + u16 alg_id; + u16 digest_size; + u16 crypto_id; +}; + enum TPM_OPS_FLAGS { TPM_OPS_AUTO_STARTUP = BIT(0), }; @@ -72,7 +78,8 @@ struct tpm_class_ops { #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) extern int tpm_is_tpm2(struct tpm_chip *chip); -extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf); +extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digest); extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash); extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); @@ -89,7 +96,8 @@ static inline int tpm_is_tpm2(struct tpm_chip *chip) return -ENODEV; } -static inline int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) +static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, + struct tpm_digest *digest) { return -ENODEV; } diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index acf2c7df7145..16a4f45863b1 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -643,12 +643,12 @@ int ima_calc_buffer_hash(const void *buf, loff_t len, return calc_buffer_shash(buf, len, hash); } -static void __init ima_pcrread(u32 idx, u8 *pcr) +static void __init ima_pcrread(u32 idx, struct tpm_digest *d) { if (!ima_tpm_chip) return; - if (tpm_pcr_read(ima_tpm_chip, idx, pcr) != 0) + if (tpm_pcr_read(ima_tpm_chip, idx, d) != 0) pr_err("Error Communicating to TPM chip\n"); } @@ -658,7 +658,7 @@ static void __init ima_pcrread(u32 idx, u8 *pcr) static int __init ima_calc_boot_aggregate_tfm(char *digest, struct crypto_shash *tfm) { - u8 pcr_i[TPM_DIGEST_SIZE]; + struct tpm_digest d = { .alg_id = TPM_ALG_SHA1, .digest = {0} }; int rc; u32 i; SHASH_DESC_ON_STACK(shash, tfm); @@ -672,9 +672,9 @@ static int __init ima_calc_boot_aggregate_tfm(char *digest, /* cumulative sha1 over tpm registers 0-7 */ for (i = TPM_PCR0; i < TPM_PCR8; i++) { - ima_pcrread(i, pcr_i); + ima_pcrread(i, &d); /* now accumulate with current aggregate */ - rc = crypto_shash_update(shash, pcr_i, TPM_DIGEST_SIZE); + rc = crypto_shash_update(shash, d.digest, TPM_DIGEST_SIZE); } if (!rc) crypto_shash_final(shash, digest); From 901615cb916dc955fb7bda4e34402bf263532e4a Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 6 Feb 2019 17:24:50 +0100 Subject: [PATCH 1656/4277] tpm: move tpm_chip definition to include/linux/tpm.h The tpm_chip structure contains the list of PCR banks currently allocated in the TPM. When support for crypto agility will be added to the TPM driver, users of the driver have to provide a digest for each allocated bank to tpm_pcr_extend(). With this patch, they can obtain the PCR bank algorithms directly from chip->allocated_banks. Signed-off-by: Roberto Sassu Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm.h | 101 ++--------------------------------------- include/linux/tpm.h | 91 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 97 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4efa304e9ece..4f85ce909122 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -25,30 +25,22 @@ #include #include -#include -#include #include #include #include #include #include -#include -#include #include #include -#include #ifdef CONFIG_X86 #include #endif -enum tpm_const { - TPM_MINOR = 224, /* officially assigned */ - TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 65536, - TPM_RETRY = 50, /* 5 seconds */ - TPM_NUM_EVENT_LOG_FILES = 3, -}; +#define TPM_MINOR 224 /* officially assigned */ +#define TPM_BUFSIZE 4096 +#define TPM_NUM_DEVICES 65536 +#define TPM_RETRY 50 enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ @@ -65,16 +57,6 @@ enum tpm_addr { TPM_ADDR = 0x4E, }; -/* Indexes the duration array */ -enum tpm_duration { - TPM_SHORT = 0, - TPM_MEDIUM = 1, - TPM_LONG = 2, - TPM_LONG_LONG = 3, - TPM_UNDEFINED, - TPM_NUM_DURATIONS = TPM_UNDEFINED, -}; - #define TPM_WARN_RETRY 0x800 #define TPM_WARN_DOING_SELFTEST 0x802 #define TPM_ERR_DEACTIVATED 0x6 @@ -179,15 +161,6 @@ enum tpm2_cc_attrs { #define TPM_VID_WINBOND 0x1050 #define TPM_VID_STM 0x104A -#define TPM_PPI_VERSION_LEN 3 - -struct tpm_space { - u32 context_tbl[3]; - u8 *context_buf; - u32 session_tbl[3]; - u8 *session_buf; -}; - enum tpm_chip_flags { TPM_CHIP_FLAG_TPM2 = BIT(1), TPM_CHIP_FLAG_IRQ = BIT(2), @@ -196,72 +169,6 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), }; -struct tpm_bios_log { - void *bios_event_log; - void *bios_event_log_end; -}; - -struct tpm_chip_seqops { - struct tpm_chip *chip; - const struct seq_operations *seqops; -}; - -struct tpm_chip { - struct device dev; - struct device devs; - struct cdev cdev; - struct cdev cdevs; - - /* A driver callback under ops cannot be run unless ops_sem is held - * (sometimes implicitly, eg for the sysfs code). ops becomes null - * when the driver is unregistered, see tpm_try_get_ops. - */ - struct rw_semaphore ops_sem; - const struct tpm_class_ops *ops; - - struct tpm_bios_log log; - struct tpm_chip_seqops bin_log_seqops; - struct tpm_chip_seqops ascii_log_seqops; - - unsigned int flags; - - int dev_num; /* /dev/tpm# */ - unsigned long is_open; /* only one allowed */ - - char hwrng_name[64]; - struct hwrng hwrng; - - struct mutex tpm_mutex; /* tpm is processing */ - - unsigned long timeout_a; /* jiffies */ - unsigned long timeout_b; /* jiffies */ - unsigned long timeout_c; /* jiffies */ - unsigned long timeout_d; /* jiffies */ - bool timeout_adjusted; - unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */ - bool duration_adjusted; - - struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES]; - - const struct attribute_group *groups[3]; - unsigned int groups_cnt; - - u32 nr_allocated_banks; - struct tpm_bank_info *allocated_banks; -#ifdef CONFIG_ACPI - acpi_handle acpi_dev_handle; - char ppi_version[TPM_PPI_VERSION_LEN + 1]; -#endif /* CONFIG_ACPI */ - - struct tpm_space work_space; - u32 last_cc; - u32 nr_commands; - u32 *cc_attrs_tbl; - - /* active locality */ - int locality; -}; - #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) struct tpm_header { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index afd022fc9d3d..816e686a73ac 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -22,6 +22,10 @@ #ifndef __LINUX_TPM_H__ #define __LINUX_TPM_H__ +#include +#include +#include +#include #include #define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */ @@ -75,6 +79,93 @@ struct tpm_class_ops { void (*clk_enable)(struct tpm_chip *chip, bool value); }; +#define TPM_NUM_EVENT_LOG_FILES 3 + +/* Indexes the duration array */ +enum tpm_duration { + TPM_SHORT = 0, + TPM_MEDIUM = 1, + TPM_LONG = 2, + TPM_LONG_LONG = 3, + TPM_UNDEFINED, + TPM_NUM_DURATIONS = TPM_UNDEFINED, +}; + +#define TPM_PPI_VERSION_LEN 3 + +struct tpm_space { + u32 context_tbl[3]; + u8 *context_buf; + u32 session_tbl[3]; + u8 *session_buf; +}; + +struct tpm_bios_log { + void *bios_event_log; + void *bios_event_log_end; +}; + +struct tpm_chip_seqops { + struct tpm_chip *chip; + const struct seq_operations *seqops; +}; + +struct tpm_chip { + struct device dev; + struct device devs; + struct cdev cdev; + struct cdev cdevs; + + /* A driver callback under ops cannot be run unless ops_sem is held + * (sometimes implicitly, eg for the sysfs code). ops becomes null + * when the driver is unregistered, see tpm_try_get_ops. + */ + struct rw_semaphore ops_sem; + const struct tpm_class_ops *ops; + + struct tpm_bios_log log; + struct tpm_chip_seqops bin_log_seqops; + struct tpm_chip_seqops ascii_log_seqops; + + unsigned int flags; + + int dev_num; /* /dev/tpm# */ + unsigned long is_open; /* only one allowed */ + + char hwrng_name[64]; + struct hwrng hwrng; + + struct mutex tpm_mutex; /* tpm is processing */ + + unsigned long timeout_a; /* jiffies */ + unsigned long timeout_b; /* jiffies */ + unsigned long timeout_c; /* jiffies */ + unsigned long timeout_d; /* jiffies */ + bool timeout_adjusted; + unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */ + bool duration_adjusted; + + struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES]; + + const struct attribute_group *groups[3]; + unsigned int groups_cnt; + + u32 nr_allocated_banks; + struct tpm_bank_info *allocated_banks; +#ifdef CONFIG_ACPI + acpi_handle acpi_dev_handle; + char ppi_version[TPM_PPI_VERSION_LEN + 1]; +#endif /* CONFIG_ACPI */ + + struct tpm_space work_space; + u32 last_cc; + u32 nr_commands; + u32 *cc_attrs_tbl; + + /* active locality */ + int locality; +}; + #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) extern int tpm_is_tpm2(struct tpm_chip *chip); From 240730437deb213a58915830884e1a99045624dc Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 6 Feb 2019 17:24:51 +0100 Subject: [PATCH 1657/4277] KEYS: trusted: explicitly use tpm_chip structure from tpm_default_chip() When crypto agility support will be added to the TPM driver, users of the driver have to retrieve the allocated banks from chip->allocated_banks and use this information to prepare the array of tpm_digest structures to be passed to tpm_pcr_extend(). This patch retrieves a tpm_chip pointer from tpm_default_chip() so that the pointer can be used to prepare the array of tpm_digest structures. Signed-off-by: Roberto Sassu Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- security/keys/trusted.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 4d98f4f87236..5b852263eae1 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -34,6 +34,7 @@ static const char hmac_alg[] = "hmac(sha1)"; static const char hash_alg[] = "sha1"; +static struct tpm_chip *chip; struct sdesc { struct shash_desc shash; @@ -362,7 +363,7 @@ int trusted_tpm_send(unsigned char *cmd, size_t buflen) int rc; dump_tpm_buf(cmd); - rc = tpm_send(NULL, cmd, buflen); + rc = tpm_send(chip, cmd, buflen); dump_tpm_buf(cmd); if (rc > 0) /* Can't return positive return codes values to keyctl */ @@ -384,10 +385,10 @@ static int pcrlock(const int pcrnum) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - ret = tpm_get_random(NULL, hash, SHA1_DIGEST_SIZE); + ret = tpm_get_random(chip, hash, SHA1_DIGEST_SIZE); if (ret != SHA1_DIGEST_SIZE) return ret; - return tpm_pcr_extend(NULL, pcrnum, hash) ? -EINVAL : 0; + return tpm_pcr_extend(chip, pcrnum, hash) ? -EINVAL : 0; } /* @@ -400,7 +401,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, unsigned char ononce[TPM_NONCE_SIZE]; int ret; - ret = tpm_get_random(NULL, ononce, TPM_NONCE_SIZE); + ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) return ret; @@ -496,7 +497,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, if (ret < 0) goto out; - ret = tpm_get_random(NULL, td->nonceodd, TPM_NONCE_SIZE); + ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) goto out; ordinal = htonl(TPM_ORD_SEAL); @@ -606,7 +607,7 @@ static int tpm_unseal(struct tpm_buf *tb, ordinal = htonl(TPM_ORD_UNSEAL); keyhndl = htonl(SRKHANDLE); - ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); + ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) { pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); return ret; @@ -751,7 +752,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay, int i; int tpm2; - tpm2 = tpm_is_tpm2(NULL); + tpm2 = tpm_is_tpm2(chip); if (tpm2 < 0) return tpm2; @@ -920,7 +921,7 @@ static struct trusted_key_options *trusted_options_alloc(void) struct trusted_key_options *options; int tpm2; - tpm2 = tpm_is_tpm2(NULL); + tpm2 = tpm_is_tpm2(chip); if (tpm2 < 0) return NULL; @@ -970,7 +971,7 @@ static int trusted_instantiate(struct key *key, size_t key_len; int tpm2; - tpm2 = tpm_is_tpm2(NULL); + tpm2 = tpm_is_tpm2(chip); if (tpm2 < 0) return tpm2; @@ -1011,7 +1012,7 @@ static int trusted_instantiate(struct key *key, switch (key_cmd) { case Opt_load: if (tpm2) - ret = tpm_unseal_trusted(NULL, payload, options); + ret = tpm_unseal_trusted(chip, payload, options); else ret = key_unseal(payload, options); dump_payload(payload); @@ -1021,13 +1022,13 @@ static int trusted_instantiate(struct key *key, break; case Opt_new: key_len = payload->key_len; - ret = tpm_get_random(NULL, payload->key, key_len); + ret = tpm_get_random(chip, payload->key, key_len); if (ret != key_len) { pr_info("trusted_key: key_create failed (%d)\n", ret); goto out; } if (tpm2) - ret = tpm_seal_trusted(NULL, payload, options); + ret = tpm_seal_trusted(chip, payload, options); else ret = key_seal(payload, options); if (ret < 0) @@ -1225,17 +1226,26 @@ static int __init init_trusted(void) { int ret; + chip = tpm_default_chip(); + if (!chip) + return -ENOENT; ret = trusted_shash_alloc(); if (ret < 0) - return ret; + goto err_put; ret = register_key_type(&key_type_trusted); if (ret < 0) - trusted_shash_release(); + goto err_release; + return 0; +err_release: + trusted_shash_release(); +err_put: + put_device(&chip->dev); return ret; } static void __exit cleanup_trusted(void) { + put_device(&chip->dev); trusted_shash_release(); unregister_key_type(&key_type_trusted); } From 0b6cf6b97b7ef1fa3c7fefab0cac897a1c4a3400 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Wed, 6 Feb 2019 17:24:52 +0100 Subject: [PATCH 1658/4277] tpm: pass an array of tpm_extend_digest structures to tpm_pcr_extend() Currently, tpm_pcr_extend() accepts as an input only a SHA1 digest. This patch replaces the hash parameter of tpm_pcr_extend() with an array of tpm_digest structures, so that the caller can provide a digest for each PCR bank currently allocated in the TPM. tpm_pcr_extend() will not extend banks for which no digest was provided, as it happened before this patch, but instead it requires that callers provide the full set of digests. Since the number of digests will always be chip->nr_allocated_banks, the count parameter has been removed. Due to the API change, ima_pcr_extend() and pcrlock() have been modified. Since the number of allocated banks is not known in advance, the memory for the digests must be dynamically allocated. To avoid performance degradation and to avoid that a PCR extend is not done due to lack of memory, the array of tpm_digest structures is allocated by the users of the TPM driver at initialization time. Signed-off-by: Roberto Sassu Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Mimi Zohar (on x86 for TPM 1.2 & PTT TPM 2.0) Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-interface.c | 30 ++++++++-------------- drivers/char/tpm/tpm.h | 2 +- drivers/char/tpm/tpm2-cmd.c | 10 +++----- include/linux/tpm.h | 5 ++-- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_init.c | 4 +++ security/integrity/ima/ima_queue.c | 27 +++++++++++++++++++- security/keys/trusted.c | 41 ++++++++++++++++++++++++------ 8 files changed, 82 insertions(+), 38 deletions(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 1c92dbeef736..83ece5639f86 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -308,42 +308,34 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * tpm_pcr_extend - extend a PCR value in SHA1 bank. * @chip: a &struct tpm_chip instance, %NULL for the default chip * @pcr_idx: the PCR to be retrieved - * @hash: the hash value used to extend the PCR value + * @digests: array of tpm_digest structures used to extend PCRs * - * Note: with TPM 2.0 extends also those banks for which no digest was - * specified in order to prevent malicious use of those PCR banks. + * Note: callers must pass a digest for every allocated PCR bank, in the same + * order of the banks in chip->allocated_banks. * * Return: same as with tpm_transmit_cmd() */ -int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) +int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digests) { int rc; - struct tpm_digest *digest_list; int i; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; + for (i = 0; i < chip->nr_allocated_banks; i++) + if (digests[i].alg_id != chip->allocated_banks[i].alg_id) + return -EINVAL; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { - digest_list = kcalloc(chip->nr_allocated_banks, - sizeof(*digest_list), GFP_KERNEL); - if (!digest_list) - return -ENOMEM; - - for (i = 0; i < chip->nr_allocated_banks; i++) { - digest_list[i].alg_id = chip->allocated_banks[i].alg_id; - memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE); - } - - rc = tpm2_pcr_extend(chip, pcr_idx, chip->nr_allocated_banks, - digest_list); - kfree(digest_list); + rc = tpm2_pcr_extend(chip, pcr_idx, digests); tpm_put_ops(chip); return rc; } - rc = tpm1_pcr_extend(chip, pcr_idx, hash, + rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest, "attempting extend a PCR value"); tpm_put_ops(chip); return rc; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4f85ce909122..2cce072f25b5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -441,7 +441,7 @@ static inline u32 tpm2_rc_value(u32 rc) int tpm2_get_timeouts(struct tpm_chip *chip); int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest, u16 *digest_size_ptr); -int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, +int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); void tpm2_flush_context(struct tpm_chip *chip, u32 handle); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 6967f15a6585..e74c5b7b64bf 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -246,12 +246,11 @@ struct tpm2_null_auth_area { * * @chip: TPM chip to use. * @pcr_idx: index of the PCR. - * @count: number of digests passed. * @digests: list of pcr banks and corresponding digest values to extend. * * Return: Same as with tpm_transmit_cmd. */ -int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, +int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests) { struct tpm_buf buf; @@ -259,9 +258,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, int rc; int i; - if (count > chip->nr_allocated_banks) - return -EINVAL; - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND); if (rc) return rc; @@ -276,9 +272,9 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count, tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area)); tpm_buf_append(&buf, (const unsigned char *)&auth_area, sizeof(auth_area)); - tpm_buf_append_u32(&buf, count); + tpm_buf_append_u32(&buf, chip->nr_allocated_banks); - for (i = 0; i < count; i++) { + for (i = 0; i < chip->nr_allocated_banks; i++) { tpm_buf_append_u16(&buf, digests[i].alg_id); tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest, chip->allocated_banks[i].digest_size); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 816e686a73ac..1b5436b213a2 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -171,7 +171,8 @@ struct tpm_chip { extern int tpm_is_tpm2(struct tpm_chip *chip); extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest); -extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash); +extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, + struct tpm_digest *digests); extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); extern int tpm_seal_trusted(struct tpm_chip *chip, @@ -194,7 +195,7 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, } static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, - const u8 *hash) + struct tpm_digest *digests) { return -ENODEV; } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index cc12f3449a72..89d65cf8053d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -153,6 +153,7 @@ int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); void ima_init_template_list(void); +int __init ima_init_digests(void); /* * used to protect h_table and sha_table diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 6bb42a9c5e47..6c9295449751 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -123,8 +123,12 @@ int __init ima_init(void) if (rc != 0) return rc; + /* It can be called before ima_init_digests(), it does not use TPM. */ ima_load_kexec_buffer(); + rc = ima_init_digests(); + if (rc != 0) + return rc; rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */ if (rc != 0) return rc; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 0e41dc1df1d4..6b6d044e0440 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -27,6 +27,9 @@ #define AUDIT_CAUSE_LEN_MAX 32 +/* pre-allocated array of tpm_digest structures to extend a PCR */ +static struct tpm_digest *digests; + LIST_HEAD(ima_measurements); /* list of all measurements */ #ifdef CONFIG_IMA_KEXEC static unsigned long binary_runtime_size; @@ -140,11 +143,15 @@ unsigned long ima_get_binary_runtime_size(void) static int ima_pcr_extend(const u8 *hash, int pcr) { int result = 0; + int i; if (!ima_tpm_chip) return result; - result = tpm_pcr_extend(ima_tpm_chip, pcr, hash); + for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) + memcpy(digests[i].digest, hash, TPM_DIGEST_SIZE); + + result = tpm_pcr_extend(ima_tpm_chip, pcr, digests); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result; @@ -211,3 +218,21 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry) mutex_unlock(&ima_extend_list_mutex); return result; } + +int __init ima_init_digests(void) +{ + int i; + + if (!ima_tpm_chip) + return 0; + + digests = kcalloc(ima_tpm_chip->nr_allocated_banks, sizeof(*digests), + GFP_NOFS); + if (!digests) + return -ENOMEM; + + for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) + digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id; + + return 0; +} diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 5b852263eae1..bcc9c6ead7fd 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -35,6 +35,7 @@ static const char hmac_alg[] = "hmac(sha1)"; static const char hash_alg[] = "sha1"; static struct tpm_chip *chip; +static struct tpm_digest *digests; struct sdesc { struct shash_desc shash; @@ -380,15 +381,10 @@ EXPORT_SYMBOL_GPL(trusted_tpm_send); */ static int pcrlock(const int pcrnum) { - unsigned char hash[SHA1_DIGEST_SIZE]; - int ret; - if (!capable(CAP_SYS_ADMIN)) return -EPERM; - ret = tpm_get_random(chip, hash, SHA1_DIGEST_SIZE); - if (ret != SHA1_DIGEST_SIZE) - return ret; - return tpm_pcr_extend(chip, pcrnum, hash) ? -EINVAL : 0; + + return tpm_pcr_extend(chip, pcrnum, digests) ? -EINVAL : 0; } /* @@ -1222,6 +1218,29 @@ hashalg_fail: return ret; } +static int __init init_digests(void) +{ + u8 digest[TPM_MAX_DIGEST_SIZE]; + int ret; + int i; + + ret = tpm_get_random(chip, digest, TPM_MAX_DIGEST_SIZE); + if (ret < 0) + return ret; + if (ret < TPM_MAX_DIGEST_SIZE) + return -EFAULT; + + digests = kcalloc(chip->nr_allocated_banks, sizeof(*digests), + GFP_KERNEL); + if (!digests) + return -ENOMEM; + + for (i = 0; i < chip->nr_allocated_banks; i++) + memcpy(digests[i].digest, digest, TPM_MAX_DIGEST_SIZE); + + return 0; +} + static int __init init_trusted(void) { int ret; @@ -1229,15 +1248,20 @@ static int __init init_trusted(void) chip = tpm_default_chip(); if (!chip) return -ENOENT; - ret = trusted_shash_alloc(); + ret = init_digests(); if (ret < 0) goto err_put; + ret = trusted_shash_alloc(); + if (ret < 0) + goto err_free; ret = register_key_type(&key_type_trusted); if (ret < 0) goto err_release; return 0; err_release: trusted_shash_release(); +err_free: + kfree(digests); err_put: put_device(&chip->dev); return ret; @@ -1246,6 +1270,7 @@ err_put: static void __exit cleanup_trusted(void) { put_device(&chip->dev); + kfree(digests); trusted_shash_release(); unregister_key_type(&key_type_trusted); } From 587bad77e9c6d73c49b4ce7996822628824ca30e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 17 Jan 2019 12:41:31 -0500 Subject: [PATCH 1659/4277] tpm/ppi: pass function revision ID to tpm_eval_dsm() Since we will need to pass different function revision numbers to tpm_eval_dsm, convert this function now to take the function revision as an additional parameter. Signed-off-by: Stefan Berger Tested-by: David Safford Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ppi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 86dd8521feef..90b69aeadc99 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -38,12 +38,11 @@ static const guid_t tpm_ppi_guid = static inline union acpi_object * tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, - union acpi_object *argv4) + union acpi_object *argv4, u64 rev) { BUG_ON(!ppi_handle); return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, - func, argv4, type); + rev, func, argv4, type); } static ssize_t tpm_show_ppi_version(struct device *dev, @@ -62,7 +61,7 @@ static ssize_t tpm_show_ppi_request(struct device *dev, struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, - ACPI_TYPE_PACKAGE, NULL); + ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID); if (!obj) return -ENXIO; @@ -126,7 +125,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, } obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, - &argv4); + &argv4, TPM_PPI_REVISION_ID); if (!obj) { return -ENXIO; } else { @@ -170,7 +169,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, if (strcmp(chip->ppi_version, "1.2") < 0) obj = &tmp; obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT, - ACPI_TYPE_INTEGER, obj); + ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID); if (!obj) { return -ENXIO; } else { @@ -196,7 +195,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev, struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, - ACPI_TYPE_PACKAGE, NULL); + ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID); if (!obj) return -ENXIO; @@ -272,7 +271,8 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, for (i = start; i <= end; i++) { tmp.integer.value = i; obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, - ACPI_TYPE_INTEGER, &argv); + ACPI_TYPE_INTEGER, &argv, + TPM_PPI_REVISION_ID); if (!obj) { return -ENOMEM; } else { From 09fe1b42583d0275657b388ad8a67c081863d47d Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 17 Jan 2019 12:41:32 -0500 Subject: [PATCH 1660/4277] tpm/ppi: rename TPM_PPI_REVISION_ID to TPM_PPI_REVISION_ID_1 TPM PPI 1.3 introduces a function revision 2 for some functions. So, rename the existing TPM_PPI_REVISION_ID to TPM_PPI_REVISION_ID_1. Signed-off-by: Stefan Berger Tested-by: David Safford Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ppi.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 90b69aeadc99..88ecdae1df03 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -20,7 +20,7 @@ #include #include "tpm.h" -#define TPM_PPI_REVISION_ID 1 +#define TPM_PPI_REVISION_ID_1 1 #define TPM_PPI_FN_VERSION 1 #define TPM_PPI_FN_SUBREQ 2 #define TPM_PPI_FN_GETREQ 3 @@ -61,7 +61,7 @@ static ssize_t tpm_show_ppi_request(struct device *dev, struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, - ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID); + ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); if (!obj) return -ENXIO; @@ -103,7 +103,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * version 1.1 */ if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_SUBREQ2)) + TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_SUBREQ2)) func = TPM_PPI_FN_SUBREQ2; /* @@ -125,7 +125,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, } obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, - &argv4, TPM_PPI_REVISION_ID); + &argv4, TPM_PPI_REVISION_ID_1); if (!obj) { return -ENXIO; } else { @@ -169,7 +169,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, if (strcmp(chip->ppi_version, "1.2") < 0) obj = &tmp; obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT, - ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID); + ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID_1); if (!obj) { return -ENXIO; } else { @@ -195,7 +195,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev, struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, - ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID); + ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); if (!obj) return -ENXIO; @@ -263,7 +263,7 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, "User not required", }; - if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID, + if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_GETOPR)) return -EPERM; @@ -272,7 +272,7 @@ static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, tmp.integer.value = i; obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, ACPI_TYPE_INTEGER, &argv, - TPM_PPI_REVISION_ID); + TPM_PPI_REVISION_ID_1); if (!obj) { return -ENOMEM; } else { @@ -338,12 +338,13 @@ void tpm_add_ppi(struct tpm_chip *chip) return; if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION)) + TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_VERSION)) return; /* Cache PPI version string. */ obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid, - TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION, + TPM_PPI_REVISION_ID_1, + TPM_PPI_FN_VERSION, NULL, ACPI_TYPE_STRING); if (obj) { strlcpy(chip->ppi_version, obj->string.pointer, From 9d4023ed4db6e01ff50cb68d782202c2f50760ae Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 17 Jan 2019 12:41:33 -0500 Subject: [PATCH 1661/4277] tpm/ppi: Display up to 101 operations as define for version 1.3 TPM PPI 1.3 defines operations up to number 101. We need to query up to this number to show the user what the firmware implements. Signed-off-by: Stefan Berger Tested-by: David Safford Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ppi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 88ecdae1df03..fdfd6271351e 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -28,7 +28,7 @@ #define TPM_PPI_FN_GETRSP 5 #define TPM_PPI_FN_SUBREQ2 7 #define TPM_PPI_FN_GETOPR 8 -#define PPI_TPM_REQ_MAX 22 +#define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */ #define PPI_VS_REQ_START 128 #define PPI_VS_REQ_END 255 From 8b60c79bcc102dba22da684c7e51e9bab1d44237 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 17 Jan 2019 12:41:34 -0500 Subject: [PATCH 1662/4277] tpm/ppi: Possibly show command parameter if TPM PPI 1.3 is used TPM PPI 1.3 introduces an additional optional command parameter that may be needed for some commands. Display the parameter if the command requires such a parameter. Only command 23 needs one. The PPI request file will show output like this then: # echo "23 16" > request # cat request 23 16 # echo "5" > request # cat request 5 Signed-off-by: Stefan Berger Tested-by: David Safford Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ppi.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index fdfd6271351e..942a2f79e9da 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -21,6 +21,7 @@ #include "tpm.h" #define TPM_PPI_REVISION_ID_1 1 +#define TPM_PPI_REVISION_ID_2 2 #define TPM_PPI_FN_VERSION 1 #define TPM_PPI_FN_SUBREQ 2 #define TPM_PPI_FN_GETREQ 3 @@ -36,6 +37,11 @@ static const guid_t tpm_ppi_guid = GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); +static bool tpm_ppi_req_has_parameter(u64 req) +{ + return req == 23; +} + static inline union acpi_object * tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, union acpi_object *argv4, u64 rev) @@ -59,9 +65,14 @@ static ssize_t tpm_show_ppi_request(struct device *dev, ssize_t size = -EINVAL; union acpi_object *obj; struct tpm_chip *chip = to_tpm_chip(dev); + u64 rev = TPM_PPI_REVISION_ID_2; + u64 req; + + if (strcmp(chip->ppi_version, "1.2") < 0) + rev = TPM_PPI_REVISION_ID_1; obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, - ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); + ACPI_TYPE_PACKAGE, NULL, rev); if (!obj) return -ENXIO; @@ -71,7 +82,23 @@ static ssize_t tpm_show_ppi_request(struct device *dev, * error. The second is pending TPM operation requested by the OS, 0 * means none and >0 means operation value. */ - if (obj->package.count == 2 && + if (obj->package.count == 3 && + obj->package.elements[0].type == ACPI_TYPE_INTEGER && + obj->package.elements[1].type == ACPI_TYPE_INTEGER && + obj->package.elements[2].type == ACPI_TYPE_INTEGER) { + if (obj->package.elements[0].integer.value) + size = -EFAULT; + else { + req = obj->package.elements[1].integer.value; + if (tpm_ppi_req_has_parameter(req)) + size = scnprintf(buf, PAGE_SIZE, + "%llu %llu\n", req, + obj->package.elements[2].integer.value); + else + size = scnprintf(buf, PAGE_SIZE, + "%llu\n", req); + } + } else if (obj->package.count == 2 && obj->package.elements[0].type == ACPI_TYPE_INTEGER && obj->package.elements[1].type == ACPI_TYPE_INTEGER) { if (obj->package.elements[0].integer.value) From 50a81b60bfe075a0023670ff86558abd02536799 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 17 Jan 2019 12:41:35 -0500 Subject: [PATCH 1663/4277] tpm/ppi: Enable submission of optional command parameter for PPI 1.3 This patch enables a user to specify the additional optional command parameter by writing it into the request file: # echo "23 16" > request # cat request 23 16 For backwards compatibility: If only 1 parameter is given then we assume this is the operation request number. # echo "5" > request # cat request 5 Signed-off-by: Stefan Berger Tested-by: David Safford Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ppi.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 942a2f79e9da..75e7a856177c 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -120,9 +120,10 @@ static ssize_t tpm_store_ppi_request(struct device *dev, u32 req; u64 ret; int func = TPM_PPI_FN_SUBREQ; - union acpi_object *obj, tmp; - union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp); + union acpi_object *obj, tmp[2]; + union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(2, tmp); struct tpm_chip *chip = to_tpm_chip(dev); + u64 rev = TPM_PPI_REVISION_ID_1; /* * the function to submit TPM operation request to pre-os environment @@ -139,20 +140,29 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * string/package type. For PPI version 1.0 and 1.1, use buffer type * for compatibility, and use package type since 1.2 according to spec. */ - if (strcmp(chip->ppi_version, "1.2") < 0) { + if (strcmp(chip->ppi_version, "1.3") == 0) { + if (sscanf(buf, "%llu %llu", &tmp[0].integer.value, + &tmp[1].integer.value) != 2) + goto ppi12; + rev = TPM_PPI_REVISION_ID_2; + tmp[0].type = ACPI_TYPE_INTEGER; + tmp[1].type = ACPI_TYPE_INTEGER; + } else if (strcmp(chip->ppi_version, "1.2") < 0) { if (sscanf(buf, "%d", &req) != 1) return -EINVAL; argv4.type = ACPI_TYPE_BUFFER; argv4.buffer.length = sizeof(req); argv4.buffer.pointer = (u8 *)&req; } else { - tmp.type = ACPI_TYPE_INTEGER; - if (sscanf(buf, "%llu", &tmp.integer.value) != 1) +ppi12: + argv4.package.count = 1; + tmp[0].type = ACPI_TYPE_INTEGER; + if (sscanf(buf, "%llu", &tmp[0].integer.value) != 1) return -EINVAL; } obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, - &argv4, TPM_PPI_REVISION_ID_1); + &argv4, rev); if (!obj) { return -ENXIO; } else { From ede033e1e863c36729de25b57145fff287415830 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 7 Feb 2019 17:28:55 +0100 Subject: [PATCH 1664/4277] dt-bindings: gpio: document the new pull-up/pull-down flags This commit extends the flags that can be used in GPIO specifiers to indicate if a pull-up resistor or pull-down resistor should be enabled. While some pinctrl DT bindings already offer the capability of configuring pull-up/pull-down resistors at the pin level, a number of simple GPIO controllers don't have any pinmuxing capability, and therefore do not rely on the pinctrl DT bindings. Such simple GPIO controllers however sometimes allow to configure pull-up and pull-down resistors on a per-pin basis, and whether such resistors should be enabled or not is a highly board-specific HW characteristic. By using two additional bits of the GPIO flag specifier, we can easily allow the Device Tree to describe which GPIOs should have their pull-up or pull-down resistors enabled. Even though the two options are mutually exclusive, we still need two bits to encode at least three states: no pull-up/pull-down, pull-up, pull-down. Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 12 ++++++++++++ include/dt-bindings/gpio/gpio.h | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index f0ba154b5723..a8895d339bfe 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -67,6 +67,18 @@ Optional standard bitfield specifiers for the last cell: https://en.wikipedia.org/wiki/Open_collector - Bit 3: 0 means the output should be maintained during sleep/low-power mode 1 means the output state can be lost during sleep/low-power mode +- Bit 4: 0 means no pull-up resistor should be enabled + 1 means a pull-up resistor should be enabled + This setting only applies to hardware with a simple on/off + control for pull-up configuration. If the hardware has more + elaborate pull-up configuration, it should be represented + using a pin control binding. +- Bit 5: 0 means no pull-down resistor should be enabled + 1 means a pull-down resistor should be enabled + This setting only applies to hardware with a simple on/off + control for pull-down configuration. If the hardware has more + elaborate pull-down configuration, it should be represented + using a pin control binding. 1.1) GPIO specifier best practices ---------------------------------- diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index 2cc10ae4bbb7..c029467e828b 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -33,4 +33,10 @@ #define GPIO_PERSISTENT 0 #define GPIO_TRANSITORY 8 +/* Bit 4 express pull up */ +#define GPIO_PULL_UP 16 + +/* Bit 5 express pull down */ +#define GPIO_PULL_DOWN 32 + #endif From 71479789851bdd9d56cd9e82892b0a3bee0a4c2a Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 7 Feb 2019 17:28:56 +0100 Subject: [PATCH 1665/4277] gpio: rename gpio_set_drive_single_ended() to gpio_set_config() This commit simply renames gpio_set_drive_single_ended() to gpio_set_config(), as the function is not specific to setting the GPIO drive type, and will be used for other purposes in followup commits. In addition, it moves the function above gpiod_direction_input(), as it will be used from gpiod_direction_input(). Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1651d7f0a303..de595fa31a1a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2518,6 +2518,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ +static int gpio_set_config(struct gpio_chip *gc, unsigned offset, + enum pin_config_param mode) +{ + unsigned long config = { PIN_CONF_PACKED(mode, 0) }; + + return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2571,14 +2579,6 @@ int gpiod_direction_input(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_direction_input); -static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset, - enum pin_config_param mode) -{ - unsigned long config = { PIN_CONF_PACKED(mode, 0) }; - - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; -} - static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { struct gpio_chip *gc = desc->gdev->chip; @@ -2675,8 +2675,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) gc = desc->gdev->chip; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { /* First see if we can enable open drain in hardware */ - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_DRAIN); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_DRAIN); if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ @@ -2684,16 +2684,16 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) return gpiod_direction_input(desc); } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_SOURCE); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ if (!value) return gpiod_direction_input(desc); } else { - gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_PUSH_PULL); + gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_PUSH_PULL); } set_output_value: From 6581eaf0e890756e093e2f44916edb5e7e6558ca Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 7 Feb 2019 17:28:57 +0100 Subject: [PATCH 1666/4277] gpio: use new gpio_set_config() helper in more places As suggested by Linus Walleij, let's use the new gpio_set_config() helper in gpiod_set_debounce() and gpiod_set_transitory(). Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index de595fa31a1a..1f239aac43df 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2725,7 +2725,7 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -2762,7 +2762,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); From d449991c4d1d0663b42db7648510a9911de21298 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 7 Feb 2019 17:28:58 +0100 Subject: [PATCH 1667/4277] gpio: add core support for pull-up/pull-down configuration This commit adds support for configuring the pull-up and pull-down resistors available in some GPIO controllers. While configuring pull-up/pull-down is already possible through the pinctrl subsystem, some GPIO controllers, especially simple ones such as GPIO expanders on I2C, don't have any pinmuxing capability and therefore do not use the pinctrl subsystem. This commit implements the GPIO_PULL_UP and GPIO_PULL_DOWN flags, which can be used from the Device Tree, to enable a pull-up or pull-down resistor on a given GPIO. The flag is simply propagated all the way to the core GPIO subsystem, where it is used to call the gpio_chip ->set_config callback with the appropriate existing PIN_CONFIG_BIAS_* values. Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 5 +++++ drivers/gpio/gpiolib.c | 18 ++++++++++++++++++ drivers/gpio/gpiolib.h | 2 ++ include/linux/gpio/machine.h | 2 ++ include/linux/of_gpio.h | 2 ++ 5 files changed, 29 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a6e1891217e2..9a8b78477f79 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -345,6 +345,11 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (of_flags & OF_GPIO_TRANSITORY) *flags |= GPIO_TRANSITORY; + if (of_flags & OF_GPIO_PULL_UP) + *flags |= GPIO_PULL_UP; + if (of_flags & OF_GPIO_PULL_DOWN) + *flags |= GPIO_PULL_DOWN; + return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1f239aac43df..22d8b37f5319 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2573,6 +2573,13 @@ int gpiod_direction_input(struct gpio_desc *desc) if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); + if (test_bit(FLAG_PULL_UP, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_UP); + else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_DOWN); + trace_gpio_direction(desc_to_gpio(desc), 1, status); return status; @@ -4050,6 +4057,17 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + gpiod_err(desc, + "both pull-up and pull-down enabled, invalid configuration\n"); + return -EINVAL; + } + + if (lflags & GPIO_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); + else if (lflags & GPIO_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (status < 0) return status; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index bc57f0dc5953..078ab17b96bf 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -219,6 +219,8 @@ struct gpio_desc { #define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ +#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ +#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ /* Connection label */ const char *label; diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index daa44eac9241..69673be10213 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -12,6 +12,8 @@ enum gpio_lookup_flags { GPIO_OPEN_SOURCE = (1 << 2), GPIO_PERSISTENT = (0 << 3), GPIO_TRANSITORY = (1 << 3), + GPIO_PULL_UP = (1 << 4), + GPIO_PULL_DOWN = (1 << 5), }; /** diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 163b79ecd01a..f9737dea9d1f 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -28,6 +28,8 @@ enum of_gpio_flags { OF_GPIO_SINGLE_ENDED = 0x2, OF_GPIO_OPEN_DRAIN = 0x4, OF_GPIO_TRANSITORY = 0x8, + OF_GPIO_PULL_UP = 0x10, + OF_GPIO_PULL_DOWN = 0x20, }; #ifdef CONFIG_OF_GPIO From 15add06841a3b0b4734a72847a73c71fd09ebe52 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 7 Feb 2019 17:28:59 +0100 Subject: [PATCH 1668/4277] gpio: pca953x: add ->set_config implementation This commit adds a minimal implementation of the ->set_config() hook, with support for the PIN_CONFIG_BIAS_PULL_UP and PIN_CONFIG_BIAS_PULL_DOWN configurations. Signed-off-by: Thomas Petazzoni Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pca953x.c | 66 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 83617fdc661d..4cace44b179c 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -178,6 +178,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) #define PCA957x_BANK_OUTPUT BIT(5) #define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2) +#define PCAL9xxx_BANK_PULL_EN BIT(8 + 3) +#define PCAL9xxx_BANK_PULL_SEL BIT(8 + 4) #define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5) #define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6) @@ -199,6 +201,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) * - Extended set, above 0x40, often chip specific. * - PCAL6524/PCAL9555A with custom PCAL IRQ handling: * Input latch register 0x40 + 2 * bank_size RW + * Pull-up/pull-down enable reg 0x40 + 3 * bank_size RW + * Pull-up/pull-down select reg 0x40 + 4 * bank_size RW * Interrupt mask register 0x40 + 5 * bank_size RW * Interrupt status register 0x40 + 6 * bank_size R * @@ -247,7 +251,8 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) { - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK | + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK | PCAL9xxx_BANK_IRQ_STAT; } @@ -268,7 +273,8 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK; + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK; return pca953x_check_register(chip, reg, bank); } @@ -473,6 +479,61 @@ exit: mutex_unlock(&chip->i2c_lock); } +static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, + unsigned int offset, + unsigned long config) +{ + u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset, + true, false); + u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset, + true, false); + u8 bit = BIT(offset % BANK_SZ); + int ret; + + /* + * pull-up/pull-down configuration requires PCAL extended + * registers + */ + if (!(chip->driver_data & PCA_PCAL)) + return -ENOTSUPP; + + mutex_lock(&chip->i2c_lock); + + /* Disable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + if (ret) + goto exit; + + /* Configure pull-up/pull-down */ + if (config == PIN_CONFIG_BIAS_PULL_UP) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + else if (config == PIN_CONFIG_BIAS_PULL_DOWN) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + if (ret) + goto exit; + + /* Enable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + +exit: + mutex_unlock(&chip->i2c_lock); + return ret; +} + +static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct pca953x_chip *chip = gpiochip_get_data(gc); + + switch (config) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + return pca953x_gpio_set_pull_up_down(chip, offset, config); + default: + return -ENOTSUPP; + } +} + static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) { struct gpio_chip *gc; @@ -485,6 +546,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->set = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->set_multiple = pca953x_gpio_set_multiple; + gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; gc->base = chip->gpio_start; From dac7da986ba39e9ff6b078ae15b561c4929edec1 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 11 Feb 2019 19:52:05 -0800 Subject: [PATCH 1669/4277] qcom: spmi-gpio: Fix boundary conditions IRQ domain translate GPIOs on the SPMI PMIC are numbered 1..ngpio, so the boundary check in pmic_gpio_domain_translate() is off by one, correct this. Fixes: ca69e2d165eb ("qcom: spmi-gpio: add support for hierarchical IRQ chip") Signed-off-by: Bjorn Andersson Reviewed-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index e01a556ac586..cb512c7a5251 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -962,7 +962,8 @@ static int pmic_gpio_domain_translate(struct irq_domain *domain, struct pmic_gpio_state, chip); - if (fwspec->param_count != 2 || fwspec->param[0] >= state->chip.ngpio) + if (fwspec->param_count != 2 || + fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) return -EINVAL; *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; From 86291029e97eaf6a9c2ed43e7968ba8cf9f9f3b7 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:21 -0500 Subject: [PATCH 1670/4277] pinctrl: qcom: ssbi-gpio: hardcode IRQ counts The probing of this driver calls platform_irq_count, which will setup all of the IRQs that are configured in device tree. In preparation for converting this driver to be a hierarchical IRQ chip, hardcode the IRQ count based on the hardware type so that all the IRQs are not configured immediately and are configured on an as-needed basis later in the boot process. This change will also allow for the removal of the interrupts property later in this patch series once the hierarchical IRQ chip support is in. This patch also removes the generic qcom,ssbi-gpio OF match since we don't know the number of pins. All of the existing upstream bindings already include the more-specific binding. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index ded7d765af2e..23641e4d4521 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -665,12 +665,11 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, } static const struct of_device_id pm8xxx_gpio_of_match[] = { - { .compatible = "qcom,pm8018-gpio" }, - { .compatible = "qcom,pm8038-gpio" }, - { .compatible = "qcom,pm8058-gpio" }, - { .compatible = "qcom,pm8917-gpio" }, - { .compatible = "qcom,pm8921-gpio" }, - { .compatible = "qcom,ssbi-gpio" }, + { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, + { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, + { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, + { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, + { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, { }, }; MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); @@ -680,20 +679,14 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) struct pm8xxx_pin_data *pin_data; struct pinctrl_pin_desc *pins; struct pm8xxx_gpio *pctrl; - int ret; - int i, npins; + int ret, i; pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) return -ENOMEM; pctrl->dev = &pdev->dev; - npins = platform_irq_count(pdev); - if (!npins) - return -EINVAL; - if (npins < 0) - return npins; - pctrl->npins = npins; + pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!pctrl->regmap) { From b5c231d8c8037f63d34199ea1667bbe1cd9f940f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:22 -0500 Subject: [PATCH 1671/4277] genirq: introduce irq_domain_translate_twocell Add a new function irq_domain_translate_twocell() that is to be used as the translate function in struct irq_domain_ops for the v2 IRQ API. This patch also changes irq_domain_xlate_twocell() from the v1 IRQ API to call irq_domain_translate_twocell() in the v2 IRQ API. This required changes to of_phandle_args_to_fwspec()'s arguments so that it can be called from multiple places. Cc: Thomas Gleixner Reviewed-by: Marc Zyngier Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- include/linux/irqdomain.h | 5 +++++ kernel/irq/irqdomain.c | 45 +++++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 35965f41d7be..fcefe0c7263f 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -419,6 +419,11 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type); +int irq_domain_translate_twocell(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type); + /* IPI functions */ int irq_reserve_ipi(struct irq_domain *domain, const struct cpumask *dest); int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 8b0be4bd6565..56a30d542b8e 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -729,16 +729,17 @@ static int irq_domain_translate(struct irq_domain *d, return 0; } -static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data, +static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, + unsigned int count, struct irq_fwspec *fwspec) { int i; - fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL; - fwspec->param_count = irq_data->args_count; + fwspec->fwnode = np ? &np->fwnode : NULL; + fwspec->param_count = count; - for (i = 0; i < irq_data->args_count; i++) - fwspec->param[i] = irq_data->args[i]; + for (i = 0; i < count; i++) + fwspec->param[i] = args[i]; } unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec) @@ -836,7 +837,9 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) { struct irq_fwspec fwspec; - of_phandle_args_to_fwspec(irq_data, &fwspec); + of_phandle_args_to_fwspec(irq_data->np, irq_data->args, + irq_data->args_count, &fwspec); + return irq_create_fwspec_mapping(&fwspec); } EXPORT_SYMBOL_GPL(irq_create_of_mapping); @@ -928,11 +931,10 @@ int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_type) { - if (WARN_ON(intsize < 2)) - return -EINVAL; - *out_hwirq = intspec[0]; - *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; - return 0; + struct irq_fwspec fwspec; + + of_phandle_args_to_fwspec(ctrlr, intspec, intsize, &fwspec); + return irq_domain_translate_twocell(d, &fwspec, out_hwirq, out_type); } EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell); @@ -968,6 +970,27 @@ const struct irq_domain_ops irq_domain_simple_ops = { }; EXPORT_SYMBOL_GPL(irq_domain_simple_ops); +/** + * irq_domain_translate_twocell() - Generic translate for direct two cell + * bindings + * + * Device Tree IRQ specifier translation function which works with two cell + * bindings where the cell values map directly to the hwirq number + * and linux irq flags. + */ +int irq_domain_translate_twocell(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (WARN_ON(fwspec->param_count < 2)) + return -EINVAL; + *out_hwirq = fwspec->param[0]; + *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + return 0; +} +EXPORT_SYMBOL_GPL(irq_domain_translate_twocell); + int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq, int node, const struct irq_affinity_desc *affinity) { From 5aa5bd563ce041d931c0dc1fc436dd18c27c60a7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 7 Feb 2019 21:16:23 -0500 Subject: [PATCH 1672/4277] genirq: introduce irq_chip_mask_ack_parent() The hierarchical irqchip never before ran into a situation where the parent is not "simple", i.e. does not implement .irq_ack() and .irq_mask() like most, but the qcom-pm8xxx.c happens to implement only .irq_mask_ack(). Since we want to make ssbi-gpio a hierarchical child of this irqchip, it must *also* only implement .irq_mask_ack() and call down to the parent, and for this we of course need irq_chip_mask_ack_parent(). Cc: Marc Zyngier Cc: Thomas Gleixner Acked-by: Marc Zyngier Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- include/linux/irq.h | 1 + kernel/irq/chip.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/linux/irq.h b/include/linux/irq.h index def2b2aac8b1..9a1a67d2e07d 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -605,6 +605,7 @@ extern void irq_chip_disable_parent(struct irq_data *data); extern void irq_chip_ack_parent(struct irq_data *data); extern int irq_chip_retrigger_hierarchy(struct irq_data *data); extern void irq_chip_mask_parent(struct irq_data *data); +extern void irq_chip_mask_ack_parent(struct irq_data *data); extern void irq_chip_unmask_parent(struct irq_data *data); extern void irq_chip_eoi_parent(struct irq_data *data); extern int irq_chip_set_affinity_parent(struct irq_data *data, diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 34e969069488..982b75e127c5 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -1277,6 +1277,17 @@ void irq_chip_mask_parent(struct irq_data *data) } EXPORT_SYMBOL_GPL(irq_chip_mask_parent); +/** + * irq_chip_mask_ack_parent - Mask and acknowledge the parent interrupt + * @data: Pointer to interrupt specific data + */ +void irq_chip_mask_ack_parent(struct irq_data *data) +{ + data = data->parent_data; + data->chip->irq_mask_ack(data); +} +EXPORT_SYMBOL_GPL(irq_chip_mask_ack_parent); + /** * irq_chip_unmask_parent - Unmask the parent interrupt * @data: Pointer to interrupt specific data From 3324a7c1a2273e502a4f3c02a021e1d15ce2c458 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:24 -0500 Subject: [PATCH 1673/4277] mfd: pm8xxx: convert to v2 irq interfaces to support hierarchical IRQ chips Convert the PM8XXX IRQ code to use the version 2 IRQ interface in order to support hierarchical IRQ chips. This is necessary so that ssbi-gpio can be setup as a hierarchical IRQ chip with PM8xxx as the parent. IRQ chips in device tree should be usable from the start without having to make an additional call to gpio[d]_to_irq() to get the proper IRQ on the parent. pm8821_irq_domain_ops and pm8821_irq_domain_map are removed by this patch since the irq_chip is now contained in the pm_irq_data struct, and that allows us to use a common IRQ mapping function. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/qcom-pm8xxx.c | 75 ++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index e6e8d81c15fd..8eb2528793f9 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -70,22 +70,23 @@ #define PM8XXX_NR_IRQS 256 #define PM8821_NR_IRQS 112 +struct pm_irq_data { + int num_irqs; + struct irq_chip *irq_chip; + void (*irq_handler)(struct irq_desc *desc); +}; + struct pm_irq_chip { struct regmap *regmap; spinlock_t pm_irq_lock; struct irq_domain *irqdomain; - unsigned int num_irqs; unsigned int num_blocks; unsigned int num_masters; + const struct pm_irq_data *pm_irq_data; + /* MUST BE AT THE END OF THIS STRUCT */ u8 config[0]; }; -struct pm_irq_data { - int num_irqs; - const struct irq_domain_ops *irq_domain_ops; - void (*irq_handler)(struct irq_desc *desc); -}; - static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int *ip) { @@ -375,21 +376,38 @@ static struct irq_chip pm8xxx_irq_chip = { .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; -static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip, + struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq, unsigned int type) { - struct pm_irq_chip *chip = d->host_data; - - irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); - irq_set_chip_data(irq, chip); + irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip, + chip, handle_level_irq, NULL, NULL); irq_set_noprobe(irq); +} + +static int pm8xxx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm_irq_chip *chip = domain->host_data; + struct irq_fwspec *fwspec = data; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + pm8xxx_irq_domain_map(chip, domain, virq + i, hwirq + i, type); return 0; } static const struct irq_domain_ops pm8xxx_irq_domain_ops = { - .xlate = irq_domain_xlate_twocell, - .map = pm8xxx_irq_domain_map, + .alloc = pm8xxx_irq_domain_alloc, + .free = irq_domain_free_irqs_common, + .translate = irq_domain_translate_twocell, }; static void pm8821_irq_mask_ack(struct irq_data *d) @@ -473,23 +491,6 @@ static struct irq_chip pm8821_irq_chip = { .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; -static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct pm_irq_chip *chip = d->host_data; - - irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq); - irq_set_chip_data(irq, chip); - irq_set_noprobe(irq); - - return 0; -} - -static const struct irq_domain_ops pm8821_irq_domain_ops = { - .xlate = irq_domain_xlate_twocell, - .map = pm8821_irq_domain_map, -}; - static const struct regmap_config ssbi_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -501,13 +502,13 @@ static const struct regmap_config ssbi_regmap_config = { static const struct pm_irq_data pm8xxx_data = { .num_irqs = PM8XXX_NR_IRQS, - .irq_domain_ops = &pm8xxx_irq_domain_ops, + .irq_chip = &pm8xxx_irq_chip, .irq_handler = pm8xxx_irq_handler, }; static const struct pm_irq_data pm8821_data = { .num_irqs = PM8821_NR_IRQS, - .irq_domain_ops = &pm8821_irq_domain_ops, + .irq_chip = &pm8821_irq_chip, .irq_handler = pm8821_irq_handler, }; @@ -571,14 +572,14 @@ static int pm8xxx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); chip->regmap = regmap; - chip->num_irqs = data->num_irqs; - chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); + chip->num_blocks = DIV_ROUND_UP(data->num_irqs, 8); chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); + chip->pm_irq_data = data; spin_lock_init(&chip->pm_irq_lock); chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, data->num_irqs, - data->irq_domain_ops, + &pm8xxx_irq_domain_ops, chip); if (!chip->irqdomain) return -ENODEV; From ee08e24c2e761e6aa3ef2c03cc2004e11fe111b3 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:25 -0500 Subject: [PATCH 1674/4277] mfd: pm8xxx: disassociate old virq if hwirq mapping already exists Check to see if the hwirq is already associated with another virq on this IRQ domain. If so, then disassociate it before associating the hwirq with the new virq. This is a temporary hack that is needed in order to not break git bisect for existing boards. The next patch in this series converts ssbi-gpio to be a hierarchical IRQ chip, then there are several patches to update all of the device tree files, and finally this patch will be reverted within the same patch series. IRQs for ssbi-gpio are all initially setup without an IRQ hierarchy this driver is probed due to the interrupts property in device tree. Once ssbi-gpio is converted to be a hierarchical IRQ chip in the next patch, existing users of gpio[d]_to_irq() will call pmic_gpio_to_irq(), and that will use the new IRQ chip code in ssbi-gpio that sets up the IRQ in an IRQ hierarchy. The hwirq is now associated with two Linux virqs and interrupts will not work as expected. This patch corrects that issue. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/qcom-pm8xxx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index 8eb2528793f9..2f99a98ccee5 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -380,6 +380,12 @@ static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip, struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq, unsigned int type) { + unsigned int old_virq; + + old_virq = irq_find_mapping(domain, hwirq); + if (old_virq) + irq_domain_disassociate(domain, old_virq); + irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip, chip, handle_level_irq, NULL, NULL); irq_set_noprobe(irq); From 9d2b563bc23acfa93e7716b3396fd2f79fa8f0cd Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:26 -0500 Subject: [PATCH 1675/4277] qcom: ssbi-gpio: add support for hierarchical IRQ chip ssbi-gpio did not have any irqchip support so consumers of this in device tree would need to call gpio[d]_to_irq() in order to get the proper IRQ on the underlying PMIC. IRQ chips in device tree should be usable from the start without the consumer having to make an additional call to get the proper IRQ on the parent. This patch adds hierarchical IRQ chip support to the ssbi-gpio code to correct this issue. The constant PM8XXX_GPIO_PHYSICAL_OFFSET is introduced to replace the hardcoded '1' that previously existed in two places in this driver to improve code readability. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 1 + drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 135 +++++++++++++++++++++-- 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 836e9f3eae4c..8e7e4255b7a4 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -149,6 +149,7 @@ config PINCTRL_QCOM_SSBI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index 23641e4d4521..84a232450000 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -55,6 +55,8 @@ #define PM8XXX_MAX_GPIOS 44 +#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 + /* custom pinconf parameters */ #define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) #define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) @@ -99,6 +101,9 @@ struct pm8xxx_gpio { struct pinctrl_desc desc; unsigned npins; + + struct fwnode_handle *fwnode; + struct irq_domain *domain; }; static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { @@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset) if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) { ret = pin->output_value; - } else { + } else if (pin->irq >= 0) { ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state); if (!ret) ret = !!state; - } + } else + ret = -EINVAL; return ret; } @@ -533,7 +539,7 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip, if (flags) *flags = gpio_desc->args[1]; - return gpio_desc->args[0] - 1; + return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; } @@ -541,8 +547,31 @@ static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; + struct irq_fwspec fwspec; + int ret; - return pin->irq; + fwspec.fwnode = pctrl->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + ret = irq_create_fwspec_mapping(&fwspec); + + /* + * Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the + * line level. + */ + pin->irq = ret; + + return ret; +} + +static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); + struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; + + pin->irq = -1; } #ifdef CONFIG_DEBUG_FS @@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s, "no", "high", "medium", "low" }; - seq_printf(s, " gpio%-2d:", offset + 1); + seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); if (pin->disable) { seq_puts(s, " ---"); } else { @@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #endif static const struct gpio_chip pm8xxx_gpio_template = { + .free = pm8xxx_gpio_free, .direction_input = pm8xxx_gpio_direction_input, .direction_output = pm8xxx_gpio_direction_output, .get = pm8xxx_gpio_get, @@ -664,6 +694,68 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, return 0; } +static struct irq_chip pm8xxx_irq_chip = { + .name = "ssbi-gpio", + .irq_mask_ack = irq_chip_mask_ack_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, +}; + +static int pm8xxx_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + + if (fwspec->param_count != 2 || fwspec->param[0] >= pctrl->chip.ngpio) + return -EINVAL; + + *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; + *type = fwspec->param[1]; + + return 0; +} + +static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, + struct pm8xxx_gpio, chip); + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + unsigned int type; + int ret, i; + + ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pm8xxx_irq_chip, pctrl, handle_level_irq, + NULL, NULL); + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = hwirq + 0xc0; + parent_fwspec.param[1] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops pm8xxx_domain_ops = { + .activate = gpiochip_irq_domain_activate, + .alloc = pm8xxx_domain_alloc, + .deactivate = gpiochip_irq_domain_deactivate, + .free = irq_domain_free_irqs_common, + .translate = pm8xxx_domain_translate, +}; + static const struct of_device_id pm8xxx_gpio_of_match[] = { { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, @@ -677,6 +769,8 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); static int pm8xxx_gpio_probe(struct platform_device *pdev) { struct pm8xxx_pin_data *pin_data; + struct irq_domain *parent_domain; + struct device_node *parent_node; struct pinctrl_pin_desc *pins; struct pm8xxx_gpio *pctrl; int ret, i; @@ -713,12 +807,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) for (i = 0; i < pctrl->desc.npins; i++) { pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); - pin_data[i].irq = platform_get_irq(pdev, i); - if (pin_data[i].irq < 0) { - dev_err(&pdev->dev, - "missing interrupts for pin %d\n", i); - return pin_data[i].irq; - } + pin_data[i].irq = -1; ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); if (ret) @@ -749,10 +838,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) pctrl->chip.of_gpio_n_cells = 2; pctrl->chip.label = dev_name(pctrl->dev); pctrl->chip.ngpio = pctrl->npins; + + parent_node = of_irq_find_parent(pctrl->dev->of_node); + if (!parent_node) + return -ENXIO; + + parent_domain = irq_find_host(parent_node); + of_node_put(parent_node); + if (!parent_domain) + return -ENXIO; + + pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node); + pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0, + pctrl->chip.ngpio, + pctrl->fwnode, + &pm8xxx_domain_ops, + &pctrl->chip); + if (!pctrl->domain) + return -ENODEV; + ret = gpiochip_add_data(&pctrl->chip, pctrl); if (ret) { dev_err(&pdev->dev, "failed register gpiochip\n"); - return ret; + goto err_chip_add_data; } /* @@ -782,6 +890,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) unregister_gpiochip: gpiochip_remove(&pctrl->chip); +err_chip_add_data: + irq_domain_remove(pctrl->domain); return ret; } @@ -791,6 +901,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev) struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev); gpiochip_remove(&pctrl->chip); + irq_domain_remove(pctrl->domain); return 0; } From e2f6c8881287cb0689408b244d1a1e651eb0f67d Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:27 -0500 Subject: [PATCH 1676/4277] arm: dts: qcom: apq8064: add interrupt controller properties Add interrupt controller properties now that ssbi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was not tested on any hardware but the same change was tested on an APQ8060 DragonBoard with no issues. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-apq8064.dtsi | 46 ++--------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi index 48c3cf427610..4744fe757cf4 100644 --- a/arch/arm/boot/dts/qcom-apq8064.dtsi +++ b/arch/arm/boot/dts/qcom-apq8064.dtsi @@ -705,50 +705,8 @@ compatible = "qcom,pm8921-gpio", "qcom,ssbi-gpio"; reg = <0x150>; - interrupts = <192 IRQ_TYPE_NONE>, - <193 IRQ_TYPE_NONE>, - <194 IRQ_TYPE_NONE>, - <195 IRQ_TYPE_NONE>, - <196 IRQ_TYPE_NONE>, - <197 IRQ_TYPE_NONE>, - <198 IRQ_TYPE_NONE>, - <199 IRQ_TYPE_NONE>, - <200 IRQ_TYPE_NONE>, - <201 IRQ_TYPE_NONE>, - <202 IRQ_TYPE_NONE>, - <203 IRQ_TYPE_NONE>, - <204 IRQ_TYPE_NONE>, - <205 IRQ_TYPE_NONE>, - <206 IRQ_TYPE_NONE>, - <207 IRQ_TYPE_NONE>, - <208 IRQ_TYPE_NONE>, - <209 IRQ_TYPE_NONE>, - <210 IRQ_TYPE_NONE>, - <211 IRQ_TYPE_NONE>, - <212 IRQ_TYPE_NONE>, - <213 IRQ_TYPE_NONE>, - <214 IRQ_TYPE_NONE>, - <215 IRQ_TYPE_NONE>, - <216 IRQ_TYPE_NONE>, - <217 IRQ_TYPE_NONE>, - <218 IRQ_TYPE_NONE>, - <219 IRQ_TYPE_NONE>, - <220 IRQ_TYPE_NONE>, - <221 IRQ_TYPE_NONE>, - <222 IRQ_TYPE_NONE>, - <223 IRQ_TYPE_NONE>, - <224 IRQ_TYPE_NONE>, - <225 IRQ_TYPE_NONE>, - <226 IRQ_TYPE_NONE>, - <227 IRQ_TYPE_NONE>, - <228 IRQ_TYPE_NONE>, - <229 IRQ_TYPE_NONE>, - <230 IRQ_TYPE_NONE>, - <231 IRQ_TYPE_NONE>, - <232 IRQ_TYPE_NONE>, - <233 IRQ_TYPE_NONE>, - <234 IRQ_TYPE_NONE>, - <235 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; From a796fab2c605d6340512c51c6c3fa1c581357993 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:28 -0500 Subject: [PATCH 1677/4277] arm: dts: qcom: msm8660: add interrupt controller properties Add interrupt controller properties now that ssbi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-msm8660.dtsi | 47 ++--------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi index 70698941f64c..a4eeebcfd63a 100644 --- a/arch/arm/boot/dts/qcom-msm8660.dtsi +++ b/arch/arm/boot/dts/qcom-msm8660.dtsi @@ -285,51 +285,8 @@ compatible = "qcom,pm8058-gpio", "qcom,ssbi-gpio"; reg = <0x150>; - interrupt-parent = <&pm8058>; - interrupts = <192 IRQ_TYPE_NONE>, - <193 IRQ_TYPE_NONE>, - <194 IRQ_TYPE_NONE>, - <195 IRQ_TYPE_NONE>, - <196 IRQ_TYPE_NONE>, - <197 IRQ_TYPE_NONE>, - <198 IRQ_TYPE_NONE>, - <199 IRQ_TYPE_NONE>, - <200 IRQ_TYPE_NONE>, - <201 IRQ_TYPE_NONE>, - <202 IRQ_TYPE_NONE>, - <203 IRQ_TYPE_NONE>, - <204 IRQ_TYPE_NONE>, - <205 IRQ_TYPE_NONE>, - <206 IRQ_TYPE_NONE>, - <207 IRQ_TYPE_NONE>, - <208 IRQ_TYPE_NONE>, - <209 IRQ_TYPE_NONE>, - <210 IRQ_TYPE_NONE>, - <211 IRQ_TYPE_NONE>, - <212 IRQ_TYPE_NONE>, - <213 IRQ_TYPE_NONE>, - <214 IRQ_TYPE_NONE>, - <215 IRQ_TYPE_NONE>, - <216 IRQ_TYPE_NONE>, - <217 IRQ_TYPE_NONE>, - <218 IRQ_TYPE_NONE>, - <219 IRQ_TYPE_NONE>, - <220 IRQ_TYPE_NONE>, - <221 IRQ_TYPE_NONE>, - <222 IRQ_TYPE_NONE>, - <223 IRQ_TYPE_NONE>, - <224 IRQ_TYPE_NONE>, - <225 IRQ_TYPE_NONE>, - <226 IRQ_TYPE_NONE>, - <227 IRQ_TYPE_NONE>, - <228 IRQ_TYPE_NONE>, - <229 IRQ_TYPE_NONE>, - <230 IRQ_TYPE_NONE>, - <231 IRQ_TYPE_NONE>, - <232 IRQ_TYPE_NONE>, - <233 IRQ_TYPE_NONE>, - <234 IRQ_TYPE_NONE>, - <235 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; From 582648f5ef14383c66b26834b9a98ade8b4da74c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:29 -0500 Subject: [PATCH 1678/4277] arm: dts: qcom: mdm9615: add interrupt controller properties Add interrupt controller properties now that ssbi-gpio is a proper hierarchical IRQ chip. The interrupts property is no longer needed so remove it. Note that the IRQs started at 24 instead of 192 like all of the other PMICs. This is the same IRQs as the MPP for this board. qcom-pm8xxx.c doesn't set the shared IRQs so this is highly likely to be a copy and paste error. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- arch/arm/boot/dts/qcom-mdm9615.dtsi | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/arm/boot/dts/qcom-mdm9615.dtsi b/arch/arm/boot/dts/qcom-mdm9615.dtsi index c852b69229c9..0ed6fc3e873c 100644 --- a/arch/arm/boot/dts/qcom-mdm9615.dtsi +++ b/arch/arm/boot/dts/qcom-mdm9615.dtsi @@ -323,13 +323,8 @@ pmicgpio: gpio@150 { compatible = "qcom,pm8018-gpio", "qcom,ssbi-gpio"; - interrupt-parent = <&pmicintc>; - interrupts = <24 IRQ_TYPE_NONE>, - <25 IRQ_TYPE_NONE>, - <26 IRQ_TYPE_NONE>, - <27 IRQ_TYPE_NONE>, - <28 IRQ_TYPE_NONE>, - <29 IRQ_TYPE_NONE>; + interrupt-controller; + #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; }; From da4f07ddc1c3108293e823e6206a49190294c2ec Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 7 Feb 2019 21:16:30 -0500 Subject: [PATCH 1679/4277] ARM: dts: qcom-apq8060: Fix up interrupt parents Before we fixed up the interrupt hierarchy for the SSBI GPIO controller, we had to use the PM8058 directly to pick interrupts. After making the interrupt controller work properly, we can reference the real interrupt parent. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- .../arm/boot/dts/qcom-apq8060-dragonboard.dts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts index 497bb065eb9d..4e6c50d45cb2 100644 --- a/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts +++ b/arch/arm/boot/dts/qcom-apq8060-dragonboard.dts @@ -93,9 +93,8 @@ vdd-supply = <&pm8058_l14>; // 2.85V aset-gpios = <&pm8058_gpio 35 GPIO_ACTIVE_LOW>; capella,aset-resistance-ohms = <100000>; - /* GPIO34 has interrupt 225 on the PM8058 */ /* Trig on both edges - getting close or far away */ - interrupts-extended = <&pm8058 225 IRQ_TYPE_EDGE_BOTH>; + interrupts-extended = <&pm8058_gpio 34 IRQ_TYPE_EDGE_BOTH>; /* MPP05 analog input to the XOADC */ io-channels = <&xoadc 0x00 0x05>; io-channel-names = "aout"; @@ -515,9 +514,8 @@ ak8975@c { compatible = "asahi-kasei,ak8975"; reg = <0x0c>; - /* FIXME: GPIO33 has interrupt 224 on the PM8058 */ - interrupt-parent = <&pm8058>; - interrupts = <224 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&pm8058_gpio>; + interrupts = <33 IRQ_TYPE_EDGE_RISING>; pinctrl-names = "default"; pinctrl-0 = <&dragon_ak8975_gpios>; vid-supply = <&pm8058_lvs0>; // 1.8V @@ -526,9 +524,8 @@ bmp085@77 { compatible = "bosch,bmp085"; reg = <0x77>; - /* FIXME: GPIO16 has interrupt 207 on the PM8058 */ - interrupt-parent = <&pm8058>; - interrupts = <207 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&pm8058_gpio>; + interrupts = <16 IRQ_TYPE_EDGE_RISING>; reset-gpios = <&tlmm 86 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&dragon_bmp085_gpios>; @@ -539,12 +536,11 @@ compatible = "invensense,mpu3050"; reg = <0x68>; /* - * GPIO17 has interrupt 208 on the - * PM8058, it is pulled high by a 10k + * GPIO17 is pulled high by a 10k * resistor to VLOGIC so needs to be * active low/falling edge. */ - interrupts-extended = <&pm8058 208 IRQ_TYPE_EDGE_FALLING>; + interrupts-extended = <&pm8058_gpio 17 IRQ_TYPE_EDGE_FALLING>; pinctrl-names = "default"; pinctrl-0 = <&dragon_mpu3050_gpios>; vlogic-supply = <&pm8058_lvs0>; // 1.8V @@ -589,11 +585,10 @@ compatible = "smsc,lan9221", "smsc,lan9115"; reg = <2 0x0 0x100>; /* - * GPIO7 has interrupt 198 on the PM8058 * The second interrupt is the PME interrupt * for network wakeup, connected to the TLMM. */ - interrupts-extended = <&pm8058 198 IRQ_TYPE_EDGE_FALLING>, + interrupts-extended = <&pm8058_gpio 7 IRQ_TYPE_EDGE_FALLING>, <&tlmm 29 IRQ_TYPE_EDGE_RISING>; reset-gpios = <&tlmm 30 GPIO_ACTIVE_LOW>; vdd33a-supply = <&dragon_veth>; From 1a25d59a55292631a6c601fda5413abc297097b7 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 7 Feb 2019 21:16:31 -0500 Subject: [PATCH 1680/4277] mfd: pm8xxx: revert "disassociate old virq if hwirq mapping already exists" Now that ssbi-gpio is a proper hierarchical IRQ chip, and all in-tree users of device tree have been updated, we can now drop the hack that was introduced to disassociate the old Linux virq if a hwirq mapping already exists. That patch was introduced to not break git bisect for any existing boards. This change was tested on an APQ8060 DragonBoard. Signed-off-by: Brian Masney Tested-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/qcom-pm8xxx.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index 2f99a98ccee5..8eb2528793f9 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -380,12 +380,6 @@ static void pm8xxx_irq_domain_map(struct pm_irq_chip *chip, struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq, unsigned int type) { - unsigned int old_virq; - - old_virq = irq_find_mapping(domain, hwirq); - if (old_virq) - irq_domain_disassociate(domain, old_virq); - irq_domain_set_info(domain, irq, hwirq, chip->pm_irq_data->irq_chip, chip, handle_level_irq, NULL, NULL); irq_set_noprobe(irq); From be3d960b0aeb97438a044d8d3ce75393d198c020 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 12 Feb 2019 17:28:58 +0100 Subject: [PATCH 1681/4277] dt-bindings: clk: add G12A AO Clock and Reset Bindings Add bindings for the Amlogic G12A AO Clock and Reset controllers. Signed-off-by: Neil Armstrong Acked-by: Jerome Brunet Link: https://lkml.kernel.org/r/20190212162859.20743-2-narmstrong@baylibre.com --- .../bindings/clock/amlogic,gxbb-aoclkc.txt | 1 + include/dt-bindings/clock/g12a-aoclkc.h | 34 +++++++++++++++++++ include/dt-bindings/reset/g12a-aoclkc.h | 18 ++++++++++ 3 files changed, 53 insertions(+) create mode 100644 include/dt-bindings/clock/g12a-aoclkc.h create mode 100644 include/dt-bindings/reset/g12a-aoclkc.h diff --git a/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt b/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt index 79511d7bb321..c41f0be5d438 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt +++ b/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt @@ -10,6 +10,7 @@ Required Properties: - GXL (S905X, S905D) : "amlogic,meson-gxl-aoclkc" - GXM (S912) : "amlogic,meson-gxm-aoclkc" - AXG (A113D, A113X) : "amlogic,meson-axg-aoclkc" + - G12A (S905X2, S905D2, S905Y2) : "amlogic,meson-g12a-aoclkc" followed by the common "amlogic,meson-gx-aoclkc" - clocks: list of clock phandle, one for each entry clock-names. - clock-names: should contain the following: diff --git a/include/dt-bindings/clock/g12a-aoclkc.h b/include/dt-bindings/clock/g12a-aoclkc.h new file mode 100644 index 000000000000..8db01ffbeb06 --- /dev/null +++ b/include/dt-bindings/clock/g12a-aoclkc.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright (c) 2016 BayLibre, SAS + * Author: Neil Armstrong + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Qiufang Dai + */ + +#ifndef DT_BINDINGS_CLOCK_AMLOGIC_MESON_G12A_AOCLK +#define DT_BINDINGS_CLOCK_AMLOGIC_MESON_G12A_AOCLK + +#define CLKID_AO_AHB 0 +#define CLKID_AO_IR_IN 1 +#define CLKID_AO_I2C_M0 2 +#define CLKID_AO_I2C_S0 3 +#define CLKID_AO_UART 4 +#define CLKID_AO_PROD_I2C 5 +#define CLKID_AO_UART2 6 +#define CLKID_AO_IR_OUT 7 +#define CLKID_AO_SAR_ADC 8 +#define CLKID_AO_MAILBOX 9 +#define CLKID_AO_M3 10 +#define CLKID_AO_AHB_SRAM 11 +#define CLKID_AO_RTI 12 +#define CLKID_AO_M4_FCLK 13 +#define CLKID_AO_M4_HCLK 14 +#define CLKID_AO_CLK81 15 +#define CLKID_AO_SAR_ADC_CLK 18 +#define CLKID_AO_32K 23 +#define CLKID_AO_CEC 27 +#define CLKID_AO_CTS_RTC_OSCIN 28 + +#endif diff --git a/include/dt-bindings/reset/g12a-aoclkc.h b/include/dt-bindings/reset/g12a-aoclkc.h new file mode 100644 index 000000000000..bd2e2337135c --- /dev/null +++ b/include/dt-bindings/reset/g12a-aoclkc.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright (c) 2016 BayLibre, SAS + * Author: Neil Armstrong + */ + +#ifndef DT_BINDINGS_RESET_AMLOGIC_MESON_G12A_AOCLK +#define DT_BINDINGS_RESET_AMLOGIC_MESON_G12A_AOCLK + +#define RESET_AO_IR_IN 0 +#define RESET_AO_UART 1 +#define RESET_AO_I2C_M 2 +#define RESET_AO_I2C_S 3 +#define RESET_AO_SAR_ADC 4 +#define RESET_AO_UART2 5 +#define RESET_AO_IR_OUT 6 + +#endif From 042f01bb7f80012e19369b7f8a52fc5b74f4856b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 12 Feb 2019 17:28:59 +0100 Subject: [PATCH 1682/4277] clk: meson: Add G12A AO Clock + Reset Controller Add the Amlogic G12A AO Clock and Reset controller driver handling generation of Always-On clocks : - AO Clocks and Reset for Always-On modules - 32K Generation for USB and CEC - SAR ADC controller clock Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet Acked-by: Jerome Brunet Link: https://lkml.kernel.org/r/20190212162859.20743-3-narmstrong@baylibre.com --- drivers/clk/meson/Kconfig | 2 + drivers/clk/meson/Makefile | 2 +- drivers/clk/meson/g12a-aoclk.c | 454 +++++++++++++++++++++++++++++++++ drivers/clk/meson/g12a-aoclk.h | 34 +++ 4 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/meson/g12a-aoclk.c create mode 100644 drivers/clk/meson/g12a-aoclk.h diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index b72439315340..3858747f5438 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -98,8 +98,10 @@ config COMMON_CLK_G12A bool depends on ARCH_MESON select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_MESON_DUALDIV select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL + select COMMON_CLK_MESON_AO_CLKC select COMMON_CLK_MESON_EE_CLKC select MFD_SYSCON help diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 32d17f18541e..021fc290e749 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -16,5 +16,5 @@ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o -obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o +obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o diff --git a/drivers/clk/meson/g12a-aoclk.c b/drivers/clk/meson/g12a-aoclk.c new file mode 100644 index 000000000000..1994e735396b --- /dev/null +++ b/drivers/clk/meson/g12a-aoclk.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson-AXG Clock Controller Driver + * + * Copyright (c) 2016 Baylibre SAS. + * Author: Michael Turquette + * + * Copyright (c) 2019 Baylibre SAS. + * Author: Neil Armstrong + */ +#include +#include +#include +#include +#include "meson-aoclk.h" +#include "g12a-aoclk.h" + +#include "clk-regmap.h" +#include "clk-dualdiv.h" + +#define IN_PREFIX "ao-in-" + +/* + * AO Configuration Clock registers offsets + * Register offsets from the data sheet must be multiplied by 4. + */ +#define AO_RTI_STATUS_REG3 0x0C +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_CLK_GATE0 0x4c +#define AO_CLK_GATE0_SP 0x50 +#define AO_OSCIN_CNTL 0x58 +#define AO_CEC_CLK_CNTL_REG0 0x74 +#define AO_CEC_CLK_CNTL_REG1 0x78 +#define AO_SAR_CLK 0x90 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + +/* + * Like every other peripheral clock gate in Amlogic Clock drivers, + * we are using CLK_IGNORE_UNUSED here, so we keep the state of the + * bootloader. The goal is to remove this flag at some point. + * Actually removing it will require some extensive test to be done safely. + */ +#define AXG_AO_GATE(_name, _reg, _bit) \ +static struct clk_regmap g12a_aoclk_##_name = { \ + .data = &(struct clk_regmap_gate_data) { \ + .offset = (_reg), \ + .bit_idx = (_bit), \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "g12a_ao_" #_name, \ + .ops = &clk_regmap_gate_ops, \ + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk" }, \ + .num_parents = 1, \ + .flags = CLK_IGNORE_UNUSED, \ + }, \ +} + +AXG_AO_GATE(ahb, AO_CLK_GATE0, 0); +AXG_AO_GATE(ir_in, AO_CLK_GATE0, 1); +AXG_AO_GATE(i2c_m0, AO_CLK_GATE0, 2); +AXG_AO_GATE(i2c_s0, AO_CLK_GATE0, 3); +AXG_AO_GATE(uart, AO_CLK_GATE0, 4); +AXG_AO_GATE(prod_i2c, AO_CLK_GATE0, 5); +AXG_AO_GATE(uart2, AO_CLK_GATE0, 6); +AXG_AO_GATE(ir_out, AO_CLK_GATE0, 7); +AXG_AO_GATE(saradc, AO_CLK_GATE0, 8); +AXG_AO_GATE(mailbox, AO_CLK_GATE0_SP, 0); +AXG_AO_GATE(m3, AO_CLK_GATE0_SP, 1); +AXG_AO_GATE(ahb_sram, AO_CLK_GATE0_SP, 2); +AXG_AO_GATE(rti, AO_CLK_GATE0_SP, 3); +AXG_AO_GATE(m4_fclk, AO_CLK_GATE0_SP, 4); +AXG_AO_GATE(m4_hclk, AO_CLK_GATE0_SP, 5); + +static struct clk_regmap g12a_aoclk_cts_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTI_PWR_CNTL_REG0, + .bit_idx = 14, + }, + .hw.init = &(struct clk_init_data){ + .name = "cts_oscin", + .ops = &clk_regmap_gate_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal" }, + .num_parents = 1, + }, +}; + +static const struct meson_clk_dualdiv_param g12a_32k_div_table[] = { + { + .dual = 1, + .n1 = 733, + .m1 = 8, + .n2 = 732, + .m2 = 11, + }, {} +}; + +/* 32k_by_oscin clock */ + +static struct clk_regmap g12a_aoclk_32k_by_oscin_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_oscin" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_32k_by_oscin_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_RTC_ALT_CLK_CNTL1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_RTC_ALT_CLK_CNTL0, + .shift = 28, + .width = 1, + }, + .table = g12a_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_32k_by_oscin_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTC_ALT_CLK_CNTL1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_div", + "g12a_ao_32k_by_oscin_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_32k_by_oscin = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_RTC_ALT_CLK_CNTL0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_32k_by_oscin", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +/* cec clock */ + +static struct clk_regmap g12a_aoclk_cec_pre = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_CEC_CLK_CNTL_REG0, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec_pre", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "cts_oscin" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_cec_div = { + .data = &(struct meson_clk_dualdiv_data){ + .n1 = { + .reg_off = AO_CEC_CLK_CNTL_REG0, + .shift = 0, + .width = 12, + }, + .n2 = { + .reg_off = AO_CEC_CLK_CNTL_REG0, + .shift = 12, + .width = 12, + }, + .m1 = { + .reg_off = AO_CEC_CLK_CNTL_REG1, + .shift = 0, + .width = 12, + }, + .m2 = { + .reg_off = AO_CEC_CLK_CNTL_REG1, + .shift = 12, + .width = 12, + }, + .dual = { + .reg_off = AO_CEC_CLK_CNTL_REG0, + .shift = 28, + .width = 1, + }, + .table = g12a_32k_div_table, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec_div", + .ops = &meson_clk_dualdiv_ops, + .parent_names = (const char *[]){ "g12a_ao_cec_pre" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap g12a_aoclk_cec_sel = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_CEC_CLK_CNTL_REG1, + .mask = 0x1, + .shift = 24, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec_sel", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "g12a_ao_cec_div", + "g12a_ao_cec_pre" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_cec = { + .data = &(struct clk_regmap_gate_data){ + .offset = AO_CEC_CLK_CNTL_REG0, + .bit_idx = 30, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cec", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "g12a_ao_cec_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_cts_rtc_oscin = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 10, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_cts_rtc_oscin", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "g12a_ao_32k_by_oscin", + IN_PREFIX "ext_32k-0" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_clk81 = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_RTI_PWR_CNTL_REG0, + .mask = 0x1, + .shift = 8, + .flags = CLK_MUX_ROUND_CLOSEST, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_clk81", + .ops = &clk_regmap_mux_ro_ops, + .parent_names = (const char *[]){ IN_PREFIX "mpeg-clk", + "g12a_ao_cts_rtc_oscin"}, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_saradc_mux = { + .data = &(struct clk_regmap_mux_data) { + .offset = AO_SAR_CLK, + .mask = 0x3, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_saradc_mux", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ IN_PREFIX "xtal", + "g12a_ao_clk81" }, + .num_parents = 2, + }, +}; + +static struct clk_regmap g12a_aoclk_saradc_div = { + .data = &(struct clk_regmap_div_data) { + .offset = AO_SAR_CLK, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_saradc_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "g12a_ao_saradc_mux" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap g12a_aoclk_saradc_gate = { + .data = &(struct clk_regmap_gate_data) { + .offset = AO_SAR_CLK, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "g12a_ao_saradc_gate", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "g12a_ao_saradc_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const unsigned int g12a_aoclk_reset[] = { + [RESET_AO_IR_IN] = 16, + [RESET_AO_UART] = 17, + [RESET_AO_I2C_M] = 18, + [RESET_AO_I2C_S] = 19, + [RESET_AO_SAR_ADC] = 20, + [RESET_AO_UART2] = 22, + [RESET_AO_IR_OUT] = 23, +}; + +static struct clk_regmap *g12a_aoclk_regmap[] = { + &g12a_aoclk_ahb, + &g12a_aoclk_ir_in, + &g12a_aoclk_i2c_m0, + &g12a_aoclk_i2c_s0, + &g12a_aoclk_uart, + &g12a_aoclk_prod_i2c, + &g12a_aoclk_uart2, + &g12a_aoclk_ir_out, + &g12a_aoclk_saradc, + &g12a_aoclk_mailbox, + &g12a_aoclk_m3, + &g12a_aoclk_ahb_sram, + &g12a_aoclk_rti, + &g12a_aoclk_m4_fclk, + &g12a_aoclk_m4_hclk, + &g12a_aoclk_cts_oscin, + &g12a_aoclk_32k_by_oscin_pre, + &g12a_aoclk_32k_by_oscin_div, + &g12a_aoclk_32k_by_oscin_sel, + &g12a_aoclk_32k_by_oscin, + &g12a_aoclk_cec_pre, + &g12a_aoclk_cec_div, + &g12a_aoclk_cec_sel, + &g12a_aoclk_cec, + &g12a_aoclk_cts_rtc_oscin, + &g12a_aoclk_clk81, + &g12a_aoclk_saradc_mux, + &g12a_aoclk_saradc_div, + &g12a_aoclk_saradc_gate, +}; + +static const struct clk_hw_onecell_data g12a_aoclk_onecell_data = { + .hws = { + [CLKID_AO_AHB] = &g12a_aoclk_ahb.hw, + [CLKID_AO_IR_IN] = &g12a_aoclk_ir_in.hw, + [CLKID_AO_I2C_M0] = &g12a_aoclk_i2c_m0.hw, + [CLKID_AO_I2C_S0] = &g12a_aoclk_i2c_s0.hw, + [CLKID_AO_UART] = &g12a_aoclk_uart.hw, + [CLKID_AO_PROD_I2C] = &g12a_aoclk_prod_i2c.hw, + [CLKID_AO_UART2] = &g12a_aoclk_uart2.hw, + [CLKID_AO_IR_OUT] = &g12a_aoclk_ir_out.hw, + [CLKID_AO_SAR_ADC] = &g12a_aoclk_saradc.hw, + [CLKID_AO_MAILBOX] = &g12a_aoclk_mailbox.hw, + [CLKID_AO_M3] = &g12a_aoclk_m3.hw, + [CLKID_AO_AHB_SRAM] = &g12a_aoclk_ahb_sram.hw, + [CLKID_AO_RTI] = &g12a_aoclk_rti.hw, + [CLKID_AO_M4_FCLK] = &g12a_aoclk_m4_fclk.hw, + [CLKID_AO_M4_HCLK] = &g12a_aoclk_m4_hclk.hw, + [CLKID_AO_CLK81] = &g12a_aoclk_clk81.hw, + [CLKID_AO_SAR_ADC_SEL] = &g12a_aoclk_saradc_mux.hw, + [CLKID_AO_SAR_ADC_DIV] = &g12a_aoclk_saradc_div.hw, + [CLKID_AO_SAR_ADC_CLK] = &g12a_aoclk_saradc_gate.hw, + [CLKID_AO_CTS_OSCIN] = &g12a_aoclk_cts_oscin.hw, + [CLKID_AO_32K_PRE] = &g12a_aoclk_32k_by_oscin_pre.hw, + [CLKID_AO_32K_DIV] = &g12a_aoclk_32k_by_oscin_div.hw, + [CLKID_AO_32K_SEL] = &g12a_aoclk_32k_by_oscin_sel.hw, + [CLKID_AO_32K] = &g12a_aoclk_32k_by_oscin.hw, + [CLKID_AO_CEC_PRE] = &g12a_aoclk_cec_pre.hw, + [CLKID_AO_CEC_DIV] = &g12a_aoclk_cec_div.hw, + [CLKID_AO_CEC_SEL] = &g12a_aoclk_cec_sel.hw, + [CLKID_AO_CEC] = &g12a_aoclk_cec.hw, + [CLKID_AO_CTS_RTC_OSCIN] = &g12a_aoclk_cts_rtc_oscin.hw, + }, + .num = NR_CLKS, +}; + +static const struct meson_aoclk_input g12a_aoclk_inputs[] = { + { .name = "xtal", .required = true }, + { .name = "mpeg-clk", .required = true }, + { .name = "ext-32k-0", .required = false }, +}; + +static const struct meson_aoclk_data g12a_aoclkc_data = { + .reset_reg = AO_RTI_GEN_CNTL_REG0, + .num_reset = ARRAY_SIZE(g12a_aoclk_reset), + .reset = g12a_aoclk_reset, + .num_clks = ARRAY_SIZE(g12a_aoclk_regmap), + .clks = g12a_aoclk_regmap, + .hw_data = &g12a_aoclk_onecell_data, + .inputs = g12a_aoclk_inputs, + .num_inputs = ARRAY_SIZE(g12a_aoclk_inputs), + .input_prefix = IN_PREFIX, +}; + +static const struct of_device_id g12a_aoclkc_match_table[] = { + { + .compatible = "amlogic,meson-g12a-aoclkc", + .data = &g12a_aoclkc_data, + }, + { } +}; + +static struct platform_driver g12a_aoclkc_driver = { + .probe = meson_aoclkc_probe, + .driver = { + .name = "g12a-aoclkc", + .of_match_table = g12a_aoclkc_match_table, + }, +}; + +builtin_platform_driver(g12a_aoclkc_driver); diff --git a/drivers/clk/meson/g12a-aoclk.h b/drivers/clk/meson/g12a-aoclk.h new file mode 100644 index 000000000000..04b0d5506641 --- /dev/null +++ b/drivers/clk/meson/g12a-aoclk.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#ifndef __G12A_AOCLKC_H +#define __G12A_AOCLKC_H + +/* + * CLKID index values + * + * These indices are entirely contrived and do not map onto the hardware. + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/g12a-aoclkc.h. Only the clocks ids we don't want + * to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. + */ +#define CLKID_AO_SAR_ADC_SEL 16 +#define CLKID_AO_SAR_ADC_DIV 17 +#define CLKID_AO_CTS_OSCIN 19 +#define CLKID_AO_32K_PRE 20 +#define CLKID_AO_32K_DIV 21 +#define CLKID_AO_32K_SEL 22 +#define CLKID_AO_CEC_PRE 24 +#define CLKID_AO_CEC_DIV 25 +#define CLKID_AO_CEC_SEL 26 + +#define NR_CLKS 29 + +#include +#include + +#endif /* __G12A_AOCLKC_H */ From 40d08f774c17add49c1db0083390b6a672030b7d Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 10 Feb 2019 23:26:02 +0100 Subject: [PATCH 1683/4277] dt-bindings: clock: meson8b: add APB clock definition Commit 8e1dd17c8b0e3f ("dt-bindings: clock: meson8b: export the CPU post dividers") added a clock with the name "ABP". The actual name of this clock is "APB". Add a new #define with the same ID but the correct name. The old #define will be dropped in a follow-up patch because each commit in the tree must compile on it's own (the old #define is still used by the clock controller driver). Fixes: 8e1dd17c8b0e3f ("dt-bindings: clock: meson8b: export the CPU post dividers") Signed-off-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Acked-by: Jerome Brunet Link: https://lkml.kernel.org/r/20190210222603.6404-2-martin.blumenstingl@googlemail.com --- include/dt-bindings/clock/meson8b-clkc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h index 5fe2923382d0..8067077a62ca 100644 --- a/include/dt-bindings/clock/meson8b-clkc.h +++ b/include/dt-bindings/clock/meson8b-clkc.h @@ -104,6 +104,7 @@ #define CLKID_MPLL2 95 #define CLKID_NAND_CLK 112 #define CLKID_ABP 124 +#define CLKID_APB 124 #define CLKID_PERIPH 126 #define CLKID_AXI 128 #define CLKID_L2_DRAM 130 From c5f09e6bd8a7537beef8ee53cd161a06b04de271 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 10 Feb 2019 23:26:03 +0100 Subject: [PATCH 1684/4277] clk: meson: meson8b: fix the naming of the APB clocks Fix a typo in the APB clock names by renaming them from "abp" to "apb". No functional changes. Fixes: a7d19b05ce817d ("clk: meson: meson8b: add the CPU clock post divider clocks") Signed-off-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Acked-by: Jerome Brunet Link: https://lkml.kernel.org/r/20190210222603.6404-2-martin.blumenstingl@googlemail.com --- drivers/clk/meson/meson8b.c | 26 +++++++++++++------------- drivers/clk/meson/meson8b.h | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 23b1e355a849..576ad42252d0 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -804,16 +804,16 @@ static struct clk_fixed_factor meson8b_cpu_clk_div8 = { }, }; -static u32 mux_table_abp[] = { 1, 2, 3, 4, 5, 6, 7 }; -static struct clk_regmap meson8b_abp_clk_sel = { +static u32 mux_table_apb[] = { 1, 2, 3, 4, 5, 6, 7 }; +static struct clk_regmap meson8b_apb_clk_sel = { .data = &(struct clk_regmap_mux_data){ .offset = HHI_SYS_CPU_CLK_CNTL1, .mask = 0x7, .shift = 3, - .table = mux_table_abp, + .table = mux_table_apb, }, .hw.init = &(struct clk_init_data){ - .name = "abp_clk_sel", + .name = "apb_clk_sel", .ops = &clk_regmap_mux_ops, .parent_names = (const char *[]){ "cpu_clk_div2", "cpu_clk_div3", @@ -826,16 +826,16 @@ static struct clk_regmap meson8b_abp_clk_sel = { }, }; -static struct clk_regmap meson8b_abp_clk_gate = { +static struct clk_regmap meson8b_apb_clk_gate = { .data = &(struct clk_regmap_gate_data){ .offset = HHI_SYS_CPU_CLK_CNTL1, .bit_idx = 16, .flags = CLK_GATE_SET_TO_DISABLE, }, .hw.init = &(struct clk_init_data){ - .name = "abp_clk_dis", + .name = "apb_clk_dis", .ops = &clk_regmap_gate_ro_ops, - .parent_names = (const char *[]){ "abp_clk_sel" }, + .parent_names = (const char *[]){ "apb_clk_sel" }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, }, @@ -1911,8 +1911,8 @@ static struct clk_hw_onecell_data meson8_hw_onecell_data = { [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw, [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw, [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw, - [CLKID_ABP_SEL] = &meson8b_abp_clk_sel.hw, - [CLKID_ABP] = &meson8b_abp_clk_gate.hw, + [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw, + [CLKID_APB] = &meson8b_apb_clk_gate.hw, [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw, [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw, [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw, @@ -2093,8 +2093,8 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_CPU_CLK_DIV6] = &meson8b_cpu_clk_div6.hw, [CLKID_CPU_CLK_DIV7] = &meson8b_cpu_clk_div7.hw, [CLKID_CPU_CLK_DIV8] = &meson8b_cpu_clk_div8.hw, - [CLKID_ABP_SEL] = &meson8b_abp_clk_sel.hw, - [CLKID_ABP] = &meson8b_abp_clk_gate.hw, + [CLKID_APB_SEL] = &meson8b_apb_clk_sel.hw, + [CLKID_APB] = &meson8b_apb_clk_gate.hw, [CLKID_PERIPH_SEL] = &meson8b_periph_clk_sel.hw, [CLKID_PERIPH] = &meson8b_periph_clk_gate.hw, [CLKID_AXI_SEL] = &meson8b_axi_clk_sel.hw, @@ -2262,8 +2262,8 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = { &meson8b_fixed_pll_dco, &meson8b_hdmi_pll_dco, &meson8b_sys_pll_dco, - &meson8b_abp_clk_sel, - &meson8b_abp_clk_gate, + &meson8b_apb_clk_sel, + &meson8b_apb_clk_gate, &meson8b_periph_clk_sel, &meson8b_periph_clk_gate, &meson8b_axi_clk_sel, diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index f212e2304ff5..b8c58faeae52 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -92,7 +92,7 @@ #define CLKID_CPU_CLK_DIV6 120 #define CLKID_CPU_CLK_DIV7 121 #define CLKID_CPU_CLK_DIV8 122 -#define CLKID_ABP_SEL 123 +#define CLKID_APB_SEL 123 #define CLKID_PERIPH_SEL 125 #define CLKID_AXI_SEL 127 #define CLKID_L2_DRAM_SEL 129 From c2df3de0d07e1d678f8269b195030391b647c1b6 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 8 Feb 2019 11:40:46 +0100 Subject: [PATCH 1685/4277] gpio: zynq: properly support runtime PM for GPIO used as interrupts The Zynq GPIO driver currently implements runtime PM by: - Enabling runtime PM support in ->probe() and letting the runtime PM reference counter drop to zero at the end of ->probe(). - Increasing the runtime PM reference counter in ->request() and decreasing it in ->free(). However, the latter is not sufficient: when a GPIO is used as an interrupt, ->request() and ->free() are not called. Due to this, the runtime PM counter remains to zero when the only GPIOs in use are used as interrupts, causing them to simply not work. To address this problem, this commit implement the ->irq_request_resources() and ->irq_release_resources() hooks, ensuring that the runtime PM counter is properly incremented/decremented. Since we override the default hooks, we keep the existing behavior by making sure they call gpiochip_reqres_irq() / gpiochip_relres_irq() respectively. Signed-off-by: Thomas Petazzoni Reviewed-by: Shubhrajyoti Datta Signed-off-by: Linus Walleij --- drivers/gpio/gpio-zynq.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index b3b4edcdffe0..00ff7b1fa8a1 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -555,6 +555,26 @@ static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int zynq_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int ret; + + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) + return ret; + + return gpiochip_reqres_irq(chip, d->hwirq); +} + +static void zynq_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + gpiochip_relres_irq(chip, d->hwirq); + pm_runtime_put(chip->parent); +} + /* irq chip descriptor */ static struct irq_chip zynq_gpio_level_irqchip = { .name = DRIVER_NAME, @@ -564,6 +584,8 @@ static struct irq_chip zynq_gpio_level_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED | IRQCHIP_MASK_ON_SUSPEND, }; @@ -576,6 +598,8 @@ static struct irq_chip zynq_gpio_edge_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_MASK_ON_SUSPEND, }; From 5340f23df8fe27a270af3fa1a93cd07293d23dd9 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Feb 2019 12:50:01 -0600 Subject: [PATCH 1686/4277] gpio: sprd: Add missing break in switch statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following warning by adding a missing break: drivers/gpio/gpio-eic-sprd.c: In function ‘sprd_eic_irq_set_type’: drivers/gpio/gpio-eic-sprd.c:403:3: warning: this statement may fall through [-Wimplicit-fallthrough=] switch (flow_type) { ^~~~~~ drivers/gpio/gpio-eic-sprd.c:435:2: note: here default: ^~~~~~~ This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Baolin Wang Signed-off-by: Linus Walleij --- drivers/gpio/gpio-eic-sprd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index e0d6a0a7bc69..64cc87d91790 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -420,6 +420,7 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) default: return -ENOTSUPP; } + break; default: dev_err(chip->parent, "Unsupported EIC type.\n"); return -ENOTSUPP; From 726dabfde6aa35a4f1508e235ae37edbbf9fbc65 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Mon, 11 Feb 2019 17:04:00 +0800 Subject: [PATCH 1687/4277] PCI: endpoint: functions: Use memcpy_fromio()/memcpy_toio() Functions copying from/to IO addresses should use the memcpy_fromio()/memcpy_toio() API rather than plain memcpy(). Fix the issue detected through the sparse tool. Fixes: 349e7a85b25f ("PCI: endpoint: functions: Add an EP function to test PCI") Suggested-by: Kishon Vijay Abraham I Signed-off-by: Wen Yang [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I CC: Lorenzo Pieralisi CC: Bjorn Helgaas CC: Gustavo Pimentel CC: Niklas Cassel CC: Greg Kroah-Hartman CC: Cyrille Pitchen CC: linux-pci@vger.kernel.org CC: linux-kernel@vger.kernel.org --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 3e86fa3c7da3..4bbd26e8a9e2 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -175,7 +175,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) goto err_map_addr; } - memcpy(buf, src_addr, reg->size); + memcpy_fromio(buf, src_addr, reg->size); crc32 = crc32_le(~0, buf, reg->size); if (crc32 != reg->checksum) @@ -230,7 +230,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) get_random_bytes(buf, reg->size); reg->checksum = crc32_le(~0, buf, reg->size); - memcpy(dst_addr, buf, reg->size); + memcpy_toio(dst_addr, buf, reg->size); /* * wait 1ms inorder for the write to complete. Without this delay L3 From 993a0b2aec52754f0897b1dab4c453be8217cae5 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 30 Jan 2019 14:01:57 -0500 Subject: [PATCH 1688/4277] ovl: Do not lose security.capability xattr over metadata file copy-up If a file has been copied up metadata only, and later data is copied up, upper loses any security.capability xattr it has (underlying filesystem clears it as upon file write). From a user's point of view, this is just a file copy-up and that should not result in losing security.capability xattr. Hence, before data copy up, save security.capability xattr (if any) and restore it on upper after data copy up is complete. Signed-off-by: Vivek Goyal Reviewed-by: Amir Goldstein Fixes: 0c2888749363 ("ovl: A new xattr OVL_XATTR_METACOPY for file on upper") Cc: # v4.19+ Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 28 ++++++++++++++++-- fs/overlayfs/overlayfs.h | 2 ++ fs/overlayfs/util.c | 63 +++++++++++++++++++++++++--------------- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 48119b23375b..68b3303e4b46 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -742,6 +742,8 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) { struct path upperpath, datapath; int err; + char *capability = NULL; + ssize_t uninitialized_var(cap_size); ovl_path_upper(c->dentry, &upperpath); if (WARN_ON(upperpath.dentry == NULL)) @@ -751,15 +753,37 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) if (WARN_ON(datapath.dentry == NULL)) return -EIO; + if (c->stat.size) { + err = cap_size = ovl_getxattr(upperpath.dentry, XATTR_NAME_CAPS, + &capability, 0); + if (err < 0 && err != -ENODATA) + goto out; + } + err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size); if (err) - return err; + goto out_free; + + /* + * Writing to upper file will clear security.capability xattr. We + * don't want that to happen for normal copy-up operation. + */ + if (capability) { + err = ovl_do_setxattr(upperpath.dentry, XATTR_NAME_CAPS, + capability, cap_size, 0); + if (err) + goto out_free; + } + err = vfs_removexattr(upperpath.dentry, OVL_XATTR_METACOPY); if (err) - return err; + goto out_free; ovl_set_upperdata(d_inode(c->dentry)); +out_free: + kfree(capability); +out: return err; } diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 5e45cb3630a0..9c6018287d57 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -277,6 +277,8 @@ int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); int ovl_check_metacopy_xattr(struct dentry *dentry); bool ovl_is_metacopy_dentry(struct dentry *dentry); char *ovl_get_redirect_xattr(struct dentry *dentry, int padding); +ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value, + size_t padding); static inline bool ovl_is_impuredir(struct dentry *dentry) { diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 7c01327b1852..4035e640f402 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -863,28 +863,49 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry) return (oe->numlower > 1); } +ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value, + size_t padding) +{ + ssize_t res; + char *buf = NULL; + + res = vfs_getxattr(dentry, name, NULL, 0); + if (res < 0) { + if (res == -ENODATA || res == -EOPNOTSUPP) + return -ENODATA; + goto fail; + } + + if (res != 0) { + buf = kzalloc(res + padding, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + res = vfs_getxattr(dentry, name, buf, res); + if (res < 0) + goto fail; + } + *value = buf; + + return res; + +fail: + pr_warn_ratelimited("overlayfs: failed to get xattr %s: err=%zi)\n", + name, res); + kfree(buf); + return res; +} + char *ovl_get_redirect_xattr(struct dentry *dentry, int padding) { int res; char *s, *next, *buf = NULL; - res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0); - if (res < 0) { - if (res == -ENODATA || res == -EOPNOTSUPP) - return NULL; - goto fail; - } - - buf = kzalloc(res + padding + 1, GFP_KERNEL); - if (!buf) - return ERR_PTR(-ENOMEM); - - if (res == 0) - goto invalid; - - res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res); + res = ovl_getxattr(dentry, OVL_XATTR_REDIRECT, &buf, padding + 1); + if (res == -ENODATA) + return NULL; if (res < 0) - goto fail; + return ERR_PTR(res); if (res == 0) goto invalid; @@ -900,15 +921,9 @@ char *ovl_get_redirect_xattr(struct dentry *dentry, int padding) } return buf; - -err_free: - kfree(buf); - return ERR_PTR(res); -fail: - pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res); - goto err_free; invalid: pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf); res = -EINVAL; - goto err_free; + kfree(buf); + return ERR_PTR(res); } From 2fe93bd43264242e4568763d1b183b1cc58066ff Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 16 Jan 2019 10:27:59 +0100 Subject: [PATCH 1689/4277] fuse: extract fuse_find_writeback() helper Call this from fuse_range_is_writeback() and fuse_writepage_in_flight(). Turn a BUG_ON() into a WARN_ON() in the process. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 52 ++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a59c16bd90ac..927d40dec376 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -329,6 +329,24 @@ u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id) return (u64) v0 + ((u64) v1 << 32); } +static struct fuse_req *fuse_find_writeback(struct fuse_inode *fi, + pgoff_t idx_from, pgoff_t idx_to) +{ + struct fuse_req *req; + + list_for_each_entry(req, &fi->writepages, writepages_entry) { + pgoff_t curr_index; + + WARN_ON(get_fuse_inode(req->inode) != fi); + curr_index = req->misc.write.in.offset >> PAGE_SHIFT; + if (idx_from < curr_index + req->num_pages && + curr_index <= idx_to) { + return req; + } + } + return NULL; +} + /* * Check if any page in a range is under writeback * @@ -340,21 +358,10 @@ static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from, { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_req *req; - bool found = false; + bool found; spin_lock(&fc->lock); - list_for_each_entry(req, &fi->writepages, writepages_entry) { - pgoff_t curr_index; - - BUG_ON(req->inode != inode); - curr_index = req->misc.write.in.offset >> PAGE_SHIFT; - if (idx_from < curr_index + req->num_pages && - curr_index <= idx_to) { - found = true; - break; - } - } + found = fuse_find_writeback(fi, idx_from, idx_to); spin_unlock(&fc->lock); return found; @@ -1744,25 +1751,17 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, struct fuse_inode *fi = get_fuse_inode(new_req->inode); struct fuse_req *tmp; struct fuse_req *old_req; - bool found = false; pgoff_t curr_index; BUG_ON(new_req->num_pages != 0); spin_lock(&fc->lock); list_del(&new_req->writepages_entry); - list_for_each_entry(old_req, &fi->writepages, writepages_entry) { - BUG_ON(old_req->inode != new_req->inode); - curr_index = old_req->misc.write.in.offset >> PAGE_SHIFT; - if (curr_index <= page->index && - page->index < curr_index + old_req->num_pages) { - found = true; - break; - } - } - if (!found) { + old_req = fuse_find_writeback(fi, page->index, page->index); + if (!old_req) { list_add(&new_req->writepages_entry, &fi->writepages); - goto out_unlock; + spin_unlock(&fc->lock); + return false; } new_req->num_pages = 1; @@ -1791,10 +1790,9 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, new_req->misc.write.next = old_req->misc.write.next; old_req->misc.write.next = new_req; } -out_unlock: spin_unlock(&fc->lock); out: - return found; + return true; } static int fuse_writepages_fill(struct page *page, From 7f305ca1928d8b7db69f428b10988a3aa3e81053 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 16 Jan 2019 10:27:59 +0100 Subject: [PATCH 1690/4277] fuse: clean up fuse_writepage_in_flight() Restructure the function to better separate the locked and the unlocked parts. Use the "old_req" local variable to mean only the queued request, and not any auxiliary requests added onto its misc.write.next list. These changes are in preparation for the following patch. Also turn BUG_ON instances into WARN_ON and add a header comment explaining what the function does. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 927d40dec376..8342df29815d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1744,6 +1744,13 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) end_page_writeback(data->orig_pages[i]); } +/* + * First recheck under fi->lock if the offending offset is still under + * writeback. If yes, then iterate write requests, to see if there's one + * already added for a page at this offset. If there's none, then insert this + * new request onto the auxiliary list, otherwise reuse the existing one by + * copying the new page contents over to the old temporary page. + */ static bool fuse_writepage_in_flight(struct fuse_req *new_req, struct page *page) { @@ -1751,9 +1758,8 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, struct fuse_inode *fi = get_fuse_inode(new_req->inode); struct fuse_req *tmp; struct fuse_req *old_req; - pgoff_t curr_index; - BUG_ON(new_req->num_pages != 0); + WARN_ON(new_req->num_pages != 0); spin_lock(&fc->lock); list_del(&new_req->writepages_entry); @@ -1766,32 +1772,34 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, new_req->num_pages = 1; for (tmp = old_req; tmp != NULL; tmp = tmp->misc.write.next) { - BUG_ON(tmp->inode != new_req->inode); + pgoff_t curr_index; + + WARN_ON(tmp->inode != new_req->inode); curr_index = tmp->misc.write.in.offset >> PAGE_SHIFT; - if (tmp->num_pages == 1 && - curr_index == page->index) { - old_req = tmp; + if (tmp->num_pages == 1 && curr_index == page->index && + test_bit(FR_PENDING, &tmp->flags)) { + copy_highpage(tmp->pages[0], page); + break; } } - if (old_req->num_pages == 1 && test_bit(FR_PENDING, &old_req->flags)) { - struct backing_dev_info *bdi = inode_to_bdi(page->mapping->host); + if (!tmp) { + new_req->misc.write.next = old_req->misc.write.next; + old_req->misc.write.next = new_req; + } - copy_highpage(old_req->pages[0], page); - spin_unlock(&fc->lock); + spin_unlock(&fc->lock); + + if (tmp) { + struct backing_dev_info *bdi = inode_to_bdi(new_req->inode); dec_wb_stat(&bdi->wb, WB_WRITEBACK); dec_node_page_state(new_req->pages[0], NR_WRITEBACK_TEMP); wb_writeout_inc(&bdi->wb); fuse_writepage_free(fc, new_req); fuse_request_free(new_req); - goto out; - } else { - new_req->misc.write.next = old_req->misc.write.next; - old_req->misc.write.next = new_req; } - spin_unlock(&fc->lock); -out: + return true; } From 419234d5958b8ec4f5e2ba8ed2e77916f844ded1 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 16 Jan 2019 10:27:59 +0100 Subject: [PATCH 1691/4277] fuse: only reuse auxiliary request in fuse_writepage_in_flight() Don't reuse the queued request, even if it only contains a single page. This is needed because previous locking changes (spliting out fiq->waitq.lock from fc->lock) broke the assumption that request will remain in FR_PENDING at least until the new page contents are copied. This fix removes a slight optimization for a rare corner case, so we really shoudln't care. Reported-by: Kirill Tkhai Fixes: fd22d62ed0c3 ("fuse: no fc->lock for iqueue parts") Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 8342df29815d..b0c32a74082f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1746,9 +1746,9 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) /* * First recheck under fi->lock if the offending offset is still under - * writeback. If yes, then iterate write requests, to see if there's one - * already added for a page at this offset. If there's none, then insert this - * new request onto the auxiliary list, otherwise reuse the existing one by + * writeback. If yes, then iterate auxiliary write requests, to see if there's + * one already added for a page at this offset. If there's none, then insert + * this new request onto the auxiliary list, otherwise reuse the existing one by * copying the new page contents over to the old temporary page. */ static bool fuse_writepage_in_flight(struct fuse_req *new_req, @@ -1771,13 +1771,14 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, } new_req->num_pages = 1; - for (tmp = old_req; tmp != NULL; tmp = tmp->misc.write.next) { + for (tmp = old_req->misc.write.next; tmp; tmp = tmp->misc.write.next) { pgoff_t curr_index; WARN_ON(tmp->inode != new_req->inode); curr_index = tmp->misc.write.in.offset >> PAGE_SHIFT; - if (tmp->num_pages == 1 && curr_index == page->index && - test_bit(FR_PENDING, &tmp->flags)) { + if (curr_index == page->index) { + WARN_ON(tmp->num_pages != 1); + WARN_ON(!test_bit(FR_PENDING, &tmp->flags)); copy_highpage(tmp->pages[0], page); break; } From e2653bd53a98412ff2fc6fa6a6ed3934da04a3f3 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:15 +0100 Subject: [PATCH 1692/4277] fuse: fix leaked aux requests Auxiliary requests chained on req->misc.write.next may be leaked on truncate. Free these as well if the parent request was truncated off. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index b0c32a74082f..ee59599f4947 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1494,6 +1494,7 @@ static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req, __releases(fc->lock) __acquires(fc->lock) { + struct fuse_req *aux, *next; struct fuse_inode *fi = get_fuse_inode(req->inode); struct fuse_write_in *inarg = &req->misc.write.in; __u64 data_size = req->num_pages * PAGE_SIZE; @@ -1520,6 +1521,15 @@ __acquires(fc->lock) out_free: fuse_writepage_finish(fc, req); spin_unlock(&fc->lock); + + /* After fuse_writepage_finish() aux request list is private */ + for (aux = req->misc.write.next; aux; aux = next) { + next = aux->misc.write.next; + aux->misc.write.next = NULL; + fuse_writepage_free(fc, aux); + fuse_put_request(fc, aux); + } + fuse_writepage_free(fc, req); fuse_put_request(fc, req); spin_lock(&fc->lock); From c5de16cca2d7268833abfd6456d73fbba447c19b Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Mon, 26 Nov 2018 12:46:20 +0300 Subject: [PATCH 1693/4277] fuse: Replace page without copying in fuse_writepage_in_flight() It looks like we can optimize page replacement and avoid copying by simple updating the request's page. [SzM: swap with new request's tmp page to avoid use after free.] Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ee59599f4947..6b6e2574e733 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1789,7 +1789,7 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, if (curr_index == page->index) { WARN_ON(tmp->num_pages != 1); WARN_ON(!test_bit(FR_PENDING, &tmp->flags)); - copy_highpage(tmp->pages[0], page); + swap(tmp->pages[0], new_req->pages[0]); break; } } From 340617508d24d699c02a1e8a5ab3f53492f53386 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Tue, 6 Nov 2018 12:15:20 +0300 Subject: [PATCH 1694/4277] fuse: Remove stale comment in end_requests() Function end_requests() does not take fc->lock. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 809c0f2f9942..3feb071a0483 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2109,11 +2109,7 @@ static __poll_t fuse_dev_poll(struct file *file, poll_table *wait) return mask; } -/* - * Abort all requests on the given list (pending or processing) - * - * This function releases and reacquires fc->lock - */ +/* Abort all requests on the given list (pending or processing) */ static void end_requests(struct fuse_conn *fc, struct list_head *head) { while (!list_empty(head)) { From 8da6e9183275c837e7d2401b3018ff81f657a19a Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Thu, 8 Nov 2018 12:05:20 +0300 Subject: [PATCH 1695/4277] fuse: Kill fasync only if interrupt is queued in queue_interrupt() We should sent signal only in case of interrupt is really queued. Not a real problem, but this makes the code clearer and intuitive. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 3feb071a0483..11246e7965e8 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -480,9 +480,9 @@ static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) if (list_empty(&req->intr_entry)) { list_add_tail(&req->intr_entry, &fiq->interrupts); wake_up_locked(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } spin_unlock(&fiq->waitq.lock); - kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) From 217316a601016d0d2913290070e6338576ae73f6 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Thu, 8 Nov 2018 12:05:25 +0300 Subject: [PATCH 1696/4277] fuse: Optimize request_end() by not taking fiq->waitq.lock We take global fiq->waitq.lock every time, when we are in this function, but interrupted requests are just small subset of all requests. This patch optimizes request_end() and makes it to take the lock when it's really needed. queue_interrupt() needs small change for that. After req is linked to interrupt list, we do smp_mb() and check for FR_FINISHED again. In case of FR_FINISHED bit has appeared, we remove req and leave the function: Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 11246e7965e8..74171d568621 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -431,10 +431,16 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) if (test_and_set_bit(FR_FINISHED, &req->flags)) goto put_request; - - spin_lock(&fiq->waitq.lock); - list_del_init(&req->intr_entry); - spin_unlock(&fiq->waitq.lock); + /* + * test_and_set_bit() implies smp_mb() between bit + * changing and below intr_entry check. Pairs with + * smp_mb() from queue_interrupt(). + */ + if (!list_empty(&req->intr_entry)) { + spin_lock(&fiq->waitq.lock); + list_del_init(&req->intr_entry); + spin_unlock(&fiq->waitq.lock); + } WARN_ON(test_bit(FR_PENDING, &req->flags)); WARN_ON(test_bit(FR_SENT, &req->flags)); if (test_bit(FR_BACKGROUND, &req->flags)) { @@ -473,12 +479,18 @@ put_request: static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { spin_lock(&fiq->waitq.lock); - if (test_bit(FR_FINISHED, &req->flags)) { - spin_unlock(&fiq->waitq.lock); - return; - } if (list_empty(&req->intr_entry)) { list_add_tail(&req->intr_entry, &fiq->interrupts); + /* + * Pairs with smp_mb() implied by test_and_set_bit() + * from request_end(). + */ + smp_mb(); + if (test_bit(FR_FINISHED, &req->flags)) { + list_del_init(&req->intr_entry); + spin_unlock(&fiq->waitq.lock); + return; + } wake_up_locked(&fiq->waitq); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } From 5e0fed717a383ce6e894334cffe7a2acc9dc17b9 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Thu, 8 Nov 2018 12:05:31 +0300 Subject: [PATCH 1697/4277] fuse: Wake up req->waitq of only if not background Currently, we wait on req->waitq in request_wait_answer() function only, and it's never used for background requests. Since wake_up() is not a light-weight macros, instead of this, it unfolds in really called function, which makes locking operations taking some cpu cycles, let's avoid its call for the case we definitely know it's completely useless. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 74171d568621..0a69656402e5 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -468,8 +468,11 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) fc->active_background--; flush_bg_queue(fc); spin_unlock(&fc->bg_lock); + } else { + /* Wake up waiter sleeping in request_wait_answer() */ + wake_up(&req->waitq); } - wake_up(&req->waitq); + if (req->end) req->end(fc, req); put_request: From 7407a10de57f5810f3f0e778eb1db0923d24ab16 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Thu, 8 Nov 2018 12:05:36 +0300 Subject: [PATCH 1698/4277] fuse: Do some refactoring in fuse_dev_do_write() This is needed for next patch. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0a69656402e5..682c7914a0a0 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1915,16 +1915,17 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, struct fuse_req *req; struct fuse_out_header oh; + err = -EINVAL; if (nbytes < sizeof(struct fuse_out_header)) - return -EINVAL; + goto out; err = fuse_copy_one(cs, &oh, sizeof(oh)); if (err) - goto err_finish; + goto copy_finish; err = -EINVAL; if (oh.len != nbytes) - goto err_finish; + goto copy_finish; /* * Zero oh.unique indicates unsolicited notification message @@ -1932,41 +1933,40 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, */ if (!oh.unique) { err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs); - return err ? err : nbytes; + goto out; } err = -EINVAL; if (oh.error <= -1000 || oh.error > 0) - goto err_finish; + goto copy_finish; spin_lock(&fpq->lock); - err = -ENOENT; - if (!fpq->connected) - goto err_unlock_pq; + req = NULL; + if (fpq->connected) + req = request_find(fpq, oh.unique & ~FUSE_INT_REQ_BIT); - req = request_find(fpq, oh.unique & ~FUSE_INT_REQ_BIT); - if (!req) - goto err_unlock_pq; + err = -ENOENT; + if (!req) { + spin_unlock(&fpq->lock); + goto copy_finish; + } /* Is it an interrupt reply ID? */ if (oh.unique & FUSE_INT_REQ_BIT) { __fuse_get_request(req); spin_unlock(&fpq->lock); - err = -EINVAL; - if (nbytes != sizeof(struct fuse_out_header)) { - fuse_put_request(fc, req); - goto err_finish; - } - - if (oh.error == -ENOSYS) + err = 0; + if (nbytes != sizeof(struct fuse_out_header)) + err = -EINVAL; + else if (oh.error == -ENOSYS) fc->no_interrupt = 1; else if (oh.error == -EAGAIN) queue_interrupt(&fc->iq, req); + fuse_put_request(fc, req); - fuse_copy_finish(cs); - return nbytes; + goto copy_finish; } clear_bit(FR_SENT, &req->flags); @@ -1992,14 +1992,12 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, spin_unlock(&fpq->lock); request_end(fc, req); - +out: return err ? err : nbytes; - err_unlock_pq: - spin_unlock(&fpq->lock); - err_finish: +copy_finish: fuse_copy_finish(cs); - return err; + goto out; } static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) From b782911b5297c4be0c2de88fc3b36a760eca6321 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Thu, 8 Nov 2018 12:05:42 +0300 Subject: [PATCH 1699/4277] fuse: Verify userspace asks to requeue interrupt that we really sent When queue_interrupt() is called from fuse_dev_do_write(), it came from userspace directly. Userspace may pass any request id, even the request's we have not interrupted (or even background's request). This patch adds sanity check to make kernel safe against that. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 682c7914a0a0..ed9318d4e7dc 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -479,9 +479,15 @@ put_request: fuse_put_request(fc, req); } -static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) +static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { spin_lock(&fiq->waitq.lock); + /* Check for we've sent request to interrupt this req */ + if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags))) { + spin_unlock(&fiq->waitq.lock); + return -EINVAL; + } + if (list_empty(&req->intr_entry)) { list_add_tail(&req->intr_entry, &fiq->interrupts); /* @@ -492,12 +498,13 @@ static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) if (test_bit(FR_FINISHED, &req->flags)) { list_del_init(&req->intr_entry); spin_unlock(&fiq->waitq.lock); - return; + return 0; } wake_up_locked(&fiq->waitq); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } spin_unlock(&fiq->waitq.lock); + return 0; } static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) @@ -1962,7 +1969,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, else if (oh.error == -ENOSYS) fc->no_interrupt = 1; else if (oh.error == -EAGAIN) - queue_interrupt(&fc->iq, req); + err = queue_interrupt(&fc->iq, req); fuse_put_request(fc, req); From ebf84d0c7220c7c9b904c405e61175d2a50cfb39 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Nov 2018 13:33:11 +0300 Subject: [PATCH 1700/4277] fuse: Add fuse_inode argument to fuse_prepare_release() Here is preparation for next patches, which introduce new fi->lock for protection of ff->write_entry linked into fi->write_files. This patch just passes new argument to the function. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/cuse.c | 3 ++- fs/fuse/dir.c | 6 ++++-- fs/fuse/file.c | 10 ++++++---- fs/fuse/fuse_i.h | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 8f68181256c0..d73eba592ba1 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -141,10 +141,11 @@ static int cuse_open(struct inode *inode, struct file *file) static int cuse_release(struct inode *inode, struct file *file) { + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fc; - fuse_sync_release(ff, file->f_flags); + fuse_sync_release(fi, ff, file->f_flags); fuse_conn_put(fc); return 0; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index e909678afa2d..46c7430b94fc 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -400,6 +400,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, struct fuse_create_in inarg; struct fuse_open_out outopen; struct fuse_entry_out outentry; + struct fuse_inode *fi; struct fuse_file *ff; /* Userspace expects S_IFREG in create mode */ @@ -451,7 +452,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, &outentry.attr, entry_attr_timeout(&outentry), 0); if (!inode) { flags &= ~(O_CREAT | O_EXCL | O_TRUNC); - fuse_sync_release(ff, flags); + fuse_sync_release(NULL, ff, flags); fuse_queue_forget(fc, forget, outentry.nodeid, 1); err = -ENOMEM; goto out_err; @@ -462,7 +463,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, fuse_dir_changed(dir); err = finish_open(file, entry, generic_file_open); if (err) { - fuse_sync_release(ff, flags); + fi = get_fuse_inode(inode); + fuse_sync_release(fi, ff, flags); } else { file->private_data = ff; fuse_finish_open(inode, file); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 6b6e2574e733..cecd9b7e0b7f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -224,7 +224,8 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) return err; } -static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode) +static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, + int flags, int opcode) { struct fuse_conn *fc = ff->fc; struct fuse_req *req = ff->reserved_req; @@ -249,11 +250,12 @@ static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode) void fuse_release_common(struct file *file, bool isdir) { + struct fuse_inode *fi = get_fuse_inode(file_inode(file)); struct fuse_file *ff = file->private_data; struct fuse_req *req = ff->reserved_req; int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; - fuse_prepare_release(ff, file->f_flags, opcode); + fuse_prepare_release(fi, ff, file->f_flags, opcode); if (ff->flock) { struct fuse_release_in *inarg = &req->misc.release.in; @@ -295,10 +297,10 @@ static int fuse_release(struct inode *inode, struct file *file) return 0; } -void fuse_sync_release(struct fuse_file *ff, int flags) +void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff, int flags) { WARN_ON(refcount_read(&ff->count) > 1); - fuse_prepare_release(ff, flags, FUSE_RELEASE); + fuse_prepare_release(fi, ff, flags, FUSE_RELEASE); /* * iput(NULL) is a no-op and since the refcount is 1 and everything's * synchronous, we are fine with not doing igrab() here" diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 2f2c92e6f8cb..1b536b6c20f9 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -817,7 +817,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc); void fuse_file_free(struct fuse_file *ff); void fuse_finish_open(struct inode *inode, struct file *file); -void fuse_sync_release(struct fuse_file *ff, int flags); +void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff, int flags); /** * Send RELEASE or RELEASEDIR request From 4510d86fbbb36872224482bb21836d47cce8be8c Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Nov 2018 13:33:17 +0300 Subject: [PATCH 1701/4277] fuse: Convert fc->attr_version into atomic64_t This patch makes fc->attr_version of atomic64_t type, so fc->lock won't be needed to read or modify it anymore. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dir.c | 19 ++----------------- fs/fuse/file.c | 8 ++++---- fs/fuse/fuse_i.h | 9 ++++++--- fs/fuse/inode.c | 4 ++-- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 46c7430b94fc..10106f23e9e8 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -149,21 +149,6 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, args->out.args[0].value = outarg; } -u64 fuse_get_attr_version(struct fuse_conn *fc) -{ - u64 curr_version; - - /* - * The spin lock isn't actually needed on 64bit archs, but we - * don't yet care too much about such optimizations. - */ - spin_lock(&fc->lock); - curr_version = fc->attr_version; - spin_unlock(&fc->lock); - - return curr_version; -} - /* * Check whether the dentry is still valid * @@ -674,7 +659,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) struct fuse_inode *fi = get_fuse_inode(inode); spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); /* * If i_nlink == 0 then unlink doesn't make sense, yet this can * happen if userspace filesystem is careless. It would be @@ -828,7 +813,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, struct fuse_inode *fi = get_fuse_inode(inode); spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); inc_nlink(inode); spin_unlock(&fc->lock); fuse_invalidate_attr(inode); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index cecd9b7e0b7f..d1becba30bf3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -187,7 +187,7 @@ void fuse_finish_open(struct inode *inode, struct file *file) struct fuse_inode *fi = get_fuse_inode(inode); spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); i_size_write(inode, 0); spin_unlock(&fc->lock); fuse_invalidate_attr(inode); @@ -608,7 +608,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) struct fuse_inode *fi = get_fuse_inode(inode); spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); spin_unlock(&fc->lock); } @@ -687,7 +687,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size, spin_lock(&fc->lock); if (attr_ver == fi->attr_version && size < inode->i_size && !test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); i_size_write(inode, size); } spin_unlock(&fc->lock); @@ -1006,7 +1006,7 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos) bool ret = false; spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); if (pos > inode->i_size) { i_size_write(inode, pos); ret = true; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1b536b6c20f9..47c7b2238f91 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -730,7 +730,7 @@ struct fuse_conn { struct fuse_req *destroy_req; /** Version counter for attribute changes */ - u64 attr_version; + atomic64_t attr_version; /** Called on final put */ void (*release)(struct fuse_conn *); @@ -770,6 +770,11 @@ static inline int invalid_nodeid(u64 nodeid) return !nodeid || nodeid == FUSE_ROOT_ID; } +static inline u64 fuse_get_attr_version(struct fuse_conn *fc) +{ + return atomic64_read(&fc->attr_version); +} + /** Device operations */ extern const struct file_operations fuse_dev_operations; @@ -1000,8 +1005,6 @@ void fuse_flush_writepages(struct inode *inode); void fuse_set_nowrite(struct inode *inode); void fuse_release_nowrite(struct inode *inode); -u64 fuse_get_attr_version(struct fuse_conn *fc); - /** * File-system tells the kernel to invalidate cache for the given node id. */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c2d4099429be..86e86fcd6267 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -163,7 +163,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); - fi->attr_version = ++fc->attr_version; + fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->i_time = attr_valid; WRITE_ONCE(fi->inval_mask, 0); @@ -624,7 +624,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns) fc->blocked = 0; fc->initialized = 0; fc->connected = 1; - fc->attr_version = 1; + atomic64_set(&fc->attr_version, 1); get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); fc->pid_ns = get_pid_ns(task_active_pid_ns(current)); fc->user_ns = get_user_ns(user_ns); From f15ecfef058d94d03bdb35dcdfda041b3de9d543 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Nov 2018 13:33:22 +0300 Subject: [PATCH 1702/4277] fuse: Introduce fi->lock to protect write related fields To minimize contention of fc->lock, this patch introduces a new spinlock for protection fuse_inode metadata: fuse_inode: writectr writepages write_files queued_writes attr_version inode: i_size i_nlink i_mtime i_ctime Also, it protects the fields changed in fuse_change_attributes_common() (too many to list). Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dir.c | 25 +++++++------ fs/fuse/file.c | 93 +++++++++++++++++++++++++----------------------- fs/fuse/fuse_i.h | 5 ++- fs/fuse/inode.c | 9 +++-- 4 files changed, 70 insertions(+), 62 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 10106f23e9e8..eb7dfb0d9513 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -658,7 +658,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) struct inode *inode = d_inode(entry); struct fuse_inode *fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->attr_version = atomic64_inc_return(&fc->attr_version); /* * If i_nlink == 0 then unlink doesn't make sense, yet this can @@ -668,7 +668,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) */ if (inode->i_nlink > 0) drop_nlink(inode); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); fuse_invalidate_attr(inode); fuse_dir_changed(dir); fuse_invalidate_entry_cache(entry); @@ -812,10 +812,10 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, if (!err) { struct fuse_inode *fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->attr_version = atomic64_inc_return(&fc->attr_version); inc_nlink(inode); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); fuse_invalidate_attr(inode); fuse_update_ctime(inode); } else if (err == -EINTR) { @@ -1343,15 +1343,14 @@ static void iattr_to_fattr(struct fuse_conn *fc, struct iattr *iattr, */ void fuse_set_nowrite(struct inode *inode) { - struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); BUG_ON(!inode_is_locked(inode)); - spin_lock(&fc->lock); + spin_lock(&fi->lock); BUG_ON(fi->writectr < 0); fi->writectr += FUSE_NOWRITE; - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); wait_event(fi->page_waitq, fi->writectr == FUSE_NOWRITE); } @@ -1372,11 +1371,11 @@ static void __fuse_release_nowrite(struct inode *inode) void fuse_release_nowrite(struct inode *inode) { - struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); __fuse_release_nowrite(inode); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); } static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_args *args, @@ -1511,7 +1510,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, goto error; } - spin_lock(&fc->lock); + spin_lock(&fi->lock); /* the kernel maintains i_mtime locally */ if (trust_local_cmtime) { if (attr->ia_valid & ATTR_MTIME) @@ -1529,10 +1528,10 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, i_size_write(inode, outarg.attr.size); if (is_truncate) { - /* NOTE: this may release/reacquire fc->lock */ + /* NOTE: this may release/reacquire fi->lock */ __fuse_release_nowrite(inode); } - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); /* * Only call invalidate_inode_pages2() after removing diff --git a/fs/fuse/file.c b/fs/fuse/file.c index d1becba30bf3..26c2523120bc 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -159,17 +159,16 @@ EXPORT_SYMBOL_GPL(fuse_do_open); static void fuse_link_write_file(struct file *file) { struct inode *inode = file_inode(file); - struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_file *ff = file->private_data; /* * file may be written through mmap, so chain it onto the * inodes's write_file list */ - spin_lock(&fc->lock); + spin_lock(&fi->lock); if (list_empty(&ff->write_entry)) list_add(&ff->write_entry, &fi->write_files); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); } void fuse_finish_open(struct inode *inode, struct file *file) @@ -186,10 +185,10 @@ void fuse_finish_open(struct inode *inode, struct file *file) if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) { struct fuse_inode *fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->attr_version = atomic64_inc_return(&fc->attr_version); i_size_write(inode, 0); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); fuse_invalidate_attr(inode); if (fc->writeback_cache) file_update_time(file); @@ -231,8 +230,13 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff, struct fuse_req *req = ff->reserved_req; struct fuse_release_in *inarg = &req->misc.release.in; + /* Inode is NULL on error path of fuse_create_open() */ + if (likely(fi)) { + spin_lock(&fi->lock); + list_del(&ff->write_entry); + spin_unlock(&fi->lock); + } spin_lock(&fc->lock); - list_del(&ff->write_entry); if (!RB_EMPTY_NODE(&ff->polled_node)) rb_erase(&ff->polled_node, &fc->polled_files); spin_unlock(&fc->lock); @@ -358,13 +362,12 @@ static struct fuse_req *fuse_find_writeback(struct fuse_inode *fi, static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from, pgoff_t idx_to) { - struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); bool found; - spin_lock(&fc->lock); + spin_lock(&fi->lock); found = fuse_find_writeback(fi, idx_from, idx_to); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); return found; } @@ -607,9 +610,9 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->attr_version = atomic64_inc_return(&fc->attr_version); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); } io->iocb->ki_complete(io->iocb, res, 0); @@ -684,13 +687,13 @@ static void fuse_read_update_size(struct inode *inode, loff_t size, struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); if (attr_ver == fi->attr_version && size < inode->i_size && !test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { fi->attr_version = atomic64_inc_return(&fc->attr_version); i_size_write(inode, size); } - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); } static void fuse_short_read(struct fuse_req *req, struct inode *inode, @@ -1005,13 +1008,13 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos) struct fuse_inode *fi = get_fuse_inode(inode); bool ret = false; - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->attr_version = atomic64_inc_return(&fc->attr_version); if (pos > inode->i_size) { i_size_write(inode, pos); ret = true; } - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); return ret; } @@ -1490,11 +1493,11 @@ static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) wake_up(&fi->page_waitq); } -/* Called under fc->lock, may release and reacquire it */ +/* Called under fi->lock, may release and reacquire it */ static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req, loff_t size) -__releases(fc->lock) -__acquires(fc->lock) +__releases(fi->lock) +__acquires(fi->lock) { struct fuse_req *aux, *next; struct fuse_inode *fi = get_fuse_inode(req->inode); @@ -1502,9 +1505,6 @@ __acquires(fc->lock) __u64 data_size = req->num_pages * PAGE_SIZE; bool queued; - if (!fc->connected) - goto out_free; - if (inarg->offset + data_size <= size) { inarg->size = data_size; } else if (inarg->offset < size) { @@ -1515,14 +1515,17 @@ __acquires(fc->lock) } req->in.args[1].size = inarg->size; - fi->writectr++; queued = fuse_request_queue_background(fc, req); - WARN_ON(!queued); + /* Fails on broken connection only */ + if (unlikely(!queued)) + goto out_free; + + fi->writectr++; return; out_free: fuse_writepage_finish(fc, req); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); /* After fuse_writepage_finish() aux request list is private */ for (aux = req->misc.write.next; aux; aux = next) { @@ -1534,18 +1537,18 @@ __acquires(fc->lock) fuse_writepage_free(fc, req); fuse_put_request(fc, req); - spin_lock(&fc->lock); + spin_lock(&fi->lock); } /* * If fi->writectr is positive (no truncate or fsync going on) send * all queued writepage requests. * - * Called with fc->lock + * Called with fi->lock */ void fuse_flush_writepages(struct inode *inode) -__releases(fc->lock) -__acquires(fc->lock) +__releases(fi->lock) +__acquires(fi->lock) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); @@ -1565,7 +1568,7 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req) struct fuse_inode *fi = get_fuse_inode(inode); mapping_set_error(inode->i_mapping, req->out.h.error); - spin_lock(&fc->lock); + spin_lock(&fi->lock); while (req->misc.write.next) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_write_in *inarg = &req->misc.write.in; @@ -1602,7 +1605,7 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req) } fi->writectr--; fuse_writepage_finish(fc, req); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); fuse_writepage_free(fc, req); } @@ -1611,13 +1614,13 @@ static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc, { struct fuse_file *ff = NULL; - spin_lock(&fc->lock); + spin_lock(&fi->lock); if (!list_empty(&fi->write_files)) { ff = list_entry(fi->write_files.next, struct fuse_file, write_entry); fuse_file_get(ff); } - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); return ff; } @@ -1688,11 +1691,11 @@ static int fuse_writepage_locked(struct page *page) inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK); inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP); - spin_lock(&fc->lock); + spin_lock(&fi->lock); list_add(&req->writepages_entry, &fi->writepages); list_add_tail(&req->list, &fi->queued_writes); fuse_flush_writepages(inode); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); end_page_writeback(page); @@ -1741,16 +1744,15 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) { struct fuse_req *req = data->req; struct inode *inode = data->inode; - struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); int num_pages = req->num_pages; int i; req->ff = fuse_file_get(data->ff); - spin_lock(&fc->lock); + spin_lock(&fi->lock); list_add_tail(&req->list, &fi->queued_writes); fuse_flush_writepages(inode); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); for (i = 0; i < num_pages; i++) end_page_writeback(data->orig_pages[i]); @@ -1773,12 +1775,12 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, WARN_ON(new_req->num_pages != 0); - spin_lock(&fc->lock); + spin_lock(&fi->lock); list_del(&new_req->writepages_entry); old_req = fuse_find_writeback(fi, page->index, page->index); if (!old_req) { list_add(&new_req->writepages_entry, &fi->writepages); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); return false; } @@ -1801,7 +1803,7 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, old_req->misc.write.next = new_req; } - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); if (tmp) { struct backing_dev_info *bdi = inode_to_bdi(new_req->inode); @@ -1822,6 +1824,7 @@ static int fuse_writepages_fill(struct page *page, struct fuse_fill_wb_data *data = _data; struct fuse_req *req = data->req; struct inode *inode = data->inode; + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = get_fuse_conn(inode); struct page *tmp_page; bool is_writeback; @@ -1892,9 +1895,9 @@ static int fuse_writepages_fill(struct page *page, req->end = fuse_writepage_end; req->inode = inode; - spin_lock(&fc->lock); + spin_lock(&fi->lock); list_add(&req->writepages_entry, &fi->writepages); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); data->req = req; } @@ -1917,12 +1920,12 @@ static int fuse_writepages_fill(struct page *page, data->orig_pages[req->num_pages] = page; /* - * Protected by fc->lock against concurrent access by + * Protected by fi->lock against concurrent access by * fuse_page_is_writeback(). */ - spin_lock(&fc->lock); + spin_lock(&fi->lock); req->num_pages++; - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); out_unlock: unlock_page(page); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 47c7b2238f91..1c610b65b1bf 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -96,7 +96,7 @@ struct fuse_inode { union { /* Write related fields (regular file only) */ struct { - /* Files usable in writepage. Protected by fc->lock */ + /* Files usable in writepage. Protected by fi->lock */ struct list_head write_files; /* Writepages pending on truncate or fsync */ @@ -144,6 +144,9 @@ struct fuse_inode { /** Lock for serializing lookup and readdir for back compatibility*/ struct mutex mutex; + + /** Lock to protect write related fields */ + spinlock_t lock; }; /** FUSE inode state bits */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 86e86fcd6267..4c767175bd34 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -97,6 +97,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->orig_ino = 0; fi->state = 0; mutex_init(&fi->mutex); + spin_lock_init(&fi->lock); fi->forget = fuse_alloc_forget(); if (!fi->forget) { kmem_cache_free(fuse_inode_cachep, inode); @@ -163,6 +164,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); + lockdep_assert_held(&fi->lock); + fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->i_time = attr_valid; WRITE_ONCE(fi->inval_mask, 0); @@ -209,10 +212,10 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, loff_t oldsize; struct timespec64 old_mtime; - spin_lock(&fc->lock); + spin_lock(&fi->lock); if ((attr_version != 0 && fi->attr_version > attr_version) || test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); return; } @@ -227,7 +230,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, */ if (!is_wb || !S_ISREG(inode->i_mode)) i_size_write(inode, attr->size); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); if (!is_wb && S_ISREG(inode->i_mode)) { bool inval = false; From c9d8f5f0692d5960ed50970ffe63756fb8f96cdb Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Nov 2018 13:33:27 +0300 Subject: [PATCH 1703/4277] fuse: Protect fi->nlookup with fi->lock This continues previous patch and introduces the same protection for nlookup field. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dir.c | 4 ++-- fs/fuse/inode.c | 4 ++-- fs/fuse/readdir.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index eb7dfb0d9513..dd0f64f7bc06 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -207,9 +207,9 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) fuse_queue_forget(fc, forget, outarg.nodeid, 1); goto invalid; } - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->nlookup++; - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); } kfree(forget); if (ret == -ENOMEM) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 4c767175bd34..3d1a63e95f69 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -325,9 +325,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, } fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->nlookup++; - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); fuse_change_attributes(inode, attr, attr_valid, attr_version); return inode; diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index ab18b78f4755..574d03f8a573 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -213,9 +213,9 @@ retry: } fi = get_fuse_inode(inode); - spin_lock(&fc->lock); + spin_lock(&fi->lock); fi->nlookup++; - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); forget_all_cached_acls(inode); fuse_change_attributes(inode, &o->attr, From 6b675738ce900dac8f8478b1d8487df420a315f3 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 9 Nov 2018 13:33:32 +0300 Subject: [PATCH 1704/4277] fuse: Protect ff->reserved_req via corresponding fi->lock This is rather natural action after previous patches, and it just decreases load of fc->lock. Signed-off-by: Kirill Tkhai Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 10 ++++++---- fs/fuse/fuse_i.h | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ed9318d4e7dc..5a7309427e0e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -251,17 +251,18 @@ static struct fuse_req *get_reserved_req(struct fuse_conn *fc, struct file *file) { struct fuse_req *req = NULL; + struct fuse_inode *fi = get_fuse_inode(file_inode(file)); struct fuse_file *ff = file->private_data; do { wait_event(fc->reserved_req_waitq, ff->reserved_req); - spin_lock(&fc->lock); + spin_lock(&fi->lock); if (ff->reserved_req) { req = ff->reserved_req; ff->reserved_req = NULL; req->stolen_file = get_file(file); } - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); } while (!req); return req; @@ -273,16 +274,17 @@ static struct fuse_req *get_reserved_req(struct fuse_conn *fc, static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req) { struct file *file = req->stolen_file; + struct fuse_inode *fi = get_fuse_inode(file_inode(file)); struct fuse_file *ff = file->private_data; WARN_ON(req->max_pages); - spin_lock(&fc->lock); + spin_lock(&fi->lock); memset(req, 0, sizeof(*req)); fuse_request_init(req, NULL, NULL, 0); BUG_ON(ff->reserved_req); ff->reserved_req = req; wake_up_all(&fc->reserved_req_waitq); - spin_unlock(&fc->lock); + spin_unlock(&fi->lock); fput(file); } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1c610b65b1bf..4fdd098e422d 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -166,7 +166,10 @@ struct fuse_file { /** Fuse connection for this file */ struct fuse_conn *fc; - /** Request reserved for flush and release */ + /* + * Request reserved for flush and release. + * Modified under relative fuse_inode::lock. + */ struct fuse_req *reserved_req; /** Kernel file handle guaranteed to be unique */ From eb98e3bdf3aa7b15b40c65063ea935f953f60c6b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:16 +0100 Subject: [PATCH 1705/4277] fuse: clean up aborted The only caller that needs fc->aborted set is fuse_conn_abort_write(). Setting fc->aborted is now racy (fuse_abort_conn() may already be in progress or finished) but there's no reason to care. Signed-off-by: Miklos Szeredi --- fs/fuse/control.c | 4 +++- fs/fuse/cuse.c | 4 ++-- fs/fuse/dev.c | 9 ++++----- fs/fuse/fuse_i.h | 2 +- fs/fuse/inode.c | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/fs/fuse/control.c b/fs/fuse/control.c index 989df5accaee..fe80bea4ad89 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -35,7 +35,9 @@ static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, { struct fuse_conn *fc = fuse_ctl_file_conn_get(file); if (fc) { - fuse_abort_conn(fc, true); + if (fc->abort_err) + fc->aborted = true; + fuse_abort_conn(fc); fuse_conn_put(fc); } return count; diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index d73eba592ba1..55a26f351467 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -408,7 +408,7 @@ err_unlock: err_region: unregister_chrdev_region(devt, 1); err: - fuse_abort_conn(fc, false); + fuse_abort_conn(fc); goto out; } @@ -587,7 +587,7 @@ static ssize_t cuse_class_abort_store(struct device *dev, { struct cuse_conn *cc = dev_get_drvdata(dev); - fuse_abort_conn(&cc->fc, false); + fuse_abort_conn(&cc->fc); return count; } static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 5a7309427e0e..8a63e52785e9 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1330,7 +1330,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, goto err_unlock; if (!fiq->connected) { - err = (fc->aborted && fc->abort_err) ? -ECONNABORTED : -ENODEV; + err = fc->aborted ? -ECONNABORTED : -ENODEV; goto err_unlock; } @@ -1377,7 +1377,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) { - err = (fc->aborted && fc->abort_err) ? -ECONNABORTED : -ENODEV; + err = fc->aborted ? -ECONNABORTED : -ENODEV; goto out_end; } if (err) { @@ -2177,7 +2177,7 @@ static void end_polls(struct fuse_conn *fc) * is OK, the request will in that case be removed from the list before we touch * it. */ -void fuse_abort_conn(struct fuse_conn *fc, bool is_abort) +void fuse_abort_conn(struct fuse_conn *fc) { struct fuse_iqueue *fiq = &fc->iq; @@ -2193,7 +2193,6 @@ void fuse_abort_conn(struct fuse_conn *fc, bool is_abort) fc->connected = 0; spin_unlock(&fc->bg_lock); - fc->aborted = is_abort; fuse_set_initialized(fc); list_for_each_entry(fud, &fc->devices, entry) { struct fuse_pqueue *fpq = &fud->pq; @@ -2271,7 +2270,7 @@ int fuse_dev_release(struct inode *inode, struct file *file) /* Are we the last open device? */ if (atomic_dec_and_test(&fc->dev_count)) { WARN_ON(fc->iq.fasync != NULL); - fuse_abort_conn(fc, false); + fuse_abort_conn(fc); } fuse_dev_free(fud); } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4fdd098e422d..b1ac587671ac 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -947,7 +947,7 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req); bool fuse_request_queue_background(struct fuse_conn *fc, struct fuse_req *req); /* Abort all requests */ -void fuse_abort_conn(struct fuse_conn *fc, bool is_abort); +void fuse_abort_conn(struct fuse_conn *fc); void fuse_wait_aborted(struct fuse_conn *fc); /** diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 3d1a63e95f69..11aac2f4eda1 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -379,7 +379,7 @@ void fuse_unlock_inode(struct inode *inode, bool locked) static void fuse_umount_begin(struct super_block *sb) { - fuse_abort_conn(get_fuse_conn_super(sb), false); + fuse_abort_conn(get_fuse_conn_super(sb)); } static void fuse_send_destroy(struct fuse_conn *fc) @@ -1245,7 +1245,7 @@ static void fuse_sb_destroy(struct super_block *sb) if (fc) { fuse_send_destroy(fc); - fuse_abort_conn(fc, false); + fuse_abort_conn(fc); fuse_wait_aborted(fc); down_write(&fc->killsb); From 75126f5504524dd0f24753d8815db42d9ab23614 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:17 +0100 Subject: [PATCH 1706/4277] fuse: use atomic64_t for khctr ...to get rid of one more fc->lock use. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 4 +--- fs/fuse/fuse_i.h | 2 +- fs/fuse/inode.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 26c2523120bc..b2a4fab08cb4 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -64,9 +64,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc) RB_CLEAR_NODE(&ff->polled_node); init_waitqueue_head(&ff->poll_wait); - spin_lock(&fc->lock); - ff->kh = ++fc->khctr; - spin_unlock(&fc->lock); + ff->kh = atomic64_inc_return(&fc->khctr); return ff; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index b1ac587671ac..033e30af519f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -544,7 +544,7 @@ struct fuse_conn { struct fuse_iqueue iq; /** The next unique kernel file handle */ - u64 khctr; + atomic64_t khctr; /** rbtree of fuse_files waiting for poll events indexed by ph */ struct rb_root polled_files; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 11aac2f4eda1..2bbb7c59d6da 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -622,7 +622,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns) atomic_set(&fc->num_waiting, 0); fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND; fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; - fc->khctr = 0; + atomic64_set(&fc->khctr, 0); fc->polled_files = RB_ROOT; fc->blocked = 0; fc->initialized = 0; From 23c94e1cdcbf5953cd380555d0781caa42311870 Mon Sep 17 00:00:00 2001 From: Martin Raiber Date: Sat, 27 Oct 2018 16:48:48 +0000 Subject: [PATCH 1707/4277] fuse: Switch to using async direct IO for FOPEN_DIRECT_IO Switch to using the async directo IO code path in fuse_direct_read_iter() and fuse_direct_write_iter(). This is especially important in connection with loop devices with direct IO enabled as loop assumes async direct io is actually async. Signed-off-by: Martin Raiber Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index b2a4fab08cb4..383843c7c0e9 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1436,10 +1436,26 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io, return res; } +static ssize_t fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter); + static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); - return __fuse_direct_read(&io, to, &iocb->ki_pos); + ssize_t res; + + if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) { + struct file *file = iocb->ki_filp; + + if (is_bad_inode(file_inode(file))) + return -EIO; + + res = fuse_direct_IO(iocb, to); + } else { + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); + + res = __fuse_direct_read(&io, to, &iocb->ki_pos); + } + + return res; } static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) @@ -1454,8 +1470,14 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) /* Don't allow parallel writes to the same file */ inode_lock(inode); res = generic_write_checks(iocb, from); - if (res > 0) - res = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE); + if (res > 0) { + if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) { + res = fuse_direct_IO(iocb, from); + } else { + res = fuse_direct_io(&io, from, &iocb->ki_pos, + FUSE_DIO_WRITE); + } + } fuse_invalidate_attr(inode); if (res > 0) fuse_write_update_size(inode, iocb->ki_pos); From 3c3db095b68c5df901d837a01a69dcd2693f85f6 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:17 +0100 Subject: [PATCH 1708/4277] fuse: use iov_iter based generic splice helpers The default splice implementation is grossly inefficient and the iter based ones work just fine, so use those instead. I've measured an 8x speedup for splice write (with len = 128k). Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 383843c7c0e9..3e9e57c765f7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3156,6 +3156,7 @@ static const struct file_operations fuse_file_operations = { .lock = fuse_file_lock, .flock = fuse_file_flock, .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, .unlocked_ioctl = fuse_file_ioctl, .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll, @@ -3174,11 +3175,12 @@ static const struct file_operations fuse_direct_io_file_operations = { .fsync = fuse_fsync, .lock = fuse_file_lock, .flock = fuse_file_flock, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, .unlocked_ioctl = fuse_file_ioctl, .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll, .fallocate = fuse_file_fallocate, - /* no splice_read */ }; static const struct address_space_operations fuse_file_aops = { From d4136d60751a5f45f47f1c3a77f6e8bafa11be1f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:17 +0100 Subject: [PATCH 1709/4277] fuse add copy_file_range to direct io fops Nothing preventing copy_file_range to work on files opened with FOPEN_DIRECT_IO. Fixes: 88bc7d5097a1 ("fuse: add support for copy_file_range()") Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 3e9e57c765f7..c86266d4eac3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3181,6 +3181,7 @@ static const struct file_operations fuse_direct_io_file_operations = { .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll, .fallocate = fuse_file_fallocate, + .copy_file_range = fuse_copy_file_range, }; static const struct address_space_operations fuse_file_aops = { From 55752a3aba1387887afa024a0732f8ae52fb0645 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:17 +0100 Subject: [PATCH 1710/4277] fuse: multiplex cached/direct_io file operations This is cleanup, as well as allowing switching between I/O modes while the file is open in the future. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 71 ++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c86266d4eac3..e5dfc5e4b999 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -19,8 +19,6 @@ #include #include -static const struct file_operations fuse_direct_io_file_operations; - static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file, int opcode, struct fuse_open_out *outargp) { @@ -174,8 +172,6 @@ void fuse_finish_open(struct inode *inode, struct file *file) struct fuse_file *ff = file->private_data; struct fuse_conn *fc = get_fuse_conn(inode); - if (ff->open_flags & FOPEN_DIRECT_IO) - file->f_op = &fuse_direct_io_file_operations; if (!(ff->open_flags & FOPEN_KEEP_CACHE)) invalidate_inode_pages2(inode->i_mapping); if (ff->open_flags & FOPEN_NONSEEKABLE) @@ -929,7 +925,7 @@ out: return err; } -static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) +static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct inode *inode = iocb->ki_filp->f_mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); @@ -1183,7 +1179,7 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, return res > 0 ? res : err; } -static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -1486,6 +1482,26 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) return res; } +static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct fuse_file *ff = iocb->ki_filp->private_data; + + if (!(ff->open_flags & FOPEN_DIRECT_IO)) + return fuse_cache_read_iter(iocb, to); + else + return fuse_direct_read_iter(iocb, to); +} + +static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct fuse_file *ff = iocb->ki_filp->private_data; + + if (!(ff->open_flags & FOPEN_DIRECT_IO)) + return fuse_cache_write_iter(iocb, from); + else + return fuse_direct_write_iter(iocb, from); +} + static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) { int i; @@ -2129,6 +2145,18 @@ static const struct vm_operations_struct fuse_file_vm_ops = { static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) { + struct fuse_file *ff = file->private_data; + + if (ff->open_flags & FOPEN_DIRECT_IO) { + /* Can't provide the coherency needed for MAP_SHARED */ + if (vma->vm_flags & VM_MAYSHARE) + return -ENODEV; + + invalidate_inode_pages2(file->f_mapping); + + return generic_file_mmap(file, vma); + } + if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) fuse_link_write_file(file); @@ -2137,17 +2165,6 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) return 0; } -static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma) -{ - /* Can't provide the coherency needed for MAP_SHARED */ - if (vma->vm_flags & VM_MAYSHARE) - return -ENODEV; - - invalidate_inode_pages2(file->f_mapping); - - return generic_file_mmap(file, vma); -} - static int convert_fuse_file_lock(struct fuse_conn *fc, const struct fuse_file_lock *ffl, struct file_lock *fl) @@ -3164,26 +3181,6 @@ static const struct file_operations fuse_file_operations = { .copy_file_range = fuse_copy_file_range, }; -static const struct file_operations fuse_direct_io_file_operations = { - .llseek = fuse_file_llseek, - .read_iter = fuse_direct_read_iter, - .write_iter = fuse_direct_write_iter, - .mmap = fuse_direct_mmap, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, - .flock = fuse_file_flock, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .unlocked_ioctl = fuse_file_ioctl, - .compat_ioctl = fuse_file_compat_ioctl, - .poll = fuse_file_poll, - .fallocate = fuse_file_fallocate, - .copy_file_range = fuse_copy_file_range, -}; - static const struct address_space_operations fuse_file_aops = { .readpage = fuse_readpage, .writepage = fuse_writepage, From 2f7b6f5bed01a3fd2abcc20d2c85b7c532eb95cd Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 24 Jan 2019 10:40:17 +0100 Subject: [PATCH 1711/4277] fuse: lift bad inode checks into callers Bad inode checks were done done in various places, and move them into fuse_file_{read|write}_iter(). Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e5dfc5e4b999..8ee0446a8322 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1131,9 +1131,6 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, int err = 0; ssize_t res = 0; - if (is_bad_inode(inode)) - return -EIO; - if (inode->i_size < pos + iov_iter_count(ii)) set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); @@ -1422,9 +1419,6 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io, ssize_t res; struct inode *inode = file_inode(io->iocb->ki_filp); - if (is_bad_inode(inode)) - return -EIO; - res = fuse_direct_io(io, iter, ppos, 0); fuse_invalidate_atime(inode); @@ -1439,11 +1433,6 @@ static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) ssize_t res; if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) { - struct file *file = iocb->ki_filp; - - if (is_bad_inode(file_inode(file))) - return -EIO; - res = fuse_direct_IO(iocb, to); } else { struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); @@ -1460,9 +1449,6 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); ssize_t res; - if (is_bad_inode(inode)) - return -EIO; - /* Don't allow parallel writes to the same file */ inode_lock(inode); res = generic_write_checks(iocb, from); @@ -1484,7 +1470,11 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct fuse_file *ff = iocb->ki_filp->private_data; + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + if (is_bad_inode(file_inode(file))) + return -EIO; if (!(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_cache_read_iter(iocb, to); @@ -1494,7 +1484,11 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { - struct fuse_file *ff = iocb->ki_filp->private_data; + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + if (is_bad_inode(file_inode(file))) + return -EIO; if (!(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_cache_write_iter(iocb, from); From d9a9ea94f748f47b1d75c6c5e33edcf74476c445 Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 7 Jan 2019 16:53:17 -0800 Subject: [PATCH 1712/4277] fuse: support clients that don't implement 'opendir' Allow filesystems to return ENOSYS from opendir, preventing the kernel from sending opendir and releasedir messages in the future. This avoids userspace transitions when filesystems don't need to keep track of state per directory handle. A new capability flag, FUSE_NO_OPENDIR_SUPPORT, parallels FUSE_NO_OPEN_SUPPORT, indicating the new semantics for returning ENOSYS from opendir. Signed-off-by: Chad Austin Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 11 +++++++---- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 3 ++- include/uapi/linux/fuse.h | 7 ++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 8ee0446a8322..cc6ffd23b80f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -90,7 +90,7 @@ static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir) if (refcount_dec_and_test(&ff->count)) { struct fuse_req *req = ff->reserved_req; - if (ff->fc->no_open && !isdir) { + if (isdir ? ff->fc->no_opendir : ff->fc->no_open) { /* * Drop the release request when client does not * implement 'open' @@ -125,7 +125,7 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, ff->fh = 0; ff->open_flags = FOPEN_KEEP_CACHE; /* Default for no-open */ - if (!fc->no_open || isdir) { + if (isdir ? !fc->no_opendir : !fc->no_open) { struct fuse_open_out outarg; int err; @@ -134,11 +134,14 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, ff->fh = outarg.fh; ff->open_flags = outarg.open_flags; - } else if (err != -ENOSYS || isdir) { + } else if (err != -ENOSYS) { fuse_file_free(ff); return err; } else { - fc->no_open = 1; + if (isdir) + fc->no_opendir = 1; + else + fc->no_open = 1; } } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 033e30af519f..0920c0c032a0 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -630,6 +630,9 @@ struct fuse_conn { /** Is open/release not implemented by fs? */ unsigned no_open:1; + /** Is opendir/releasedir not implemented by fs? */ + unsigned no_opendir:1; + /** Is fsync not implemented by fs? */ unsigned no_fsync:1; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 2bbb7c59d6da..1b3f3b67d9f0 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -972,7 +972,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL | - FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS; + FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS | + FUSE_NO_OPENDIR_SUPPORT; req->in.h.opcode = FUSE_INIT; req->in.numargs = 1; req->in.args[0].size = sizeof(*arg); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index b4967d48bfda..2ac598614a8f 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -122,6 +122,9 @@ * - add FOPEN_CACHE_DIR * - add FUSE_MAX_PAGES, add max_pages to init_out * - add FUSE_CACHE_SYMLINKS + * + * 7.29 + * - add FUSE_NO_OPENDIR_SUPPORT flag */ #ifndef _LINUX_FUSE_H @@ -157,7 +160,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 28 +#define FUSE_KERNEL_MINOR_VERSION 29 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -259,6 +262,7 @@ struct fuse_file_lock { * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages * FUSE_CACHE_SYMLINKS: cache READLINK responses + * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -284,6 +288,7 @@ struct fuse_file_lock { #define FUSE_ABORT_ERROR (1 << 21) #define FUSE_MAX_PAGES (1 << 22) #define FUSE_CACHE_SYMLINKS (1 << 23) +#define FUSE_NO_OPENDIR_SUPPORT (1 << 24) /** * CUSE INIT request/reply flags From fabf7e0262d0bd57739d29aeac94c44b0542ff1f Mon Sep 17 00:00:00 2001 From: Chad Austin Date: Mon, 28 Jan 2019 16:34:34 -0800 Subject: [PATCH 1713/4277] fuse: cache readdir calls if filesystem opts out of opendir If a filesystem returns ENOSYS from opendir and thus opts out of opendir and releasedir requests, it almost certainly would also like readdir results cached. Default open_flags to FOPEN_KEEP_CACHE and FOPEN_CACHE_DIR in that case. With this patch, I've measured recursive directory enumeration across large FUSE mounts to be faster than native mounts. Signed-off-by: Chad Austin Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index cc6ffd23b80f..06096b60f1df 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -124,7 +124,8 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, return -ENOMEM; ff->fh = 0; - ff->open_flags = FOPEN_KEEP_CACHE; /* Default for no-open */ + /* Default for no-open */ + ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0); if (isdir ? !fc->no_opendir : !fc->no_open) { struct fuse_open_out outarg; int err; From 981e545a698aee98ed9c11202fe9bc93242f3cec Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 24 Jan 2019 19:47:29 +0900 Subject: [PATCH 1714/4277] kconfig: rename zconf.l to lexer.l Use a more logical name. Signed-off-by: Masahiro Yamada --- scripts/kconfig/Makefile | 6 +++--- scripts/kconfig/{zconf.l => lexer.l} | 0 scripts/kconfig/lkc.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename scripts/kconfig/{zconf.l => lexer.l} (100%) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 181973509a05..9278519dbcf3 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -143,10 +143,10 @@ help: # =========================================================================== # object files used by all kconfig flavours -common-objs := confdata.o expr.o symbol.o preprocess.o zconf.lex.o zconf.tab.o +common-objs := confdata.o expr.o lexer.lex.o preprocess.o symbol.o zconf.tab.o -$(obj)/zconf.lex.o: $(obj)/zconf.tab.h -HOSTCFLAGS_zconf.lex.o := -I$(src) +$(obj)/lexer.lex.o: $(obj)/zconf.tab.h +HOSTCFLAGS_lexer.lex.o := -I$(src) HOSTCFLAGS_zconf.tab.o := -I$(src) # conf: Used for defconfig, oldconfig and related targets diff --git a/scripts/kconfig/zconf.l b/scripts/kconfig/lexer.l similarity index 100% rename from scripts/kconfig/zconf.l rename to scripts/kconfig/lexer.l diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 531ff7c57d92..d871539e4b45 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -90,7 +90,7 @@ void *xrealloc(void *p, size_t size); char *xstrdup(const char *s); char *xstrndup(const char *s, size_t n); -/* zconf.l */ +/* lexer.l */ int yylex(void); struct gstr { From 769a1c02267854c48852532e5d7b595afcfe8dd7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 24 Jan 2019 19:47:30 +0900 Subject: [PATCH 1715/4277] kconfig: rename zconf.y to parser.y Use a more logical name. Signed-off-by: Masahiro Yamada --- scripts/kconfig/Makefile | 7 ++++--- scripts/kconfig/expr.h | 2 +- scripts/kconfig/lexer.l | 2 +- scripts/kconfig/{zconf.y => parser.y} | 0 4 files changed, 6 insertions(+), 5 deletions(-) rename scripts/kconfig/{zconf.y => parser.y} (100%) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 9278519dbcf3..7c5dc31c1d95 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -143,11 +143,12 @@ help: # =========================================================================== # object files used by all kconfig flavours -common-objs := confdata.o expr.o lexer.lex.o preprocess.o symbol.o zconf.tab.o +common-objs := confdata.o expr.o lexer.lex.o parser.tab.o preprocess.o \ + symbol.o -$(obj)/lexer.lex.o: $(obj)/zconf.tab.h +$(obj)/lexer.lex.o: $(obj)/parser.tab.h HOSTCFLAGS_lexer.lex.o := -I$(src) -HOSTCFLAGS_zconf.tab.o := -I$(src) +HOSTCFLAGS_parser.tab.o := -I$(src) # conf: Used for defconfig, oldconfig and related targets hostprogs-y += conf diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 999edb60cd53..8dde65bc3165 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -172,7 +172,7 @@ struct symbol { * int "BAZ Value" * range 1..255 * - * Please, also check zconf.y:print_symbol() when modifying the + * Please, also check parser.y:print_symbol() when modifying the * list of property types! */ enum prop_type { diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l index b2d0a3b0bce9..c9df1c8b9824 100644 --- a/scripts/kconfig/lexer.l +++ b/scripts/kconfig/lexer.l @@ -15,7 +15,7 @@ #include #include "lkc.h" -#include "zconf.tab.h" +#include "parser.tab.h" #define YY_DECL static int yylex1(void) diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/parser.y similarity index 100% rename from scripts/kconfig/zconf.y rename to scripts/kconfig/parser.y From ec482cc1c1180c65da582e17f4e79d204bb338e0 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:23:44 -0500 Subject: [PATCH 1716/4277] xprtrdma: Fix sparse warnings linux/net/sunrpc/xprtrdma/rpc_rdma.c:375:63: warning: incorrect type in argument 5 (different base types) linux/net/sunrpc/xprtrdma/rpc_rdma.c:375:63: expected unsigned int [usertype] xid linux/net/sunrpc/xprtrdma/rpc_rdma.c:375:63: got restricted __be32 [usertype] rq_xid linux/net/sunrpc/xprtrdma/rpc_rdma.c:432:62: warning: incorrect type in argument 5 (different base types) linux/net/sunrpc/xprtrdma/rpc_rdma.c:432:62: expected unsigned int [usertype] xid linux/net/sunrpc/xprtrdma/rpc_rdma.c:432:62: got restricted __be32 [usertype] rq_xid linux/net/sunrpc/xprtrdma/rpc_rdma.c:489:62: warning: incorrect type in argument 5 (different base types) linux/net/sunrpc/xprtrdma/rpc_rdma.c:489:62: expected unsigned int [usertype] xid linux/net/sunrpc/xprtrdma/rpc_rdma.c:489:62: got restricted __be32 [usertype] rq_xid Fixes: 0a93fbcb16e6 ("xprtrdma: Plant XID in on-the-wire RDMA ... ") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/frwr_ops.c | 4 ++-- net/sunrpc/xprtrdma/xprt_rdma.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 6a561056b538..52cb6c1b0c2b 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -391,7 +391,7 @@ frwr_wc_localinv_wake(struct ib_cq *cq, struct ib_wc *wc) */ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg, - int nsegs, bool writing, u32 xid, + int nsegs, bool writing, __be32 xid, struct rpcrdma_mr **out) { struct rpcrdma_ia *ia = &r_xprt->rx_ia; @@ -446,7 +446,7 @@ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, goto out_mapmr_err; ibmr->iova &= 0x00000000ffffffff; - ibmr->iova |= ((u64)cpu_to_be32(xid)) << 32; + ibmr->iova |= ((u64)be32_to_cpu(xid)) << 32; key = (u8)(ibmr->rkey & 0x000000FF); ib_update_fast_reg_key(ibmr, ++key); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 5a18472f2c9c..33db208fa9a8 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -577,7 +577,7 @@ void frwr_release_mr(struct rpcrdma_mr *mr); size_t frwr_maxpages(struct rpcrdma_xprt *r_xprt); struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg, - int nsegs, bool writing, u32 xid, + int nsegs, bool writing, __be32 xid, struct rpcrdma_mr **mr); int frwr_send(struct rpcrdma_ia *ia, struct rpcrdma_req *req); void frwr_reminv(struct rpcrdma_rep *rep, struct list_head *mrs); From d4550bbee66f4ba5a5e9bbe8055006332ebfc58b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:23:49 -0500 Subject: [PATCH 1717/4277] xprtrdma: Check inline size before providing a Write chunk In very rare cases, an NFS READ operation might predict that the non-payload part of the RPC Call is large. For instance, an NFSv4 COMPOUND with a large GETATTR result, in combination with a large Kerberos credential, could push the non-payload part to be several kilobytes. If the non-payload part is larger than the connection's inline threshold, the client is required to provision a Reply chunk. The current Linux client does not check for this case. There are two obvious ways to handle it: a. Provision a Write chunk for the payload and a Reply chunk for the non-payload part b. Provision a Reply chunk for the whole RPC Reply Some testing at a recent NFS bake-a-thon showed that servers can mostly handle a. but there are some corner cases that do not work yet. b. already works (it has to, to handle krb5i/p), but could be somewhat less efficient. However, I expect this scenario to be very rare -- no-one has reported a problem yet. So I'm going to implement b. Sometime later I will provide some patches to help make b. a little more efficient by more carefully choosing the Reply chunk's segment sizes to ensure the payload is optimally aligned. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index d18614e02b4e..7774aee7c013 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -164,6 +164,21 @@ static bool rpcrdma_results_inline(struct rpcrdma_xprt *r_xprt, return rqst->rq_rcv_buf.buflen <= ia->ri_max_inline_read; } +/* The client is required to provide a Reply chunk if the maximum + * size of the non-payload part of the RPC Reply is larger than + * the inline threshold. + */ +static bool +rpcrdma_nonpayload_inline(const struct rpcrdma_xprt *r_xprt, + const struct rpc_rqst *rqst) +{ + const struct xdr_buf *buf = &rqst->rq_rcv_buf; + const struct rpcrdma_ia *ia = &r_xprt->rx_ia; + + return buf->head[0].iov_len + buf->tail[0].iov_len < + ia->ri_max_inline_read; +} + /* Split @vec on page boundaries into SGEs. FMR registers pages, not * a byte range. Other modes coalesce these SGEs into a single MR * when they can. @@ -762,7 +777,8 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst) */ if (rpcrdma_results_inline(r_xprt, rqst)) wtype = rpcrdma_noch; - else if (ddp_allowed && rqst->rq_rcv_buf.flags & XDRBUF_READ) + else if ((ddp_allowed && rqst->rq_rcv_buf.flags & XDRBUF_READ) && + rpcrdma_nonpayload_inline(r_xprt, rqst)) wtype = rpcrdma_writech; else wtype = rpcrdma_replych; From e340c2d6ef2a8cdcc11672f8cc839c30ad360e52 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:23:54 -0500 Subject: [PATCH 1718/4277] xprtrdma: Reduce the doorbell rate (Receive) Post RECV WRs in batches to reduce the hardware doorbell rate per transport. This helps the RPC-over-RDMA client scale better in number of transports. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 2 ++ net/sunrpc/xprtrdma/xprt_rdma.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 4994e75945b8..b4e997d53ec7 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1480,6 +1480,8 @@ rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp) if (ep->rep_receive_count > needed) goto out; needed -= ep->rep_receive_count; + if (!temp) + needed += RPCRDMA_MAX_RECV_BATCH; count = 0; wr = NULL; diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 33db208fa9a8..10f6593e1a6a 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -205,6 +205,16 @@ struct rpcrdma_rep { struct ib_recv_wr rr_recv_wr; }; +/* To reduce the rate at which a transport invokes ib_post_recv + * (and thus the hardware doorbell rate), xprtrdma posts Receive + * WRs in batches. + * + * Setting this to zero disables Receive post batching. + */ +enum { + RPCRDMA_MAX_RECV_BATCH = 7, +}; + /* struct rpcrdma_sendctx - DMA mapped SGEs to unmap after Send completes */ struct rpcrdma_req; From fb024a07c69f81e97f3fd252b9102f5816066884 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 18 Jan 2019 17:12:12 -0700 Subject: [PATCH 1719/4277] selftests: don't kill child immediately in get_metadata() test This this test forks a child, and then the parent waits for a write() to a pipe signalling the child is ready to be attached to. If something in the child ASSERTs before it does this write, the test will hang waiting for it. Instead, let's EXPECT, so that execution continues until we do the write. Any failure after that is fine and can ASSERT. Signed-off-by: Tycho Andersen Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/seccomp/seccomp_bpf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 7e632b465ab4..e67395f08cd9 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -2985,11 +2985,11 @@ TEST(get_metadata) }; /* one with log, one without */ - ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, + EXPECT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG, &prog)); - ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog)); + EXPECT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog)); - ASSERT_EQ(0, close(pipefd[0])); + EXPECT_EQ(0, close(pipefd[0])); ASSERT_EQ(1, write(pipefd[1], "1", 1)); ASSERT_EQ(0, close(pipefd[1])); From 0b54b443a9de2db726625b2bd535fadca5b137a6 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 18 Jan 2019 17:12:13 -0700 Subject: [PATCH 1720/4277] selftests: fix typo in seccomp_bpf.c There used to be an explanation here because it could trigger lockdep previously, but now we're not doing recursive locking, so it really is just for grins. Signed-off-by: Tycho Andersen Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/seccomp/seccomp_bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index e67395f08cd9..01d1d06e3668 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -3077,7 +3077,7 @@ TEST(user_notification_basic) EXPECT_EQ(true, WIFEXITED(status)); EXPECT_EQ(0, WEXITSTATUS(status)); - /* Add some no-op filters so for grins. */ + /* Add some no-op filters for grins. */ EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0); EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0); EXPECT_EQ(seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog), 0); From a18261d71bbc625bc32abbcded71acc408cca393 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 18 Jan 2019 17:12:14 -0700 Subject: [PATCH 1721/4277] selftest: include stdio.h in kselftest.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While playing around with a way to skip the seccomp get_metadata test, I noticed that this header uses printf() without defining it, leading to, ../kselftest.h: In function ‘ksft_print_header’: ../kselftest.h:61:3: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration] printf("TAP version 13\n"); ^~~~~~ ../kselftest.h:61:3: warning: incompatible implicit declaration of built-in function ‘printf’ ../kselftest.h:61:3: note: include ‘’ or provide a declaration of ‘printf’ if user code doesn't also use printf. Signed-off-by: Tycho Andersen Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index a3edb2c8e43d..47e1d995c182 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -13,6 +13,7 @@ #include #include #include +#include /* define kselftest exit codes */ #define KSFT_PASS 0 From 3aa415dd2128e478ea3225b59308766de0e94d6b Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 18 Jan 2019 17:12:15 -0700 Subject: [PATCH 1722/4277] selftests: skip seccomp get_metadata test if not real root The get_metadata() test requires real root, so let's skip it if we're not real root. Note that I used XFAIL here because that's what the test does later if CONFIG_CHEKCKPOINT_RESTORE happens to not be enabled. After looking at the code, there doesn't seem to be a nice way to skip tests defined as TEST(), since there's no return code (I tried exit(KSFT_SKIP), but that didn't work either...). So let's do it this way to be consistent, and easier to fix when someone comes along and fixes it. Signed-off-by: Tycho Andersen Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/seccomp/seccomp_bpf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 01d1d06e3668..48354550d194 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -2971,6 +2971,12 @@ TEST(get_metadata) struct seccomp_metadata md; long ret; + /* Only real root can get metadata. */ + if (geteuid()) { + XFAIL(return, "get_metadata requires real root"); + return; + } + ASSERT_EQ(0, pipe(pipefd)); pid = fork(); From c7140706cb8affe0155c62b13c59940597825bac Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 18 Jan 2019 17:12:16 -0700 Subject: [PATCH 1723/4277] selftests: set NO_NEW_PRIVS bit in seccomp user tests seccomp() doesn't allow users who aren't root in their userns to attach filters unless they have the nnp bit set, so let's set it so that these tests can pass when run as an unprivileged user. This idea stolen from the other seccomp tests, which use this trick :) Signed-off-by: Tycho Andersen Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/seccomp/seccomp_bpf.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 48354550d194..abff7afd3345 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -3068,6 +3068,11 @@ TEST(user_notification_basic) .filter = filter, }; + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + pid = fork(); ASSERT_GE(pid, 0); @@ -3149,6 +3154,11 @@ TEST(user_notification_kill_in_middle) struct seccomp_notif req = {}; struct seccomp_notif_resp resp = {}; + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + listener = user_trap_syscall(__NR_getpid, SECCOMP_FILTER_FLAG_NEW_LISTENER); ASSERT_GE(listener, 0); @@ -3196,6 +3206,11 @@ TEST(user_notification_signal) struct seccomp_notif_resp resp = {}; char c; + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + ASSERT_EQ(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair), 0); listener = user_trap_syscall(__NR_gettid, @@ -3261,6 +3276,11 @@ TEST(user_notification_closed_listener) long ret; int status, listener; + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + listener = user_trap_syscall(__NR_getpid, SECCOMP_FILTER_FLAG_NEW_LISTENER); ASSERT_GE(listener, 0); @@ -3330,6 +3350,10 @@ TEST(user_notification_sibling_pid_ns) struct seccomp_notif req = {}; struct seccomp_notif_resp resp = {}; + ASSERT_EQ(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), 0) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + listener = user_trap_syscall(__NR_getpid, SECCOMP_FILTER_FLAG_NEW_LISTENER); ASSERT_GE(listener, 0); From 30d53a5860cf6743db011719d414456b10773d6a Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 18 Jan 2019 17:12:17 -0700 Subject: [PATCH 1724/4277] selftests: unshare userns in seccomp pidns testcases The pid ns cannot be unshare()d as an unprivileged user without owning the userns as well. Let's unshare the userns so that we can subsequently unshare the pidns. This also means that we don't need to set the no new privs bit as in the other test cases, since we're unsharing the userns. Signed-off-by: Tycho Andersen Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/seccomp/seccomp_bpf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index abff7afd3345..54587b081979 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -3313,7 +3313,7 @@ TEST(user_notification_child_pid_ns) struct seccomp_notif req = {}; struct seccomp_notif_resp resp = {}; - ASSERT_EQ(unshare(CLONE_NEWPID), 0); + ASSERT_EQ(unshare(CLONE_NEWUSER | CLONE_NEWPID), 0); listener = user_trap_syscall(__NR_getpid, SECCOMP_FILTER_FLAG_NEW_LISTENER); ASSERT_GE(listener, 0); @@ -3416,6 +3416,8 @@ TEST(user_notification_fault_recv) struct seccomp_notif req = {}; struct seccomp_notif_resp resp = {}; + ASSERT_EQ(unshare(CLONE_NEWUSER), 0); + listener = user_trap_syscall(__NR_getpid, SECCOMP_FILTER_FLAG_NEW_LISTENER); ASSERT_GE(listener, 0); From 37406a60fac7de336dec331a8707a94462ac5a5e Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 12 Feb 2019 12:32:41 -0500 Subject: [PATCH 1725/4277] drm: Merge __drm_atomic_helper_disable_all() into drm_atomic_helper_disable_all() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only difference between the functions is the clean_old_fbs argument, whose use was removed in the patch referenced below. So remove the internal copy and drop the guts back into drm_atomic_helper_disable_all() Changes in v2: - Instead of just removing the unused arg, merge the functions Fixes: e00fb8564ee9 ("drm: Stop updating plane->crtc/fb/old_fb on atomic drivers") Cc: Ville Syrjälä Cc: Maarten Lankhorst Cc: Harry Wentland Cc: Sinclair Yeh # This email bounces :( Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Reviewed-by: Ville Syrjälä Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190212173245.51980-1-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 59 +++++++++++++---------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index c53ecbd9abdd..7a7231d00779 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3030,9 +3030,31 @@ commit: return 0; } -static int __drm_atomic_helper_disable_all(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx, - bool clean_old_fbs) +/** + * drm_atomic_helper_disable_all - disable all currently active outputs + * @dev: DRM device + * @ctx: lock acquisition context + * + * Loops through all connectors, finding those that aren't turned off and then + * turns them off by setting their DPMS mode to OFF and deactivating the CRTC + * that they are connected to. + * + * This is used for example in suspend/resume to disable all currently active + * functions when suspending. If you just want to shut down everything at e.g. + * driver unload, look at drm_atomic_helper_shutdown(). + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * 0 on success or a negative error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and + * drm_atomic_helper_shutdown(). + */ +int drm_atomic_helper_disable_all(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) { struct drm_atomic_state *state; struct drm_connector_state *conn_state; @@ -3090,35 +3112,6 @@ free: drm_atomic_state_put(state); return ret; } - -/** - * drm_atomic_helper_disable_all - disable all currently active outputs - * @dev: DRM device - * @ctx: lock acquisition context - * - * Loops through all connectors, finding those that aren't turned off and then - * turns them off by setting their DPMS mode to OFF and deactivating the CRTC - * that they are connected to. - * - * This is used for example in suspend/resume to disable all currently active - * functions when suspending. If you just want to shut down everything at e.g. - * driver unload, look at drm_atomic_helper_shutdown(). - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * 0 on success or a negative error code on failure. - * - * See also: - * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and - * drm_atomic_helper_shutdown(). - */ -int drm_atomic_helper_disable_all(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) -{ - return __drm_atomic_helper_disable_all(dev, ctx, false); -} EXPORT_SYMBOL(drm_atomic_helper_disable_all); /** @@ -3139,7 +3132,7 @@ void drm_atomic_helper_shutdown(struct drm_device *dev) DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); - ret = __drm_atomic_helper_disable_all(dev, &ctx, true); + ret = drm_atomic_helper_disable_all(dev, &ctx); if (ret) DRM_ERROR("Disabling all crtc's during unload failed with %i\n", ret); From 121e357ac72836662cc13598d0e835fe846f1c95 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 27 Jan 2019 01:42:51 -0800 Subject: [PATCH 1726/4277] selftests/harness: Update named initializer syntax The harness was still using old-style GNU named initializer syntax. Fix this so Clang will stop warning: seccomp_bpf.c:2924:1: warning: use of GNU old-style field designator extension [-Wgnu-designator] ./../kselftest_harness.h:147:25: note: expanded from macro 'TEST' ^ ./../kselftest_harness.h:172:5: note: expanded from macro '__TEST_IMPL' fn: &test_name, termsig: _signal }; \ ^ Signed-off-by: Kees Cook Reviewed-by: Nick Desaulniers Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest_harness.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index 76d654ef3234..2d90c98eeb67 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -168,8 +168,8 @@ #define __TEST_IMPL(test_name, _signal) \ static void test_name(struct __test_metadata *_metadata); \ static struct __test_metadata _##test_name##_object = \ - { name: "global." #test_name, \ - fn: &test_name, termsig: _signal }; \ + { .name = "global." #test_name, \ + .fn = &test_name, .termsig = _signal }; \ static void __attribute__((constructor)) _register_##test_name(void) \ { \ __register_test(&_##test_name##_object); \ @@ -304,9 +304,9 @@ } \ static struct __test_metadata \ _##fixture_name##_##test_name##_object = { \ - name: #fixture_name "." #test_name, \ - fn: &wrapper_##fixture_name##_##test_name, \ - termsig: signal, \ + .name = #fixture_name "." #test_name, \ + .fn = &wrapper_##fixture_name##_##test_name, \ + .termsig = signal, \ }; \ static void __attribute__((constructor)) \ _register_##fixture_name##_##test_name(void) \ From ed492c2ad46450791c80f5e260f48d36e3a044eb Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 27 Jan 2019 01:43:57 -0800 Subject: [PATCH 1727/4277] selftests/seccomp: Actually sleep for 1/10th second Clang noticed that some none-zero sleep()s were actually using zero anyway. This switches to nanosleep() to gain sub-second granularity. seccomp_bpf.c:2625:9: warning: implicit conversion from 'double' to 'unsigned int' changes value from 0.1 to 0 [-Wliteral-conversion] sleep(0.1); ~~~~~ ^~~ Signed-off-by: Kees Cook Reviewed-by: Nick Desaulniers Signed-off-by: Shuah Khan --- tools/testing/selftests/seccomp/seccomp_bpf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 54587b081979..f69d2ee29742 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -2611,6 +2611,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) { long ret, sib; void *status; + struct timespec delay = { .tv_nsec = 100000000 }; ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); @@ -2664,7 +2665,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); /* Poll for actual task death. pthread_join doesn't guarantee it. */ while (!kill(self->sibling[sib].system_tid, 0)) - sleep(0.1); + nanosleep(&delay, NULL); /* Switch to the remaining sibling */ sib = !sib; @@ -2689,7 +2690,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) EXPECT_EQ(0, (long)status); /* Poll for actual task death. pthread_join doesn't guarantee it. */ while (!kill(self->sibling[sib].system_tid, 0)) - sleep(0.1); + nanosleep(&delay, NULL); ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &self->apply_prog); From 6f701383368d54e8d43fad2a0bac30f877899568 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:00 -0500 Subject: [PATCH 1728/4277] SUNRPC: Display symbolic flag names in RPC trace events Human-readable flags make it easier to observe RPC scheduling decisions and other operational details. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/trace/events/sunrpc.h | 56 +++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 0d5d0d91f861..f88b0f52aa7e 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -77,6 +77,50 @@ TRACE_EVENT(rpc_request, ) ); +TRACE_DEFINE_ENUM(RPC_TASK_ASYNC); +TRACE_DEFINE_ENUM(RPC_TASK_SWAPPER); +TRACE_DEFINE_ENUM(RPC_CALL_MAJORSEEN); +TRACE_DEFINE_ENUM(RPC_TASK_ROOTCREDS); +TRACE_DEFINE_ENUM(RPC_TASK_DYNAMIC); +TRACE_DEFINE_ENUM(RPC_TASK_KILLED); +TRACE_DEFINE_ENUM(RPC_TASK_SOFT); +TRACE_DEFINE_ENUM(RPC_TASK_SOFTCONN); +TRACE_DEFINE_ENUM(RPC_TASK_SENT); +TRACE_DEFINE_ENUM(RPC_TASK_TIMEOUT); +TRACE_DEFINE_ENUM(RPC_TASK_NOCONNECT); +TRACE_DEFINE_ENUM(RPC_TASK_NO_RETRANS_TIMEOUT); + +#define rpc_show_task_flags(flags) \ + __print_flags(flags, "|", \ + { RPC_TASK_ASYNC, "ASYNC" }, \ + { RPC_TASK_SWAPPER, "SWAPPER" }, \ + { RPC_CALL_MAJORSEEN, "MAJORSEEN" }, \ + { RPC_TASK_ROOTCREDS, "ROOTCREDS" }, \ + { RPC_TASK_DYNAMIC, "DYNAMIC" }, \ + { RPC_TASK_KILLED, "KILLED" }, \ + { RPC_TASK_SOFT, "SOFT" }, \ + { RPC_TASK_SOFTCONN, "SOFTCONN" }, \ + { RPC_TASK_SENT, "SENT" }, \ + { RPC_TASK_TIMEOUT, "TIMEOUT" }, \ + { RPC_TASK_NOCONNECT, "NOCONNECT" }, \ + { RPC_TASK_NO_RETRANS_TIMEOUT, "NORTO" }) + +TRACE_DEFINE_ENUM(RPC_TASK_RUNNING); +TRACE_DEFINE_ENUM(RPC_TASK_QUEUED); +TRACE_DEFINE_ENUM(RPC_TASK_ACTIVE); +TRACE_DEFINE_ENUM(RPC_TASK_NEED_XMIT); +TRACE_DEFINE_ENUM(RPC_TASK_NEED_RECV); +TRACE_DEFINE_ENUM(RPC_TASK_MSG_PIN_WAIT); + +#define rpc_show_runstate(flags) \ + __print_flags(flags, "|", \ + { (1UL << RPC_TASK_RUNNING), "RUNNING" }, \ + { (1UL << RPC_TASK_QUEUED), "QUEUED" }, \ + { (1UL << RPC_TASK_ACTIVE), "ACTIVE" }, \ + { (1UL << RPC_TASK_NEED_XMIT), "NEED_XMIT" }, \ + { (1UL << RPC_TASK_NEED_RECV), "NEED_RECV" }, \ + { (1UL << RPC_TASK_MSG_PIN_WAIT), "MSG_PIN_WAIT" }) + DECLARE_EVENT_CLASS(rpc_task_running, TP_PROTO(const struct rpc_task *task, const void *action), @@ -102,10 +146,10 @@ DECLARE_EVENT_CLASS(rpc_task_running, __entry->flags = task->tk_flags; ), - TP_printk("task:%u@%d flags=%4.4x state=%4.4lx status=%d action=%pf", + TP_printk("task:%u@%d flags=%s runstate=%s status=%d action=%pf", __entry->task_id, __entry->client_id, - __entry->flags, - __entry->runstate, + rpc_show_task_flags(__entry->flags), + rpc_show_runstate(__entry->runstate), __entry->status, __entry->action ) @@ -149,10 +193,10 @@ DECLARE_EVENT_CLASS(rpc_task_queued, __assign_str(q_name, rpc_qname(q)); ), - TP_printk("task:%u@%d flags=%4.4x state=%4.4lx status=%d timeout=%lu queue=%s", + TP_printk("task:%u@%d flags=%s runstate=%s status=%d timeout=%lu queue=%s", __entry->task_id, __entry->client_id, - __entry->flags, - __entry->runstate, + rpc_show_task_flags(__entry->flags), + rpc_show_runstate(__entry->runstate), __entry->status, __entry->timeout, __get_str(q_name) From a2b956463764da57ba4f54d0be7d742b50c2040f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 21 Dec 2018 18:54:00 -0600 Subject: [PATCH 1729/4277] dt-bindings: arm: l2x0: Convert L2 cache to json-schema Convert the ARM L2 cache controller binding to json-schema. Signed-off-by: Rob Herring --- .../devicetree/bindings/arm/l2c2x0.txt | 114 -------- .../devicetree/bindings/arm/l2c2x0.yaml | 248 ++++++++++++++++++ 2 files changed, 248 insertions(+), 114 deletions(-) delete mode 100644 Documentation/devicetree/bindings/arm/l2c2x0.txt create mode 100644 Documentation/devicetree/bindings/arm/l2c2x0.yaml diff --git a/Documentation/devicetree/bindings/arm/l2c2x0.txt b/Documentation/devicetree/bindings/arm/l2c2x0.txt deleted file mode 100644 index fbe6cb21f4cf..000000000000 --- a/Documentation/devicetree/bindings/arm/l2c2x0.txt +++ /dev/null @@ -1,114 +0,0 @@ -* ARM L2 Cache Controller - -ARM cores often have a separate L2C210/L2C220/L2C310 (also known as PL210/PL220/ -PL310 and variants) based level 2 cache controller. All these various implementations -of the L2 cache controller have compatible programming models (Note 1). -Some of the properties that are just prefixed "cache-*" are taken from section -3.7.3 of the Devicetree Specification which can be found at: -https://www.devicetree.org/specifications/ - -The ARM L2 cache representation in the device tree should be done as follows: - -Required properties: - -- compatible : should be one of: - "arm,pl310-cache" - "arm,l220-cache" - "arm,l210-cache" - "bcm,bcm11351-a2-pl310-cache": DEPRECATED by "brcm,bcm11351-a2-pl310-cache" - "brcm,bcm11351-a2-pl310-cache": For Broadcom bcm11351 chipset where an - offset needs to be added to the address before passing down to the L2 - cache controller - "marvell,aurora-system-cache": Marvell Controller designed to be - compatible with the ARM one, with system cache mode (meaning - maintenance operations on L1 are broadcasted to the L2 and L2 - performs the same operation). - "marvell,aurora-outer-cache": Marvell Controller designed to be - compatible with the ARM one with outer cache mode. - "marvell,tauros3-cache": Marvell Tauros3 cache controller, compatible - with arm,pl310-cache controller. -- cache-unified : Specifies the cache is a unified cache. -- cache-level : Should be set to 2 for a level 2 cache. -- reg : Physical base address and size of cache controller's memory mapped - registers. - -Optional properties: - -- arm,data-latency : Cycles of latency for Data RAM accesses. Specifies 3 cells of - read, write and setup latencies. Minimum valid values are 1. Controllers - without setup latency control should use a value of 0. -- arm,tag-latency : Cycles of latency for Tag RAM accesses. Specifies 3 cells of - read, write and setup latencies. Controllers without setup latency control - should use 0. Controllers without separate read and write Tag RAM latency - values should only use the first cell. -- arm,dirty-latency : Cycles of latency for Dirty RAMs. This is a single cell. -- arm,filter-ranges : Starting address and length of window to - filter. Addresses in the filter window are directed to the M1 port. Other - addresses will go to the M0 port. -- arm,io-coherent : indicates that the system is operating in an hardware - I/O coherent mode. Valid only when the arm,pl310-cache compatible - string is used. -- interrupts : 1 combined interrupt. -- cache-size : specifies the size in bytes of the cache -- cache-sets : specifies the number of associativity sets of the cache -- cache-block-size : specifies the size in bytes of a cache block -- cache-line-size : specifies the size in bytes of a line in the cache, - if this is not specified, the line size is assumed to be equal to the - cache block size -- cache-id-part: cache id part number to be used if it is not present - on hardware -- wt-override: If present then L2 is forced to Write through mode -- arm,double-linefill : Override double linefill enable setting. Enable if - non-zero, disable if zero. -- arm,double-linefill-incr : Override double linefill on INCR read. Enable - if non-zero, disable if zero. -- arm,double-linefill-wrap : Override double linefill on WRAP read. Enable - if non-zero, disable if zero. -- arm,prefetch-drop : Override prefetch drop enable setting. Enable if non-zero, - disable if zero. -- arm,prefetch-offset : Override prefetch offset value. Valid values are - 0-7, 15, 23, and 31. -- arm,shared-override : The default behavior of the L220 or PL310 cache - controllers with respect to the shareable attribute is to transform "normal - memory non-cacheable transactions" into "cacheable no allocate" (for reads) - or "write through no write allocate" (for writes). - On systems where this may cause DMA buffer corruption, this property must be - specified to indicate that such transforms are precluded. -- arm,parity-enable : enable parity checking on the L2 cache (L220 or PL310). -- arm,parity-disable : disable parity checking on the L2 cache (L220 or PL310). -- arm,outer-sync-disable : disable the outer sync operation on the L2 cache. - Some core tiles, especially ARM PB11MPCore have a faulty L220 cache that - will randomly hang unless outer sync operations are disabled. -- prefetch-data : Data prefetch. Value: <0> (forcibly disable), <1> - (forcibly enable), property absent (retain settings set by firmware) -- prefetch-instr : Instruction prefetch. Value: <0> (forcibly disable), - <1> (forcibly enable), property absent (retain settings set by - firmware) -- arm,dynamic-clock-gating : L2 dynamic clock gating. Value: <0> (forcibly - disable), <1> (forcibly enable), property absent (OS specific behavior, - preferably retain firmware settings) -- arm,standby-mode: L2 standby mode enable. Value <0> (forcibly disable), - <1> (forcibly enable), property absent (OS specific behavior, - preferably retain firmware settings) -- arm,early-bresp-disable : Disable the CA9 optimization Early BRESP (PL310) -- arm,full-line-zero-disable : Disable the CA9 optimization Full line of zero - write (PL310) - -Example: - -L2: cache-controller { - compatible = "arm,pl310-cache"; - reg = <0xfff12000 0x1000>; - arm,data-latency = <1 1 1>; - arm,tag-latency = <2 2 2>; - arm,filter-ranges = <0x80000000 0x8000000>; - cache-unified; - cache-level = <2>; - interrupts = <45>; -}; - -Note 1: The description in this document doesn't apply to integrated L2 - cache controllers as found in e.g. Cortex-A15/A7/A57/A53. These - integrated L2 controllers are assumed to be all preconfigured by - early secure boot code. Thus no need to deal with their configuration - in the kernel at all. diff --git a/Documentation/devicetree/bindings/arm/l2c2x0.yaml b/Documentation/devicetree/bindings/arm/l2c2x0.yaml new file mode 100644 index 000000000000..bfc5c185561c --- /dev/null +++ b/Documentation/devicetree/bindings/arm/l2c2x0.yaml @@ -0,0 +1,248 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/l2c2x0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM L2 Cache Controller + +maintainers: + - Rob Herring + +description: |+ + ARM cores often have a separate L2C210/L2C220/L2C310 (also known as PL210/ + PL220/PL310 and variants) based level 2 cache controller. All these various + implementations of the L2 cache controller have compatible programming + models (Note 1). Some of the properties that are just prefixed "cache-*" are + taken from section 3.7.3 of the Devicetree Specification which can be found + at: + https://www.devicetree.org/specifications/ + + Note 1: The description in this document doesn't apply to integrated L2 + cache controllers as found in e.g. Cortex-A15/A7/A57/A53. These + integrated L2 controllers are assumed to be all preconfigured by + early secure boot code. Thus no need to deal with their configuration + in the kernel at all. + +allOf: + - $ref: /schemas/cache-controller.yaml# + +properties: + compatible: + enum: + - arm,pl310-cache + - arm,l220-cache + - arm,l210-cache + # DEPRECATED by "brcm,bcm11351-a2-pl310-cache" + - bcm,bcm11351-a2-pl310-cache + # For Broadcom bcm11351 chipset where an + # offset needs to be added to the address before passing down to the L2 + # cache controller + - brcm,bcm11351-a2-pl310-cache + # Marvell Controller designed to be + # compatible with the ARM one, with system cache mode (meaning + # maintenance operations on L1 are broadcasted to the L2 and L2 + # performs the same operation). + - marvell,aurora-system-cache + # Marvell Controller designed to be + # compatible with the ARM one with outer cache mode. + - marvell,aurora-outer-cache + # Marvell Tauros3 cache controller, compatible + # with arm,pl310-cache controller. + - marvell,tauros3-cache + + cache-level: + const: 2 + + cache-unified: true + cache-size: true + cache-sets: true + cache-block-size: true + cache-line-size: true + + reg: + maxItems: 1 + + arm,data-latency: + description: Cycles of latency for Data RAM accesses. Specifies 3 cells of + read, write and setup latencies. Minimum valid values are 1. Controllers + without setup latency control should use a value of 0. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - minItems: 2 + maxItems: 3 + items: + minimum: 0 + maximum: 8 + + arm,tag-latency: + description: Cycles of latency for Tag RAM accesses. Specifies 3 cells of + read, write and setup latencies. Controllers without setup latency control + should use 0. Controllers without separate read and write Tag RAM latency + values should only use the first cell. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - minItems: 1 + maxItems: 3 + items: + minimum: 0 + maximum: 8 + + arm,dirty-latency: + description: Cycles of latency for Dirty RAMs. This is a single cell. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - minimum: 1 + maximum: 8 + + arm,filter-ranges: + description: Starting address and length of window to + filter. Addresses in the filter window are directed to the M1 port. Other + addresses will go to the M0 port. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + minItems: 2 + maxItems: 2 + + arm,io-coherent: + description: indicates that the system is operating in an hardware + I/O coherent mode. Valid only when the arm,pl310-cache compatible + string is used. + type: boolean + + interrupts: + # Either a single combined interrupt or up to 9 individual interrupts + minItems: 1 + maxItems: 9 + + cache-id-part: + description: cache id part number to be used if it is not present + on hardware + $ref: /schemas/types.yaml#/definitions/uint32 + + wt-override: + description: If present then L2 is forced to Write through mode + type: boolean + + arm,double-linefill: + description: Override double linefill enable setting. Enable if + non-zero, disable if zero. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,double-linefill-incr: + description: Override double linefill on INCR read. Enable + if non-zero, disable if zero. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,double-linefill-wrap: + description: Override double linefill on WRAP read. Enable + if non-zero, disable if zero. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,prefetch-drop: + description: Override prefetch drop enable setting. Enable if non-zero, + disable if zero. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,prefetch-offset: + description: Override prefetch offset value. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1, 2, 3, 4, 5, 6, 7, 15, 23, 31 ] + + arm,shared-override: + description: The default behavior of the L220 or PL310 cache + controllers with respect to the shareable attribute is to transform "normal + memory non-cacheable transactions" into "cacheable no allocate" (for reads) + or "write through no write allocate" (for writes). + On systems where this may cause DMA buffer corruption, this property must + be specified to indicate that such transforms are precluded. + type: boolean + + arm,parity-enable: + description: enable parity checking on the L2 cache (L220 or PL310). + type: boolean + + arm,parity-disable: + description: disable parity checking on the L2 cache (L220 or PL310). + type: boolean + + arm,outer-sync-disable: + description: disable the outer sync operation on the L2 cache. + Some core tiles, especially ARM PB11MPCore have a faulty L220 cache that + will randomly hang unless outer sync operations are disabled. + type: boolean + + prefetch-data: + description: | + Data prefetch. Value: <0> (forcibly disable), <1> + (forcibly enable), property absent (retain settings set by firmware) + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + prefetch-instr: + description: | + Instruction prefetch. Value: <0> (forcibly disable), + <1> (forcibly enable), property absent (retain settings set by + firmware) + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,dynamic-clock-gating: + description: | + L2 dynamic clock gating. Value: <0> (forcibly + disable), <1> (forcibly enable), property absent (OS specific behavior, + preferably retain firmware settings) + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,standby-mode: + description: L2 standby mode enable. Value <0> (forcibly disable), + <1> (forcibly enable), property absent (OS specific behavior, + preferably retain firmware settings) + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [ 0, 1 ] + + arm,early-bresp-disable: + description: Disable the CA9 optimization Early BRESP (PL310) + type: boolean + + arm,full-line-zero-disable: + description: Disable the CA9 optimization Full line of zero + write (PL310) + type: boolean + +required: + - compatible + - cache-unified + - reg + +additionalProperties: false + +examples: + - | + cache-controller@fff12000 { + compatible = "arm,pl310-cache"; + reg = <0xfff12000 0x1000>; + arm,data-latency = <1 1 1>; + arm,tag-latency = <2 2 2>; + arm,filter-ranges = <0x80000000 0x8000000>; + cache-unified; + cache-level = <2>; + interrupts = <45>; + }; + +... From 66ed144f147a785fada14c53f06a25079b764043 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 19 Dec 2018 15:16:50 -0600 Subject: [PATCH 1730/4277] dt-bindings: interrupt-controller: Convert ARM GIC to json-schema Convert the ARM GIC binding document to DT schema format using json-schema. Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Rob Herring --- .../bindings/interrupt-controller/arm,gic.txt | 171 -------------- .../interrupt-controller/arm,gic.yaml | 223 ++++++++++++++++++ 2 files changed, 223 insertions(+), 171 deletions(-) delete mode 100644 Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt create mode 100644 Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt deleted file mode 100644 index 2f3244648646..000000000000 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt +++ /dev/null @@ -1,171 +0,0 @@ -* ARM Generic Interrupt Controller - -ARM SMP cores are often associated with a GIC, providing per processor -interrupts (PPI), shared processor interrupts (SPI) and software -generated interrupts (SGI). - -Primary GIC is attached directly to the CPU and typically has PPIs and SGIs. -Secondary GICs are cascaded into the upward interrupt controller and do not -have PPIs or SGIs. - -Main node required properties: - -- compatible : should be one of: - "arm,arm1176jzf-devchip-gic" - "arm,arm11mp-gic" - "arm,cortex-a15-gic" - "arm,cortex-a7-gic" - "arm,cortex-a9-gic" - "arm,eb11mp-gic" - "arm,gic-400" - "arm,pl390" - "arm,tc11mp-gic" - "brcm,brahma-b15-gic" - "nvidia,tegra210-agic" - "qcom,msm-8660-qgic" - "qcom,msm-qgic2" -- interrupt-controller : Identifies the node as an interrupt controller -- #interrupt-cells : Specifies the number of cells needed to encode an - interrupt source. The type shall be a and the value shall be 3. - - The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI - interrupts. - - The 2nd cell contains the interrupt number for the interrupt type. - SPI interrupts are in the range [0-987]. PPI interrupts are in the - range [0-15]. - - The 3rd cell is the flags, encoded as follows: - bits[3:0] trigger type and level flags. - 1 = low-to-high edge triggered - 2 = high-to-low edge triggered (invalid for SPIs) - 4 = active high level-sensitive - 8 = active low level-sensitive (invalid for SPIs). - bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of - the 8 possible cpus attached to the GIC. A bit set to '1' indicated - the interrupt is wired to that CPU. Only valid for PPI interrupts. - Also note that the configurability of PPI interrupts is IMPLEMENTATION - DEFINED and as such not guaranteed to be present (most SoC available - in 2014 seem to ignore the setting of this flag and use the hardware - default value). - -- reg : Specifies base physical address(s) and size of the GIC registers. The - first region is the GIC distributor register base and size. The 2nd region is - the GIC cpu interface register base and size. - -Optional -- interrupts : Interrupt source of the parent interrupt controller on - secondary GICs, or VGIC maintenance interrupt on primary GIC (see - below). - -- cpu-offset : per-cpu offset within the distributor and cpu interface - regions, used when the GIC doesn't have banked registers. The offset is - cpu-offset * cpu-nr. - -- clocks : List of phandle and clock-specific pairs, one for each entry - in clock-names. -- clock-names : List of names for the GIC clock input(s). Valid clock names - depend on the GIC variant: - "ic_clk" (for "arm,arm11mp-gic") - "PERIPHCLKEN" (for "arm,cortex-a15-gic") - "PERIPHCLK", "PERIPHCLKEN" (for "arm,cortex-a9-gic") - "clk" (for "arm,gic-400" and "nvidia,tegra210") - "gclk" (for "arm,pl390") - -- power-domains : A phandle and PM domain specifier as defined by bindings of - the power controller specified by phandle, used when the GIC - is part of a Power or Clock Domain. - - -Example: - - intc: interrupt-controller@fff11000 { - compatible = "arm,cortex-a9-gic"; - #interrupt-cells = <3>; - #address-cells = <1>; - interrupt-controller; - reg = <0xfff11000 0x1000>, - <0xfff10100 0x100>; - }; - - -* GIC virtualization extensions (VGIC) - -For ARM cores that support the virtualization extensions, additional -properties must be described (they only exist if the GIC is the -primary interrupt controller). - -Required properties: - -- reg : Additional regions specifying the base physical address and - size of the VGIC registers. The first additional region is the GIC - virtual interface control register base and size. The 2nd additional - region is the GIC virtual cpu interface register base and size. - -- interrupts : VGIC maintenance interrupt. - -Example: - - interrupt-controller@2c001000 { - compatible = "arm,cortex-a15-gic"; - #interrupt-cells = <3>; - interrupt-controller; - reg = <0x2c001000 0x1000>, - <0x2c002000 0x2000>, - <0x2c004000 0x2000>, - <0x2c006000 0x2000>; - interrupts = <1 9 0xf04>; - }; - - -* GICv2m extension for MSI/MSI-x support (Optional) - -Certain revisions of GIC-400 supports MSI/MSI-x via V2M register frame(s). -This is enabled by specifying v2m sub-node(s). - -Required properties: - -- compatible : The value here should contain "arm,gic-v2m-frame". - -- msi-controller : Identifies the node as an MSI controller. - -- reg : GICv2m MSI interface register base and size - -Optional properties: - -- arm,msi-base-spi : When the MSI_TYPER register contains an incorrect - value, this property should contain the SPI base of - the MSI frame, overriding the HW value. - -- arm,msi-num-spis : When the MSI_TYPER register contains an incorrect - value, this property should contain the number of - SPIs assigned to the frame, overriding the HW value. - -Example: - - interrupt-controller@e1101000 { - compatible = "arm,gic-400"; - #interrupt-cells = <3>; - #address-cells = <2>; - #size-cells = <2>; - interrupt-controller; - interrupts = <1 8 0xf04>; - ranges = <0 0 0 0xe1100000 0 0x100000>; - reg = <0x0 0xe1110000 0 0x01000>, - <0x0 0xe112f000 0 0x02000>, - <0x0 0xe1140000 0 0x10000>, - <0x0 0xe1160000 0 0x10000>; - v2m0: v2m@8000 { - compatible = "arm,gic-v2m-frame"; - msi-controller; - reg = <0x0 0x80000 0 0x1000>; - }; - - .... - - v2mN: v2m@9000 { - compatible = "arm,gic-v2m-frame"; - msi-controller; - reg = <0x0 0x90000 0 0x1000>; - }; - }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml new file mode 100644 index 000000000000..758fbd7128e7 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml @@ -0,0 +1,223 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Generic Interrupt Controller v1 and v2 + +maintainers: + - Marc Zyngier + +description: |+ + ARM SMP cores are often associated with a GIC, providing per processor + interrupts (PPI), shared processor interrupts (SPI) and software + generated interrupts (SGI). + + Primary GIC is attached directly to the CPU and typically has PPIs and SGIs. + Secondary GICs are cascaded into the upward interrupt controller and do not + have PPIs or SGIs. + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - arm,arm11mp-gic + - arm,cortex-a15-gic + - arm,cortex-a7-gic + - arm,cortex-a5-gic + - arm,cortex-a9-gic + - arm,eb11mp-gic + - arm,gic-400 + - arm,pl390 + - arm,tc11mp-gic + - nvidia,tegra210-agic + - qcom,msm-8660-qgic + - qcom,msm-qgic2 + + - items: + - const: arm,arm1176jzf-devchip-gic + - const: arm,arm11mp-gic + + - items: + - const: brcm,brahma-b15-gic + - const: arm,cortex-a15-gic + + interrupt-controller: true + + "#address-cells": + enum: [ 0, 1 ] + "#size-cells": + const: 1 + + "#interrupt-cells": + const: 3 + description: | + The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI + interrupts. + + The 2nd cell contains the interrupt number for the interrupt type. + SPI interrupts are in the range [0-987]. PPI interrupts are in the + range [0-15]. + + The 3rd cell is the flags, encoded as follows: + bits[3:0] trigger type and level flags. + 1 = low-to-high edge triggered + 2 = high-to-low edge triggered (invalid for SPIs) + 4 = active high level-sensitive + 8 = active low level-sensitive (invalid for SPIs). + bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of + the 8 possible cpus attached to the GIC. A bit set to '1' indicated + the interrupt is wired to that CPU. Only valid for PPI interrupts. + Also note that the configurability of PPI interrupts is IMPLEMENTATION + DEFINED and as such not guaranteed to be present (most SoC available + in 2014 seem to ignore the setting of this flag and use the hardware + default value). + + reg: + description: | + Specifies base physical address(s) and size of the GIC registers. The + first region is the GIC distributor register base and size. The 2nd region + is the GIC cpu interface register base and size. + + For GICv2 with virtualization extensions, additional regions are + required for specifying the base physical address and size of the VGIC + registers. The first additional region is the GIC virtual interface + control register base and size. The 2nd additional region is the GIC + virtual cpu interface register base and size. + minItems: 2 + maxItems: 4 + + interrupts: + description: Interrupt source of the parent interrupt controller on + secondary GICs, or VGIC maintenance interrupt on primary GIC (see + below). + maxItems: 1 + + cpu-offset: + description: per-cpu offset within the distributor and cpu interface + regions, used when the GIC doesn't have banked registers. The offset + is cpu-offset * cpu-nr. + $ref: /schemas/types.yaml#/definitions/uint32 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + description: List of names for the GIC clock input(s). Valid clock names + depend on the GIC variant. + oneOf: + - const: ic_clk # for "arm,arm11mp-gic" + - const: PERIPHCLKEN # for "arm,cortex-a15-gic" + - items: # for "arm,cortex-a9-gic" + - const: PERIPHCLK + - const: PERIPHCLKEN + - const: clk # for "arm,gic-400" and "nvidia,tegra210" + - const: gclk #for "arm,pl390" + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + +patternProperties: + "^v2m@[0-9a-f]+$": + description: | + * GICv2m extension for MSI/MSI-x support (Optional) + + Certain revisions of GIC-400 supports MSI/MSI-x via V2M register frame(s). + This is enabled by specifying v2m sub-node(s). + + properties: + compatible: + const: arm,gic-v2m-frame + + msi-controller: true + + reg: + maxItems: 1 + description: GICv2m MSI interface register base and size + + arm,msi-base-spi: + description: When the MSI_TYPER register contains an incorrect value, + this property should contain the SPI base of the MSI frame, overriding + the HW value. + $ref: /schemas/types.yaml#/definitions/uint32 + + arm,msi-num-spis: + description: When the MSI_TYPER register contains an incorrect value, + this property should contain the number of SPIs assigned to the + frame, overriding the HW value. + $ref: /schemas/types.yaml#/definitions/uint32 + + required: + - compatible + - msi-controller + - reg + + additionalProperties: false + +additionalProperties: false + +examples: + - | + // GICv1 + intc: interrupt-controller@fff11000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <1>; + interrupt-controller; + reg = <0xfff11000 0x1000>, + <0xfff10100 0x100>; + }; + + - | + // GICv2 + interrupt-controller@2c001000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x2c001000 0x1000>, + <0x2c002000 0x2000>, + <0x2c004000 0x2000>, + <0x2c006000 0x2000>; + interrupts = <1 9 0xf04>; + }; + + - | + // GICv2m extension for MSI/MSI-x support + interrupt-controller@e1101000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + #address-cells = <2>; + #size-cells = <2>; + interrupt-controller; + interrupts = <1 8 0xf04>; + ranges = <0 0 0 0xe1100000 0 0x100000>; + reg = <0x0 0xe1110000 0 0x01000>, + <0x0 0xe112f000 0 0x02000>, + <0x0 0xe1140000 0 0x10000>, + <0x0 0xe1160000 0 0x10000>; + + v2m0: v2m@8000 { + compatible = "arm,gic-v2m-frame"; + msi-controller; + reg = <0x0 0x80000 0 0x1000>; + }; + + //... + + v2mN: v2m@9000 { + compatible = "arm,gic-v2m-frame"; + msi-controller; + reg = <0x0 0x90000 0 0x1000>; + }; + }; +... From 36c79bc7ef792e8f5e0cc0eb012ad21a7981e908 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 19 Dec 2018 15:16:50 -0600 Subject: [PATCH 1731/4277] dt-bindings: interrupt-controller: Convert ARM GICv3 to json-schema Convert the ARM GICv3 binding document to DT schema format using json-schema. Cc: Thomas Gleixner Cc: Jason Cooper Cc: Marc Zyngier Signed-off-by: Rob Herring --- .../interrupt-controller/arm,gic-v3.txt | 175 ----------- .../interrupt-controller/arm,gic-v3.yaml | 279 ++++++++++++++++++ 2 files changed, 279 insertions(+), 175 deletions(-) delete mode 100644 Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt create mode 100644 Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt deleted file mode 100644 index b83bb8249074..000000000000 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt +++ /dev/null @@ -1,175 +0,0 @@ -* ARM Generic Interrupt Controller, version 3 - -AArch64 SMP cores are often associated with a GICv3, providing Private -Peripheral Interrupts (PPI), Shared Peripheral Interrupts (SPI), -Software Generated Interrupts (SGI), and Locality-specific Peripheral -Interrupts (LPI). - -Main node required properties: - -- compatible : should at least contain "arm,gic-v3" or either - "qcom,msm8996-gic-v3", "arm,gic-v3" for msm8996 SoCs - to address SoC specific bugs/quirks -- interrupt-controller : Identifies the node as an interrupt controller -- #interrupt-cells : Specifies the number of cells needed to encode an - interrupt source. Must be a single cell with a value of at least 3. - If the system requires describing PPI affinity, then the value must - be at least 4. - - The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI - interrupts. Other values are reserved for future use. - - The 2nd cell contains the interrupt number for the interrupt type. - SPI interrupts are in the range [0-987]. PPI interrupts are in the - range [0-15]. - - The 3rd cell is the flags, encoded as follows: - bits[3:0] trigger type and level flags. - 1 = edge triggered - 4 = level triggered - - The 4th cell is a phandle to a node describing a set of CPUs this - interrupt is affine to. The interrupt must be a PPI, and the node - pointed must be a subnode of the "ppi-partitions" subnode. For - interrupt types other than PPI or PPIs that are not partitionned, - this cell must be zero. See the "ppi-partitions" node description - below. - - Cells 5 and beyond are reserved for future use and must have a value - of 0 if present. - -- reg : Specifies base physical address(s) and size of the GIC - registers, in the following order: - - GIC Distributor interface (GICD) - - GIC Redistributors (GICR), one range per redistributor region - - GIC CPU interface (GICC) - - GIC Hypervisor interface (GICH) - - GIC Virtual CPU interface (GICV) - - GICC, GICH and GICV are optional. - -- interrupts : Interrupt source of the VGIC maintenance interrupt. - -Optional - -- redistributor-stride : If using padding pages, specifies the stride - of consecutive redistributors. Must be a multiple of 64kB. - -- #redistributor-regions: The number of independent contiguous regions - occupied by the redistributors. Required if more than one such - region is present. - -- msi-controller: Boolean property. Identifies the node as an MSI - controller. Only present if the Message Based Interrupt - functionnality is being exposed by the HW, and the mbi-ranges - property present. - -- mbi-ranges: A list of pairs , where "intid" is the first - SPI of a range that can be used an MBI, and "span" the size of that - range. Multiple ranges can be provided. Requires "msi-controller" to - be set. - -- mbi-alias: Address property. Base address of an alias of the GICD - region containing only the {SET,CLR}SPI registers to be used if - isolation is required, and if supported by the HW. - -Sub-nodes: - -PPI affinity can be expressed as a single "ppi-partitions" node, -containing a set of sub-nodes, each with the following property: -- affinity: Should be a list of phandles to CPU nodes (as described in -Documentation/devicetree/bindings/arm/cpus.txt). - -GICv3 has one or more Interrupt Translation Services (ITS) that are -used to route Message Signalled Interrupts (MSI) to the CPUs. - -These nodes must have the following properties: -- compatible : Should at least contain "arm,gic-v3-its". -- msi-controller : Boolean property. Identifies the node as an MSI controller -- #msi-cells: Must be <1>. The single msi-cell is the DeviceID of the device - which will generate the MSI. -- reg: Specifies the base physical address and size of the ITS - registers. - -Optional: -- socionext,synquacer-pre-its: (u32, u32) tuple describing the untranslated - address and size of the pre-ITS window. - -The main GIC node must contain the appropriate #address-cells, -#size-cells and ranges properties for the reg property of all ITS -nodes. - -Examples: - - gic: interrupt-controller@2cf00000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <3>; - #address-cells = <2>; - #size-cells = <2>; - ranges; - interrupt-controller; - reg = <0x0 0x2f000000 0 0x10000>, // GICD - <0x0 0x2f100000 0 0x200000>, // GICR - <0x0 0x2c000000 0 0x2000>, // GICC - <0x0 0x2c010000 0 0x2000>, // GICH - <0x0 0x2c020000 0 0x2000>; // GICV - interrupts = <1 9 4>; - - msi-controller; - mbi-ranges = <256 128>; - - gic-its@2c200000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <1>; - reg = <0x0 0x2c200000 0 0x20000>; - }; - }; - - gic: interrupt-controller@2c010000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <4>; - #address-cells = <2>; - #size-cells = <2>; - ranges; - interrupt-controller; - redistributor-stride = <0x0 0x40000>; // 256kB stride - #redistributor-regions = <2>; - reg = <0x0 0x2c010000 0 0x10000>, // GICD - <0x0 0x2d000000 0 0x800000>, // GICR 1: CPUs 0-31 - <0x0 0x2e000000 0 0x800000>; // GICR 2: CPUs 32-63 - <0x0 0x2c040000 0 0x2000>, // GICC - <0x0 0x2c060000 0 0x2000>, // GICH - <0x0 0x2c080000 0 0x2000>; // GICV - interrupts = <1 9 4>; - - gic-its@2c200000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <1>; - reg = <0x0 0x2c200000 0 0x20000>; - }; - - gic-its@2c400000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <1>; - reg = <0x0 0x2c400000 0 0x20000>; - }; - - ppi-partitions { - part0: interrupt-partition-0 { - affinity = <&cpu0 &cpu2>; - }; - - part1: interrupt-partition-1 { - affinity = <&cpu1 &cpu3>; - }; - }; - }; - - - device@0 { - reg = <0 0 0 4>; - interrupts = <1 1 4 &part0>; - }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml new file mode 100644 index 000000000000..c34df35a25fc --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml @@ -0,0 +1,279 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/arm,gic-v3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Generic Interrupt Controller, version 3 + +maintainers: + - Marc Zyngier + +description: | + AArch64 SMP cores are often associated with a GICv3, providing Private + Peripheral Interrupts (PPI), Shared Peripheral Interrupts (SPI), + Software Generated Interrupts (SGI), and Locality-specific Peripheral + Interrupts (LPI). + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - qcom,msm8996-gic-v3 + - const: arm,gic-v3 + - const: arm,gic-v3 + + interrupt-controller: true + + "#address-cells": + enum: [ 0, 1, 2 ] + "#size-cells": + enum: [ 1, 2 ] + + ranges: true + + "#interrupt-cells": + description: | + Specifies the number of cells needed to encode an interrupt source. + Must be a single cell with a value of at least 3. + If the system requires describing PPI affinity, then the value must + be at least 4. + + The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI + interrupts. Other values are reserved for future use. + + The 2nd cell contains the interrupt number for the interrupt type. + SPI interrupts are in the range [0-987]. PPI interrupts are in the + range [0-15]. + + The 3rd cell is the flags, encoded as follows: + bits[3:0] trigger type and level flags. + 1 = edge triggered + 4 = level triggered + + The 4th cell is a phandle to a node describing a set of CPUs this + interrupt is affine to. The interrupt must be a PPI, and the node + pointed must be a subnode of the "ppi-partitions" subnode. For + interrupt types other than PPI or PPIs that are not partitionned, + this cell must be zero. See the "ppi-partitions" node description + below. + + Cells 5 and beyond are reserved for future use and must have a value + of 0 if present. + enum: [ 3, 4 ] + + reg: + description: | + Specifies base physical address(s) and size of the GIC + registers, in the following order: + - GIC Distributor interface (GICD) + - GIC Redistributors (GICR), one range per redistributor region + - GIC CPU interface (GICC) + - GIC Hypervisor interface (GICH) + - GIC Virtual CPU interface (GICV) + + GICC, GICH and GICV are optional. + minItems: 2 + maxItems: 4096 # Should be enough? + + interrupts: + description: + Interrupt source of the VGIC maintenance interrupt. + maxItems: 1 + + redistributor-stride: + description: + If using padding pages, specifies the stride of consecutive + redistributors. Must be a multiple of 64kB. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64 + - multipleOf: 0x10000 + exclusiveMinimum: 0 + + "#redistributor-regions": + description: + The number of independent contiguous regions occupied by the + redistributors. Required if more than one such region is present. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - maximum: 4096 # Should be enough? + + msi-controller: + description: + Only present if the Message Based Interrupt functionnality is + being exposed by the HW, and the mbi-ranges property present. + + mbi-ranges: + description: + A list of pairs , where "intid" is the first SPI of a range + that can be used an MBI, and "span" the size of that range. Multiple + ranges can be provided. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-matrix + - items: + minItems: 2 + maxItems: 2 + + mbi-alias: + description: + Address property. Base address of an alias of the GICD region containing + only the {SET,CLR}SPI registers to be used if isolation is required, + and if supported by the HW. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + minItems: 1 + maxItems: 2 + + ppi-partitions: + type: object + description: + PPI affinity can be expressed as a single "ppi-partitions" node, + containing a set of sub-nodes. + patternProperties: + "^interrupt-partition-[0-9]+$": + properties: + affinity: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Should be a list of phandles to CPU nodes (as described in + Documentation/devicetree/bindings/arm/cpus.yaml). + + required: + - affinity + +dependencies: + mbi-ranges: [ msi-controller ] + msi-controller: [ mbi-ranges ] + +required: + - compatible + - interrupts + - reg + +patternProperties: + "^gic-its@": false + "^interrupt-controller@[0-9a-f]+$": false + # msi-controller is preferred, but allow other names + "^(msi-controller|gic-its|interrupt-controller)@[0-9a-f]+$": + type: object + description: + GICv3 has one or more Interrupt Translation Services (ITS) that are + used to route Message Signalled Interrupts (MSI) to the CPUs. + properties: + compatible: + const: arm,gic-v3-its + + msi-controller: true + + "#msi-cells": + description: + The single msi-cell is the DeviceID of the device which will generate + the MSI. + const: 1 + + reg: + description: + Specifies the base physical address and size of the ITS registers. + maxItems: 1 + + socionext,synquacer-pre-its: + description: + (u32, u32) tuple describing the untranslated + address and size of the pre-ITS window. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + minItems: 2 + maxItems: 2 + + required: + - compatible + - msi-controller + - "#msi-cells" + - reg + + additionalProperties: false + +additionalProperties: false + +examples: + - | + gic: interrupt-controller@2cf00000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + interrupt-controller; + reg = <0x2f000000 0x10000>, // GICD + <0x2f100000 0x200000>, // GICR + <0x2c000000 0x2000>, // GICC + <0x2c010000 0x2000>, // GICH + <0x2c020000 0x2000>; // GICV + interrupts = <1 9 4>; + + msi-controller; + mbi-ranges = <256 128>; + + msi-controller@2c200000 { + compatible = "arm,gic-v3-its"; + msi-controller; + #msi-cells = <1>; + reg = <0x2c200000 0x20000>; + }; + }; + + interrupt-controller@2c010000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <4>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + interrupt-controller; + redistributor-stride = <0x0 0x40000>; // 256kB stride + #redistributor-regions = <2>; + reg = <0x2c010000 0x10000>, // GICD + <0x2d000000 0x800000>, // GICR 1: CPUs 0-31 + <0x2e000000 0x800000>, // GICR 2: CPUs 32-63 + <0x2c040000 0x2000>, // GICC + <0x2c060000 0x2000>, // GICH + <0x2c080000 0x2000>; // GICV + interrupts = <1 9 4>; + + msi-controller@2c200000 { + compatible = "arm,gic-v3-its"; + msi-controller; + #msi-cells = <1>; + reg = <0x2c200000 0x20000>; + }; + + msi-controller@2c400000 { + compatible = "arm,gic-v3-its"; + msi-controller; + #msi-cells = <1>; + reg = <0x2c400000 0x20000>; + }; + + ppi-partitions { + part0: interrupt-partition-0 { + affinity = <&cpu0 &cpu2>; + }; + + part1: interrupt-partition-1 { + affinity = <&cpu1 &cpu3>; + }; + }; + }; + + + device@0 { + reg = <0 4>; + interrupts = <1 1 4 &part0>; + }; + +... From e8ac9389f0d7c74fd988a5ce77765588f74dacd2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 12 Feb 2019 11:22:33 +0000 Subject: [PATCH 1732/4277] RDMA: Fix allocation failure on pointer pd The null check on an allocation failure on pd is currently checking if pd is non-null rather than null. Fix this by adding the missing ! operator. Fixes: 21a428a019c9 ("RDMA: Handle PD allocations by IB/core") Signed-off-by: Colin Ian King Reviewed-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c index c8c90072badd..97515c340134 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c @@ -745,7 +745,7 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev) ibdev = &hr_dev->ib_dev; pd = rdma_zalloc_drv_obj(ibdev, ib_pd); - if (pd) + if (!pd) goto alloc_mem_failed; pd->device = ibdev; From 36d577089d207c4832c4f24902167dcd47635ebe Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Tue, 12 Feb 2019 10:52:24 -0600 Subject: [PATCH 1733/4277] RDMA/rdmavt: Adapt to handle non-uniform sizes on umem SGEs rdmavt expects a uniform size on all umem SGEs which is currently at PAGE_SIZE. Adapt to a umem API change which could return non-uniform sized SGEs due to combining contiguous PAGE_SIZE regions into an SGE. Use for_each_sg_page variant to unfold the larger SGEs into a list of PAGE_SIZE elements. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Reviewed-by: Michael J. Ruhl Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rdmavt/mr.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/sw/rdmavt/mr.c b/drivers/infiniband/sw/rdmavt/mr.c index 8b1c1e8dd7ef..728795043496 100644 --- a/drivers/infiniband/sw/rdmavt/mr.c +++ b/drivers/infiniband/sw/rdmavt/mr.c @@ -381,8 +381,8 @@ struct ib_mr *rvt_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, { struct rvt_mr *mr; struct ib_umem *umem; - struct scatterlist *sg; - int n, m, entry; + struct sg_page_iter sg_iter; + int n, m; struct ib_mr *ret; if (length == 0) @@ -407,23 +407,21 @@ struct ib_mr *rvt_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mr->mr.access_flags = mr_access_flags; mr->umem = umem; - mr->mr.page_shift = umem->page_shift; + mr->mr.page_shift = PAGE_SHIFT; m = 0; n = 0; - for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { + for_each_sg_page (umem->sg_head.sgl, &sg_iter, umem->nmap, 0) { void *vaddr; - vaddr = page_address(sg_page(sg)); + vaddr = page_address(sg_page_iter_page(&sg_iter)); if (!vaddr) { ret = ERR_PTR(-EINVAL); goto bail_inval; } mr->mr.map[m]->segs[n].vaddr = vaddr; - mr->mr.map[m]->segs[n].length = BIT(umem->page_shift); - trace_rvt_mr_user_seg(&mr->mr, m, n, vaddr, - BIT(umem->page_shift)); - n++; - if (n == RVT_SEGSZ) { + mr->mr.map[m]->segs[n].length = PAGE_SIZE; + trace_rvt_mr_user_seg(&mr->mr, m, n, vaddr, PAGE_SIZE); + if (++n == RVT_SEGSZ) { m++; n = 0; } From 52a572e9f7d3a0e496b6b93870fbd71dd530306a Mon Sep 17 00:00:00 2001 From: "Shiraz, Saleem" Date: Tue, 12 Feb 2019 11:01:14 -0600 Subject: [PATCH 1734/4277] RDMA/nes: Use for_each_sg_dma_page iterator for umem SGL Use the for_each_sg_dma_page iterator variant to walk the umem DMA-mapped SGL and get the page DMA address. This avoids the extra loop to iterate pages in the SGE when for_each_sg iterator is used. Additionally, purge umem->page_shift usage in the driver as its only relevant for ODP MRs. Use system page size and shift instead. Signed-off-by: Shiraz, Saleem Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/nes/nes_verbs.c | 224 +++++++++++--------------- 1 file changed, 91 insertions(+), 133 deletions(-) diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index f18b28ae4bd9..138844299d73 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -2098,7 +2098,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, struct nes_device *nesdev = nesvnic->nesdev; struct nes_adapter *nesadapter = nesdev->nesadapter; struct ib_mr *ibmr = ERR_PTR(-EINVAL); - struct scatterlist *sg; + struct sg_dma_page_iter dma_iter; struct nes_ucontext *nes_ucontext; struct nes_pbl *nespbl; struct nes_mr *nesmr; @@ -2106,10 +2106,9 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, struct nes_mem_reg_req req; struct nes_vpbl vpbl; struct nes_root_vpbl root_vpbl; - int entry, page_index; + int page_index; int page_count = 0; int err, pbl_depth = 0; - int chunk_pages; int ret; u32 stag; u32 stag_index = 0; @@ -2121,7 +2120,6 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u16 pbl_count; u8 single_page = 1; u8 stag_key; - int first_page = 1; region = ib_umem_get(udata, start, length, acc, 0); if (IS_ERR(region)) { @@ -2172,127 +2170,99 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, } nesmr->region = region; - for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) { - if (sg_dma_address(sg) & ~PAGE_MASK) { - ib_umem_release(region); - nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index); - nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n", - (unsigned int) sg_dma_address(sg)); - ibmr = ERR_PTR(-EINVAL); - kfree(nesmr); - goto reg_user_mr_err; - } + for_each_sg_dma_page (region->sg_head.sgl, &dma_iter, region->nmap, 0) { - if (!sg_dma_len(sg)) { - ib_umem_release(region); - nes_free_resource(nesadapter, nesadapter->allocated_mrs, - stag_index); - nes_debug(NES_DBG_MR, "Invalid Buffer Size\n"); - ibmr = ERR_PTR(-EINVAL); - kfree(nesmr); - goto reg_user_mr_err; - } - - region_length += sg_dma_len(sg); - chunk_pages = sg_dma_len(sg) >> 12; + region_length += PAGE_SIZE; region_length -= skip_pages << 12; - for (page_index = skip_pages; page_index < chunk_pages; page_index++) { - skip_pages = 0; - if ((page_count != 0) && (page_count << 12) - (ib_umem_offset(region) & (4096 - 1)) >= region->length) - goto enough_pages; - if ((page_count&0x01FF) == 0) { - if (page_count >= 1024 * 512) { + skip_pages = 0; + if ((page_count != 0) && (page_count << 12) - (ib_umem_offset(region) & (4096 - 1)) >= region->length) + goto enough_pages; + if ((page_count & 0x01FF) == 0) { + if (page_count >= 1024 * 512) { + ib_umem_release(region); + nes_free_resource(nesadapter, + nesadapter->allocated_mrs, stag_index); + kfree(nesmr); + ibmr = ERR_PTR(-E2BIG); + goto reg_user_mr_err; + } + if (root_pbl_index == 1) { + root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, + 8192, &root_vpbl.pbl_pbase); + nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n", + root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase); + if (!root_vpbl.pbl_vbase) { ib_umem_release(region); - nes_free_resource(nesadapter, - nesadapter->allocated_mrs, stag_index); + pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, + vpbl.pbl_pbase); + nes_free_resource(nesadapter, nesadapter->allocated_mrs, + stag_index); kfree(nesmr); - ibmr = ERR_PTR(-E2BIG); - goto reg_user_mr_err; - } - if (root_pbl_index == 1) { - root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, - 8192, &root_vpbl.pbl_pbase); - nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n", - root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase); - if (!root_vpbl.pbl_vbase) { - ib_umem_release(region); - pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, - vpbl.pbl_pbase); - nes_free_resource(nesadapter, nesadapter->allocated_mrs, - stag_index); - kfree(nesmr); - ibmr = ERR_PTR(-ENOMEM); - goto reg_user_mr_err; - } - root_vpbl.leaf_vpbl = kcalloc(1024, - sizeof(*root_vpbl.leaf_vpbl), - GFP_KERNEL); - if (!root_vpbl.leaf_vpbl) { - ib_umem_release(region); - pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase, - root_vpbl.pbl_pbase); - pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, - vpbl.pbl_pbase); - nes_free_resource(nesadapter, nesadapter->allocated_mrs, - stag_index); - kfree(nesmr); - ibmr = ERR_PTR(-ENOMEM); - goto reg_user_mr_err; - } - root_vpbl.pbl_vbase[0].pa_low = - cpu_to_le32((u32)vpbl.pbl_pbase); - root_vpbl.pbl_vbase[0].pa_high = - cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32))); - root_vpbl.leaf_vpbl[0] = vpbl; - } - vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096, - &vpbl.pbl_pbase); - nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n", - vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase); - if (!vpbl.pbl_vbase) { - ib_umem_release(region); - nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index); ibmr = ERR_PTR(-ENOMEM); - kfree(nesmr); goto reg_user_mr_err; } - if (1 <= root_pbl_index) { - root_vpbl.pbl_vbase[root_pbl_index].pa_low = - cpu_to_le32((u32)vpbl.pbl_pbase); - root_vpbl.pbl_vbase[root_pbl_index].pa_high = - cpu_to_le32((u32)((((u64)vpbl.pbl_pbase)>>32))); - root_vpbl.leaf_vpbl[root_pbl_index] = vpbl; + root_vpbl.leaf_vpbl = kcalloc(1024, + sizeof(*root_vpbl.leaf_vpbl), + GFP_KERNEL); + if (!root_vpbl.leaf_vpbl) { + ib_umem_release(region); + pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase, + root_vpbl.pbl_pbase); + pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, + vpbl.pbl_pbase); + nes_free_resource(nesadapter, nesadapter->allocated_mrs, + stag_index); + kfree(nesmr); + ibmr = ERR_PTR(-ENOMEM); + goto reg_user_mr_err; } - root_pbl_index++; - cur_pbl_index = 0; + root_vpbl.pbl_vbase[0].pa_low = + cpu_to_le32((u32)vpbl.pbl_pbase); + root_vpbl.pbl_vbase[0].pa_high = + cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32))); + root_vpbl.leaf_vpbl[0] = vpbl; } - if (single_page) { - if (page_count != 0) { - if ((last_dma_addr+4096) != - (sg_dma_address(sg)+ - (page_index*4096))) - single_page = 0; - last_dma_addr = sg_dma_address(sg)+ - (page_index*4096); - } else { - first_dma_addr = sg_dma_address(sg)+ - (page_index*4096); - last_dma_addr = first_dma_addr; - } + vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096, + &vpbl.pbl_pbase); + nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n", + vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase); + if (!vpbl.pbl_vbase) { + ib_umem_release(region); + nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index); + ibmr = ERR_PTR(-ENOMEM); + kfree(nesmr); + goto reg_user_mr_err; } - - vpbl.pbl_vbase[cur_pbl_index].pa_low = - cpu_to_le32((u32)(sg_dma_address(sg)+ - (page_index*4096))); - vpbl.pbl_vbase[cur_pbl_index].pa_high = - cpu_to_le32((u32)((((u64)(sg_dma_address(sg)+ - (page_index*4096))) >> 32))); - cur_pbl_index++; - page_count++; + if (1 <= root_pbl_index) { + root_vpbl.pbl_vbase[root_pbl_index].pa_low = + cpu_to_le32((u32)vpbl.pbl_pbase); + root_vpbl.pbl_vbase[root_pbl_index].pa_high = + cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32))); + root_vpbl.leaf_vpbl[root_pbl_index] = vpbl; + } + root_pbl_index++; + cur_pbl_index = 0; } + if (single_page) { + if (page_count != 0) { + if ((last_dma_addr + 4096) != sg_page_iter_dma_address(&dma_iter)) + single_page = 0; + last_dma_addr = sg_page_iter_dma_address(&dma_iter); + } else { + first_dma_addr = sg_page_iter_dma_address(&dma_iter); + last_dma_addr = first_dma_addr; + } + } + + vpbl.pbl_vbase[cur_pbl_index].pa_low = + cpu_to_le32((u32)(sg_page_iter_dma_address(&dma_iter))); + vpbl.pbl_vbase[cur_pbl_index].pa_high = + cpu_to_le32((u32)((u64)(sg_page_iter_dma_address(&dma_iter)))); + cur_pbl_index++; + page_count++; } - enough_pages: +enough_pages: nes_debug(NES_DBG_MR, "calculating stag, stag_index=0x%08x, driver_key=0x%08x," " stag_key=0x%08x\n", stag_index, driver_key, stag_key); @@ -2334,7 +2304,7 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, ibmr = ERR_PTR(-ENOMEM); } - reg_user_mr_err: +reg_user_mr_err: /* free the resources */ if (root_pbl_index == 1) { pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, @@ -2401,26 +2371,14 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, nespbl->pbl_size, (unsigned long) nespbl->pbl_pbase, (void *) nespbl->pbl_vbase, nespbl->user_base); - for_each_sg(region->sg_head.sgl, sg, region->nmap, entry) { - chunk_pages = sg_dma_len(sg) >> 12; - chunk_pages += (sg_dma_len(sg) & (4096-1)) ? 1 : 0; - if (first_page) { - nespbl->page = sg_page(sg); - first_page = 0; - } - - for (page_index = 0; page_index < chunk_pages; page_index++) { - ((__le32 *)pbl)[0] = cpu_to_le32((u32) - (sg_dma_address(sg)+ - (page_index*4096))); - ((__le32 *)pbl)[1] = cpu_to_le32(((u64) - (sg_dma_address(sg)+ - (page_index*4096)))>>32); - nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl, - (unsigned long long)*pbl, - le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0])); - pbl++; - } + nespbl->page = sg_page(region->sg_head.sgl); + for_each_sg_dma_page(region->sg_head.sgl, &dma_iter, region->nmap, 0) { + ((__le32 *)pbl)[0] = cpu_to_le32((u32)(sg_page_iter_dma_address(&dma_iter))); + ((__le32 *)pbl)[1] = cpu_to_le32(((u64)(sg_page_iter_dma_address(&dma_iter)))>>32); + nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl, + (unsigned long long)*pbl, + le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0])); + pbl++; } if (req.reg_type == IWNES_MEMREG_TYPE_QP) { From ebb09b33c60c46fd4f7ffa0af9e693eebe765d1b Mon Sep 17 00:00:00 2001 From: Leonid Ravich Date: Tue, 12 Feb 2019 22:09:28 +0200 Subject: [PATCH 1735/4277] NTB: add new parameter to peer_db_addr() db_bit and db_data NTB door bell usage depends on NTB hardware. ex: intel NTB gen1 has one peer door bell register which can be controlled by the bitmap writen to it, while Intel NTB gen3 has a registers per door bell and the data trigering the each door bell is always 1. therefore exposing only peer door bell address forcing the user to be aware of such low level details Signed-off-by: Leonid Ravich Acked-by: Logan Gunthorpe Acked-by: Dave Jiang Acked-by: Allen Hubbe Signed-off-by: Jon Mason --- drivers/ntb/hw/intel/ntb_hw_gen1.c | 25 ++++++++++++++----- drivers/ntb/hw/intel/ntb_hw_gen1.h | 5 ++-- drivers/ntb/hw/intel/ntb_hw_gen3.c | 33 +++++++++++++++++++++++++- drivers/ntb/hw/mscc/ntb_hw_switchtec.c | 9 ++++++- include/linux/ntb.h | 10 +++++--- 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c index 2ad263f708da..bb57ec239029 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c @@ -180,7 +180,7 @@ int ndev_mw_to_bar(struct intel_ntb_dev *ndev, int idx) return ndev->reg->mw_bar[idx]; } -static inline int ndev_db_addr(struct intel_ntb_dev *ndev, +void ndev_db_addr(struct intel_ntb_dev *ndev, phys_addr_t *db_addr, resource_size_t *db_size, phys_addr_t reg_addr, unsigned long reg) { @@ -196,8 +196,6 @@ static inline int ndev_db_addr(struct intel_ntb_dev *ndev, *db_size = ndev->reg->db_size; dev_dbg(&ndev->ntb.pdev->dev, "Peer db size %llx\n", *db_size); } - - return 0; } u64 ndev_db_read(struct intel_ntb_dev *ndev, @@ -1111,13 +1109,28 @@ int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) ndev->self_reg->db_mask); } -int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size) +static int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, + resource_size_t *db_size, u64 *db_data, int db_bit) { + u64 db_bits; struct intel_ntb_dev *ndev = ntb_ndev(ntb); - return ndev_db_addr(ndev, db_addr, db_size, ndev->peer_addr, + if (unlikely(db_bit >= BITS_PER_LONG_LONG)) + return -EINVAL; + + db_bits = BIT_ULL(db_bit); + + if (unlikely(db_bits & ~ntb_ndev(ntb)->db_valid_mask)) + return -EINVAL; + + ndev_db_addr(ndev, db_addr, db_size, ndev->peer_addr, ndev->peer_reg->db_bell); + + if (db_data) + *db_data = db_bits; + + + return 0; } static int intel_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.h b/drivers/ntb/hw/intel/ntb_hw_gen1.h index ad8ec1444436..544cf5c06f4d 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.h +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.h @@ -147,6 +147,9 @@ extern struct intel_b2b_addr xeon_b2b_dsd_addr; int ndev_init_isr(struct intel_ntb_dev *ndev, int msix_min, int msix_max, int msix_shift, int total_shift); enum ntb_topo xeon_ppd_topo(struct intel_ntb_dev *ndev, u8 ppd); +void ndev_db_addr(struct intel_ntb_dev *ndev, + phys_addr_t *db_addr, resource_size_t *db_size, + phys_addr_t reg_addr, unsigned long reg); u64 ndev_db_read(struct intel_ntb_dev *ndev, void __iomem *mmio); int ndev_db_write(struct intel_ntb_dev *ndev, u64 db_bits, void __iomem *mmio); @@ -166,8 +169,6 @@ int intel_ntb_db_vector_count(struct ntb_dev *ntb); u64 intel_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector); int intel_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits); int intel_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits); -int intel_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size); int intel_ntb_spad_is_unsafe(struct ntb_dev *ntb); int intel_ntb_spad_count(struct ntb_dev *ntb); u32 intel_ntb_spad_read(struct ntb_dev *ntb, int idx); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen3.c b/drivers/ntb/hw/intel/ntb_hw_gen3.c index b3fa24778f94..f475b56a3f49 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen3.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen3.c @@ -532,6 +532,37 @@ static int intel_ntb3_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, return 0; } +int intel_ntb3_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, + resource_size_t *db_size, + u64 *db_data, int db_bit) +{ + phys_addr_t db_addr_base; + struct intel_ntb_dev *ndev = ntb_ndev(ntb); + + if (unlikely(db_bit >= BITS_PER_LONG_LONG)) + return -EINVAL; + + if (unlikely(BIT_ULL(db_bit) & ~ntb_ndev(ntb)->db_valid_mask)) + return -EINVAL; + + ndev_db_addr(ndev, &db_addr_base, db_size, ndev->peer_addr, + ndev->peer_reg->db_bell); + + if (db_addr) { + *db_addr = db_addr_base + (db_bit * 4); + dev_dbg(&ndev->ntb.pdev->dev, "Peer db addr %llx db bit %d\n", + *db_addr, db_bit); + } + + if (db_data) { + *db_data = 1; + dev_dbg(&ndev->ntb.pdev->dev, "Peer db data %llx db bit %d\n", + *db_data, db_bit); + } + + return 0; +} + static int intel_ntb3_peer_db_set(struct ntb_dev *ntb, u64 db_bits) { struct intel_ntb_dev *ndev = ntb_ndev(ntb); @@ -584,7 +615,7 @@ const struct ntb_dev_ops intel_ntb3_ops = { .db_clear = intel_ntb3_db_clear, .db_set_mask = intel_ntb_db_set_mask, .db_clear_mask = intel_ntb_db_clear_mask, - .peer_db_addr = intel_ntb_peer_db_addr, + .peer_db_addr = intel_ntb3_peer_db_addr, .peer_db_set = intel_ntb3_peer_db_set, .spad_is_unsafe = intel_ntb_spad_is_unsafe, .spad_count = intel_ntb_spad_count, diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index f6f00354047b..9ae944597708 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -710,11 +710,16 @@ static u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb) static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size) + resource_size_t *db_size, + u64 *db_data, + int db_bit) { struct switchtec_ntb *sndev = ntb_sndev(ntb); unsigned long offset; + if (unlikely(db_bit >= BITS_PER_LONG_LONG)) + return -EINVAL; + offset = (unsigned long)sndev->mmio_peer_dbmsg->odb - (unsigned long)sndev->stdev->mmio; @@ -724,6 +729,8 @@ static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, *db_addr = pci_resource_start(ntb->pdev, 0) + offset; if (db_size) *db_size = sizeof(u32); + if (db_data) + *db_data = BIT_ULL(db_bit) << sndev->db_peer_shift; return 0; } diff --git a/include/linux/ntb.h b/include/linux/ntb.h index 181d16601dd9..56a92e3ae3ae 100644 --- a/include/linux/ntb.h +++ b/include/linux/ntb.h @@ -296,7 +296,8 @@ struct ntb_dev_ops { int (*db_clear_mask)(struct ntb_dev *ntb, u64 db_bits); int (*peer_db_addr)(struct ntb_dev *ntb, - phys_addr_t *db_addr, resource_size_t *db_size); + phys_addr_t *db_addr, resource_size_t *db_size, + u64 *db_data, int db_bit); u64 (*peer_db_read)(struct ntb_dev *ntb); int (*peer_db_set)(struct ntb_dev *ntb, u64 db_bits); int (*peer_db_clear)(struct ntb_dev *ntb, u64 db_bits); @@ -1078,6 +1079,8 @@ static inline int ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) * @ntb: NTB device context. * @db_addr: OUT - The address of the peer doorbell register. * @db_size: OUT - The number of bytes to write the peer doorbell register. + * @db_data: OUT - The data of peer doorbell register + * @db_bit: door bell bit number * * Return the address of the peer doorbell register. This may be used, for * example, by drivers that offload memory copy operations to a dma engine. @@ -1091,12 +1094,13 @@ static inline int ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) */ static inline int ntb_peer_db_addr(struct ntb_dev *ntb, phys_addr_t *db_addr, - resource_size_t *db_size) + resource_size_t *db_size, + u64 *db_data, int db_bit) { if (!ntb->ops->peer_db_addr) return -EINVAL; - return ntb->ops->peer_db_addr(ntb, db_addr, db_size); + return ntb->ops->peer_db_addr(ntb, db_addr, db_size, db_data, db_bit); } /** From 0ccc61b1c76e5163c6fea6cf83bd18e7ea244c5b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:05 -0500 Subject: [PATCH 1736/4277] SUNRPC: Add xdr_stream::rqst field Having access to the controlling rpc_rqst means a trace point in the XDR code can report: - the XID - the task ID and client ID - the p_name of RPC being processed Subsequent patches will introduce such trace points. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- fs/nfs/callback_xdr.c | 5 +++-- fs/nfs/flexfilelayout/flexfilelayout.c | 2 +- include/linux/sunrpc/xdr.h | 8 ++++++-- net/sunrpc/auth.c | 4 ++-- net/sunrpc/auth_gss/auth_gss.c | 4 ++-- net/sunrpc/xdr.c | 12 +++++++++--- net/sunrpc/xprtrdma/backchannel.c | 2 +- net/sunrpc/xprtrdma/rpc_rdma.c | 4 ++-- 8 files changed, 26 insertions(+), 15 deletions(-) diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index a87a56273407..bc7c1766a2be 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -943,10 +943,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) }; unsigned int nops = 0; - xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); + xdr_init_decode(&xdr_in, &rqstp->rq_arg, + rqstp->rq_arg.head[0].iov_base, NULL); p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len); - xdr_init_encode(&xdr_out, &rqstp->rq_res, p); + xdr_init_encode(&xdr_out, &rqstp->rq_res, p, NULL); status = decode_compound_hdr_arg(&xdr_in, &hdr_arg); if (status == htonl(NFS4ERR_RESOURCE)) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 63abe705f4ca..32701b6a9566 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2036,7 +2036,7 @@ ff_layout_encode_layoutreturn(struct xdr_stream *xdr, dprintk("%s: Begin\n", __func__); - xdr_init_encode(&tmp_xdr, &tmp_buf, NULL); + xdr_init_encode(&tmp_xdr, &tmp_buf, NULL, NULL); ff_layout_encode_ioerr(&tmp_xdr, args, ff_args); ff_layout_encode_iostats_array(&tmp_xdr, args, ff_args); diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 2ec128060239..787939d13643 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -217,6 +217,8 @@ struct xdr_stream { struct kvec scratch; /* Scratch buffer */ struct page **page_ptr; /* pointer to the current page */ unsigned int nwords; /* Remaining decode buffer length */ + + struct rpc_rqst *rqst; /* For debugging */ }; /* @@ -227,7 +229,8 @@ typedef void (*kxdreproc_t)(struct rpc_rqst *rqstp, struct xdr_stream *xdr, typedef int (*kxdrdproc_t)(struct rpc_rqst *rqstp, struct xdr_stream *xdr, void *obj); -extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); +extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, + __be32 *p, struct rpc_rqst *rqst); extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); extern void xdr_commit_encode(struct xdr_stream *xdr); extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len); @@ -235,7 +238,8 @@ extern int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen); extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, unsigned int len); extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); -extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); +extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, + __be32 *p, struct rpc_rqst *rqst); extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, struct page **pages, unsigned int len); extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen); diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index f3023bbc0b7f..8dfab6119e6a 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -798,7 +798,7 @@ static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, { struct xdr_stream xdr; - xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data); + xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data, rqstp); encode(rqstp, &xdr, obj); } @@ -823,7 +823,7 @@ rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, { struct xdr_stream xdr; - xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data); + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data, rqstp); return decode(rqstp, &xdr, obj); } diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 1531b0219344..a42672e81792 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1722,7 +1722,7 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, { struct xdr_stream xdr; - xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p); + xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p, rqstp); encode(rqstp, &xdr, obj); } @@ -1998,7 +1998,7 @@ gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, { struct xdr_stream xdr; - xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p, rqstp); return decode(rqstp, &xdr, obj); } diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index f302c6eb8779..345f08b634ee 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -483,6 +483,7 @@ EXPORT_SYMBOL_GPL(xdr_stream_pos); * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer in which to encode data * @p: current pointer inside XDR buffer + * @rqst: pointer to controlling rpc_rqst, for debugging * * Note: at the moment the RPC client only passes the length of our * scratch buffer in the xdr_buf's header kvec. Previously this @@ -491,7 +492,8 @@ EXPORT_SYMBOL_GPL(xdr_stream_pos); * of the buffer length, and takes care of adjusting the kvec * length for us. */ -void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) +void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, + struct rpc_rqst *rqst) { struct kvec *iov = buf->head; int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len; @@ -513,6 +515,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) buf->len += len; iov->iov_len += len; } + xdr->rqst = rqst; } EXPORT_SYMBOL_GPL(xdr_init_encode); @@ -819,8 +822,10 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr) * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer from which to decode data * @p: current pointer inside XDR buffer + * @rqst: pointer to controlling rpc_rqst, for debugging */ -void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) +void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, + struct rpc_rqst *rqst) { xdr->buf = buf; xdr->scratch.iov_base = NULL; @@ -836,6 +841,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) xdr->nwords -= p - xdr->p; xdr->p = p; } + xdr->rqst = rqst; } EXPORT_SYMBOL_GPL(xdr_init_decode); @@ -854,7 +860,7 @@ void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, buf->page_len = len; buf->buflen = len; buf->len = len; - xdr_init_decode(xdr, buf, NULL); + xdr_init_decode(xdr, buf, NULL, NULL); } EXPORT_SYMBOL_GPL(xdr_init_decode_pages); diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c index 0de9b3e63770..98c1e43eb7b1 100644 --- a/net/sunrpc/xprtrdma/backchannel.c +++ b/net/sunrpc/xprtrdma/backchannel.c @@ -123,7 +123,7 @@ static int rpcrdma_bc_marshal_reply(struct rpc_rqst *rqst) rpcrdma_set_xdrlen(&req->rl_hdrbuf, 0); xdr_init_encode(&req->rl_stream, &req->rl_hdrbuf, - req->rl_rdmabuf->rg_base); + req->rl_rdmabuf->rg_base, rqst); p = xdr_reserve_space(&req->rl_stream, 28); if (unlikely(!p)) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 7774aee7c013..6c1fb270f127 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -748,7 +748,7 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst) rpcrdma_set_xdrlen(&req->rl_hdrbuf, 0); xdr_init_encode(xdr, &req->rl_hdrbuf, - req->rl_rdmabuf->rg_base); + req->rl_rdmabuf->rg_base, rqst); /* Fixed header fields */ ret = -EMSGSIZE; @@ -1329,7 +1329,7 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) /* Fixed transport header fields */ xdr_init_decode(&rep->rr_stream, &rep->rr_hdrbuf, - rep->rr_hdrbuf.head[0].iov_base); + rep->rr_hdrbuf.head[0].iov_base, NULL); p = xdr_inline_decode(&rep->rr_stream, 4 * sizeof(*p)); if (unlikely(!p)) goto out_shortreply; From ed675ed9da6d951322efd72d739d6b5ce1c18f02 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 31 Jan 2019 11:54:16 -0700 Subject: [PATCH 1737/4277] =?UTF-8?q?selftests:=20ir:=20fix=20warning:=20"?= =?UTF-8?q?%s"=20directive=20output=20may=20be=20truncated=20=E2=80=99=20d?= =?UTF-8?q?irective=20output=20may=20be=20truncated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following warning by sizing the buffer to max. of sysfs path max. size + d_name max. size. gcc -Wall -O2 -I../../../include/uapi ir_loopback.c -o ../tools/testing/selftests/ir/ir_loopback ir_loopback.c: In function ‘lirc_open’: ir_loopback.c:71:37: warning: ‘%s’ directive output may be truncated writing up to 255 bytes into a region of size 95 [-Wformat-truncation=] snprintf(buf, sizeof(buf), "/dev/%s", dent->d_name); ^~ In file included from /usr/include/stdio.h:862:0, from ir_loopback.c:14: /usr/include/x86_64-linux-gnu/bits/stdio2.h:64:10: note: ‘__builtin___snprintf_chk’ output between 6 and 261 bytes into a destination of size 100 return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ __bos (__s), __fmt, __va_arg_pack ()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Shuah Khan Acked-by: Sean Young Signed-off-by: Shuah Khan --- tools/testing/selftests/ir/ir_loopback.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/ir/ir_loopback.c b/tools/testing/selftests/ir/ir_loopback.c index 858c19caf224..8cdf1b89ac9c 100644 --- a/tools/testing/selftests/ir/ir_loopback.c +++ b/tools/testing/selftests/ir/ir_loopback.c @@ -27,6 +27,8 @@ #define TEST_SCANCODES 10 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define SYSFS_PATH_MAX 256 +#define DNAME_PATH_MAX 256 static const struct { enum rc_proto proto; @@ -56,7 +58,7 @@ static const struct { int lirc_open(const char *rc) { struct dirent *dent; - char buf[100]; + char buf[SYSFS_PATH_MAX + DNAME_PATH_MAX]; DIR *d; int fd; From a5180977a3253a14aec5534fead920078ee98943 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 31 Jan 2019 13:00:54 -0700 Subject: [PATCH 1738/4277] selftests: ir: skip when lirc device doesn't exist. Skip instead of fail when lirc device doesn't exist. Signed-off-by: Shuah Khan Acked-by: Sean Young Signed-off-by: Shuah Khan --- tools/testing/selftests/ir/ir_loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ir/ir_loopback.c b/tools/testing/selftests/ir/ir_loopback.c index 8cdf1b89ac9c..ff351bb7c163 100644 --- a/tools/testing/selftests/ir/ir_loopback.c +++ b/tools/testing/selftests/ir/ir_loopback.c @@ -76,7 +76,7 @@ int lirc_open(const char *rc) } if (!dent) - ksft_exit_fail_msg("cannot find lirc device for %s\n", rc); + ksft_exit_skip("cannot find lirc device for %s\n", rc); closedir(d); From 6d771c60e50f89d017a74df62900ecd7ce65ab61 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 31 Jan 2019 12:43:35 -0700 Subject: [PATCH 1739/4277] selftests: ir: skip when non-root user runs the test Skip instead of fail when non-root user runs the test. Signed-off-by: Shuah Khan Acked-by: Sean Young Signed-off-by: Shuah Khan --- tools/testing/selftests/ir/ir_loopback.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/testing/selftests/ir/ir_loopback.sh b/tools/testing/selftests/ir/ir_loopback.sh index 0a0b8dfa39be..b90dc9939f45 100755 --- a/tools/testing/selftests/ir/ir_loopback.sh +++ b/tools/testing/selftests/ir/ir_loopback.sh @@ -4,6 +4,11 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 +if [ $UID != 0 ]; then + echo "Please run ir_loopback test as root [SKIP]" + exit $ksft_skip +fi + if ! /sbin/modprobe -q -n rc-loopback; then echo "ir_loopback: module rc-loopback is not found [SKIP]" exit $ksft_skip From ae2917093fb60bdc1ed3e5757b74801420569a9a Mon Sep 17 00:00:00 2001 From: Abhishek Goel Date: Tue, 5 Feb 2019 04:51:28 -0600 Subject: [PATCH 1740/4277] tools/power/cpupower: Display boost frequency separately cpufreq driver creates sysfs file "scaling_boost_frequency" for platforms which support boost frequency. Cpupower now prints boost frequencies separately. For few x86 vendors who already have different way to get boost frequency, will continue to use the existing logic. Rest of the platforms will rely on "scaling_boost_frequency" file to display boost frequency. Signed-off-by: Abhishek Goel Signed-off-by: Shuah Khan --- tools/power/cpupower/lib/cpufreq.c | 19 +++++----- tools/power/cpupower/lib/cpufreq.h | 16 ++++----- tools/power/cpupower/utils/cpufreq-info.c | 42 +++++++++++++++++------ 3 files changed, 50 insertions(+), 27 deletions(-) diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c index 0c0f3e3f0d80..80650497fb80 100644 --- a/tools/power/cpupower/lib/cpufreq.c +++ b/tools/power/cpupower/lib/cpufreq.c @@ -333,17 +333,20 @@ void cpufreq_put_available_governors(struct cpufreq_available_governors *any) } -struct cpufreq_available_frequencies -*cpufreq_get_available_frequencies(unsigned int cpu) +struct cpufreq_frequencies +*cpufreq_get_frequencies(const char *type, unsigned int cpu) { - struct cpufreq_available_frequencies *first = NULL; - struct cpufreq_available_frequencies *current = NULL; + struct cpufreq_frequencies *first = NULL; + struct cpufreq_frequencies *current = NULL; char one_value[SYSFS_PATH_MAX]; char linebuf[MAX_LINE_LEN]; + char fname[MAX_LINE_LEN]; unsigned int pos, i; unsigned int len; - len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", + snprintf(fname, MAX_LINE_LEN, "scaling_%s_frequencies", type); + + len = sysfs_cpufreq_read_file(cpu, fname, linebuf, sizeof(linebuf)); if (len == 0) return NULL; @@ -389,9 +392,9 @@ struct cpufreq_available_frequencies return NULL; } -void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies - *any) { - struct cpufreq_available_frequencies *tmp, *next; +void cpufreq_put_frequencies(struct cpufreq_frequencies *any) +{ + struct cpufreq_frequencies *tmp, *next; if (!any) return; diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h index 60beaf5ed2ea..775738269cbf 100644 --- a/tools/power/cpupower/lib/cpufreq.h +++ b/tools/power/cpupower/lib/cpufreq.h @@ -28,10 +28,10 @@ struct cpufreq_available_governors { struct cpufreq_available_governors *first; }; -struct cpufreq_available_frequencies { +struct cpufreq_frequencies { unsigned long frequency; - struct cpufreq_available_frequencies *next; - struct cpufreq_available_frequencies *first; + struct cpufreq_frequencies *next; + struct cpufreq_frequencies *first; }; @@ -129,14 +129,14 @@ void cpufreq_put_available_governors( * * Only present on _some_ ->target() cpufreq drivers. For information purposes * only. Please free allocated memory by calling - * cpufreq_put_available_frequencies after use. + * cpufreq_put_frequencies after use. */ -struct cpufreq_available_frequencies -*cpufreq_get_available_frequencies(unsigned int cpu); +struct cpufreq_frequencies +*cpufreq_get_frequencies(const char *type, unsigned int cpu); -void cpufreq_put_available_frequencies( - struct cpufreq_available_frequencies *first); +void cpufreq_put_frequencies( + struct cpufreq_frequencies *first); /* determine affected CPUs diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index c3f39d5128ee..10290b308797 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -161,19 +161,12 @@ static void print_duration(unsigned long duration) return; } -/* --boost / -b */ - -static int get_boost_mode(unsigned int cpu) +static int get_boost_mode_x86(unsigned int cpu) { int support, active, b_states = 0, ret, pstate_no, i; /* ToDo: Make this more global */ unsigned long pstates[MAX_HW_PSTATES] = {0,}; - if (cpupower_cpu_info.vendor != X86_VENDOR_AMD && - cpupower_cpu_info.vendor != X86_VENDOR_HYGON && - cpupower_cpu_info.vendor != X86_VENDOR_INTEL) - return 0; - ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states); if (ret) { printf(_("Error while evaluating Boost Capabilities" @@ -248,6 +241,33 @@ static int get_boost_mode(unsigned int cpu) return 0; } +/* --boost / -b */ + +static int get_boost_mode(unsigned int cpu) +{ + struct cpufreq_frequencies *freqs; + + if (cpupower_cpu_info.vendor == X86_VENDOR_AMD || + cpupower_cpu_info.vendor == X86_VENDOR_HYGON || + cpupower_cpu_info.vendor == X86_VENDOR_INTEL) + return get_boost_mode_x86(cpu); + + freqs = cpufreq_get_frequencies("boost", cpu); + if (freqs) { + printf(_(" boost frequency steps: ")); + while (freqs->next) { + print_speed(freqs->frequency); + printf(", "); + freqs = freqs->next; + } + print_speed(freqs->frequency); + printf("\n"); + cpufreq_put_frequencies(freqs); + } + + return 0; +} + /* --freq / -f */ static int get_freq_kernel(unsigned int cpu, unsigned int human) @@ -456,7 +476,7 @@ static int get_latency(unsigned int cpu, unsigned int human) static void debug_output_one(unsigned int cpu) { - struct cpufreq_available_frequencies *freqs; + struct cpufreq_frequencies *freqs; get_driver(cpu); get_related_cpus(cpu); @@ -464,7 +484,7 @@ static void debug_output_one(unsigned int cpu) get_latency(cpu, 1); get_hardware_limits(cpu, 1); - freqs = cpufreq_get_available_frequencies(cpu); + freqs = cpufreq_get_frequencies("available", cpu); if (freqs) { printf(_(" available frequency steps: ")); while (freqs->next) { @@ -474,7 +494,7 @@ static void debug_output_one(unsigned int cpu) } print_speed(freqs->frequency); printf("\n"); - cpufreq_put_available_frequencies(freqs); + cpufreq_put_frequencies(freqs); } get_available_governors(cpu); From 5582863f450ca44c472dbf4812a62f4e0e68b0c8 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:10 -0500 Subject: [PATCH 1741/4277] SUNRPC: Add XDR overflow trace event This can help field troubleshooting without needing the overhead of a full network capture (ie, tcpdump). Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/trace/events/sunrpc.h | 67 +++++++++++++++++++++++++++++++++++ net/sunrpc/xdr.c | 24 +++++++++---- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index f88b0f52aa7e..fbc41b8142d3 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -254,6 +254,73 @@ TRACE_EVENT(rpc_stats_latency, __entry->backlog, __entry->rtt, __entry->execute) ); +TRACE_EVENT(rpc_xdr_overflow, + TP_PROTO( + const struct xdr_stream *xdr, + size_t requested + ), + + TP_ARGS(xdr, requested), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(int, version) + __field(size_t, requested) + __field(const void *, end) + __field(const void *, p) + __field(const void *, head_base) + __field(size_t, head_len) + __field(const void *, tail_base) + __field(size_t, tail_len) + __field(unsigned int, page_len) + __field(unsigned int, len) + __string(progname, + xdr->rqst->rq_task->tk_client->cl_program->name) + __string(procedure, + xdr->rqst->rq_task->tk_msg.rpc_proc->p_name) + ), + + TP_fast_assign( + if (xdr->rqst) { + const struct rpc_task *task = xdr->rqst->rq_task; + + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __assign_str(progname, + task->tk_client->cl_program->name) + __entry->version = task->tk_client->cl_vers; + __assign_str(procedure, task->tk_msg.rpc_proc->p_name) + } else { + __entry->task_id = 0; + __entry->client_id = 0; + __assign_str(progname, "unknown") + __entry->version = 0; + __assign_str(procedure, "unknown") + } + __entry->requested = requested; + __entry->end = xdr->end; + __entry->p = xdr->p; + __entry->head_base = xdr->buf->head[0].iov_base, + __entry->head_len = xdr->buf->head[0].iov_len, + __entry->page_len = xdr->buf->page_len, + __entry->tail_base = xdr->buf->tail[0].iov_base, + __entry->tail_len = xdr->buf->tail[0].iov_len, + __entry->len = xdr->buf->len; + ), + + TP_printk( + "task:%u@%u %sv%d %s requested=%zu p=%p end=%p xdr=[%p,%zu]/%u/[%p,%zu]/%u\n", + __entry->task_id, __entry->client_id, + __get_str(progname), __entry->version, __get_str(procedure), + __entry->requested, __entry->p, __entry->end, + __entry->head_base, __entry->head_len, + __entry->page_len, + __entry->tail_base, __entry->tail_len, + __entry->len + ) +); + /* * First define the enums in the below macros to be exported to userspace * via TRACE_DEFINE_ENUM(). diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 345f08b634ee..6d0b615a02ae 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -16,6 +16,7 @@ #include #include #include +#include /* * XDR functions for basic NFS types @@ -554,9 +555,9 @@ static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr, int frag1bytes, frag2bytes; if (nbytes > PAGE_SIZE) - return NULL; /* Bigger buffers require special handling */ + goto out_overflow; /* Bigger buffers require special handling */ if (xdr->buf->len + nbytes > xdr->buf->buflen) - return NULL; /* Sorry, we're totally out of space */ + goto out_overflow; /* Sorry, we're totally out of space */ frag1bytes = (xdr->end - xdr->p) << 2; frag2bytes = nbytes - frag1bytes; if (xdr->iov) @@ -585,6 +586,9 @@ static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr, xdr->buf->page_len += frag2bytes; xdr->buf->len += nbytes; return p; +out_overflow: + trace_rpc_xdr_overflow(xdr, nbytes); + return NULL; } /** @@ -902,20 +906,23 @@ static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes) size_t cplen = (char *)xdr->end - (char *)xdr->p; if (nbytes > xdr->scratch.iov_len) - return NULL; + goto out_overflow; p = __xdr_inline_decode(xdr, cplen); if (p == NULL) return NULL; memcpy(cpdest, p, cplen); + if (!xdr_set_next_buffer(xdr)) + goto out_overflow; cpdest += cplen; nbytes -= cplen; - if (!xdr_set_next_buffer(xdr)) - return NULL; p = __xdr_inline_decode(xdr, nbytes); if (p == NULL) return NULL; memcpy(cpdest, p, nbytes); return xdr->scratch.iov_base; +out_overflow: + trace_rpc_xdr_overflow(xdr, nbytes); + return NULL; } /** @@ -932,14 +939,17 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) { __be32 *p; - if (nbytes == 0) + if (unlikely(nbytes == 0)) return xdr->p; if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) - return NULL; + goto out_overflow; p = __xdr_inline_decode(xdr, nbytes); if (p != NULL) return p; return xdr_copy_to_scratch(xdr, nbytes); +out_overflow: + trace_rpc_xdr_overflow(xdr, nbytes); + return NULL; } EXPORT_SYMBOL_GPL(xdr_inline_decode); From 7be9cea3600ba24f45733f823cb4ecb5a40f5db7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:16 -0500 Subject: [PATCH 1742/4277] SUNRPC: Add trace event that reports reply page vector alignment We don't want READ payloads that are partially in the head iovec and in the page buffer because this requires pull-up, which can be expensive. The NFS/RPC client tries hard to predict the size of the head iovec so that the incoming READ data payload lands only in the page vector, but it doesn't always get it right. To help diagnose such problems, add a trace point in the logic that decodes READ-like operations that reports whether pull-up is being done. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/trace/events/sunrpc.h | 59 +++++++++++++++++++++++++++++++++++ net/sunrpc/xdr.c | 33 ++++++++++++++++---- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index fbc41b8142d3..627650800676 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -321,6 +321,65 @@ TRACE_EVENT(rpc_xdr_overflow, ) ); +TRACE_EVENT(rpc_xdr_alignment, + TP_PROTO( + const struct xdr_stream *xdr, + size_t offset, + unsigned int copied + ), + + TP_ARGS(xdr, offset, copied), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(int, version) + __field(size_t, offset) + __field(unsigned int, copied) + __field(const void *, head_base) + __field(size_t, head_len) + __field(const void *, tail_base) + __field(size_t, tail_len) + __field(unsigned int, page_len) + __field(unsigned int, len) + __string(progname, + xdr->rqst->rq_task->tk_client->cl_program->name) + __string(procedure, + xdr->rqst->rq_task->tk_msg.rpc_proc->p_name) + ), + + TP_fast_assign( + const struct rpc_task *task = xdr->rqst->rq_task; + + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __assign_str(progname, + task->tk_client->cl_program->name) + __entry->version = task->tk_client->cl_vers; + __assign_str(procedure, task->tk_msg.rpc_proc->p_name) + + __entry->offset = offset; + __entry->copied = copied; + __entry->head_base = xdr->buf->head[0].iov_base, + __entry->head_len = xdr->buf->head[0].iov_len, + __entry->page_len = xdr->buf->page_len, + __entry->tail_base = xdr->buf->tail[0].iov_base, + __entry->tail_len = xdr->buf->tail[0].iov_len, + __entry->len = xdr->buf->len; + ), + + TP_printk( + "task:%u@%u %sv%d %s offset=%zu copied=%u xdr=[%p,%zu]/%u/[%p,%zu]/%u\n", + __entry->task_id, __entry->client_id, + __get_str(progname), __entry->version, __get_str(procedure), + __entry->offset, __entry->copied, + __entry->head_base, __entry->head_len, + __entry->page_len, + __entry->tail_base, __entry->tail_len, + __entry->len + ) +); + /* * First define the enums in the below macros to be exported to userspace * via TRACE_DEFINE_ENUM(). diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 6d0b615a02ae..5f0aa53fa4ae 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -347,13 +347,15 @@ EXPORT_SYMBOL_GPL(_copy_from_pages); * 'len' bytes. The extra data is not lost, but is instead * moved into the inlined pages and/or the tail. */ -static void +static unsigned int xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) { struct kvec *head, *tail; size_t copy, offs; unsigned int pglen = buf->page_len; + unsigned int result; + result = 0; tail = buf->tail; head = buf->head; @@ -367,6 +369,7 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) copy = tail->iov_len - len; memmove((char *)tail->iov_base + len, tail->iov_base, copy); + result += copy; } /* Copy from the inlined pages into the tail */ copy = len; @@ -377,11 +380,13 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) copy = 0; else if (copy > tail->iov_len - offs) copy = tail->iov_len - offs; - if (copy != 0) + if (copy != 0) { _copy_from_pages((char *)tail->iov_base + offs, buf->pages, buf->page_base + pglen + offs - len, copy); + result += copy; + } /* Do we also need to copy data from the head into the tail ? */ if (len > pglen) { offs = copy = len - pglen; @@ -391,6 +396,7 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) (char *)head->iov_base + head->iov_len - offs, copy); + result += copy; } } /* Now handle pages */ @@ -406,12 +412,15 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) _copy_to_pages(buf->pages, buf->page_base, (char *)head->iov_base + head->iov_len - len, copy); + result += copy; } head->iov_len -= len; buf->buflen -= len; /* Have we truncated the message? */ if (buf->len > buf->buflen) buf->len = buf->buflen; + + return result; } /** @@ -423,14 +432,16 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) * 'len' bytes. The extra data is not lost, but is instead * moved into the tail. */ -static void +static unsigned int xdr_shrink_pagelen(struct xdr_buf *buf, size_t len) { struct kvec *tail; size_t copy; unsigned int pglen = buf->page_len; unsigned int tailbuf_len; + unsigned int result; + result = 0; tail = buf->tail; BUG_ON (len > pglen); @@ -448,18 +459,22 @@ xdr_shrink_pagelen(struct xdr_buf *buf, size_t len) if (tail->iov_len > len) { char *p = (char *)tail->iov_base + len; memmove(p, tail->iov_base, tail->iov_len - len); + result += tail->iov_len - len; } else copy = tail->iov_len; /* Copy from the inlined pages into the tail */ _copy_from_pages((char *)tail->iov_base, buf->pages, buf->page_base + pglen - len, copy); + result += copy; } buf->page_len -= len; buf->buflen -= len; /* Have we truncated the message? */ if (buf->len > buf->buflen) buf->len = buf->buflen; + + return result; } void @@ -959,13 +974,17 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) struct kvec *iov; unsigned int nwords = XDR_QUADLEN(len); unsigned int cur = xdr_stream_pos(xdr); + unsigned int copied, offset; if (xdr->nwords == 0) return 0; + /* Realign pages to current pointer position */ - iov = buf->head; + iov = buf->head; if (iov->iov_len > cur) { - xdr_shrink_bufhead(buf, iov->iov_len - cur); + offset = iov->iov_len - cur; + copied = xdr_shrink_bufhead(buf, offset); + trace_rpc_xdr_alignment(xdr, offset, copied); xdr->nwords = XDR_QUADLEN(buf->len - cur); } @@ -977,7 +996,9 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) len = buf->page_len; else if (nwords < xdr->nwords) { /* Truncate page data and move it into the tail */ - xdr_shrink_pagelen(buf, buf->page_len - len); + offset = buf->page_len - len; + copied = xdr_shrink_pagelen(buf, offset); + trace_rpc_xdr_alignment(xdr, offset, copied); xdr->nwords = XDR_QUADLEN(buf->len - cur); } return len; From eb72f484a5eb94c53a241e6a7811270fb25200ad Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:21 -0500 Subject: [PATCH 1743/4277] NFS: Remove print_overflow_msg() This issue is now captured by a trace point in the RPC client. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- fs/lockd/clnt4xdr.c | 14 -- fs/lockd/clntxdr.c | 14 -- fs/nfs/callback_xdr.c | 59 +++--- fs/nfs/nfs2xdr.c | 84 +++----- fs/nfs/nfs3xdr.c | 163 +++++---------- fs/nfs/nfs42xdr.c | 21 +- fs/nfs/nfs4xdr.c | 451 +++++++++++------------------------------ fs/nfsd/nfs4callback.c | 13 -- 8 files changed, 219 insertions(+), 600 deletions(-) diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index 214a2fa1f1e3..7df6324ccb8a 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -74,17 +74,6 @@ static void nlm4_compute_offsets(const struct nlm_lock *lock, *l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); } -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("lockd: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - - /* * Encode/decode NLMv4 basic data types * @@ -176,7 +165,6 @@ out_size: dprintk("NFS: returned cookie was too long: %u\n", length); return -EIO; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } @@ -236,7 +224,6 @@ out_bad_xdr: __func__, be32_to_cpup(p)); return -EIO; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } @@ -309,7 +296,6 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result) out: return error; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 747b9c8c940a..4df62f635529 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -70,17 +70,6 @@ static void nlm_compute_offsets(const struct nlm_lock *lock, *l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1); } -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("lockd: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - - /* * Encode/decode NLMv3 basic data types * @@ -173,7 +162,6 @@ out_size: dprintk("NFS: returned cookie was too long: %u\n", length); return -EIO; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } @@ -231,7 +219,6 @@ out_enum: __func__, be32_to_cpup(p)); return -EIO; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } @@ -303,7 +290,6 @@ static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result) out: return error; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index bc7c1766a2be..06233bfa6d73 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -72,16 +72,6 @@ static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p) return xdr_ressize_check(rqstp, p); } -static __be32 *read_buf(struct xdr_stream *xdr, size_t nbytes) -{ - __be32 *p; - - p = xdr_inline_decode(xdr, nbytes); - if (unlikely(p == NULL)) - printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed!\n"); - return p; -} - static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str, size_t maxlen) { @@ -98,13 +88,13 @@ static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh) { __be32 *p; - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); fh->size = ntohl(*p); if (fh->size > NFS4_FHSIZE) return htonl(NFS4ERR_BADHANDLE); - p = read_buf(xdr, fh->size); + p = xdr_inline_decode(xdr, fh->size); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); memcpy(&fh->data[0], p, fh->size); @@ -117,11 +107,11 @@ static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) __be32 *p; unsigned int attrlen; - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); attrlen = ntohl(*p); - p = read_buf(xdr, attrlen << 2); + p = xdr_inline_decode(xdr, attrlen << 2); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); if (likely(attrlen > 0)) @@ -135,7 +125,7 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) { __be32 *p; - p = read_buf(xdr, NFS4_STATEID_SIZE); + p = xdr_inline_decode(xdr, NFS4_STATEID_SIZE); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); memcpy(stateid->data, p, NFS4_STATEID_SIZE); @@ -156,7 +146,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound status = decode_string(xdr, &hdr->taglen, &hdr->tag, CB_OP_TAGLEN_MAXSZ); if (unlikely(status != 0)) return status; - p = read_buf(xdr, 12); + p = xdr_inline_decode(xdr, 12); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); hdr->minorversion = ntohl(*p++); @@ -176,7 +166,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op) { __be32 *p; - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE_HDR); *op = ntohl(*p); @@ -205,7 +195,7 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, status = decode_delegation_stateid(xdr, &args->stateid); if (unlikely(status != 0)) return status; - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); args->truncate = ntohl(*p); @@ -227,7 +217,7 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, __be32 status = 0; uint32_t iomode; - p = read_buf(xdr, 4 * sizeof(uint32_t)); + p = xdr_inline_decode(xdr, 4 * sizeof(uint32_t)); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); @@ -245,14 +235,14 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, if (unlikely(status != 0)) return status; - p = read_buf(xdr, 2 * sizeof(uint64_t)); + p = xdr_inline_decode(xdr, 2 * sizeof(uint64_t)); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); p = xdr_decode_hyper(p, &args->cbl_range.offset); p = xdr_decode_hyper(p, &args->cbl_range.length); return decode_layout_stateid(xdr, &args->cbl_stateid); } else if (args->cbl_recall_type == RETURN_FSID) { - p = read_buf(xdr, 2 * sizeof(uint64_t)); + p = xdr_inline_decode(xdr, 2 * sizeof(uint64_t)); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); p = xdr_decode_hyper(p, &args->cbl_fsid.major); @@ -275,7 +265,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp, args->ndevs = 0; /* Num of device notifications */ - p = read_buf(xdr, sizeof(uint32_t)); + p = xdr_inline_decode(xdr, sizeof(uint32_t)); if (unlikely(p == NULL)) { status = htonl(NFS4ERR_BADXDR); goto out; @@ -298,7 +288,8 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp, for (i = 0; i < n; i++) { struct cb_devicenotifyitem *dev = &args->devs[i]; - p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE); + p = xdr_inline_decode(xdr, (4 * sizeof(uint32_t)) + + NFS4_DEVICEID4_SIZE); if (unlikely(p == NULL)) { status = htonl(NFS4ERR_BADXDR); goto err; @@ -329,7 +320,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp, p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) { - p = read_buf(xdr, sizeof(uint32_t)); + p = xdr_inline_decode(xdr, sizeof(uint32_t)); if (unlikely(p == NULL)) { status = htonl(NFS4ERR_BADXDR); goto err; @@ -359,7 +350,7 @@ static __be32 decode_sessionid(struct xdr_stream *xdr, { __be32 *p; - p = read_buf(xdr, NFS4_MAX_SESSIONID_LEN); + p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); @@ -379,13 +370,13 @@ static __be32 decode_rc_list(struct xdr_stream *xdr, goto out; status = htonl(NFS4ERR_RESOURCE); - p = read_buf(xdr, sizeof(uint32_t)); + p = xdr_inline_decode(xdr, sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; rc_list->rcl_nrefcalls = ntohl(*p++); if (rc_list->rcl_nrefcalls) { - p = read_buf(xdr, + p = xdr_inline_decode(xdr, rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t)); if (unlikely(p == NULL)) goto out; @@ -418,7 +409,7 @@ static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, if (status) return status; - p = read_buf(xdr, 5 * sizeof(uint32_t)); + p = xdr_inline_decode(xdr, 5 * sizeof(uint32_t)); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); @@ -461,7 +452,7 @@ static __be32 decode_recallany_args(struct svc_rqst *rqstp, uint32_t bitmap[2]; __be32 *p, status; - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); args->craa_objs_to_keep = ntohl(*p++); @@ -480,7 +471,7 @@ static __be32 decode_recallslot_args(struct svc_rqst *rqstp, struct cb_recallslotargs *args = argp; __be32 *p; - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); args->crsa_target_highest_slotid = ntohl(*p++); @@ -492,14 +483,14 @@ static __be32 decode_lockowner(struct xdr_stream *xdr, struct cb_notify_lock_arg __be32 *p; unsigned int len; - p = read_buf(xdr, 12); + p = xdr_inline_decode(xdr, 12); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); p = xdr_decode_hyper(p, &args->cbnl_owner.clientid); len = be32_to_cpu(*p); - p = read_buf(xdr, len); + p = xdr_inline_decode(xdr, len); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); @@ -537,7 +528,7 @@ static __be32 decode_write_response(struct xdr_stream *xdr, __be32 *p; /* skip the always zero field */ - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) goto out; p++; @@ -577,7 +568,7 @@ static __be32 decode_offload_args(struct svc_rqst *rqstp, return status; /* decode status */ - p = read_buf(xdr, 4); + p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) goto out; args->error = ntohl(*p++); diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 350675e3ed47..76614311f957 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -79,17 +79,6 @@ static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); } -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("NFS: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - - /* * Encode/decode NFSv2 basic data types * @@ -110,8 +99,8 @@ static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; count = be32_to_cpup(p); recvd = xdr_read_pages(xdr, count); if (unlikely(count > recvd)) @@ -125,9 +114,6 @@ out_cheating: "count %u > recvd %u\n", count, recvd); count = recvd; goto out; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -157,13 +143,10 @@ static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; *status = be32_to_cpup(p); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -205,14 +188,11 @@ static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) __be32 *p; p = xdr_inline_decode(xdr, NFS2_FHSIZE); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; fh->size = NFS2_FHSIZE; memcpy(fh->data, p, NFS2_FHSIZE); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -282,8 +262,8 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) __be32 *p; p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; fattr->valid |= NFS_ATTR_FATTR_V2; @@ -325,9 +305,6 @@ out_uid: out_gid: dprintk("NFS: returned invalid gid\n"); return -EINVAL; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -416,23 +393,20 @@ static int decode_filename_inline(struct xdr_stream *xdr, u32 count; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; count = be32_to_cpup(p); if (count > NFS3_MAXNAMLEN) goto out_nametoolong; p = xdr_inline_decode(xdr, count); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; *name = (const char *)p; *length = count; return 0; out_nametoolong: dprintk("NFS: returned filename too long: %u\n", count); return -ENAMETOOLONG; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -455,8 +429,8 @@ static int decode_path(struct xdr_stream *xdr) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; length = be32_to_cpup(p); if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) goto out_size; @@ -472,9 +446,6 @@ out_cheating: dprintk("NFS: server cheating in pathname result: " "length %u > received %u\n", length, recvd); return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -951,12 +922,12 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int error; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; if (*p++ == xdr_zero) { p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; if (*p++ == xdr_zero) return -EAGAIN; entry->eof = 1; @@ -964,8 +935,8 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, } p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; entry->ino = be32_to_cpup(p); error = decode_filename_inline(xdr, &entry->name, &entry->len); @@ -978,17 +949,13 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, */ entry->prev_cookie = entry->cookie; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; entry->cookie = be32_to_cpup(p); entry->d_type = DT_UNKNOWN; return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EAGAIN; } /* @@ -1052,17 +1019,14 @@ static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) __be32 *p; p = xdr_inline_decode(xdr, NFS_info_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; result->tsize = be32_to_cpup(p++); result->bsize = be32_to_cpup(p++); result->blocks = be32_to_cpup(p++); result->bfree = be32_to_cpup(p++); result->bavail = be32_to_cpup(p); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr, diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 78df4eb60f85..e5619803a4f6 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -118,17 +118,6 @@ static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); } -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("NFS: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - - /* * Encode/decode NFSv3 basic data types * @@ -151,13 +140,10 @@ static int decode_uint32(struct xdr_stream *xdr, u32 *value) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; *value = be32_to_cpup(p); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_uint64(struct xdr_stream *xdr, u64 *value) @@ -165,13 +151,10 @@ static int decode_uint64(struct xdr_stream *xdr, u64 *value) __be32 *p; p = xdr_inline_decode(xdr, 8); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; xdr_decode_hyper(p, value); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -211,14 +194,14 @@ static int decode_inline_filename3(struct xdr_stream *xdr, u32 count; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; count = be32_to_cpup(p); if (count > NFS3_MAXNAMLEN) goto out_nametoolong; p = xdr_inline_decode(xdr, count); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; *name = (const char *)p; *length = count; return 0; @@ -226,9 +209,6 @@ static int decode_inline_filename3(struct xdr_stream *xdr, out_nametoolong: dprintk("NFS: returned filename too long: %u\n", count); return -ENAMETOOLONG; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -249,8 +229,8 @@ static int decode_nfspath3(struct xdr_stream *xdr) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; count = be32_to_cpup(p); if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN)) goto out_nametoolong; @@ -267,9 +247,6 @@ out_cheating: dprintk("NFS: server cheating in pathname result: " "count %u > recvd %u\n", count, recvd); return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -303,13 +280,10 @@ static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier) __be32 *p; p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; memcpy(verifier, p, NFS3_COOKIEVERFSIZE); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -330,13 +304,10 @@ static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier * __be32 *p; p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; memcpy(verifier->data, p, NFS3_WRITEVERFSIZE); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -364,13 +335,10 @@ static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; *status = be32_to_cpup(p); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -453,23 +421,20 @@ static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; length = be32_to_cpup(p++); if (unlikely(length > NFS3_FHSIZE)) goto out_toobig; p = xdr_inline_decode(xdr, length); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; fh->size = length; memcpy(fh->data, p, length); return 0; out_toobig: dprintk("NFS: file handle size (%u) too big\n", length); return -E2BIG; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static void zero_nfs_fh3(struct nfs_fh *fh) @@ -655,8 +620,8 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) __be32 *p; p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; p = xdr_decode_ftype3(p, &fmode); @@ -690,9 +655,6 @@ out_uid: out_gid: dprintk("NFS: returned invalid gid\n"); return -EINVAL; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -710,14 +672,11 @@ static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; if (*p != xdr_zero) return decode_fattr3(xdr, fattr); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -733,8 +692,8 @@ static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) __be32 *p; p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; fattr->valid |= NFS_ATTR_FATTR_PRESIZE | NFS_ATTR_FATTR_PRECHANGE @@ -747,9 +706,6 @@ static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -773,14 +729,11 @@ static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) __be32 *p; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; if (*p != xdr_zero) return decode_wcc_attr(xdr, fattr); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr) @@ -808,15 +761,12 @@ out: static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh) { __be32 *p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; if (*p != xdr_zero) return decode_nfs_fh3(xdr, fh); zero_nfs_fh3(fh); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -1643,8 +1593,8 @@ static int decode_read3resok(struct xdr_stream *xdr, __be32 *p; p = xdr_inline_decode(xdr, 4 + 4 + 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; count = be32_to_cpup(p++); eof = be32_to_cpup(p++); ocount = be32_to_cpup(p++); @@ -1667,9 +1617,6 @@ out_cheating: count = recvd; eof = 0; goto out; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr, @@ -1731,22 +1678,18 @@ static int decode_write3resok(struct xdr_stream *xdr, __be32 *p; p = xdr_inline_decode(xdr, 4 + 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; result->count = be32_to_cpup(p++); result->verf->committed = be32_to_cpup(p++); if (unlikely(result->verf->committed > NFS_FILE_SYNC)) goto out_badvalue; if (decode_writeverf3(xdr, &result->verf->verifier)) - goto out_eio; + return -EIO; return result->count; out_badvalue: dprintk("NFS: bad stable_how value: %u\n", result->verf->committed); return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); -out_eio: - return -EIO; } static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr, @@ -2010,12 +1953,12 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, u64 new_cookie; p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; if (*p == xdr_zero) { p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; if (*p == xdr_zero) return -EAGAIN; entry->eof = 1; @@ -2051,8 +1994,8 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, /* In fact, a post_op_fh3: */ p = xdr_inline_decode(xdr, 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EAGAIN; if (*p != xdr_zero) { error = decode_nfs_fh3(xdr, entry->fh); if (unlikely(error)) { @@ -2069,9 +2012,6 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EAGAIN; out_truncated: dprintk("NFS: directory entry contains invalid file handle\n"); *entry = old; @@ -2183,8 +2123,8 @@ static int decode_fsstat3resok(struct xdr_stream *xdr, __be32 *p; p = xdr_inline_decode(xdr, 8 * 6 + 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; p = xdr_decode_size3(p, &result->tbytes); p = xdr_decode_size3(p, &result->fbytes); p = xdr_decode_size3(p, &result->abytes); @@ -2193,9 +2133,6 @@ static int decode_fsstat3resok(struct xdr_stream *xdr, xdr_decode_size3(p, &result->afiles); /* ignore invarsec */ return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req, @@ -2255,8 +2192,8 @@ static int decode_fsinfo3resok(struct xdr_stream *xdr, __be32 *p; p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; result->rtmax = be32_to_cpup(p++); result->rtpref = be32_to_cpup(p++); result->rtmult = be32_to_cpup(p++); @@ -2270,9 +2207,6 @@ static int decode_fsinfo3resok(struct xdr_stream *xdr, /* ignore properties */ result->lease_time = 0; return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req, @@ -2328,15 +2262,12 @@ static int decode_pathconf3resok(struct xdr_stream *xdr, __be32 *p; p = xdr_inline_decode(xdr, 4 * 6); - if (unlikely(p == NULL)) - goto out_overflow; + if (unlikely(!p)) + return -EIO; result->max_link = be32_to_cpup(p++); result->max_namelen = be32_to_cpup(p); /* ignore remaining fields */ return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req, diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 69f72ed2bf87..22b3425df08f 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -394,7 +394,7 @@ static int decode_write_response(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; count = be32_to_cpup(p); if (count > 1) return -EREMOTEIO; @@ -402,18 +402,14 @@ static int decode_write_response(struct xdr_stream *xdr, status = decode_opaque_fixed(xdr, &res->stateid, NFS4_STATEID_SIZE); if (unlikely(status)) - goto out_overflow; + return -EIO; } p = xdr_inline_decode(xdr, 8 + 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; p = xdr_decode_hyper(p, &res->count); res->verifier.committed = be32_to_cpup(p); return decode_verifier(xdr, &res->verifier.verifier); - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_copy_requirements(struct xdr_stream *xdr, @@ -422,14 +418,11 @@ static int decode_copy_requirements(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4 + 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->consecutive = be32_to_cpup(p++); res->synchronous = be32_to_cpup(p++); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_copy(struct xdr_stream *xdr, struct nfs42_copy_res *res) @@ -474,15 +467,11 @@ static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res) p = xdr_inline_decode(xdr, 4 + 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->sr_eof = be32_to_cpup(p++); p = xdr_decode_hyper(p, &res->sr_offset); return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_layoutstats(struct xdr_stream *xdr) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 2fc8f6fa25e4..24e6a45a5726 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -3144,22 +3144,12 @@ static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req, } #endif /* CONFIG_NFS_V4_1 */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("nfs: %s: prematurely hit end of receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string) { ssize_t ret = xdr_stream_decode_opaque_inline(xdr, (void **)string, NFS4_OPAQUE_LIMIT); - if (unlikely(ret < 0)) { - if (ret == -EBADMSG) - print_overflow_msg(__func__, xdr); + if (unlikely(ret < 0)) return -EIO; - } *len = ret; return 0; } @@ -3170,22 +3160,19 @@ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; hdr->status = be32_to_cpup(p++); hdr->taglen = be32_to_cpup(p); p = xdr_inline_decode(xdr, hdr->taglen + 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; hdr->tag = (char *)p; p += XDR_QUADLEN(hdr->taglen); hdr->nops = be32_to_cpup(p); if (unlikely(hdr->nops < 1)) return nfs4_stat_to_errno(hdr->status); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static bool __decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected, @@ -3214,7 +3201,6 @@ out_bad_operation: *nfs_retval = -EREMOTEIO; return false; out_overflow: - print_overflow_msg(__func__, xdr); *nfs_retval = -EIO; return false; } @@ -3235,10 +3221,9 @@ static int decode_ace(struct xdr_stream *xdr, void *ace) char *str; p = xdr_inline_decode(xdr, 12); - if (likely(p)) - return decode_opaque_inline(xdr, &strlen, &str); - print_overflow_msg(__func__, xdr); - return -EIO; + if (unlikely(!p)) + return -EIO; + return decode_opaque_inline(xdr, &strlen, &str); } static ssize_t @@ -3249,10 +3234,9 @@ decode_bitmap4(struct xdr_stream *xdr, uint32_t *bitmap, size_t sz) ret = xdr_stream_decode_uint32_array(xdr, bitmap, sz); if (likely(ret >= 0)) return ret; - if (ret == -EMSGSIZE) - return sz; - print_overflow_msg(__func__, xdr); - return -EIO; + if (ret != -EMSGSIZE) + return -EIO; + return sz; } static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) @@ -3268,13 +3252,10 @@ static int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, unsigne p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *attrlen = be32_to_cpup(p); *savep = xdr_stream_pos(xdr); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask) @@ -3303,7 +3284,7 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t * if (likely(bitmap[0] & FATTR4_WORD0_TYPE)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *type = be32_to_cpup(p); if (*type < NF4REG || *type > NF4NAMEDATTR) { dprintk("%s: bad type %d\n", __func__, *type); @@ -3314,9 +3295,6 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t * } dprintk("%s: type=0%o\n", __func__, nfs_type2fmt[*type]); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_fh_expire_type(struct xdr_stream *xdr, @@ -3330,15 +3308,12 @@ static int decode_attr_fh_expire_type(struct xdr_stream *xdr, if (likely(bitmap[0] & FATTR4_WORD0_FH_EXPIRE_TYPE)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *type = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_FH_EXPIRE_TYPE; } dprintk("%s: expire type=0x%x\n", __func__, *type); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change) @@ -3352,7 +3327,7 @@ static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, change); bitmap[0] &= ~FATTR4_WORD0_CHANGE; ret = NFS_ATTR_FATTR_CHANGE; @@ -3360,9 +3335,6 @@ static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t dprintk("%s: change attribute=%Lu\n", __func__, (unsigned long long)*change); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *size) @@ -3376,16 +3348,13 @@ static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t * if (likely(bitmap[0] & FATTR4_WORD0_SIZE)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, size); bitmap[0] &= ~FATTR4_WORD0_SIZE; ret = NFS_ATTR_FATTR_SIZE; } dprintk("%s: file size=%Lu\n", __func__, (unsigned long long)*size); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) @@ -3398,15 +3367,12 @@ static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, ui if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *res = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT; } dprintk("%s: link support=%s\n", __func__, *res == 0 ? "false" : "true"); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) @@ -3419,15 +3385,12 @@ static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *res = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT; } dprintk("%s: symlink support=%s\n", __func__, *res == 0 ? "false" : "true"); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid) @@ -3442,7 +3405,7 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs if (likely(bitmap[0] & FATTR4_WORD0_FSID)) { p = xdr_inline_decode(xdr, 16); if (unlikely(!p)) - goto out_overflow; + return -EIO; p = xdr_decode_hyper(p, &fsid->major); xdr_decode_hyper(p, &fsid->minor); bitmap[0] &= ~FATTR4_WORD0_FSID; @@ -3452,9 +3415,6 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs (unsigned long long)fsid->major, (unsigned long long)fsid->minor); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) @@ -3467,15 +3427,12 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint if (likely(bitmap[0] & FATTR4_WORD0_LEASE_TIME)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *res = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_LEASE_TIME; } dprintk("%s: file size=%u\n", __func__, (unsigned int)*res); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap, int32_t *res) @@ -3487,14 +3444,11 @@ static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap, int32_t * if (likely(bitmap[0] & FATTR4_WORD0_RDATTR_ERROR)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; *res = -be32_to_cpup(p); } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_exclcreat_supported(struct xdr_stream *xdr, @@ -3526,13 +3480,13 @@ static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, stru if (likely(bitmap[0] & FATTR4_WORD0_FILEHANDLE)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p); if (len > NFS4_FHSIZE) return -EIO; p = xdr_inline_decode(xdr, len); if (unlikely(!p)) - goto out_overflow; + return -EIO; if (fh != NULL) { memcpy(fh->data, p, len); fh->size = len; @@ -3540,9 +3494,6 @@ static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, stru bitmap[0] &= ~FATTR4_WORD0_FILEHANDLE; } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) @@ -3555,15 +3506,12 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *res = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT; } dprintk("%s: ACLs supported=%u\n", __func__, (unsigned int)*res); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid) @@ -3577,16 +3525,13 @@ static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, fileid); bitmap[0] &= ~FATTR4_WORD0_FILEID; ret = NFS_ATTR_FATTR_FILEID; } dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid) @@ -3600,16 +3545,13 @@ static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitma if (likely(bitmap[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, fileid); bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; ret = NFS_ATTR_FATTR_MOUNTED_ON_FILEID; } dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) @@ -3623,15 +3565,12 @@ static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uin if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL; } dprintk("%s: files avail=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) @@ -3645,15 +3584,12 @@ static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[0] &= ~FATTR4_WORD0_FILES_FREE; } dprintk("%s: files free=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) @@ -3667,15 +3603,12 @@ static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uin if (likely(bitmap[0] & FATTR4_WORD0_FILES_TOTAL)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[0] &= ~FATTR4_WORD0_FILES_TOTAL; } dprintk("%s: files total=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path) @@ -3686,7 +3619,7 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path) p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; n = be32_to_cpup(p); if (n == 0) goto root_path; @@ -3718,9 +3651,6 @@ out_eio: dprintk(" status %d", status); status = -EIO; goto out; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res) @@ -3745,7 +3675,7 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st goto out; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + goto out_eio; n = be32_to_cpup(p); if (n <= 0) goto out_eio; @@ -3758,7 +3688,7 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st loc = &res->locations[res->nlocations]; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + goto out_eio; m = be32_to_cpup(p); dprintk("%s: servers:\n", __func__); @@ -3796,8 +3726,6 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st out: dprintk("%s: fs_locations done, error = %d\n", __func__, status); return status; -out_overflow: - print_overflow_msg(__func__, xdr); out_eio: status = -EIO; goto out; @@ -3814,15 +3742,12 @@ static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uin if (likely(bitmap[0] & FATTR4_WORD0_MAXFILESIZE)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[0] &= ~FATTR4_WORD0_MAXFILESIZE; } dprintk("%s: maxfilesize=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink) @@ -3836,15 +3761,12 @@ static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_ if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *maxlink = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_MAXLINK; } dprintk("%s: maxlink=%u\n", __func__, *maxlink); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxname) @@ -3858,15 +3780,12 @@ static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_ if (likely(bitmap[0] & FATTR4_WORD0_MAXNAME)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *maxname = be32_to_cpup(p); bitmap[0] &= ~FATTR4_WORD0_MAXNAME; } dprintk("%s: maxname=%u\n", __func__, *maxname); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) @@ -3881,7 +3800,7 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_ uint64_t maxread; p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, &maxread); if (maxread > 0x7FFFFFFF) maxread = 0x7FFFFFFF; @@ -3890,9 +3809,6 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_ } dprintk("%s: maxread=%lu\n", __func__, (unsigned long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) @@ -3907,7 +3823,7 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32 uint64_t maxwrite; p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, &maxwrite); if (maxwrite > 0x7FFFFFFF) maxwrite = 0x7FFFFFFF; @@ -3916,9 +3832,6 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32 } dprintk("%s: maxwrite=%lu\n", __func__, (unsigned long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *mode) @@ -3933,7 +3846,7 @@ static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *m if (likely(bitmap[1] & FATTR4_WORD1_MODE)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; tmp = be32_to_cpup(p); *mode = tmp & ~S_IFMT; bitmap[1] &= ~FATTR4_WORD1_MODE; @@ -3941,9 +3854,6 @@ static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *m } dprintk("%s: file mode=0%o\n", __func__, (unsigned int)*mode); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *nlink) @@ -3957,16 +3867,13 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t if (likely(bitmap[1] & FATTR4_WORD1_NUMLINKS)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; *nlink = be32_to_cpup(p); bitmap[1] &= ~FATTR4_WORD1_NUMLINKS; ret = NFS_ATTR_FATTR_NLINK; } dprintk("%s: nlink=%u\n", __func__, (unsigned int)*nlink); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static ssize_t decode_nfs4_string(struct xdr_stream *xdr, @@ -4011,10 +3918,9 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, return NFS_ATTR_FATTR_OWNER; } out: - if (len != -EBADMSG) - return 0; - print_overflow_msg(__func__, xdr); - return -EIO; + if (len == -EBADMSG) + return -EIO; + return 0; } static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, @@ -4046,10 +3952,9 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, return NFS_ATTR_FATTR_GROUP; } out: - if (len != -EBADMSG) - return 0; - print_overflow_msg(__func__, xdr); - return -EIO; + if (len == -EBADMSG) + return -EIO; + return 0; } static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rdev) @@ -4066,7 +3971,7 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; major = be32_to_cpup(p++); minor = be32_to_cpup(p); tmp = MKDEV(major, minor); @@ -4077,9 +3982,6 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde } dprintk("%s: rdev=(0x%x:0x%x)\n", __func__, major, minor); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) @@ -4093,15 +3995,12 @@ static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uin if (likely(bitmap[1] & FATTR4_WORD1_SPACE_AVAIL)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[1] &= ~FATTR4_WORD1_SPACE_AVAIL; } dprintk("%s: space avail=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) @@ -4115,15 +4014,12 @@ static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint if (likely(bitmap[1] & FATTR4_WORD1_SPACE_FREE)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[1] &= ~FATTR4_WORD1_SPACE_FREE; } dprintk("%s: space free=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) @@ -4137,15 +4033,12 @@ static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uin if (likely(bitmap[1] & FATTR4_WORD1_SPACE_TOTAL)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); bitmap[1] &= ~FATTR4_WORD1_SPACE_TOTAL; } dprintk("%s: space total=%Lu\n", __func__, (unsigned long long)*res); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *used) @@ -4159,7 +4052,7 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint if (likely(bitmap[1] & FATTR4_WORD1_SPACE_USED)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, used); bitmap[1] &= ~FATTR4_WORD1_SPACE_USED; ret = NFS_ATTR_FATTR_SPACE_USED; @@ -4167,9 +4060,6 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint dprintk("%s: space used=%Lu\n", __func__, (unsigned long long)*used); return ret; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static __be32 * @@ -4189,12 +4079,9 @@ static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time) p = xdr_inline_decode(xdr, nfstime4_maxsz << 2); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_nfstime4(p, time); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) @@ -4265,19 +4152,19 @@ static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap, if (likely(bitmap[2] & FATTR4_WORD2_SECURITY_LABEL)) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; lfs = be32_to_cpup(p++); p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; pi = be32_to_cpup(p++); p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p++); p = xdr_inline_decode(xdr, len); if (unlikely(!p)) - goto out_overflow; + return -EIO; if (len < NFS4_MAXLABELLEN) { if (label) { memcpy(label->label, p, len); @@ -4295,10 +4182,6 @@ static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap, dprintk("%s: label=%s, len=%d, PI=%d, LFS=%d\n", __func__, (char *)label->label, label->len, label->pi, label->lfs); return status; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) @@ -4342,14 +4225,11 @@ static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *c p = xdr_inline_decode(xdr, 20); if (unlikely(!p)) - goto out_overflow; + return -EIO; cinfo->atomic = be32_to_cpup(p++); p = xdr_decode_hyper(p, &cinfo->before); xdr_decode_hyper(p, &cinfo->after); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_access(struct xdr_stream *xdr, u32 *supported, u32 *access) @@ -4363,24 +4243,19 @@ static int decode_access(struct xdr_stream *xdr, u32 *supported, u32 *access) return status; p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; supp = be32_to_cpup(p++); acc = be32_to_cpup(p); *supported = supp; *access = acc; return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len) { ssize_t ret = xdr_stream_decode_opaque_fixed(xdr, buf, len); - if (unlikely(ret < 0)) { - print_overflow_msg(__func__, xdr); + if (unlikely(ret < 0)) return -EIO; - } return 0; } @@ -4460,13 +4335,11 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) return status; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; bmlen = be32_to_cpup(p); p = xdr_inline_decode(xdr, bmlen << 2); if (likely(p)) return 0; -out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } @@ -4574,13 +4447,10 @@ static int decode_threshold_hint(struct xdr_stream *xdr, if (likely(bitmap[0] & hint_bit)) { p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, res); } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_first_threshold_item4(struct xdr_stream *xdr, @@ -4593,10 +4463,8 @@ static int decode_first_threshold_item4(struct xdr_stream *xdr, /* layout type */ p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) { - print_overflow_msg(__func__, xdr); + if (unlikely(!p)) return -EIO; - } res->l_type = be32_to_cpup(p); /* thi_hintset bitmap */ @@ -4654,7 +4522,7 @@ static int decode_attr_mdsthreshold(struct xdr_stream *xdr, return -EREMOTEIO; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; num = be32_to_cpup(p); if (num == 0) return 0; @@ -4667,9 +4535,6 @@ static int decode_attr_mdsthreshold(struct xdr_stream *xdr, bitmap[2] &= ~FATTR4_WORD2_MDSTHRESHOLD; } return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, @@ -4857,7 +4722,7 @@ static int decode_pnfs_layout_types(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; fsinfo->nlayouttypes = be32_to_cpup(p); /* pNFS is not supported by the underlying file system */ @@ -4867,7 +4732,7 @@ static int decode_pnfs_layout_types(struct xdr_stream *xdr, /* Decode and set first layout type, move xdr->p past unused types */ p = xdr_inline_decode(xdr, fsinfo->nlayouttypes * 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; /* If we get too many, then just cap it at the max */ if (fsinfo->nlayouttypes > NFS_MAX_LAYOUT_TYPES) { @@ -4879,9 +4744,6 @@ static int decode_pnfs_layout_types(struct xdr_stream *xdr, for(i = 0; i < fsinfo->nlayouttypes; ++i) fsinfo->layouttype[i] = be32_to_cpup(p++); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } /* @@ -4915,10 +4777,8 @@ static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap, *res = 0; if (bitmap[2] & FATTR4_WORD2_LAYOUT_BLKSIZE) { p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) { - print_overflow_msg(__func__, xdr); + if (unlikely(!p)) return -EIO; - } *res = be32_to_cpup(p); bitmap[2] &= ~FATTR4_WORD2_LAYOUT_BLKSIZE; } @@ -4937,10 +4797,8 @@ static int decode_attr_clone_blksize(struct xdr_stream *xdr, uint32_t *bitmap, *res = 0; if (bitmap[2] & FATTR4_WORD2_CLONE_BLKSIZE) { p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) { - print_overflow_msg(__func__, xdr); + if (unlikely(!p)) return -EIO; - } *res = be32_to_cpup(p); bitmap[2] &= ~FATTR4_WORD2_CLONE_BLKSIZE; } @@ -5016,19 +4874,16 @@ static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh) p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p); if (len > NFS4_FHSIZE) return -EIO; fh->size = len; p = xdr_inline_decode(xdr, len); if (unlikely(!p)) - goto out_overflow; + return -EIO; memcpy(fh->data, p, len); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) @@ -5052,7 +4907,7 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) p = xdr_inline_decode(xdr, 32); /* read 32 bytes */ if (unlikely(!p)) - goto out_overflow; + return -EIO; p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */ p = xdr_decode_hyper(p, &length); type = be32_to_cpup(p++); /* 4 byte read */ @@ -5069,11 +4924,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */ namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */ p = xdr_inline_decode(xdr, namelen); /* variable size field */ - if (likely(p)) - return -NFS4ERR_DENIED; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; + if (likely(!p)) + return -EIO; + return -NFS4ERR_DENIED; } static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res) @@ -5142,7 +4995,7 @@ static int decode_space_limit(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 12); if (unlikely(!p)) - goto out_overflow; + return -EIO; limit_type = be32_to_cpup(p++); switch (limit_type) { case NFS4_LIMIT_SIZE: @@ -5156,9 +5009,6 @@ static int decode_space_limit(struct xdr_stream *xdr, maxsize >>= PAGE_SHIFT; *pagemod_limit = min_t(u64, maxsize, ULONG_MAX); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_rw_delegation(struct xdr_stream *xdr, @@ -5173,7 +5023,7 @@ static int decode_rw_delegation(struct xdr_stream *xdr, return status; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->do_recall = be32_to_cpup(p); switch (delegation_type) { @@ -5186,9 +5036,6 @@ static int decode_rw_delegation(struct xdr_stream *xdr, return -EIO; } return decode_ace(xdr, NULL); -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_no_delegation(struct xdr_stream *xdr, struct nfs_openres *res) @@ -5198,7 +5045,7 @@ static int decode_no_delegation(struct xdr_stream *xdr, struct nfs_openres *res) p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; why_no_delegation = be32_to_cpup(p); switch (why_no_delegation) { case WND4_CONTENTION: @@ -5207,9 +5054,6 @@ static int decode_no_delegation(struct xdr_stream *xdr, struct nfs_openres *res) /* Ignore for now */ } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res) @@ -5219,7 +5063,7 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res) p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; delegation_type = be32_to_cpup(p); res->delegation_type = 0; switch (delegation_type) { @@ -5232,9 +5076,6 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res) return decode_no_delegation(xdr, res); } return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) @@ -5256,7 +5097,7 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->rflags = be32_to_cpup(p++); bmlen = be32_to_cpup(p); if (bmlen > 10) @@ -5264,7 +5105,7 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) p = xdr_inline_decode(xdr, bmlen << 2); if (unlikely(!p)) - goto out_overflow; + return -EIO; savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE); for (i = 0; i < savewords; ++i) res->attrset[i] = be32_to_cpup(p++); @@ -5275,9 +5116,6 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) xdr_error: dprintk("%s: Bitmap too large! Length = %u\n", __func__, bmlen); return -EIO; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) @@ -5326,7 +5164,7 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, return status; p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; eof = be32_to_cpup(p++); count = be32_to_cpup(p); recvd = xdr_read_pages(xdr, count); @@ -5339,9 +5177,6 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, res->eof = eof; res->count = count; return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) @@ -5374,7 +5209,7 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) /* Convert length of symlink */ p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p); if (len >= rcvbuf->page_len || len <= 0) { dprintk("nfs: server returned giant symlink!\n"); @@ -5395,9 +5230,6 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) */ xdr_terminate_string(rcvbuf, len); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_remove(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) @@ -5500,7 +5332,6 @@ static int decode_setattr(struct xdr_stream *xdr) return status; if (decode_bitmap4(xdr, NULL, 0) >= 0) return 0; - print_overflow_msg(__func__, xdr); return -EIO; } @@ -5512,7 +5343,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_re p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; opnum = be32_to_cpup(p++); if (opnum != OP_SETCLIENTID) { dprintk("nfs: decode_setclientid: Server returned operation" @@ -5523,7 +5354,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_re if (nfserr == NFS_OK) { p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE); if (unlikely(!p)) - goto out_overflow; + return -EIO; p = xdr_decode_hyper(p, &res->clientid); memcpy(res->confirm.data, p, NFS4_VERIFIER_SIZE); } else if (nfserr == NFSERR_CLID_INUSE) { @@ -5532,28 +5363,25 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_re /* skip netid string */ p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p); p = xdr_inline_decode(xdr, len); if (unlikely(!p)) - goto out_overflow; + return -EIO; /* skip uaddr string */ p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p); p = xdr_inline_decode(xdr, len); if (unlikely(!p)) - goto out_overflow; + return -EIO; return -NFSERR_CLID_INUSE; } else return nfs4_stat_to_errno(nfserr); return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_setclientid_confirm(struct xdr_stream *xdr) @@ -5572,13 +5400,10 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_pgio_res *res) p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->count = be32_to_cpup(p++); res->verf->committed = be32_to_cpup(p++); return decode_write_verifier(xdr, &res->verf->verifier); -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_delegreturn(struct xdr_stream *xdr) @@ -5594,30 +5419,24 @@ static int decode_secinfo_gss(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; oid_len = be32_to_cpup(p); if (oid_len > GSS_OID_MAX_LEN) - goto out_err; + return -EINVAL; p = xdr_inline_decode(xdr, oid_len); if (unlikely(!p)) - goto out_overflow; + return -EIO; memcpy(flavor->flavor_info.oid.data, p, oid_len); flavor->flavor_info.oid.len = oid_len; p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; flavor->flavor_info.qop = be32_to_cpup(p++); flavor->flavor_info.service = be32_to_cpup(p); return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; -out_err: - return -EINVAL; } static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) @@ -5629,7 +5448,7 @@ static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->flavors->num_flavors = 0; num_flavors = be32_to_cpup(p); @@ -5641,7 +5460,7 @@ static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; sec_flavor->flavor = be32_to_cpup(p); if (sec_flavor->flavor == RPC_AUTH_GSS) { @@ -5655,9 +5474,6 @@ static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res status = 0; out: return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) @@ -5711,11 +5527,11 @@ static int decode_exchange_id(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; xdr_decode_hyper(p, &res->clientid); p = xdr_inline_decode(xdr, 12); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->seqid = be32_to_cpup(p++); res->flags = be32_to_cpup(p++); @@ -5739,7 +5555,7 @@ static int decode_exchange_id(struct xdr_stream *xdr, /* server_owner4.so_minor_id */ p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; p = xdr_decode_hyper(p, &res->server_owner->minor_id); /* server_owner4.so_major_id */ @@ -5759,7 +5575,7 @@ static int decode_exchange_id(struct xdr_stream *xdr, /* Implementation Id */ p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; impl_id_count = be32_to_cpup(p++); if (impl_id_count) { @@ -5778,16 +5594,13 @@ static int decode_exchange_id(struct xdr_stream *xdr, /* nii_date */ p = xdr_inline_decode(xdr, 12); if (unlikely(!p)) - goto out_overflow; + return -EIO; p = xdr_decode_hyper(p, &res->impl_id->date.seconds); res->impl_id->date.nseconds = be32_to_cpup(p); /* if there's more than one entry, ignore the rest */ } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_chan_attrs(struct xdr_stream *xdr, @@ -5798,7 +5611,7 @@ static int decode_chan_attrs(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 28); if (unlikely(!p)) - goto out_overflow; + return -EIO; val = be32_to_cpup(p++); /* headerpadsz */ if (val) return -EINVAL; /* no support for header padding yet */ @@ -5816,12 +5629,9 @@ static int decode_chan_attrs(struct xdr_stream *xdr, if (nr_attrs == 1) { p = xdr_inline_decode(xdr, 4); /* skip rdma_attrs */ if (unlikely(!p)) - goto out_overflow; + return -EIO; } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid) @@ -5844,7 +5654,7 @@ static int decode_bind_conn_to_session(struct xdr_stream *xdr, /* dir flags, rdma mode bool */ p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->dir = be32_to_cpup(p++); if (res->dir == 0 || res->dir > NFS4_CDFS4_BOTH) @@ -5855,9 +5665,6 @@ static int decode_bind_conn_to_session(struct xdr_stream *xdr, res->use_conn_in_rdma_mode = true; return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_create_session(struct xdr_stream *xdr, @@ -5875,7 +5682,7 @@ static int decode_create_session(struct xdr_stream *xdr, /* seqid, flags */ p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->seqid = be32_to_cpup(p++); res->flags = be32_to_cpup(p); @@ -5884,9 +5691,6 @@ static int decode_create_session(struct xdr_stream *xdr, if (!status) status = decode_chan_attrs(xdr, &res->bc_attrs); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_destroy_session(struct xdr_stream *xdr, void *dummy) @@ -5967,7 +5771,6 @@ out_err: res->sr_status = status; return status; out_overflow: - print_overflow_msg(__func__, xdr); status = -EIO; goto out_err; #else /* CONFIG_NFS_V4_1 */ @@ -5995,7 +5798,7 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr, if (status == -ETOOSMALL) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; pdev->mincount = be32_to_cpup(p); dprintk("%s: Min count too small. mincnt = %u\n", __func__, pdev->mincount); @@ -6005,7 +5808,7 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; type = be32_to_cpup(p++); if (type != pdev->layout_type) { dprintk("%s: layout mismatch req: %u pdev: %u\n", @@ -6019,19 +5822,19 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr, */ pdev->mincount = be32_to_cpup(p); if (xdr_read_pages(xdr, pdev->mincount) != pdev->mincount) - goto out_overflow; + return -EIO; /* Parse notification bitmap, verifying that it is zero. */ p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; len = be32_to_cpup(p); if (len) { uint32_t i; p = xdr_inline_decode(xdr, 4 * len); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->notification = be32_to_cpup(p++); for (i = 1; i < len; i++) { @@ -6043,9 +5846,6 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr, } } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, @@ -6115,7 +5915,6 @@ out: res->status = status; return status; out_overflow: - print_overflow_msg(__func__, xdr); status = -EIO; goto out; } @@ -6131,16 +5930,13 @@ static int decode_layoutreturn(struct xdr_stream *xdr, return status; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->lrs_present = be32_to_cpup(p); if (res->lrs_present) status = decode_layout_stateid(xdr, &res->stateid); else nfs4_stateid_copy(&res->stateid, &invalid_stateid); return status; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_layoutcommit(struct xdr_stream *xdr, @@ -6158,19 +5954,16 @@ static int decode_layoutcommit(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; sizechanged = be32_to_cpup(p); if (sizechanged) { /* throw away new size */ p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) - goto out_overflow; + return -EIO; } return 0; -out_overflow: - print_overflow_msg(__func__, xdr); - return -EIO; } static int decode_test_stateid(struct xdr_stream *xdr, @@ -6186,21 +5979,17 @@ static int decode_test_stateid(struct xdr_stream *xdr, p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; num_res = be32_to_cpup(p++); if (num_res != 1) - goto out; + return -EIO; p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EIO; res->status = be32_to_cpup(p++); return status; -out_overflow: - print_overflow_msg(__func__, xdr); -out: - return -EIO; } static int decode_free_stateid(struct xdr_stream *xdr, @@ -7570,11 +7359,11 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, uint64_t new_cookie; __be32 *p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EAGAIN; if (*p == xdr_zero) { p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) - goto out_overflow; + return -EAGAIN; if (*p == xdr_zero) return -EAGAIN; entry->eof = 1; @@ -7583,13 +7372,13 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, p = xdr_inline_decode(xdr, 12); if (unlikely(!p)) - goto out_overflow; + return -EAGAIN; p = xdr_decode_hyper(p, &new_cookie); entry->len = be32_to_cpup(p); p = xdr_inline_decode(xdr, entry->len); if (unlikely(!p)) - goto out_overflow; + return -EAGAIN; entry->name = (const char *) p; /* @@ -7601,14 +7390,14 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, entry->fattr->valid = 0; if (decode_attr_bitmap(xdr, bitmap) < 0) - goto out_overflow; + return -EAGAIN; if (decode_attr_length(xdr, &len, &savep) < 0) - goto out_overflow; + return -EAGAIN; if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, NULL, entry->label, entry->server) < 0) - goto out_overflow; + return -EAGAIN; if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) entry->ino = entry->fattr->mounted_on_fileid; else if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) @@ -7622,10 +7411,6 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, entry->cookie = new_cookie; return 0; - -out_overflow: - print_overflow_msg(__func__, xdr); - return -EAGAIN; } /* diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index c74e4538d0eb..a9d24d5a967c 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -60,16 +60,6 @@ struct nfs4_cb_compound_hdr { int status; }; -/* - * Handle decode buffer overflows out-of-line. - */ -static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) -{ - dprintk("NFS: %s prematurely hit the end of our receive buffer. " - "Remaining buffer length is %tu words.\n", - func, xdr->end - xdr->p); -} - static __be32 *xdr_encode_empty_array(__be32 *p) { *p++ = xdr_zero; @@ -240,7 +230,6 @@ static int decode_cb_op_status(struct xdr_stream *xdr, *status = nfs_cb_stat_to_errno(be32_to_cpup(p)); return 0; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; out_unexpected: dprintk("NFSD: Callback server returned operation %d but " @@ -309,7 +298,6 @@ static int decode_cb_compound4res(struct xdr_stream *xdr, hdr->nops = be32_to_cpup(p); return 0; out_overflow: - print_overflow_msg(__func__, xdr); return -EIO; } @@ -437,7 +425,6 @@ out: cb->cb_seq_status = status; return status; out_overflow: - print_overflow_msg(__func__, xdr); status = -EIO; goto out; } From c6c5df293bf1b488cf8459aac658aecfdccb13a9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Feb 2019 08:58:40 -0800 Subject: [PATCH 1744/4277] nfit/ars: Attempt a short-ARS whenever the ARS state is idle at boot If query-ARS reports that ARS has stopped and requires continuation attempt to retrieve short-ARS results before continuing the long operation. Fixes: bc6ba8085842 ("nfit, address-range-scrub: rework and simplify ARS...") Cc: Reported-by: Krzysztof Rusocki Reviewed-by: Toshi Kani Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index e18ade5d74e9..3d681a92ff7f 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3012,6 +3012,7 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc, switch (acpi_nfit_query_poison(acpi_desc)) { case 0: + case -ENOSPC: case -EAGAIN: rc = ars_start(acpi_desc, nfit_spa, ARS_REQ_SHORT); /* shouldn't happen, try again later */ @@ -3036,7 +3037,6 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc, break; case -EBUSY: case -ENOMEM: - case -ENOSPC: /* * BIOS was using ARS, wait for it to complete (or * resources to become available) and then perform our From f23f658404229278af49284871518174ebbe3580 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:26 -0500 Subject: [PATCH 1745/4277] NFS: Add trace events to report non-zero NFS status codes These can help field troubleshooting without needing the overhead of a full network capture (ie, tcpdump). Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- fs/nfs/nfs2xdr.c | 7 ++++ fs/nfs/nfs3xdr.c | 7 ++++ fs/nfs/nfs4trace.h | 25 ++++++++++++++ fs/nfs/nfs4xdr.c | 12 ++++--- fs/nfs/nfstrace.c | 1 + fs/nfs/nfstrace.h | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 4 deletions(-) diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 76614311f957..bac3a4e2cb5d 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -22,6 +22,7 @@ #include #include #include +#include "nfstrace.h" #include "internal.h" #define NFSDBG_FACILITY NFSDBG_XDR @@ -145,7 +146,13 @@ static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) return -EIO; + if (unlikely(*p != cpu_to_be32(NFS_OK))) + goto out_status; + *status = 0; + return 0; +out_status: *status = be32_to_cpup(p); + trace_nfs_xdr_status((int)*status); return 0; } diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index e5619803a4f6..4aa3ffe1800e 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -21,6 +21,7 @@ #include #include #include +#include "nfstrace.h" #include "internal.h" #define NFSDBG_FACILITY NFSDBG_XDR @@ -337,7 +338,13 @@ static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status) p = xdr_inline_decode(xdr, 4); if (unlikely(!p)) return -EIO; + if (unlikely(*p != cpu_to_be32(NFS3_OK))) + goto out_status; + *status = 0; + return 0; +out_status: *status = be32_to_cpup(p); + trace_nfs_xdr_status((int)*status); return 0; } diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index b4557cf685fb..cd1a5c08da9a 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -524,6 +524,31 @@ TRACE_EVENT(nfs4_setup_sequence, ) ); +TRACE_EVENT(nfs4_xdr_status, + TP_PROTO( + u32 op, + int error + ), + + TP_ARGS(op, error), + + TP_STRUCT__entry( + __field(u32, op) + __field(int, error) + ), + + TP_fast_assign( + __entry->op = op; + __entry->error = -error; + ), + + TP_printk( + "operation %d: nfs status %d (%s)", + __entry->op, + __entry->error, show_nfsv4_errors(__entry->error) + ) +); + DECLARE_EVENT_CLASS(nfs4_open_event, TP_PROTO( const struct nfs_open_context *ctx, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 24e6a45a5726..38a4cbc18657 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -54,6 +54,7 @@ #include #include "nfs4_fs.h" +#include "nfs4trace.h" #include "internal.h" #include "nfs4idmap.h" #include "nfs4session.h" @@ -3188,11 +3189,14 @@ static bool __decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected, opnum = be32_to_cpup(p++); if (unlikely(opnum != expected)) goto out_bad_operation; + if (unlikely(*p != cpu_to_be32(NFS_OK))) + goto out_status; + *nfs_retval = 0; + return true; +out_status: nfserr = be32_to_cpup(p); - if (nfserr == NFS_OK) - *nfs_retval = 0; - else - *nfs_retval = nfs4_stat_to_errno(nfserr); + trace_nfs4_xdr_status(opnum, nfserr); + *nfs_retval = nfs4_stat_to_errno(nfserr); return true; out_bad_operation: dprintk("nfs: Server returned operation" diff --git a/fs/nfs/nfstrace.c b/fs/nfs/nfstrace.c index b60d5fbd7727..a90b363500c2 100644 --- a/fs/nfs/nfstrace.c +++ b/fs/nfs/nfstrace.c @@ -11,3 +11,4 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter); EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit); +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_xdr_status); diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index bd60f8d1e181..a0d6910aa03a 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h @@ -969,6 +969,91 @@ TRACE_EVENT(nfs_commit_done, ) ); +TRACE_DEFINE_ENUM(NFS_OK); +TRACE_DEFINE_ENUM(NFSERR_PERM); +TRACE_DEFINE_ENUM(NFSERR_NOENT); +TRACE_DEFINE_ENUM(NFSERR_IO); +TRACE_DEFINE_ENUM(NFSERR_NXIO); +TRACE_DEFINE_ENUM(NFSERR_ACCES); +TRACE_DEFINE_ENUM(NFSERR_EXIST); +TRACE_DEFINE_ENUM(NFSERR_XDEV); +TRACE_DEFINE_ENUM(NFSERR_NODEV); +TRACE_DEFINE_ENUM(NFSERR_NOTDIR); +TRACE_DEFINE_ENUM(NFSERR_ISDIR); +TRACE_DEFINE_ENUM(NFSERR_INVAL); +TRACE_DEFINE_ENUM(NFSERR_FBIG); +TRACE_DEFINE_ENUM(NFSERR_NOSPC); +TRACE_DEFINE_ENUM(NFSERR_ROFS); +TRACE_DEFINE_ENUM(NFSERR_MLINK); +TRACE_DEFINE_ENUM(NFSERR_NAMETOOLONG); +TRACE_DEFINE_ENUM(NFSERR_NOTEMPTY); +TRACE_DEFINE_ENUM(NFSERR_DQUOT); +TRACE_DEFINE_ENUM(NFSERR_STALE); +TRACE_DEFINE_ENUM(NFSERR_REMOTE); +TRACE_DEFINE_ENUM(NFSERR_WFLUSH); +TRACE_DEFINE_ENUM(NFSERR_BADHANDLE); +TRACE_DEFINE_ENUM(NFSERR_NOT_SYNC); +TRACE_DEFINE_ENUM(NFSERR_BAD_COOKIE); +TRACE_DEFINE_ENUM(NFSERR_NOTSUPP); +TRACE_DEFINE_ENUM(NFSERR_TOOSMALL); +TRACE_DEFINE_ENUM(NFSERR_SERVERFAULT); +TRACE_DEFINE_ENUM(NFSERR_BADTYPE); +TRACE_DEFINE_ENUM(NFSERR_JUKEBOX); + +#define nfs_show_status(x) \ + __print_symbolic(x, \ + { NFS_OK, "OK" }, \ + { NFSERR_PERM, "PERM" }, \ + { NFSERR_NOENT, "NOENT" }, \ + { NFSERR_IO, "IO" }, \ + { NFSERR_NXIO, "NXIO" }, \ + { NFSERR_ACCES, "ACCES" }, \ + { NFSERR_EXIST, "EXIST" }, \ + { NFSERR_XDEV, "XDEV" }, \ + { NFSERR_NODEV, "NODEV" }, \ + { NFSERR_NOTDIR, "NOTDIR" }, \ + { NFSERR_ISDIR, "ISDIR" }, \ + { NFSERR_INVAL, "INVAL" }, \ + { NFSERR_FBIG, "FBIG" }, \ + { NFSERR_NOSPC, "NOSPC" }, \ + { NFSERR_ROFS, "ROFS" }, \ + { NFSERR_MLINK, "MLINK" }, \ + { NFSERR_NAMETOOLONG, "NAMETOOLONG" }, \ + { NFSERR_NOTEMPTY, "NOTEMPTY" }, \ + { NFSERR_DQUOT, "DQUOT" }, \ + { NFSERR_STALE, "STALE" }, \ + { NFSERR_REMOTE, "REMOTE" }, \ + { NFSERR_WFLUSH, "WFLUSH" }, \ + { NFSERR_BADHANDLE, "BADHANDLE" }, \ + { NFSERR_NOT_SYNC, "NOTSYNC" }, \ + { NFSERR_BAD_COOKIE, "BADCOOKIE" }, \ + { NFSERR_NOTSUPP, "NOTSUPP" }, \ + { NFSERR_TOOSMALL, "TOOSMALL" }, \ + { NFSERR_SERVERFAULT, "REMOTEIO" }, \ + { NFSERR_BADTYPE, "BADTYPE" }, \ + { NFSERR_JUKEBOX, "JUKEBOX" }) + +TRACE_EVENT(nfs_xdr_status, + TP_PROTO( + int error + ), + + TP_ARGS(error), + + TP_STRUCT__entry( + __field(int, error) + ), + + TP_fast_assign( + __entry->error = error; + ), + + TP_printk( + "error=%d (%s)", + __entry->error, nfs_show_status(__entry->error) + ) +); + #endif /* _TRACE_NFS_H */ #undef TRACE_INCLUDE_PATH From 80125d4ae70aff049b170dd21ea5d007694d629a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:32 -0500 Subject: [PATCH 1746/4277] SUNRPC: Remove some dprintk() call sites from auth functions Clean up: Reduce dprintk noise by removing dprintk() call sites from hot path that do not report exceptions. These are usually replaceable with function graph tracing. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/auth.c | 29 ----------------------------- net/sunrpc/auth_unix.c | 9 +-------- 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 8dfab6119e6a..275e84e817b7 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -17,10 +17,6 @@ #include #include -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) -# define RPCDBG_FACILITY RPCDBG_AUTH -#endif - #define RPC_CREDCACHE_DEFAULT_HASHBITS (4) struct rpc_cred_cache { struct hlist_head *hashtable; @@ -267,8 +263,6 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size) } } rcu_read_unlock(); - - dprintk("RPC: %s returns %d\n", __func__, result); return result; } EXPORT_SYMBOL_GPL(rpcauth_list_flavors); @@ -636,9 +630,6 @@ rpcauth_lookupcred(struct rpc_auth *auth, int flags) struct rpc_cred *ret; const struct cred *cred = current_cred(); - dprintk("RPC: looking up %s cred\n", - auth->au_ops->au_name); - memset(&acred, 0, sizeof(acred)); acred.cred = cred; ret = auth->au_ops->lookup_cred(auth, &acred, flags); @@ -670,8 +661,6 @@ rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) }; struct rpc_cred *ret; - dprintk("RPC: %5u looking up %s cred\n", - task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags); put_cred(acred.cred); return ret; @@ -688,8 +677,6 @@ rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags) if (!acred.principal) return NULL; - dprintk("RPC: %5u looking up %s machine cred\n", - task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); return auth->au_ops->lookup_cred(auth, &acred, lookupflags); } @@ -698,8 +685,6 @@ rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) { struct rpc_auth *auth = task->tk_client->cl_auth; - dprintk("RPC: %5u looking up %s cred\n", - task->tk_pid, auth->au_ops->au_name); return rpcauth_lookupcred(auth, lookupflags); } @@ -776,9 +761,6 @@ rpcauth_marshcred(struct rpc_task *task, __be32 *p) { struct rpc_cred *cred = task->tk_rqstp->rq_cred; - dprintk("RPC: %5u marshaling %s cred %p\n", - task->tk_pid, cred->cr_auth->au_ops->au_name, cred); - return cred->cr_ops->crmarshal(task, p); } @@ -787,9 +769,6 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p) { struct rpc_cred *cred = task->tk_rqstp->rq_cred; - dprintk("RPC: %5u validating %s cred %p\n", - task->tk_pid, cred->cr_auth->au_ops->au_name, cred); - return cred->cr_ops->crvalidate(task, p); } @@ -808,8 +787,6 @@ rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, { struct rpc_cred *cred = task->tk_rqstp->rq_cred; - dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", - task->tk_pid, cred->cr_ops->cr_name, cred); if (cred->cr_ops->crwrap_req) return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); /* By default, we encode the arguments normally. */ @@ -833,8 +810,6 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, { struct rpc_cred *cred = task->tk_rqstp->rq_cred; - dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", - task->tk_pid, cred->cr_ops->cr_name, cred); if (cred->cr_ops->crunwrap_resp) return cred->cr_ops->crunwrap_resp(task, decode, rqstp, data, obj); @@ -865,8 +840,6 @@ rpcauth_refreshcred(struct rpc_task *task) goto out; cred = task->tk_rqstp->rq_cred; } - dprintk("RPC: %5u refreshing %s cred %p\n", - task->tk_pid, cred->cr_auth->au_ops->au_name, cred); err = cred->cr_ops->crrefresh(task); out: @@ -880,8 +853,6 @@ rpcauth_invalcred(struct rpc_task *task) { struct rpc_cred *cred = task->tk_rqstp->rq_cred; - dprintk("RPC: %5u invalidating %s cred %p\n", - task->tk_pid, cred->cr_auth->au_ops->au_name, cred); if (cred) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); } diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 387f6b3ffbea..fc8a59134640 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -28,8 +28,6 @@ static mempool_t *unix_pool; static struct rpc_auth * unx_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) { - dprintk("RPC: creating UNIX authenticator for client %p\n", - clnt); refcount_inc(&unix_auth.au_count); return &unix_auth; } @@ -37,7 +35,6 @@ unx_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) static void unx_destroy(struct rpc_auth *auth) { - dprintk("RPC: destroying UNIX authenticator %p\n", auth); } /* @@ -48,10 +45,6 @@ unx_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) { struct rpc_cred *ret = mempool_alloc(unix_pool, GFP_NOFS); - dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", - from_kuid(&init_user_ns, acred->cred->fsuid), - from_kgid(&init_user_ns, acred->cred->fsgid)); - rpcauth_init_cred(ret, acred, auth, &unix_credops); ret->cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; return ret; @@ -61,7 +54,7 @@ static void unx_free_cred_callback(struct rcu_head *head) { struct rpc_cred *rpc_cred = container_of(head, struct rpc_cred, cr_rcu); - dprintk("RPC: unx_free_cred %p\n", rpc_cred); + put_cred(rpc_cred->cr_cred); mempool_free(rpc_cred, unix_pool); } From 70ca7ba2dbe4f1858b85e30269c8408a8bb8f272 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 11 Feb 2019 18:12:30 +0200 Subject: [PATCH 1747/4277] dma-mapping: move debug configuration options to kernel/dma This is a follow up to the commit cf65a0f6f6ff ("dma-mapping: move all DMA mapping code to kernel/dma") which moved source code of DMA API to kernel/dma folder. Since there is no file left in the lib that require DMA API debugging options move the latter to kernel/dma as well. Cc: Christoph Hellwig Signed-off-by: Andy Shevchenko Signed-off-by: Christoph Hellwig --- kernel/dma/Kconfig | 36 ++++++++++++++++++++++++++++++++++++ lib/Kconfig.debug | 36 ------------------------------------ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig index ca88b867e7fe..61cebea36d89 100644 --- a/kernel/dma/Kconfig +++ b/kernel/dma/Kconfig @@ -53,3 +53,39 @@ config DMA_REMAP config DMA_DIRECT_REMAP bool select DMA_REMAP + +config DMA_API_DEBUG + bool "Enable debugging of DMA-API usage" + select NEED_DMA_MAP_STATE + help + Enable this option to debug the use of the DMA API by device drivers. + With this option you will be able to detect common bugs in device + drivers like double-freeing of DMA mappings or freeing mappings that + were never allocated. + + This also attempts to catch cases where a page owned by DMA is + accessed by the cpu in a way that could cause data corruption. For + example, this enables cow_user_page() to check that the source page is + not undergoing DMA. + + This option causes a performance degradation. Use only if you want to + debug device drivers and dma interactions. + + If unsure, say N. + +config DMA_API_DEBUG_SG + bool "Debug DMA scatter-gather usage" + default y + depends on DMA_API_DEBUG + help + Perform extra checking that callers of dma_map_sg() have respected the + appropriate segment length/boundary limits for the given device when + preparing DMA scatterlists. + + This is particularly likely to have been overlooked in cases where the + dma_map_sg() API is used for general bulk mapping of pages rather than + preparing literal scatter-gather descriptors, where there is a risk of + unexpected behaviour from DMA API implementations if the scatterlist + is technically out-of-spec. + + If unsure, say N. diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d4df5b24d75e..ef5d7c08e5b9 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1655,42 +1655,6 @@ config PROVIDE_OHCI1394_DMA_INIT See Documentation/debugging-via-ohci1394.txt for more information. -config DMA_API_DEBUG - bool "Enable debugging of DMA-API usage" - select NEED_DMA_MAP_STATE - help - Enable this option to debug the use of the DMA API by device drivers. - With this option you will be able to detect common bugs in device - drivers like double-freeing of DMA mappings or freeing mappings that - were never allocated. - - This also attempts to catch cases where a page owned by DMA is - accessed by the cpu in a way that could cause data corruption. For - example, this enables cow_user_page() to check that the source page is - not undergoing DMA. - - This option causes a performance degradation. Use only if you want to - debug device drivers and dma interactions. - - If unsure, say N. - -config DMA_API_DEBUG_SG - bool "Debug DMA scatter-gather usage" - default y - depends on DMA_API_DEBUG - help - Perform extra checking that callers of dma_map_sg() have respected the - appropriate segment length/boundary limits for the given device when - preparing DMA scatterlists. - - This is particularly likely to have been overlooked in cases where the - dma_map_sg() API is used for general bulk mapping of pages rather than - preparing literal scatter-gather descriptors, where there is a risk of - unexpected behaviour from DMA API implementations if the scatterlist - is technically out-of-spec. - - If unsure, say N. - menuconfig RUNTIME_TESTING_MENU bool "Runtime Testing" def_bool y From 347cb6af8710b72cf9685fdc09d07873cf42d51f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 7 Jan 2019 13:36:20 -0500 Subject: [PATCH 1748/4277] dma-mapping: add a kconfig symbol for arch_setup_dma_ops availability Signed-off-by: Christoph Hellwig Acked-by: Paul Burton # MIPS Acked-by: Catalin Marinas # arm64 --- arch/arc/Kconfig | 1 + arch/arc/include/asm/Kbuild | 1 + arch/arc/include/asm/dma-mapping.h | 13 ------------- arch/arm/Kconfig | 1 + arch/arm/include/asm/dma-mapping.h | 4 ---- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/dma-mapping.h | 4 ---- arch/mips/Kconfig | 1 + arch/mips/include/asm/dma-mapping.h | 10 ---------- arch/mips/mm/dma-noncoherent.c | 8 ++++++++ include/linux/dma-mapping.h | 12 ++++++++---- kernel/dma/Kconfig | 3 +++ 12 files changed, 24 insertions(+), 35 deletions(-) delete mode 100644 arch/arc/include/asm/dma-mapping.h diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 376366a7db81..2ab27d88eb1c 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -11,6 +11,7 @@ config ARC select ARC_TIMERS select ARCH_HAS_DMA_COHERENT_TO_PFN select ARCH_HAS_PTE_SPECIAL + select ARCH_HAS_SETUP_DMA_OPS select ARCH_HAS_SYNC_DMA_FOR_CPU select ARCH_HAS_SYNC_DMA_FOR_DEVICE select ARCH_SUPPORTS_ATOMIC_RMW if ARC_HAS_LLSC diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index caa270261521..b41f8881ecc8 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -3,6 +3,7 @@ generic-y += bugs.h generic-y += compat.h generic-y += device.h generic-y += div64.h +generic-y += dma-mapping.h generic-y += emergency-restart.h generic-y += extable.h generic-y += ftrace.h diff --git a/arch/arc/include/asm/dma-mapping.h b/arch/arc/include/asm/dma-mapping.h deleted file mode 100644 index c946c0a83e76..000000000000 --- a/arch/arc/include/asm/dma-mapping.h +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// (C) 2018 Synopsys, Inc. (www.synopsys.com) - -#ifndef ASM_ARC_DMA_MAPPING_H -#define ASM_ARC_DMA_MAPPING_H - -#include - -void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, - const struct iommu_ops *iommu, bool coherent); -#define arch_setup_dma_ops arch_setup_dma_ops - -#endif diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 664e918e2624..c1cf44f00870 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -12,6 +12,7 @@ config ARM select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_PTE_SPECIAL if ARM_LPAE select ARCH_HAS_PHYS_TO_DMA + select ARCH_HAS_SETUP_DMA_OPS select ARCH_HAS_SET_MEMORY select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL select ARCH_HAS_STRICT_MODULE_RWX if MMU diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 31d3b96f0f4b..a224b6e39e58 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -96,10 +96,6 @@ static inline unsigned long dma_max_pfn(struct device *dev) } #define dma_max_pfn(dev) dma_max_pfn(dev) -#define arch_setup_dma_ops arch_setup_dma_ops -extern void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, - const struct iommu_ops *iommu, bool coherent); - #ifdef CONFIG_MMU #define arch_teardown_dma_ops arch_teardown_dma_ops extern void arch_teardown_dma_ops(struct device *dev); diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index a4168d366127..63909f318d56 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -22,6 +22,7 @@ config ARM64 select ARCH_HAS_KCOV select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_PTE_SPECIAL + select ARCH_HAS_SETUP_DMA_OPS select ARCH_HAS_SET_MEMORY select ARCH_HAS_STRICT_KERNEL_RWX select ARCH_HAS_STRICT_MODULE_RWX diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index 95dbf3ef735a..de96507ee2c1 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -29,10 +29,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return NULL; } -void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, - const struct iommu_ops *iommu, bool coherent); -#define arch_setup_dma_ops arch_setup_dma_ops - #ifdef CONFIG_IOMMU_DMA void arch_teardown_dma_ops(struct device *dev); #define arch_teardown_dma_ops arch_teardown_dma_ops diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 0d14f51d0002..dc5d70f674e0 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1118,6 +1118,7 @@ config DMA_MAYBE_COHERENT config DMA_PERDEV_COHERENT bool + select ARCH_HAS_SETUP_DMA_OPS select DMA_NONCOHERENT config DMA_NONCOHERENT diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h index 20dfaad3a55d..34de7b17b41b 100644 --- a/arch/mips/include/asm/dma-mapping.h +++ b/arch/mips/include/asm/dma-mapping.h @@ -15,14 +15,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) #endif } -#define arch_setup_dma_ops arch_setup_dma_ops -static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, - u64 size, const struct iommu_ops *iommu, - bool coherent) -{ -#ifdef CONFIG_DMA_PERDEV_COHERENT - dev->dma_coherent = coherent; -#endif -} - #endif /* _ASM_DMA_MAPPING_H */ diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c index cb38461391cb..0606fc87b294 100644 --- a/arch/mips/mm/dma-noncoherent.c +++ b/arch/mips/mm/dma-noncoherent.c @@ -159,3 +159,11 @@ void arch_dma_cache_sync(struct device *dev, void *vaddr, size_t size, dma_sync_virt(vaddr, size, direction); } + +#ifdef CONFIG_DMA_PERDEV_COHERENT +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, + const struct iommu_ops *iommu, bool coherent) +{ + dev->dma_coherent = coherent; +} +#endif diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index b904d55247ab..2b20d60e6158 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -671,11 +671,15 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) return dma_set_mask_and_coherent(dev, mask); } -#ifndef arch_setup_dma_ops +#ifdef CONFIG_ARCH_HAS_SETUP_DMA_OPS +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, + const struct iommu_ops *iommu, bool coherent); +#else static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, - u64 size, const struct iommu_ops *iommu, - bool coherent) { } -#endif + u64 size, const struct iommu_ops *iommu, bool coherent) +{ +} +#endif /* CONFIG_ARCH_HAS_SETUP_DMA_OPS */ #ifndef arch_teardown_dma_ops static inline void arch_teardown_dma_ops(struct device *dev) { } diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig index 61cebea36d89..6014cad35e58 100644 --- a/kernel/dma/Kconfig +++ b/kernel/dma/Kconfig @@ -19,6 +19,9 @@ config ARCH_HAS_DMA_COHERENCE_H config HAVE_GENERIC_DMA_COHERENT bool +config ARCH_HAS_SETUP_DMA_OPS + bool + config ARCH_HAS_SYNC_DMA_FOR_DEVICE bool From dc2acded38957dfa6b7b7e0203b4b8cb8d818ce6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 21 Dec 2018 22:14:44 +0100 Subject: [PATCH 1749/4277] dma-mapping: add a kconfig symbol for arch_teardown_dma_ops availability Signed-off-by: Christoph Hellwig Acked-by: Catalin Marinas # arm64 --- arch/arm/Kconfig | 1 + arch/arm/include/asm/dma-mapping.h | 5 ----- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/dma-mapping.h | 5 ----- include/linux/dma-mapping.h | 10 +++++++--- kernel/dma/Kconfig | 3 +++ 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c1cf44f00870..4bb36ae71b14 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -16,6 +16,7 @@ config ARM select ARCH_HAS_SET_MEMORY select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL select ARCH_HAS_STRICT_MODULE_RWX if MMU + select ARCH_HAS_TEARDOWN_DMA_OPS if MMU select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_HAS_GCOV_PROFILE_ALL diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index a224b6e39e58..03ba90ffc0f8 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -96,11 +96,6 @@ static inline unsigned long dma_max_pfn(struct device *dev) } #define dma_max_pfn(dev) dma_max_pfn(dev) -#ifdef CONFIG_MMU -#define arch_teardown_dma_ops arch_teardown_dma_ops -extern void arch_teardown_dma_ops(struct device *dev); -#endif - /* do not use this function in a driver */ static inline bool is_device_dma_coherent(struct device *dev) { diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 63909f318d56..87ec7be25e97 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -29,6 +29,7 @@ config ARM64 select ARCH_HAS_SYNC_DMA_FOR_DEVICE select ARCH_HAS_SYNC_DMA_FOR_CPU select ARCH_HAS_SYSCALL_WRAPPER + select ARCH_HAS_TEARDOWN_DMA_OPS if IOMMU_SUPPORT select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAVE_NMI_SAFE_CMPXCHG select ARCH_INLINE_READ_LOCK if !PREEMPT diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index de96507ee2c1..de98191e4c7d 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -29,11 +29,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return NULL; } -#ifdef CONFIG_IOMMU_DMA -void arch_teardown_dma_ops(struct device *dev); -#define arch_teardown_dma_ops arch_teardown_dma_ops -#endif - /* * Do not use this function in a driver, it is only provided for * arch/arm/mm/xen.c, which is used by arm64 as well. diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 2b20d60e6158..4210c5c1dd21 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -681,9 +681,13 @@ static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, } #endif /* CONFIG_ARCH_HAS_SETUP_DMA_OPS */ -#ifndef arch_teardown_dma_ops -static inline void arch_teardown_dma_ops(struct device *dev) { } -#endif +#ifdef CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS +void arch_teardown_dma_ops(struct device *dev); +#else +static inline void arch_teardown_dma_ops(struct device *dev) +{ +} +#endif /* CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS */ static inline unsigned int dma_get_max_seg_size(struct device *dev) { diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig index 6014cad35e58..bde9179c6ed7 100644 --- a/kernel/dma/Kconfig +++ b/kernel/dma/Kconfig @@ -22,6 +22,9 @@ config HAVE_GENERIC_DMA_COHERENT config ARCH_HAS_SETUP_DMA_OPS bool +config ARCH_HAS_TEARDOWN_DMA_OPS + bool + config ARCH_HAS_SYNC_DMA_FOR_DEVICE bool From fd25ffdfd0b520f25d5c532e962dfc20d58cf4f5 Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Thu, 24 Jan 2019 15:22:13 -0800 Subject: [PATCH 1750/4277] of: unittest: remove report of expected error update_node_properties() reports an error when the test data contains a node (such as "/aliases") that already exists in the base devicetree. The error is caused by of_fdt_unflatten_tree() autogenerating the "name" property, thus both the existing node and the new node will have a property with the same name. Suppress reporting the known error. Signed-off-by: Frank Rowand Signed-off-by: Rob Herring --- drivers/of/unittest.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 84427384654d..3249fe259d30 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1116,9 +1116,12 @@ static void update_node_properties(struct device_node *np, for (prop = np->properties; prop != NULL; prop = save_next) { save_next = prop->next; ret = of_add_property(dup, prop); - if (ret) + if (ret) { + if (ret == -EEXIST && !strcmp(prop->name, "name")) + continue; pr_err("unittest internal error: unable to add testdata property %pOF/%s", np, prop->name); + } } } From 89716dc353bdf1ab34c3a41d3f157ce28b4fbe7d Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Thu, 24 Jan 2019 15:22:14 -0800 Subject: [PATCH 1751/4277] of: unittest: add caution to function header comment Name of function attach_node_and_children() is misleading because if the node already exists in the livetree then only the node's properties are attached. This works for the existing test data, but add comment warning of this misleading name. Signed-off-by: Frank Rowand Signed-off-by: Rob Herring --- drivers/of/unittest.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 3249fe259d30..872956500c27 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1127,7 +1127,11 @@ static void update_node_properties(struct device_node *np, /** * attach_node_and_children - attaches nodes - * and its children to live tree + * and its children to live tree. + * CAUTION: misleading function name - if node @np already exists in + * the live tree then children of @np are *not* attached to the live + * tree. This works for the current test devicetree nodes because such + * nodes do not have child nodes. * * @np: Node to attach to live tree */ From 067fb11b12af1448f7bbcacca41e470cb775e9fa Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:37 -0500 Subject: [PATCH 1752/4277] SUNRPC: Remove rpc_xprt::tsh_size tsh_size was added to accommodate transports that send a pre-amble before each RPC message. However, this assumes the pre-amble is fixed in size, which isn't true for some transports. That makes tsh_size not very generic. Also I'd like to make the estimation of RPC send and receive buffer sizes more precise. tsh_size doesn't currently appear to be accounted for at all by call_allocate. Therefore let's just remove the tsh_size concept, and make the only transports that have a non-zero tsh_size employ a direct approach. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xprt.h | 7 -- net/sunrpc/auth_gss/auth_gss.c | 3 +- net/sunrpc/clnt.c | 1 - net/sunrpc/svc.c | 19 +---- net/sunrpc/xprtrdma/svc_rdma_backchannel.c | 1 - net/sunrpc/xprtrdma/transport.c | 1 - net/sunrpc/xprtsock.c | 91 ++++++++++++++-------- 7 files changed, 65 insertions(+), 58 deletions(-) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index ad7e910b119d..3a391544299e 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -196,8 +196,6 @@ struct rpc_xprt { size_t max_payload; /* largest RPC payload size, in bytes */ - unsigned int tsh_size; /* size of transport specific - header */ struct rpc_wait_queue binding; /* requests waiting on rpcbind */ struct rpc_wait_queue sending; /* requests waiting to send */ @@ -362,11 +360,6 @@ struct rpc_xprt * xprt_alloc(struct net *net, size_t size, unsigned int max_req); void xprt_free(struct rpc_xprt *); -static inline __be32 *xprt_skip_transport_header(struct rpc_xprt *xprt, __be32 *p) -{ - return p + xprt->tsh_size; -} - static inline int xprt_enable_swap(struct rpc_xprt *xprt) { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index a42672e81792..4b52e2b11c58 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1563,8 +1563,7 @@ gss_marshal(struct rpc_task *task, __be32 *p) /* We compute the checksum for the verifier over the xdr-encoded bytes * starting with the xid and ending at the end of the credential: */ - iov.iov_base = xprt_skip_transport_header(req->rq_xprt, - req->rq_snd_buf.head[0].iov_base); + iov.iov_base = req->rq_snd_buf.head[0].iov_base; iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; xdr_buf_from_iov(&iov, &verf_buf); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index d7ec6132c046..c4203f6138ef 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -2331,7 +2331,6 @@ rpc_encode_header(struct rpc_task *task) /* FIXME: check buffer size? */ - p = xprt_skip_transport_header(req->rq_xprt, p); *p++ = req->rq_xid; /* XID */ *p++ = htonl(RPC_CALL); /* CALL */ *p++ = htonl(RPC_VERSION); /* RPC version */ diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index e87ddb9f7feb..dbd19697ee38 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1144,17 +1144,6 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) static __printf(2,3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) {} #endif -/* - * Setup response header for TCP, it has a 4B record length field. - */ -static void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp) -{ - struct kvec *resv = &rqstp->rq_res.head[0]; - - /* tcp needs a space for the record length... */ - svc_putnl(resv, 0); -} - /* * Common routine for processing the RPC request. */ @@ -1182,10 +1171,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv) set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); clear_bit(RQ_DROPME, &rqstp->rq_flags); - /* Setup reply header */ - if (rqstp->rq_prot == IPPROTO_TCP) - svc_tcp_prep_reply_hdr(rqstp); - svc_putu32(resv, rqstp->rq_xid); vers = svc_getnl(argv); @@ -1443,6 +1428,10 @@ svc_process(struct svc_rqst *rqstp) goto out_drop; } + /* Reserve space for the record marker */ + if (rqstp->rq_prot == IPPROTO_TCP) + svc_putnl(resv, 0); + /* Returns 1 for send, 0 for drop */ if (likely(svc_process_common(rqstp, argv, resv))) return svc_send(rqstp); diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c index b908f2ca08fd..907464c2a9f0 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c +++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c @@ -304,7 +304,6 @@ xprt_setup_rdma_bc(struct xprt_create *args) xprt->idle_timeout = RPCRDMA_IDLE_DISC_TO; xprt->prot = XPRT_TRANSPORT_BC_RDMA; - xprt->tsh_size = 0; xprt->ops = &xprt_rdma_bc_procs; memcpy(&xprt->addr, args->dstaddr, args->addrlen); diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index fbc171ebfe91..e7274dc10120 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -332,7 +332,6 @@ xprt_setup_rdma(struct xprt_create *args) xprt->idle_timeout = RPCRDMA_IDLE_DISC_TO; xprt->resvport = 0; /* privileged port not needed */ - xprt->tsh_size = 0; /* RPC-RDMA handles framing */ xprt->ops = &xprt_rdma_procs; /* diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 7754aa3e434f..ae09d850cd11 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -696,6 +696,40 @@ xs_stream_reset_connect(struct sock_xprt *transport) #define XS_SENDMSG_FLAGS (MSG_DONTWAIT | MSG_NOSIGNAL) +/* Common case: + * - stream transport + * - sending from byte 0 of the message + * - the message is wholly contained in @xdr's head iovec + */ +static int xs_send_rm_and_kvec(struct socket *sock, struct xdr_buf *xdr, + unsigned int remainder) +{ + struct msghdr msg = { + .msg_flags = XS_SENDMSG_FLAGS | (remainder ? MSG_MORE : 0) + }; + rpc_fraghdr marker = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | + (u32)xdr->len); + struct kvec iov[2] = { + { + .iov_base = &marker, + .iov_len = sizeof(marker) + }, + { + .iov_base = xdr->head[0].iov_base, + .iov_len = xdr->head[0].iov_len + }, + }; + int ret; + + ret = kernel_sendmsg(sock, &msg, iov, 2, + iov[0].iov_len + iov[1].iov_len); + if (ret < 0) + return ret; + if (ret < iov[0].iov_len) + return -EPIPE; + return ret - iov[0].iov_len; +} + static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen, struct kvec *vec, unsigned int base, int more) { struct msghdr msg = { @@ -779,7 +813,11 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, if (base < xdr->head[0].iov_len || addr != NULL) { unsigned int len = xdr->head[0].iov_len - base; remainder -= len; - err = xs_send_kvec(sock, addr, addrlen, &xdr->head[0], base, remainder != 0); + if (!base && !addr) + err = xs_send_rm_and_kvec(sock, xdr, remainder); + else + err = xs_send_kvec(sock, addr, addrlen, &xdr->head[0], + base, remainder != 0); if (remainder == 0 || err != len) goto out; *sent_p += err; @@ -869,16 +907,6 @@ xs_send_request_was_aborted(struct sock_xprt *transport, struct rpc_rqst *req) return transport->xmit.offset != 0 && req->rq_bytes_sent == 0; } -/* - * Construct a stream transport record marker in @buf. - */ -static inline void xs_encode_stream_record_marker(struct xdr_buf *buf) -{ - u32 reclen = buf->len - sizeof(rpc_fraghdr); - rpc_fraghdr *base = buf->head[0].iov_base; - *base = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | reclen); -} - /** * xs_local_send_request - write an RPC request to an AF_LOCAL socket * @req: pointer to RPC request @@ -905,8 +933,6 @@ static int xs_local_send_request(struct rpc_rqst *req) return -ENOTCONN; } - xs_encode_stream_record_marker(&req->rq_snd_buf); - xs_pktdump("packet data:", req->rq_svec->iov_base, req->rq_svec->iov_len); @@ -1057,8 +1083,6 @@ static int xs_tcp_send_request(struct rpc_rqst *req) return -ENOTCONN; } - xs_encode_stream_record_marker(&req->rq_snd_buf); - xs_pktdump("packet data:", req->rq_svec->iov_base, req->rq_svec->iov_len); @@ -2534,26 +2558,35 @@ static int bc_sendto(struct rpc_rqst *req) { int len; struct xdr_buf *xbufp = &req->rq_snd_buf; - struct rpc_xprt *xprt = req->rq_xprt; struct sock_xprt *transport = - container_of(xprt, struct sock_xprt, xprt); - struct socket *sock = transport->sock; + container_of(req->rq_xprt, struct sock_xprt, xprt); unsigned long headoff; unsigned long tailoff; + struct page *tailpage; + struct msghdr msg = { + .msg_flags = MSG_MORE + }; + rpc_fraghdr marker = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | + (u32)xbufp->len); + struct kvec iov = { + .iov_base = &marker, + .iov_len = sizeof(marker), + }; - xs_encode_stream_record_marker(xbufp); + len = kernel_sendmsg(transport->sock, &msg, &iov, 1, iov.iov_len); + if (len != iov.iov_len) + return -EAGAIN; + tailpage = NULL; + if (xbufp->tail[0].iov_len) + tailpage = virt_to_page(xbufp->tail[0].iov_base); tailoff = (unsigned long)xbufp->tail[0].iov_base & ~PAGE_MASK; headoff = (unsigned long)xbufp->head[0].iov_base & ~PAGE_MASK; - len = svc_send_common(sock, xbufp, + len = svc_send_common(transport->sock, xbufp, virt_to_page(xbufp->head[0].iov_base), headoff, - xbufp->tail[0].iov_base, tailoff); - - if (len != xbufp->len) { - printk(KERN_NOTICE "Error sending entire callback!\n"); - len = -EAGAIN; - } - + tailpage, tailoff); + if (len != xbufp->len) + return -EAGAIN; return len; } @@ -2793,7 +2826,6 @@ static struct rpc_xprt *xs_setup_local(struct xprt_create *args) transport = container_of(xprt, struct sock_xprt, xprt); xprt->prot = 0; - xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32); xprt->max_payload = RPC_MAX_FRAGMENT_SIZE; xprt->bind_timeout = XS_BIND_TO; @@ -2862,7 +2894,6 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args) transport = container_of(xprt, struct sock_xprt, xprt); xprt->prot = IPPROTO_UDP; - xprt->tsh_size = 0; /* XXX: header size can vary due to auth type, IPv6, etc. */ xprt->max_payload = (1U << 16) - (MAX_HEADER << 3); @@ -2942,7 +2973,6 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args) transport = container_of(xprt, struct sock_xprt, xprt); xprt->prot = IPPROTO_TCP; - xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32); xprt->max_payload = RPC_MAX_FRAGMENT_SIZE; xprt->bind_timeout = XS_BIND_TO; @@ -3015,7 +3045,6 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args) transport = container_of(xprt, struct sock_xprt, xprt); xprt->prot = IPPROTO_TCP; - xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32); xprt->max_payload = RPC_MAX_FRAGMENT_SIZE; xprt->timeout = &xs_tcp_default_timeout; From d0377392201b4cab670dafd092b17164f49d8b8e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 3 Feb 2019 20:11:53 +0100 Subject: [PATCH 1753/4277] mfd/sm501: depend on HAS_DMA Currently the sm501 mfd driver can be compiled without any dependencies, but through the use of dma_declare_coherent it really depends on having DMA and iomem support. Normally we don't explicitly require DMA support as we have stubs for it if on UML, but in this case the driver selects support for dma_declare_coherent and thus also requires memmap support. Guard this by an explicit dependency. Signed-off-by: Christoph Hellwig Acked-by: Lee Jones --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f461460a2aeb..f15f6489803d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1066,6 +1066,7 @@ config MFD_SI476X_CORE config MFD_SM501 tristate "Silicon Motion SM501" + depends on HAS_DMA ---help--- This is the core driver for the Silicon Motion SM501 multimedia companion chip. This device is a multifunction device which may From 2b2812961302c38500c1027778e371c895f1cac4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 25 Dec 2018 14:03:32 +0100 Subject: [PATCH 1754/4277] device.h: dma_mem is only needed for HAVE_GENERIC_DMA_COHERENT No need to carry an unused field around. Signed-off-by: Christoph Hellwig Reviewed-by: Greg Kroah-Hartman --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index 6cb4640b6160..be544400acdd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1017,8 +1017,10 @@ struct device { struct list_head dma_pools; /* dma pools (if dma'ble) */ +#ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ +#endif #ifdef CONFIG_DMA_CMA struct cma *cma_area; /* contiguous memory area for dma allocations */ From 34e04eedd1cf1be714abb0e5976338cc72ccc05f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Feb 2019 10:10:52 +0100 Subject: [PATCH 1755/4277] of: select OF_RESERVED_MEM automatically The OF_RESERVED_MEM can be used if we have either CMA or the generic declare coherent code built and we support the early flattened DT. So don't bother making it a user visible options that is selected by most configs that fit the above category, but just select it when the requirements are met. Signed-off-by: Christoph Hellwig Reviewed-by: Rob Herring --- arch/arc/Kconfig | 1 - arch/arm/Kconfig | 1 - arch/arm64/Kconfig | 1 - arch/csky/Kconfig | 1 - arch/powerpc/Kconfig | 1 - arch/xtensa/Kconfig | 1 - drivers/of/Kconfig | 5 ++--- 7 files changed, 2 insertions(+), 9 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 2ab27d88eb1c..ab8d6131c954 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -45,7 +45,6 @@ config ARC select MODULES_USE_ELF_RELA select OF select OF_EARLY_FLATTREE - select OF_RESERVED_MEM select PCI_SYSCALL if PCI select PERF_USE_VMALLOC if ARC_CACHE_VIPT_ALIASING diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4bb36ae71b14..e07e5c184d2f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -103,7 +103,6 @@ config ARM select MODULES_USE_ELF_REL select NEED_DMA_MAP_STATE select OF_EARLY_FLATTREE if OF - select OF_RESERVED_MEM if OF select OLD_SIGACTION select OLD_SIGSUSPEND3 select PCI_SYSCALL if PCI diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 87ec7be25e97..fbcf521e1c9f 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -165,7 +165,6 @@ config ARM64 select NEED_SG_DMA_LENGTH select OF select OF_EARLY_FLATTREE - select OF_RESERVED_MEM select PCI_DOMAINS_GENERIC if PCI select PCI_ECAM if (ACPI && PCI) select PCI_SYSCALL if PCI diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig index 398113c845f5..0a9595afe9be 100644 --- a/arch/csky/Kconfig +++ b/arch/csky/Kconfig @@ -42,7 +42,6 @@ config CSKY select MODULES_USE_ELF_RELA if MODULES select OF select OF_EARLY_FLATTREE - select OF_RESERVED_MEM select PERF_USE_VMALLOC if CPU_CK610 select RTC_LIB select TIMER_OF diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 2890d36eb531..5cc4eea362c6 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -233,7 +233,6 @@ config PPC select NEED_SG_DMA_LENGTH select OF select OF_EARLY_FLATTREE - select OF_RESERVED_MEM select OLD_SIGACTION if PPC32 select OLD_SIGSUSPEND select PCI_DOMAINS if PCI diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 20a0756f27ef..e242a405151e 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -447,7 +447,6 @@ config USE_OF bool "Flattened Device Tree support" select OF select OF_EARLY_FLATTREE - select OF_RESERVED_MEM help Include support for flattened device tree machine descriptions. diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index ad3fcad4d75b..3607fd2810e4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -81,10 +81,9 @@ config OF_MDIO OpenFirmware MDIO bus (Ethernet PHY) accessors config OF_RESERVED_MEM - depends on OF_EARLY_FLATTREE bool - help - Helpers to allow for reservation of memory regions + depends on OF_EARLY_FLATTREE + default y if HAVE_GENERIC_DMA_COHERENT || DMA_CMA config OF_RESOLVE bool From be4311a262bcf29da60c1ef6b5a457fe5d9cccef Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Feb 2019 22:25:09 +0100 Subject: [PATCH 1756/4277] dma-mapping: remove an incorrect __iommem annotation memmap return a regular void pointer, not and __iomem one. Signed-off-by: Christoph Hellwig --- kernel/dma/coherent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c index 66f0fb7e9a3a..4b76aba574c2 100644 --- a/kernel/dma/coherent.c +++ b/kernel/dma/coherent.c @@ -43,7 +43,7 @@ static int dma_init_coherent_memory( struct dma_coherent_mem **mem) { struct dma_coherent_mem *dma_mem = NULL; - void __iomem *mem_base = NULL; + void *mem_base = NULL; int pages = size >> PAGE_SHIFT; int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); int ret; From fe9a270519c72bccb3af524db7ea6c7b67700d50 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:43 -0500 Subject: [PATCH 1757/4277] SUNRPC: Add build option to disable support for insecure enctypes Enable distributions to enforce the rejection of ancient and insecure Kerberos enctypes in the kernel's RPCSEC_GSS implementation. These are the single-DES encryption types that were deprecated in 2012 by RFC 6649. Enctypes that were deprecated more recently (by RFC 8429) remain fully supported for now because they are still likely to be widely used. Signed-off-by: Chuck Lever Acked-by: Simo Sorce Signed-off-by: Anna Schumaker --- include/linux/sunrpc/gss_krb5_enctypes.h | 42 +++++++++++++++++++++++- net/sunrpc/Kconfig | 16 +++++++++ net/sunrpc/auth_gss/gss_krb5_mech.c | 2 ++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/gss_krb5_enctypes.h b/include/linux/sunrpc/gss_krb5_enctypes.h index ec6234eee89c..981c89cef19d 100644 --- a/include/linux/sunrpc/gss_krb5_enctypes.h +++ b/include/linux/sunrpc/gss_krb5_enctypes.h @@ -1,4 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Dumb way to share this static piece of information with nfsd + * Define the string that exports the set of kernel-supported + * Kerberos enctypes. This list is sent via upcall to gssd, and + * is also exposed via the nfsd /proc API. The consumers generally + * treat this as an ordered list, where the first item in the list + * is the most preferred. + */ + +#ifndef _LINUX_SUNRPC_GSS_KRB5_ENCTYPES_H +#define _LINUX_SUNRPC_GSS_KRB5_ENCTYPES_H + +#ifdef CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES + +/* + * NB: This list includes encryption types that were deprecated + * by RFC 8429 (DES3_CBC_SHA1 and ARCFOUR_HMAC). + * + * ENCTYPE_AES256_CTS_HMAC_SHA1_96 + * ENCTYPE_AES128_CTS_HMAC_SHA1_96 + * ENCTYPE_DES3_CBC_SHA1 + * ENCTYPE_ARCFOUR_HMAC + */ +#define KRB5_SUPPORTED_ENCTYPES "18,17,16,23" + +#else /* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */ + +/* + * NB: This list includes encryption types that were deprecated + * by RFC 8429 and RFC 6649. + * + * ENCTYPE_AES256_CTS_HMAC_SHA1_96 + * ENCTYPE_AES128_CTS_HMAC_SHA1_96 + * ENCTYPE_DES3_CBC_SHA1 + * ENCTYPE_ARCFOUR_HMAC + * ENCTYPE_DES_CBC_MD5 + * ENCTYPE_DES_CBC_CRC + * ENCTYPE_DES_CBC_MD4 */ #define KRB5_SUPPORTED_ENCTYPES "18,17,16,23,3,1,2" + +#endif /* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */ + +#endif /* _LINUX_SUNRPC_GSS_KRB5_ENCTYPES_H */ diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index ac09ca803296..83f5617bae07 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -34,6 +34,22 @@ config RPCSEC_GSS_KRB5 If unsure, say Y. +config CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES + bool "Secure RPC: Disable insecure Kerberos encryption types" + depends on RPCSEC_GSS_KRB5 + default n + help + Choose Y here to disable the use of deprecated encryption types + with the Kerberos version 5 GSS-API mechanism (RFC 1964). The + deprecated encryption types include DES-CBC-MD5, DES-CBC-CRC, + and DES-CBC-MD4. These types were deprecated by RFC 6649 because + they were found to be insecure. + + N is the default because many sites have deployed KDCs and + keytabs that contain only these deprecated encryption types. + Choosing Y prevents the use of known-insecure encryption types + but might result in compatibility problems. + config SUNRPC_DEBUG bool "RPC: Enable dprintk debugging" depends on SUNRPC && SYSCTL diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index eab71fc7af3e..be31a58d54e0 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -53,6 +53,7 @@ static struct gss_api_mech gss_kerberos_mech; /* forward declaration */ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { +#ifndef CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES /* * DES (All DES enctypes are mapped to the same gss functionality) */ @@ -74,6 +75,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { .cksumlength = 8, .keyed_cksum = 0, }, +#endif /* CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES */ /* * RC4-HMAC */ From e8680a24a269bd6dcb533f4e4a5faba9ae58925c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:48 -0500 Subject: [PATCH 1758/4277] SUNRPC: Use struct xdr_stream when constructing RPC Call header Modernize and harden the code path that constructs each RPC Call message. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/auth.h | 15 ++- include/linux/sunrpc/xdr.h | 6 + include/trace/events/sunrpc.h | 29 +++++ net/sunrpc/auth.c | 62 +++++++---- net/sunrpc/auth_gss/auth_gss.c | 193 ++++++++++++++++----------------- net/sunrpc/auth_null.c | 21 ++-- net/sunrpc/auth_unix.c | 57 ++++++---- net/sunrpc/clnt.c | 62 ++++++----- 8 files changed, 265 insertions(+), 180 deletions(-) diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index eed3cb16ccf1..96e237f8e60b 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -131,11 +131,12 @@ struct rpc_credops { void (*crdestroy)(struct rpc_cred *); int (*crmatch)(struct auth_cred *, struct rpc_cred *, int); - __be32 * (*crmarshal)(struct rpc_task *, __be32 *); + int (*crmarshal)(struct rpc_task *task, + struct xdr_stream *xdr); int (*crrefresh)(struct rpc_task *); __be32 * (*crvalidate)(struct rpc_task *, __be32 *); - int (*crwrap_req)(struct rpc_task *, kxdreproc_t, - void *, __be32 *, void *); + int (*crwrap_req)(struct rpc_task *task, + struct xdr_stream *xdr); int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t, void *, __be32 *, void *); int (*crkey_timeout)(struct rpc_cred *); @@ -165,9 +166,13 @@ struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred * void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); void put_rpccred(struct rpc_cred *); -__be32 * rpcauth_marshcred(struct rpc_task *, __be32 *); +int rpcauth_marshcred(struct rpc_task *task, + struct xdr_stream *xdr); __be32 * rpcauth_checkverf(struct rpc_task *, __be32 *); -int rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, __be32 *data, void *obj); +int rpcauth_wrap_req_encode(struct rpc_task *task, + struct xdr_stream *xdr); +int rpcauth_wrap_req(struct rpc_task *task, + struct xdr_stream *xdr); int rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, __be32 *data, void *obj); bool rpcauth_xmit_need_reencode(struct rpc_task *task); int rpcauth_refreshcred(struct rpc_task *); diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 787939d13643..6df9ac1ca471 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -87,6 +87,12 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) #define xdr_one cpu_to_be32(1) #define xdr_two cpu_to_be32(2) +#define rpc_auth_null cpu_to_be32(RPC_AUTH_NULL) +#define rpc_auth_unix cpu_to_be32(RPC_AUTH_UNIX) +#define rpc_auth_gss cpu_to_be32(RPC_AUTH_GSS) + +#define rpc_call cpu_to_be32(RPC_CALL) + #define rpc_success cpu_to_be32(RPC_SUCCESS) #define rpc_prog_unavail cpu_to_be32(RPC_PROG_UNAVAIL) #define rpc_prog_mismatch cpu_to_be32(RPC_PROG_MISMATCH) diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 627650800676..2b3f9d139e75 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -213,6 +213,35 @@ DECLARE_EVENT_CLASS(rpc_task_queued, DEFINE_RPC_QUEUED_EVENT(sleep); DEFINE_RPC_QUEUED_EVENT(wakeup); +DECLARE_EVENT_CLASS(rpc_failure, + + TP_PROTO(const struct rpc_task *task), + + TP_ARGS(task), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + ), + + TP_printk("task:%u@%u", + __entry->task_id, __entry->client_id) +); + +#define DEFINE_RPC_FAILURE(name) \ + DEFINE_EVENT(rpc_failure, rpc_bad_##name, \ + TP_PROTO( \ + const struct rpc_task *task \ + ), \ + TP_ARGS(task)) + +DEFINE_RPC_FAILURE(callhdr); + TRACE_EVENT(rpc_stats_latency, TP_PROTO( diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 275e84e817b7..add2135d9b01 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -756,12 +756,21 @@ destroy: } EXPORT_SYMBOL_GPL(put_rpccred); -__be32 * -rpcauth_marshcred(struct rpc_task *task, __be32 *p) +/** + * rpcauth_marshcred - Append RPC credential to end of @xdr + * @task: controlling RPC task + * @xdr: xdr_stream containing initial portion of RPC Call header + * + * On success, an appropriate verifier is added to @xdr, @xdr is + * updated to point past the verifier, and zero is returned. + * Otherwise, @xdr is in an undefined state and a negative errno + * is returned. + */ +int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr) { - struct rpc_cred *cred = task->tk_rqstp->rq_cred; + const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; - return cred->cr_ops->crmarshal(task, p); + return ops->crmarshal(task, xdr); } __be32 * @@ -772,27 +781,38 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p) return cred->cr_ops->crvalidate(task, p); } -static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, - __be32 *data, void *obj) +/** + * rpcauth_wrap_req_encode - XDR encode the RPC procedure + * @task: controlling RPC task + * @xdr: stream where on-the-wire bytes are to be marshalled + * + * On success, @xdr contains the encoded and wrapped message. + * Otherwise, @xdr is in an undefined state. + */ +int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr) { - struct xdr_stream xdr; + kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode; - xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data, rqstp); - encode(rqstp, &xdr, obj); -} - -int -rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, - __be32 *data, void *obj) -{ - struct rpc_cred *cred = task->tk_rqstp->rq_cred; - - if (cred->cr_ops->crwrap_req) - return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); - /* By default, we encode the arguments normally. */ - rpcauth_wrap_req_encode(encode, rqstp, data, obj); + encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp); return 0; } +EXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode); + +/** + * rpcauth_wrap_req - XDR encode and wrap the RPC procedure + * @task: controlling RPC task + * @xdr: stream where on-the-wire bytes are to be marshalled + * + * On success, @xdr contains the encoded and wrapped message, + * and zero is returned. Otherwise, @xdr is in an undefined + * state and a negative errno is returned. + */ +int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) +{ + const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; + + return ops->crwrap_req(task, xdr); +} static int rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 4b52e2b11c58..b333b1bdad45 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1526,18 +1526,20 @@ out: } /* -* Marshal credentials. -* Maybe we should keep a cached credential for performance reasons. -*/ -static __be32 * -gss_marshal(struct rpc_task *task, __be32 *p) + * Marshal credentials. + * + * The expensive part is computing the verifier. We can't cache a + * pre-computed version of the verifier because the seqno, which + * is different every time, is included in the MIC. + */ +static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) { struct rpc_rqst *req = task->tk_rqstp; struct rpc_cred *cred = req->rq_cred; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); - __be32 *cred_len; + __be32 *p, *cred_len; u32 maj_stat = 0; struct xdr_netobj mic; struct kvec iov; @@ -1545,7 +1547,13 @@ gss_marshal(struct rpc_task *task, __be32 *p) dprintk("RPC: %5u %s\n", task->tk_pid, __func__); - *p++ = htonl(RPC_AUTH_GSS); + /* Credential */ + + p = xdr_reserve_space(xdr, 7 * sizeof(*p) + + ctx->gc_wire_ctx.len); + if (!p) + goto out_put_ctx; + *p++ = rpc_auth_gss; cred_len = p++; spin_lock(&ctx->gc_seq_lock); @@ -1554,12 +1562,14 @@ gss_marshal(struct rpc_task *task, __be32 *p) if (req->rq_seqno == MAXSEQ) goto out_expired; - *p++ = htonl((u32) RPC_GSS_VERSION); - *p++ = htonl((u32) ctx->gc_proc); - *p++ = htonl((u32) req->rq_seqno); - *p++ = htonl((u32) gss_cred->gc_service); + *p++ = cpu_to_be32(RPC_GSS_VERSION); + *p++ = cpu_to_be32(ctx->gc_proc); + *p++ = cpu_to_be32(req->rq_seqno); + *p++ = cpu_to_be32(gss_cred->gc_service); p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); - *cred_len = htonl((p - (cred_len + 1)) << 2); + *cred_len = cpu_to_be32((p - (cred_len + 1)) << 2); + + /* Verifier */ /* We compute the checksum for the verifier over the xdr-encoded bytes * starting with the xid and ending at the end of the credential: */ @@ -1567,27 +1577,27 @@ gss_marshal(struct rpc_task *task, __be32 *p) iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; xdr_buf_from_iov(&iov, &verf_buf); - /* set verifier flavor*/ - *p++ = htonl(RPC_AUTH_GSS); - + p = xdr_reserve_space(xdr, sizeof(*p)); + if (!p) + goto out_put_ctx; + *p++ = rpc_auth_gss; mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); - if (maj_stat == GSS_S_CONTEXT_EXPIRED) { + if (maj_stat == GSS_S_CONTEXT_EXPIRED) goto out_expired; - } else if (maj_stat != 0) { - pr_warn("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); - task->tk_status = -EIO; + else if (maj_stat != 0) + goto out_put_ctx; + if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) goto out_put_ctx; - } - p = xdr_encode_opaque(p, NULL, mic.len); gss_put_ctx(ctx); - return p; + return 0; out_expired: + gss_put_ctx(ctx); clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); - task->tk_status = -EKEYEXPIRED; + return -EKEYEXPIRED; out_put_ctx: gss_put_ctx(ctx); - return NULL; + return -EMSGSIZE; } static int gss_renew_cred(struct rpc_task *task) @@ -1716,61 +1726,45 @@ out_bad: return ret; } -static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, - __be32 *p, void *obj) +static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, + struct rpc_task *task, struct xdr_stream *xdr) { - struct xdr_stream xdr; - - xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p, rqstp); - encode(rqstp, &xdr, obj); -} - -static inline int -gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, - kxdreproc_t encode, struct rpc_rqst *rqstp, - __be32 *p, void *obj) -{ - struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; - struct xdr_buf integ_buf; - __be32 *integ_len = NULL; + struct rpc_rqst *rqstp = task->tk_rqstp; + struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf; struct xdr_netobj mic; - u32 offset; - __be32 *q; - struct kvec *iov; - u32 maj_stat = 0; - int status = -EIO; + __be32 *p, *integ_len; + u32 offset, maj_stat; + p = xdr_reserve_space(xdr, 2 * sizeof(*p)); + if (!p) + goto wrap_failed; integ_len = p++; + *p = cpu_to_be32(rqstp->rq_seqno); + + if (rpcauth_wrap_req_encode(task, xdr)) + goto wrap_failed; + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; - *p++ = htonl(rqstp->rq_seqno); - - gss_wrap_req_encode(encode, rqstp, p, obj); - if (xdr_buf_subsegment(snd_buf, &integ_buf, offset, snd_buf->len - offset)) - return status; - *integ_len = htonl(integ_buf.len); + goto wrap_failed; + *integ_len = cpu_to_be32(integ_buf.len); - /* guess whether we're in the head or the tail: */ - if (snd_buf->page_len || snd_buf->tail[0].iov_len) - iov = snd_buf->tail; - else - iov = snd_buf->head; - p = iov->iov_base + iov->iov_len; + p = xdr_reserve_space(xdr, 0); + if (!p) + goto wrap_failed; mic.data = (u8 *)(p + 1); - maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); - status = -EIO; /* XXX? */ if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); else if (maj_stat) - return status; - q = xdr_encode_opaque(p, NULL, mic.len); - - offset = (u8 *)q - (u8 *)p; - iov->iov_len += offset; - snd_buf->len += offset; + goto wrap_failed; + /* Check that the trailing MIC fit in the buffer, after the fact */ + if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) + goto wrap_failed; return 0; +wrap_failed: + return -EMSGSIZE; } static void @@ -1821,61 +1815,63 @@ out: return -EAGAIN; } -static inline int -gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, - kxdreproc_t encode, struct rpc_rqst *rqstp, - __be32 *p, void *obj) +static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, + struct rpc_task *task, struct xdr_stream *xdr) { + struct rpc_rqst *rqstp = task->tk_rqstp; struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; - u32 offset; - u32 maj_stat; + u32 pad, offset, maj_stat; int status; - __be32 *opaque_len; + __be32 *p, *opaque_len; struct page **inpages; int first; - int pad; struct kvec *iov; - char *tmp; + status = -EIO; + p = xdr_reserve_space(xdr, 2 * sizeof(*p)); + if (!p) + goto wrap_failed; opaque_len = p++; - offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; - *p++ = htonl(rqstp->rq_seqno); + *p = cpu_to_be32(rqstp->rq_seqno); - gss_wrap_req_encode(encode, rqstp, p, obj); + if (rpcauth_wrap_req_encode(task, xdr)) + goto wrap_failed; status = alloc_enc_pages(rqstp); - if (status) - return status; + if (unlikely(status)) + goto wrap_failed; first = snd_buf->page_base >> PAGE_SHIFT; inpages = snd_buf->pages + first; snd_buf->pages = rqstp->rq_enc_pages; snd_buf->page_base -= first << PAGE_SHIFT; /* - * Give the tail its own page, in case we need extra space in the - * head when wrapping: + * Move the tail into its own page, in case gss_wrap needs + * more space in the head when wrapping. * - * call_allocate() allocates twice the slack space required - * by the authentication flavor to rq_callsize. - * For GSS, slack is GSS_CRED_SLACK. + * Still... Why can't gss_wrap just slide the tail down? */ if (snd_buf->page_len || snd_buf->tail[0].iov_len) { + char *tmp; + tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); snd_buf->tail[0].iov_base = tmp; } + status = -EIO; + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); /* slack space should prevent this ever happening: */ - BUG_ON(snd_buf->len > snd_buf->buflen); - status = -EIO; + if (unlikely(snd_buf->len > snd_buf->buflen)) + goto wrap_failed; /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was * done anyway, so it's safe to put the request on the wire: */ if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); else if (maj_stat) - return status; + goto wrap_failed; - *opaque_len = htonl(snd_buf->len - offset); - /* guess whether we're in the head or the tail: */ + *opaque_len = cpu_to_be32(snd_buf->len - offset); + /* guess whether the pad goes into the head or the tail: */ if (snd_buf->page_len || snd_buf->tail[0].iov_len) iov = snd_buf->tail; else @@ -1887,37 +1883,36 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, snd_buf->len += pad; return 0; +wrap_failed: + return status; } -static int -gss_wrap_req(struct rpc_task *task, - kxdreproc_t encode, void *rqstp, __be32 *p, void *obj) +static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) { struct rpc_cred *cred = task->tk_rqstp->rq_cred; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); - int status = -EIO; + int status; dprintk("RPC: %5u %s\n", task->tk_pid, __func__); + status = -EIO; if (ctx->gc_proc != RPC_GSS_PROC_DATA) { /* The spec seems a little ambiguous here, but I think that not * wrapping context destruction requests makes the most sense. */ - gss_wrap_req_encode(encode, rqstp, p, obj); - status = 0; + status = rpcauth_wrap_req_encode(task, xdr); goto out; } switch (gss_cred->gc_service) { case RPC_GSS_SVC_NONE: - gss_wrap_req_encode(encode, rqstp, p, obj); - status = 0; + status = rpcauth_wrap_req_encode(task, xdr); break; case RPC_GSS_SVC_INTEGRITY: - status = gss_wrap_req_integ(cred, ctx, encode, rqstp, p, obj); + status = gss_wrap_req_integ(cred, ctx, task, xdr); break; case RPC_GSS_SVC_PRIVACY: - status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj); + status = gss_wrap_req_priv(cred, ctx, task, xdr); break; } out: diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index d0ceac57c06e..797f8472c21b 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -59,15 +59,21 @@ nul_match(struct auth_cred *acred, struct rpc_cred *cred, int taskflags) /* * Marshal credential. */ -static __be32 * -nul_marshal(struct rpc_task *task, __be32 *p) +static int +nul_marshal(struct rpc_task *task, struct xdr_stream *xdr) { - *p++ = htonl(RPC_AUTH_NULL); - *p++ = 0; - *p++ = htonl(RPC_AUTH_NULL); - *p++ = 0; + __be32 *p; - return p; + p = xdr_reserve_space(xdr, 4 * sizeof(*p)); + if (!p) + return -EMSGSIZE; + /* Credential */ + *p++ = rpc_auth_null; + *p++ = xdr_zero; + /* Verifier */ + *p++ = rpc_auth_null; + *p = xdr_zero; + return 0; } /* @@ -125,6 +131,7 @@ const struct rpc_credops null_credops = { .crdestroy = nul_destroy_cred, .crmatch = nul_match, .crmarshal = nul_marshal, + .crwrap_req = rpcauth_wrap_req_encode, .crrefresh = nul_refresh, .crvalidate = nul_validate, }; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index fc8a59134640..1d5b7ed9c6f7 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -99,37 +99,55 @@ unx_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) * Marshal credentials. * Maybe we should keep a cached credential for performance reasons. */ -static __be32 * -unx_marshal(struct rpc_task *task, __be32 *p) +static int +unx_marshal(struct rpc_task *task, struct xdr_stream *xdr) { struct rpc_clnt *clnt = task->tk_client; struct rpc_cred *cred = task->tk_rqstp->rq_cred; - __be32 *base, *hold; + __be32 *p, *cred_len, *gidarr_len; int i; struct group_info *gi = cred->cr_cred->group_info; - *p++ = htonl(RPC_AUTH_UNIX); - base = p++; - *p++ = htonl(jiffies/HZ); + /* Credential */ - /* - * Copy the UTS nodename captured when the client was created. - */ - p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); + p = xdr_reserve_space(xdr, 3 * sizeof(*p)); + if (!p) + goto marshal_failed; + *p++ = rpc_auth_unix; + cred_len = p++; + *p++ = xdr_zero; /* stamp */ + if (xdr_stream_encode_opaque(xdr, clnt->cl_nodename, + clnt->cl_nodelen) < 0) + goto marshal_failed; + p = xdr_reserve_space(xdr, 3 * sizeof(*p)); + if (!p) + goto marshal_failed; + *p++ = cpu_to_be32(from_kuid(&init_user_ns, cred->cr_cred->fsuid)); + *p++ = cpu_to_be32(from_kgid(&init_user_ns, cred->cr_cred->fsgid)); - *p++ = htonl((u32) from_kuid(&init_user_ns, cred->cr_cred->fsuid)); - *p++ = htonl((u32) from_kgid(&init_user_ns, cred->cr_cred->fsgid)); - hold = p++; + gidarr_len = p++; if (gi) for (i = 0; i < UNX_NGROUPS && i < gi->ngroups; i++) - *p++ = htonl((u32) from_kgid(&init_user_ns, gi->gid[i])); - *hold = htonl(p - hold - 1); /* gid array length */ - *base = htonl((p - base - 1) << 2); /* cred length */ + *p++ = cpu_to_be32(from_kgid(&init_user_ns, + gi->gid[i])); + *gidarr_len = cpu_to_be32(p - gidarr_len - 1); + *cred_len = cpu_to_be32((p - cred_len - 1) << 2); + p = xdr_reserve_space(xdr, (p - gidarr_len - 1) << 2); + if (!p) + goto marshal_failed; - *p++ = htonl(RPC_AUTH_NULL); - *p++ = htonl(0); + /* Verifier */ - return p; + p = xdr_reserve_space(xdr, 2 * sizeof(*p)); + if (!p) + goto marshal_failed; + *p++ = rpc_auth_null; + *p = xdr_zero; + + return 0; + +marshal_failed: + return -EMSGSIZE; } /* @@ -202,6 +220,7 @@ const struct rpc_credops unix_credops = { .crdestroy = unx_destroy_cred, .crmatch = unx_match, .crmarshal = unx_marshal, + .crwrap_req = rpcauth_wrap_req_encode, .crrefresh = unx_refresh, .crvalidate = unx_validate, }; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index c4203f6138ef..d6750b7f169a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -77,7 +77,8 @@ static void call_timeout(struct rpc_task *task); static void call_connect(struct rpc_task *task); static void call_connect_status(struct rpc_task *task); -static __be32 *rpc_encode_header(struct rpc_task *task); +static int rpc_encode_header(struct rpc_task *task, + struct xdr_stream *xdr); static __be32 *rpc_verify_header(struct rpc_task *task); static int rpc_ping(struct rpc_clnt *clnt); @@ -1728,10 +1729,7 @@ static void rpc_xdr_encode(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; - kxdreproc_t encode; - __be32 *p; - - dprint_status(task); + struct xdr_stream xdr; xdr_buf_init(&req->rq_snd_buf, req->rq_buffer, @@ -1740,18 +1738,13 @@ rpc_xdr_encode(struct rpc_task *task) req->rq_rbuffer, req->rq_rcvsize); - p = rpc_encode_header(task); - if (p == NULL) + req->rq_snd_buf.head[0].iov_len = 0; + xdr_init_encode(&xdr, &req->rq_snd_buf, + req->rq_snd_buf.head[0].iov_base, req); + if (rpc_encode_header(task, &xdr)) return; - encode = task->tk_msg.rpc_proc->p_encode; - if (encode == NULL) - return; - - task->tk_status = rpcauth_wrap_req(task, encode, req, p, - task->tk_msg.rpc_argp); - if (task->tk_status == 0) - xprt_request_prepare(req); + task->tk_status = rpcauth_wrap_req(task, &xdr); } /* @@ -1762,6 +1755,7 @@ call_encode(struct rpc_task *task) { if (!rpc_task_need_encode(task)) goto out; + dprint_status(task); /* Encode here so that rpcsec_gss can use correct sequence number. */ rpc_xdr_encode(task); /* Did the encode result in an error condition? */ @@ -1779,6 +1773,8 @@ call_encode(struct rpc_task *task) rpc_exit(task, task->tk_status); } return; + } else { + xprt_request_prepare(task->tk_rqstp); } /* Add task to reply queue before transmission to avoid races */ @@ -2322,25 +2318,33 @@ out_retry: } } -static __be32 * -rpc_encode_header(struct rpc_task *task) +static int +rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr) { struct rpc_clnt *clnt = task->tk_client; struct rpc_rqst *req = task->tk_rqstp; - __be32 *p = req->rq_svec[0].iov_base; + __be32 *p; + int error; - /* FIXME: check buffer size? */ + error = -EMSGSIZE; + p = xdr_reserve_space(xdr, RPC_CALLHDRSIZE << 2); + if (!p) + goto out_fail; + *p++ = req->rq_xid; + *p++ = rpc_call; + *p++ = cpu_to_be32(RPC_VERSION); + *p++ = cpu_to_be32(clnt->cl_prog); + *p++ = cpu_to_be32(clnt->cl_vers); + *p = cpu_to_be32(task->tk_msg.rpc_proc->p_proc); - *p++ = req->rq_xid; /* XID */ - *p++ = htonl(RPC_CALL); /* CALL */ - *p++ = htonl(RPC_VERSION); /* RPC version */ - *p++ = htonl(clnt->cl_prog); /* program number */ - *p++ = htonl(clnt->cl_vers); /* program version */ - *p++ = htonl(task->tk_msg.rpc_proc->p_proc); /* procedure */ - p = rpcauth_marshcred(task, p); - if (p) - req->rq_slen = xdr_adjust_iovec(&req->rq_svec[0], p); - return p; + error = rpcauth_marshcred(task, xdr); + if (error < 0) + goto out_fail; + return 0; +out_fail: + trace_rpc_bad_callhdr(task); + rpc_exit(task, error); + return error; } static __be32 * From 7f5667a5f8c4ff85b14ccce9d41f9244bd30ab68 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:53 -0500 Subject: [PATCH 1759/4277] SUNRPC: Clean up rpc_verify_header() - Recover some instruction count because I'm about to introduce a few xdr_inline_decode call sites - Replace dprintk() call sites with trace points - Reduce the hot path so it fits in fewer cachelines I've also renamed it rpc_decode_header() to match everything else in the RPC client. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xdr.h | 7 +- include/trace/events/sunrpc.h | 52 ++++++++ net/sunrpc/clnt.c | 223 +++++++++++++++------------------- 3 files changed, 154 insertions(+), 128 deletions(-) diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 6df9ac1ca471..c54041950cc0 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -92,6 +92,9 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) #define rpc_auth_gss cpu_to_be32(RPC_AUTH_GSS) #define rpc_call cpu_to_be32(RPC_CALL) +#define rpc_reply cpu_to_be32(RPC_REPLY) + +#define rpc_msg_accepted cpu_to_be32(RPC_MSG_ACCEPTED) #define rpc_success cpu_to_be32(RPC_SUCCESS) #define rpc_prog_unavail cpu_to_be32(RPC_PROG_UNAVAIL) @@ -101,6 +104,9 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) #define rpc_system_err cpu_to_be32(RPC_SYSTEM_ERR) #define rpc_drop_reply cpu_to_be32(RPC_DROP_REPLY) +#define rpc_mismatch cpu_to_be32(RPC_MISMATCH) +#define rpc_auth_error cpu_to_be32(RPC_AUTH_ERROR) + #define rpc_auth_ok cpu_to_be32(RPC_AUTH_OK) #define rpc_autherr_badcred cpu_to_be32(RPC_AUTH_BADCRED) #define rpc_autherr_rejectedcred cpu_to_be32(RPC_AUTH_REJECTEDCRED) @@ -109,7 +115,6 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) #define rpc_autherr_tooweak cpu_to_be32(RPC_AUTH_TOOWEAK) #define rpcsec_gsserr_credproblem cpu_to_be32(RPCSEC_GSS_CREDPROBLEM) #define rpcsec_gsserr_ctxproblem cpu_to_be32(RPCSEC_GSS_CTXPROBLEM) -#define rpc_autherr_oldseqnum cpu_to_be32(101) /* * Miscellaneous XDR helper functions diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 2b3f9d139e75..0654e9c50371 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -241,6 +241,58 @@ DECLARE_EVENT_CLASS(rpc_failure, TP_ARGS(task)) DEFINE_RPC_FAILURE(callhdr); +DEFINE_RPC_FAILURE(verifier); + +DECLARE_EVENT_CLASS(rpc_reply_event, + + TP_PROTO( + const struct rpc_task *task + ), + + TP_ARGS(task), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, xid) + __string(progname, task->tk_client->cl_program->name) + __field(u32, version) + __string(procname, rpc_proc_name(task)) + __string(servername, task->tk_xprt->servername) + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __entry->xid = be32_to_cpu(task->tk_rqstp->rq_xid); + __assign_str(progname, task->tk_client->cl_program->name) + __entry->version = task->tk_client->cl_vers; + __assign_str(procname, rpc_proc_name(task)) + __assign_str(servername, task->tk_xprt->servername) + ), + + TP_printk("task:%u@%d server=%s xid=0x%08x %sv%d %s", + __entry->task_id, __entry->client_id, __get_str(servername), + __entry->xid, __get_str(progname), __entry->version, + __get_str(procname)) +) + +#define DEFINE_RPC_REPLY_EVENT(name) \ + DEFINE_EVENT(rpc_reply_event, rpc__##name, \ + TP_PROTO( \ + const struct rpc_task *task \ + ), \ + TP_ARGS(task)) + +DEFINE_RPC_REPLY_EVENT(prog_unavail); +DEFINE_RPC_REPLY_EVENT(prog_mismatch); +DEFINE_RPC_REPLY_EVENT(proc_unavail); +DEFINE_RPC_REPLY_EVENT(garbage_args); +DEFINE_RPC_REPLY_EVENT(unparsable); +DEFINE_RPC_REPLY_EVENT(mismatch); +DEFINE_RPC_REPLY_EVENT(stale_creds); +DEFINE_RPC_REPLY_EVENT(bad_creds); +DEFINE_RPC_REPLY_EVENT(auth_tooweak); TRACE_EVENT(rpc_stats_latency, diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index d6750b7f169a..e9735089bd66 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -79,7 +79,7 @@ static void call_connect_status(struct rpc_task *task); static int rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr); -static __be32 *rpc_verify_header(struct rpc_task *task); +static __be32 *rpc_decode_header(struct rpc_task *task); static int rpc_ping(struct rpc_clnt *clnt); static void rpc_register_client(struct rpc_clnt *clnt) @@ -2292,7 +2292,7 @@ call_decode(struct rpc_task *task) goto out_retry; } - p = rpc_verify_header(task); + p = rpc_decode_header(task); if (IS_ERR(p)) { if (p == ERR_PTR(-EAGAIN)) goto out_retry; @@ -2308,7 +2308,7 @@ call_decode(struct rpc_task *task) return; out_retry: task->tk_status = 0; - /* Note: rpc_verify_header() may have freed the RPC slot */ + /* Note: rpc_decode_header() may have freed the RPC slot */ if (task->tk_rqstp == req) { xdr_free_bvec(&req->rq_rcv_buf); req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0; @@ -2347,164 +2347,133 @@ out_fail: return error; } -static __be32 * -rpc_verify_header(struct rpc_task *task) +static noinline __be32 * +rpc_decode_header(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0]; int len = task->tk_rqstp->rq_rcv_buf.len >> 2; __be32 *p = iov->iov_base; - u32 n; int error = -EACCES; - if ((task->tk_rqstp->rq_rcv_buf.len & 3) != 0) { - /* RFC-1014 says that the representation of XDR data must be a - * multiple of four bytes - * - if it isn't pointer subtraction in the NFS client may give - * undefined results - */ - dprintk("RPC: %5u %s: XDR representation not a multiple of" - " 4 bytes: 0x%x\n", task->tk_pid, __func__, - task->tk_rqstp->rq_rcv_buf.len); - error = -EIO; - goto out_err; - } + /* RFC-1014 says that the representation of XDR data must be a + * multiple of four bytes + * - if it isn't pointer subtraction in the NFS client may give + * undefined results + */ + if (task->tk_rqstp->rq_rcv_buf.len & 3) + goto out_badlen; if ((len -= 3) < 0) - goto out_overflow; + goto out_unparsable; - p += 1; /* skip XID */ - if ((n = ntohl(*p++)) != RPC_REPLY) { - dprintk("RPC: %5u %s: not an RPC reply: %x\n", - task->tk_pid, __func__, n); - error = -EIO; - goto out_garbage; - } + p++; /* skip XID */ + if (*p++ != rpc_reply) + goto out_unparsable; + if (*p++ != rpc_msg_accepted) + goto out_msg_denied; - if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { - if (--len < 0) - goto out_overflow; - switch ((n = ntohl(*p++))) { - case RPC_AUTH_ERROR: - break; - case RPC_MISMATCH: - dprintk("RPC: %5u %s: RPC call version mismatch!\n", - task->tk_pid, __func__); - error = -EPROTONOSUPPORT; - goto out_err; - default: - dprintk("RPC: %5u %s: RPC call rejected, " - "unknown error: %x\n", - task->tk_pid, __func__, n); - error = -EIO; - goto out_err; - } - if (--len < 0) - goto out_overflow; - switch ((n = ntohl(*p++))) { - case RPC_AUTH_REJECTEDCRED: - case RPC_AUTH_REJECTEDVERF: - case RPCSEC_GSS_CREDPROBLEM: - case RPCSEC_GSS_CTXPROBLEM: - if (!task->tk_cred_retry) - break; - task->tk_cred_retry--; - dprintk("RPC: %5u %s: retry stale creds\n", - task->tk_pid, __func__); - rpcauth_invalcred(task); - /* Ensure we obtain a new XID! */ - xprt_release(task); - task->tk_action = call_reserve; - goto out_retry; - case RPC_AUTH_BADCRED: - case RPC_AUTH_BADVERF: - /* possibly garbled cred/verf? */ - if (!task->tk_garb_retry) - break; - task->tk_garb_retry--; - dprintk("RPC: %5u %s: retry garbled creds\n", - task->tk_pid, __func__); - task->tk_action = call_encode; - goto out_retry; - case RPC_AUTH_TOOWEAK: - printk(KERN_NOTICE "RPC: server %s requires stronger " - "authentication.\n", - task->tk_xprt->servername); - break; - default: - dprintk("RPC: %5u %s: unknown auth error: %x\n", - task->tk_pid, __func__, n); - error = -EIO; - } - dprintk("RPC: %5u %s: call rejected %d\n", - task->tk_pid, __func__, n); - goto out_err; - } p = rpcauth_checkverf(task, p); - if (IS_ERR(p)) { - error = PTR_ERR(p); - dprintk("RPC: %5u %s: auth check failed with %d\n", - task->tk_pid, __func__, error); - goto out_garbage; /* bad verifier, retry */ - } + if (IS_ERR(p)) + goto out_verifier; + len = p - (__be32 *)iov->iov_base - 1; if (len < 0) - goto out_overflow; - switch ((n = ntohl(*p++))) { - case RPC_SUCCESS: + goto out_unparsable; + switch (*p++) { + case rpc_success: return p; - case RPC_PROG_UNAVAIL: - dprintk("RPC: %5u %s: program %u is unsupported " - "by server %s\n", task->tk_pid, __func__, - (unsigned int)clnt->cl_prog, - task->tk_xprt->servername); + case rpc_prog_unavail: + trace_rpc__prog_unavail(task); error = -EPFNOSUPPORT; goto out_err; - case RPC_PROG_MISMATCH: - dprintk("RPC: %5u %s: program %u, version %u unsupported " - "by server %s\n", task->tk_pid, __func__, - (unsigned int)clnt->cl_prog, - (unsigned int)clnt->cl_vers, - task->tk_xprt->servername); + case rpc_prog_mismatch: + trace_rpc__prog_mismatch(task); error = -EPROTONOSUPPORT; goto out_err; - case RPC_PROC_UNAVAIL: - dprintk("RPC: %5u %s: proc %s unsupported by program %u, " - "version %u on server %s\n", - task->tk_pid, __func__, - rpc_proc_name(task), - clnt->cl_prog, clnt->cl_vers, - task->tk_xprt->servername); + case rpc_proc_unavail: + trace_rpc__proc_unavail(task); error = -EOPNOTSUPP; goto out_err; - case RPC_GARBAGE_ARGS: - dprintk("RPC: %5u %s: server saw garbage\n", - task->tk_pid, __func__); - break; /* retry */ + case rpc_garbage_args: + trace_rpc__garbage_args(task); + break; default: - dprintk("RPC: %5u %s: server accept status: %x\n", - task->tk_pid, __func__, n); - /* Also retry */ + trace_rpc__unparsable(task); } out_garbage: clnt->cl_stats->rpcgarbage++; if (task->tk_garb_retry) { task->tk_garb_retry--; - dprintk("RPC: %5u %s: retrying\n", - task->tk_pid, __func__); task->tk_action = call_encode; -out_retry: return ERR_PTR(-EAGAIN); } out_err: rpc_exit(task, error); - dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid, - __func__, error); return ERR_PTR(error); -out_overflow: - dprintk("RPC: %5u %s: server reply was truncated.\n", task->tk_pid, - __func__); + +out_badlen: + trace_rpc__unparsable(task); + error = -EIO; + goto out_err; + +out_unparsable: + trace_rpc__unparsable(task); + error = -EIO; goto out_garbage; + +out_verifier: + trace_rpc_bad_verifier(task); + error = PTR_ERR(p); + goto out_garbage; + +out_msg_denied: + switch (*p++) { + case rpc_auth_error: + break; + case rpc_mismatch: + trace_rpc__mismatch(task); + error = -EPROTONOSUPPORT; + goto out_err; + default: + trace_rpc__unparsable(task); + error = -EIO; + goto out_err; + } + + switch (*p++) { + case rpc_autherr_rejectedcred: + case rpc_autherr_rejectedverf: + case rpcsec_gsserr_credproblem: + case rpcsec_gsserr_ctxproblem: + if (!task->tk_cred_retry) + break; + task->tk_cred_retry--; + trace_rpc__stale_creds(task); + rpcauth_invalcred(task); + /* Ensure we obtain a new XID! */ + xprt_release(task); + task->tk_action = call_reserve; + return ERR_PTR(-EAGAIN); + case rpc_autherr_badcred: + case rpc_autherr_badverf: + /* possibly garbled cred/verf? */ + if (!task->tk_garb_retry) + break; + task->tk_garb_retry--; + trace_rpc__bad_creds(task); + task->tk_action = call_encode; + return ERR_PTR(-EAGAIN); + case rpc_autherr_tooweak: + trace_rpc__auth_tooweak(task); + pr_warn("RPC: server %s requires stronger authentication.\n", + task->tk_xprt->servername); + break; + default: + trace_rpc__unparsable(task); + error = -EIO; + } + goto out_err; } static void rpcproc_encode_null(struct rpc_rqst *rqstp, struct xdr_stream *xdr, From 7f8e12f1e21524bd498477893b235e908843f1b7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 13 Feb 2019 10:08:29 -0600 Subject: [PATCH 1760/4277] scsi: qlogicpti: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. As prom_name is not used for anything else, remove it. Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Martin K. Petersen --- drivers/scsi/qlogicpti.c | 3 +-- drivers/scsi/qlogicpti.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index e35ce762d454..0e22512bd3e4 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1314,8 +1314,7 @@ static int qpti_sbus_probe(struct platform_device *op) qpti->qhost = host; qpti->op = op; qpti->qpti_id = nqptis; - strcpy(qpti->prom_name, op->dev.of_node->name); - qpti->is_pti = strcmp(qpti->prom_name, "QLGC,isp"); + qpti->is_pti = !of_node_name_eq(op->dev.of_node, "QLGC,isp"); if (qpti_map_regs(qpti) < 0) goto fail_unlink; diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h index 884ad72ade57..2b6374e08a7d 100644 --- a/drivers/scsi/qlogicpti.h +++ b/drivers/scsi/qlogicpti.h @@ -364,7 +364,6 @@ struct qlogicpti { int qpti_id; int scsi_id; int prom_node; - char prom_name[64]; int irq; char differential, ultra, clock; unsigned char bursts; @@ -379,7 +378,7 @@ struct qlogicpti { #define SREG_IMASK 0x0c /* Interrupt level */ #define SREG_SPMASK 0x03 /* Mask for switch pack */ unsigned char swsreg; - unsigned int + unsigned int gotirq : 1, /* this instance got an irq */ is_pti : 1; /* Non-zero if this is a PTI board. */ }; From 258f84fae3aced90613b44e69b123b87c693ccaf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 12 Feb 2019 15:29:45 +0000 Subject: [PATCH 1761/4277] scsi: lpfc: fix a handful of indentation issues There are a handful of statements that are indented incorrectly. Fix these. Signed-off-by: Colin Ian King Acked-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_bsg.c | 4 ++-- drivers/scsi/lpfc/lpfc_debugfs.c | 4 ++-- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/scsi/lpfc/lpfc_mbox.c | 4 ++-- drivers/scsi/lpfc/lpfc_sli.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 8698af86485d..41ec26305b3a 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -2947,7 +2947,7 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri, cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys); cmd->un.cont64[i].tus.f.bdeSize = ((struct lpfc_dmabufext *)mp[i])->size; - cmd->ulpBdeCount = ++i; + cmd->ulpBdeCount = ++i; if ((--num_bde > 0) && (i < 2)) continue; @@ -4682,7 +4682,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct bsg_job *job, * Don't allow mailbox commands to be sent when blocked or when in * the middle of discovery */ - if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { + if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { rc = -EAGAIN; goto job_done; } diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index ee98ea1c68f9..1215eaa530db 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1833,7 +1833,7 @@ lpfc_debugfs_disc_trc_open(struct inode *inode, struct file *file) int rc = -ENOMEM; if (!lpfc_debugfs_max_disc_trc) { - rc = -ENOSPC; + rc = -ENOSPC; goto out; } @@ -1883,7 +1883,7 @@ lpfc_debugfs_slow_ring_trc_open(struct inode *inode, struct file *file) int rc = -ENOMEM; if (!lpfc_debugfs_max_slow_ring_trc) { - rc = -ENOSPC; + rc = -ENOSPC; goto out; } diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index eef6b929c327..2f1835de1e6a 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -10119,7 +10119,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) dev_printk(KERN_ERR, &pdev->dev, "ioremap failed for SLI4 PCI config " "registers.\n"); - goto out; + goto out; } lpfc_sli4_bar0_register_memmap(phba, if_type); } diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index f6a5083a621e..f9ab93820791 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -2095,8 +2095,8 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq) if (phba->nvmet_support) { bf_set(lpfc_mbx_rq_ftr_rq_mrqp, &mboxq->u.mqe.un.req_ftrs, 1); /* iaab/iaar NOT set for now */ - bf_set(lpfc_mbx_rq_ftr_rq_iaab, &mboxq->u.mqe.un.req_ftrs, 0); - bf_set(lpfc_mbx_rq_ftr_rq_iaar, &mboxq->u.mqe.un.req_ftrs, 0); + bf_set(lpfc_mbx_rq_ftr_rq_iaab, &mboxq->u.mqe.un.req_ftrs, 0); + bf_set(lpfc_mbx_rq_ftr_rq_iaar, &mboxq->u.mqe.un.req_ftrs, 0); } return; } diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index c4c319245a66..62957d682d3f 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1008,7 +1008,7 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, if (!ndlp->active_rrqs_xri_bitmap) return 0; if (test_bit(xritag, ndlp->active_rrqs_xri_bitmap)) - return 1; + return 1; else return 0; } From 7f2f787c10596f486644d730a0a23e78abe8cbe0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Feb 2019 14:14:09 +0100 Subject: [PATCH 1762/4277] gpio: pcf857x: Simpify wake-up handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike gpio_rcar_irq_set_wake(), which this solution was based on, pcf857x_irq_set_wake() does not need to do anything else than calling irq_set_irq_wake() for the upstream interrupt controller. Hence there is no reason for making this call conditional, and no longer a need for keeping a copy of the interrupt number that also serves as a flag. Just propagate irq_set_irq_wake() to the upstream interrupt controller, using the original interrupt number. Signed-off-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pcf857x.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index adf72dda25a2..064c1d0a93e0 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -88,7 +88,6 @@ struct pcf857x { struct mutex lock; /* protect 'out' */ unsigned out; /* software latch */ unsigned status; /* current status */ - unsigned int irq_parent; unsigned irq_enabled; /* enabled irqs */ int (*write)(struct i2c_client *client, unsigned data); @@ -210,18 +209,7 @@ static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on) { struct pcf857x *gpio = irq_data_get_irq_chip_data(data); - int error = 0; - - if (gpio->irq_parent) { - error = irq_set_irq_wake(gpio->irq_parent, on); - if (error) { - dev_dbg(&gpio->client->dev, - "irq %u doesn't support irq_set_wake\n", - gpio->irq_parent); - gpio->irq_parent = 0; - } - } - return error; + return irq_set_irq_wake(gpio->client->irq, on); } static void pcf857x_irq_enable(struct irq_data *data) @@ -394,7 +382,6 @@ static int pcf857x_probe(struct i2c_client *client, gpiochip_set_nested_irqchip(&gpio->chip, &pcf857x_irq_chip, client->irq); - gpio->irq_parent = client->irq; } /* Let platform code set up the GPIOs and their users. From 760a160e8b899f5ebcab99da17feebbe40ec42f1 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Wed, 13 Feb 2019 20:36:39 -0500 Subject: [PATCH 1763/4277] spmi: pmic-arb: select IRQ_DOMAIN_HIERARCHY in Kconfig Select IRQ_DOMAIN_HIERARCHY for pmic-arb in Kconfig since this driver uses the version 2 IRQ interfaces. IRQ_DOMAIN_HIERARCHY selects IRQ_DOMAIN, so it can be removed from here. Signed-off-by: Brian Masney Reported-by: Randy Dunlap Acked-by: Randy Dunlap # build-tested Signed-off-by: Linus Walleij --- drivers/spmi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 0d3b70b3bda8..d48ed7c2c6c4 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -12,7 +12,7 @@ if SPMI config SPMI_MSM_PMIC_ARB tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" - select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM default ARCH_QCOM From 4fa882c9f628b312d697cfcefaa6e973ce8ece3e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 7 Aug 2018 12:07:43 +0200 Subject: [PATCH 1764/4277] eeprom: at24: remove at24_platform_data There are no more users of at24_platform_data. Remove the relevant header and modify the driver code to not use it anymore. Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 - drivers/misc/eeprom/at24.c | 162 +++++++++++++---------------- include/linux/platform_data/at24.h | 60 ----------- 3 files changed, 75 insertions(+), 148 deletions(-) delete mode 100644 include/linux/platform_data/at24.h diff --git a/MAINTAINERS b/MAINTAINERS index 9919840d54cd..d901919dd475 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2503,7 +2503,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git S: Maintained F: Documentation/devicetree/bindings/eeprom/at24.txt F: drivers/misc/eeprom/at24.c -F: include/linux/platform_data/at24.h ATA OVER ETHERNET (AOE) DRIVER M: "Ed L. Cashin" diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index ddfcf4ade7bf..b806a403ca46 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -22,10 +22,24 @@ #include #include #include -#include #include #include +/* Address pointer is 16 bit. */ +#define AT24_FLAG_ADDR16 BIT(7) +/* sysfs-entry will be read-only. */ +#define AT24_FLAG_READONLY BIT(6) +/* sysfs-entry will be world-readable. */ +#define AT24_FLAG_IRUGO BIT(5) +/* Take always 8 addresses (24c00). */ +#define AT24_FLAG_TAKE8ADDR BIT(4) +/* Factory-programmed serial number. */ +#define AT24_FLAG_SERIAL BIT(3) +/* Factory-programmed mac address. */ +#define AT24_FLAG_MAC BIT(2) +/* Does not auto-rollover reads to the next slave address. */ +#define AT24_FLAG_NO_RDROL BIT(1) + /* * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. * Differences between different vendor product lines (like Atmel AT24C or @@ -107,10 +121,6 @@ module_param_named(write_timeout, at24_write_timeout, uint, 0); MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); struct at24_chip_data { - /* - * these fields mirror their equivalents in - * struct at24_platform_data - */ u32 byte_len; u8 flags; }; @@ -471,63 +481,11 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) return 0; } -static void at24_properties_to_pdata(struct device *dev, - struct at24_platform_data *chip) -{ - int err; - u32 val; - - if (device_property_present(dev, "read-only")) - chip->flags |= AT24_FLAG_READONLY; - if (device_property_present(dev, "no-read-rollover")) - chip->flags |= AT24_FLAG_NO_RDROL; - - err = device_property_read_u32(dev, "address-width", &val); - if (!err) { - switch (val) { - case 8: - if (chip->flags & AT24_FLAG_ADDR16) - dev_warn(dev, "Override address width to be 8, while default is 16\n"); - chip->flags &= ~AT24_FLAG_ADDR16; - break; - case 16: - chip->flags |= AT24_FLAG_ADDR16; - break; - default: - dev_warn(dev, "Bad \"address-width\" property: %u\n", - val); - } - } - - err = device_property_read_u32(dev, "size", &val); - if (!err) - chip->byte_len = val; - - err = device_property_read_u32(dev, "pagesize", &val); - if (!err) { - chip->page_size = val; - } else { - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip->page_size = 1; - } -} - -static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) +static const struct at24_chip_data *at24_get_chip_data(struct device *dev) { struct device_node *of_node = dev->of_node; const struct at24_chip_data *cdata; const struct i2c_device_id *id; - struct at24_platform_data *pd; - - pd = dev_get_platdata(dev); - if (pd) { - memcpy(pdata, pd, sizeof(*pdata)); - return 0; - } id = i2c_match_id(at24_ids, to_i2c_client(dev)); @@ -544,13 +502,9 @@ static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) cdata = acpi_device_get_match_data(dev); if (!cdata) - return -ENODEV; + return ERR_PTR(-ENODEV); - pdata->byte_len = cdata->byte_len; - pdata->flags = cdata->flags; - at24_properties_to_pdata(dev, pdata); - - return 0; + return cdata; } static void at24_remove_dummy_clients(struct at24_data *at24) @@ -619,7 +573,8 @@ static int at24_probe(struct i2c_client *client) { struct regmap_config regmap_config = { }; struct nvmem_config nvmem_config = { }; - struct at24_platform_data pdata = { }; + u32 byte_len, page_size, flags, addrw; + const struct at24_chip_data *cdata; struct device *dev = &client->dev; bool i2c_fn_i2c, i2c_fn_block; unsigned int i, num_addresses; @@ -634,35 +589,72 @@ static int at24_probe(struct i2c_client *client) i2c_fn_block = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); - err = at24_get_pdata(dev, &pdata); + cdata = at24_get_chip_data(dev); + if (IS_ERR(cdata)) + return PTR_ERR(cdata); + + err = device_property_read_u32(dev, "pagesize", &page_size); if (err) - return err; + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + page_size = 1; + + flags = cdata->flags; + if (device_property_present(dev, "read-only")) + flags |= AT24_FLAG_READONLY; + if (device_property_present(dev, "no-read-rollover")) + flags |= AT24_FLAG_NO_RDROL; + + err = device_property_read_u32(dev, "address-width", &addrw); + if (!err) { + switch (addrw) { + case 8: + if (flags & AT24_FLAG_ADDR16) + dev_warn(dev, + "Override address width to be 8, while default is 16\n"); + flags &= ~AT24_FLAG_ADDR16; + break; + case 16: + flags |= AT24_FLAG_ADDR16; + break; + default: + dev_warn(dev, "Bad \"address-width\" property: %u\n", + addrw); + } + } + + err = device_property_read_u32(dev, "size", &byte_len); + if (err) + byte_len = cdata->byte_len; if (!i2c_fn_i2c && !i2c_fn_block) - pdata.page_size = 1; + page_size = 1; - if (!pdata.page_size) { + if (!page_size) { dev_err(dev, "page_size must not be 0!\n"); return -EINVAL; } - if (!is_power_of_2(pdata.page_size)) + if (!is_power_of_2(page_size)) dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); - if (pdata.flags & AT24_FLAG_TAKE8ADDR) + if (flags & AT24_FLAG_TAKE8ADDR) num_addresses = 8; else - num_addresses = DIV_ROUND_UP(pdata.byte_len, - (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); - if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) { + if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { dev_err(dev, "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); return -EINVAL; } regmap_config.val_bits = 8; - regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8; regmap_config.disable_locking = true; regmap = devm_regmap_init_i2c(client, ®map_config); @@ -675,11 +667,11 @@ static int at24_probe(struct i2c_client *client) return -ENOMEM; mutex_init(&at24->lock); - at24->byte_len = pdata.byte_len; - at24->page_size = pdata.page_size; - at24->flags = pdata.flags; + at24->byte_len = byte_len; + at24->page_size = page_size; + at24->flags = flags; at24->num_addresses = num_addresses; - at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len); + at24->offset_adj = at24_get_offset_adj(flags, byte_len); at24->client[0].client = client; at24->client[0].regmap = regmap; @@ -687,10 +679,10 @@ static int at24_probe(struct i2c_client *client) if (IS_ERR(at24->wp_gpio)) return PTR_ERR(at24->wp_gpio); - writable = !(pdata.flags & AT24_FLAG_READONLY); + writable = !(flags & AT24_FLAG_READONLY); if (writable) { at24->write_max = min_t(unsigned int, - pdata.page_size, at24_io_limit); + page_size, at24_io_limit); if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) at24->write_max = I2C_SMBUS_BLOCK_MAX; } @@ -733,7 +725,7 @@ static int at24_probe(struct i2c_client *client) nvmem_config.priv = at24; nvmem_config.stride = 1; nvmem_config.word_size = 1; - nvmem_config.size = pdata.byte_len; + nvmem_config.size = byte_len; at24->nvmem = devm_nvmem_register(dev, &nvmem_config); if (IS_ERR(at24->nvmem)) { @@ -742,13 +734,9 @@ static int at24_probe(struct i2c_client *client) } dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", - pdata.byte_len, client->name, + byte_len, client->name, writable ? "writable" : "read-only", at24->write_max); - /* export data to kernel code */ - if (pdata.setup) - pdata.setup(at24->nvmem, pdata.context); - return 0; err_clients: diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h deleted file mode 100644 index 63507ff464ee..000000000000 --- a/include/linux/platform_data/at24.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * at24.h - platform_data for the at24 (generic eeprom) driver - * (C) Copyright 2008 by Pengutronix - * (C) Copyright 2012 by Wolfram Sang - * same license as the driver - */ - -#ifndef _LINUX_AT24_H -#define _LINUX_AT24_H - -#include -#include -#include - -/** - * struct at24_platform_data - data to set up at24 (generic eeprom) driver - * @byte_len: size of eeprom in byte - * @page_size: number of byte which can be written in one go - * @flags: tunable options, check AT24_FLAG_* defines - * @setup: an optional callback invoked after eeprom is probed; enables kernel - code to access eeprom via nvmem, see example - * @context: optional parameter passed to setup() - * - * If you set up a custom eeprom type, please double-check the parameters. - * Especially page_size needs extra care, as you risk data loss if your value - * is bigger than what the chip actually supports! - * - * An example in pseudo code for a setup() callback: - * - * void get_mac_addr(struct nvmem_device *nvmem, void *context) - * { - * u8 *mac_addr = ethernet_pdata->mac_addr; - * off_t offset = context; - * - * // Read MAC addr from EEPROM - * if (nvmem_device_read(nvmem, offset, ETH_ALEN, mac_addr) == ETH_ALEN) - * pr_info("Read MAC addr from EEPROM: %pM\n", mac_addr); - * } - * - * This function pointer and context can now be set up in at24_platform_data. - */ - -struct at24_platform_data { - u32 byte_len; /* size (sum of all addr) */ - u16 page_size; /* for writes */ - u8 flags; -#define AT24_FLAG_ADDR16 BIT(7) /* address pointer is 16 bit */ -#define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */ -#define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */ -#define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ -#define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */ -#define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */ -#define AT24_FLAG_NO_RDROL BIT(1) /* does not auto-rollover reads to */ - /* the next slave address */ - - void (*setup)(struct nvmem_device *nvmem, void *context); - void *context; -}; - -#endif /* _LINUX_AT24_H */ From e7224a116f0dac593233143849f25bc095f8d2cc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 5 Feb 2019 14:03:44 +0100 Subject: [PATCH 1765/4277] dt-bindings: at24: add the 'num-addresses' property Currently the at24 driver only creates additional i2c dummies for atmel,24c00 and it's hard-coded. Some other chips (like for example Microchip's 24AA02T) also take more slave addresses despite being otherwise compatible with already supported variants. Add a new property to the device tree binding document that defines the total number of i2c slave addresses taken by the device. The addresses are counted starting from the one in the reg property. Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/eeprom/at24.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/eeprom/at24.txt b/Documentation/devicetree/bindings/eeprom/at24.txt index f9a7c984274c..0e456bbc1213 100644 --- a/Documentation/devicetree/bindings/eeprom/at24.txt +++ b/Documentation/devicetree/bindings/eeprom/at24.txt @@ -75,6 +75,8 @@ Optional properties: - address-width: number of address bits (one of 8, 16). + - num-addresses: total number of i2c slave addresses this device takes + Example: eeprom@52 { @@ -82,4 +84,5 @@ eeprom@52 { reg = <0x52>; pagesize = <32>; wp-gpios = <&gpio1 3 0>; + num-addresses = <8>; }; From 950bcbbe31548636d24e49b3abaf3780204f7fe8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 5 Feb 2019 14:18:25 +0100 Subject: [PATCH 1766/4277] eeprom: at24: implement support for 'num-addresses' property If the device node defines 'num-addresses', let it override the default behavior. Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index b806a403ca46..63aa541c9608 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -641,11 +641,14 @@ static int at24_probe(struct i2c_client *client) if (!is_power_of_2(page_size)) dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); - if (flags & AT24_FLAG_TAKE8ADDR) - num_addresses = 8; - else - num_addresses = DIV_ROUND_UP(byte_len, - (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + err = device_property_read_u32(dev, "num-addresses", &num_addresses); + if (err) { + if (flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(byte_len, + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); + } if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { dev_err(dev, From de744e01aa3af421470eb9725574e3cbce319053 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Wed, 13 Feb 2019 20:36:40 -0500 Subject: [PATCH 1767/4277] mfd: pm8xxx: select IRQ_DOMAIN_HIERARCHY in Kconfig Select IRQ_DOMAIN_HIERARCHY for pm8xxx in Kconfig since this driver uses the version 2 IRQ interfaces. IRQ_DOMAIN_HIERARCHY selects IRQ_DOMAIN, so it can be removed from here. Signed-off-by: Brian Masney Acked-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8c5dfdce4326..001a84749852 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -927,7 +927,7 @@ config UCB1400_CORE config MFD_PM8XXX tristate "Qualcomm PM8xxx PMIC chips driver" depends on (ARM || HEXAGON || COMPILE_TEST) - select IRQ_DOMAIN + select IRQ_DOMAIN_HIERARCHY select MFD_CORE select REGMAP help From 79890c2ec4860c3b715f89248c51abcc76a1fa39 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Wed, 13 Feb 2019 20:36:41 -0500 Subject: [PATCH 1768/4277] qcom: ssbi-gpio: correct boundary conditions in pm8xxx_domain_translate SSBI GPIOs are numbered 1..ngpio, so the boundary check in pm8xxx_domain_translate() is off by one. This patch corrects that check. Signed-off-by: Brian Masney Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index 84a232450000..08dd62b5cebe 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -710,7 +710,8 @@ static int pm8xxx_domain_translate(struct irq_domain *domain, struct pm8xxx_gpio *pctrl = container_of(domain->host_data, struct pm8xxx_gpio, chip); - if (fwspec->param_count != 2 || fwspec->param[0] >= pctrl->chip.ngpio) + if (fwspec->param_count != 2 || fwspec->param[0] < 1 || + fwspec->param[0] > pctrl->chip.ngpio) return -EINVAL; *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; From 1ff85bfa1614df6cacec780cd5922dc0b1c340c7 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 28 Dec 2018 07:33:35 +0000 Subject: [PATCH 1769/4277] HSI: omap_ssi_port: fix debugfs_simple_attr.cocci warnings Use DEFINE_DEBUGFS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTRIBUTE for debugfs files. Semantic patch information: Rationale: DEFINE_SIMPLE_ATTRIBUTE + debugfs_create_file() imposes some significant overhead as compared to DEFINE_DEBUGFS_ATTRIBUTE + debugfs_create_file_unsafe(). Generated by: scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci Signed-off-by: YueHaibing Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi_port.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index b2b3989ccfd2..afbf1345709d 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -162,7 +162,7 @@ static int ssi_div_set(void *data, u64 val) return 0; } -DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n"); static int ssi_debug_add_port(struct omap_ssi_port *omap_port, struct dentry *dir) @@ -177,8 +177,8 @@ static int ssi_debug_add_port(struct omap_ssi_port *omap_port, dir = debugfs_create_dir("sst", dir); if (!dir) return -ENOMEM; - debugfs_create_file("divisor", S_IRUGO | S_IWUSR, dir, port, - &ssi_sst_div_fops); + debugfs_create_file_unsafe("divisor", 0644, dir, port, + &ssi_sst_div_fops); return 0; } From a0584ee9aed805446b044ce855e67264f0dc619e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:24:58 -0500 Subject: [PATCH 1770/4277] SUNRPC: Use struct xdr_stream when decoding RPC Reply header Modernize and harden the code path that parses an RPC Reply message. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/auth.h | 15 ++- include/linux/sunrpc/xdr.h | 1 + net/sunrpc/auth.c | 63 ++++++---- net/sunrpc/auth_gss/auth_gss.c | 204 ++++++++++++++++++--------------- net/sunrpc/auth_null.c | 29 ++--- net/sunrpc/auth_unix.c | 40 ++++--- net/sunrpc/clnt.c | 88 +++++++------- 7 files changed, 241 insertions(+), 199 deletions(-) diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 96e237f8e60b..c51e1893f77e 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -134,11 +134,12 @@ struct rpc_credops { int (*crmarshal)(struct rpc_task *task, struct xdr_stream *xdr); int (*crrefresh)(struct rpc_task *); - __be32 * (*crvalidate)(struct rpc_task *, __be32 *); + int (*crvalidate)(struct rpc_task *task, + struct xdr_stream *xdr); int (*crwrap_req)(struct rpc_task *task, struct xdr_stream *xdr); - int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t, - void *, __be32 *, void *); + int (*crunwrap_resp)(struct rpc_task *task, + struct xdr_stream *xdr); int (*crkey_timeout)(struct rpc_cred *); char * (*crstringify_acceptor)(struct rpc_cred *); bool (*crneed_reencode)(struct rpc_task *); @@ -168,12 +169,16 @@ struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); void put_rpccred(struct rpc_cred *); int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr); -__be32 * rpcauth_checkverf(struct rpc_task *, __be32 *); +int rpcauth_checkverf(struct rpc_task *task, + struct xdr_stream *xdr); int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr); int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr); -int rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, __be32 *data, void *obj); +int rpcauth_unwrap_resp_decode(struct rpc_task *task, + struct xdr_stream *xdr); +int rpcauth_unwrap_resp(struct rpc_task *task, + struct xdr_stream *xdr); bool rpcauth_xmit_need_reencode(struct rpc_task *task); int rpcauth_refreshcred(struct rpc_task *); void rpcauth_invalcred(struct rpc_task *); diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index c54041950cc0..65af6a204b75 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -89,6 +89,7 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) #define rpc_auth_null cpu_to_be32(RPC_AUTH_NULL) #define rpc_auth_unix cpu_to_be32(RPC_AUTH_UNIX) +#define rpc_auth_short cpu_to_be32(RPC_AUTH_SHORT) #define rpc_auth_gss cpu_to_be32(RPC_AUTH_GSS) #define rpc_call cpu_to_be32(RPC_CALL) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index add2135d9b01..e7861026b9e5 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -17,6 +17,8 @@ #include #include +#include + #define RPC_CREDCACHE_DEFAULT_HASHBITS (4) struct rpc_cred_cache { struct hlist_head *hashtable; @@ -773,14 +775,6 @@ int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr) return ops->crmarshal(task, xdr); } -__be32 * -rpcauth_checkverf(struct rpc_task *task, __be32 *p) -{ - struct rpc_cred *cred = task->tk_rqstp->rq_cred; - - return cred->cr_ops->crvalidate(task, p); -} - /** * rpcauth_wrap_req_encode - XDR encode the RPC procedure * @task: controlling RPC task @@ -814,27 +808,52 @@ int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) return ops->crwrap_req(task, xdr); } -static int -rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, - __be32 *data, void *obj) +/** + * rpcauth_checkverf - Validate verifier in RPC Reply header + * @task: controlling RPC task + * @xdr: xdr_stream containing RPC Reply header + * + * On success, @xdr is updated to point past the verifier and + * zero is returned. Otherwise, @xdr is in an undefined state + * and a negative errno is returned. + */ +int +rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr) { - struct xdr_stream xdr; + const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; - xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data, rqstp); - return decode(rqstp, &xdr, obj); + return ops->crvalidate(task, xdr); } +/** + * rpcauth_unwrap_resp_decode - Invoke XDR decode function + * @task: controlling RPC task + * @xdr: stream where the Reply message resides + * + * Returns zero on success; otherwise a negative errno is returned. + */ int -rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, - __be32 *data, void *obj) +rpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr) { - struct rpc_cred *cred = task->tk_rqstp->rq_cred; + kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode; - if (cred->cr_ops->crunwrap_resp) - return cred->cr_ops->crunwrap_resp(task, decode, rqstp, - data, obj); - /* By default, we decode the arguments normally. */ - return rpcauth_unwrap_req_decode(decode, rqstp, data, obj); + return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp); +} +EXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode); + +/** + * rpcauth_unwrap_resp - Invoke unwrap and decode function for the cred + * @task: controlling RPC task + * @xdr: stream where the Reply message resides + * + * Returns zero on success; otherwise a negative errno is returned. + */ +int +rpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) +{ + const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; + + return ops->crunwrap_resp(task, xdr); } bool diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index b333b1bdad45..206788e8b787 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1671,59 +1671,62 @@ gss_refresh_null(struct rpc_task *task) return 0; } -static __be32 * -gss_validate(struct rpc_task *task, __be32 *p) +static int +gss_validate(struct rpc_task *task, struct xdr_stream *xdr) { struct rpc_cred *cred = task->tk_rqstp->rq_cred; struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); - __be32 *seq = NULL; + __be32 *p, *seq = NULL; struct kvec iov; struct xdr_buf verf_buf; struct xdr_netobj mic; - u32 flav,len; - u32 maj_stat; - __be32 *ret = ERR_PTR(-EIO); + u32 len, maj_stat; + int status; - dprintk("RPC: %5u %s\n", task->tk_pid, __func__); + p = xdr_inline_decode(xdr, 2 * sizeof(*p)); + if (!p) + goto validate_failed; + if (*p++ != rpc_auth_gss) + goto validate_failed; + len = be32_to_cpup(p); + if (len > RPC_MAX_AUTH_SIZE) + goto validate_failed; + p = xdr_inline_decode(xdr, len); + if (!p) + goto validate_failed; - flav = ntohl(*p++); - if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) - goto out_bad; - if (flav != RPC_AUTH_GSS) - goto out_bad; seq = kmalloc(4, GFP_NOFS); if (!seq) - goto out_bad; - *seq = htonl(task->tk_rqstp->rq_seqno); + goto validate_failed; + *seq = cpu_to_be32(task->tk_rqstp->rq_seqno); iov.iov_base = seq; iov.iov_len = 4; xdr_buf_from_iov(&iov, &verf_buf); mic.data = (u8 *)p; mic.len = len; - - ret = ERR_PTR(-EACCES); maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); - if (maj_stat) { - dprintk("RPC: %5u %s: gss_verify_mic returned error 0x%08x\n", - task->tk_pid, __func__, maj_stat); - goto out_bad; - } + if (maj_stat) + goto bad_mic; + /* We leave it to unwrap to calculate au_rslack. For now we just * calculate the length of the verifier: */ cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; + status = 0; +out: gss_put_ctx(ctx); - dprintk("RPC: %5u %s: gss_verify_mic succeeded.\n", - task->tk_pid, __func__); kfree(seq); - return p + XDR_QUADLEN(len); -out_bad: - gss_put_ctx(ctx); - dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__, - PTR_ERR(ret)); - kfree(seq); - return ret; + return status; + +validate_failed: + status = -EIO; + goto out; +bad_mic: + dprintk("RPC: %5u %s: gss_verify_mic returned error 0x%08x\n", + task->tk_pid, __func__, maj_stat); + status = -EACCES; + goto out; } static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, @@ -1921,79 +1924,98 @@ out: return status; } -static inline int -gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, - struct rpc_rqst *rqstp, __be32 **p) +static int +gss_unwrap_resp_auth(struct rpc_cred *cred) { - struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; - struct xdr_buf integ_buf; - struct xdr_netobj mic; - u32 data_offset, mic_offset; - u32 integ_len; - u32 maj_stat; - int status = -EIO; + cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize; + return 0; +} - integ_len = ntohl(*(*p)++); +static int +gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, + struct rpc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf; + u32 data_offset, mic_offset, integ_len, maj_stat; + struct xdr_netobj mic; + __be32 *p; + + p = xdr_inline_decode(xdr, 2 * sizeof(*p)); + if (unlikely(!p)) + goto unwrap_failed; + integ_len = be32_to_cpup(p++); if (integ_len & 3) - return status; - data_offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base; + goto unwrap_failed; + data_offset = (u8 *)(p) - (u8 *)rcv_buf->head[0].iov_base; mic_offset = integ_len + data_offset; if (mic_offset > rcv_buf->len) - return status; - if (ntohl(*(*p)++) != rqstp->rq_seqno) - return status; - - if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, - mic_offset - data_offset)) - return status; + goto unwrap_failed; + if (be32_to_cpup(p) != rqstp->rq_seqno) + goto unwrap_failed; + if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, integ_len)) + goto unwrap_failed; if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) - return status; - + goto unwrap_failed; maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); if (maj_stat != GSS_S_COMPLETE) - return status; + goto bad_mic; + + cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 + + 1 + XDR_QUADLEN(mic.len); return 0; +unwrap_failed: + return -EIO; +bad_mic: + dprintk("RPC: %s: gss_verify_mic returned error 0x%08x\n", + __func__, maj_stat); + return -EIO; } -static inline int +static int gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, - struct rpc_rqst *rqstp, __be32 **p) + struct rpc_rqst *rqstp, struct xdr_stream *xdr) { - struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; - u32 offset; - u32 opaque_len; - u32 maj_stat; - int status = -EIO; + struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; + struct kvec *head = rqstp->rq_rcv_buf.head; + unsigned int savedlen = rcv_buf->len; + u32 offset, opaque_len, maj_stat; + __be32 *p; - opaque_len = ntohl(*(*p)++); - offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base; + p = xdr_inline_decode(xdr, 2 * sizeof(*p)); + if (unlikely(!p)) + goto unwrap_failed; + opaque_len = be32_to_cpup(p++); + offset = (u8 *)(p) - (u8 *)head->iov_base; if (offset + opaque_len > rcv_buf->len) - return status; - /* remove padding: */ + goto unwrap_failed; rcv_buf->len = offset + opaque_len; maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf); if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); if (maj_stat != GSS_S_COMPLETE) - return status; - if (ntohl(*(*p)++) != rqstp->rq_seqno) - return status; + goto bad_unwrap; + /* gss_unwrap decrypted the sequence number */ + if (be32_to_cpup(p++) != rqstp->rq_seqno) + goto unwrap_failed; + /* gss_unwrap redacts the opaque blob from the head iovec. + * rcv_buf has changed, thus the stream needs to be reset. + */ + xdr_init_decode(xdr, rcv_buf, p, rqstp); + + cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 + + XDR_QUADLEN(savedlen - rcv_buf->len); return 0; -} - -static int -gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, - __be32 *p, void *obj) -{ - struct xdr_stream xdr; - - xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p, rqstp); - return decode(rqstp, &xdr, obj); +unwrap_failed: + return -EIO; +bad_unwrap: + dprintk("RPC: %s: gss_unwrap returned error 0x%08x\n", + __func__, maj_stat); + return -EIO; } static bool @@ -2037,39 +2059,33 @@ out: } static int -gss_unwrap_resp(struct rpc_task *task, - kxdrdproc_t decode, void *rqstp, __be32 *p, void *obj) +gss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) { - struct rpc_cred *cred = task->tk_rqstp->rq_cred; + struct rpc_rqst *rqstp = task->tk_rqstp; + struct rpc_cred *cred = rqstp->rq_cred; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); - __be32 *savedp = p; - struct kvec *head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head; - int savedlen = head->iov_len; - int status = -EIO; + int status = -EIO; if (ctx->gc_proc != RPC_GSS_PROC_DATA) goto out_decode; switch (gss_cred->gc_service) { case RPC_GSS_SVC_NONE: + status = gss_unwrap_resp_auth(cred); break; case RPC_GSS_SVC_INTEGRITY: - status = gss_unwrap_resp_integ(cred, ctx, rqstp, &p); - if (status) - goto out; + status = gss_unwrap_resp_integ(cred, ctx, rqstp, xdr); break; case RPC_GSS_SVC_PRIVACY: - status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p); - if (status) - goto out; + status = gss_unwrap_resp_priv(cred, ctx, rqstp, xdr); break; } - /* take into account extra slack for integrity and privacy cases: */ - cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp) - + (savedlen - head->iov_len); + if (status) + goto out; + out_decode: - status = gss_unwrap_req_decode(decode, rqstp, p, obj); + status = rpcauth_unwrap_resp_decode(task, xdr); out: gss_put_ctx(ctx); dprintk("RPC: %5u %s returning %d\n", diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 797f8472c21b..bf96975ffc4b 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -86,25 +86,19 @@ nul_refresh(struct rpc_task *task) return 0; } -static __be32 * -nul_validate(struct rpc_task *task, __be32 *p) +static int +nul_validate(struct rpc_task *task, struct xdr_stream *xdr) { - rpc_authflavor_t flavor; - u32 size; + __be32 *p; - flavor = ntohl(*p++); - if (flavor != RPC_AUTH_NULL) { - printk("RPC: bad verf flavor: %u\n", flavor); - return ERR_PTR(-EIO); - } - - size = ntohl(*p++); - if (size != 0) { - printk("RPC: bad verf size: %u\n", size); - return ERR_PTR(-EIO); - } - - return p; + p = xdr_inline_decode(xdr, 2 * sizeof(*p)); + if (!p) + return -EIO; + if (*p++ != rpc_auth_null) + return -EIO; + if (*p != xdr_zero) + return -EIO; + return 0; } const struct rpc_authops authnull_ops = { @@ -134,6 +128,7 @@ const struct rpc_credops null_credops = { .crwrap_req = rpcauth_wrap_req_encode, .crrefresh = nul_refresh, .crvalidate = nul_validate, + .crunwrap_resp = rpcauth_unwrap_resp_decode, }; static diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 1d5b7ed9c6f7..5ea84a96f96e 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -160,29 +160,32 @@ unx_refresh(struct rpc_task *task) return 0; } -static __be32 * -unx_validate(struct rpc_task *task, __be32 *p) +static int +unx_validate(struct rpc_task *task, struct xdr_stream *xdr) { - rpc_authflavor_t flavor; - u32 size; + __be32 *p; + u32 size; - flavor = ntohl(*p++); - if (flavor != RPC_AUTH_NULL && - flavor != RPC_AUTH_UNIX && - flavor != RPC_AUTH_SHORT) { - printk("RPC: bad verf flavor: %u\n", flavor); - return ERR_PTR(-EIO); + p = xdr_inline_decode(xdr, 2 * sizeof(*p)); + if (!p) + return -EIO; + switch (*p++) { + case rpc_auth_null: + case rpc_auth_unix: + case rpc_auth_short: + break; + default: + return -EIO; } + size = be32_to_cpup(p); + if (size > RPC_MAX_AUTH_SIZE) + return -EIO; + p = xdr_inline_decode(xdr, size); + if (!p) + return -EIO; - size = ntohl(*p++); - if (size > RPC_MAX_AUTH_SIZE) { - printk("RPC: giant verf size: %u\n", size); - return ERR_PTR(-EIO); - } task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2; - p += (size >> 2); - - return p; + return 0; } int __init rpc_init_authunix(void) @@ -223,4 +226,5 @@ const struct rpc_credops unix_credops = { .crwrap_req = rpcauth_wrap_req_encode, .crrefresh = unx_refresh, .crvalidate = unx_validate, + .crunwrap_resp = rpcauth_unwrap_resp_decode, }; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index e9735089bd66..803e93105af1 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -79,7 +79,8 @@ static void call_connect_status(struct rpc_task *task); static int rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr); -static __be32 *rpc_decode_header(struct rpc_task *task); +static int rpc_decode_header(struct rpc_task *task, + struct xdr_stream *xdr); static int rpc_ping(struct rpc_clnt *clnt); static void rpc_register_client(struct rpc_clnt *clnt) @@ -2251,12 +2252,11 @@ call_decode(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; struct rpc_rqst *req = task->tk_rqstp; - kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode; - __be32 *p; + struct xdr_stream xdr; dprint_status(task); - if (!decode) { + if (!task->tk_msg.rpc_proc->p_decode) { task->tk_action = rpc_exit_task; return; } @@ -2292,29 +2292,27 @@ call_decode(struct rpc_task *task) goto out_retry; } - p = rpc_decode_header(task); - if (IS_ERR(p)) { - if (p == ERR_PTR(-EAGAIN)) - goto out_retry; + xdr_init_decode(&xdr, &req->rq_rcv_buf, + req->rq_rcv_buf.head[0].iov_base, req); + switch (rpc_decode_header(task, &xdr)) { + case 0: + task->tk_action = rpc_exit_task; + task->tk_status = rpcauth_unwrap_resp(task, &xdr); + dprintk("RPC: %5u %s result %d\n", + task->tk_pid, __func__, task->tk_status); return; - } - task->tk_action = rpc_exit_task; - - task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, - task->tk_msg.rpc_resp); - - dprintk("RPC: %5u call_decode result %d\n", task->tk_pid, - task->tk_status); - return; + case -EAGAIN: out_retry: - task->tk_status = 0; - /* Note: rpc_decode_header() may have freed the RPC slot */ - if (task->tk_rqstp == req) { - xdr_free_bvec(&req->rq_rcv_buf); - req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0; - if (task->tk_client->cl_discrtry) - xprt_conditional_disconnect(req->rq_xprt, - req->rq_connect_cookie); + task->tk_status = 0; + /* Note: rpc_decode_header() may have freed the RPC slot */ + if (task->tk_rqstp == req) { + xdr_free_bvec(&req->rq_rcv_buf); + req->rq_reply_bytes_recvd = 0; + req->rq_rcv_buf.len = 0; + if (task->tk_client->cl_discrtry) + xprt_conditional_disconnect(req->rq_xprt, + req->rq_connect_cookie); + } } } @@ -2347,14 +2345,12 @@ out_fail: return error; } -static noinline __be32 * -rpc_decode_header(struct rpc_task *task) +static noinline int +rpc_decode_header(struct rpc_task *task, struct xdr_stream *xdr) { struct rpc_clnt *clnt = task->tk_client; - struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0]; - int len = task->tk_rqstp->rq_rcv_buf.len >> 2; - __be32 *p = iov->iov_base; int error = -EACCES; + __be32 *p; /* RFC-1014 says that the representation of XDR data must be a * multiple of four bytes @@ -2363,25 +2359,26 @@ rpc_decode_header(struct rpc_task *task) */ if (task->tk_rqstp->rq_rcv_buf.len & 3) goto out_badlen; - if ((len -= 3) < 0) - goto out_unparsable; + p = xdr_inline_decode(xdr, 3 * sizeof(*p)); + if (!p) + goto out_unparsable; p++; /* skip XID */ if (*p++ != rpc_reply) goto out_unparsable; if (*p++ != rpc_msg_accepted) goto out_msg_denied; - p = rpcauth_checkverf(task, p); - if (IS_ERR(p)) + error = rpcauth_checkverf(task, xdr); + if (error) goto out_verifier; - len = p - (__be32 *)iov->iov_base - 1; - if (len < 0) + p = xdr_inline_decode(xdr, sizeof(*p)); + if (!p) goto out_unparsable; - switch (*p++) { + switch (*p) { case rpc_success: - return p; + return 0; case rpc_prog_unavail: trace_rpc__prog_unavail(task); error = -EPFNOSUPPORT; @@ -2406,11 +2403,11 @@ out_garbage: if (task->tk_garb_retry) { task->tk_garb_retry--; task->tk_action = call_encode; - return ERR_PTR(-EAGAIN); + return -EAGAIN; } out_err: rpc_exit(task, error); - return ERR_PTR(error); + return error; out_badlen: trace_rpc__unparsable(task); @@ -2424,10 +2421,12 @@ out_unparsable: out_verifier: trace_rpc_bad_verifier(task); - error = PTR_ERR(p); goto out_garbage; out_msg_denied: + p = xdr_inline_decode(xdr, sizeof(*p)); + if (!p) + goto out_unparsable; switch (*p++) { case rpc_auth_error: break; @@ -2441,6 +2440,9 @@ out_msg_denied: goto out_err; } + p = xdr_inline_decode(xdr, sizeof(*p)); + if (!p) + goto out_unparsable; switch (*p++) { case rpc_autherr_rejectedcred: case rpc_autherr_rejectedverf: @@ -2454,7 +2456,7 @@ out_msg_denied: /* Ensure we obtain a new XID! */ xprt_release(task); task->tk_action = call_reserve; - return ERR_PTR(-EAGAIN); + return -EAGAIN; case rpc_autherr_badcred: case rpc_autherr_badverf: /* possibly garbled cred/verf? */ @@ -2463,7 +2465,7 @@ out_msg_denied: task->tk_garb_retry--; trace_rpc__bad_creds(task); task->tk_action = call_encode; - return ERR_PTR(-EAGAIN); + return -EAGAIN; case rpc_autherr_tooweak: trace_rpc__auth_tooweak(task); pr_warn("RPC: server %s requires stronger authentication.\n", From 2d803dbe1c2190be303d3c0891be21210d48b92d Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Jan 2019 17:13:28 +0100 Subject: [PATCH 1771/4277] dt-bindings: gpio: pca953x: document the nxp,pcal6416 The NXP PCAL6416 is a variant of the PCA GPIO expander, with 16 GPIOs. Signed-off-by: Thomas Petazzoni Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca953x.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt index 4e3c550e319a..fb144e2b6522 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt @@ -16,6 +16,7 @@ Required properties: nxp,pca9574 nxp,pca9575 nxp,pca9698 + nxp,pcal6416 nxp,pcal6524 nxp,pcal9555a maxim,max7310 From aac1e3c9680b47e1bdadeea22bda3da3de6399b0 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 3 Jan 2019 17:13:29 +0100 Subject: [PATCH 1772/4277] gpio: pca953x: add support for pcal6416 type The NXP PCAL6416, documented at [1], is a variant of the PCA GPIO expander with 16 GPIOs, and supporting an interrupt and the "extended" features for interrupt, pull-up/pull-down configuration, etc. [1] https://www.nxp.com/docs/en/data-sheet/PCAL6416A.pdf Signed-off-by: Thomas Petazzoni Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 0dc96419efe3..e26ee4ba07f4 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -88,6 +88,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, + { "pcal6416", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, { "pcal6524", 24 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, }, From 0faf40e373dd8f6ef4fca367a0db1e8c16f5e01e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Jan 2019 11:28:44 +0000 Subject: [PATCH 1773/4277] gpio: madera: Add missing const madera_gpio_chip is only used as a template so it can be marked as const. Signed-off-by: Charles Keepax Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-madera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index 7ba68d1a0932..c9dad0543672 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -107,7 +107,7 @@ static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset, MADERA_GPIO1_CTRL_1 + reg_offset, ret); } -static struct gpio_chip madera_gpio_chip = { +static const struct gpio_chip madera_gpio_chip = { .label = "madera", .owner = THIS_MODULE, .request = gpiochip_generic_request, From f3463daab2bc185075d64d1153fc73b195f56cab Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 17 Jan 2019 11:13:21 +0100 Subject: [PATCH 1774/4277] Documentation: gpio: driver: fix wire name for I2C Typo: the data line is called "SDA" not "SCA". Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- Documentation/driver-api/gpio/driver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index a92d8837b62b..3043167fc557 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -135,7 +135,7 @@ This configuration is normally used as a way to achieve one of two things: - inverse wire-OR on an I/O line, for example a GPIO line, making it possible for any driving stage on the line to drive it low even if any other output to the same line is simultaneously driving it high. A special case of this - is driving the SCL and SCA lines of an I2C bus, which is by definition a + is driving the SCL and SDA lines of an I2C bus, which is by definition a wire-OR bus. Both usecases require that the line be equipped with a pull-up resistor. This From 6911845227578d57855cad2e728bfc53842ab5ee Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 23 Jan 2019 12:37:44 +0100 Subject: [PATCH 1775/4277] gpio: tegra186: Use TEGRA186_ prefix for GPIO names The new prefix allows the GPIOs to be uniquely identified on a per-chip basis, which makes it easier to distinguish Tegra186 specific GPIOs from those of later chips such as Tegra194 which supports a very different set of GPIOs. Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 66ec38bb7954..7d42e3d7572c 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -529,8 +529,8 @@ static int tegra186_gpio_remove(struct platform_device *pdev) return 0; } -#define TEGRA_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA_MAIN_GPIO_PORT_##port] = { \ +#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ + [TEGRA186_MAIN_GPIO_PORT_##port] = { \ .name = #port, \ .offset = base, \ .pins = count, \ @@ -538,29 +538,29 @@ static int tegra186_gpio_remove(struct platform_device *pdev) } static const struct tegra_gpio_port tegra186_main_ports[] = { - TEGRA_MAIN_GPIO_PORT( A, 0x2000, 7, 2), - TEGRA_MAIN_GPIO_PORT( B, 0x3000, 7, 3), - TEGRA_MAIN_GPIO_PORT( C, 0x3200, 7, 3), - TEGRA_MAIN_GPIO_PORT( D, 0x3400, 6, 3), - TEGRA_MAIN_GPIO_PORT( E, 0x2200, 8, 2), - TEGRA_MAIN_GPIO_PORT( F, 0x2400, 6, 2), - TEGRA_MAIN_GPIO_PORT( G, 0x4200, 6, 4), - TEGRA_MAIN_GPIO_PORT( H, 0x1000, 7, 1), - TEGRA_MAIN_GPIO_PORT( I, 0x0800, 8, 0), - TEGRA_MAIN_GPIO_PORT( J, 0x5000, 8, 5), - TEGRA_MAIN_GPIO_PORT( K, 0x5200, 1, 5), - TEGRA_MAIN_GPIO_PORT( L, 0x1200, 8, 1), - TEGRA_MAIN_GPIO_PORT( M, 0x5600, 6, 5), - TEGRA_MAIN_GPIO_PORT( N, 0x0000, 7, 0), - TEGRA_MAIN_GPIO_PORT( O, 0x0200, 4, 0), - TEGRA_MAIN_GPIO_PORT( P, 0x4000, 7, 4), - TEGRA_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), - TEGRA_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), - TEGRA_MAIN_GPIO_PORT( T, 0x0600, 4, 0), - TEGRA_MAIN_GPIO_PORT( X, 0x1400, 8, 1), - TEGRA_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), - TEGRA_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), - TEGRA_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), + TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2), + TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3), + TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3), + TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3), + TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2), + TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2), + TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4), + TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1), + TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0), + TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5), + TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5), + TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1), + TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5), + TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0), + TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0), + TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4), + TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), + TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), + TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0), + TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1), + TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), + TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), + TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), }; static const struct tegra_gpio_soc tegra186_main_soc = { @@ -569,8 +569,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .name = "tegra186-gpio", }; -#define TEGRA_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA_AON_GPIO_PORT_##port] = { \ +#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ + [TEGRA186_AON_GPIO_PORT_##port] = { \ .name = #port, \ .offset = base, \ .pins = count, \ @@ -578,14 +578,14 @@ static const struct tegra_gpio_soc tegra186_main_soc = { } static const struct tegra_gpio_port tegra186_aon_ports[] = { - TEGRA_AON_GPIO_PORT( S, 0x0200, 5, 0), - TEGRA_AON_GPIO_PORT( U, 0x0400, 6, 0), - TEGRA_AON_GPIO_PORT( V, 0x0800, 8, 0), - TEGRA_AON_GPIO_PORT( W, 0x0a00, 8, 0), - TEGRA_AON_GPIO_PORT( Z, 0x0e00, 4, 0), - TEGRA_AON_GPIO_PORT(AA, 0x0c00, 8, 0), - TEGRA_AON_GPIO_PORT(EE, 0x0600, 3, 0), - TEGRA_AON_GPIO_PORT(FF, 0x0000, 5, 0), + TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0), + TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0), + TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0), + TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0), + TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0), + TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0), + TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0), + TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0), }; static const struct tegra_gpio_soc tegra186_aon_soc = { From 68b7587baabfa42ca5625b709691358fee51b9d8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 23 Jan 2019 08:00:58 +0800 Subject: [PATCH 1776/4277] gpio: altera-a10sr: Trivial coding style fix Change the coding style to make it does error checking first. This also fixes checkpatch warning about line over 80 characters. Signed-off-by: Axel Lin Tested by: Thor Thayer Reviewed by: Thor Thayer Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-altera-a10sr.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index 7f9e0304b510..1cea4efccf7c 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -58,19 +58,20 @@ static void altr_a10sr_gpio_set(struct gpio_chip *chip, unsigned int offset, static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc, unsigned int nr) { - if (nr >= (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) - return 0; - return -EINVAL; + if (nr < (ALTR_A10SR_IN_VALID_RANGE_LO - ALTR_A10SR_LED_VALID_SHIFT)) + return -EINVAL; + + return 0; } static int altr_a10sr_gpio_direction_output(struct gpio_chip *gc, unsigned int nr, int value) { - if (nr <= (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) { - altr_a10sr_gpio_set(gc, nr, value); - return 0; - } - return -EINVAL; + if (nr > (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) + return -EINVAL; + + altr_a10sr_gpio_set(gc, nr, value); + return 0; } static const struct gpio_chip altr_a10sr_gc = { From 0c77668ddb4e7bdfbca462c6185d154d0b8889ae Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:04 -0500 Subject: [PATCH 1777/4277] SUNRPC: Introduce trace points in rpc_auth_gss.ko Add infrastructure for trace points in the RPC_AUTH_GSS kernel module, and add a few sample trace points. These report exceptional or unexpected events, and observe the assignment of GSS sequence numbers. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/trace/events/rpcgss.h | 361 +++++++++++++++++++++++++++++++++ include/trace/events/rpcrdma.h | 12 +- include/trace/events/sunrpc.h | 61 +++++- net/sunrpc/auth_gss/Makefile | 2 +- net/sunrpc/auth_gss/auth_gss.c | 165 ++++++++------- net/sunrpc/auth_gss/trace.c | 11 + net/sunrpc/xprt.c | 10 +- 7 files changed, 530 insertions(+), 92 deletions(-) create mode 100644 include/trace/events/rpcgss.h create mode 100644 net/sunrpc/auth_gss/trace.c diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h new file mode 100644 index 000000000000..d1f7fe1b6fe4 --- /dev/null +++ b/include/trace/events/rpcgss.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Oracle. All rights reserved. + * + * Trace point definitions for the "rpcgss" subsystem. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rpcgss + +#if !defined(_TRACE_RPCRDMA_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_RPCGSS_H + +#include + +/** + ** GSS-API related trace events + **/ + +TRACE_DEFINE_ENUM(GSS_S_BAD_MECH); +TRACE_DEFINE_ENUM(GSS_S_BAD_NAME); +TRACE_DEFINE_ENUM(GSS_S_BAD_NAMETYPE); +TRACE_DEFINE_ENUM(GSS_S_BAD_BINDINGS); +TRACE_DEFINE_ENUM(GSS_S_BAD_STATUS); +TRACE_DEFINE_ENUM(GSS_S_BAD_SIG); +TRACE_DEFINE_ENUM(GSS_S_NO_CRED); +TRACE_DEFINE_ENUM(GSS_S_NO_CONTEXT); +TRACE_DEFINE_ENUM(GSS_S_DEFECTIVE_TOKEN); +TRACE_DEFINE_ENUM(GSS_S_DEFECTIVE_CREDENTIAL); +TRACE_DEFINE_ENUM(GSS_S_CREDENTIALS_EXPIRED); +TRACE_DEFINE_ENUM(GSS_S_CONTEXT_EXPIRED); +TRACE_DEFINE_ENUM(GSS_S_FAILURE); +TRACE_DEFINE_ENUM(GSS_S_BAD_QOP); +TRACE_DEFINE_ENUM(GSS_S_UNAUTHORIZED); +TRACE_DEFINE_ENUM(GSS_S_UNAVAILABLE); +TRACE_DEFINE_ENUM(GSS_S_DUPLICATE_ELEMENT); +TRACE_DEFINE_ENUM(GSS_S_NAME_NOT_MN); +TRACE_DEFINE_ENUM(GSS_S_CONTINUE_NEEDED); +TRACE_DEFINE_ENUM(GSS_S_DUPLICATE_TOKEN); +TRACE_DEFINE_ENUM(GSS_S_OLD_TOKEN); +TRACE_DEFINE_ENUM(GSS_S_UNSEQ_TOKEN); +TRACE_DEFINE_ENUM(GSS_S_GAP_TOKEN); + +#define show_gss_status(x) \ + __print_flags(x, "|", \ + { GSS_S_BAD_MECH, "GSS_S_BAD_MECH" }, \ + { GSS_S_BAD_NAME, "GSS_S_BAD_NAME" }, \ + { GSS_S_BAD_NAMETYPE, "GSS_S_BAD_NAMETYPE" }, \ + { GSS_S_BAD_BINDINGS, "GSS_S_BAD_BINDINGS" }, \ + { GSS_S_BAD_STATUS, "GSS_S_BAD_STATUS" }, \ + { GSS_S_BAD_SIG, "GSS_S_BAD_SIG" }, \ + { GSS_S_NO_CRED, "GSS_S_NO_CRED" }, \ + { GSS_S_NO_CONTEXT, "GSS_S_NO_CONTEXT" }, \ + { GSS_S_DEFECTIVE_TOKEN, "GSS_S_DEFECTIVE_TOKEN" }, \ + { GSS_S_DEFECTIVE_CREDENTIAL, "GSS_S_DEFECTIVE_CREDENTIAL" }, \ + { GSS_S_CREDENTIALS_EXPIRED, "GSS_S_CREDENTIALS_EXPIRED" }, \ + { GSS_S_CONTEXT_EXPIRED, "GSS_S_CONTEXT_EXPIRED" }, \ + { GSS_S_FAILURE, "GSS_S_FAILURE" }, \ + { GSS_S_BAD_QOP, "GSS_S_BAD_QOP" }, \ + { GSS_S_UNAUTHORIZED, "GSS_S_UNAUTHORIZED" }, \ + { GSS_S_UNAVAILABLE, "GSS_S_UNAVAILABLE" }, \ + { GSS_S_DUPLICATE_ELEMENT, "GSS_S_DUPLICATE_ELEMENT" }, \ + { GSS_S_NAME_NOT_MN, "GSS_S_NAME_NOT_MN" }, \ + { GSS_S_CONTINUE_NEEDED, "GSS_S_CONTINUE_NEEDED" }, \ + { GSS_S_DUPLICATE_TOKEN, "GSS_S_DUPLICATE_TOKEN" }, \ + { GSS_S_OLD_TOKEN, "GSS_S_OLD_TOKEN" }, \ + { GSS_S_UNSEQ_TOKEN, "GSS_S_UNSEQ_TOKEN" }, \ + { GSS_S_GAP_TOKEN, "GSS_S_GAP_TOKEN" }) + + +DECLARE_EVENT_CLASS(rpcgss_gssapi_event, + TP_PROTO( + const struct rpc_task *task, + u32 maj_stat + ), + + TP_ARGS(task, maj_stat), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, maj_stat) + + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __entry->maj_stat = maj_stat; + ), + + TP_printk("task:%u@%u maj_stat=%s", + __entry->task_id, __entry->client_id, + __entry->maj_stat == 0 ? + "GSS_S_COMPLETE" : show_gss_status(__entry->maj_stat)) +); + +#define DEFINE_GSSAPI_EVENT(name) \ + DEFINE_EVENT(rpcgss_gssapi_event, rpcgss_##name, \ + TP_PROTO( \ + const struct rpc_task *task, \ + u32 maj_stat \ + ), \ + TP_ARGS(task, maj_stat)) + +TRACE_EVENT(rpcgss_import_ctx, + TP_PROTO( + int status + ), + + TP_ARGS(status), + + TP_STRUCT__entry( + __field(int, status) + ), + + TP_fast_assign( + __entry->status = status; + ), + + TP_printk("status=%d", __entry->status) +); + +DEFINE_GSSAPI_EVENT(get_mic); +DEFINE_GSSAPI_EVENT(verify_mic); +DEFINE_GSSAPI_EVENT(wrap); +DEFINE_GSSAPI_EVENT(unwrap); + + +/** + ** GSS auth unwrap failures + **/ + +TRACE_EVENT(rpcgss_unwrap_failed, + TP_PROTO( + const struct rpc_task *task + ), + + TP_ARGS(task), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + ), + + TP_printk("task:%u@%u", __entry->task_id, __entry->client_id) +); + +TRACE_EVENT(rpcgss_bad_seqno, + TP_PROTO( + const struct rpc_task *task, + u32 expected, + u32 received + ), + + TP_ARGS(task, expected, received), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, expected) + __field(u32, received) + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __entry->expected = expected; + __entry->received = received; + ), + + TP_printk("task:%u@%u expected seqno %u, received seqno %u", + __entry->task_id, __entry->client_id, + __entry->expected, __entry->received) +); + +TRACE_EVENT(rpcgss_seqno, + TP_PROTO( + const struct rpc_task *task + ), + + TP_ARGS(task), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, xid) + __field(u32, seqno) + ), + + TP_fast_assign( + const struct rpc_rqst *rqst = task->tk_rqstp; + + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __entry->xid = be32_to_cpu(rqst->rq_xid); + __entry->seqno = rqst->rq_seqno; + ), + + TP_printk("task:%u@%u xid=0x%08x seqno=%u", + __entry->task_id, __entry->client_id, + __entry->xid, __entry->seqno) +); + +TRACE_EVENT(rpcgss_need_reencode, + TP_PROTO( + const struct rpc_task *task, + u32 seq_xmit, + bool ret + ), + + TP_ARGS(task, seq_xmit, ret), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, xid) + __field(u32, seq_xmit) + __field(u32, seqno) + __field(bool, ret) + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __entry->xid = be32_to_cpu(task->tk_rqstp->rq_xid); + __entry->seq_xmit = seq_xmit; + __entry->seqno = task->tk_rqstp->rq_seqno; + __entry->ret = ret; + ), + + TP_printk("task:%u@%u xid=0x%08x rq_seqno=%u seq_xmit=%u reencode %sneeded", + __entry->task_id, __entry->client_id, + __entry->xid, __entry->seqno, __entry->seq_xmit, + __entry->ret ? "" : "un") +); + +/** + ** gssd upcall related trace events + **/ + +TRACE_EVENT(rpcgss_upcall_msg, + TP_PROTO( + const char *buf + ), + + TP_ARGS(buf), + + TP_STRUCT__entry( + __string(msg, buf) + ), + + TP_fast_assign( + __assign_str(msg, buf) + ), + + TP_printk("msg='%s'", __get_str(msg)) +); + +TRACE_EVENT(rpcgss_upcall_result, + TP_PROTO( + u32 uid, + int result + ), + + TP_ARGS(uid, result), + + TP_STRUCT__entry( + __field(u32, uid) + __field(int, result) + + ), + + TP_fast_assign( + __entry->uid = uid; + __entry->result = result; + ), + + TP_printk("for uid %u, result=%d", __entry->uid, __entry->result) +); + +TRACE_EVENT(rpcgss_context, + TP_PROTO( + unsigned long expiry, + unsigned long now, + unsigned int timeout, + unsigned int len, + const u8 *data + ), + + TP_ARGS(expiry, now, timeout, len, data), + + TP_STRUCT__entry( + __field(unsigned long, expiry) + __field(unsigned long, now) + __field(unsigned int, timeout) + __field(int, len) + __string(acceptor, data) + ), + + TP_fast_assign( + __entry->expiry = expiry; + __entry->now = now; + __entry->timeout = timeout; + __entry->len = len; + strncpy(__get_str(acceptor), data, len); + ), + + TP_printk("gc_expiry=%lu now=%lu timeout=%u acceptor=%.*s", + __entry->expiry, __entry->now, __entry->timeout, + __entry->len, __get_str(acceptor)) +); + + +/** + ** Miscellaneous events + */ + +TRACE_DEFINE_ENUM(RPC_AUTH_GSS_KRB5); +TRACE_DEFINE_ENUM(RPC_AUTH_GSS_KRB5I); +TRACE_DEFINE_ENUM(RPC_AUTH_GSS_KRB5P); + +#define show_pseudoflavor(x) \ + __print_symbolic(x, \ + { RPC_AUTH_GSS_KRB5, "RPC_AUTH_GSS_KRB5" }, \ + { RPC_AUTH_GSS_KRB5I, "RPC_AUTH_GSS_KRB5I" }, \ + { RPC_AUTH_GSS_KRB5P, "RPC_AUTH_GSS_KRB5P" }) + + +TRACE_EVENT(rpcgss_createauth, + TP_PROTO( + unsigned int flavor, + int error + ), + + TP_ARGS(flavor, error), + + TP_STRUCT__entry( + __field(unsigned int, flavor) + __field(int, error) + + ), + + TP_fast_assign( + __entry->flavor = flavor; + __entry->error = error; + ), + + TP_printk("flavor=%s error=%d", + show_pseudoflavor(__entry->flavor), __entry->error) +); + + +#endif /* _TRACE_RPCGSS_H */ + +#include diff --git a/include/trace/events/rpcrdma.h b/include/trace/events/rpcrdma.h index 399b1aedc927..962975b4313f 100644 --- a/include/trace/events/rpcrdma.h +++ b/include/trace/events/rpcrdma.h @@ -521,12 +521,18 @@ TRACE_EVENT(xprtrdma_post_send, TP_STRUCT__entry( __field(const void *, req) + __field(unsigned int, task_id) + __field(unsigned int, client_id) __field(int, num_sge) __field(int, signaled) __field(int, status) ), TP_fast_assign( + const struct rpc_rqst *rqst = &req->rl_slot; + + __entry->task_id = rqst->rq_task->tk_pid; + __entry->client_id = rqst->rq_task->tk_client->cl_clid; __entry->req = req; __entry->num_sge = req->rl_sendctx->sc_wr.num_sge; __entry->signaled = req->rl_sendctx->sc_wr.send_flags & @@ -534,9 +540,11 @@ TRACE_EVENT(xprtrdma_post_send, __entry->status = status; ), - TP_printk("req=%p, %d SGEs%s, status=%d", + TP_printk("task:%u@%u req=%p (%d SGE%s) %sstatus=%d", + __entry->task_id, __entry->client_id, __entry->req, __entry->num_sge, - (__entry->signaled ? ", signaled" : ""), + (__entry->num_sge == 1 ? "" : "s"), + (__entry->signaled ? "signaled " : ""), __entry->status ) ); diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 0654e9c50371..e58dda8e038c 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -655,9 +655,68 @@ DECLARE_EVENT_CLASS(rpc_xprt_event, DEFINE_RPC_XPRT_EVENT(timer); DEFINE_RPC_XPRT_EVENT(lookup_rqst); -DEFINE_RPC_XPRT_EVENT(transmit); DEFINE_RPC_XPRT_EVENT(complete_rqst); +TRACE_EVENT(xprt_transmit, + TP_PROTO( + const struct rpc_rqst *rqst, + int status + ), + + TP_ARGS(rqst, status), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, xid) + __field(u32, seqno) + __field(int, status) + ), + + TP_fast_assign( + __entry->task_id = rqst->rq_task->tk_pid; + __entry->client_id = rqst->rq_task->tk_client->cl_clid; + __entry->xid = be32_to_cpu(rqst->rq_xid); + __entry->seqno = rqst->rq_seqno; + __entry->status = status; + ), + + TP_printk( + "task:%u@%u xid=0x%08x seqno=%u status=%d", + __entry->task_id, __entry->client_id, __entry->xid, + __entry->seqno, __entry->status) +); + +TRACE_EVENT(xprt_enq_xmit, + TP_PROTO( + const struct rpc_task *task, + int stage + ), + + TP_ARGS(task, stage), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(u32, xid) + __field(u32, seqno) + __field(int, stage) + ), + + TP_fast_assign( + __entry->task_id = task->tk_pid; + __entry->client_id = task->tk_client->cl_clid; + __entry->xid = be32_to_cpu(task->tk_rqstp->rq_xid); + __entry->seqno = task->tk_rqstp->rq_seqno; + __entry->stage = stage; + ), + + TP_printk( + "task:%u@%u xid=0x%08x seqno=%u stage=%d", + __entry->task_id, __entry->client_id, __entry->xid, + __entry->seqno, __entry->stage) +); + TRACE_EVENT(xprt_ping, TP_PROTO(const struct rpc_xprt *xprt, int status), diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile index c374268b008f..4a29f4c5dac4 100644 --- a/net/sunrpc/auth_gss/Makefile +++ b/net/sunrpc/auth_gss/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o auth_rpcgss-y := auth_gss.o gss_generic_token.o \ gss_mech_switch.o svcauth_gss.o \ - gss_rpc_upcall.o gss_rpc_xdr.o + gss_rpc_upcall.o gss_rpc_xdr.o trace.o obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 206788e8b787..3d1fbd6d3370 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -55,6 +55,8 @@ #include "../netns.h" +#include + static const struct rpc_authops authgss_ops; static const struct rpc_credops gss_credops; @@ -260,6 +262,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct } ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS); if (ret < 0) { + trace_rpcgss_import_ctx(ret); p = ERR_PTR(ret); goto err; } @@ -275,12 +278,9 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct if (IS_ERR(p)) goto err; done: - dprintk("RPC: %s Success. gc_expiry %lu now %lu timeout %u acceptor %.*s\n", - __func__, ctx->gc_expiry, now, timeout, ctx->gc_acceptor.len, - ctx->gc_acceptor.data); - return p; + trace_rpcgss_context(ctx->gc_expiry, now, timeout, + ctx->gc_acceptor.len, ctx->gc_acceptor.data); err: - dprintk("RPC: %s returns error %ld\n", __func__, -PTR_ERR(p)); return p; } @@ -354,10 +354,8 @@ __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth if (auth && pos->auth->service != auth->service) continue; refcount_inc(&pos->count); - dprintk("RPC: %s found msg %p\n", __func__, pos); return pos; } - dprintk("RPC: %s found nothing\n", __func__); return NULL; } @@ -456,7 +454,7 @@ static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, size_t buflen = sizeof(gss_msg->databuf); int len; - len = scnprintf(p, buflen, "mech=%s uid=%d ", mech->gm_name, + len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name, from_kuid(&init_user_ns, gss_msg->uid)); buflen -= len; p += len; @@ -467,7 +465,7 @@ static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, * identity that we are authenticating to. */ if (target_name) { - len = scnprintf(p, buflen, "target=%s ", target_name); + len = scnprintf(p, buflen, " target=%s", target_name); buflen -= len; p += len; gss_msg->msg.len += len; @@ -487,11 +485,11 @@ static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, char *c = strchr(service_name, '@'); if (!c) - len = scnprintf(p, buflen, "service=%s ", + len = scnprintf(p, buflen, " service=%s", service_name); else len = scnprintf(p, buflen, - "service=%.*s srchost=%s ", + " service=%.*s srchost=%s", (int)(c - service_name), service_name, c + 1); buflen -= len; @@ -500,17 +498,17 @@ static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, } if (mech->gm_upcall_enctypes) { - len = scnprintf(p, buflen, "enctypes=%s ", + len = scnprintf(p, buflen, " enctypes=%s", mech->gm_upcall_enctypes); buflen -= len; p += len; gss_msg->msg.len += len; } + trace_rpcgss_upcall_msg(gss_msg->databuf); len = scnprintf(p, buflen, "\n"); if (len == 0) goto out_overflow; gss_msg->msg.len += len; - gss_msg->msg.data = gss_msg->databuf; return 0; out_overflow: @@ -603,8 +601,6 @@ gss_refresh_upcall(struct rpc_task *task) struct rpc_pipe *pipe; int err = 0; - dprintk("RPC: %5u %s for uid %u\n", - task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_cred->fsuid)); gss_msg = gss_setup_upcall(gss_auth, cred); if (PTR_ERR(gss_msg) == -EAGAIN) { /* XXX: warning on the first, under the assumption we @@ -612,7 +608,8 @@ gss_refresh_upcall(struct rpc_task *task) warn_gssd(); task->tk_timeout = 15*HZ; rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL); - return -EAGAIN; + err = -EAGAIN; + goto out; } if (IS_ERR(gss_msg)) { err = PTR_ERR(gss_msg); @@ -635,9 +632,8 @@ gss_refresh_upcall(struct rpc_task *task) spin_unlock(&pipe->lock); gss_release_msg(gss_msg); out: - dprintk("RPC: %5u %s for uid %u result %d\n", - task->tk_pid, __func__, - from_kuid(&init_user_ns, cred->cr_cred->fsuid), err); + trace_rpcgss_upcall_result(from_kuid(&init_user_ns, + cred->cr_cred->fsuid), err); return err; } @@ -652,14 +648,13 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) DEFINE_WAIT(wait); int err; - dprintk("RPC: %s for uid %u\n", - __func__, from_kuid(&init_user_ns, cred->cr_cred->fsuid)); retry: err = 0; /* if gssd is down, just skip upcalling altogether */ if (!gssd_running(net)) { warn_gssd(); - return -EACCES; + err = -EACCES; + goto out; } gss_msg = gss_setup_upcall(gss_auth, cred); if (PTR_ERR(gss_msg) == -EAGAIN) { @@ -700,8 +695,8 @@ out_intr: finish_wait(&gss_msg->waitqueue, &wait); gss_release_msg(gss_msg); out: - dprintk("RPC: %s for uid %u result %d\n", - __func__, from_kuid(&init_user_ns, cred->cr_cred->fsuid), err); + trace_rpcgss_upcall_result(from_kuid(&init_user_ns, + cred->cr_cred->fsuid), err); return err; } @@ -794,7 +789,6 @@ err_put_ctx: err: kfree(buf); out: - dprintk("RPC: %s returning %zd\n", __func__, err); return err; } @@ -863,8 +857,6 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); if (msg->errno < 0) { - dprintk("RPC: %s releasing msg %p\n", - __func__, gss_msg); refcount_inc(&gss_msg->count); gss_unhash_msg(gss_msg); if (msg->errno == -ETIMEDOUT) @@ -1024,8 +1016,6 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) struct rpc_auth * auth; int err = -ENOMEM; /* XXX? */ - dprintk("RPC: creating GSS authenticator for client %p\n", clnt); - if (!try_module_get(THIS_MODULE)) return ERR_PTR(err); if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) @@ -1041,10 +1031,8 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) gss_auth->net = get_net(rpc_net_ns(clnt)); err = -EINVAL; gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); - if (!gss_auth->mech) { - dprintk("RPC: Pseudoflavor %d not found!\n", flavor); + if (!gss_auth->mech) goto err_put_net; - } gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); if (gss_auth->service == 0) goto err_put_mech; @@ -1099,6 +1087,7 @@ err_free: kfree(gss_auth); out_dec: module_put(THIS_MODULE); + trace_rpcgss_createauth(flavor, err); return ERR_PTR(err); } @@ -1135,9 +1124,6 @@ gss_destroy(struct rpc_auth *auth) struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); - dprintk("RPC: destroying GSS authenticator %p flavor %d\n", - auth, auth->au_flavor); - if (hash_hashed(&gss_auth->hash)) { spin_lock(&gss_auth_hash_lock); hash_del(&gss_auth->hash); @@ -1300,8 +1286,6 @@ gss_send_destroy_context(struct rpc_cred *cred) static void gss_do_free_ctx(struct gss_cl_ctx *ctx) { - dprintk("RPC: %s\n", __func__); - gss_delete_sec_context(&ctx->gc_gss_ctx); kfree(ctx->gc_wire_ctx.data); kfree(ctx->gc_acceptor.data); @@ -1324,7 +1308,6 @@ gss_free_ctx(struct gss_cl_ctx *ctx) static void gss_free_cred(struct gss_cred *gss_cred) { - dprintk("RPC: %s cred=%p\n", __func__, gss_cred); kfree(gss_cred); } @@ -1381,10 +1364,6 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t struct gss_cred *cred = NULL; int err = -ENOMEM; - dprintk("RPC: %s for uid %d, flavor %d\n", - __func__, from_kuid(&init_user_ns, acred->cred->fsuid), - auth->au_flavor); - if (!(cred = kzalloc(sizeof(*cred), gfp))) goto out_err; @@ -1400,7 +1379,6 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t return &cred->gc_base; out_err: - dprintk("RPC: %s failed with error %d\n", __func__, err); return ERR_PTR(err); } @@ -1544,15 +1522,14 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) struct xdr_netobj mic; struct kvec iov; struct xdr_buf verf_buf; - - dprintk("RPC: %5u %s\n", task->tk_pid, __func__); + int status; /* Credential */ p = xdr_reserve_space(xdr, 7 * sizeof(*p) + ctx->gc_wire_ctx.len); if (!p) - goto out_put_ctx; + goto marshal_failed; *p++ = rpc_auth_gss; cred_len = p++; @@ -1560,7 +1537,8 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ; spin_unlock(&ctx->gc_seq_lock); if (req->rq_seqno == MAXSEQ) - goto out_expired; + goto expired; + trace_rpcgss_seqno(task); *p++ = cpu_to_be32(RPC_GSS_VERSION); *p++ = cpu_to_be32(ctx->gc_proc); @@ -1579,25 +1557,31 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) p = xdr_reserve_space(xdr, sizeof(*p)); if (!p) - goto out_put_ctx; + goto marshal_failed; *p++ = rpc_auth_gss; mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); if (maj_stat == GSS_S_CONTEXT_EXPIRED) - goto out_expired; + goto expired; else if (maj_stat != 0) - goto out_put_ctx; + goto bad_mic; if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) - goto out_put_ctx; - gss_put_ctx(ctx); - return 0; -out_expired: + goto marshal_failed; + status = 0; +out: gss_put_ctx(ctx); + return status; +expired: clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); - return -EKEYEXPIRED; -out_put_ctx: - gss_put_ctx(ctx); - return -EMSGSIZE; + status = -EKEYEXPIRED; + goto out; +marshal_failed: + status = -EMSGSIZE; + goto out; +bad_mic: + trace_rpcgss_get_mic(task, maj_stat); + status = -EIO; + goto out; } static int gss_renew_cred(struct rpc_task *task) @@ -1723,8 +1707,7 @@ validate_failed: status = -EIO; goto out; bad_mic: - dprintk("RPC: %5u %s: gss_verify_mic returned error 0x%08x\n", - task->tk_pid, __func__, maj_stat); + trace_rpcgss_verify_mic(task, maj_stat); status = -EACCES; goto out; } @@ -1761,13 +1744,16 @@ static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); else if (maj_stat) - goto wrap_failed; + goto bad_mic; /* Check that the trailing MIC fit in the buffer, after the fact */ if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) goto wrap_failed; return 0; wrap_failed: return -EMSGSIZE; +bad_mic: + trace_rpcgss_get_mic(task, maj_stat); + return -EIO; } static void @@ -1860,7 +1846,6 @@ static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); snd_buf->tail[0].iov_base = tmp; } - status = -EIO; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); /* slack space should prevent this ever happening: */ @@ -1871,7 +1856,7 @@ static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, if (maj_stat == GSS_S_CONTEXT_EXPIRED) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); else if (maj_stat) - goto wrap_failed; + goto bad_wrap; *opaque_len = cpu_to_be32(snd_buf->len - offset); /* guess whether the pad goes into the head or the tail: */ @@ -1888,6 +1873,9 @@ static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, return 0; wrap_failed: return status; +bad_wrap: + trace_rpcgss_wrap(task, maj_stat); + return -EIO; } static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) @@ -1898,7 +1886,6 @@ static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); int status; - dprintk("RPC: %5u %s\n", task->tk_pid, __func__); status = -EIO; if (ctx->gc_proc != RPC_GSS_PROC_DATA) { /* The spec seems a little ambiguous here, but I think that not @@ -1917,10 +1904,11 @@ static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) case RPC_GSS_SVC_PRIVACY: status = gss_wrap_req_priv(cred, ctx, task, xdr); break; + default: + status = -EIO; } out: gss_put_ctx(ctx); - dprintk("RPC: %5u %s returning %d\n", task->tk_pid, __func__, status); return status; } @@ -1932,8 +1920,9 @@ gss_unwrap_resp_auth(struct rpc_cred *cred) } static int -gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, - struct rpc_rqst *rqstp, struct xdr_stream *xdr) +gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, + struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, + struct xdr_stream *xdr) { struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf; u32 data_offset, mic_offset, integ_len, maj_stat; @@ -1951,7 +1940,7 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, if (mic_offset > rcv_buf->len) goto unwrap_failed; if (be32_to_cpup(p) != rqstp->rq_seqno) - goto unwrap_failed; + goto bad_seqno; if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, integ_len)) goto unwrap_failed; @@ -1967,16 +1956,20 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 1 + XDR_QUADLEN(mic.len); return 0; unwrap_failed: + trace_rpcgss_unwrap_failed(task); + return -EIO; +bad_seqno: + trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(p)); return -EIO; bad_mic: - dprintk("RPC: %s: gss_verify_mic returned error 0x%08x\n", - __func__, maj_stat); + trace_rpcgss_verify_mic(task, maj_stat); return -EIO; } static int -gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, - struct rpc_rqst *rqstp, struct xdr_stream *xdr) +gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, + struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, + struct xdr_stream *xdr) { struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; struct kvec *head = rqstp->rq_rcv_buf.head; @@ -2000,7 +1993,7 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, goto bad_unwrap; /* gss_unwrap decrypted the sequence number */ if (be32_to_cpup(p++) != rqstp->rq_seqno) - goto unwrap_failed; + goto bad_seqno; /* gss_unwrap redacts the opaque blob from the head iovec. * rcv_buf has changed, thus the stream needs to be reset. @@ -2011,10 +2004,13 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, XDR_QUADLEN(savedlen - rcv_buf->len); return 0; unwrap_failed: + trace_rpcgss_unwrap_failed(task); + return -EIO; +bad_seqno: + trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p)); return -EIO; bad_unwrap: - dprintk("RPC: %s: gss_unwrap returned error 0x%08x\n", - __func__, maj_stat); + trace_rpcgss_unwrap(task, maj_stat); return -EIO; } @@ -2030,14 +2026,14 @@ gss_xmit_need_reencode(struct rpc_task *task) struct rpc_rqst *req = task->tk_rqstp; struct rpc_cred *cred = req->rq_cred; struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); - u32 win, seq_xmit; + u32 win, seq_xmit = 0; bool ret = true; if (!ctx) - return true; + goto out; if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq))) - goto out; + goto out_ctx; seq_xmit = READ_ONCE(ctx->gc_seq_xmit); while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) { @@ -2046,15 +2042,18 @@ gss_xmit_need_reencode(struct rpc_task *task) seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno); if (seq_xmit == tmp) { ret = false; - goto out; + goto out_ctx; } } win = ctx->gc_win; if (win > 0) ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win); -out: + +out_ctx: gss_put_ctx(ctx); +out: + trace_rpcgss_need_reencode(task, seq_xmit, ret); return ret; } @@ -2075,10 +2074,10 @@ gss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) status = gss_unwrap_resp_auth(cred); break; case RPC_GSS_SVC_INTEGRITY: - status = gss_unwrap_resp_integ(cred, ctx, rqstp, xdr); + status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr); break; case RPC_GSS_SVC_PRIVACY: - status = gss_unwrap_resp_priv(cred, ctx, rqstp, xdr); + status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr); break; } if (status) @@ -2088,8 +2087,6 @@ out_decode: status = rpcauth_unwrap_resp_decode(task, xdr); out: gss_put_ctx(ctx); - dprintk("RPC: %5u %s returning %d\n", - task->tk_pid, __func__, status); return status; } diff --git a/net/sunrpc/auth_gss/trace.c b/net/sunrpc/auth_gss/trace.c new file mode 100644 index 000000000000..5576f1e66de9 --- /dev/null +++ b/net/sunrpc/auth_gss/trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, 2019 Oracle. All rights reserved. + */ + +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index f1ec2110efeb..bc7489f1fe55 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1165,6 +1165,7 @@ xprt_request_enqueue_transmit(struct rpc_task *task) /* Note: req is added _before_ pos */ list_add_tail(&req->rq_xmit, &pos->rq_xmit); INIT_LIST_HEAD(&req->rq_xmit2); + trace_xprt_enq_xmit(task, 1); goto out; } } else if (RPC_IS_SWAPPER(task)) { @@ -1176,6 +1177,7 @@ xprt_request_enqueue_transmit(struct rpc_task *task) /* Note: req is added _before_ pos */ list_add_tail(&req->rq_xmit, &pos->rq_xmit); INIT_LIST_HEAD(&req->rq_xmit2); + trace_xprt_enq_xmit(task, 2); goto out; } } else if (!req->rq_seqno) { @@ -1184,11 +1186,13 @@ xprt_request_enqueue_transmit(struct rpc_task *task) continue; list_add_tail(&req->rq_xmit2, &pos->rq_xmit2); INIT_LIST_HEAD(&req->rq_xmit); + trace_xprt_enq_xmit(task, 3); goto out; } } list_add_tail(&req->rq_xmit, &xprt->xmit_queue); INIT_LIST_HEAD(&req->rq_xmit2); + trace_xprt_enq_xmit(task, 4); out: set_bit(RPC_TASK_NEED_XMIT, &task->tk_runstate); spin_unlock(&xprt->queue_lock); @@ -1313,8 +1317,6 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) int is_retrans = RPC_WAS_SENT(task); int status; - dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen); - if (!req->rq_bytes_sent) { if (xprt_request_data_received(task)) { status = 0; @@ -1336,9 +1338,9 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) connect_cookie = xprt->connect_cookie; status = xprt->ops->send_request(req); - trace_xprt_transmit(xprt, req->rq_xid, status); if (status != 0) { req->rq_ntrans--; + trace_xprt_transmit(req, status); return status; } @@ -1347,7 +1349,6 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) xprt_inject_disconnect(xprt); - dprintk("RPC: %5u xmit complete\n", task->tk_pid); task->tk_flags |= RPC_TASK_SENT; spin_lock_bh(&xprt->transport_lock); @@ -1360,6 +1361,7 @@ xprt_request_transmit(struct rpc_rqst *req, struct rpc_task *snd_task) req->rq_connect_cookie = connect_cookie; out_dequeue: + trace_xprt_transmit(req, status); xprt_request_dequeue_transmit(task); rpc_wake_up_queued_task_set_status(&xprt->sending, task, status); return status; From 241b1f419f0ea9966d574d7cc67377c74982a125 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:09 -0500 Subject: [PATCH 1778/4277] SUNRPC: Remove xdr_buf_trim() The key action of xdr_buf_trim() is that it shortens buf->len, the length of the xdr_buf's content. The other actions -- shortening the head, pages, and tail components -- are actually not necessary. In particular, changing the size of those components can corrupt the RPC message contained in the buffer. This is an accident waiting to happen rather than a current bug, as far as we know. Signed-off-by: Chuck Lever Acked-by: Bruce Fields Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xdr.h | 1 - net/sunrpc/auth_gss/gss_krb5_wrap.c | 8 +++--- net/sunrpc/auth_gss/svcauth_gss.c | 2 +- net/sunrpc/xdr.c | 41 ----------------------------- 4 files changed, 6 insertions(+), 46 deletions(-) diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 65af6a204b75..9ee3970ba59c 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -179,7 +179,6 @@ xdr_adjust_iovec(struct kvec *iov, __be32 *p) extern void xdr_shift_buf(struct xdr_buf *, size_t); extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *); extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, unsigned int, unsigned int); -extern void xdr_buf_trim(struct xdr_buf *, unsigned int); extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, unsigned int); extern int read_bytes_from_xdr_buf(struct xdr_buf *, unsigned int, void *, unsigned int); extern int write_bytes_to_xdr_buf(struct xdr_buf *, unsigned int, void *, unsigned int); diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index 5cdde6cb703a..14a0aff0cd84 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -570,14 +570,16 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf) */ movelen = min_t(unsigned int, buf->head[0].iov_len, buf->len); movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip; - BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen > - buf->head[0].iov_len); + if (offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen > + buf->head[0].iov_len) + return GSS_S_FAILURE; memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen); buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip; buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip; /* Trim off the trailing "extra count" and checksum blob */ - xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip); + buf->len -= ec + GSS_KRB5_TOK_HDR_LEN + tailskip; + return GSS_S_COMPLETE; } diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 152790ed309c..f1aabab4a4c2 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -896,7 +896,7 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g if (svc_getnl(&buf->head[0]) != seq) goto out; /* trim off the mic and padding at the end before returning */ - xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4); + buf->len -= 4 + round_up_to_quad(mic.len); stat = 0; out: kfree(mic.data); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 5f0aa53fa4ae..4bce61978062 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -1139,47 +1139,6 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, } EXPORT_SYMBOL_GPL(xdr_buf_subsegment); -/** - * xdr_buf_trim - lop at most "len" bytes off the end of "buf" - * @buf: buf to be trimmed - * @len: number of bytes to reduce "buf" by - * - * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note - * that it's possible that we'll trim less than that amount if the xdr_buf is - * too small, or if (for instance) it's all in the head and the parser has - * already read too far into it. - */ -void xdr_buf_trim(struct xdr_buf *buf, unsigned int len) -{ - size_t cur; - unsigned int trim = len; - - if (buf->tail[0].iov_len) { - cur = min_t(size_t, buf->tail[0].iov_len, trim); - buf->tail[0].iov_len -= cur; - trim -= cur; - if (!trim) - goto fix_len; - } - - if (buf->page_len) { - cur = min_t(unsigned int, buf->page_len, trim); - buf->page_len -= cur; - trim -= cur; - if (!trim) - goto fix_len; - } - - if (buf->head[0].iov_len) { - cur = min_t(size_t, buf->head[0].iov_len, trim); - buf->head[0].iov_len -= cur; - trim -= cur; - } -fix_len: - buf->len -= (len - trim); -} -EXPORT_SYMBOL_GPL(xdr_buf_trim); - static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) { unsigned int this_len; From 2573a46499956c2ff311e1a65052364243161e78 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:15 -0500 Subject: [PATCH 1779/4277] SUNRPC: Add SPDX IDs to some net/sunrpc/auth_gss/ files Files under net/sunrpc/auth_gss/ do not yet have SPDX ID tags. This directory is somewhat complicated because most of these files have license boilerplate that is not strictly GPL 2.0. In this patch I add ID tags where there is an obvious match. The less recognizable licenses are still under research. For reference, SPDX IDs added in this patch correspond to the following license text: GPL-2.0 https://spdx.org/licenses/GPL-2.0.html GPL-2.0+ https://spdx.org/licenses/GPL-2.0+.html BSD-3-Clause https://spdx.org/licenses/BSD-3-Clause.html Cc: Simo Sorce Cc: Kate Stewart Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/auth_gss/auth_gss.c | 27 +-------------------------- net/sunrpc/auth_gss/gss_krb5_mech.c | 27 +-------------------------- net/sunrpc/auth_gss/gss_mech_switch.c | 27 +-------------------------- net/sunrpc/auth_gss/gss_rpc_upcall.c | 15 +-------------- net/sunrpc/auth_gss/gss_rpc_upcall.h | 16 ++-------------- net/sunrpc/auth_gss/gss_rpc_xdr.c | 15 +-------------- net/sunrpc/auth_gss/gss_rpc_xdr.h | 17 +---------------- net/sunrpc/auth_gss/svcauth_gss.c | 1 + 8 files changed, 9 insertions(+), 136 deletions(-) diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 3d1fbd6d3370..fda454c9b594 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause /* * linux/net/sunrpc/auth_gss/auth_gss.c * @@ -8,34 +9,8 @@ * * Dug Song * Andy Adamson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include #include diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index be31a58d54e0..56cc85c5bc06 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause /* * linux/net/sunrpc/gss_krb5_mech.c * @@ -6,32 +7,6 @@ * * Andy Adamson * J. Bruce Fields - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index 379318dff534..82060099a429 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause /* * linux/net/sunrpc/gss_mech_switch.c * @@ -5,32 +6,6 @@ * All rights reserved. * * J. Bruce Fields - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c index 73dcda060335..0349f455a862 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.c +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * linux/net/sunrpc/gss_rpc_upcall.c * * Copyright (C) 2012 Simo Sorce - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.h b/net/sunrpc/auth_gss/gss_rpc_upcall.h index 1e542aded90a..31e96344167e 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.h +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.h @@ -1,21 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * linux/net/sunrpc/gss_rpc_upcall.h * * Copyright (C) 2012 Simo Sorce - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GSS_RPC_UPCALL_H @@ -45,4 +32,5 @@ void gssp_free_upcall_data(struct gssp_upcall_data *data); void init_gssp_clnt(struct sunrpc_net *); int set_gssp_clnt(struct net *); void clear_gssp_clnt(struct sunrpc_net *); + #endif /* _GSS_RPC_UPCALL_H */ diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c index 006062ad5f58..2ff7b7083eba 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.c +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * GSS Proxy upcall module * * Copyright (C) 2012 Simo Sorce - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h index 146c31032917..3f17411b7e65 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.h +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h @@ -1,21 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * GSS Proxy upcall module * * Copyright (C) 2012 Simo Sorce - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _LINUX_GSS_RPC_XDR_H @@ -262,6 +249,4 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, #define GSSX_ARG_wrap_size_limit_sz 0 #define GSSX_RES_wrap_size_limit_sz 0 - - #endif /* _LINUX_GSS_RPC_XDR_H */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index f1aabab4a4c2..0c5d7896d6dd 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Neil Brown * J. Bruce Fields From cf500bac8fd48b57f38ece890235923d4ed5ee91 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:20 -0500 Subject: [PATCH 1780/4277] SUNRPC: Introduce rpc_prepare_reply_pages() prepare_reply_buffer() and its NFSv4 equivalents expose the details of the RPC header and the auth slack values to upper layer consumers, creating a layering violation, and duplicating code. Remedy these issues by adding a new RPC client API that hides those details from upper layers in a common helper function. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- fs/nfs/nfs2xdr.c | 27 +++++-------------- fs/nfs/nfs3xdr.c | 29 +++++--------------- fs/nfs/nfs4xdr.c | 51 +++++++++++++++-------------------- include/linux/sunrpc/clnt.h | 3 +++ include/trace/events/sunrpc.h | 37 +++++++++++++++++++++++++ net/sunrpc/clnt.c | 19 +++++++++++++ net/sunrpc/xdr.c | 9 +++++++ 7 files changed, 102 insertions(+), 73 deletions(-) diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index bac3a4e2cb5d..1dcd0feda32d 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -65,21 +65,6 @@ static int nfs_stat_to_errno(enum nfs_stat); -/* - * While encoding arguments, set up the reply buffer in advance to - * receive reply data directly into the page cache. - */ -static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, - unsigned int base, unsigned int len, - unsigned int bufsize) -{ - struct rpc_auth *auth = req->rq_cred->cr_auth; - unsigned int replen; - - replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize; - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); -} - /* * Encode/decode NFSv2 basic data types * @@ -593,8 +578,8 @@ static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, const struct nfs_readlinkargs *args = data; encode_fhandle(xdr, args->fh); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->pglen, NFS_readlinkres_sz); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->pglen, NFS_readlinkres_sz); } /* @@ -629,8 +614,8 @@ static void nfs2_xdr_enc_readargs(struct rpc_rqst *req, const struct nfs_pgio_args *args = data; encode_readargs(xdr, args); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->count, NFS_readres_sz); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->count, NFS_readres_sz); req->rq_rcv_buf.flags |= XDRBUF_READ; } @@ -787,8 +772,8 @@ static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, const struct nfs_readdirargs *args = data; encode_readdirargs(xdr, args); - prepare_reply_buffer(req, args->pages, 0, - args->count, NFS_readdirres_sz); + rpc_prepare_reply_pages(req, args->pages, 0, + args->count, NFS_readdirres_sz); } /* diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 4aa3ffe1800e..a54dcf4bfb1d 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -104,21 +104,6 @@ static const umode_t nfs_type2fmt[] = { [NF3FIFO] = S_IFIFO, }; -/* - * While encoding arguments, set up the reply buffer in advance to - * receive reply data directly into the page cache. - */ -static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, - unsigned int base, unsigned int len, - unsigned int bufsize) -{ - struct rpc_auth *auth = req->rq_cred->cr_auth; - unsigned int replen; - - replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize; - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); -} - /* * Encode/decode NFSv3 basic data types * @@ -910,8 +895,8 @@ static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req, const struct nfs3_readlinkargs *args = data; encode_nfs_fh3(xdr, args->fh); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->pglen, NFS3_readlinkres_sz); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->pglen, NFS3_readlinkres_sz); } /* @@ -943,8 +928,8 @@ static void nfs3_xdr_enc_read3args(struct rpc_rqst *req, unsigned int replen = args->replen ? args->replen : NFS3_readres_sz; encode_read3args(xdr, args); - prepare_reply_buffer(req, args->pages, args->pgbase, - args->count, replen); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->count, replen); req->rq_rcv_buf.flags |= XDRBUF_READ; } @@ -1236,7 +1221,7 @@ static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req, const struct nfs3_readdirargs *args = data; encode_readdir3args(xdr, args); - prepare_reply_buffer(req, args->pages, 0, + rpc_prepare_reply_pages(req, args->pages, 0, args->count, NFS3_readdirres_sz); } @@ -1278,7 +1263,7 @@ static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req, const struct nfs3_readdirargs *args = data; encode_readdirplus3args(xdr, args); - prepare_reply_buffer(req, args->pages, 0, + rpc_prepare_reply_pages(req, args->pages, 0, args->count, NFS3_readdirres_sz); } @@ -1323,7 +1308,7 @@ static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req, encode_nfs_fh3(xdr, args->fh); encode_uint32(xdr, args->mask); if (args->mask & (NFS_ACL | NFS_DFACL)) { - prepare_reply_buffer(req, args->pages, 0, + rpc_prepare_reply_pages(req, args->pages, 0, NFSACL_MAXPAGES << PAGE_SHIFT, ACL3_getaclres_sz); req->rq_rcv_buf.flags |= XDRBUF_SPARSE_PAGES; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 38a4cbc18657..d0fa18df32ea 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1016,12 +1016,11 @@ static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) { __be32 *p; - struct rpc_auth *auth = req->rq_cred->cr_auth; /* initialize running count of expected bytes in reply. * NOTE: the replied tag SHOULD be the same is the one sent, * but this is not required as a MUST for the server to do so. */ - hdr->replen = RPC_REPHDRSIZE + auth->au_rslack + 3 + hdr->taglen; + hdr->replen = 3 + hdr->taglen; WARN_ON_ONCE(hdr->taglen > NFS4_MAXTAGLEN); encode_string(xdr, hdr->taglen, hdr->tag); @@ -2341,9 +2340,9 @@ static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr, encode_getfattr_open(xdr, args->bitmask, args->open_bitmap, &hdr); if (args->lg_args) { encode_layoutget(xdr, args->lg_args, &hdr); - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, - args->lg_args->layout.pages, - 0, args->lg_args->layout.pglen); + rpc_prepare_reply_pages(req, args->lg_args->layout.pages, 0, + args->lg_args->layout.pglen, + hdr.replen); } encode_nops(&hdr); } @@ -2387,9 +2386,9 @@ static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, encode_getfattr_open(xdr, args->bitmask, args->open_bitmap, &hdr); if (args->lg_args) { encode_layoutget(xdr, args->lg_args, &hdr); - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, - args->lg_args->layout.pages, - 0, args->lg_args->layout.pglen); + rpc_prepare_reply_pages(req, args->lg_args->layout.pages, 0, + args->lg_args->layout.pglen, + hdr.replen); } encode_nops(&hdr); } @@ -2499,8 +2498,8 @@ static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr, encode_putfh(xdr, args->fh, &hdr); encode_readlink(xdr, args, req, &hdr); - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, - args->pgbase, args->pglen); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->pglen, hdr.replen); encode_nops(&hdr); } @@ -2520,11 +2519,8 @@ static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr, encode_putfh(xdr, args->fh, &hdr); encode_readdir(xdr, args, req, &hdr); - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages, - args->pgbase, args->count); - dprintk("%s: inlined page args = (%u, %p, %u, %u)\n", - __func__, hdr.replen << 2, args->pages, - args->pgbase, args->count); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->count, hdr.replen); encode_nops(&hdr); } @@ -2544,8 +2540,8 @@ static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr, encode_putfh(xdr, args->fh, &hdr); encode_read(xdr, args, &hdr); - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, - args->pages, args->pgbase, args->count); + rpc_prepare_reply_pages(req, args->pages, args->pgbase, + args->count, hdr.replen); req->rq_rcv_buf.flags |= XDRBUF_READ; encode_nops(&hdr); } @@ -2591,9 +2587,8 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr, encode_getattr(xdr, nfs4_acl_bitmap, NULL, ARRAY_SIZE(nfs4_acl_bitmap), &hdr); - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, - args->acl_pages, 0, args->acl_len); - + rpc_prepare_reply_pages(req, args->acl_pages, 0, + args->acl_len, replen); encode_nops(&hdr); } @@ -2814,9 +2809,8 @@ static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, encode_fs_locations(xdr, args->bitmask, &hdr); } - /* Set up reply kvec to capture returned fs_locations array. */ - xdr_inline_pages(&req->rq_rcv_buf, replen << 2, - (struct page **)&args->page, 0, PAGE_SIZE); + rpc_prepare_reply_pages(req, (struct page **)&args->page, 0, + PAGE_SIZE, replen); encode_nops(&hdr); } @@ -3018,10 +3012,8 @@ static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req, /* set up reply kvec. Subtract notification bitmap max size (2) * so that notification bitmap is put in xdr_buf tail */ - xdr_inline_pages(&req->rq_rcv_buf, (hdr.replen - 2) << 2, - args->pdev->pages, args->pdev->pgbase, - args->pdev->pglen); - + rpc_prepare_reply_pages(req, args->pdev->pages, args->pdev->pgbase, + args->pdev->pglen, hdr.replen - 2); encode_nops(&hdr); } @@ -3042,9 +3034,8 @@ static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req, encode_putfh(xdr, NFS_FH(args->inode), &hdr); encode_layoutget(xdr, args, &hdr); - xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, - args->layout.pages, 0, args->layout.pglen); - + rpc_prepare_reply_pages(req, args->layout.pages, 0, + args->layout.pglen, hdr.replen); encode_nops(&hdr); } diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 1c441714d569..98bc9883b230 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -169,6 +169,9 @@ int rpcb_v4_register(struct net *net, const u32 program, const char *netid); void rpcb_getport_async(struct rpc_task *); +void rpc_prepare_reply_pages(struct rpc_rqst *req, struct page **pages, + unsigned int base, unsigned int len, + unsigned int hdrsize); void rpc_call_start(struct rpc_task *); int rpc_call_async(struct rpc_clnt *clnt, const struct rpc_message *msg, int flags, diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index e58dda8e038c..8451f30c6a0f 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -461,6 +461,43 @@ TRACE_EVENT(rpc_xdr_alignment, ) ); +TRACE_EVENT(rpc_reply_pages, + TP_PROTO( + const struct rpc_rqst *req + ), + + TP_ARGS(req), + + TP_STRUCT__entry( + __field(unsigned int, task_id) + __field(unsigned int, client_id) + __field(const void *, head_base) + __field(size_t, head_len) + __field(const void *, tail_base) + __field(size_t, tail_len) + __field(unsigned int, page_len) + ), + + TP_fast_assign( + __entry->task_id = req->rq_task->tk_pid; + __entry->client_id = req->rq_task->tk_client->cl_clid; + + __entry->head_base = req->rq_rcv_buf.head[0].iov_base; + __entry->head_len = req->rq_rcv_buf.head[0].iov_len; + __entry->page_len = req->rq_rcv_buf.page_len; + __entry->tail_base = req->rq_rcv_buf.tail[0].iov_base; + __entry->tail_len = req->rq_rcv_buf.tail[0].iov_len; + ), + + TP_printk( + "task:%u@%u xdr=[%p,%zu]/%u/[%p,%zu]\n", + __entry->task_id, __entry->client_id, + __entry->head_base, __entry->head_len, + __entry->page_len, + __entry->tail_base, __entry->tail_len + ) +); + /* * First define the enums in the below macros to be exported to userspace * via TRACE_DEFINE_ENUM(). diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 803e93105af1..f780605fffe0 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1164,6 +1164,25 @@ struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req) } #endif /* CONFIG_SUNRPC_BACKCHANNEL */ +/** + * rpc_prepare_reply_pages - Prepare to receive a reply data payload into pages + * @req: RPC request to prepare + * @pages: vector of struct page pointers + * @base: offset in first page where receive should start, in bytes + * @len: expected size of the upper layer data payload, in bytes + * @hdrsize: expected size of upper layer reply header, in XDR words + * + */ +void rpc_prepare_reply_pages(struct rpc_rqst *req, struct page **pages, + unsigned int base, unsigned int len, + unsigned int hdrsize) +{ + hdrsize += RPC_REPHDRSIZE + req->rq_cred->cr_auth->au_rslack; + xdr_inline_pages(&req->rq_rcv_buf, hdrsize << 2, pages, base, len); + trace_rpc_reply_pages(req); +} +EXPORT_SYMBOL_GPL(rpc_prepare_reply_pages); + void rpc_call_start(struct rpc_task *task) { diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 4bce61978062..7cca51560442 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -163,6 +163,15 @@ xdr_free_bvec(struct xdr_buf *buf) buf->bvec = NULL; } +/** + * xdr_inline_pages - Prepare receive buffer for a large reply + * @xdr: xdr_buf into which reply will be placed + * @offset: expected offset where data payload will start, in bytes + * @pages: vector of struct page pointers + * @base: offset in first page where receive should start, in bytes + * @len: expected size of the upper layer data payload, in bytes + * + */ void xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, struct page **pages, unsigned int base, unsigned int len) From 02ef04e432babf8fc703104212314e54112ecd2d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:25 -0500 Subject: [PATCH 1781/4277] NFS: Account for XDR pad of buf->pages Certain NFS results (eg. READLINK) might expect a data payload that is not an exact multiple of 4 bytes. In this case, XDR encoding is required to pad that payload so its length on the wire is a multiple of 4 bytes. The constants that define the maximum size of each NFS result do not appear to account for this extra word. In each case where the data payload is to be received into pages: - 1 word is added to the size of the receive buffer allocated by call_allocate - rpc_inline_rcv_pages subtracts 1 word from @hdrsize so that the extra buffer space falls into the rcv_buf's tail iovec - If buf->pagelen is word-aligned, an XDR pad is not needed and is thus removed from the tail Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- fs/nfs/nfs2xdr.c | 6 +++--- fs/nfs/nfs3xdr.c | 10 +++++----- fs/nfs/nfs4xdr.c | 15 ++++++++------- net/sunrpc/clnt.c | 6 +++++- net/sunrpc/xdr.c | 2 ++ 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 1dcd0feda32d..a7ed29de0a40 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -56,11 +56,11 @@ #define NFS_attrstat_sz (1+NFS_fattr_sz) #define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz) -#define NFS_readlinkres_sz (2) -#define NFS_readres_sz (1+NFS_fattr_sz+1) +#define NFS_readlinkres_sz (2+1) +#define NFS_readres_sz (1+NFS_fattr_sz+1+1) #define NFS_writeres_sz (NFS_attrstat_sz) #define NFS_stat_sz (1) -#define NFS_readdirres_sz (1) +#define NFS_readdirres_sz (1+1) #define NFS_statfsres_sz (1+NFS_info_sz) static int nfs_stat_to_errno(enum nfs_stat); diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index a54dcf4bfb1d..110358f4986d 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -69,13 +69,13 @@ #define NFS3_removeres_sz (NFS3_setattrres_sz) #define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)) #define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1) -#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1) -#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3) +#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1+1) +#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3+1) #define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4) #define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) #define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz)) #define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) -#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2) +#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2+1) #define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13) #define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12) #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) @@ -85,7 +85,7 @@ #define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \ XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)) #define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \ - XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)) + XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)+1) #define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz) static int nfs3_stat_to_errno(enum nfs_stat); @@ -1629,7 +1629,7 @@ static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr, result->op_status = status; if (status != NFS3_OK) goto out_status; - result->replen = 3 + ((xdr_stream_pos(xdr) - pos) >> 2); + result->replen = 4 + ((xdr_stream_pos(xdr) - pos) >> 2); error = decode_read3resok(xdr, result); out: return error; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index d0fa18df32ea..6d9d5e2f6308 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -215,14 +215,14 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, nfs4_fattr_bitmap_maxsz) #define encode_read_maxsz (op_encode_hdr_maxsz + \ encode_stateid_maxsz + 3) -#define decode_read_maxsz (op_decode_hdr_maxsz + 2) +#define decode_read_maxsz (op_decode_hdr_maxsz + 2 + 1) #define encode_readdir_maxsz (op_encode_hdr_maxsz + \ 2 + encode_verifier_maxsz + 5 + \ nfs4_label_maxsz) #define decode_readdir_maxsz (op_decode_hdr_maxsz + \ - decode_verifier_maxsz) + decode_verifier_maxsz + 1) #define encode_readlink_maxsz (op_encode_hdr_maxsz) -#define decode_readlink_maxsz (op_decode_hdr_maxsz + 1) +#define decode_readlink_maxsz (op_decode_hdr_maxsz + 1 + 1) #define encode_write_maxsz (op_encode_hdr_maxsz + \ encode_stateid_maxsz + 4) #define decode_write_maxsz (op_decode_hdr_maxsz + \ @@ -284,14 +284,14 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, #define decode_delegreturn_maxsz (op_decode_hdr_maxsz) #define encode_getacl_maxsz (encode_getattr_maxsz) #define decode_getacl_maxsz (op_decode_hdr_maxsz + \ - nfs4_fattr_bitmap_maxsz + 1) + nfs4_fattr_bitmap_maxsz + 1 + 1) #define encode_setacl_maxsz (op_encode_hdr_maxsz + \ encode_stateid_maxsz + 3) #define decode_setacl_maxsz (decode_setattr_maxsz) #define encode_fs_locations_maxsz \ (encode_getattr_maxsz) #define decode_fs_locations_maxsz \ - (0) + (1) #define encode_secinfo_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz) #define decode_secinfo_maxsz (op_decode_hdr_maxsz + 1 + ((NFS_MAX_SECFLAVORS * (16 + GSS_OID_MAX_LEN)) / 4)) @@ -392,12 +392,13 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, 1 /* opaque devaddr4 length */ + \ /* devaddr4 payload is read into page */ \ 1 /* notification bitmap length */ + \ - 1 /* notification bitmap, word 0 */) + 1 /* notification bitmap, word 0 */ + \ + 1 /* possible XDR padding */) #define encode_layoutget_maxsz (op_encode_hdr_maxsz + 10 + \ encode_stateid_maxsz) #define decode_layoutget_maxsz (op_decode_hdr_maxsz + 8 + \ decode_stateid_maxsz + \ - XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE)) + XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE) + 1) #define encode_layoutcommit_maxsz (op_encode_hdr_maxsz + \ 2 /* offset */ + \ 2 /* length */ + \ diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index f780605fffe0..4ea38b029e2f 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1177,7 +1177,11 @@ void rpc_prepare_reply_pages(struct rpc_rqst *req, struct page **pages, unsigned int base, unsigned int len, unsigned int hdrsize) { - hdrsize += RPC_REPHDRSIZE + req->rq_cred->cr_auth->au_rslack; + /* Subtract one to force an extra word of buffer space for the + * payload's XDR pad to fall into the rcv_buf's tail iovec. + */ + hdrsize += RPC_REPHDRSIZE + req->rq_cred->cr_auth->au_rslack - 1; + xdr_inline_pages(&req->rq_rcv_buf, hdrsize << 2, pages, base, len); trace_rpc_reply_pages(req); } diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 7cca51560442..aa8177ddcbda 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -189,6 +189,8 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, tail->iov_base = buf + offset; tail->iov_len = buflen - offset; + if ((xdr->page_len & 3) == 0) + tail->iov_len -= sizeof(__be32); xdr->buflen += len; } From 41cb8d189c9d4964df52a6f497cab7b301ae831b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:44:59 +0530 Subject: [PATCH 1782/4277] PCI: endpoint: Add new pci_epc_ops to get EPC features Add a new pci_epc_ops ->get_features() to get the features supported by the EPC. Since EPC can provide different features to different functions, the ->get_features() ops takes _func_no_ as an argument. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epc-core.c | 30 +++++++++++++++++++++++++++++ include/linux/pci-epc.h | 22 +++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 094dcc3203b8..5a099479d9ab 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -83,6 +83,36 @@ err: } EXPORT_SYMBOL_GPL(pci_epc_get); +/** + * pci_epc_get_features() - get the features supported by EPC + * @epc: the features supported by *this* EPC device will be returned + * @func_no: the features supported by the EPC device specific to the + * endpoint function with func_no will be returned + * + * Invoke to get the features provided by the EPC which may be + * specific to an endpoint function. Returns pci_epc_features on success + * and NULL for any failures. + */ +const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, + u8 func_no) +{ + const struct pci_epc_features *epc_features; + unsigned long flags; + + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + return NULL; + + if (!epc->ops->get_features) + return NULL; + + spin_lock_irqsave(&epc->lock, flags); + epc_features = epc->ops->get_features(epc, func_no); + spin_unlock_irqrestore(&epc->lock, flags); + + return epc_features; +} +EXPORT_SYMBOL_GPL(pci_epc_get_features); + /** * pci_epc_stop() - stop the PCI link * @epc: the link of the EPC device that has to be stopped diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 37dab8116901..79fbcf94e14d 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -59,6 +59,8 @@ struct pci_epc_ops { enum pci_epc_irq_type type, u16 interrupt_num); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); + const struct pci_epc_features* (*get_features)(struct pci_epc *epc, + u8 func_no); struct module *owner; }; @@ -100,6 +102,24 @@ struct pci_epc { unsigned int features; }; +/** + * struct pci_epc_features - features supported by a EPC device per function + * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up + * @msi_capable: indicate if the endpoint function has MSI capability + * @msix_capable: indicate if the endpoint function has MSI-X capability + * @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver + * @bar_fixed_64bit: bitmap to indicate fixed 64bit BARs + * @bar_fixed_size: Array specifying the size supported by each BAR + */ +struct pci_epc_features { + unsigned int linkup_notifier : 1; + unsigned int msi_capable : 1; + unsigned int msix_capable : 1; + u8 reserved_bar; + u8 bar_fixed_64bit; + u64 bar_fixed_size[BAR_5 + 1]; +}; + #define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0) #define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3)) #define EPC_FEATURE_MSIX_AVAILABLE BIT(4) @@ -158,6 +178,8 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, enum pci_epc_irq_type type, u16 interrupt_num); int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); +const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, + u8 func_no); struct pci_epc *pci_epc_get(const char *epc_name); void pci_epc_put(struct pci_epc *epc); From fee35cb76a54c87985410ea6aa12002e5d38b367 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:00 +0530 Subject: [PATCH 1783/4277] PCI: dwc: Add ->get_features() callback function to dw_pcie_ep_ops Each platform using Designware PCIe core can support different set of endpoint features. Add a new callback function ->get_features() in dw_pcie_ep_ops so that each platform using Designware PCIe core can advertise its supported features to the endpoint function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-ep.c | 12 ++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index a543c45c7224..7a2925a16ab8 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -355,6 +355,17 @@ static int dw_pcie_ep_start(struct pci_epc *epc) return pci->ops->start_link(pci); } +static const struct pci_epc_features* +dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + + if (!ep->ops->get_features) + return NULL; + + return ep->ops->get_features(ep); +} + static const struct pci_epc_ops epc_ops = { .write_header = dw_pcie_ep_write_header, .set_bar = dw_pcie_ep_set_bar, @@ -368,6 +379,7 @@ static const struct pci_epc_ops epc_ops = { .raise_irq = dw_pcie_ep_raise_irq, .start = dw_pcie_ep_start, .stop = dw_pcie_ep_stop, + .get_features = dw_pcie_ep_get_features, }; int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 9943d8c68335..1f56e6ae34ff 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -192,6 +192,7 @@ struct dw_pcie_ep_ops { void (*ep_init)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, enum pci_epc_irq_type type, u16 interrupt_num); + const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); }; struct dw_pcie_ep { From 3b4322e589a630fe35944ced5852655fcc4a5d24 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:01 +0530 Subject: [PATCH 1784/4277] PCI: designware-plat: Populate ->get_features() dw_pcie_ep_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by Designware PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-plat.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index c12bf794d69c..bd0516afc86f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -100,9 +100,22 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +static const struct pci_epc_features dw_plat_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = true, +}; + +static const struct pci_epc_features* +dw_plat_pcie_get_features(struct dw_pcie_ep *ep) +{ + return &dw_plat_pcie_epc_features; +} + static struct dw_pcie_ep_ops pcie_ep_ops = { .ep_init = dw_plat_pcie_ep_init, .raise_irq = dw_plat_pcie_ep_raise_irq, + .get_features = dw_plat_pcie_get_features, }; static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, From 4894467e78619232a79e39c2f26ae8378c4500ed Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:02 +0530 Subject: [PATCH 1785/4277] PCI: pci-dra7xx: Populate ->get_features() dw_pcie_ep_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by DRA7xx PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index a32d6dde7a57..15620cfa617b 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -389,9 +389,22 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +static const struct pci_epc_features dra7xx_pcie_epc_features = { + .linkup_notifier = true, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +dra7xx_pcie_get_features(struct dw_pcie_ep *ep) +{ + return &dra7xx_pcie_epc_features; +} + static struct dw_pcie_ep_ops pcie_ep_ops = { .ep_init = dra7xx_pcie_ep_init, .raise_irq = dra7xx_pcie_raise_irq, + .get_features = dra7xx_pcie_get_features, }; static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, From 146221768c74bbd969f968b61ec95a0254a6b311 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:03 +0530 Subject: [PATCH 1786/4277] PCI: rockchip: Populate ->get_features() dw_pcie_ep_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by Rockchip PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-rockchip-ep.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index b8163c56a142..9b60ad323ac7 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -505,6 +505,18 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc) return 0; } +static const struct pci_epc_features rockchip_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + return &rockchip_pcie_epc_features; +} + static const struct pci_epc_ops rockchip_pcie_epc_ops = { .write_header = rockchip_pcie_ep_write_header, .set_bar = rockchip_pcie_ep_set_bar, @@ -515,6 +527,7 @@ static const struct pci_epc_ops rockchip_pcie_epc_ops = { .get_msi = rockchip_pcie_ep_get_msi, .raise_irq = rockchip_pcie_ep_raise_irq, .start = rockchip_pcie_ep_start, + .get_features = rockchip_pcie_ep_get_features, }; static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, From 67c777e6015d857a5e9662c68281d83d946d9b70 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:04 +0530 Subject: [PATCH 1787/4277] PCI: cadence: Populate ->get_features() cdns_pcie_epc_ops Populate ->get_features() dw_pcie_ep_ops to return the EPC features supported by Cadence PCIe endpoint controller. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-ep.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index c3a088910f48..14c2545bb17e 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -411,6 +411,18 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) return 0; } +static const struct pci_epc_features cdns_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + return &cdns_pcie_epc_features; +} + static const struct pci_epc_ops cdns_pcie_epc_ops = { .write_header = cdns_pcie_ep_write_header, .set_bar = cdns_pcie_ep_set_bar, @@ -421,6 +433,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = { .get_msi = cdns_pcie_ep_get_msi, .raise_irq = cdns_pcie_ep_raise_irq, .start = cdns_pcie_ep_start, + .get_features = cdns_pcie_ep_get_features, }; static const struct of_device_id cdns_pcie_ep_of_match[] = { From a00275baa68e1ee226cc659f54dc3a571f3ad600 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:31 -0500 Subject: [PATCH 1788/4277] SUNRPC: Make AUTH_SYS and AUTH_NULL set au_verfsize au_verfsize will be needed for a non-flavor-specific computation in a subsequent patch. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/auth.h | 3 +-- net/sunrpc/auth_gss/auth_gss.c | 1 + net/sunrpc/auth_null.c | 1 + net/sunrpc/auth_unix.c | 5 ++++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index c51e1893f77e..359dfdd04e77 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -77,8 +77,7 @@ struct rpc_auth { /* guess at number of u32's auth adds before * reply data; normally the verifier size: */ unsigned int au_rslack; - /* for gss, used to calculate au_rslack: */ - unsigned int au_verfsize; + unsigned int au_verfsize; /* size of reply verifier */ unsigned int au_flags; /* various flags */ const struct rpc_authops *au_ops; /* operations */ diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index fda454c9b594..731e7a482e18 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1016,6 +1016,7 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) auth = &gss_auth->rpc_auth; auth->au_cslack = GSS_CRED_SLACK >> 2; auth->au_rslack = GSS_VERF_SLACK >> 2; + auth->au_verfsize = GSS_VERF_SLACK >> 2; auth->au_flags = 0; auth->au_ops = &authgss_ops; auth->au_flavor = flavor; diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index bf96975ffc4b..9ae08248a9e1 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -114,6 +114,7 @@ static struct rpc_auth null_auth = { .au_cslack = NUL_CALLSLACK, .au_rslack = NUL_REPLYSLACK, + .au_verfsize = NUL_REPLYSLACK, .au_ops = &authnull_ops, .au_flavor = RPC_AUTH_NULL, .au_count = REFCOUNT_INIT(1), diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 5ea84a96f96e..a93c56442487 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -163,6 +163,7 @@ unx_refresh(struct rpc_task *task) static int unx_validate(struct rpc_task *task, struct xdr_stream *xdr) { + struct rpc_auth *auth = task->tk_rqstp->rq_cred->cr_auth; __be32 *p; u32 size; @@ -184,7 +185,8 @@ unx_validate(struct rpc_task *task, struct xdr_stream *xdr) if (!p) return -EIO; - task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2; + auth->au_verfsize = XDR_QUADLEN(size) + 2; + auth->au_rslack = XDR_QUADLEN(size) + 2; return 0; } @@ -212,6 +214,7 @@ static struct rpc_auth unix_auth = { .au_cslack = UNX_CALLSLACK, .au_rslack = NUL_REPLYSLACK, + .au_verfsize = NUL_REPLYSLACK, .au_ops = &authunix_ops, .au_flavor = RPC_AUTH_UNIX, .au_count = REFCOUNT_INIT(1), From ca8655483c8849953b993196ad6adc9370a75d66 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:42 -0800 Subject: [PATCH 1789/4277] i2c: tegra: sort all the include headers alphabetically This patch sorts all the include headers alphabetically for the I2C Tegra driver. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index c77adbbea0c7..79c6aa87499b 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -6,24 +6,21 @@ * Author: Colin Cross */ -#include -#include -#include #include +#include #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include - -#include +#include +#include +#include +#include +#include +#include +#include #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 From ce9562424501dee2ce44e6f22d8c9e82917f40d1 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:43 -0800 Subject: [PATCH 1790/4277] i2c: tegra: add bus clear Master Support Bus clear feature of Tegra I2C controller helps to recover from bus hang when I2C master loses the bus arbitration due to the slave device holding SDA LOW continuously for some unknown reasons. Per I2C specification, the device that held the bus LOW should release it within 9 clock pulses. During bus clear operation, Tegra I2C controller sends 9 clock pulses and terminates the transaction with STOP condition. Upon successful bus clear operation, bus goes to idle state and driver retries the transaction. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 81 ++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 79c6aa87499b..a46446dbc48a 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -51,6 +51,7 @@ #define I2C_FIFO_STATUS_RX_SHIFT 0 #define I2C_INT_MASK 0x064 #define I2C_INT_STATUS 0x068 +#define I2C_INT_BUS_CLR_DONE BIT(11) #define I2C_INT_PACKET_XFER_COMPLETE BIT(7) #define I2C_INT_ALL_PACKETS_XFER_COMPLETE BIT(6) #define I2C_INT_TX_FIFO_OVERFLOW BIT(5) @@ -93,6 +94,15 @@ #define I2C_HEADER_MASTER_ADDR_SHIFT 12 #define I2C_HEADER_SLAVE_ADDR_SHIFT 1 +#define I2C_BUS_CLEAR_CNFG 0x084 +#define I2C_BC_SCLK_THRESHOLD 9 +#define I2C_BC_SCLK_THRESHOLD_SHIFT 16 +#define I2C_BC_STOP_COND BIT(2) +#define I2C_BC_TERMINATE BIT(1) +#define I2C_BC_ENABLE BIT(0) +#define I2C_BUS_CLEAR_STATUS 0x088 +#define I2C_BC_STATUS BIT(0) + #define I2C_CONFIG_LOAD 0x08C #define I2C_MSTR_CONFIG_LOAD BIT(0) #define I2C_SLV_CONFIG_LOAD BIT(1) @@ -154,6 +164,8 @@ enum msg_end_type { * be transferred in one go. * @quirks: i2c adapter quirks for limiting write/read transfer size and not * allowing 0 length transfers. + * @supports_bus_clear: Bus Clear support to recover from bus hang during + * SDA stuck low from device for some unknown reasons. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -167,6 +179,7 @@ struct tegra_i2c_hw_feature { bool has_slcg_override_reg; bool has_mst_fifo; const struct i2c_adapter_quirks *quirks; + bool supports_bus_clear; }; /** @@ -640,6 +653,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) goto err; } + /* + * I2C transfer is terminated during the bus clear so skip + * processing the other interrupts. + */ + if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) + goto err; + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { if (i2c_dev->msg_buf_remaining) tegra_i2c_empty_rx_fifo(i2c_dev); @@ -668,6 +688,8 @@ err: tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | I2C_INT_RX_FIFO_DATA_REQ); + if (i2c_dev->hw->supports_bus_clear) + tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); i2c_writel(i2c_dev, status, I2C_INT_STATUS); if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); @@ -678,6 +700,44 @@ done: return IRQ_HANDLED; } +static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) +{ + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int err; + unsigned long time_left; + u32 reg; + + reinit_completion(&i2c_dev->msg_complete); + reg = (I2C_BC_SCLK_THRESHOLD << I2C_BC_SCLK_THRESHOLD_SHIFT) | + I2C_BC_STOP_COND | I2C_BC_TERMINATE; + i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); + if (i2c_dev->hw->has_config_load_reg) { + err = tegra_i2c_wait_for_config_load(i2c_dev); + if (err) + return err; + } + + reg |= I2C_BC_ENABLE; + i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); + tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); + + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, + TEGRA_I2C_TIMEOUT); + if (time_left == 0) { + dev_err(i2c_dev->dev, "timed out for bus clear\n"); + return -ETIMEDOUT; + } + + reg = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS); + if (!(reg & I2C_BC_STATUS)) { + dev_err(i2c_dev->dev, + "un-recovered arbitration lost\n"); + return -EIO; + } + + return -EAGAIN; +} + static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *msg, enum msg_end_type end_state) { @@ -759,6 +819,13 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, return 0; tegra_i2c_init(i2c_dev); + /* start recovery upon arbitration loss in single master mode */ + if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { + if (!i2c_dev->is_multimaster_mode) + return i2c_recover_bus(&i2c_dev->adapter); + return -EAGAIN; + } + if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { if (msg->flags & I2C_M_IGNORE_NAK) return 0; @@ -841,6 +908,10 @@ static const struct i2c_adapter_quirks tegra194_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, }; +static struct i2c_bus_recovery_info tegra_i2c_recovery_info = { + .recover_bus = tegra_i2c_issue_bus_clear, +}; + static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_continue_xfer_support = false, .has_per_pkt_xfer_complete_irq = false, @@ -853,6 +924,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -867,6 +939,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -881,6 +954,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -895,6 +969,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -909,6 +984,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -923,6 +999,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, + .supports_bus_clear = true, }; /* Match table for of_platform binding */ @@ -974,6 +1051,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->base = base; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.retries = 1; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; @@ -1051,6 +1129,9 @@ static int tegra_i2c_probe(struct platform_device *pdev) } } + if (i2c_dev->hw->supports_bus_clear) + i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; + ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); From f4e3f4ae1d9c9330de355f432b69952e8cef650c Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:44 -0800 Subject: [PATCH 1791/4277] i2c: tegra: fix maximum transfer size Tegra186 and prior supports maximum 4K bytes per packet transfer including 12 bytes of packet header. This patch fixes max write length limit to account packet header size for transfers. Cc: stable@vger.kernel.org # 4.4+ Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index a46446dbc48a..036cab795426 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -901,7 +901,7 @@ static const struct i2c_algorithm tegra_i2c_algo = { static const struct i2c_adapter_quirks tegra_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, .max_read_len = 4096, - .max_write_len = 4096, + .max_write_len = 4096 - 12, }; static const struct i2c_adapter_quirks tegra194_i2c_quirks = { From b03ff2a23359d0dd6f0a1516c6a9e9c4760ed230 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:45 -0800 Subject: [PATCH 1792/4277] i2c: tegra: update maximum transfer size Tegra194 supports maximum 64K bytes per packet including 12 bytes of packet header irrespective of PIO or DMA mode transfer. This patch updates Tegra194 max write length to account for packet header size for transfers. Cc: stable@vger.kernel.org # 4.20+ Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 036cab795426..f8265bd5d02c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -125,6 +125,9 @@ #define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 #define I2C_MST_FIFO_STATUS_TX_SHIFT 16 +/* Packet header size in bytes */ +#define I2C_PACKET_HEADER_SIZE 12 + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -900,12 +903,13 @@ static const struct i2c_algorithm tegra_i2c_algo = { /* payload size is only 12 bit */ static const struct i2c_adapter_quirks tegra_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, - .max_read_len = 4096, - .max_write_len = 4096 - 12, + .max_read_len = SZ_4K, + .max_write_len = SZ_4K - I2C_PACKET_HEADER_SIZE, }; static const struct i2c_adapter_quirks tegra194_i2c_quirks = { .flags = I2C_AQ_NO_ZERO_LEN, + .max_write_len = SZ_64K - I2C_PACKET_HEADER_SIZE, }; static struct i2c_bus_recovery_info tegra_i2c_recovery_info = { From 86c92b9965ff1758952cd0d6c5f19eeeef291eea Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:46 -0800 Subject: [PATCH 1793/4277] i2c: tegra: Add DMA support This patch adds DMA support for Tegra I2C. Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for transfer size of the max FIFO depth and DMA mode is used for transfer size higher than max FIFO depth to save CPU overhead. PIO mode needs full intervention of CPU to fill or empty FIFO's and also need to service multiple data requests interrupt for the same transaction. This adds delay between data bytes of the same transfer when CPU is fully loaded and some slave devices has internal timeout for no bus activity and stops transaction to avoid bus hang. DMA mode is helpful in such cases. DMA mode is also helpful for Large transfers during downloading or uploading FW over I2C to some external devices. Tegra210 and prior Tegra chips use APBDMA driver which is replaced with GPCDMA on Tegra186 and Tegra194. This patch uses has_apb_dma flag in hw_feature to differentiate DMA driver change between Tegra chipset. APBDMA driver is registered from module-init level and this patch also has a change to register I2C driver at module-init level rather than subsys-init to avoid deferring I2C probe till APBDMA driver is registered. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 430 +++++++++++++++++++++++++++++---- 1 file changed, 382 insertions(+), 48 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index f8265bd5d02c..3b923411c9a7 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -42,8 +44,8 @@ #define I2C_FIFO_CONTROL 0x05c #define I2C_FIFO_CONTROL_TX_FLUSH BIT(1) #define I2C_FIFO_CONTROL_RX_FLUSH BIT(0) -#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5 -#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2 +#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5) +#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2) #define I2C_FIFO_STATUS 0x060 #define I2C_FIFO_STATUS_TX_MASK 0xF0 #define I2C_FIFO_STATUS_TX_SHIFT 4 @@ -128,6 +130,13 @@ /* Packet header size in bytes */ #define I2C_PACKET_HEADER_SIZE 12 +/* + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode, + * above this, controller will use DMA to fill FIFO. + * MAX PIO len is 20 bytes excluding packet header. + */ +#define I2C_PIO_MODE_MAX_LEN 32 + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -169,6 +178,7 @@ enum msg_end_type { * allowing 0 length transfers. * @supports_bus_clear: Bus Clear support to recover from bus hang during * SDA stuck low from device for some unknown reasons. + * @has_apb_dma: Support of APBDMA on corresponding Tegra chip. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -183,6 +193,7 @@ struct tegra_i2c_hw_feature { bool has_mst_fifo; const struct i2c_adapter_quirks *quirks; bool supports_bus_clear; + bool has_apb_dma; }; /** @@ -194,6 +205,7 @@ struct tegra_i2c_hw_feature { * @fast_clk: clock reference for fast clock of I2C controller * @rst: reset control for the I2C controller * @base: ioremapped registers cookie + * @base_phys: physical base address of the I2C controller * @cont_id: I2C controller ID, used for packet header * @irq: IRQ number of transfer complete interrupt * @irq_disabled: used to track whether or not the interrupt is enabled @@ -207,6 +219,13 @@ struct tegra_i2c_hw_feature { * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes * @is_multimaster_mode: track if I2C controller is in multi-master mode * @xfer_lock: lock to serialize transfer submission and processing + * @tx_dma_chan: DMA transmit channel + * @rx_dma_chan: DMA receive channel + * @dma_phys: handle to DMA resources + * @dma_buf: pointer to allocated DMA buffer + * @dma_buf_size: DMA buffer size + * @is_curr_dma_xfer: indicates active DMA transfer + * @dma_complete: DMA completion notifier */ struct tegra_i2c_dev { struct device *dev; @@ -216,6 +235,7 @@ struct tegra_i2c_dev { struct clk *fast_clk; struct reset_control *rst; void __iomem *base; + phys_addr_t base_phys; int cont_id; int irq; bool irq_disabled; @@ -229,6 +249,13 @@ struct tegra_i2c_dev { u16 clk_divisor_non_hs_mode; bool is_multimaster_mode; spinlock_t xfer_lock; + struct dma_chan *tx_dma_chan; + struct dma_chan *rx_dma_chan; + dma_addr_t dma_phys; + u32 *dma_buf; + unsigned int dma_buf_size; + bool is_curr_dma_xfer; + struct completion dma_complete; }; static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, @@ -297,6 +324,109 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); } +static void tegra_i2c_dma_complete(void *args) +{ + struct tegra_i2c_dev *i2c_dev = args; + + complete(&i2c_dev->dma_complete); +} + +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len) +{ + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction dir; + struct dma_chan *chan; + + dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len); + reinit_completion(&i2c_dev->dma_complete); + dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan; + dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys, + len, dir, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!dma_desc) { + dev_err(i2c_dev->dev, "failed to get DMA descriptor\n"); + return -EINVAL; + } + + dma_desc->callback = tegra_i2c_dma_complete; + dma_desc->callback_param = i2c_dev; + dmaengine_submit(dma_desc); + dma_async_issue_pending(chan); + return 0; +} + +static void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev) +{ + if (i2c_dev->dma_buf) { + dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size, + i2c_dev->dma_buf, i2c_dev->dma_phys); + i2c_dev->dma_buf = NULL; + } + + if (i2c_dev->tx_dma_chan) { + dma_release_channel(i2c_dev->tx_dma_chan); + i2c_dev->tx_dma_chan = NULL; + } + + if (i2c_dev->rx_dma_chan) { + dma_release_channel(i2c_dev->rx_dma_chan); + i2c_dev->rx_dma_chan = NULL; + } +} + +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) +{ + struct dma_chan *chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int err; + + if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA) || + !i2c_dev->hw->has_apb_dma) { + err = -ENODEV; + goto err_out; + } + + chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); + goto err_out; + } + + i2c_dev->rx_dma_chan = chan; + + chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); + goto err_out; + } + + i2c_dev->tx_dma_chan = chan; + + dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size, + &dma_phys, GFP_KERNEL | __GFP_NOWARN); + if (!dma_buf) { + dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n"); + err = -ENOMEM; + goto err_out; + } + + i2c_dev->dma_buf = dma_buf; + i2c_dev->dma_phys = dma_phys; + return 0; + +err_out: + tegra_i2c_release_dma(i2c_dev); + if (err != -EPROBE_DEFER) { + dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err); + dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + return 0; + } + + return err; +} + static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) { unsigned long timeout = jiffies + HZ; @@ -574,16 +704,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); } - if (i2c_dev->hw->has_mst_fifo) { - val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | - I2C_MST_FIFO_CONTROL_RX_TRIG(1); - i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL); - } else { - val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | - 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; - i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); - } - err = tegra_i2c_flush_fifos(i2c_dev); if (err) goto err; @@ -663,25 +783,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) goto err; - if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { - if (i2c_dev->msg_buf_remaining) - tegra_i2c_empty_rx_fifo(i2c_dev); - else - BUG(); - } + if (!i2c_dev->is_curr_dma_xfer) { + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_empty_rx_fifo(i2c_dev); + else + BUG(); + } - if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { - if (i2c_dev->msg_buf_remaining) - tegra_i2c_fill_tx_fifo(i2c_dev); - else - tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ); + if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_fill_tx_fifo(i2c_dev); + else + tegra_i2c_mask_irq(i2c_dev, + I2C_INT_TX_FIFO_DATA_REQ); + } } i2c_writel(i2c_dev, status, I2C_INT_STATUS); if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + /* + * During message read XFER_COMPLETE interrupt is triggered prior to + * DMA completion and during message write XFER_COMPLETE interrupt is + * triggered after DMA completion. + * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer. + * so forcing msg_buf_remaining to 0 in DMA mode. + */ if (status & I2C_INT_PACKET_XFER_COMPLETE) { + if (i2c_dev->is_curr_dma_xfer) + i2c_dev->msg_buf_remaining = 0; BUG_ON(i2c_dev->msg_buf_remaining); complete(&i2c_dev->msg_complete); } @@ -697,12 +829,91 @@ err: if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + if (i2c_dev->is_curr_dma_xfer) { + if (i2c_dev->msg_read) + dmaengine_terminate_async(i2c_dev->rx_dma_chan); + else + dmaengine_terminate_async(i2c_dev->tx_dma_chan); + + complete(&i2c_dev->dma_complete); + } + complete(&i2c_dev->msg_complete); done: spin_unlock(&i2c_dev->xfer_lock); return IRQ_HANDLED; } +static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, + size_t len) +{ + u32 val, reg; + u8 dma_burst; + struct dma_slave_config slv_config = {0}; + struct dma_chan *chan; + int ret; + unsigned long reg_offset; + + if (i2c_dev->hw->has_mst_fifo) + reg = I2C_MST_FIFO_CONTROL; + else + reg = I2C_FIFO_CONTROL; + + if (i2c_dev->is_curr_dma_xfer) { + if (len & 0xF) + dma_burst = 1; + else if (len & 0x10) + dma_burst = 4; + else + dma_burst = 8; + + if (i2c_dev->msg_read) { + chan = i2c_dev->rx_dma_chan; + reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO); + slv_config.src_addr = i2c_dev->base_phys + reg_offset; + slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slv_config.src_maxburst = dma_burst; + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst); + else + val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst); + } else { + chan = i2c_dev->tx_dma_chan; + reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO); + slv_config.dst_addr = i2c_dev->base_phys + reg_offset; + slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slv_config.dst_maxburst = dma_burst; + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst); + else + val = I2C_FIFO_CONTROL_TX_TRIG(dma_burst); + } + + slv_config.device_fc = true; + ret = dmaengine_slave_config(chan, &slv_config); + if (ret < 0) { + dev_err(i2c_dev->dev, "DMA slave config failed: %d\n", + ret); + dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + tegra_i2c_release_dma(i2c_dev); + i2c_dev->is_curr_dma_xfer = false; + } else { + goto out; + } + } + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | + I2C_MST_FIFO_CONTROL_RX_TRIG(1); + else + val = I2C_FIFO_CONTROL_TX_TRIG(8) | + I2C_FIFO_CONTROL_RX_TRIG(1); +out: + i2c_writel(i2c_dev, val, reg); +} + static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) { struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); @@ -748,6 +959,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, u32 int_mask; unsigned long time_left; unsigned long flags; + size_t xfer_size; + u32 *buffer = NULL; + int err = 0; + bool dma; tegra_i2c_flush_fifos(i2c_dev); @@ -757,19 +972,57 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_dev->msg_read = (msg->flags & I2C_M_RD); reinit_completion(&i2c_dev->msg_complete); + if (i2c_dev->msg_read) + xfer_size = msg->len; + else + xfer_size = msg->len + I2C_PACKET_HEADER_SIZE; + + xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD); + i2c_dev->is_curr_dma_xfer = (xfer_size > I2C_PIO_MODE_MAX_LEN) && + i2c_dev->dma_buf; + tegra_i2c_config_fifo_trig(i2c_dev, xfer_size); + dma = i2c_dev->is_curr_dma_xfer; spin_lock_irqsave(&i2c_dev->xfer_lock, flags); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; tegra_i2c_unmask_irq(i2c_dev, int_mask); + if (dma) { + if (i2c_dev->msg_read) { + dma_sync_single_for_device(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_FROM_DEVICE); + err = tegra_i2c_dma_submit(i2c_dev, xfer_size); + if (err < 0) { + dev_err(i2c_dev->dev, + "starting RX DMA failed, err %d\n", + err); + goto unlock; + } + + } else { + dma_sync_single_for_cpu(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_TO_DEVICE); + buffer = i2c_dev->dma_buf; + } + } packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | PACKET_HEADER0_PROTOCOL_I2C | (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | (1 << PACKET_HEADER0_PACKET_ID_SHIFT); - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = msg->len - 1; - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = I2C_HEADER_IE_ENABLE; if (end_state == MSG_END_CONTINUE) @@ -786,23 +1039,77 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, packet_header |= I2C_HEADER_CONT_ON_NAK; if (msg->flags & I2C_M_RD) packet_header |= I2C_HEADER_READ; - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); - if (!(msg->flags & I2C_M_RD)) - tegra_i2c_fill_tx_fifo(i2c_dev); + if (!i2c_dev->msg_read) { + if (dma) { + memcpy(buffer, msg->buf, msg->len); + dma_sync_single_for_device(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_TO_DEVICE); + err = tegra_i2c_dma_submit(i2c_dev, xfer_size); + if (err < 0) { + dev_err(i2c_dev->dev, + "starting TX DMA failed, err %d\n", + err); + goto unlock; + } + } else { + tegra_i2c_fill_tx_fifo(i2c_dev); + } + } if (i2c_dev->hw->has_per_pkt_xfer_complete_irq) int_mask |= I2C_INT_PACKET_XFER_COMPLETE; - if (msg->flags & I2C_M_RD) - int_mask |= I2C_INT_RX_FIFO_DATA_REQ; - else if (i2c_dev->msg_buf_remaining) - int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + if (!dma) { + if (msg->flags & I2C_M_RD) + int_mask |= I2C_INT_RX_FIFO_DATA_REQ; + else if (i2c_dev->msg_buf_remaining) + int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + } tegra_i2c_unmask_irq(i2c_dev, int_mask); - spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", i2c_readl(i2c_dev, I2C_INT_MASK)); +unlock: + spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); + + if (dma) { + if (err) + return err; + + time_left = wait_for_completion_timeout( + &i2c_dev->dma_complete, + TEGRA_I2C_TIMEOUT); + if (time_left == 0) { + dev_err(i2c_dev->dev, "DMA transfer timeout\n"); + dmaengine_terminate_sync(i2c_dev->msg_read ? + i2c_dev->rx_dma_chan : + i2c_dev->tx_dma_chan); + tegra_i2c_init(i2c_dev); + return -ETIMEDOUT; + } + + if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) { + dma_sync_single_for_cpu(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_FROM_DEVICE); + memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, + msg->len); + } + + if (i2c_dev->msg_err != I2C_ERR_NONE) + dmaengine_synchronize(i2c_dev->msg_read ? + i2c_dev->rx_dma_chan : + i2c_dev->tx_dma_chan); + } + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); @@ -818,6 +1125,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, time_left, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); + i2c_dev->is_curr_dma_xfer = false; if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; @@ -929,6 +1237,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -944,6 +1253,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -959,6 +1269,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -974,6 +1285,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -989,6 +1301,23 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, +}; + +static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .has_single_clk_source = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_fast_plus_mode = 0x10, + .has_config_load_reg = true, + .has_multi_master_mode = true, + .has_slcg_override_reg = true, + .has_mst_fifo = true, + .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, + .has_apb_dma = false, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -1004,11 +1333,13 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = false, }; /* Match table for of_platform binding */ static const struct of_device_id tegra_i2c_of_match[] = { { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, + { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, @@ -1026,11 +1357,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) struct clk *div_clk; struct clk *fast_clk; void __iomem *base; + phys_addr_t base_phys; int irq; int ret = 0; int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base_phys = res->start; base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); @@ -1053,6 +1386,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) return -ENOMEM; i2c_dev->base = base; + i2c_dev->base_phys = base_phys; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.retries = 1; @@ -1072,7 +1406,10 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node, "nvidia,tegra20-i2c-dvc"); i2c_dev->adapter.quirks = i2c_dev->hw->quirks; + i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len + + I2C_PACKET_HEADER_SIZE; init_completion(&i2c_dev->msg_complete); + init_completion(&i2c_dev->dma_complete); spin_lock_init(&i2c_dev->xfer_lock); if (!i2c_dev->hw->has_single_clk_source) { @@ -1136,17 +1473,21 @@ static int tegra_i2c_probe(struct platform_device *pdev) if (i2c_dev->hw->supports_bus_clear) i2c_dev->adapter.bus_recovery_info = &tegra_i2c_recovery_info; + ret = tegra_i2c_init_dma(i2c_dev); + if (ret < 0) + goto disable_div_clk; + ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); - goto disable_div_clk; + goto release_dma; } ret = devm_request_irq(&pdev->dev, i2c_dev->irq, tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); - goto disable_div_clk; + goto release_dma; } i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); @@ -1160,10 +1501,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c_dev->adapter); if (ret) - goto disable_div_clk; + goto release_dma; return 0; +release_dma: + tegra_i2c_release_dma(i2c_dev); + disable_div_clk: if (i2c_dev->is_multimaster_mode) clk_disable(i2c_dev->div_clk); @@ -1200,6 +1544,7 @@ static int tegra_i2c_remove(struct platform_device *pdev) if (!i2c_dev->hw->has_single_clk_source) clk_unprepare(i2c_dev->fast_clk); + tegra_i2c_release_dma(i2c_dev); return 0; } @@ -1223,18 +1568,7 @@ static struct platform_driver tegra_i2c_driver = { }, }; -static int __init tegra_i2c_init_driver(void) -{ - return platform_driver_register(&tegra_i2c_driver); -} - -static void __exit tegra_i2c_exit_driver(void) -{ - platform_driver_unregister(&tegra_i2c_driver); -} - -subsys_initcall(tegra_i2c_init_driver); -module_exit(tegra_i2c_exit_driver); +module_platform_driver(tegra_i2c_driver); MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver"); MODULE_AUTHOR("Colin Cross"); From 35e77d21baa04b554bf3dc9a08dfa7e569286e51 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:36 -0500 Subject: [PATCH 1794/4277] SUNRPC: Add rpc_auth::au_ralign field Currently rpc_inline_rcv_pages() uses au_rslack to estimate the size of the upper layer reply header. This is fine for auth flavors where au_verfsize == au_rslack. However, some auth flavors have more going on. krb5i for example has two more words after the verifier, and another blob following the RPC message. The calculation involving au_rslack pushes the upper layer reply header too far into the rcv_buf. au_rslack is still valuable: it's the amount of buffer space needed for the reply, and is used when allocating the reply buffer. We'll keep that. But, add a new field that can be used to properly estimate the location of the upper layer header in each RPC reply, based on the auth flavor in use. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/auth.h | 9 ++++----- net/sunrpc/auth_gss/auth_gss.c | 18 +++++++++++++----- net/sunrpc/auth_null.c | 1 + net/sunrpc/auth_unix.c | 1 + net/sunrpc/clnt.c | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 359dfdd04e77..5f9076fdb090 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -74,13 +74,12 @@ struct rpc_cred_cache; struct rpc_authops; struct rpc_auth { unsigned int au_cslack; /* call cred size estimate */ - /* guess at number of u32's auth adds before - * reply data; normally the verifier size: */ - unsigned int au_rslack; + unsigned int au_rslack; /* reply cred size estimate */ unsigned int au_verfsize; /* size of reply verifier */ + unsigned int au_ralign; /* words before UL header */ - unsigned int au_flags; /* various flags */ - const struct rpc_authops *au_ops; /* operations */ + unsigned int au_flags; + const struct rpc_authops *au_ops; rpc_authflavor_t au_flavor; /* pseudoflavor (note may * differ from the flavor in * au_ops->au_flavor in gss diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 731e7a482e18..c67e2ad151ae 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1017,6 +1017,7 @@ gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) auth->au_cslack = GSS_CRED_SLACK >> 2; auth->au_rslack = GSS_VERF_SLACK >> 2; auth->au_verfsize = GSS_VERF_SLACK >> 2; + auth->au_ralign = GSS_VERF_SLACK >> 2; auth->au_flags = 0; auth->au_ops = &authgss_ops; auth->au_flavor = flavor; @@ -1891,7 +1892,10 @@ out: static int gss_unwrap_resp_auth(struct rpc_cred *cred) { - cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize; + struct rpc_auth *auth = cred->cr_auth; + + auth->au_rslack = auth->au_verfsize; + auth->au_ralign = auth->au_verfsize; return 0; } @@ -1902,6 +1906,7 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, { struct xdr_buf integ_buf, *rcv_buf = &rqstp->rq_rcv_buf; u32 data_offset, mic_offset, integ_len, maj_stat; + struct rpc_auth *auth = cred->cr_auth; struct xdr_netobj mic; __be32 *p; @@ -1928,8 +1933,8 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, if (maj_stat != GSS_S_COMPLETE) goto bad_mic; - cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 + - 1 + XDR_QUADLEN(mic.len); + auth->au_rslack = auth->au_verfsize + 2 + 1 + XDR_QUADLEN(mic.len); + auth->au_ralign = auth->au_verfsize + 2; return 0; unwrap_failed: trace_rpcgss_unwrap_failed(task); @@ -1949,6 +1954,7 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, { struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; struct kvec *head = rqstp->rq_rcv_buf.head; + struct rpc_auth *auth = cred->cr_auth; unsigned int savedlen = rcv_buf->len; u32 offset, opaque_len, maj_stat; __be32 *p; @@ -1976,8 +1982,10 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, */ xdr_init_decode(xdr, rcv_buf, p, rqstp); - cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + 2 + - XDR_QUADLEN(savedlen - rcv_buf->len); + auth->au_rslack = auth->au_verfsize + 2 + + XDR_QUADLEN(savedlen - rcv_buf->len); + auth->au_ralign = auth->au_verfsize + 2 + + XDR_QUADLEN(savedlen - rcv_buf->len); return 0; unwrap_failed: trace_rpcgss_unwrap_failed(task); diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 9ae08248a9e1..41a633a4049e 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -115,6 +115,7 @@ struct rpc_auth null_auth = { .au_cslack = NUL_CALLSLACK, .au_rslack = NUL_REPLYSLACK, .au_verfsize = NUL_REPLYSLACK, + .au_ralign = NUL_REPLYSLACK, .au_ops = &authnull_ops, .au_flavor = RPC_AUTH_NULL, .au_count = REFCOUNT_INIT(1), diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index a93c56442487..c048eb6deaaf 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -187,6 +187,7 @@ unx_validate(struct rpc_task *task, struct xdr_stream *xdr) auth->au_verfsize = XDR_QUADLEN(size) + 2; auth->au_rslack = XDR_QUADLEN(size) + 2; + auth->au_ralign = XDR_QUADLEN(size) + 2; return 0; } diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 4ea38b029e2f..99bfeb17367c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1180,7 +1180,7 @@ void rpc_prepare_reply_pages(struct rpc_rqst *req, struct page **pages, /* Subtract one to force an extra word of buffer space for the * payload's XDR pad to fall into the rcv_buf's tail iovec. */ - hdrsize += RPC_REPHDRSIZE + req->rq_cred->cr_auth->au_rslack - 1; + hdrsize += RPC_REPHDRSIZE + req->rq_cred->cr_auth->au_ralign - 1; xdr_inline_pages(&req->rq_rcv_buf, hdrsize << 2, pages, base, len); trace_rpc_reply_pages(req); From 80d40626cc76f65f2699573a95ecc90a31dcf50e Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:47 -0800 Subject: [PATCH 1795/4277] i2c: tegra: update transfer timeout Tegra194 allows max of 64K bytes and Tegra186 and prior allows max of 4K bytes of transfer per packet. one sec timeout is not enough for transfers more than 10K bytes at STD bus rate. This patch updates I2C transfer timeout based on the transfer size and I2C bus rate to allow enough time during max transfer size at lower bus speed. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 3b923411c9a7..cfecd81789c3 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -24,7 +24,6 @@ #include #include -#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 #define I2C_CNFG 0x000 @@ -936,7 +935,7 @@ static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(50)); if (time_left == 0) { dev_err(i2c_dev->dev, "timed out for bus clear\n"); return -ETIMEDOUT; @@ -963,6 +962,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, u32 *buffer = NULL; int err = 0; bool dma; + u16 xfer_time = 100; tegra_i2c_flush_fifos(i2c_dev); @@ -982,6 +982,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_dev->dma_buf; tegra_i2c_config_fifo_trig(i2c_dev, xfer_size); dma = i2c_dev->is_curr_dma_xfer; + /* + * Transfer time in mSec = Total bits / transfer rate + * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits + */ + xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC, + i2c_dev->bus_clk_rate); spin_lock_irqsave(&i2c_dev->xfer_lock, flags); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; @@ -1085,7 +1091,7 @@ unlock: time_left = wait_for_completion_timeout( &i2c_dev->dma_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(xfer_time)); if (time_left == 0) { dev_err(i2c_dev->dev, "DMA transfer timeout\n"); dmaengine_terminate_sync(i2c_dev->msg_read ? @@ -1111,7 +1117,7 @@ unlock: } time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(xfer_time)); tegra_i2c_mask_irq(i2c_dev, int_mask); if (time_left == 0) { @@ -1390,6 +1396,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.retries = 1; + i2c_dev->adapter.timeout = 6 * HZ; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; From 0940d24912e9256fdf172f84c54ffd91680f05d0 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 12 Feb 2019 11:06:48 -0800 Subject: [PATCH 1796/4277] i2c: tegra: add i2c interface timing support This patch adds I2C interface timing registers support for proper bus rate configuration along with meeting the I2C spec setup and hold times based on the tuning performed on Tegra210, Tegra186 and Tegra194 platforms. I2C_INTERFACE_TIMING_0 register contains TLOW and THIGH field and Tegra I2C controller design uses them as a part of internal clock divisor. I2C_INTERFACE_TIMING_1 register contains the setup and hold times for start and stop conditions. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 189 +++++++++++++++++++++++++++------ 1 file changed, 159 insertions(+), 30 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index cfecd81789c3..a4cd79c9f7a7 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -126,6 +126,15 @@ #define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 #define I2C_MST_FIFO_STATUS_TX_SHIFT 16 +#define I2C_INTERFACE_TIMING_0 0x94 +#define I2C_THIGH_SHIFT 8 +#define I2C_INTERFACE_TIMING_1 0x98 + +#define I2C_STANDARD_MODE 100000 +#define I2C_FAST_MODE 400000 +#define I2C_FAST_PLUS_MODE 1000000 +#define I2C_HS_MODE 3500000 + /* Packet header size in bytes */ #define I2C_PACKET_HEADER_SIZE 12 @@ -160,7 +169,10 @@ enum msg_end_type { * @has_config_load_reg: Has the config load register to load the new * configuration. * @clk_divisor_hs_mode: Clock divisor in HS mode. - * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is + * @clk_divisor_std_mode: Clock divisor in standard mode. It is + * applicable if there is no fast clock source i.e. single clock + * source. + * @clk_divisor_fast_mode: Clock divisor in fast mode. It is * applicable if there is no fast clock source i.e. single clock * source. * @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is @@ -178,6 +190,18 @@ enum msg_end_type { * @supports_bus_clear: Bus Clear support to recover from bus hang during * SDA stuck low from device for some unknown reasons. * @has_apb_dma: Support of APBDMA on corresponding Tegra chip. + * @tlow_std_mode: Low period of the clock in standard mode. + * @thigh_std_mode: High period of the clock in standard mode. + * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes. + * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes. + * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions + * in standard mode. + * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop + * conditions in fast/fast-plus modes. + * @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions + * in HS mode. + * @has_interface_timing_reg: Has interface timing register to program the tuned + * timing settings. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -185,7 +209,8 @@ struct tegra_i2c_hw_feature { bool has_single_clk_source; bool has_config_load_reg; int clk_divisor_hs_mode; - int clk_divisor_std_fast_mode; + int clk_divisor_std_mode; + int clk_divisor_fast_mode; u16 clk_divisor_fast_plus_mode; bool has_multi_master_mode; bool has_slcg_override_reg; @@ -193,6 +218,14 @@ struct tegra_i2c_hw_feature { const struct i2c_adapter_quirks *quirks; bool supports_bus_clear; bool has_apb_dma; + u8 tlow_std_mode; + u8 thigh_std_mode; + u8 tlow_fast_fastplus_mode; + u8 thigh_fast_fastplus_mode; + u32 setup_hold_time_std_mode; + u32 setup_hold_time_fast_fast_plus_mode; + u32 setup_hold_time_hs_mode; + bool has_interface_timing_reg; }; /** @@ -660,11 +693,13 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev) return 0; } -static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) +static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev, bool clk_reinit) { u32 val; int err; - u32 clk_divisor; + u32 clk_divisor, clk_multiplier; + u32 tsu_thd = 0; + u8 tlow, thigh; err = pm_runtime_get_sync(i2c_dev->dev); if (err < 0) { @@ -694,6 +729,41 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT; i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR); + if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE && + i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE) { + tlow = i2c_dev->hw->tlow_fast_fastplus_mode; + thigh = i2c_dev->hw->thigh_fast_fastplus_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode; + } else { + tlow = i2c_dev->hw->tlow_std_mode; + thigh = i2c_dev->hw->thigh_std_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_std_mode; + } + + if (i2c_dev->hw->has_interface_timing_reg) { + val = (thigh << I2C_THIGH_SHIFT) | tlow; + i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0); + } + + /* + * configure setup and hold times only when tsu_thd is non-zero. + * otherwise, preserve the chip default values + */ + if (i2c_dev->hw->has_interface_timing_reg && tsu_thd) + i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1); + + if (!clk_reinit) { + clk_multiplier = (tlow + thigh + 2); + clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1); + err = clk_set_rate(i2c_dev->div_clk, + i2c_dev->bus_clk_rate * clk_multiplier); + if (err) { + dev_err(i2c_dev->dev, + "failed changing clock rate: %d\n", err); + goto err; + } + } + if (!i2c_dev->is_dvc) { u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); @@ -1097,7 +1167,7 @@ unlock: dmaengine_terminate_sync(i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan); - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); return -ETIMEDOUT; } @@ -1123,7 +1193,7 @@ unlock: if (time_left == 0) { dev_err(i2c_dev->dev, "i2c transfer timed out\n"); - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); return -ETIMEDOUT; } @@ -1135,7 +1205,7 @@ unlock: if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); /* start recovery upon arbitration loss in single master mode */ if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { if (!i2c_dev->is_multimaster_mode) @@ -1235,7 +1305,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_per_pkt_xfer_complete_irq = false, .has_single_clk_source = false, .clk_divisor_hs_mode = 3, - .clk_divisor_std_fast_mode = 0, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1244,6 +1315,14 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -1251,7 +1330,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_per_pkt_xfer_complete_irq = false, .has_single_clk_source = false, .clk_divisor_hs_mode = 3, - .clk_divisor_std_fast_mode = 0, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1260,6 +1340,14 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -1267,7 +1355,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1276,6 +1365,14 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -1283,7 +1380,8 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = false, @@ -1292,6 +1390,14 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -1299,7 +1405,8 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -1308,6 +1415,14 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_fast_plus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { @@ -1315,7 +1430,8 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x16, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -1324,6 +1440,14 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = false, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x3, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_fast_plus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -1331,8 +1455,9 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, - .clk_divisor_fast_plus_mode = 0x10, + .clk_divisor_std_mode = 0x4f, + .clk_divisor_fast_mode = 0x3c, + .clk_divisor_fast_plus_mode = 0x16, .has_config_load_reg = true, .has_multi_master_mode = true, .has_slcg_override_reg = true, @@ -1340,6 +1465,14 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .quirks = &tegra194_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = false, + .tlow_std_mode = 0x8, + .thigh_std_mode = 0x7, + .tlow_fast_fastplus_mode = 0x2, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_fast_plus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x090909, + .has_interface_timing_reg = true, }; /* Match table for of_platform binding */ @@ -1366,7 +1499,6 @@ static int tegra_i2c_probe(struct platform_device *pdev) phys_addr_t base_phys; int irq; int ret = 0; - int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base_phys = res->start; @@ -1438,20 +1570,17 @@ static int tegra_i2c_probe(struct platform_device *pdev) } } - i2c_dev->clk_divisor_non_hs_mode = - i2c_dev->hw->clk_divisor_std_fast_mode; - if (i2c_dev->hw->clk_divisor_fast_plus_mode && - (i2c_dev->bus_clk_rate == 1000000)) + if (i2c_dev->bus_clk_rate > I2C_FAST_MODE && + i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE) i2c_dev->clk_divisor_non_hs_mode = - i2c_dev->hw->clk_divisor_fast_plus_mode; - - clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1); - ret = clk_set_rate(i2c_dev->div_clk, - i2c_dev->bus_clk_rate * clk_multiplier); - if (ret) { - dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret); - goto unprepare_fast_clk; - } + i2c_dev->hw->clk_divisor_fast_plus_mode; + else if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE && + i2c_dev->bus_clk_rate <= I2C_FAST_MODE) + i2c_dev->clk_divisor_non_hs_mode = + i2c_dev->hw->clk_divisor_fast_mode; + else + i2c_dev->clk_divisor_non_hs_mode = + i2c_dev->hw->clk_divisor_std_mode; ret = clk_prepare(i2c_dev->div_clk); if (ret < 0) { @@ -1484,7 +1613,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) if (ret < 0) goto disable_div_clk; - ret = tegra_i2c_init(i2c_dev); + ret = tegra_i2c_init(i2c_dev, false); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); goto release_dma; From e7663ef5ae0f02e3b902eb0305dec981333eb3e1 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:30 +0100 Subject: [PATCH 1797/4277] i2c: ocores: stop transfer on timeout Detecting a timeout is ok, but we also need to assert a STOP command on the bus in order to prevent it from generating interrupts when there are no on going transfers. Example: very long transmission. 1. ocores_xfer: START a transfer 2. ocores_isr : handle byte by byte the transfer 3. ocores_xfer: goes in timeout [[bugfix here]] 4. ocores_xfer: return to I2C subsystem and to the I2C driver 5. I2C driver : it may clean up the i2c_msg memory 6. ocores_isr : receives another interrupt (pending bytes to be transferred) but the i2c_msg memory is invalid now So, since the transfer was too long, we have to detect the timeout and STOP the transfer. Another point is that we have a critical region here. When handling the timeout condition we may have a running IRQ handler. For this reason I introduce a spinlock. In order to make easier to understan locking I have: - added a new function to handle timeout - modified the current ocores_process() function in order to be protected by the new spinlock Like this it is obvious at first sight that this locking serializes the execution of ocores_process() and ocores_process_timeout() Signed-off-by: Federico Vaga Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 54 +++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 87f9caacba85..aa852028d8c1 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -25,7 +25,12 @@ #include #include #include +#include +/** + * @process_lock: protect I2C transfer process. + * ocores_process() and ocores_process_timeout() can't run in parallel. + */ struct ocores_i2c { void __iomem *base; u32 reg_shift; @@ -36,6 +41,7 @@ struct ocores_i2c { int pos; int nmsgs; int state; /* see STATE_ */ + spinlock_t process_lock; struct clk *clk; int ip_clock_khz; int bus_clock_khz; @@ -141,19 +147,26 @@ static void ocores_process(struct ocores_i2c *i2c) { struct i2c_msg *msg = i2c->msg; u8 stat = oc_getreg(i2c, OCI2C_STATUS); + unsigned long flags; + + /* + * If we spin here is because we are in timeout, so we are going + * to be in STATE_ERROR. See ocores_process_timeout() + */ + spin_lock_irqsave(&i2c->process_lock, flags); if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { /* stop has been sent */ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); wake_up(&i2c->wait); - return; + goto out; } /* error? */ if (stat & OCI2C_STAT_ARBLOST) { i2c->state = STATE_ERROR; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); - return; + goto out; } if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { @@ -163,7 +176,7 @@ static void ocores_process(struct ocores_i2c *i2c) if (stat & OCI2C_STAT_NACK) { i2c->state = STATE_ERROR; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); - return; + goto out; } } else msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); @@ -184,14 +197,14 @@ static void ocores_process(struct ocores_i2c *i2c) oc_setreg(i2c, OCI2C_DATA, addr); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); - return; + goto out; } else i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; } else { i2c->state = STATE_DONE; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); - return; + goto out; } } @@ -202,6 +215,9 @@ static void ocores_process(struct ocores_i2c *i2c) oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); } + +out: + spin_unlock_irqrestore(&i2c->process_lock, flags); } static irqreturn_t ocores_isr(int irq, void *dev_id) @@ -213,9 +229,24 @@ static irqreturn_t ocores_isr(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * Process timeout event + * @i2c: ocores I2C device instance + */ +static void ocores_process_timeout(struct ocores_i2c *i2c) +{ + unsigned long flags; + + spin_lock_irqsave(&i2c->process_lock, flags); + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + spin_unlock_irqrestore(&i2c->process_lock, flags); +} + static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct ocores_i2c *i2c = i2c_get_adapdata(adap); + int ret; i2c->msg = msgs; i2c->pos = 0; @@ -225,11 +256,14 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); - if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || - (i2c->state == STATE_DONE), HZ)) - return (i2c->state == STATE_DONE) ? num : -EIO; - else + ret = wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ); + if (ret == 0) { + ocores_process_timeout(i2c); return -ETIMEDOUT; + } + + return (i2c->state == STATE_DONE) ? num : -EIO; } static int ocores_init(struct device *dev, struct ocores_i2c *i2c) @@ -422,6 +456,8 @@ static int ocores_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; + spin_lock_init(&i2c->process_lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(i2c->base)) From 2dc9834688ae3ba057d46e60525a11ec146ae3df Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:31 +0100 Subject: [PATCH 1798/4277] i2c: ocores: do not handle IRQ if IF is not set If the Interrupt Flag (IF) is not set, we should not handle the IRQ: - the line can be shared with other devices - it can be a spurious interrupt To avoid reading twice the status register, the ocores_process() function expects it to be read by the caller. Signed-off-by: Federico Vaga Acked-by: Peter Korsgaard Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index aa852028d8c1..fcc25582c648 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -143,10 +143,9 @@ static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) return i2c->getreg(i2c, reg); } -static void ocores_process(struct ocores_i2c *i2c) +static void ocores_process(struct ocores_i2c *i2c, u8 stat) { struct i2c_msg *msg = i2c->msg; - u8 stat = oc_getreg(i2c, OCI2C_STATUS); unsigned long flags; /* @@ -223,8 +222,12 @@ out: static irqreturn_t ocores_isr(int irq, void *dev_id) { struct ocores_i2c *i2c = dev_id; + u8 stat = oc_getreg(i2c, OCI2C_STATUS); - ocores_process(i2c); + if (!(stat & OCI2C_STAT_IF)) + return IRQ_NONE; + + ocores_process(i2c, stat); return IRQ_HANDLED; } From 69c8c0c0efa84cc7c95ef774e97cac237b0f6d36 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:32 +0100 Subject: [PATCH 1799/4277] i2c: ocores: add polling interface This driver assumes that an interrupt line is always available for the I2C master. This is not always the case and this patch adds support for a polling version. Report from Andrew Lunn: I did some timing tests for this. On my box, we request a udelay of 80uS. The kernel actually delays for about 79uS. We then spin in ocores_wait() for an additional 10-11uS, which is 3 to 4 iterations. There are actually 9 bits on the wire, not 8, since there is an ACK/NACK bit after the actual data transfer. So i changed the delay to (9 * 1000) / i2c->bus_clock_khz. That resulted in ocores_wait() mostly not looping at all. But for reading an 4K AT24 EEPROM, it increased the read time by 10ms, from 424ms to 434ms. So we should probably keep with 8. Signed-off-by: Federico Vaga Tested-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 182 ++++++++++++++++++++++++++++---- 1 file changed, 161 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index fcc25582c648..5dea7b9ab7e5 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -26,6 +27,9 @@ #include #include #include +#include + +#define OCORES_FLAG_POLL BIT(0) /** * @process_lock: protect I2C transfer process. @@ -35,6 +39,7 @@ struct ocores_i2c { void __iomem *base; u32 reg_shift; u32 reg_io_width; + unsigned long flags; wait_queue_head_t wait; struct i2c_adapter adap; struct i2c_msg *msg; @@ -246,10 +251,116 @@ static void ocores_process_timeout(struct ocores_i2c *i2c) spin_unlock_irqrestore(&i2c->process_lock, flags); } -static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +/** + * Wait until something change in a given register + * @i2c: ocores I2C device instance + * @reg: register to query + * @mask: bitmask to apply on register value + * @val: expected result + * @timeout: timeout in jiffies + * + * Timeout is necessary to avoid to stay here forever when the chip + * does not answer correctly. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_wait(struct ocores_i2c *i2c, + int reg, u8 mask, u8 val, + const unsigned long timeout) +{ + unsigned long j; + + j = jiffies + timeout; + while (1) { + u8 status = oc_getreg(i2c, reg); + + if ((status & mask) == val) + break; + + if (time_after(jiffies, j)) + return -ETIMEDOUT; + } + return 0; +} + +/** + * Wait until is possible to process some data + * @i2c: ocores I2C device instance + * + * Used when the device is in polling mode (interrupts disabled). + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_poll_wait(struct ocores_i2c *i2c) +{ + u8 mask; + int err; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* transfer is over */ + mask = OCI2C_STAT_BUSY; + } else { + /* on going transfer */ + mask = OCI2C_STAT_TIP; + /* + * We wait for the data to be transferred (8bit), + * then we start polling on the ACK/NACK bit + */ + udelay((8 * 1000) / i2c->bus_clock_khz); + } + + /* + * once we are here we expect to get the expected result immediately + * so if after 1ms we timeout then something is broken. + */ + err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1)); + if (err) + dev_warn(i2c->adap.dev.parent, + "%s: STATUS timeout, bit 0x%x did not clear in 1ms\n", + __func__, mask); + return err; +} + +/** + * It handles an IRQ-less transfer + * @i2c: ocores I2C device instance + * + * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same + * (only that IRQ are not produced). This means that we can re-use entirely + * ocores_isr(), we just add our polling code around it. + * + * It can run in atomic context + */ +static void ocores_process_polling(struct ocores_i2c *i2c) +{ + while (1) { + irqreturn_t ret; + int err; + + err = ocores_poll_wait(i2c); + if (err) { + i2c->state = STATE_ERROR; + break; /* timeout */ + } + + ret = ocores_isr(-1, i2c); + if (ret == IRQ_NONE) + break; /* all messages have been transferred */ + } +} + +static int ocores_xfer_core(struct ocores_i2c *i2c, + struct i2c_msg *msgs, int num, + bool polling) { - struct ocores_i2c *i2c = i2c_get_adapdata(adap); int ret; + u8 ctrl; + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + if (polling) + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN); + else + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN); i2c->msg = msgs; i2c->pos = 0; @@ -259,16 +370,37 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); - ret = wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || - (i2c->state == STATE_DONE), HZ); - if (ret == 0) { - ocores_process_timeout(i2c); - return -ETIMEDOUT; + if (polling) { + ocores_process_polling(i2c); + } else { + ret = wait_event_timeout(i2c->wait, + (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ); + if (ret == 0) { + ocores_process_timeout(i2c); + return -ETIMEDOUT; + } } return (i2c->state == STATE_DONE) ? num : -EIO; } +static int ocores_xfer_polling(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true); +} + +static int ocores_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct ocores_i2c *i2c = i2c_get_adapdata(adap); + + if (i2c->flags & OCORES_FLAG_POLL) + return ocores_xfer_polling(adap, msgs, num); + return ocores_xfer_core(i2c, msgs, num, false); +} + static int ocores_init(struct device *dev, struct ocores_i2c *i2c) { int prescale; @@ -276,7 +408,8 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c) u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* make sure the device is disabled */ - oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1; prescale = clamp(prescale, 0, 0xffff); @@ -294,7 +427,7 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c) /* Init the device */ oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); - oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN); return 0; } @@ -451,10 +584,6 @@ static int ocores_i2c_probe(struct platform_device *pdev) int ret; int i; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; @@ -509,18 +638,29 @@ static int ocores_i2c_probe(struct platform_device *pdev) } } + init_waitqueue_head(&i2c->wait); + + irq = platform_get_irq(pdev, 0); + if (irq == -ENXIO) { + i2c->flags |= OCORES_FLAG_POLL; + } else { + if (irq < 0) + return irq; + } + + if (!(i2c->flags & OCORES_FLAG_POLL)) { + ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_clk; + } + } + ret = ocores_init(&pdev->dev, i2c); if (ret) goto err_clk; - init_waitqueue_head(&i2c->wait); - ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0, - pdev->name, i2c); - if (ret) { - dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto err_clk; - } - /* hook up driver to tree */ platform_set_drvdata(pdev, i2c); i2c->adap = ocores_adapter; From 2c7e4928b35660e2147d14d5e42849c22f44b55f Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:33 +0100 Subject: [PATCH 1800/4277] i2c: ocores: add SPDX tag It adds the SPDX tag and it removes the old text about the GPLv2. Signed-off-by: Federico Vaga Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 5 +---- include/linux/platform_data/i2c-ocores.h | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 5dea7b9ab7e5..78085a88d866 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * i2c-ocores.c: I2C bus driver for OpenCores I2C controller * (https://opencores.org/project/i2c/overview) @@ -6,10 +7,6 @@ * * Support for the GRLIB port of the controller by * Andreas Larsson - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/include/linux/platform_data/i2c-ocores.h b/include/linux/platform_data/i2c-ocores.h index 113d6b12f650..8c416ff8affd 100644 --- a/include/linux/platform_data/i2c-ocores.h +++ b/include/linux/platform_data/i2c-ocores.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * i2c-ocores.h - definitions for the i2c-ocores interface * * Peter Korsgaard - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _LINUX_I2C_OCORES_H From fac9c29fc7d5b57724b88f54d27e11b20404a2ec Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Feb 2019 09:51:34 +0100 Subject: [PATCH 1801/4277] i2c: ocores: checkpatch fixes Miscellaneous style fixes from checkpatch Signed-off-by: Federico Vaga Reviewed-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 78085a88d866..b32d67c90d67 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -179,8 +179,9 @@ static void ocores_process(struct ocores_i2c *i2c, u8 stat) oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); goto out; } - } else + } else { msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + } /* end of msg? */ if (i2c->pos == msg->len) { @@ -197,11 +198,11 @@ static void ocores_process(struct ocores_i2c *i2c, u8 stat) i2c->state = STATE_START; oc_setreg(i2c, OCI2C_DATA, addr); - oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); goto out; - } else - i2c->state = (msg->flags & I2C_M_RD) - ? STATE_READ : STATE_WRITE; + } + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; } else { i2c->state = STATE_DONE; oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); @@ -461,13 +462,16 @@ static const struct of_device_id ocores_i2c_match[] = { MODULE_DEVICE_TABLE(of, ocores_i2c_match); #ifdef CONFIG_OF -/* Read and write functions for the GRLIB port of the controller. Registers are +/* + * Read and write functions for the GRLIB port of the controller. Registers are * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one - * register. The subsequent registers has their offset decreased accordingly. */ + * register. The subsequent registers have their offsets decreased accordingly. + */ static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) { u32 rd; int rreg = reg; + if (reg != OCI2C_PRELOW) rreg--; rd = ioread32be(i2c->base + (rreg << i2c->reg_shift)); @@ -481,6 +485,7 @@ static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) { u32 curr, wr; int rreg = reg; + if (reg != OCI2C_PRELOW) rreg--; if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) { @@ -569,7 +574,7 @@ static int ocores_i2c_of_probe(struct platform_device *pdev, return 0; } #else -#define ocores_i2c_of_probe(pdev,i2c) -ENODEV +#define ocores_i2c_of_probe(pdev, i2c) -ENODEV #endif static int ocores_i2c_probe(struct platform_device *pdev) @@ -686,10 +691,11 @@ err_clk: static int ocores_i2c_remove(struct platform_device *pdev) { struct ocores_i2c *i2c = platform_get_drvdata(pdev); + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* disable i2c logic */ - oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL) - & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); /* remove adapter & data */ i2c_del_adapter(&i2c->adap); @@ -707,7 +713,8 @@ static int ocores_i2c_suspend(struct device *dev) u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); /* make sure the device is disabled */ - oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); if (!IS_ERR(i2c->clk)) clk_disable_unprepare(i2c->clk); From 809445d4b7fa7101d9434182bebbd8c84d7e4b65 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 14 Feb 2019 04:24:49 +0100 Subject: [PATCH 1802/4277] i2c: ocores: Add support for IO mapper registers. Some implementations of the OCORES i2c bus master use IO mapped registers. Add support for getting the IO registers from the platform data, and register accessor functions. Signed-off-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index b32d67c90d67..0d90a82a2c03 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -34,6 +34,7 @@ */ struct ocores_i2c { void __iomem *base; + int iobase; u32 reg_shift; u32 reg_io_width; unsigned long flags; @@ -135,6 +136,16 @@ static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg) return ioread32be(i2c->base + (reg << i2c->reg_shift)); } +static void oc_setreg_io_8(struct ocores_i2c *i2c, int reg, u8 value) +{ + outb(value, i2c->iobase + reg); +} + +static inline u8 oc_getreg_io_8(struct ocores_i2c *i2c, int reg) +{ + return inb(i2c->iobase + reg); +} + static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) { i2c->setreg(i2c, reg, value); @@ -593,9 +604,24 @@ static int ocores_i2c_probe(struct platform_device *pdev) spin_lock_init(&i2c->process_lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2c->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(i2c->base)) - return PTR_ERR(i2c->base); + if (res) { + i2c->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + } else { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EINVAL; + i2c->iobase = res->start; + if (!devm_request_region(&pdev->dev, res->start, + resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Can't get I/O resource.\n"); + return -EBUSY; + } + i2c->setreg = oc_setreg_io_8; + i2c->getreg = oc_getreg_io_8; + } pdata = dev_get_platdata(&pdev->dev); if (pdata) { From 237b5f66e1ed8a58662f29bcd04442953cdb8b55 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 14 Feb 2019 04:24:50 +0100 Subject: [PATCH 1803/4277] i2c: ocores: Add support for bus clock via platform data Add the I2C bus clock speed to the platform data structure. If not set, default to 100KHz as before. Signed-off-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 5 ++++- include/linux/platform_data/i2c-ocores.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 0d90a82a2c03..4eea18689e99 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -628,7 +628,10 @@ static int ocores_i2c_probe(struct platform_device *pdev) i2c->reg_shift = pdata->reg_shift; i2c->reg_io_width = pdata->reg_io_width; i2c->ip_clock_khz = pdata->clock_khz; - i2c->bus_clock_khz = 100; + if (pdata->bus_khz) + i2c->bus_clock_khz = pdata->bus_khz; + else + i2c->bus_clock_khz = 100; } else { ret = ocores_i2c_of_probe(pdev, i2c); if (ret) diff --git a/include/linux/platform_data/i2c-ocores.h b/include/linux/platform_data/i2c-ocores.h index 8c416ff8affd..e6326cbafe59 100644 --- a/include/linux/platform_data/i2c-ocores.h +++ b/include/linux/platform_data/i2c-ocores.h @@ -12,6 +12,7 @@ struct ocores_i2c_platform_data { u32 reg_shift; /* register offset shift value */ u32 reg_io_width; /* register io read/write width */ u32 clock_khz; /* input clock in kHz */ + u32 bus_khz; /* bus clock in kHz */ bool big_endian; /* registers are big endian */ u8 num_devices; /* number of devices in the devices list */ struct i2c_board_info const *devices; /* devices connected to the bus */ From 2c94b8eca1a26cd46010d6e73a23da5f2e93a19d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 11 Feb 2019 11:25:41 -0500 Subject: [PATCH 1804/4277] SUNRPC: Use au_rslack when computing reply buffer size au_rslack is significantly smaller than (au_cslack << 2). Using that value results in smaller receive buffers. In some cases this eliminates an extra segment in Reply chunks (RPC/RDMA). Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/clnt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 99bfeb17367c..241e8423fd0c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1690,7 +1690,7 @@ call_refreshresult(struct rpc_task *task) static void call_allocate(struct rpc_task *task) { - unsigned int slack = task->tk_rqstp->rq_cred->cr_auth->au_cslack; + const struct rpc_auth *auth = task->tk_rqstp->rq_cred->cr_auth; struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; const struct rpc_procinfo *proc = task->tk_msg.rpc_proc; @@ -1715,9 +1715,10 @@ call_allocate(struct rpc_task *task) * and reply headers, and convert both values * to byte sizes. */ - req->rq_callsize = RPC_CALLHDRSIZE + (slack << 1) + proc->p_arglen; + req->rq_callsize = RPC_CALLHDRSIZE + (auth->au_cslack << 1) + + proc->p_arglen; req->rq_callsize <<= 2; - req->rq_rcvsize = RPC_REPHDRSIZE + slack + proc->p_replen; + req->rq_rcvsize = RPC_REPHDRSIZE + auth->au_rslack + proc->p_replen; req->rq_rcvsize <<= 2; status = xprt->ops->buf_alloc(task); From d8434c31378d5f2009741fa171bd1143c77a6e4a Mon Sep 17 00:00:00 2001 From: John Sperbeck Date: Tue, 12 Feb 2019 19:40:57 -0800 Subject: [PATCH 1805/4277] i2c: core-smbus: don't trace smbus_reply data on errors If an smbus transfer fails, there's no guarantee that the output buffer was written. So, avoid trying to show the output buffer when tracing after an error. This was 'mostly harmless', but would trip up kasan checking if left-over cruft in byte 0 is a large length, causing us to read from unwritten memory. Signed-off-by: John Sperbeck Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-smbus.c | 2 +- include/trace/events/smbus.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 9cd66cabb84f..132119112596 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -585,7 +585,7 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, trace: /* If enabled, the reply tracepoint is conditional on read_write. */ trace_smbus_reply(adapter, addr, flags, read_write, - command, protocol, data); + command, protocol, data, res); trace_smbus_result(adapter, addr, flags, read_write, command, protocol, res); diff --git a/include/trace/events/smbus.h b/include/trace/events/smbus.h index d2fb6e1d3e10..a4892a187842 100644 --- a/include/trace/events/smbus.h +++ b/include/trace/events/smbus.h @@ -138,9 +138,9 @@ TRACE_EVENT_CONDITION(smbus_reply, TP_PROTO(const struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, - const union i2c_smbus_data *data), - TP_ARGS(adap, addr, flags, read_write, command, protocol, data), - TP_CONDITION(read_write == I2C_SMBUS_READ), + const union i2c_smbus_data *data, int res), + TP_ARGS(adap, addr, flags, read_write, command, protocol, data, res), + TP_CONDITION(res >= 0 && read_write == I2C_SMBUS_READ), TP_STRUCT__entry( __field(int, adapter_nr ) __field(__u16, addr ) From 704e0e613a6d584fde1c80ead0329e918b4f8671 Mon Sep 17 00:00:00 2001 From: chenglang Date: Sun, 3 Feb 2019 16:13:03 +0800 Subject: [PATCH 1806/4277] RDMA/hns: Limit minimum ROCE CQ depth to 64 This patch modifies the minimum CQ depth specification of hip08 and is consistent with the processing of hip06. Signed-off-by: chenglang Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index c648ee825852..5677d6ffac7d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -1512,6 +1512,7 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev) caps->max_wqes = HNS_ROCE_V2_MAX_WQE_NUM; caps->num_cqs = HNS_ROCE_V2_MAX_CQ_NUM; caps->num_srqs = HNS_ROCE_V2_MAX_SRQ_NUM; + caps->min_cqes = HNS_ROCE_MIN_CQE_NUM; caps->max_cqes = HNS_ROCE_V2_MAX_CQE_NUM; caps->max_srqwqes = HNS_ROCE_V2_MAX_SRQWQE_NUM; caps->max_sq_sg = HNS_ROCE_V2_MAX_SQ_SGE_NUM; From ab22bf05216a6bb4812448f3a8609489047cf311 Mon Sep 17 00:00:00 2001 From: Yixian Liu Date: Sun, 3 Feb 2019 16:13:04 +0800 Subject: [PATCH 1807/4277] RDMA/hns: Fix the state of rereg mr The state of mr after reregister operation should be set to valid state. Otherwise, it will keep the same as the state before reregistered. Signed-off-by: Yixian Liu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 5677d6ffac7d..d7416df5b848 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -2190,6 +2190,9 @@ static int hns_roce_v2_rereg_write_mtpt(struct hns_roce_dev *hr_dev, struct hns_roce_v2_mpt_entry *mpt_entry = mb_buf; int ret = 0; + roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_MPT_ST_M, + V2_MPT_BYTE_4_MPT_ST_S, V2_MPT_ST_VALID); + if (flags & IB_MR_REREG_PD) { roce_set_field(mpt_entry->byte_4_pd_hop_st, V2_MPT_BYTE_4_PD_M, V2_MPT_BYTE_4_PD_S, pdn); From f7f27a5f03cc9f47cc14f75a5be25f0f26b1b5ff Mon Sep 17 00:00:00 2001 From: Yixian Liu Date: Sun, 3 Feb 2019 16:13:05 +0800 Subject: [PATCH 1808/4277] RDMA/hns: Set allocated memory to zero for wrid The memory allocated for wrid should be initialized to zero. Signed-off-by: Yixian Liu Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_qp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 51ca22b9f960..728ad8b6de2f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -763,10 +763,10 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, goto err_mtt; } - hr_qp->sq.wrid = kmalloc_array(hr_qp->sq.wqe_cnt, sizeof(u64), - GFP_KERNEL); - hr_qp->rq.wrid = kmalloc_array(hr_qp->rq.wqe_cnt, sizeof(u64), - GFP_KERNEL); + hr_qp->sq.wrid = kcalloc(hr_qp->sq.wqe_cnt, sizeof(u64), + GFP_KERNEL); + hr_qp->rq.wrid = kcalloc(hr_qp->rq.wqe_cnt, sizeof(u64), + GFP_KERNEL); if (!hr_qp->sq.wrid || !hr_qp->rq.wrid) { ret = -ENOMEM; goto err_wrid; From e95c716c7faa0d0eede5eabb6fea2504709e25b6 Mon Sep 17 00:00:00 2001 From: Yixian Liu Date: Sun, 3 Feb 2019 16:13:06 +0800 Subject: [PATCH 1809/4277] RDMA/hns: Delete useful prints for aeq subtype event Current all messages printed for aeq subtype event are wrong. Thus, delete them and only the value of subtype event is printed. Signed-off-by: Yixian Liu Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 57 +++------------------- 1 file changed, 6 insertions(+), 51 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index d7416df5b848..0e0f98c78494 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4658,64 +4658,19 @@ static void hns_roce_irq_work_handle(struct work_struct *work) dev_warn(dev, "Send queue drained.\n"); break; case HNS_ROCE_EVENT_TYPE_WQ_CATAS_ERROR: - dev_err(dev, "Local work queue catastrophic error.\n"); + dev_err(dev, "Local work queue 0x%x catas error, sub_type:%d\n", + qpn, irq_work->sub_type); hns_roce_set_qps_to_err(irq_work->hr_dev, qpn); - switch (irq_work->sub_type) { - case HNS_ROCE_LWQCE_QPC_ERROR: - dev_err(dev, "QP %d, QPC error.\n", qpn); - break; - case HNS_ROCE_LWQCE_MTU_ERROR: - dev_err(dev, "QP %d, MTU error.\n", qpn); - break; - case HNS_ROCE_LWQCE_WQE_BA_ADDR_ERROR: - dev_err(dev, "QP %d, WQE BA addr error.\n", qpn); - break; - case HNS_ROCE_LWQCE_WQE_ADDR_ERROR: - dev_err(dev, "QP %d, WQE addr error.\n", qpn); - break; - case HNS_ROCE_LWQCE_SQ_WQE_SHIFT_ERROR: - dev_err(dev, "QP %d, WQE shift error.\n", qpn); - break; - default: - dev_err(dev, "Unhandled sub_event type %d.\n", - irq_work->sub_type); - break; - } break; case HNS_ROCE_EVENT_TYPE_INV_REQ_LOCAL_WQ_ERROR: - dev_err(dev, "Invalid request local work queue error.\n"); + dev_err(dev, "Invalid request local work queue 0x%x error.\n", + qpn); hns_roce_set_qps_to_err(irq_work->hr_dev, qpn); break; case HNS_ROCE_EVENT_TYPE_LOCAL_WQ_ACCESS_ERROR: - dev_err(dev, "Local access violation work queue error.\n"); + dev_err(dev, "Local access violation work queue 0x%x error, sub_type:%d\n", + qpn, irq_work->sub_type); hns_roce_set_qps_to_err(irq_work->hr_dev, qpn); - switch (irq_work->sub_type) { - case HNS_ROCE_LAVWQE_R_KEY_VIOLATION: - dev_err(dev, "QP %d, R_key violation.\n", qpn); - break; - case HNS_ROCE_LAVWQE_LENGTH_ERROR: - dev_err(dev, "QP %d, length error.\n", qpn); - break; - case HNS_ROCE_LAVWQE_VA_ERROR: - dev_err(dev, "QP %d, VA error.\n", qpn); - break; - case HNS_ROCE_LAVWQE_PD_ERROR: - dev_err(dev, "QP %d, PD error.\n", qpn); - break; - case HNS_ROCE_LAVWQE_RW_ACC_ERROR: - dev_err(dev, "QP %d, rw acc error.\n", qpn); - break; - case HNS_ROCE_LAVWQE_KEY_STATE_ERROR: - dev_err(dev, "QP %d, key state error.\n", qpn); - break; - case HNS_ROCE_LAVWQE_MR_OPERATION_ERROR: - dev_err(dev, "QP %d, MR operation error.\n", qpn); - break; - default: - dev_err(dev, "Unhandled sub_event type %d.\n", - irq_work->sub_type); - break; - } break; case HNS_ROCE_EVENT_TYPE_SRQ_LIMIT_REACH: dev_warn(dev, "SRQ limit reach.\n"); From dad1f9802ecee3a21143293b2505e1b57b1ae525 Mon Sep 17 00:00:00 2001 From: Lijun Ou Date: Sun, 3 Feb 2019 16:13:07 +0800 Subject: [PATCH 1810/4277] RDMA/hns: Configure capacity of hns device This patch adds new device capability for IB_DEVICE_MEM_MGT_EXTENSIONS to indicate device support for the following features: 1. Fast register memory region. 2. send with remote invalidate by frmr 3. local invalidate memory regsion As well as adds the max depth of frmr page list len. Signed-off-by: Yangyang Li Signed-off-by: Lijun Ou Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hns/hns_roce_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index ccf10622586c..29fb4fbba5ba 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -226,6 +226,11 @@ static int hns_roce_query_device(struct ib_device *ib_dev, props->max_srq_sge = hr_dev->caps.max_srq_sges; } + if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_FRMR) { + props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS; + props->max_fast_reg_page_list_len = HNS_ROCE_FRMR_MAX_PA; + } + return 0; } From c528f7bd362b097eeeafa6fbbeccd9750b79c7ba Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 Jan 2019 20:07:45 +0300 Subject: [PATCH 1811/4277] Revert "PCI/PME: Implement runtime PM callbacks" This reverts commit 0e157e52860441cb26051f131dd0b5ae3187a07b. Heiner reported that the commit in question prevents his network adapter from triggering PME and waking up when network cable is plugged. The commit tried to prevent root port waking up from D3cold immediately but looks like disabing root port PME interrupt is not the right way to fix that issue so revert it now. The patch following proposes an alternative solution to that issue. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202103 Fixes: 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") Reported-by: Heiner Kallweit Tested-by: Heiner Kallweit Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki CC: stable@vger.kernel.org # v4.20+ --- drivers/pci/pcie/pme.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 0dbcf429089f..1a8b85051b1b 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -432,31 +432,6 @@ static void pcie_pme_remove(struct pcie_device *srv) kfree(get_service_data(srv)); } -static int pcie_pme_runtime_suspend(struct pcie_device *srv) -{ - struct pcie_pme_service_data *data = get_service_data(srv); - - spin_lock_irq(&data->lock); - pcie_pme_interrupt_enable(srv->port, false); - pcie_clear_root_pme_status(srv->port); - data->noirq = true; - spin_unlock_irq(&data->lock); - - return 0; -} - -static int pcie_pme_runtime_resume(struct pcie_device *srv) -{ - struct pcie_pme_service_data *data = get_service_data(srv); - - spin_lock_irq(&data->lock); - pcie_pme_interrupt_enable(srv->port, true); - data->noirq = false; - spin_unlock_irq(&data->lock); - - return 0; -} - static struct pcie_port_service_driver pcie_pme_driver = { .name = "pcie_pme", .port_type = PCI_EXP_TYPE_ROOT_PORT, @@ -464,8 +439,6 @@ static struct pcie_port_service_driver pcie_pme_driver = { .probe = pcie_pme_probe, .suspend = pcie_pme_suspend, - .runtime_suspend = pcie_pme_runtime_suspend, - .runtime_resume = pcie_pme_runtime_resume, .resume = pcie_pme_resume, .remove = pcie_pme_remove, }; From 538bcaa6261b77e71d37f5596c33127c1a3ec3f7 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 14 Feb 2019 16:27:14 -0500 Subject: [PATCH 1812/4277] jbd2: fix race when writing superblock The jbd2 superblock is lockless now, so there is probably a race condition between writing it so disk and modifing contents of it, which may lead to checksum error. The following race is the one case that we have captured. jbd2 fsstress jbd2_journal_commit_transaction jbd2_journal_update_sb_log_tail jbd2_write_superblock jbd2_superblock_csum_set jbd2_journal_revoke jbd2_journal_set_features(revork) modify superblock submit_bh(checksum incorrect) Fix this by locking the buffer head before modifing it. We always write the jbd2 superblock after we modify it, so this just means calling the lock_buffer() a little earlier. This checksum corruption problem can be reproduced by xfstests generic/475. Reported-by: zhangyi (F) Suggested-by: Jan Kara Signed-off-by: Theodore Ts'o --- fs/jbd2/journal.c | 52 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 88d8f22d2cba..67ac91b53050 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1356,6 +1356,10 @@ static int journal_reset(journal_t *journal) return jbd2_journal_start_thread(journal); } +/* + * This function expects that the caller will have locked the journal + * buffer head, and will return with it unlocked + */ static int jbd2_write_superblock(journal_t *journal, int write_flags) { struct buffer_head *bh = journal->j_sb_buffer; @@ -1365,7 +1369,6 @@ static int jbd2_write_superblock(journal_t *journal, int write_flags) trace_jbd2_write_superblock(journal, write_flags); if (!(journal->j_flags & JBD2_BARRIER)) write_flags &= ~(REQ_FUA | REQ_PREFLUSH); - lock_buffer(bh); if (buffer_write_io_error(bh)) { /* * Oh, dear. A previous attempt to write the journal @@ -1424,6 +1427,7 @@ int jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid, jbd_debug(1, "JBD2: updating superblock (start %lu, seq %u)\n", tail_block, tail_tid); + lock_buffer(journal->j_sb_buffer); sb->s_sequence = cpu_to_be32(tail_tid); sb->s_start = cpu_to_be32(tail_block); @@ -1454,18 +1458,17 @@ static void jbd2_mark_journal_empty(journal_t *journal, int write_op) journal_superblock_t *sb = journal->j_superblock; BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex)); - read_lock(&journal->j_state_lock); - /* Is it already empty? */ - if (sb->s_start == 0) { - read_unlock(&journal->j_state_lock); + lock_buffer(journal->j_sb_buffer); + if (sb->s_start == 0) { /* Is it already empty? */ + unlock_buffer(journal->j_sb_buffer); return; } + jbd_debug(1, "JBD2: Marking journal as empty (seq %d)\n", journal->j_tail_sequence); sb->s_sequence = cpu_to_be32(journal->j_tail_sequence); sb->s_start = cpu_to_be32(0); - read_unlock(&journal->j_state_lock); jbd2_write_superblock(journal, write_op); @@ -1488,9 +1491,8 @@ void jbd2_journal_update_sb_errno(journal_t *journal) journal_superblock_t *sb = journal->j_superblock; int errcode; - read_lock(&journal->j_state_lock); + lock_buffer(journal->j_sb_buffer); errcode = journal->j_errno; - read_unlock(&journal->j_state_lock); if (errcode == -ESHUTDOWN) errcode = 0; jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", errcode); @@ -1894,28 +1896,27 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, sb = journal->j_superblock; + /* Load the checksum driver if necessary */ + if ((journal->j_chksum_driver == NULL) && + INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V3)) { + journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0); + if (IS_ERR(journal->j_chksum_driver)) { + printk(KERN_ERR "JBD2: Cannot load crc32c driver.\n"); + journal->j_chksum_driver = NULL; + return 0; + } + /* Precompute checksum seed for all metadata */ + journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, + sizeof(sb->s_uuid)); + } + + lock_buffer(journal->j_sb_buffer); + /* If enabling v3 checksums, update superblock */ if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V3)) { sb->s_checksum_type = JBD2_CRC32C_CHKSUM; sb->s_feature_compat &= ~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM); - - /* Load the checksum driver */ - if (journal->j_chksum_driver == NULL) { - journal->j_chksum_driver = crypto_alloc_shash("crc32c", - 0, 0); - if (IS_ERR(journal->j_chksum_driver)) { - printk(KERN_ERR "JBD2: Cannot load crc32c " - "driver.\n"); - journal->j_chksum_driver = NULL; - return 0; - } - - /* Precompute checksum seed for all metadata */ - journal->j_csum_seed = jbd2_chksum(journal, ~0, - sb->s_uuid, - sizeof(sb->s_uuid)); - } } /* If enabling v1 checksums, downgrade superblock */ @@ -1927,6 +1928,7 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, sb->s_feature_compat |= cpu_to_be32(compat); sb->s_feature_ro_compat |= cpu_to_be32(ro); sb->s_feature_incompat |= cpu_to_be32(incompat); + unlock_buffer(journal->j_sb_buffer); return 1; #undef COMPAT_FEATURE_ON From a58ca992661a4bc6a1dfa60e9d6f606e97784149 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 14 Feb 2019 16:28:14 -0500 Subject: [PATCH 1813/4277] jbd2: fold jbd2_superblock_csum_{verify,set} into their callers The functions jbd2_superblock_csum_verify() and jbd2_superblock_csum_set() only get called from one location, so to simplify things, fold them into their callers. Signed-off-by: Theodore Ts'o --- fs/jbd2/journal.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 67ac91b53050..382c030cc78b 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -142,22 +142,6 @@ static __be32 jbd2_superblock_csum(journal_t *j, journal_superblock_t *sb) return cpu_to_be32(csum); } -static int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb) -{ - if (!jbd2_journal_has_csum_v2or3(j)) - return 1; - - return sb->s_checksum == jbd2_superblock_csum(j, sb); -} - -static void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb) -{ - if (!jbd2_journal_has_csum_v2or3(j)) - return; - - sb->s_checksum = jbd2_superblock_csum(j, sb); -} - /* * Helper function used to manage commit timeouts */ @@ -1384,7 +1368,8 @@ static int jbd2_write_superblock(journal_t *journal, int write_flags) clear_buffer_write_io_error(bh); set_buffer_uptodate(bh); } - jbd2_superblock_csum_set(journal, sb); + if (jbd2_journal_has_csum_v2or3(journal)) + sb->s_checksum = jbd2_superblock_csum(journal, sb); get_bh(bh); bh->b_end_io = end_buffer_write_sync; ret = submit_bh(REQ_OP_WRITE, write_flags, bh); @@ -1597,17 +1582,18 @@ static int journal_get_superblock(journal_t *journal) } } - /* Check superblock checksum */ - if (!jbd2_superblock_csum_verify(journal, sb)) { - printk(KERN_ERR "JBD2: journal checksum error\n"); - err = -EFSBADCRC; - goto out; - } + if (jbd2_journal_has_csum_v2or3(journal)) { + /* Check superblock checksum */ + if (sb->s_checksum != jbd2_superblock_csum(journal, sb)) { + printk(KERN_ERR "JBD2: journal checksum error\n"); + err = -EFSBADCRC; + goto out; + } - /* Precompute checksum seed for all metadata */ - if (jbd2_journal_has_csum_v2or3(journal)) + /* Precompute checksum seed for all metadata */ journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, sizeof(sb->s_uuid)); + } set_buffer_verified(bh); From 36f0a1ccb3d4c328dfa263412ec28b77970c523b Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 14 Feb 2019 01:57:10 +0000 Subject: [PATCH 1814/4277] RDMA/iwpm: Remove set but not used variable 'msg_seq' Fixes gcc '-Wunused-but-set-variable' warning: drivers/infiniband/core/iwpm_util.c: In function 'iwpm_send_hello': drivers/infiniband/core/iwpm_util.c:811:6: warning: variable 'msg_seq' set but not used [-Wunused-but-set-variable] It never used since introduction in commit b0bad9ad514f ("RDMA/IWPM: Support no port mapping requirements") Signed-off-by: YueHaibing Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/iwpm_util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index 7b97f6e2075f..a5d2a20ee697 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -808,7 +808,6 @@ int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version) { struct sk_buff *skb = NULL; struct nlmsghdr *nlh; - u32 msg_seq; const char *err_str = ""; int ret = -EINVAL; @@ -818,7 +817,6 @@ int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version) goto hello_num_error; } nlh->nlmsg_seq = iwpm_get_nlmsg_seq(); - msg_seq = 0; err_str = "Unable to put attribute of abi_version into nlmsg"; ret = ibnl_put_attr(skb, nlh, sizeof(u16), &abi_version, IWPM_NLA_HELLO_ABI_VERSION); From 0dd9ce18b9f315878894351595a53dd81fdd8598 Mon Sep 17 00:00:00 2001 From: Erez Alfasi Date: Wed, 13 Feb 2019 19:12:02 +0200 Subject: [PATCH 1815/4277] IB/ipoib: Use __func__ instead of function's name Changed debug statements to use %s and __func__ instead of hard-coded function's name. Signed-off-by: Erez Alfasi Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 3d3407d1feb4..ee4cca80f00b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -613,7 +613,7 @@ static void path_free(struct net_device *dev, struct ipoib_path *path) while ((skb = __skb_dequeue(&path->queue))) dev_kfree_skb_irq(skb); - ipoib_dbg(ipoib_priv(dev), "path_free\n"); + ipoib_dbg(ipoib_priv(dev), "%s\n", __func__); /* remove all neigh connected to this path */ ipoib_del_neighs_by_gid(dev, path->pathrec.dgid.raw); @@ -1641,7 +1641,7 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev) { struct ipoib_dev_priv *priv = ipoib_priv(dev); - ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n"); + ipoib_dbg(priv, "%s\n", __func__); init_completion(&priv->ntbl.deleted); cancel_delayed_work_sync(&priv->neigh_reap_task); From c9e716eb9b3455a83ed7c5f5a81256a3da779a95 Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Thu, 14 Feb 2019 17:52:18 -0500 Subject: [PATCH 1816/4277] ext4: don't update s_rev_level if not required Don't update the superblock s_rev_level during mount if it isn't actually necessary, only if superblock features are being set by the kernel. This was originally added for ext3 since it always set the INCOMPAT_RECOVER and HAS_JOURNAL features during mount, but this is not needed since no journal mode was added to ext4. That will allow Geert to mount his 20-year-old ext2 rev 0.0 m68k filesystem, as a testament of the backward compatibility of ext4. Fixes: 0390131ba84f ("ext4: Allow ext4 to run without a journal") Signed-off-by: Andreas Dilger Signed-off-by: Theodore Ts'o --- fs/ext4/ext4.h | 6 +++++- fs/ext4/inode.c | 1 - fs/ext4/super.c | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 508a37ec9271..b8fde74ff76d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1665,6 +1665,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) #define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */ #define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 +extern void ext4_update_dynamic_rev(struct super_block *sb); + #define EXT4_FEATURE_COMPAT_FUNCS(name, flagname) \ static inline bool ext4_has_feature_##name(struct super_block *sb) \ { \ @@ -1673,6 +1675,7 @@ static inline bool ext4_has_feature_##name(struct super_block *sb) \ } \ static inline void ext4_set_feature_##name(struct super_block *sb) \ { \ + ext4_update_dynamic_rev(sb); \ EXT4_SB(sb)->s_es->s_feature_compat |= \ cpu_to_le32(EXT4_FEATURE_COMPAT_##flagname); \ } \ @@ -1690,6 +1693,7 @@ static inline bool ext4_has_feature_##name(struct super_block *sb) \ } \ static inline void ext4_set_feature_##name(struct super_block *sb) \ { \ + ext4_update_dynamic_rev(sb); \ EXT4_SB(sb)->s_es->s_feature_ro_compat |= \ cpu_to_le32(EXT4_FEATURE_RO_COMPAT_##flagname); \ } \ @@ -1707,6 +1711,7 @@ static inline bool ext4_has_feature_##name(struct super_block *sb) \ } \ static inline void ext4_set_feature_##name(struct super_block *sb) \ { \ + ext4_update_dynamic_rev(sb); \ EXT4_SB(sb)->s_es->s_feature_incompat |= \ cpu_to_le32(EXT4_FEATURE_INCOMPAT_##flagname); \ } \ @@ -2675,7 +2680,6 @@ do { \ #endif -extern void ext4_update_dynamic_rev(struct super_block *sb); extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb, __u32 compat); extern int ext4_update_rocompat_feature(handle_t *handle, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9c3402ec85bc..2b6eefbc3b34 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5345,7 +5345,6 @@ static int ext4_do_update_inode(handle_t *handle, err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh); if (err) goto out_brelse; - ext4_update_dynamic_rev(sb); ext4_set_feature_large_file(sb); ext4_handle_sync(handle); err = ext4_handle_dirty_super(handle, sb); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index fb12d3c17c1b..6e4cac646345 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2249,7 +2249,6 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, es->s_max_mnt_count = cpu_to_le16(EXT4_DFL_MAX_MNT_COUNT); le16_add_cpu(&es->s_mnt_count, 1); ext4_update_tstamp(es, s_mtime); - ext4_update_dynamic_rev(sb); if (sbi->s_journal) ext4_set_feature_journal_needs_recovery(sb); From 2e57b7cebb988a27cee44626ae91424e73823bfb Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 11 Feb 2019 10:54:44 -0800 Subject: [PATCH 1817/4277] i2c: aspeed: Add multi-master use case support In multi-master environment, this driver's master cannot know exactly when a peer master sends data to this driver's slave so cases can be happened that this master tries sending data through the master_xfer function but slave data from a peer master is still being processed or slave xfer is started by a peer immediately after it queues a master command. To support multi-master use cases properly, this H/W provides arbitration in physical level and it provides priority based command handling too to avoid conflicts in multi-master environment, means that if a master and a slave events happen at the same time, H/W will handle a higher priority event first and a pending event will be handled when bus comes back to the idle state. To support this H/W feature properly, this patch adds the 'pending' state of master and its handling code so that the pending master xfer can be continued after slave operation properly. Signed-off-by: Jae Hyun Yoo Reviewed-by: Brendan Higgins Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-aspeed.c | 119 +++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 833b6b6a4c7e..6c8b38fd6e64 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -117,6 +117,7 @@ enum aspeed_i2c_master_state { ASPEED_I2C_MASTER_INACTIVE, + ASPEED_I2C_MASTER_PENDING, ASPEED_I2C_MASTER_START, ASPEED_I2C_MASTER_TX_FIRST, ASPEED_I2C_MASTER_TX, @@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { }; enum aspeed_i2c_slave_state { - ASPEED_I2C_SLAVE_STOP, + ASPEED_I2C_SLAVE_INACTIVE, ASPEED_I2C_SLAVE_START, ASPEED_I2C_SLAVE_READ_REQUESTED, ASPEED_I2C_SLAVE_READ_PROCESSED, ASPEED_I2C_SLAVE_WRITE_REQUESTED, ASPEED_I2C_SLAVE_WRITE_RECEIVED, + ASPEED_I2C_SLAVE_STOP, }; struct aspeed_i2c_bus { @@ -156,6 +158,8 @@ struct aspeed_i2c_bus { int cmd_err; /* Protected only by i2c_lock_bus */ int master_xfer_result; + /* Multi-master */ + bool multi_master; #if IS_ENABLED(CONFIG_I2C_SLAVE) struct i2c_client *slave; enum aspeed_i2c_slave_state slave_state; @@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) } /* Slave is not currently active, irq was for someone else. */ - if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) + if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) return irq_handled; dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", @@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; bus->slave_state = ASPEED_I2C_SLAVE_STOP; } - if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { + if (irq_status & ASPEED_I2CD_INTR_TX_NAK && + bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { irq_handled |= ASPEED_I2CD_INTR_TX_NAK; bus->slave_state = ASPEED_I2C_SLAVE_STOP; } - if (irq_status & ASPEED_I2CD_INTR_TX_ACK) - irq_handled |= ASPEED_I2CD_INTR_TX_ACK; switch (bus->slave_state) { case ASPEED_I2C_SLAVE_READ_REQUESTED: - if (irq_status & ASPEED_I2CD_INTR_TX_ACK) + if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) dev_err(bus->dev, "Unexpected ACK on read request.\n"); bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); @@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); break; case ASPEED_I2C_SLAVE_READ_PROCESSED: - if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { dev_err(bus->dev, "Expected ACK after processed read.\n"); + break; + } + irq_handled |= ASPEED_I2CD_INTR_TX_ACK; i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); @@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) break; case ASPEED_I2C_SLAVE_STOP: i2c_slave_event(slave, I2C_SLAVE_STOP, &value); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; + break; + case ASPEED_I2C_SLAVE_START: + /* Slave was just started. Waiting for the next event. */; break; default: - dev_err(bus->dev, "unhandled slave_state: %d\n", + dev_err(bus->dev, "unknown slave_state: %d\n", bus->slave_state); + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; break; } @@ -329,6 +340,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) u8 slave_addr = i2c_8bit_addr_from_msg(msg); bus->master_state = ASPEED_I2C_MASTER_START; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * If it's requested in the middle of a slave session, set the master + * state to 'pending' then H/W will continue handling this master + * command when the bus comes back to the idle state. + */ + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) + bus->master_state = ASPEED_I2C_MASTER_PENDING; +#endif /* CONFIG_I2C_SLAVE */ + bus->buf_index = 0; if (msg->flags & I2C_M_RD) { @@ -384,10 +406,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) bus->master_state = ASPEED_I2C_MASTER_INACTIVE; irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; goto out_complete; - } else { - /* Master is not currently active, irq was for someone else. */ - if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) - goto out_no_complete; } /* @@ -399,12 +417,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) if (ret) { dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", irq_status); - bus->cmd_err = ret; - bus->master_state = ASPEED_I2C_MASTER_INACTIVE; irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); - goto out_complete; + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { + bus->cmd_err = ret; + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; + goto out_complete; + } } +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * A pending master command will be started by H/W when the bus comes + * back to idle state after completing a slave operation so change the + * master state from 'pending' to 'start' at here if slave is inactive. + */ + if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) + goto out_no_complete; + + bus->master_state = ASPEED_I2C_MASTER_START; + } +#endif /* CONFIG_I2C_SLAVE */ + + /* Master is not currently active, irq was for someone else. */ + if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || + bus->master_state == ASPEED_I2C_MASTER_PENDING) + goto out_no_complete; + /* We are in an invalid state; reset bus to a known state. */ if (!bus->msgs) { dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n", @@ -423,6 +462,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) * then update the state and handle the new state below. */ if (bus->master_state == ASPEED_I2C_MASTER_START) { +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * If a peer master starts a xfer immediately after it queues a + * master command, change its state to 'pending' then H/W will + * continue the queued master xfer just after completing the + * slave mode session. + */ + if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { + bus->master_state = ASPEED_I2C_MASTER_PENDING; + dev_dbg(bus->dev, + "master goes pending due to a slave start\n"); + goto out_no_complete; + } +#endif /* CONFIG_I2C_SLAVE */ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { bus->cmd_err = -ENXIO; @@ -566,7 +619,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) * interrupt bits. Each case needs to be handled using corresponding * handlers depending on the current state. */ - if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && + bus->master_state != ASPEED_I2C_MASTER_PENDING) { irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); irq_remaining &= ~irq_handled; if (irq_remaining) @@ -601,15 +655,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, { struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); unsigned long time_left, flags; - int ret = 0; spin_lock_irqsave(&bus->lock, flags); bus->cmd_err = 0; - /* If bus is busy, attempt recovery. We assume a single master - * environment. - */ - if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { + /* If bus is busy in a single master environment, attempt recovery. */ + if (!bus->multi_master && + (readl(bus->base + ASPEED_I2C_CMD_REG) & + ASPEED_I2CD_BUS_BUSY_STS)) { + int ret; + spin_unlock_irqrestore(&bus->lock, flags); ret = aspeed_i2c_recover_bus(bus); if (ret) @@ -629,10 +684,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, time_left = wait_for_completion_timeout(&bus->cmd_complete, bus->adap.timeout); - if (time_left == 0) + if (time_left == 0) { + /* + * If timed out and bus is still busy in a multi master + * environment, attempt recovery at here. + */ + if (bus->multi_master && + (readl(bus->base + ASPEED_I2C_CMD_REG) & + ASPEED_I2CD_BUS_BUSY_STS)) + aspeed_i2c_recover_bus(bus); + return -ETIMEDOUT; - else - return bus->master_xfer_result; + } + + return bus->master_xfer_result; } static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) @@ -672,7 +737,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) __aspeed_i2c_reg_slave(bus, client->addr); bus->slave = client; - bus->slave_state = ASPEED_I2C_SLAVE_STOP; + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; spin_unlock_irqrestore(&bus->lock, flags); return 0; @@ -827,7 +892,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, if (ret < 0) return ret; - if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) + if (of_property_read_bool(pdev->dev.of_node, "multi-master")) + bus->multi_master = true; + else fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; /* Enable Master Mode */ From 8a6d508a8055ea2ef8477e649662287e57727f54 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 12 Feb 2019 14:06:57 +0800 Subject: [PATCH 1818/4277] i2c: expand minor range when registering chrdev region Actually, total amount of available minor number for a single major is MINORMASK + 1. So expand minor range when registering chrdev region. Signed-off-by: Chengguang Xu [wsa: fixed typo in commit message] Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index ccd76c71af09..3f7b9af11137 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -52,7 +52,7 @@ struct i2c_dev { struct cdev cdev; }; -#define I2C_MINORS MINORMASK +#define I2C_MINORS (MINORMASK + 1) static LIST_HEAD(i2c_dev_list); static DEFINE_SPINLOCK(i2c_dev_list_lock); From 1e9efe6c9976552e88c6e6feaca3a78b8cf5aaf6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:05 +0530 Subject: [PATCH 1819/4277] PCI: endpoint: Add helper to get first unreserved BAR Add a helper function pci_epc_get_first_free_bar() to get the first unreserved BAR that can be used for endpoint function. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epc-core.c | 23 +++++++++++++++++++++++ include/linux/pci-epc.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 5a099479d9ab..e4712a0f249c 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -83,6 +83,29 @@ err: } EXPORT_SYMBOL_GPL(pci_epc_get); +/** + * pci_epc_get_first_free_bar() - helper to get first unreserved BAR + * @epc_features: pci_epc_features structure that holds the reserved bar bitmap + * + * Invoke to get the first unreserved BAR that can be used for endpoint + * function. For any incorrect value in reserved_bar return '0'. + */ +unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features + *epc_features) +{ + int free_bar; + + if (!epc_features) + return 0; + + free_bar = ffz(epc_features->reserved_bar); + if (free_bar > 5) + return 0; + + return free_bar; +} +EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar); + /** * pci_epc_get_features() - get the features supported by EPC * @epc: the features supported by *this* EPC device will be returned diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 79fbcf94e14d..94e1ecff98ce 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -180,6 +180,8 @@ int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, u8 func_no); +unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features + *epc_features); struct pci_epc *pci_epc_get(const char *epc_name); void pci_epc_put(struct pci_epc *epc); From 5544d67ed11245ccb64099deb32831308297bf6b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:06 +0530 Subject: [PATCH 1820/4277] PCI: endpoint: Fix pci_epf_alloc_space() to set correct MEM TYPE flags pci_epf_alloc_space() sets the MEM TYPE flags to indicate a 32-bit Base Address Register irrespective of the size. Fix it here to indicate 64-bit BAR if the size is > 2GB. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epf-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 825fa24427a3..8bfdcd291196 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -131,7 +131,9 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) epf->bar[bar].phys_addr = phys_addr; epf->bar[bar].size = size; epf->bar[bar].barno = bar; - epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY; + epf->bar[bar].flags |= upper_32_bits(size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; return space; } From 0342e9a797db42a7d4d083d10b5d3f38b0cfc193 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:07 +0530 Subject: [PATCH 1821/4277] PCI: pci-epf-test: Remove setting epf_bar flags in function driver Now that pci_epf_alloc_space() sets BAR MEM TYPE flags as 64Bit or 32Bit based on size, remove setting it in function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 4bbd26e8a9e2..ad0efa4446ba 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -406,10 +406,6 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; - epf_bar->flags |= upper_32_bits(epf_bar->size) ? - PCI_BASE_ADDRESS_MEM_TYPE_64 : - PCI_BASE_ADDRESS_MEM_TYPE_32; - ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); From b866c56b66d88a632e2fa6b922c4ea051937acbd Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:08 +0530 Subject: [PATCH 1822/4277] PCI: pci-epf-test: Do not allocate next BARs memory if current BAR is 64Bit It's useless to allocate memory for next BAR if the current BAR is a 64Bit BAR. Stop allocating memory for the next BAR, if the current BARs flag indicates this is a 64Bit BAR. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index ad0efa4446ba..a385927a9239 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -429,6 +429,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct device *dev = &epf->dev; + struct pci_epf_bar *epf_bar; void *base; int bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar; @@ -442,6 +443,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) epf_test->reg[test_reg_bar] = base; for (bar = BAR_0; bar <= BAR_5; bar++) { + epf_bar = &epf->bar[bar]; if (bar == test_reg_bar) continue; base = pci_epf_alloc_space(epf, bar_size[bar], bar); @@ -449,6 +451,8 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) dev_err(dev, "Failed to allocate space for BAR%d\n", bar); epf_test->reg[bar] = base; + if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) + bar++; } return 0; From 2c04c5b8eef797dca99699cfb55ff42dd3c12c23 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:09 +0530 Subject: [PATCH 1823/4277] PCI: pci-epf-test: Use pci_epc_get_features() to get EPC features Use pci_epc_get_features() to get EPC features such as linkup notifier support, MSI/MSIX capable, BAR configuration etc and use it for configuring pci-epf-test. Since these features are now obtained directly from EPC driver, remove pci_epf_test_data which was initially added to have EPC features in endpoint function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 87 ++++++++++++------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index a385927a9239..d0b91da49bf4 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -47,9 +47,8 @@ struct pci_epf_test { void *reg[6]; struct pci_epf *epf; enum pci_barno test_reg_bar; - bool linkup_notifier; - bool msix_available; struct delayed_work cmd_handler; + const struct pci_epc_features *epc_features; }; struct pci_epf_test_reg { @@ -71,11 +70,6 @@ static struct pci_epf_header test_header = { .interrupt_pin = PCI_INTERRUPT_INTA, }; -struct pci_epf_test_data { - enum pci_barno test_reg_bar; - bool linkup_notifier; -}; - static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; static int pci_epf_test_copy(struct pci_epf_test *epf_test) @@ -402,10 +396,16 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) struct device *dev = &epf->dev; struct pci_epf_test *epf_test = epf_get_drvdata(epf); enum pci_barno test_reg_bar = epf_test->test_reg_bar; + const struct pci_epc_features *epc_features; + + epc_features = epf_test->epc_features; for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; + if (!!(epc_features->reserved_bar & (1 << bar))) + continue; + ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); @@ -433,6 +433,9 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) void *base; int bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar; + const struct pci_epc_features *epc_features; + + epc_features = epf_test->epc_features; base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg), test_reg_bar); @@ -446,6 +449,10 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) epf_bar = &epf->bar[bar]; if (bar == test_reg_bar) continue; + + if (!!(epc_features->reserved_bar & (1 << bar))) + continue; + base = pci_epf_alloc_space(epf, bar_size[bar], bar); if (!base) dev_err(dev, "Failed to allocate space for BAR%d\n", @@ -458,25 +465,50 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) return 0; } +static void pci_epf_configure_bar(struct pci_epf *epf, + const struct pci_epc_features *epc_features) +{ + struct pci_epf_bar *epf_bar; + bool bar_fixed_64bit; + int i; + + for (i = BAR_0; i <= BAR_5; i++) { + epf_bar = &epf->bar[i]; + bar_fixed_64bit = !!(epc_features->bar_fixed_64bit & (1 << i)); + if (bar_fixed_64bit) + epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + if (epc_features->bar_fixed_size[i]) + bar_size[i] = epc_features->bar_fixed_size[i]; + } +} + static int pci_epf_test_bind(struct pci_epf *epf) { int ret; struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epf_header *header = epf->header; + const struct pci_epc_features *epc_features; + enum pci_barno test_reg_bar = BAR_0; struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; + bool linkup_notifier = false; + bool msix_capable = false; + bool msi_capable = true; if (WARN_ON_ONCE(!epc)) return -EINVAL; - if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER) - epf_test->linkup_notifier = false; - else - epf_test->linkup_notifier = true; + epc_features = pci_epc_get_features(epc, epf->func_no); + if (epc_features) { + linkup_notifier = epc_features->linkup_notifier; + msix_capable = epc_features->msix_capable; + msi_capable = epc_features->msi_capable; + test_reg_bar = pci_epc_get_first_free_bar(epc_features); + pci_epf_configure_bar(epf, epc_features); + } - epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE; - - epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features); + epf_test->test_reg_bar = test_reg_bar; + epf_test->epc_features = epc_features; ret = pci_epc_write_header(epc, epf->func_no, header); if (ret) { @@ -492,13 +524,15 @@ static int pci_epf_test_bind(struct pci_epf *epf) if (ret) return ret; - ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); - if (ret) { - dev_err(dev, "MSI configuration failed\n"); - return ret; + if (msi_capable) { + ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); + if (ret) { + dev_err(dev, "MSI configuration failed\n"); + return ret; + } } - if (epf_test->msix_available) { + if (msix_capable) { ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts); if (ret) { dev_err(dev, "MSI-X configuration failed\n"); @@ -506,7 +540,7 @@ static int pci_epf_test_bind(struct pci_epf *epf) } } - if (!epf_test->linkup_notifier) + if (!linkup_notifier) queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); return 0; @@ -523,17 +557,6 @@ static int pci_epf_test_probe(struct pci_epf *epf) { struct pci_epf_test *epf_test; struct device *dev = &epf->dev; - const struct pci_epf_device_id *match; - struct pci_epf_test_data *data; - enum pci_barno test_reg_bar = BAR_0; - bool linkup_notifier = true; - - match = pci_epf_match_device(pci_epf_test_ids, epf); - data = (struct pci_epf_test_data *)match->driver_data; - if (data) { - test_reg_bar = data->test_reg_bar; - linkup_notifier = data->linkup_notifier; - } epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL); if (!epf_test) @@ -541,8 +564,6 @@ static int pci_epf_test_probe(struct pci_epf *epf) epf->header = &test_header; epf_test->epf = epf; - epf_test->test_reg_bar = test_reg_bar; - epf_test->linkup_notifier = linkup_notifier; INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler); From c274c9f4ea758bd6667a363f275e45275f2b5672 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:10 +0530 Subject: [PATCH 1824/4277] PCI: cadence: Remove pci_epf_linkup() from Cadence EP driver pci_epf_linkup() is intended to be invoked if the EPC supports linkup notification. Now that pci-epf-test uses the get_features() callback, which indicates Cadence EP driver doesn't support the linkup notification, remove pci_epf_linkup() from Cadence EP driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-cadence-ep.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index 14c2545bb17e..def7820cb824 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -396,18 +396,6 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) cfg |= BIT(epf->func_no); cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); - /* - * The PCIe links are automatically established by the controller - * once for all at powerup: the software can neither start nor stop - * those links later at runtime. - * - * Then we only have to notify the EP core that our links are already - * established. However we don't call directly pci_epc_linkup() because - * we've already locked the epc->lock. - */ - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - return 0; } From f1267978457e77ae7ddabe82ad48cf34f54e96fb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:11 +0530 Subject: [PATCH 1825/4277] PCI: rockchip: Remove pci_epf_linkup() from Rockchip EP driver pci_epf_linkup() is intended to be invoked if the EPC supports linkup notification. Now that pci-epf-test uses get_features callback, which indicates Rockchip EP driver doesn't support linkup notification, remove pci_epf_linkup() from Rockchip EP driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Acked-by: Shawn Lin --- drivers/pci/controller/pcie-rockchip-ep.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 9b60ad323ac7..a5d799e2dff2 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -499,9 +499,6 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc) rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG); - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - return 0; } From 92f2b028418c6f59f92703eed111136ca0d22c39 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:12 +0530 Subject: [PATCH 1826/4277] PCI: designware-plat: Remove setting epc->features in Designware plat EP driver Now that pci-epf-test uses get_features callback and dw_plat_pcie_epc_features in Designware plat EP driver already indicates it doesn't support linkup notification and is MSIX capable, remove setting epc->features which is not used anymore by the endpoint function driver. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-plat.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index bd0516afc86f..3be87126aef3 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -70,14 +70,10 @@ static const struct dw_pcie_ops dw_pcie_ops = { static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct pci_epc *epc = ep->epc; enum pci_barno bar; for (bar = BAR_0; bar <= BAR_5; bar++) dw_pcie_ep_reset_bar(pci, bar); - - epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER; - epc->features |= EPC_FEATURE_MSIX_AVAILABLE; } static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, From 35ce0d7922d68021062a955407740d262f9ac811 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jan 2019 16:45:13 +0530 Subject: [PATCH 1827/4277] PCI: endpoint: Remove features member in struct pci_epc Since EPC features are now implemented using pci_epc_features and all the EPC drivers are moved to using pci_epc_features, remove features member in struct pci_epc and all the helper macros for configuring the features. Tested-by: Gustavo Pimentel Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- include/linux/pci-epc.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 94e1ecff98ce..c3ffa3917f88 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -99,7 +99,6 @@ struct pci_epc { struct config_group *group; /* spinlock to protect against concurrent access of EP controller */ spinlock_t lock; - unsigned int features; }; /** @@ -120,14 +119,6 @@ struct pci_epc_features { u64 bar_fixed_size[BAR_5 + 1]; }; -#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0) -#define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3)) -#define EPC_FEATURE_MSIX_AVAILABLE BIT(4) -#define EPC_FEATURE_SET_BAR(features, bar) \ - (features |= (EPC_FEATURE_BAR_MASK & (bar << 1))) -#define EPC_FEATURE_GET_BAR(features) \ - ((features & EPC_FEATURE_BAR_MASK) >> 1) - #define to_pci_epc(device) container_of((device), struct pci_epc, dev) #define pci_epc_create(dev, ops) \ From 7f736599d632dde93bda79cca54fd5dcfd937e06 Mon Sep 17 00:00:00 2001 From: Huang Zijiang Date: Thu, 14 Feb 2019 14:39:07 +0800 Subject: [PATCH 1828/4277] EDAC, altera: Add missing of_node_put() The call to of_parse_phandle() returns a node pointer with refcount incremented thus it must be explicitly decremented here after the last usage. Signed-off-by: Huang Zijiang Signed-off-by: Borislav Petkov Reviewed-by: Thor Thayer Cc: James Morse Cc: Mauro Carvalho Chehab Cc: linux-edac Cc: wang.yi59@zte.com.cn Link: https://lkml.kernel.org/r/1550126347-27984-1-git-send-email-huang.zijiang@zte.com.cn --- drivers/edac/altera_edac.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index c89d82aa2776..98e0bd8d9f50 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -1046,14 +1046,17 @@ altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask, return -ENODEV; } - if (of_address_to_resource(sysmgr_np, 0, &res)) + if (of_address_to_resource(sysmgr_np, 0, &res)) { + of_node_put(sysmgr_np); return -ENOMEM; + } /* Need physical address for SMCC call */ base = res.start; ecc_mgr_map = regmap_init(NULL, NULL, (void *)base, &s10_sdram_regmap_cfg); + of_node_put(sysmgr_np); } of_node_put(np_eccmgr); if (IS_ERR(ecc_mgr_map)) { From 3f4da372ec8e4ce58c17ac4f2e3c8891bbfea17e Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Tue, 12 Feb 2019 21:24:28 +0000 Subject: [PATCH 1829/4277] EDAC/mce_amd: Decode MCA_STATUS[Scrub] bit Previous AMD systems have had a bit in MCA_STATUS to indicate that an error was detected on a scrub operation. However, this bit was defined differently within different banks and families/models. Starting with Family 17h, MCA_STATUS[40] is either Reserved/Read-as-Zero or defined as "Scrub", for all MCA banks and CPU models. Therefore, this bit can be defined as the "Scrub" bit. Define MCA_STATUS[40] as "Scrub" and decode it in the AMD MCE decoding module for Family 17h and newer systems. Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: James Morse Cc: linux-edac Cc: Mauro Carvalho Chehab Cc: Pu Wen Cc: Qiuxu Zhuo Cc: Thomas Gleixner Cc: Tony Luck Cc: Vishal Verma Cc: x86-ml Link: https://lkml.kernel.org/r/20190212212417.107049-1-Yazen.Ghannam@amd.com --- arch/x86/include/asm/mce.h | 1 + drivers/edac/mce_amd.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 299a38536567..22d05e3835f0 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -48,6 +48,7 @@ #define MCI_STATUS_SYNDV BIT_ULL(53) /* synd reg. valid */ #define MCI_STATUS_DEFERRED BIT_ULL(44) /* uncorrected error, deferred exception */ #define MCI_STATUS_POISON BIT_ULL(43) /* access poisonous data */ +#define MCI_STATUS_SCRUB BIT_ULL(40) /* Error detected during scrub operation */ /* * McaX field if set indicates a given bank supports MCA extensions: diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index f286b880f981..b349c22bb386 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -1078,6 +1078,9 @@ amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) if (ecc) pr_cont("|%sECC", ((ecc == 2) ? "C" : "U")); + if (fam >= 0x17) + pr_cont("|%s", (m->status & MCI_STATUS_SCRUB ? "Scrub" : "-")); + pr_cont("]: 0x%016llx\n", m->status); if (m->status & MCI_STATUS_ADDRV) From a0bcd3c0b8a52ba0eb74371fa6be15ad0390ba67 Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Tue, 12 Feb 2019 21:24:29 +0000 Subject: [PATCH 1830/4277] EDAC/mce_amd: Decode MCA_STATUS in bit definition order Sort the MCA_STATUS bits in decode output to follow how they are defined in the register. The order is as follows: Bit | Decode ------------ 62 | Over 61 | UC 59 | MiscV 58 | AddrV 57 | PCC 55 | TCC 53 | SyndV 46 | CECC 45 | UECC 44 | Deferred 43 | Poison 40 | Scrub [ bp: Massage a bit. ] Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: Mauro Carvalho Chehab Cc: linux-edac Cc: x86@kernel.org Link: https://lkml.kernel.org/r/20190212212417.107049-2-Yazen.Ghannam@amd.com --- drivers/edac/mce_amd.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index b349c22bb386..0a1814dad6cf 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -1051,8 +1051,24 @@ amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) ((m->status & MCI_STATUS_UC) ? "UE" : (m->status & MCI_STATUS_DEFERRED) ? "-" : "CE"), ((m->status & MCI_STATUS_MISCV) ? "MiscV" : "-"), - ((m->status & MCI_STATUS_PCC) ? "PCC" : "-"), - ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-")); + ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-"), + ((m->status & MCI_STATUS_PCC) ? "PCC" : "-")); + + if (boot_cpu_has(X86_FEATURE_SMCA)) { + u32 low, high; + u32 addr = MSR_AMD64_SMCA_MCx_CONFIG(m->bank); + + if (!rdmsr_safe(addr, &low, &high) && + (low & MCI_CONFIG_MCAX)) + pr_cont("|%s", ((m->status & MCI_STATUS_TCC) ? "TCC" : "-")); + + pr_cont("|%s", ((m->status & MCI_STATUS_SYNDV) ? "SyndV" : "-")); + } + + /* do the two bits[14:13] together */ + ecc = (m->status >> 45) & 0x3; + if (ecc) + pr_cont("|%sECC", ((ecc == 2) ? "C" : "U")); if (fam >= 0x15) { pr_cont("|%s", (m->status & MCI_STATUS_DEFERRED ? "Deferred" : "-")); @@ -1062,22 +1078,6 @@ amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) pr_cont("|%s", (m->status & MCI_STATUS_POISON ? "Poison" : "-")); } - if (boot_cpu_has(X86_FEATURE_SMCA)) { - u32 low, high; - u32 addr = MSR_AMD64_SMCA_MCx_CONFIG(m->bank); - - pr_cont("|%s", ((m->status & MCI_STATUS_SYNDV) ? "SyndV" : "-")); - - if (!rdmsr_safe(addr, &low, &high) && - (low & MCI_CONFIG_MCAX)) - pr_cont("|%s", ((m->status & MCI_STATUS_TCC) ? "TCC" : "-")); - } - - /* do the two bits[14:13] together */ - ecc = (m->status >> 45) & 0x3; - if (ecc) - pr_cont("|%sECC", ((ecc == 2) ? "C" : "U")); - if (fam >= 0x17) pr_cont("|%s", (m->status & MCI_STATUS_SCRUB ? "Scrub" : "-")); From 77b773aea5a8abccbd25101390e1ba4b33b3fca0 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 15 Jan 2019 11:15:12 +0200 Subject: [PATCH 1831/4277] clk: ti: move clk_hw_omap list handling under generic part of the driver Currently the clk_hw_omap list is handled under the autoidle code, but it should be accessible generically. Add a few APIs towards this, and update the autoidle code to use the generic implementations. Signed-off-by: Tero Kristo Acked-by: Tony Lindgren Tested-by: Keerthy --- drivers/clk/ti/autoidle.c | 65 ++++++++++++++++----------------------- drivers/clk/ti/clk.c | 42 +++++++++++++++++++++++++ drivers/clk/ti/clock.h | 1 + 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index 7bb9afbe4058..a129b4b36ea3 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -35,7 +35,20 @@ struct clk_ti_autoidle { #define AUTOIDLE_LOW 0x1 static LIST_HEAD(autoidle_clks); -static LIST_HEAD(clk_hw_omap_clocks); + +static int _omap2_clk_deny_idle(struct clk_hw_omap *clk) +{ + if (clk->ops && clk->ops->deny_idle) + clk->ops->deny_idle(clk); + return 0; +} + +static int _omap2_clk_allow_idle(struct clk_hw_omap *clk) +{ + if (clk->ops && clk->ops->allow_idle) + clk->ops->allow_idle(clk); + return 0; +} /** * omap2_clk_deny_idle - disable autoidle on an OMAP clock @@ -45,12 +58,9 @@ static LIST_HEAD(clk_hw_omap_clocks); */ int omap2_clk_deny_idle(struct clk *clk) { - struct clk_hw_omap *c; + struct clk_hw_omap *c = to_clk_hw_omap(__clk_get_hw(clk)); - c = to_clk_hw_omap(__clk_get_hw(clk)); - if (c->ops && c->ops->deny_idle) - c->ops->deny_idle(c); - return 0; + return _omap2_clk_deny_idle(c); } /** @@ -61,12 +71,9 @@ int omap2_clk_deny_idle(struct clk *clk) */ int omap2_clk_allow_idle(struct clk *clk) { - struct clk_hw_omap *c; + struct clk_hw_omap *c = to_clk_hw_omap(__clk_get_hw(clk)); - c = to_clk_hw_omap(__clk_get_hw(clk)); - if (c->ops && c->ops->allow_idle) - c->ops->allow_idle(c); - return 0; + return _omap2_clk_allow_idle(c); } static void _allow_autoidle(struct clk_ti_autoidle *clk) @@ -167,26 +174,6 @@ int __init of_ti_clk_autoidle_setup(struct device_node *node) return 0; } -/** - * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock - * @hw: struct clk_hw * to initialize - * - * Add an OMAP clock @clk to the internal list of OMAP clocks. Used - * temporarily for autoidle handling, until this support can be - * integrated into the common clock framework code in some way. No - * return value. - */ -void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw) -{ - struct clk_hw_omap *c; - - if (clk_hw_get_flags(hw) & CLK_IS_BASIC) - return; - - c = to_clk_hw_omap(hw); - list_add(&c->node, &clk_hw_omap_clocks); -} - /** * omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that * support it @@ -198,11 +185,11 @@ void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw) */ int omap2_clk_enable_autoidle_all(void) { - struct clk_hw_omap *c; + int ret; - list_for_each_entry(c, &clk_hw_omap_clocks, node) - if (c->ops && c->ops->allow_idle) - c->ops->allow_idle(c); + ret = omap2_clk_for_each(_omap2_clk_allow_idle); + if (ret) + return ret; _clk_generic_allow_autoidle_all(); @@ -220,11 +207,11 @@ int omap2_clk_enable_autoidle_all(void) */ int omap2_clk_disable_autoidle_all(void) { - struct clk_hw_omap *c; + int ret; - list_for_each_entry(c, &clk_hw_omap_clocks, node) - if (c->ops && c->ops->deny_idle) - c->ops->deny_idle(c); + ret = omap2_clk_for_each(_omap2_clk_deny_idle); + if (ret) + return ret; _clk_generic_deny_autoidle_all(); diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index d0cd58534781..817284324d41 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -31,6 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "%s: " fmt, __func__ +static LIST_HEAD(clk_hw_omap_clocks); struct ti_clk_ll_ops *ti_clk_ll_ops; static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS]; @@ -517,3 +518,44 @@ struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, return clk; } + +/** + * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock + * @hw: struct clk_hw * to initialize + * + * Add an OMAP clock @clk to the internal list of OMAP clocks. Used + * temporarily for autoidle handling, until this support can be + * integrated into the common clock framework code in some way. No + * return value. + */ +void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw) +{ + struct clk_hw_omap *c; + + c = to_clk_hw_omap(hw); + list_add(&c->node, &clk_hw_omap_clocks); +} + +/** + * omap2_clk_for_each - call function for each registered clk_hw_omap + * @fn: pointer to a callback function + * + * Call @fn for each registered clk_hw_omap, passing @hw to each + * function. @fn must return 0 for success or any other value for + * failure. If @fn returns non-zero, the iteration across clocks + * will stop and the non-zero return value will be passed to the + * caller of omap2_clk_for_each(). + */ +int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw)) +{ + int ret; + struct clk_hw_omap *hw; + + list_for_each_entry(hw, &clk_hw_omap_clocks, node) { + ret = (*fn)(hw); + if (ret) + break; + } + + return ret; +} diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 9f312a219510..e5b8af38cfa0 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -301,6 +301,7 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, unsigned long *parent_rate); int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, struct clk_rate_request *req); +int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw)); extern struct ti_clk_ll_ops *ti_clk_ll_ops; From 7fd79ee78a0318bd23f92c3cf3f4aba03a5dd6ad Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 15 Jan 2019 11:15:13 +0200 Subject: [PATCH 1832/4277] clk: ti: add new API for checking if a provided clock is an OMAP clock omap2_clk_is_hw_omap can now be used to verify if the provided clk_hw is an omap HW clock or not. This is done to replace the usage of CLK_IS_BASIC flag within the TI clock drivers. Signed-off-by: Tero Kristo Acked-by: Tony Lindgren Tested-by: Keerthy --- drivers/clk/ti/clk.c | 19 +++++++++++++++++++ drivers/clk/ti/clock.h | 1 + 2 files changed, 20 insertions(+) diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 817284324d41..122af2680833 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -559,3 +559,22 @@ int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw)) return ret; } + +/** + * omap2_clk_is_hw_omap - check if the provided clk_hw is OMAP clock + * @hw: clk_hw to check if it is an omap clock or not + * + * Checks if the provided clk_hw is OMAP clock or not. Returns true if + * it is, false otherwise. + */ +bool omap2_clk_is_hw_omap(struct clk_hw *hw) +{ + struct clk_hw_omap *oclk; + + list_for_each_entry(oclk, &clk_hw_omap_clocks, node) { + if (&oclk->hw == hw) + return true; + } + + return false; +} diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index e5b8af38cfa0..034ff6a2be07 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -302,6 +302,7 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, struct clk_rate_request *req); int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw)); +bool omap2_clk_is_hw_omap(struct clk_hw *hw); extern struct ti_clk_ll_ops *ti_clk_ll_ops; From 8aa09cf322c196df95b52ed63c4cae605296f343 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 15 Jan 2019 11:15:14 +0200 Subject: [PATCH 1833/4277] clk: ti: remove usage of CLK_IS_BASIC Remove the usage of CLK_IS_BASIC flag completely from TI clock driver. In most cases, the use is completely redundant, but in some cases we need to use the new API to check if the clock is an OMAP clock or not. Signed-off-by: Tero Kristo Acked-by: Tony Lindgren Tested-by: Keerthy --- drivers/clk/ti/adpll.c | 2 +- drivers/clk/ti/clkctrl.c | 2 +- drivers/clk/ti/clockdomain.c | 2 +- drivers/clk/ti/divider.c | 2 +- drivers/clk/ti/dpll3xxx.c | 2 +- drivers/clk/ti/mux.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index 688e403333b9..0c210984765a 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -614,7 +614,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d, init.name = child_name; init.ops = ops; - init.flags = CLK_IS_BASIC; + init.flags = 0; co->hw.init = &init; parent_names[0] = __clk_get_name(clk0); parent_names[1] = __clk_get_name(clk1); diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c index 40630eb950fc..bf32d996177f 100644 --- a/drivers/clk/ti/clkctrl.c +++ b/drivers/clk/ti/clkctrl.c @@ -276,7 +276,7 @@ _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, init.parent_names = parents; init.num_parents = num_parents; init.ops = ops; - init.flags = CLK_IS_BASIC; + init.flags = 0; clk = ti_clk_register(NULL, clk_hw, init.name); if (IS_ERR_OR_NULL(clk)) { diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index 07a805125e98..423a99b9f10c 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -143,7 +143,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) continue; } clk_hw = __clk_get_hw(clk); - if (clk_hw_get_flags(clk_hw) & CLK_IS_BASIC) { + if (!omap2_clk_is_hw_omap(clk_hw)) { pr_warn("can't setup clkdm for basic clk %s\n", __clk_get_name(clk)); continue; diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 8d77090ad94a..cb5a81963ba9 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -336,7 +336,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, init.name = name; init.ops = &ti_clk_divider_ops; - init.flags = flags | CLK_IS_BASIC; + init.flags = flags; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c index 44b6b6403753..3dde6c8c3354 100644 --- a/drivers/clk/ti/dpll3xxx.c +++ b/drivers/clk/ti/dpll3xxx.c @@ -731,7 +731,7 @@ static struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw) do { do { hw = clk_hw_get_parent(hw); - } while (hw && (clk_hw_get_flags(hw) & CLK_IS_BASIC)); + } while (hw && (!omap2_clk_is_hw_omap(hw))); if (!hw) break; pclk = to_clk_hw_omap(hw); diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 883bdde94d04..b7f9a4f068bf 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -143,7 +143,7 @@ static struct clk *_register_mux(struct device *dev, const char *name, init.name = name; init.ops = &ti_clk_mux_ops; - init.flags = flags | CLK_IS_BASIC; + init.flags = flags; init.parent_names = parent_names; init.num_parents = num_parents; From ead478250b950f1f082d4cb57bed3adeacfe53c3 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 15 Jan 2019 11:15:15 +0200 Subject: [PATCH 1834/4277] clk: ti: generalize the init sequence of clk_hw_omap clocks Add a generic API for initializing clocks of clk_hw_omap type clocks, and convert the whole TI clock driver suite to use this for registering the clocks. Also, get rid of the now redundant API for adding the clocks to the OMAP HW clocks list; instead this is used directly from the register API. Signed-off-by: Tero Kristo Acked-by: Tony Lindgren Tested-by: Keerthy --- drivers/clk/ti/apll.c | 4 ++-- drivers/clk/ti/clk.c | 31 +++++++++++++++++++++---------- drivers/clk/ti/clock.h | 3 ++- drivers/clk/ti/dpll.c | 11 ++++------- drivers/clk/ti/gate.c | 2 +- drivers/clk/ti/interface.c | 4 +--- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 222f68bc3f2a..015a657d3382 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -165,7 +165,7 @@ static void __init omap_clk_register_apll(void *user, ad->clk_bypass = __clk_get_hw(clk); - clk = ti_clk_register(NULL, &clk_hw->hw, node->name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name); if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); kfree(clk_hw->hw.init->parent_names); @@ -402,7 +402,7 @@ static void __init of_omap2_apll_setup(struct device_node *node) if (ret) goto cleanup; - clk = clk_register(NULL, &clk_hw->hw); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name); if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); kfree(init); diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 122af2680833..d1f948163d6a 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -520,20 +520,31 @@ struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, } /** - * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock - * @hw: struct clk_hw * to initialize + * ti_clk_register_omap_hw - register a clk_hw_omap to the clock framework + * @dev: device for this clock + * @hw: hardware clock handle + * @con: connection ID for this clock * - * Add an OMAP clock @clk to the internal list of OMAP clocks. Used - * temporarily for autoidle handling, until this support can be - * integrated into the common clock framework code in some way. No - * return value. + * Registers a clk_hw_omap clock to the clock framewor, adds a clock alias + * for it, and adds the list to the available clk_hw_omap type clocks. + * Returns a handle to the registered clock if successful, ERR_PTR value + * in failure. */ -void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw) +struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw, + const char *con) { - struct clk_hw_omap *c; + struct clk *clk; + struct clk_hw_omap *oclk; - c = to_clk_hw_omap(hw); - list_add(&c->node, &clk_hw_omap_clocks); + clk = ti_clk_register(dev, hw, con); + if (IS_ERR(clk)) + return clk; + + oclk = to_clk_hw_omap(hw); + + list_add(&oclk->node, &clk_hw_omap_clocks); + + return clk; } /** diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 034ff6a2be07..1c0fac59d809 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -203,6 +203,8 @@ typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *); struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, const char *con); +struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw, + const char *con); int ti_clk_add_alias(struct device *dev, struct clk *clk, const char *con); void ti_clk_add_aliases(void); @@ -221,7 +223,6 @@ int ti_clk_retry_init(struct device_node *node, void *user, ti_of_clk_init_cb_t func); int ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type); -void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw); int of_ti_clk_autoidle_setup(struct device_node *node); void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks); diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 6c3329bc116f..659dadb23279 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -192,10 +192,9 @@ static void __init _register_dpll(void *user, dd->clk_bypass = __clk_get_hw(clk); /* register the clock */ - clk = ti_clk_register(NULL, &clk_hw->hw, node->name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name); if (!IS_ERR(clk)) { - omap2_init_clk_hw_omap_clocks(&clk_hw->hw); of_clk_add_provider(node, of_clk_src_simple_get, clk); kfree(clk_hw->hw.init->parent_names); kfree(clk_hw->hw.init); @@ -265,14 +264,12 @@ static void _register_dpll_x2(struct device_node *node, #endif /* register the clock */ - clk = ti_clk_register(NULL, &clk_hw->hw, name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name); - if (IS_ERR(clk)) { + if (IS_ERR(clk)) kfree(clk_hw); - } else { - omap2_init_clk_hw_omap_clocks(&clk_hw->hw); + else of_clk_add_provider(node, of_clk_src_simple_get, clk); - } } #endif diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c index 1c78fff5513c..504c0e91cdc7 100644 --- a/drivers/clk/ti/gate.c +++ b/drivers/clk/ti/gate.c @@ -123,7 +123,7 @@ static struct clk *_register_gate(struct device *dev, const char *name, init.flags = flags; - clk = ti_clk_register(NULL, &clk_hw->hw, name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name); if (IS_ERR(clk)) kfree(clk_hw); diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c index 87e00c2ee957..83e34429d3b1 100644 --- a/drivers/clk/ti/interface.c +++ b/drivers/clk/ti/interface.c @@ -57,12 +57,10 @@ static struct clk *_register_interface(struct device *dev, const char *name, init.num_parents = 1; init.parent_names = &parent_name; - clk = ti_clk_register(NULL, &clk_hw->hw, name); + clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name); if (IS_ERR(clk)) kfree(clk_hw); - else - omap2_init_clk_hw_omap_clocks(&clk_hw->hw); return clk; } From d277ce2d3a75c6c116a6119c3745694f5941eff5 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 16 Jan 2019 23:04:27 +0100 Subject: [PATCH 1835/4277] clk: ti: add a usecount for autoidle Multiple users might deny autoidle on a clock. So we should have some counting here, also according to the comment in _setup_iclk_autoidle(). Also setting autoidle regs is not atomic, so there is another reason for locking. Signed-off-by: Andreas Kemnade Acked-by: Tony Lindgren Tested-by: Keerthy Signed-off-by: Tero Kristo --- drivers/clk/ti/autoidle.c | 32 ++++++++++++++++++++++++++++---- include/linux/clk/ti.h | 1 + 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index a129b4b36ea3..964e97b5478a 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -36,17 +36,41 @@ struct clk_ti_autoidle { static LIST_HEAD(autoidle_clks); +/* + * we have some non-atomic read/write + * operations behind it, so lets + * take one lock for handling autoidle + * of all clocks + */ +static DEFINE_SPINLOCK(autoidle_spinlock); + static int _omap2_clk_deny_idle(struct clk_hw_omap *clk) { - if (clk->ops && clk->ops->deny_idle) - clk->ops->deny_idle(clk); + if (clk->ops && clk->ops->deny_idle) { + unsigned long irqflags; + + spin_lock_irqsave(&autoidle_spinlock, irqflags); + clk->autoidle_count++; + if (clk->autoidle_count == 1) + clk->ops->deny_idle(clk); + + spin_unlock_irqrestore(&autoidle_spinlock, irqflags); + } return 0; } static int _omap2_clk_allow_idle(struct clk_hw_omap *clk) { - if (clk->ops && clk->ops->allow_idle) - clk->ops->allow_idle(clk); + if (clk->ops && clk->ops->allow_idle) { + unsigned long irqflags; + + spin_lock_irqsave(&autoidle_spinlock, irqflags); + clk->autoidle_count--; + if (clk->autoidle_count == 0) + clk->ops->allow_idle(clk); + + spin_unlock_irqrestore(&autoidle_spinlock, irqflags); + } return 0; } diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index eacc5df57b99..78872efc7be0 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -160,6 +160,7 @@ struct clk_hw_omap { struct clockdomain *clkdm; const struct clk_hw_omap_ops *ops; u32 context; + int autoidle_count; }; /* From fddf4e29a6ce97d693e2a2e759f495ddc4ac2474 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 16 Jan 2019 23:04:28 +0100 Subject: [PATCH 1836/4277] clk: ti: check clock type before doing autoidle ops Code might use autoidle api with clocks not being omap2 clocks, so check if clock type is really omap2. Signed-off-by: Andreas Kemnade Acked-by: Tony Lindgren Tested-by: Keerthy Signed-off-by: Tero Kristo --- drivers/clk/ti/autoidle.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index 964e97b5478a..1cae226759dd 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -82,9 +82,15 @@ static int _omap2_clk_allow_idle(struct clk_hw_omap *clk) */ int omap2_clk_deny_idle(struct clk *clk) { - struct clk_hw_omap *c = to_clk_hw_omap(__clk_get_hw(clk)); + struct clk_hw *hw = __clk_get_hw(clk); - return _omap2_clk_deny_idle(c); + if (omap2_clk_is_hw_omap(hw)) { + struct clk_hw_omap *c = to_clk_hw_omap(hw); + + return _omap2_clk_deny_idle(c); + } + + return -EINVAL; } /** @@ -95,9 +101,15 @@ int omap2_clk_deny_idle(struct clk *clk) */ int omap2_clk_allow_idle(struct clk *clk) { - struct clk_hw_omap *c = to_clk_hw_omap(__clk_get_hw(clk)); + struct clk_hw *hw = __clk_get_hw(clk); - return _omap2_clk_allow_idle(c); + if (omap2_clk_is_hw_omap(hw)) { + struct clk_hw_omap *c = to_clk_hw_omap(hw); + + return _omap2_clk_allow_idle(c); + } + + return -EINVAL; } static void _allow_autoidle(struct clk_ti_autoidle *clk) From 12af39cad78e9cb8172793ca5d7f369eb144578b Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 16 Jan 2019 23:04:29 +0100 Subject: [PATCH 1837/4277] ARM: OMAP2+: hwmod: disable ick autoidling when a hwmod requires that Deny autoidle for hwmods with the OCPIF_SWSUP_IDLE flag, that makes hwmods working properly which cannot handle autoidle properly in lower power states. Affected is e. g. the omap_hdq. Since an ick might have mulitple users, autoidle is disabled when an individual user requires that rather than in _setup_iclk_autoidle. dss_ick is an example for that. Signed-off-by: Andreas Kemnade Acked-by: Tony Lindgren Tested-by: Keerthy Signed-off-by: Tero Kristo --- arch/arm/mach-omap2/omap_hwmod.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index b5531dd3ae9c..3a04c73ac03c 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1002,8 +1002,10 @@ static int _enable_clocks(struct omap_hwmod *oh) clk_enable(oh->_clk); list_for_each_entry(os, &oh->slave_ports, node) { - if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) + if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { + omap2_clk_deny_idle(os->_clk); clk_enable(os->_clk); + } } /* The opt clocks are controlled by the device driver. */ @@ -1055,8 +1057,10 @@ static int _disable_clocks(struct omap_hwmod *oh) clk_disable(oh->_clk); list_for_each_entry(os, &oh->slave_ports, node) { - if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) + if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { clk_disable(os->_clk); + omap2_clk_allow_idle(os->_clk); + } } if (oh->flags & HWMOD_OPT_CLKS_NEEDED) @@ -2436,9 +2440,13 @@ static void _setup_iclk_autoidle(struct omap_hwmod *oh) continue; if (os->flags & OCPIF_SWSUP_IDLE) { - /* XXX omap_iclk_deny_idle(c); */ + /* + * we might have multiple users of one iclk with + * different requirements, disable autoidle when + * the module is enabled, e.g. dss iclk + */ } else { - /* XXX omap_iclk_allow_idle(c); */ + /* we are enabling autoidle afterwards anyways */ clk_enable(os->_clk); } } From 8a2ee44a371c8cbef587ea609908c3cbf1645231 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 15 Feb 2019 19:13:07 +0800 Subject: [PATCH 1838/4277] btrfs: look at bi_size for repair decisions bio_readpage_error currently uses bi_vcnt to decide if it is worth retrying an I/O. But the vector count is mostly an implementation artifact - it really should figure out if there is more than a single sector worth retrying. Use bi_size for that and shift by PAGE_SHIFT. This really should be blocks/sectors, but given that btrfs doesn't support a sector size different from the PAGE_SIZE using the page size keeps the changes to a minimum. Reviewed-by: Omar Sandoval Reviewed-by: David Sterba Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/btrfs/extent_io.c | 2 +- include/linux/bio.h | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 52abe4082680..dc8ba3ee515d 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2350,7 +2350,7 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset, int read_mode = 0; blk_status_t status; int ret; - unsigned failed_bio_pages = bio_pages_all(failed_bio); + unsigned failed_bio_pages = failed_bio->bi_iter.bi_size >> PAGE_SHIFT; BUG_ON(bio_op(failed_bio) == REQ_OP_WRITE); diff --git a/include/linux/bio.h b/include/linux/bio.h index 7380b094dcca..72b4f7be2106 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -263,12 +263,6 @@ static inline void bio_get_last_bvec(struct bio *bio, struct bio_vec *bv) bv->bv_len = iter.bi_bvec_done; } -static inline unsigned bio_pages_all(struct bio *bio) -{ - WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)); - return bio->bi_vcnt; -} - static inline struct bio_vec *bio_first_bvec_all(struct bio *bio) { WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)); From 1a67356e9a4829da2935dd338630a550c59c8489 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:08 +0800 Subject: [PATCH 1839/4277] block: don't use bio->bi_vcnt to figure out segment number It is wrong to use bio->bi_vcnt to figure out how many segments there are in the bio even though CLONED flag isn't set on this bio, because this bio may be splitted or advanced. So always use bio_segments() in blk_recount_segments(), and it shouldn't cause any performance loss now because the physical segment number is figured out in blk_queue_split() and BIO_SEG_VALID is set meantime since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"). Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Fixes: 76d8137a3113 ("blk-merge: recaculate segment if it isn't less than max segments") Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-merge.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/block/blk-merge.c b/block/blk-merge.c index 71e9ac03f621..f85d878f313d 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -367,13 +367,7 @@ void blk_recalc_rq_segments(struct request *rq) void blk_recount_segments(struct request_queue *q, struct bio *bio) { - unsigned short seg_cnt; - - /* estimate segment number by bi_vcnt for non-cloned bio */ - if (bio_flagged(bio, BIO_CLONED)) - seg_cnt = bio_segments(bio); - else - seg_cnt = bio->bi_vcnt; + unsigned short seg_cnt = bio_segments(bio); if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) && (seg_cnt < queue_max_segments(q))) From 19d62f6d00972f957c94aba0975c14490cfed385 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:09 +0800 Subject: [PATCH 1840/4277] block: remove bvec_iter_rewind() Commit 7759eb23fd980 ("block: remove bio_rewind_iter()") removes bio_rewind_iter(), then no one uses bvec_iter_rewind() any more, so remove it. Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- include/linux/bvec.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/include/linux/bvec.h b/include/linux/bvec.h index 02c73c6aa805..ba0ae40e77c9 100644 --- a/include/linux/bvec.h +++ b/include/linux/bvec.h @@ -92,30 +92,6 @@ static inline bool bvec_iter_advance(const struct bio_vec *bv, return true; } -static inline bool bvec_iter_rewind(const struct bio_vec *bv, - struct bvec_iter *iter, - unsigned int bytes) -{ - while (bytes) { - unsigned len = min(bytes, iter->bi_bvec_done); - - if (iter->bi_bvec_done == 0) { - if (WARN_ONCE(iter->bi_idx == 0, - "Attempted to rewind iter beyond " - "bvec's boundaries\n")) { - return false; - } - iter->bi_idx--; - iter->bi_bvec_done = __bvec_iter_bvec(bv, *iter)->bv_len; - continue; - } - bytes -= len; - iter->bi_size += len; - iter->bi_bvec_done -= len; - } - return true; -} - #define for_each_bvec(bvl, bio_vec, iter, start) \ for (iter = (start); \ (iter).bi_size && \ From 3d75ca0adef4280650c6690a0c4702a74a6f3c95 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:10 +0800 Subject: [PATCH 1841/4277] block: introduce multi-page bvec helpers This patch introduces helpers of 'mp_bvec_iter_*' for multi-page bvec support. The introduced helpers treate one bvec as real multi-page segment, which may include more than one pages. The existed helpers of bvec_iter_* are interfaces for supporting current bvec iterator which is thought as single-page by drivers, fs, dm and etc. These introduced helpers will build single-page bvec in flight, so this way won't break current bio/bvec users, which needn't any change. Follows some multi-page bvec background: - bvecs stored in bio->bi_io_vec is always multi-page style - bvec(struct bio_vec) represents one physically contiguous I/O buffer, now the buffer may include more than one page after multi-page bvec is supported, and all these pages represented by one bvec is physically contiguous. Before multi-page bvec support, at most one page is included in one bvec, we call it single-page bvec. - .bv_page of the bvec points to the 1st page in the multi-page bvec - .bv_offset of the bvec is the offset of the buffer in the bvec The effect on the current drivers/filesystem/dm/bcache/...: - almost everyone supposes that one bvec only includes one single page, so we keep the sp interface not changed, for example, bio_for_each_segment() still returns single-page bvec - bio_for_each_segment_all() will return single-page bvec too - during iterating, iterator variable(struct bvec_iter) is always updated in multi-page bvec style, and bvec_iter_advance() is kept not changed - returned(copied) single-page bvec is built in flight by bvec helpers from the stored multi-page bvec Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- include/linux/bvec.h | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/include/linux/bvec.h b/include/linux/bvec.h index ba0ae40e77c9..0ae729b1c9fe 100644 --- a/include/linux/bvec.h +++ b/include/linux/bvec.h @@ -23,6 +23,7 @@ #include #include #include +#include /* * was unsigned short, but we might as well be ready for > 64kB I/O pages @@ -50,16 +51,39 @@ struct bvec_iter { */ #define __bvec_iter_bvec(bvec, iter) (&(bvec)[(iter).bi_idx]) -#define bvec_iter_page(bvec, iter) \ +/* multi-page (mp_bvec) helpers */ +#define mp_bvec_iter_page(bvec, iter) \ (__bvec_iter_bvec((bvec), (iter))->bv_page) -#define bvec_iter_len(bvec, iter) \ +#define mp_bvec_iter_len(bvec, iter) \ min((iter).bi_size, \ __bvec_iter_bvec((bvec), (iter))->bv_len - (iter).bi_bvec_done) -#define bvec_iter_offset(bvec, iter) \ +#define mp_bvec_iter_offset(bvec, iter) \ (__bvec_iter_bvec((bvec), (iter))->bv_offset + (iter).bi_bvec_done) +#define mp_bvec_iter_page_idx(bvec, iter) \ + (mp_bvec_iter_offset((bvec), (iter)) / PAGE_SIZE) + +#define mp_bvec_iter_bvec(bvec, iter) \ +((struct bio_vec) { \ + .bv_page = mp_bvec_iter_page((bvec), (iter)), \ + .bv_len = mp_bvec_iter_len((bvec), (iter)), \ + .bv_offset = mp_bvec_iter_offset((bvec), (iter)), \ +}) + +/* For building single-page bvec in flight */ + #define bvec_iter_offset(bvec, iter) \ + (mp_bvec_iter_offset((bvec), (iter)) % PAGE_SIZE) + +#define bvec_iter_len(bvec, iter) \ + min_t(unsigned, mp_bvec_iter_len((bvec), (iter)), \ + PAGE_SIZE - bvec_iter_offset((bvec), (iter))) + +#define bvec_iter_page(bvec, iter) \ + nth_page(mp_bvec_iter_page((bvec), (iter)), \ + mp_bvec_iter_page_idx((bvec), (iter))) + #define bvec_iter_bvec(bvec, iter) \ ((struct bio_vec) { \ .bv_page = bvec_iter_page((bvec), (iter)), \ From d18d91740ad22e9d7998884c4d80523d0ba95ddf Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:11 +0800 Subject: [PATCH 1842/4277] block: introduce bio_for_each_bvec() and rq_for_each_bvec() bio_for_each_bvec() is used for iterating over multi-page bvec for bio split & merge code. rq_for_each_bvec() can be used for drivers which may handle the multi-page bvec directly, so far loop is one perfect use case. Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- include/linux/bio.h | 10 ++++++++++ include/linux/blkdev.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/include/linux/bio.h b/include/linux/bio.h index 72b4f7be2106..7ef8a7505c0a 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -156,6 +156,16 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter, #define bio_for_each_segment(bvl, bio, iter) \ __bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter) +#define __bio_for_each_bvec(bvl, bio, iter, start) \ + for (iter = (start); \ + (iter).bi_size && \ + ((bvl = mp_bvec_iter_bvec((bio)->bi_io_vec, (iter))), 1); \ + bio_advance_iter((bio), &(iter), (bvl).bv_len)) + +/* iterate over multi-page bvec */ +#define bio_for_each_bvec(bvl, bio, iter) \ + __bio_for_each_bvec(bvl, bio, iter, (bio)->bi_iter) + #define bio_iter_last(bvec, iter) ((iter).bi_size == (bvec).bv_len) static inline unsigned bio_segments(struct bio *bio) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 3603270cb82d..b6292d469ea4 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -792,6 +792,10 @@ struct req_iterator { __rq_for_each_bio(_iter.bio, _rq) \ bio_for_each_segment(bvl, _iter.bio, _iter.iter) +#define rq_for_each_bvec(bvl, _rq, _iter) \ + __rq_for_each_bio(_iter.bio, _rq) \ + bio_for_each_bvec(bvl, _iter.bio, _iter.iter) + #define rq_iter_last(bvec, _iter) \ (_iter.bio->bi_next == NULL && \ bio_iter_last(bvec, _iter.iter)) From dcebd755926b0f39dd1e3ef75bd3b46943400df0 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:12 +0800 Subject: [PATCH 1843/4277] block: use bio_for_each_bvec() to compute multi-page bvec count First it is more efficient to use bio_for_each_bvec() in both blk_bio_segment_split() and __blk_recalc_rq_segments() to compute how many multi-page bvecs there are in the bio. Secondly once bio_for_each_bvec() is used, the bvec may need to be splitted because its length can be very longer than max segment size, so we have to split the big bvec into several segments. Thirdly when splitting multi-page bvec into segments, the max segment limit may be reached, so the bio split need to be considered under this situation too. Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-merge.c | 103 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/block/blk-merge.c b/block/blk-merge.c index f85d878f313d..4ef56b2d2aa5 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -161,6 +161,73 @@ static inline unsigned get_max_io_size(struct request_queue *q, return sectors; } +static unsigned get_max_segment_size(struct request_queue *q, + unsigned offset) +{ + unsigned long mask = queue_segment_boundary(q); + + /* default segment boundary mask means no boundary limit */ + if (mask == BLK_SEG_BOUNDARY_MASK) + return queue_max_segment_size(q); + + return min_t(unsigned long, mask - (mask & offset) + 1, + queue_max_segment_size(q)); +} + +/* + * Split the bvec @bv into segments, and update all kinds of + * variables. + */ +static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv, + unsigned *nsegs, unsigned *last_seg_size, + unsigned *front_seg_size, unsigned *sectors) +{ + unsigned len = bv->bv_len; + unsigned total_len = 0; + unsigned new_nsegs = 0, seg_size = 0; + + /* + * Multi-page bvec may be too big to hold in one segment, so the + * current bvec has to be splitted as multiple segments. + */ + while (len && new_nsegs + *nsegs < queue_max_segments(q)) { + seg_size = get_max_segment_size(q, bv->bv_offset + total_len); + seg_size = min(seg_size, len); + + new_nsegs++; + total_len += seg_size; + len -= seg_size; + + if ((bv->bv_offset + total_len) & queue_virt_boundary(q)) + break; + } + + if (!new_nsegs) + return !!len; + + /* update front segment size */ + if (!*nsegs) { + unsigned first_seg_size; + + if (new_nsegs == 1) + first_seg_size = get_max_segment_size(q, bv->bv_offset); + else + first_seg_size = queue_max_segment_size(q); + + if (*front_seg_size < first_seg_size) + *front_seg_size = first_seg_size; + } + + /* update other varibles */ + *last_seg_size = seg_size; + *nsegs += new_nsegs; + if (sectors) + *sectors += total_len >> 9; + + /* split in the middle of the bvec if len != 0 */ + return !!len; +} + static struct bio *blk_bio_segment_split(struct request_queue *q, struct bio *bio, struct bio_set *bs, @@ -174,7 +241,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q, struct bio *new = NULL; const unsigned max_sectors = get_max_io_size(q, bio); - bio_for_each_segment(bv, bio, iter) { + bio_for_each_bvec(bv, bio, iter) { /* * If the queue doesn't support SG gaps and adding this * offset would create a gap, disallow it. @@ -189,8 +256,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q, */ if (nsegs < queue_max_segments(q) && sectors < max_sectors) { - nsegs++; - sectors = max_sectors; + /* split in the middle of bvec */ + bv.bv_len = (max_sectors - sectors) << 9; + bvec_split_segs(q, &bv, &nsegs, + &seg_size, + &front_seg_size, + §ors); } goto split; } @@ -212,14 +283,12 @@ new_segment: if (nsegs == queue_max_segments(q)) goto split; - if (nsegs == 1 && seg_size > front_seg_size) - front_seg_size = seg_size; - - nsegs++; bvprv = bv; bvprvp = &bvprv; - seg_size = bv.bv_len; - sectors += bv.bv_len >> 9; + + if (bvec_split_segs(q, &bv, &nsegs, &seg_size, + &front_seg_size, §ors)) + goto split; } @@ -233,8 +302,6 @@ split: bio = new; } - if (nsegs == 1 && seg_size > front_seg_size) - front_seg_size = seg_size; bio->bi_seg_front_size = front_seg_size; if (seg_size > bio->bi_seg_back_size) bio->bi_seg_back_size = seg_size; @@ -297,6 +364,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q, struct bio_vec bv, bvprv = { NULL }; int prev = 0; unsigned int seg_size, nr_phys_segs; + unsigned front_seg_size = bio->bi_seg_front_size; struct bio *fbio, *bbio; struct bvec_iter iter; @@ -316,7 +384,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q, seg_size = 0; nr_phys_segs = 0; for_each_bio(bio) { - bio_for_each_segment(bv, bio, iter) { + bio_for_each_bvec(bv, bio, iter) { /* * If SG merging is disabled, each bio vector is * a segment @@ -336,20 +404,15 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q, continue; } new_segment: - if (nr_phys_segs == 1 && seg_size > - fbio->bi_seg_front_size) - fbio->bi_seg_front_size = seg_size; - - nr_phys_segs++; bvprv = bv; prev = 1; - seg_size = bv.bv_len; + bvec_split_segs(q, &bv, &nr_phys_segs, &seg_size, + &front_seg_size, NULL); } bbio = bio; } - if (nr_phys_segs == 1 && seg_size > fbio->bi_seg_front_size) - fbio->bi_seg_front_size = seg_size; + fbio->bi_seg_front_size = front_seg_size; if (seg_size > bbio->bi_seg_back_size) bbio->bi_seg_back_size = seg_size; From 862e5a5e6fa2c7fd514c179e9ceea27a49a3327f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:13 +0800 Subject: [PATCH 1844/4277] block: use bio_for_each_bvec() to map sg It is more efficient to use bio_for_each_bvec() to map sg, meantime we have to consider splitting multipage bvec as done in blk_bio_segment_split(). Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-merge.c | 70 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/block/blk-merge.c b/block/blk-merge.c index 4ef56b2d2aa5..1912499b08b7 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -464,6 +464,54 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio, return biovec_phys_mergeable(q, &end_bv, &nxt_bv); } +static struct scatterlist *blk_next_sg(struct scatterlist **sg, + struct scatterlist *sglist) +{ + if (!*sg) + return sglist; + + /* + * If the driver previously mapped a shorter list, we could see a + * termination bit prematurely unless it fully inits the sg table + * on each mapping. We KNOW that there must be more entries here + * or the driver would be buggy, so force clear the termination bit + * to avoid doing a full sg_init_table() in drivers for each command. + */ + sg_unmark_end(*sg); + return sg_next(*sg); +} + +static unsigned blk_bvec_map_sg(struct request_queue *q, + struct bio_vec *bvec, struct scatterlist *sglist, + struct scatterlist **sg) +{ + unsigned nbytes = bvec->bv_len; + unsigned nsegs = 0, total = 0, offset = 0; + + while (nbytes > 0) { + unsigned seg_size; + struct page *pg; + unsigned idx; + + *sg = blk_next_sg(sg, sglist); + + seg_size = get_max_segment_size(q, bvec->bv_offset + total); + seg_size = min(nbytes, seg_size); + + offset = (total + bvec->bv_offset) % PAGE_SIZE; + idx = (total + bvec->bv_offset) / PAGE_SIZE; + pg = nth_page(bvec->bv_page, idx); + + sg_set_page(*sg, pg, seg_size, offset); + + total += seg_size; + nbytes -= seg_size; + nsegs++; + } + + return nsegs; +} + static inline void __blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec, struct scatterlist *sglist, struct bio_vec *bvprv, @@ -481,25 +529,7 @@ __blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec, (*sg)->length += nbytes; } else { new_segment: - if (!*sg) - *sg = sglist; - else { - /* - * If the driver previously mapped a shorter - * list, we could see a termination bit - * prematurely unless it fully inits the sg - * table on each mapping. We KNOW that there - * must be more entries here or the driver - * would be buggy, so force clear the - * termination bit to avoid doing a full - * sg_init_table() in drivers for each command. - */ - sg_unmark_end(*sg); - *sg = sg_next(*sg); - } - - sg_set_page(*sg, bvec->bv_page, nbytes, bvec->bv_offset); - (*nsegs)++; + (*nsegs) += blk_bvec_map_sg(q, bvec, sglist, sg); } *bvprv = *bvec; } @@ -521,7 +551,7 @@ static int __blk_bios_map_sg(struct request_queue *q, struct bio *bio, int nsegs = 0; for_each_bio(bio) - bio_for_each_segment(bvec, bio, iter) + bio_for_each_bvec(bvec, bio, iter) __blk_segment_map_sg(q, &bvec, sglist, &bvprv, sg, &nsegs); From 45a3fb95298b326ab8175f2bd97bd8666017b692 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:14 +0800 Subject: [PATCH 1845/4277] block: introduce mp_bvec_last_segment() BTRFS and guard_bio_eod() need to get the last singlepage segment from one multipage bvec, so introduce this helper to make them happy. Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- include/linux/bvec.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/linux/bvec.h b/include/linux/bvec.h index 0ae729b1c9fe..21f76bad7be2 100644 --- a/include/linux/bvec.h +++ b/include/linux/bvec.h @@ -131,4 +131,26 @@ static inline bool bvec_iter_advance(const struct bio_vec *bv, .bi_bvec_done = 0, \ } +/* + * Get the last single-page segment from the multi-page bvec and store it + * in @seg + */ +static inline void mp_bvec_last_segment(const struct bio_vec *bvec, + struct bio_vec *seg) +{ + unsigned total = bvec->bv_offset + bvec->bv_len; + unsigned last_page = (total - 1) / PAGE_SIZE; + + seg->bv_page = nth_page(bvec->bv_page, last_page); + + /* the whole segment is inside the last page */ + if (bvec->bv_offset >= last_page * PAGE_SIZE) { + seg->bv_offset = bvec->bv_offset % PAGE_SIZE; + seg->bv_len = bvec->bv_len; + } else { + seg->bv_offset = 0; + seg->bv_len = total - last_page * PAGE_SIZE; + } +} + #endif /* __LINUX_BVEC_ITER_H */ From f70f44640759728d6e31326acbee08ca22d1066f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:15 +0800 Subject: [PATCH 1846/4277] fs/buffer.c: use bvec iterator to truncate the bio Once multi-page bvec is enabled, the last bvec may include more than one page, this patch use mp_bvec_last_segment() to truncate the bio. Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- fs/buffer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/buffer.c b/fs/buffer.c index 52d024bfdbc1..817871274c77 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -3032,7 +3032,10 @@ void guard_bio_eod(int op, struct bio *bio) /* ..and clear the end of the buffer for reads */ if (op == REQ_OP_READ) { - zero_user(bvec->bv_page, bvec->bv_offset + bvec->bv_len, + struct bio_vec bv; + + mp_bvec_last_segment(bvec, &bv); + zero_user(bv.bv_page, bv.bv_offset + bv.bv_len, truncated_bytes); } } From c3a7ce738009912f9d237bdabf4a20038522de10 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:16 +0800 Subject: [PATCH 1847/4277] btrfs: use mp_bvec_last_segment to get bio's last page Preparing for supporting multi-page bvec. Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- fs/btrfs/extent_io.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index dc8ba3ee515d..986ef49b0269 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2697,11 +2697,12 @@ static int __must_check submit_one_bio(struct bio *bio, int mirror_num, { blk_status_t ret = 0; struct bio_vec *bvec = bio_last_bvec_all(bio); - struct page *page = bvec->bv_page; + struct bio_vec bv; struct extent_io_tree *tree = bio->bi_private; u64 start; - start = page_offset(page) + bvec->bv_offset; + mp_bvec_last_segment(bvec, &bv); + start = page_offset(bv.bv_page) + bv.bv_offset; bio->bi_private = NULL; From 86af5952a8470f96b53830372c64469cb7ce780c Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:17 +0800 Subject: [PATCH 1848/4277] block: loop: pass multi-page bvec to iov_iter iov_iter is implemented on bvec itererator helpers, so it is safe to pass multi-page bvec to it, and this way is much more efficient than passing one page in each bvec. Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- drivers/block/loop.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index cf5538942834..8ef583197414 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -511,21 +511,22 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, loff_t pos, bool rw) { struct iov_iter iter; + struct req_iterator rq_iter; struct bio_vec *bvec; struct request *rq = blk_mq_rq_from_pdu(cmd); struct bio *bio = rq->bio; struct file *file = lo->lo_backing_file; + struct bio_vec tmp; unsigned int offset; - int segments = 0; + int nr_bvec = 0; int ret; - if (rq->bio != rq->biotail) { - struct req_iterator iter; - struct bio_vec tmp; + rq_for_each_bvec(tmp, rq, rq_iter) + nr_bvec++; - __rq_for_each_bio(bio, rq) - segments += bio_segments(bio); - bvec = kmalloc_array(segments, sizeof(struct bio_vec), + if (rq->bio != rq->biotail) { + + bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec), GFP_NOIO); if (!bvec) return -EIO; @@ -534,10 +535,10 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, /* * The bios of the request may be started from the middle of * the 'bvec' because of bio splitting, so we can't directly - * copy bio->bi_iov_vec to new bvec. The rq_for_each_segment + * copy bio->bi_iov_vec to new bvec. The rq_for_each_bvec * API will take care of all details for us. */ - rq_for_each_segment(tmp, rq, iter) { + rq_for_each_bvec(tmp, rq, rq_iter) { *bvec = tmp; bvec++; } @@ -551,11 +552,10 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, */ offset = bio->bi_iter.bi_bvec_done; bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter); - segments = bio_segments(bio); } atomic_set(&cmd->ref, 2); - iov_iter_bvec(&iter, rw, bvec, segments, blk_rq_bytes(rq)); + iov_iter_bvec(&iter, rw, bvec, nr_bvec, blk_rq_bytes(rq)); iter.iov_offset = offset; cmd->iocb.ki_pos = pos; From 2e1f4f4d2481d8bf111904c3e45fc0c4c94bf76e Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:18 +0800 Subject: [PATCH 1849/4277] bcache: avoid to use bio_for_each_segment_all() in bch_bio_alloc_pages() bch_bio_alloc_pages() is always called on one new bio, so it is safe to access the bvec table directly. Given it is the only kind of this case, open code the bvec table access since bio_for_each_segment_all() will be changed to support for iterating over multipage bvec. Acked-by: Coly Li Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- drivers/md/bcache/util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c index 20eddeac1531..62fb917f7a4f 100644 --- a/drivers/md/bcache/util.c +++ b/drivers/md/bcache/util.c @@ -270,7 +270,11 @@ int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask) int i; struct bio_vec *bv; - bio_for_each_segment_all(bv, bio, i) { + /* + * This is called on freshly new bio, so it is safe to access the + * bvec table directly. + */ + for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++, i++) { bv->bv_page = alloc_page(gfp_mask); if (!bv->bv_page) { while (--bv >= bio->bi_io_vec) From 6dc4f100c175dd0511ae8674786e7c9006cdfbfa Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:19 +0800 Subject: [PATCH 1850/4277] block: allow bio_for_each_segment_all() to iterate over multi-page bvec This patch introduces one extra iterator variable to bio_for_each_segment_all(), then we can allow bio_for_each_segment_all() to iterate over multi-page bvec. Given it is just one mechannical & simple change on all bio_for_each_segment_all() users, this patch does tree-wide change in one single patch, so that we can avoid to use a temporary helper for this conversion. Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/bio.c | 27 ++++++++++++++++++--------- block/bounce.c | 6 ++++-- drivers/md/bcache/btree.c | 3 ++- drivers/md/dm-crypt.c | 3 ++- drivers/md/raid1.c | 3 ++- drivers/staging/erofs/data.c | 3 ++- drivers/staging/erofs/unzip_vle.c | 3 ++- fs/block_dev.c | 6 ++++-- fs/btrfs/compression.c | 3 ++- fs/btrfs/disk-io.c | 3 ++- fs/btrfs/extent_io.c | 9 ++++++--- fs/btrfs/inode.c | 6 ++++-- fs/btrfs/raid56.c | 3 ++- fs/crypto/bio.c | 3 ++- fs/direct-io.c | 4 +++- fs/exofs/ore.c | 3 ++- fs/exofs/ore_raid.c | 3 ++- fs/ext4/page-io.c | 3 ++- fs/ext4/readpage.c | 3 ++- fs/f2fs/data.c | 9 ++++++--- fs/gfs2/lops.c | 9 ++++++--- fs/gfs2/meta_io.c | 3 ++- fs/iomap.c | 6 ++++-- fs/mpage.c | 3 ++- fs/xfs/xfs_aops.c | 5 +++-- include/linux/bio.h | 11 +++++++++-- include/linux/bvec.h | 30 ++++++++++++++++++++++++++++++ 27 files changed, 127 insertions(+), 46 deletions(-) diff --git a/block/bio.c b/block/bio.c index 4db1008309ed..968b12fea564 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1072,8 +1072,9 @@ static int bio_copy_from_iter(struct bio *bio, struct iov_iter *iter) { int i; struct bio_vec *bvec; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { ssize_t ret; ret = copy_page_from_iter(bvec->bv_page, @@ -1103,8 +1104,9 @@ static int bio_copy_to_iter(struct bio *bio, struct iov_iter iter) { int i; struct bio_vec *bvec; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { ssize_t ret; ret = copy_page_to_iter(bvec->bv_page, @@ -1126,8 +1128,9 @@ void bio_free_pages(struct bio *bio) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) __free_page(bvec->bv_page); } EXPORT_SYMBOL(bio_free_pages); @@ -1295,6 +1298,7 @@ struct bio *bio_map_user_iov(struct request_queue *q, struct bio *bio; int ret; struct bio_vec *bvec; + struct bvec_iter_all iter_all; if (!iov_iter_count(iter)) return ERR_PTR(-EINVAL); @@ -1368,7 +1372,7 @@ struct bio *bio_map_user_iov(struct request_queue *q, return bio; out_unmap: - bio_for_each_segment_all(bvec, bio, j) { + bio_for_each_segment_all(bvec, bio, j, iter_all) { put_page(bvec->bv_page); } bio_put(bio); @@ -1379,11 +1383,12 @@ static void __bio_unmap_user(struct bio *bio) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; /* * make sure we dirty pages we wrote to */ - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { if (bio_data_dir(bio) == READ) set_page_dirty_lock(bvec->bv_page); @@ -1475,8 +1480,9 @@ static void bio_copy_kern_endio_read(struct bio *bio) char *p = bio->bi_private; struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { memcpy(p, page_address(bvec->bv_page), bvec->bv_len); p += bvec->bv_len; } @@ -1585,8 +1591,9 @@ void bio_set_pages_dirty(struct bio *bio) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { if (!PageCompound(bvec->bv_page)) set_page_dirty_lock(bvec->bv_page); } @@ -1596,8 +1603,9 @@ static void bio_release_pages(struct bio *bio) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) put_page(bvec->bv_page); } @@ -1644,8 +1652,9 @@ void bio_check_pages_dirty(struct bio *bio) struct bio_vec *bvec; unsigned long flags; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page)) goto defer; } diff --git a/block/bounce.c b/block/bounce.c index ffb9e9ecfa7e..add085e28b1d 100644 --- a/block/bounce.c +++ b/block/bounce.c @@ -165,11 +165,12 @@ static void bounce_end_io(struct bio *bio, mempool_t *pool) struct bio_vec *bvec, orig_vec; int i; struct bvec_iter orig_iter = bio_orig->bi_iter; + struct bvec_iter_all iter_all; /* * free up bounce indirect pages used */ - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { orig_vec = bio_iter_iovec(bio_orig, orig_iter); if (bvec->bv_page != orig_vec.bv_page) { dec_zone_page_state(bvec->bv_page, NR_BOUNCE); @@ -294,6 +295,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, bool bounce = false; int sectors = 0; bool passthrough = bio_is_passthrough(*bio_orig); + struct bvec_iter_all iter_all; bio_for_each_segment(from, *bio_orig, iter) { if (i++ < BIO_MAX_PAGES) @@ -313,7 +315,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, bio = bounce_clone_bio(*bio_orig, GFP_NOIO, passthrough ? NULL : &bounce_bio_set); - bio_for_each_segment_all(to, bio, i) { + bio_for_each_segment_all(to, bio, i, iter_all) { struct page *page = to->bv_page; if (page_to_pfn(page) <= q->limits.bounce_pfn) diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 23cb1dc7296b..64def336f053 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -432,8 +432,9 @@ static void do_btree_node_write(struct btree *b) int j; struct bio_vec *bv; void *base = (void *) ((unsigned long) i & ~(PAGE_SIZE - 1)); + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bv, b->bio, j) + bio_for_each_segment_all(bv, b->bio, j, iter_all) memcpy(page_address(bv->bv_page), base + j * PAGE_SIZE, PAGE_SIZE); diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 47d4e0d30bf0..9a29037f5615 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1447,8 +1447,9 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone) { unsigned int i; struct bio_vec *bv; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bv, clone, i) { + bio_for_each_segment_all(bv, clone, i, iter_all) { BUG_ON(!bv->bv_page); mempool_free(bv->bv_page, &cc->page_pool); } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 7e63ccc4ae7b..88c61d3090b0 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2112,13 +2112,14 @@ static void process_checks(struct r1bio *r1_bio) struct page **spages = get_resync_pages(sbio)->pages; struct bio_vec *bi; int page_len[RESYNC_PAGES] = { 0 }; + struct bvec_iter_all iter_all; if (sbio->bi_end_io != end_sync_read) continue; /* Now we can 'fixup' the error value */ sbio->bi_status = 0; - bio_for_each_segment_all(bi, sbio, j) + bio_for_each_segment_all(bi, sbio, j, iter_all) page_len[j] = bi->bv_len; if (!status) { diff --git a/drivers/staging/erofs/data.c b/drivers/staging/erofs/data.c index 5a55f0bfdfbb..4871ba7b7d9a 100644 --- a/drivers/staging/erofs/data.c +++ b/drivers/staging/erofs/data.c @@ -20,8 +20,9 @@ static inline void read_endio(struct bio *bio) int i; struct bio_vec *bvec; const blk_status_t err = bio->bi_status; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; /* page is already locked */ diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c index 4ac1099a39c6..c057c5616b1d 100644 --- a/drivers/staging/erofs/unzip_vle.c +++ b/drivers/staging/erofs/unzip_vle.c @@ -830,8 +830,9 @@ static inline void z_erofs_vle_read_endio(struct bio *bio) #ifdef EROFS_FS_HAS_MANAGED_CACHE struct address_space *mc = NULL; #endif + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; bool cachemngd = false; diff --git a/fs/block_dev.c b/fs/block_dev.c index 58a4c1217fa8..7758adee6efe 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -211,6 +211,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, ssize_t ret; blk_qc_t qc; int i; + struct bvec_iter_all iter_all; if ((pos | iov_iter_alignment(iter)) & (bdev_logical_block_size(bdev) - 1)) @@ -260,7 +261,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, } __set_current_state(TASK_RUNNING); - bio_for_each_segment_all(bvec, &bio, i) { + bio_for_each_segment_all(bvec, &bio, i, iter_all) { if (should_dirty && !PageCompound(bvec->bv_page)) set_page_dirty_lock(bvec->bv_page); put_page(bvec->bv_page); @@ -329,8 +330,9 @@ static void blkdev_bio_end_io(struct bio *bio) } else { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) put_page(bvec->bv_page); bio_put(bio); } diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 548057630b69..6896ea60c843 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -162,13 +162,14 @@ csum_failed: } else { int i; struct bio_vec *bvec; + struct bvec_iter_all iter_all; /* * we have verified the checksum already, set page * checked so the end_io handlers know about it */ ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, cb->orig_bio, i) + bio_for_each_segment_all(bvec, cb->orig_bio, i, iter_all) SetPageChecked(bvec->bv_page); bio_endio(cb->orig_bio); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 6a2a2a951705..ca1b7da6dd1b 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -832,9 +832,10 @@ static blk_status_t btree_csum_one_bio(struct bio *bio) struct bio_vec *bvec; struct btrfs_root *root; int i, ret = 0; + struct bvec_iter_all iter_all; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { root = BTRFS_I(bvec->bv_page->mapping->host)->root; ret = csum_dirty_buffer(root->fs_info, bvec->bv_page); if (ret) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 986ef49b0269..4ed58c9a94a9 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2422,9 +2422,10 @@ static void end_bio_extent_writepage(struct bio *bio) u64 start; u64 end; int i; + struct bvec_iter_all iter_all; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; struct inode *inode = page->mapping->host; struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); @@ -2493,9 +2494,10 @@ static void end_bio_extent_readpage(struct bio *bio) int mirror; int ret; int i; + struct bvec_iter_all iter_all; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; struct inode *inode = page->mapping->host; struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); @@ -3635,9 +3637,10 @@ static void end_bio_extent_buffer_writepage(struct bio *bio) struct bio_vec *bvec; struct extent_buffer *eb; int i, done; + struct bvec_iter_all iter_all; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; eb = (struct extent_buffer *)page->private; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5c349667c761..7ade5769f691 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7777,6 +7777,7 @@ static void btrfs_retry_endio_nocsum(struct bio *bio) struct bio_vec *bvec; struct extent_io_tree *io_tree, *failure_tree; int i; + struct bvec_iter_all iter_all; if (bio->bi_status) goto end; @@ -7788,7 +7789,7 @@ static void btrfs_retry_endio_nocsum(struct bio *bio) done->uptodate = 1; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) clean_io_failure(BTRFS_I(inode)->root->fs_info, failure_tree, io_tree, done->start, bvec->bv_page, btrfs_ino(BTRFS_I(inode)), 0); @@ -7867,6 +7868,7 @@ static void btrfs_retry_endio(struct bio *bio) int uptodate; int ret; int i; + struct bvec_iter_all iter_all; if (bio->bi_status) goto end; @@ -7880,7 +7882,7 @@ static void btrfs_retry_endio(struct bio *bio) failure_tree = &BTRFS_I(inode)->io_failure_tree; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { ret = __readpage_endio_check(inode, io_bio, i, bvec->bv_page, bvec->bv_offset, done->start, bvec->bv_len); diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index e74455eb42f9..1869ba8e5981 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1443,10 +1443,11 @@ static void set_bio_pages_uptodate(struct bio *bio) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; ASSERT(!bio_flagged(bio, BIO_CLONED)); - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) SetPageUptodate(bvec->bv_page); } diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 0959044c5cee..5759bcd018cd 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -30,8 +30,9 @@ static void __fscrypt_decrypt_bio(struct bio *bio, bool done) { struct bio_vec *bv; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bv, bio, i) { + bio_for_each_segment_all(bv, bio, i, iter_all) { struct page *page = bv->bv_page; int ret = fscrypt_decrypt_page(page->mapping->host, page, PAGE_SIZE, 0, page->index); diff --git a/fs/direct-io.c b/fs/direct-io.c index ec2fb6fe6d37..9bb015bc4a83 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -551,7 +551,9 @@ static blk_status_t dio_bio_complete(struct dio *dio, struct bio *bio) if (dio->is_async && dio->op == REQ_OP_READ && dio->should_dirty) { bio_check_pages_dirty(bio); /* transfers ownership */ } else { - bio_for_each_segment_all(bvec, bio, i) { + struct bvec_iter_all iter_all; + + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; if (dio->op == REQ_OP_READ && !PageCompound(page) && diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c index 5331a15a61f1..24a8e34882e9 100644 --- a/fs/exofs/ore.c +++ b/fs/exofs/ore.c @@ -420,8 +420,9 @@ static void _clear_bio(struct bio *bio) { struct bio_vec *bv; unsigned i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bv, bio, i) { + bio_for_each_segment_all(bv, bio, i, iter_all) { unsigned this_count = bv->bv_len; if (likely(PAGE_SIZE == this_count)) diff --git a/fs/exofs/ore_raid.c b/fs/exofs/ore_raid.c index 199590f36203..e83bab54b03e 100644 --- a/fs/exofs/ore_raid.c +++ b/fs/exofs/ore_raid.c @@ -468,11 +468,12 @@ static void _mark_read4write_pages_uptodate(struct ore_io_state *ios, int ret) /* loop on all devices all pages */ for (d = 0; d < ios->numdevs; d++) { struct bio *bio = ios->per_dev[d].bio; + struct bvec_iter_all iter_all; if (!bio) continue; - bio_for_each_segment_all(bv, bio, i) { + bio_for_each_segment_all(bv, bio, i, iter_all) { struct page *page = bv->bv_page; SetPageUptodate(page); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 2aa62d58d8dd..cff4c4aa7a9c 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -63,8 +63,9 @@ static void ext4_finish_bio(struct bio *bio) { int i; struct bio_vec *bvec; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; #ifdef CONFIG_EXT4_FS_ENCRYPTION struct page *data_page = NULL; diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 6aa282ee455a..e53639784892 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -72,6 +72,7 @@ static void mpage_end_io(struct bio *bio) { struct bio_vec *bv; int i; + struct bvec_iter_all iter_all; if (ext4_bio_encrypted(bio)) { if (bio->bi_status) { @@ -81,7 +82,7 @@ static void mpage_end_io(struct bio *bio) return; } } - bio_for_each_segment_all(bv, bio, i) { + bio_for_each_segment_all(bv, bio, i, iter_all) { struct page *page = bv->bv_page; if (!bio->bi_status) { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f91d8630c9a2..da060b77f64d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -87,8 +87,9 @@ static void __read_end_io(struct bio *bio) struct page *page; struct bio_vec *bv; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bv, bio, i) { + bio_for_each_segment_all(bv, bio, i, iter_all) { page = bv->bv_page; /* PG_error was set if any post_read step failed */ @@ -164,13 +165,14 @@ static void f2fs_write_end_io(struct bio *bio) struct f2fs_sb_info *sbi = bio->bi_private; struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; if (time_to_inject(sbi, FAULT_WRITE_IO)) { f2fs_show_injection_info(FAULT_WRITE_IO); bio->bi_status = BLK_STS_IOERR; } - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; enum count_type type = WB_DATA_TYPE(page); @@ -347,6 +349,7 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, struct bio_vec *bvec; struct page *target; int i; + struct bvec_iter_all iter_all; if (!io->bio) return false; @@ -354,7 +357,7 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, if (!inode && !page && !ino) return true; - bio_for_each_segment_all(bvec, io->bio, i) { + bio_for_each_segment_all(bvec, io->bio, i, iter_all) { if (bvec->bv_page->mapping) target = bvec->bv_page; diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 94dcab655bc0..15deefeaafd0 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -170,7 +170,8 @@ u64 gfs2_log_bmap(struct gfs2_sbd *sdp) * that is pinned in the pagecache. */ -static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, struct bio_vec *bvec, +static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, + struct bio_vec *bvec, blk_status_t error) { struct buffer_head *bh, *next; @@ -208,6 +209,7 @@ static void gfs2_end_log_write(struct bio *bio) struct bio_vec *bvec; struct page *page; int i; + struct bvec_iter_all iter_all; if (bio->bi_status) { fs_err(sdp, "Error %d writing to journal, jid=%u\n", @@ -215,7 +217,7 @@ static void gfs2_end_log_write(struct bio *bio) wake_up(&sdp->sd_logd_waitq); } - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { page = bvec->bv_page; if (page_has_buffers(page)) gfs2_end_log_write_bh(sdp, bvec, bio->bi_status); @@ -388,8 +390,9 @@ static void gfs2_end_log_read(struct bio *bio) struct page *page; struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { page = bvec->bv_page; if (bio->bi_status) { int err = blk_status_to_errno(bio->bi_status); diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index be9c0bf697fe..3201342404a7 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -190,8 +190,9 @@ static void gfs2_meta_read_endio(struct bio *bio) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) { + bio_for_each_segment_all(bvec, bio, i, iter_all) { struct page *page = bvec->bv_page; struct buffer_head *bh = page_buffers(page); unsigned int len = bvec->bv_len; diff --git a/fs/iomap.c b/fs/iomap.c index a3088fae567b..af736acd9006 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -267,8 +267,9 @@ iomap_read_end_io(struct bio *bio) int error = blk_status_to_errno(bio->bi_status); struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) iomap_read_page_end_io(bvec, error); bio_put(bio); } @@ -1559,8 +1560,9 @@ static void iomap_dio_bio_end_io(struct bio *bio) } else { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) put_page(bvec->bv_page); bio_put(bio); } diff --git a/fs/mpage.c b/fs/mpage.c index c820dc9bebab..3f19da75178b 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -48,8 +48,9 @@ static void mpage_end_io(struct bio *bio) { struct bio_vec *bv; int i; + struct bvec_iter_all iter_all; - bio_for_each_segment_all(bv, bio, i) { + bio_for_each_segment_all(bv, bio, i, iter_all) { struct page *page = bv->bv_page; page_endio(page, bio_op(bio), blk_status_to_errno(bio->bi_status)); diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 338b9d9984e0..1f1829e506e8 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -62,7 +62,7 @@ xfs_find_daxdev_for_inode( static void xfs_finish_page_writeback( struct inode *inode, - struct bio_vec *bvec, + struct bio_vec *bvec, int error) { struct iomap_page *iop = to_iomap_page(bvec->bv_page); @@ -98,6 +98,7 @@ xfs_destroy_ioend( for (bio = &ioend->io_inline_bio; bio; bio = next) { struct bio_vec *bvec; int i; + struct bvec_iter_all iter_all; /* * For the last bio, bi_private points to the ioend, so we @@ -109,7 +110,7 @@ xfs_destroy_ioend( next = bio->bi_private; /* walk each page on bio, ending page IO on them */ - bio_for_each_segment_all(bvec, bio, i) + bio_for_each_segment_all(bvec, bio, i, iter_all) xfs_finish_page_writeback(inode, bvec, error); bio_put(bio); } diff --git a/include/linux/bio.h b/include/linux/bio.h index 7ef8a7505c0a..089370eb84d9 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -128,12 +128,19 @@ static inline bool bio_full(struct bio *bio) return bio->bi_vcnt >= bio->bi_max_vecs; } +#define mp_bvec_for_each_segment(bv, bvl, i, iter_all) \ + for (bv = bvec_init_iter_all(&iter_all); \ + (iter_all.done < (bvl)->bv_len) && \ + (mp_bvec_next_segment((bvl), &iter_all), 1); \ + iter_all.done += bv->bv_len, i += 1) + /* * drivers should _never_ use the all version - the bio may have been split * before it got to the driver and the driver won't own all of it */ -#define bio_for_each_segment_all(bvl, bio, i) \ - for (i = 0, bvl = (bio)->bi_io_vec; i < (bio)->bi_vcnt; i++, bvl++) +#define bio_for_each_segment_all(bvl, bio, i, iter_all) \ + for (i = 0, iter_all.idx = 0; iter_all.idx < (bio)->bi_vcnt; iter_all.idx++) \ + mp_bvec_for_each_segment(bvl, &((bio)->bi_io_vec[iter_all.idx]), i, iter_all) static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter, unsigned bytes) diff --git a/include/linux/bvec.h b/include/linux/bvec.h index 21f76bad7be2..30a57b68d017 100644 --- a/include/linux/bvec.h +++ b/include/linux/bvec.h @@ -45,6 +45,12 @@ struct bvec_iter { current bvec */ }; +struct bvec_iter_all { + struct bio_vec bv; + int idx; + unsigned done; +}; + /* * various member access, note that bio_data should of course not be used * on highmem page vectors @@ -131,6 +137,30 @@ static inline bool bvec_iter_advance(const struct bio_vec *bv, .bi_bvec_done = 0, \ } +static inline struct bio_vec *bvec_init_iter_all(struct bvec_iter_all *iter_all) +{ + iter_all->bv.bv_page = NULL; + iter_all->done = 0; + + return &iter_all->bv; +} + +static inline void mp_bvec_next_segment(const struct bio_vec *bvec, + struct bvec_iter_all *iter_all) +{ + struct bio_vec *bv = &iter_all->bv; + + if (bv->bv_page) { + bv->bv_page = nth_page(bv->bv_page, 1); + bv->bv_offset = 0; + } else { + bv->bv_page = bvec->bv_page; + bv->bv_offset = bvec->bv_offset; + } + bv->bv_len = min_t(unsigned int, PAGE_SIZE - bv->bv_offset, + bvec->bv_len - iter_all->done); +} + /* * Get the last single-page segment from the multi-page bvec and store it * in @seg From 07173c3ec276cbb18dc0e0687d37d310e98a1480 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:20 +0800 Subject: [PATCH 1851/4277] block: enable multipage bvecs This patch pulls the trigger for multi-page bvecs. Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/bio.c | 22 +++++++++++++++------- fs/iomap.c | 4 ++-- fs/xfs/xfs_aops.c | 4 ++-- include/linux/bio.h | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/block/bio.c b/block/bio.c index 968b12fea564..83a2dfa417ca 100644 --- a/block/bio.c +++ b/block/bio.c @@ -753,6 +753,8 @@ EXPORT_SYMBOL(bio_add_pc_page); * @page: page to add * @len: length of the data to add * @off: offset of the data in @page + * @same_page: if %true only merge if the new data is in the same physical + * page as the last segment of the bio. * * Try to add the data at @page + @off to the last bvec of @bio. This is a * a useful optimisation for file systems with a block size smaller than the @@ -761,19 +763,25 @@ EXPORT_SYMBOL(bio_add_pc_page); * Return %true on success or %false on failure. */ bool __bio_try_merge_page(struct bio *bio, struct page *page, - unsigned int len, unsigned int off) + unsigned int len, unsigned int off, bool same_page) { if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) return false; if (bio->bi_vcnt > 0) { struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; + phys_addr_t vec_end_addr = page_to_phys(bv->bv_page) + + bv->bv_offset + bv->bv_len - 1; + phys_addr_t page_addr = page_to_phys(page); - if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) { - bv->bv_len += len; - bio->bi_iter.bi_size += len; - return true; - } + if (vec_end_addr + 1 != page_addr + off) + return false; + if (same_page && (vec_end_addr & PAGE_MASK) != page_addr) + return false; + + bv->bv_len += len; + bio->bi_iter.bi_size += len; + return true; } return false; } @@ -819,7 +827,7 @@ EXPORT_SYMBOL_GPL(__bio_add_page); int bio_add_page(struct bio *bio, struct page *page, unsigned int len, unsigned int offset) { - if (!__bio_try_merge_page(bio, page, len, offset)) { + if (!__bio_try_merge_page(bio, page, len, offset, false)) { if (bio_full(bio)) return 0; __bio_add_page(bio, page, len, offset); diff --git a/fs/iomap.c b/fs/iomap.c index af736acd9006..0c350e658b7f 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -318,7 +318,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data, */ sector = iomap_sector(iomap, pos); if (ctx->bio && bio_end_sector(ctx->bio) == sector) { - if (__bio_try_merge_page(ctx->bio, page, plen, poff)) + if (__bio_try_merge_page(ctx->bio, page, plen, poff, true)) goto done; is_contig = true; } @@ -349,7 +349,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data, ctx->bio->bi_end_io = iomap_read_end_io; } - __bio_add_page(ctx->bio, page, plen, poff); + bio_add_page(ctx->bio, page, plen, poff); done: /* * Move the caller beyond our range so that it keeps making progress. diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 1f1829e506e8..b9fd44168f61 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -616,12 +616,12 @@ xfs_add_to_ioend( bdev, sector); } - if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff)) { + if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) { if (iop) atomic_inc(&iop->write_count); if (bio_full(wpc->ioend->io_bio)) xfs_chain_bio(wpc->ioend, wbc, bdev, sector); - __bio_add_page(wpc->ioend->io_bio, page, len, poff); + bio_add_page(wpc->ioend->io_bio, page, len, poff); } wpc->ioend->io_size += len; diff --git a/include/linux/bio.h b/include/linux/bio.h index 089370eb84d9..9f77adcfde82 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -441,7 +441,7 @@ extern int bio_add_page(struct bio *, struct page *, unsigned int,unsigned int); extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *, unsigned int, unsigned int); bool __bio_try_merge_page(struct bio *bio, struct page *page, - unsigned int len, unsigned int off); + unsigned int len, unsigned int off, bool same_page); void __bio_add_page(struct bio *bio, struct page *page, unsigned int len, unsigned int off); int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter); From 6861428921b51113520cd47897be6c2774e4fc58 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:21 +0800 Subject: [PATCH 1852/4277] block: always define BIO_MAX_PAGES as 256 Now multi-page bvec can cover CONFIG_THP_SWAP, so we don't need to increase BIO_MAX_PAGES for it. CONFIG_THP_SWAP needs to split one THP into normal pages and adds them all to one bio. With multipage-bvec, it just takes one bvec to hold them all. Reviewed-by: Omar Sandoval Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- include/linux/bio.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/linux/bio.h b/include/linux/bio.h index 9f77adcfde82..bdd11d4c2f05 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -34,15 +34,7 @@ #define BIO_BUG_ON #endif -#ifdef CONFIG_THP_SWAP -#if HPAGE_PMD_NR > 256 -#define BIO_MAX_PAGES HPAGE_PMD_NR -#else #define BIO_MAX_PAGES 256 -#endif -#else -#define BIO_MAX_PAGES 256 -#endif #define bio_prio(bio) (bio)->bi_ioprio #define bio_set_prio(bio, prio) ((bio)->bi_ioprio = prio) From ac4fa1d107addb2c6b21067d8945a39316a09fc8 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:22 +0800 Subject: [PATCH 1853/4277] block: document usage of bio iterator helpers Now multi-page bvec is supported, some helpers may return page by page, meantime some may return segment by segment, this patch documents the usage. Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- Documentation/block/biovecs.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Documentation/block/biovecs.txt b/Documentation/block/biovecs.txt index 25689584e6e0..ce6eccaf5df7 100644 --- a/Documentation/block/biovecs.txt +++ b/Documentation/block/biovecs.txt @@ -117,3 +117,28 @@ Other implications: size limitations and the limitations of the underlying devices. Thus there's no need to define ->merge_bvec_fn() callbacks for individual block drivers. + +Usage of helpers: +================= + +* The following helpers whose names have the suffix of "_all" can only be used +on non-BIO_CLONED bio. They are usually used by filesystem code. Drivers +shouldn't use them because the bio may have been split before it reached the +driver. + + bio_for_each_segment_all() + bio_first_bvec_all() + bio_first_page_all() + bio_last_bvec_all() + +* The following helpers iterate over single-page segment. The passed 'struct +bio_vec' will contain a single-page IO vector during the iteration + + bio_for_each_segment() + bio_for_each_segment_all() + +* The following helpers iterate over multi-page bvec. The passed 'struct +bio_vec' will contain a multi-page IO vector during the iteration + + bio_for_each_bvec() + rq_for_each_bvec() From 2705c93742e91730d335838025d75d8043861174 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:23 +0800 Subject: [PATCH 1854/4277] block: kill QUEUE_FLAG_NO_SG_MERGE Since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"), physical segment number is mainly figured out in blk_queue_split() for fast path, and the flag of BIO_SEG_VALID is set there too. Now only blk_recount_segments() and blk_recalc_rq_segments() use this flag. Basically blk_recount_segments() is bypassed in fast path given BIO_SEG_VALID is set in blk_queue_split(). For another user of blk_recalc_rq_segments(): - run in partial completion branch of blk_update_request, which is an unusual case - run in blk_cloned_rq_check_limits(), still not a big problem if the flag is killed since dm-rq is the only user. Multi-page bvec is enabled now, not doing S/G merging is rather pointless with the current setup of the I/O path, as it isn't going to save you a significant amount of cycles. Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-merge.c | 31 ++++++------------------------- block/blk-mq-debugfs.c | 1 - block/blk-mq.c | 3 --- drivers/md/dm-table.c | 13 ------------- include/linux/blkdev.h | 1 - 5 files changed, 6 insertions(+), 43 deletions(-) diff --git a/block/blk-merge.c b/block/blk-merge.c index 1912499b08b7..bed065904677 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -358,8 +358,7 @@ void blk_queue_split(struct request_queue *q, struct bio **bio) EXPORT_SYMBOL(blk_queue_split); static unsigned int __blk_recalc_rq_segments(struct request_queue *q, - struct bio *bio, - bool no_sg_merge) + struct bio *bio) { struct bio_vec bv, bvprv = { NULL }; int prev = 0; @@ -385,13 +384,6 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q, nr_phys_segs = 0; for_each_bio(bio) { bio_for_each_bvec(bv, bio, iter) { - /* - * If SG merging is disabled, each bio vector is - * a segment - */ - if (no_sg_merge) - goto new_segment; - if (prev) { if (seg_size + bv.bv_len > queue_max_segment_size(q)) @@ -421,27 +413,16 @@ new_segment: void blk_recalc_rq_segments(struct request *rq) { - bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE, - &rq->q->queue_flags); - - rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio, - no_sg_merge); + rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio); } void blk_recount_segments(struct request_queue *q, struct bio *bio) { - unsigned short seg_cnt = bio_segments(bio); + struct bio *nxt = bio->bi_next; - if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) && - (seg_cnt < queue_max_segments(q))) - bio->bi_phys_segments = seg_cnt; - else { - struct bio *nxt = bio->bi_next; - - bio->bi_next = NULL; - bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio, false); - bio->bi_next = nxt; - } + bio->bi_next = NULL; + bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio); + bio->bi_next = nxt; bio_set_flag(bio, BIO_SEG_VALID); } diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index c782e81db627..697d6213c82b 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -128,7 +128,6 @@ static const char *const blk_queue_flag_name[] = { QUEUE_FLAG_NAME(SAME_FORCE), QUEUE_FLAG_NAME(DEAD), QUEUE_FLAG_NAME(INIT_DONE), - QUEUE_FLAG_NAME(NO_SG_MERGE), QUEUE_FLAG_NAME(POLL), QUEUE_FLAG_NAME(WC), QUEUE_FLAG_NAME(FUA), diff --git a/block/blk-mq.c b/block/blk-mq.c index 44d471ff8754..fa508ee31742 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2837,9 +2837,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, set->map[HCTX_TYPE_POLL].nr_queues) blk_queue_flag_set(QUEUE_FLAG_POLL, q); - if (!(set->flags & BLK_MQ_F_SG_MERGE)) - blk_queue_flag_set(QUEUE_FLAG_NO_SG_MERGE, q); - q->sg_reserved_size = INT_MAX; INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 4b1be754cc41..ba9481f1bf3c 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1698,14 +1698,6 @@ static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev, return q && !blk_queue_add_random(q); } -static int queue_supports_sg_merge(struct dm_target *ti, struct dm_dev *dev, - sector_t start, sector_t len, void *data) -{ - struct request_queue *q = bdev_get_queue(dev->bdev); - - return q && !test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags); -} - static bool dm_table_all_devices_attribute(struct dm_table *t, iterate_devices_callout_fn func) { @@ -1902,11 +1894,6 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, if (!dm_table_supports_write_zeroes(t)) q->limits.max_write_zeroes_sectors = 0; - if (dm_table_all_devices_attribute(t, queue_supports_sg_merge)) - blk_queue_flag_clear(QUEUE_FLAG_NO_SG_MERGE, q); - else - blk_queue_flag_set(QUEUE_FLAG_NO_SG_MERGE, q); - dm_table_verify_integrity(t); /* diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b6292d469ea4..faed9d9eb84c 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -588,7 +588,6 @@ struct request_queue { #define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */ #define QUEUE_FLAG_DEAD 13 /* queue tear-down finished */ #define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */ -#define QUEUE_FLAG_NO_SG_MERGE 15 /* don't attempt to merge SG segments*/ #define QUEUE_FLAG_POLL 16 /* IO polling enabled if set */ #define QUEUE_FLAG_WC 17 /* Write back caching */ #define QUEUE_FLAG_FUA 18 /* device supports FUA writes */ From 56d18f62f556b80105e38e7975975cf7465aae3e Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Feb 2019 19:13:24 +0800 Subject: [PATCH 1855/4277] block: kill BLK_MQ_F_SG_MERGE QUEUE_FLAG_NO_SG_MERGE has been killed, so kill BLK_MQ_F_SG_MERGE too. Reviewed-by: Christoph Hellwig Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-mq-debugfs.c | 1 - drivers/block/loop.c | 2 +- drivers/block/nbd.c | 2 +- drivers/block/rbd.c | 2 +- drivers/block/skd_main.c | 1 - drivers/block/xen-blkfront.c | 2 +- drivers/md/dm-rq.c | 2 +- drivers/mmc/core/queue.c | 3 +-- drivers/scsi/scsi_lib.c | 2 +- include/linux/blk-mq.h | 1 - 10 files changed, 7 insertions(+), 11 deletions(-) diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 697d6213c82b..c39247c5ddb6 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -249,7 +249,6 @@ static const char *const alloc_policy_name[] = { static const char *const hctx_flag_name[] = { HCTX_FLAG_NAME(SHOULD_MERGE), HCTX_FLAG_NAME(TAG_SHARED), - HCTX_FLAG_NAME(SG_MERGE), HCTX_FLAG_NAME(BLOCKING), HCTX_FLAG_NAME(NO_SCHED), }; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 8ef583197414..3d63ad036398 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1937,7 +1937,7 @@ static int loop_add(struct loop_device **l, int i) lo->tag_set.queue_depth = 128; lo->tag_set.numa_node = NUMA_NO_NODE; lo->tag_set.cmd_size = sizeof(struct loop_cmd); - lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE; + lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; lo->tag_set.driver_data = lo; err = blk_mq_alloc_tag_set(&lo->tag_set); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 7c9a949e876b..32a7ba1674b7 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1571,7 +1571,7 @@ static int nbd_dev_add(int index) nbd->tag_set.numa_node = NUMA_NO_NODE; nbd->tag_set.cmd_size = sizeof(struct nbd_cmd); nbd->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | - BLK_MQ_F_SG_MERGE | BLK_MQ_F_BLOCKING; + BLK_MQ_F_BLOCKING; nbd->tag_set.driver_data = nbd; err = blk_mq_alloc_tag_set(&nbd->tag_set); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 1e92b61d0bd5..abe9e1c89227 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3988,7 +3988,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) rbd_dev->tag_set.ops = &rbd_mq_ops; rbd_dev->tag_set.queue_depth = rbd_dev->opts->queue_depth; rbd_dev->tag_set.numa_node = NUMA_NO_NODE; - rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE; + rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; rbd_dev->tag_set.nr_hw_queues = 1; rbd_dev->tag_set.cmd_size = sizeof(struct work_struct); diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c index ab893a7571a2..7d3ad6c22ee5 100644 --- a/drivers/block/skd_main.c +++ b/drivers/block/skd_main.c @@ -2843,7 +2843,6 @@ static int skd_cons_disk(struct skd_device *skdev) skdev->sgs_per_request * sizeof(struct scatterlist); skdev->tag_set.numa_node = NUMA_NO_NODE; skdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | - BLK_MQ_F_SG_MERGE | BLK_ALLOC_POLICY_TO_MQ_FLAG(BLK_TAG_ALLOC_FIFO); skdev->tag_set.driver_data = skdev; rc = blk_mq_alloc_tag_set(&skdev->tag_set); diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 0ed4b200fa58..d43a5677ccbc 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -977,7 +977,7 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size, } else info->tag_set.queue_depth = BLK_RING_SIZE(info); info->tag_set.numa_node = NUMA_NO_NODE; - info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE; + info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; info->tag_set.cmd_size = sizeof(struct blkif_req); info->tag_set.driver_data = info; diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index 4eb5f8c56535..b2f8eb2365ee 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -527,7 +527,7 @@ int dm_mq_init_request_queue(struct mapped_device *md, struct dm_table *t) md->tag_set->ops = &dm_mq_ops; md->tag_set->queue_depth = dm_get_blk_mq_queue_depth(); md->tag_set->numa_node = md->numa_node_id; - md->tag_set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE; + md->tag_set->flags = BLK_MQ_F_SHOULD_MERGE; md->tag_set->nr_hw_queues = dm_get_blk_mq_nr_hw_queues(); md->tag_set->driver_data = md; diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 35cc138b096d..cc19e71c71d4 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -410,8 +410,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card) else mq->tag_set.queue_depth = MMC_QUEUE_DEPTH; mq->tag_set.numa_node = NUMA_NO_NODE; - mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE | - BLK_MQ_F_BLOCKING; + mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING; mq->tag_set.nr_hw_queues = 1; mq->tag_set.cmd_size = sizeof(struct mmc_queue_req); mq->tag_set.driver_data = mq; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6d65ac584eba..6cadbe945bdb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1899,7 +1899,7 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost) shost->tag_set.queue_depth = shost->can_queue; shost->tag_set.cmd_size = cmd_size; shost->tag_set.numa_node = NUMA_NO_NODE; - shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE; + shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; shost->tag_set.flags |= BLK_ALLOC_POLICY_TO_MQ_FLAG(shost->hostt->tag_alloc_policy); shost->tag_set.driver_data = shost; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 0e030f5f76b6..b0c814bcc7e3 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -218,7 +218,6 @@ struct blk_mq_ops { enum { BLK_MQ_F_SHOULD_MERGE = 1 << 0, BLK_MQ_F_TAG_SHARED = 1 << 1, - BLK_MQ_F_SG_MERGE = 1 << 2, BLK_MQ_F_BLOCKING = 1 << 5, BLK_MQ_F_NO_SCHED = 1 << 6, BLK_MQ_F_ALLOC_POLICY_START_BIT = 8, From f79b3f338564e7674dbe6375bcf685c2ba483efe Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Mon, 11 Feb 2019 15:00:48 -0500 Subject: [PATCH 1856/4277] ftrace: Allow enabling of filters via index of available_filter_functions Enabling of large number of functions by echoing in a large subset of the functions in available_filter_functions can take a very long time. The process requires testing all functions registered by the function tracer (which is in the 10s of thousands), and doing a kallsyms lookup to convert the ip address into a name, then comparing that name with the string passed in. When a function causes the function tracer to crash the system, a binary bisect of the available_filter_functions can be done to find the culprit. But this requires passing in half of the functions in available_filter_functions over and over again, which makes it basically a O(n^2) operation. With 40,000 functions, that ends up bing 1,600,000,000 opertions! And enabling this can take over 20 minutes. As a quick speed up, if a number is passed into one of the filter files, instead of doing a search, it just enables the function at the corresponding line of the available_filter_functions file. That is: # echo 50 > set_ftrace_filter # cat set_ftrace_filter x86_pmu_commit_txn # head -50 available_filter_functions | tail -1 x86_pmu_commit_txn This allows setting of half the available_filter_functions to take place in less than a second! # time seq 20000 > set_ftrace_filter real 0m0.042s user 0m0.005s sys 0m0.015s # wc -l set_ftrace_filter 20000 set_ftrace_filter Signed-off-by: Steven Rostedt (VMware) --- Documentation/trace/ftrace.rst | 38 ++++++++++++++++++++++++++++++ kernel/trace/ftrace.c | 30 +++++++++++++++++++++++ kernel/trace/trace.h | 1 + kernel/trace/trace_events_filter.c | 5 ++++ 4 files changed, 74 insertions(+) diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst index 6ce2763a2a3e..7c5e6d6ab5d1 100644 --- a/Documentation/trace/ftrace.rst +++ b/Documentation/trace/ftrace.rst @@ -233,6 +233,12 @@ of ftrace. Here is a list of some of the key files: This interface also allows for commands to be used. See the "Filter commands" section for more details. + As a speed up, since processing strings can't be quite expensive + and requires a check of all functions registered to tracing, instead + an index can be written into this file. A number (starting with "1") + written will instead select the same corresponding at the line position + of the "available_filter_functions" file. + set_ftrace_notrace: This has an effect opposite to that of @@ -2835,6 +2841,38 @@ Produces:: We can see that there's no more lock or preempt tracing. +Selecting function filters via index +------------------------------------ + +Because processing of strings is expensive (the address of the function +needs to be looked up before comparing to the string being passed in), +an index can be used as well to enable functions. This is useful in the +case of setting thousands of specific functions at a time. By passing +in a list of numbers, no string processing will occur. Instead, the function +at the specific location in the internal array (which corresponds to the +functions in the "available_filter_functions" file), is selected. + +:: + + # echo 1 > set_ftrace_filter + +Will select the first function listed in "available_filter_functions" + +:: + + # head -1 available_filter_functions + trace_initcall_finish_cb + + # cat set_ftrace_filter + trace_initcall_finish_cb + + # head -50 available_filter_functions | tail -1 + x86_pmu_commit_txn + + # echo 1 50 > set_ftrace_filter + # cat set_ftrace_filter + trace_initcall_finish_cb + x86_pmu_commit_txn Dynamic ftrace with the function graph tracer --------------------------------------------- diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index aac7847c0214..fa79323331b2 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3701,6 +3701,31 @@ enter_record(struct ftrace_hash *hash, struct dyn_ftrace *rec, int clear_filter) return ret; } +static int +add_rec_by_index(struct ftrace_hash *hash, struct ftrace_glob *func_g, + int clear_filter) +{ + long index = simple_strtoul(func_g->search, NULL, 0); + struct ftrace_page *pg; + struct dyn_ftrace *rec; + + /* The index starts at 1 */ + if (--index < 0) + return 0; + + do_for_each_ftrace_rec(pg, rec) { + if (pg->index <= index) { + index -= pg->index; + /* this is a double loop, break goes to the next page */ + break; + } + rec = &pg->records[index]; + enter_record(hash, rec, clear_filter); + return 1; + } while_for_each_ftrace_rec(); + return 0; +} + static int ftrace_match_record(struct dyn_ftrace *rec, struct ftrace_glob *func_g, struct ftrace_glob *mod_g, int exclude_mod) @@ -3769,6 +3794,11 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod) if (unlikely(ftrace_disabled)) goto out_unlock; + if (func_g.type == MATCH_INDEX) { + found = add_rec_by_index(hash, &func_g, clear_filter); + goto out_unlock; + } + do_for_each_ftrace_rec(pg, rec) { if (rec->flags & FTRACE_FL_DISABLED) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index a34fa5e76abb..ae7df090b93e 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1459,6 +1459,7 @@ enum regex_type { MATCH_MIDDLE_ONLY, MATCH_END_ONLY, MATCH_GLOB, + MATCH_INDEX, }; struct regex { diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index f052ecb085e9..ade606c33231 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -825,6 +825,9 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not) *search = buff; + if (isdigit(buff[0])) + return MATCH_INDEX; + for (i = 0; i < len; i++) { if (buff[i] == '*') { if (!i) { @@ -862,6 +865,8 @@ static void filter_build_regex(struct filter_pred *pred) } switch (type) { + /* MATCH_INDEX should not happen, but if it does, match full */ + case MATCH_INDEX: case MATCH_FULL: r->match = regex_match_full; break; From ce59b8e99c2c3e5828bce0b434850c6fec9d2397 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Wed, 16 Jan 2019 13:20:27 +0200 Subject: [PATCH 1857/4277] uprobes: convert uprobe.ref to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable uprobe.ref is used as pure reference counter. Convert it to refcount_t and fix up the operations. **Important note for maintainers: Some functions from refcount_t API defined in lib/refcount.c have different memory ordering guarantees than their atomic counterparts. The full comparison can be seen in https://lkml.org/lkml/2017/11/15/57 and it is hopefully soon in state to be merged to the documentation tree. Normally the differences should not matter since refcount_t provides enough guarantees to satisfy the refcounting use cases, but in some rare cases it might matter. Please double check that you don't have some undocumented memory guarantees for this variable usage. For the uprobe.ref it might make a difference in following places: - put_uprobe(): decrement in refcount_dec_and_test() only provides RELEASE ordering and control dependency on success vs. fully ordered atomic counterpart Link: http://lkml.kernel.org/r/1547637627-29526-1-git-send-email-elena.reshetova@intel.com Suggested-by: Kees Cook Acked-by: Oleg Nesterov Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Reviewed-by: Srikar Dronamraju Signed-off-by: Elena Reshetova Signed-off-by: Steven Rostedt (VMware) --- kernel/events/uprobes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 8aef47ee7bfa..0a8bf7a4fc5e 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -66,7 +66,7 @@ static struct percpu_rw_semaphore dup_mmap_sem; struct uprobe { struct rb_node rb_node; /* node in the rb tree */ - atomic_t ref; + refcount_t ref; struct rw_semaphore register_rwsem; struct rw_semaphore consumer_rwsem; struct list_head pending_list; @@ -560,13 +560,13 @@ set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long v static struct uprobe *get_uprobe(struct uprobe *uprobe) { - atomic_inc(&uprobe->ref); + refcount_inc(&uprobe->ref); return uprobe; } static void put_uprobe(struct uprobe *uprobe) { - if (atomic_dec_and_test(&uprobe->ref)) { + if (refcount_dec_and_test(&uprobe->ref)) { /* * If application munmap(exec_vma) before uprobe_unregister() * gets called, we don't get a chance to remove uprobe from @@ -657,7 +657,7 @@ static struct uprobe *__insert_uprobe(struct uprobe *uprobe) rb_link_node(&uprobe->rb_node, parent, p); rb_insert_color(&uprobe->rb_node, &uprobes_tree); /* get access + creation ref */ - atomic_set(&uprobe->ref, 2); + refcount_set(&uprobe->ref, 2); return u; } From 3d9dfd060391928bd615db62ecddea5e1255edfd Mon Sep 17 00:00:00 2001 From: Shamir Rabinovitch Date: Thu, 7 Feb 2019 18:44:47 +0200 Subject: [PATCH 1858/4277] IB/uverbs: Add ib_ucontext to uverbs_attr_bundle sent from ioctl and cmd flows Add ib_ucontext to the uverbs_attr_bundle sent down the iocl and cmd flows as soon as the flow has ib_uobject. In addition, remove rdma_get_ucontext helper function that is only used by ib_umem_get. Signed-off-by: Shamir Rabinovitch Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/rdma_core.c | 32 ++++++++++++++++++++++++++ drivers/infiniband/core/umem.c | 10 +++++--- drivers/infiniband/core/uverbs_cmd.c | 2 +- drivers/infiniband/core/uverbs_ioctl.c | 3 +++ drivers/infiniband/core/uverbs_main.c | 25 +------------------- include/rdma/ib_verbs.h | 2 -- include/rdma/uverbs_ioctl.h | 1 + include/rdma/uverbs_std_types.h | 18 +++++++++++---- 8 files changed, 58 insertions(+), 35 deletions(-) diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index a260d2f8e0b7..96f919fe86e7 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -438,6 +438,38 @@ free: uverbs_uobject_put(uobj); return ERR_PTR(ret); } +struct ib_uobject *_uobj_get_read(enum uverbs_default_objects type, + u32 object_id, + struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj; + + uobj = rdma_lookup_get_uobject(uobj_get_type(attrs, type), attrs->ufile, + object_id, UVERBS_LOOKUP_READ); + if (IS_ERR(uobj)) + return uobj; + + attrs->context = uobj->context; + + return uobj; +} + +struct ib_uobject *_uobj_get_write(enum uverbs_default_objects type, + u32 object_id, + struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj; + + uobj = rdma_lookup_get_uobject(uobj_get_type(attrs, type), attrs->ufile, + object_id, UVERBS_LOOKUP_WRITE); + + if (IS_ERR(uobj)) + return uobj; + + attrs->context = uobj->context; + + return uobj; +} static struct ib_uobject * alloc_begin_idr_uobject(const struct uverbs_api_object *obj, diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index b69d3efa8712..fe5551562dbc 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -96,9 +96,13 @@ struct ib_umem *ib_umem_get(struct ib_udata *udata, unsigned long addr, struct scatterlist *sg, *sg_list_start; unsigned int gup_flags = FOLL_WRITE; - context = rdma_get_ucontext(udata); - if (IS_ERR(context)) - return ERR_CAST(context); + if (!udata) + return ERR_PTR(-EIO); + + context = container_of(udata, struct uverbs_attr_bundle, driver_udata) + ->context; + if (!context) + return ERR_PTR(-EIO); if (dmasync) dma_attrs |= DMA_ATTR_WRITE_BARRIER; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 5ac143f22df0..c2e7733a7fe0 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2634,7 +2634,7 @@ void flow_resources_add(struct ib_uflow_resources *uflow_res, } EXPORT_SYMBOL(flow_resources_add); -static int kern_spec_to_ib_spec_action(const struct uverbs_attr_bundle *attrs, +static int kern_spec_to_ib_spec_action(struct uverbs_attr_bundle *attrs, struct ib_uverbs_flow_spec *kern_spec, union ib_flow_spec *ib_spec, struct ib_uflow_resources *uflow_res) diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index 0ca04d224015..e1379949e663 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -213,6 +213,7 @@ static int uverbs_process_idrs_array(struct bundle_priv *pbundle, ret = PTR_ERR(attr->uobjects[i]); break; } + pbundle->bundle.context = attr->uobjects[i]->context; } attr->len = i; @@ -330,6 +331,7 @@ static int uverbs_process_attr(struct bundle_priv *pbundle, uattr->data_s64); if (IS_ERR(o_attr->uobject)) return PTR_ERR(o_attr->uobject); + pbundle->bundle.context = o_attr->uobject->context; __set_bit(attr_bkey, pbundle->uobj_finalize); if (spec->u.obj.access == UVERBS_ACCESS_NEW) { @@ -592,6 +594,7 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile, pbundle->method_elm = method_elm; pbundle->method_key = attrs_iter.index; pbundle->bundle.ufile = ufile; + pbundle->bundle.context = NULL; /* only valid if bundle has uobject */ pbundle->radix = &uapi->radix; pbundle->radix_slots = slot; pbundle->radix_slots_len = radix_tree_chunk_size(&attrs_iter); diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index accc61cc93ac..70b7d80431a9 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -101,30 +101,6 @@ struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile) } EXPORT_SYMBOL(ib_uverbs_get_ucontext_file); -/* rdma_get_ucontext - Return the ucontext from a udata - * @udata: The udata to get the context from - * - * This can only be called from within a uapi method that was passed ib_udata - * as a parameter. It returns the ucontext associated with the udata, or ERR_PTR - * if the udata is NULL or the ucontext has been disassociated. - */ -struct ib_ucontext *rdma_get_ucontext(struct ib_udata *udata) -{ - if (!udata) - return ERR_PTR(-EIO); - - /* - * FIXME: Really all cases that get here with a udata will have - * already called ib_uverbs_get_ucontext_file, or located a uobject - * that points to a ucontext. We could store that result in the udata - * so this function can't fail. - */ - return ib_uverbs_get_ucontext_file( - container_of(udata, struct uverbs_attr_bundle, driver_udata) - ->ufile); -} -EXPORT_SYMBOL(rdma_get_ucontext); - int uverbs_dealloc_mw(struct ib_mw *mw) { struct ib_pd *pd = mw->pd; @@ -719,6 +695,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, memset(bundle.attr_present, 0, sizeof(bundle.attr_present)); bundle.ufile = file; + bundle.context = NULL; /* only valid if bundle has uobject */ if (!method_elm->is_ex) { size_t in_len = hdr.in_words * 4 - sizeof(hdr); size_t out_len = hdr.out_words * 4; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 135fab2c016c..64ee7c08be22 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -4247,8 +4247,6 @@ void rdma_roce_rescan_device(struct ib_device *ibdev); struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile); -struct ib_ucontext *rdma_get_ucontext(struct ib_udata *udata); - int uverbs_destroy_def_handler(struct uverbs_attr_bundle *attrs); struct net_device *rdma_alloc_netdev(struct ib_device *device, u8 port_num, diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h index 27da906beea7..b14a9ee786e9 100644 --- a/include/rdma/uverbs_ioctl.h +++ b/include/rdma/uverbs_ioctl.h @@ -652,6 +652,7 @@ struct uverbs_attr_bundle { struct ib_udata driver_udata; struct ib_udata ucore; struct ib_uverbs_file *ufile; + struct ib_ucontext *context; DECLARE_BITMAP(attr_present, UVERBS_API_ATTR_BKEY_LEN); struct uverbs_attr attrs[]; }; diff --git a/include/rdma/uverbs_std_types.h b/include/rdma/uverbs_std_types.h index 883abcf6d36e..794c47565971 100644 --- a/include/rdma/uverbs_std_types.h +++ b/include/rdma/uverbs_std_types.h @@ -48,9 +48,12 @@ #define uobj_get_type(_attrs, _object) \ uapi_get_object((_attrs)->ufile->device->uapi, _object) +struct ib_uobject *_uobj_get_read(enum uverbs_default_objects type, + u32 object_id, + struct uverbs_attr_bundle *attrs); + #define uobj_get_read(_type, _id, _attrs) \ - rdma_lookup_get_uobject(uobj_get_type(_attrs, _type), (_attrs)->ufile, \ - _uobj_check_id(_id), UVERBS_LOOKUP_READ) + _uobj_get_read(_type, _uobj_check_id(_id), _attrs) #define ufd_get_read(_type, _fdnum, _attrs) \ rdma_lookup_get_uobject(uobj_get_type(_attrs, _type), (_attrs)->ufile, \ @@ -67,9 +70,12 @@ static inline void *_uobj_get_obj_read(struct ib_uobject *uobj) ((struct ib_##_object *)_uobj_get_obj_read( \ uobj_get_read(_type, _id, _attrs))) +struct ib_uobject *_uobj_get_write(enum uverbs_default_objects type, + u32 object_id, + struct uverbs_attr_bundle *attrs); + #define uobj_get_write(_type, _id, _attrs) \ - rdma_lookup_get_uobject(uobj_get_type(_attrs, _type), (_attrs)->ufile, \ - _uobj_check_id(_id), UVERBS_LOOKUP_WRITE) + _uobj_get_write(_type, _uobj_check_id(_id), _attrs) int __uobj_perform_destroy(const struct uverbs_api_object *obj, u32 id, const struct uverbs_attr_bundle *attrs); @@ -123,8 +129,10 @@ __uobj_alloc(const struct uverbs_api_object *obj, { struct ib_uobject *uobj = rdma_alloc_begin_uobject(obj, attrs->ufile); - if (!IS_ERR(uobj)) + if (!IS_ERR(uobj)) { *ib_dev = uobj->context->device; + attrs->context = uobj->context; + } return uobj; } From 730623f4a56fa42d4559715ff2f4a5c32b3ae8bf Mon Sep 17 00:00:00 2001 From: Shamir Rabinovitch Date: Thu, 7 Feb 2019 18:44:48 +0200 Subject: [PATCH 1859/4277] IB/verbs: Add helper function rdma_udata_to_drv_context Helper function to get driver's context out of ib_udata wrapped in uverbs_attr_bundle for user objects or NULL for kernel objects. Signed-off-by: Shamir Rabinovitch Signed-off-by: Jason Gunthorpe --- include/rdma/uverbs_ioctl.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h index b14a9ee786e9..28570ac2b6a0 100644 --- a/include/rdma/uverbs_ioctl.h +++ b/include/rdma/uverbs_ioctl.h @@ -664,6 +664,23 @@ static inline bool uverbs_attr_is_valid(const struct uverbs_attr_bundle *attrs_b attrs_bundle->attr_present); } +/** + * rdma_udata_to_drv_context - Helper macro to get the driver's context out of + * ib_udata which is embedded in uverbs_attr_bundle. + * + * If udata is not NULL this cannot fail. Otherwise a NULL udata will result + * in a NULL ucontext pointer, as a safety precaution. Callers should be using + * 'udata' to determine if the driver call is in user or kernel mode, not + * 'ucontext'. + * + */ +#define rdma_udata_to_drv_context(udata, drv_dev_struct, member) \ + (udata ? container_of(container_of(udata, struct uverbs_attr_bundle, \ + driver_udata) \ + ->context, \ + drv_dev_struct, member) : \ + (drv_dev_struct *)NULL) + #define IS_UVERBS_COPY_ERR(_ret) ((_ret) && (_ret) != -ENOENT) static inline const struct uverbs_attr *uverbs_attr_get(const struct uverbs_attr_bundle *attrs_bundle, From c89f7f98c971e0cabc819b6c0fe6bf509287b7e0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 15 Feb 2019 13:49:18 -0600 Subject: [PATCH 1860/4277] PCI/portdrv: Use conventional Device ID table formatting The pci_device_id table was technically correct, but unusually formatted, which made adding entries error-prone. Change the format so it's obvious how to add entries. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 0acca3596807..a289e734b9a4 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -182,10 +182,10 @@ static void pcie_portdrv_err_resume(struct pci_dev *dev) /* * LINUX Device Driver Model */ -static const struct pci_device_id port_pci_ids[] = { { +static const struct pci_device_id port_pci_ids[] = { /* handle any PCI-Express port */ - PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0), - }, { /* end: all zeroes */ } + { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) }, + { }, }; static const struct pci_error_handlers pcie_portdrv_err_handler = { From f0cfecea8d1e8e0cd5d5053f9452b3a450f49eb5 Mon Sep 17 00:00:00 2001 From: Honghui Zhang Date: Thu, 14 Feb 2019 13:21:17 +0800 Subject: [PATCH 1861/4277] PCI/portdrv: Support PCIe services on subtractive decode bridges The Class Code for subtractive decode PCI-to-PCI bridge is 060401h; add an entry to make portdrv support this type of bridge. This allows use of PCIe services on subtractive decode ports. Signed-off-by: Honghui Zhang [bhelgaas: add braces surrounding entry] Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index a289e734b9a4..99d2abe88d0b 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -185,6 +185,8 @@ static void pcie_portdrv_err_resume(struct pci_dev *dev) static const struct pci_device_id port_pci_ids[] = { /* handle any PCI-Express port */ { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) }, + /* subtractive decode PCI-to-PCI bridge, class type is 060401h */ + { PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x01), ~0) }, { }, }; From bbe54ea5330d828cc396d451c0e1e5c3f9764c1e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 Jan 2019 20:07:46 +0300 Subject: [PATCH 1862/4277] PCI: pciehp: Disable Data Link Layer State Changed event on suspend Commit 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") tried to solve an issue where the hierarchy immediately wakes up when it is transitioned into D3cold. However, it turns out to prevent PME propagation on some systems that do not support D3cold. I looked more closely at what might cause the immediate wakeup. It happens when the ACPI power resource of the root port is turned off. The AML code associated with the _OFF() method of the ACPI power resource starts a PCIe L2/L3 Ready transition and waits for it to complete. Right after the L2/L3 Ready transition is started the root port receives a PME from the downstream port. The simplest hierarchy where this happens looks like this: 00:1d.0 PCIe Root Port ^ | v 05:00.0 PCIe switch #1 upstream port 06:01.0 PCIe switch #1 downstream hotplug port ^ | v 08:00.0 PCIe switch #2 upstream port It seems that the PCIe link between the two switches, before PME_Turn_Off/PME_TO_Ack is complete for the whole hierarchy, goes inactive and triggers PME towards the root port bringing it back to D0. The L2/L3 Ready sequence is described in PCIe r4.0 spec sections 5.2 and 5.3.3 but unfortunately they do not state what happens if DLLSCE is enabled during the sequence. Disabling Data Link Layer State Changed event (DLLSCE) seems to prevent the issue and still allows the downstream hotplug port to notice when a device is plugged/unplugged. Link: https://bugzilla.kernel.org/show_bug.cgi?id=202593 Fixes: 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki CC: stable@vger.kernel.org # v4.20+ --- drivers/pci/hotplug/pciehp_hpc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7dd443aea5a5..c0fb64ace05a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -736,12 +736,25 @@ void pcie_clear_hotplug_events(struct controller *ctrl) void pcie_enable_interrupt(struct controller *ctrl) { - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE); + u16 mask; + + mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; + pcie_write_cmd(ctrl, mask, mask); } void pcie_disable_interrupt(struct controller *ctrl) { - pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE); + u16 mask; + + /* + * Mask hot-plug interrupt to prevent it triggering immediately + * when the link goes inactive (we still get PME when any of the + * enabled events is detected). Same goes with Link Layer State + * changed event which generates PME immediately when the link goes + * inactive so mask it as well. + */ + mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; + pcie_write_cmd(ctrl, 0, mask); } /* From bb0e9b1d2a1f933b4d899cae039363297270198d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Feb 2019 15:31:26 +0000 Subject: [PATCH 1863/4277] i2c: tegra: change phrasing, "fallbacking" to "falling back" The phrasing in two dev_err messages is using fallbacking which os less understandable than "falling back", so fix this up. Signed-off-by: Colin Ian King Acked-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index a4cd79c9f7a7..5a403c3ab66c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -452,7 +452,7 @@ err_out: tegra_i2c_release_dma(i2c_dev); if (err != -EPROBE_DEFER) { dev_err(i2c_dev->dev, "cannot use DMA: %d\n", err); - dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + dev_err(i2c_dev->dev, "falling back to PIO\n"); return 0; } @@ -965,7 +965,7 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, if (ret < 0) { dev_err(i2c_dev->dev, "DMA slave config failed: %d\n", ret); - dev_err(i2c_dev->dev, "fallbacking to PIO\n"); + dev_err(i2c_dev->dev, "falling back to PIO\n"); tegra_i2c_release_dma(i2c_dev); i2c_dev->is_curr_dma_xfer = false; } else { From 89944450547334aa6655e0cd4aec8df1897a205a Mon Sep 17 00:00:00 2001 From: Shamir Rabinovitch Date: Thu, 7 Feb 2019 18:44:49 +0200 Subject: [PATCH 1864/4277] IB/{hw,sw}: Remove 'uobject->context' dependency in object creation APIs Now when we have the udata passed to all the ib_xxx object creation APIs and the additional macro 'rdma_udata_to_drv_context' to get the ib_ucontext from ib_udata stored in uverbs_attr_bundle, we can finally start to remove the dependency of the drivers in the ib_xxx->uobject->context. Signed-off-by: Shamir Rabinovitch Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 20 +++--- drivers/infiniband/hw/cxgb3/iwch_provider.c | 4 +- drivers/infiniband/hw/cxgb4/qp.c | 9 +-- drivers/infiniband/hw/hns/hns_roce_qp.c | 21 +++--- drivers/infiniband/hw/i40iw/i40iw_verbs.c | 9 +-- drivers/infiniband/hw/mlx4/mr.c | 12 ++-- drivers/infiniband/hw/mlx4/qp.c | 72 +++++++++++--------- drivers/infiniband/hw/mlx4/srq.c | 9 ++- drivers/infiniband/hw/mlx5/devx.c | 21 +++--- drivers/infiniband/hw/mlx5/qp.c | 58 +++++++++------- drivers/infiniband/hw/mlx5/srq.c | 8 +-- drivers/infiniband/hw/mthca/mthca_provider.c | 20 +++--- drivers/infiniband/hw/mthca/mthca_qp.c | 13 ++-- drivers/infiniband/hw/mthca/mthca_srq.c | 21 +++--- drivers/infiniband/hw/nes/nes_verbs.c | 12 ++-- drivers/infiniband/hw/qedr/verbs.c | 2 +- drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 5 +- drivers/infiniband/sw/rdmavt/qp.c | 5 +- drivers/infiniband/sw/rdmavt/srq.c | 5 +- drivers/infiniband/sw/rdmavt/vt.c | 4 -- drivers/infiniband/sw/rxe/rxe_qp.c | 8 ++- drivers/infiniband/sw/rxe/rxe_verbs.c | 6 +- include/rdma/rdma_vt.h | 5 ++ 23 files changed, 194 insertions(+), 155 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 2ed778683c6b..83bf6f5d67c0 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "bnxt_ulp.h" @@ -722,12 +723,11 @@ struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd, /* Write AVID to shared page. */ if (udata) { - struct ib_ucontext *ib_uctx = ib_pd->uobject->context; - struct bnxt_re_ucontext *uctx; + struct bnxt_re_ucontext *uctx = rdma_udata_to_drv_context( + udata, struct bnxt_re_ucontext, ib_uctx); unsigned long flag; u32 *wrptr; - uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); spin_lock_irqsave(&uctx->sh_lock, flag); wrptr = (u32 *)(uctx->shpg + BNXT_RE_AVID_OFFT); *wrptr = ah->qplib_ah.id; @@ -872,10 +872,9 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; struct ib_umem *umem; int bytes = 0, psn_sz; - struct ib_ucontext *context = pd->ib_pd.uobject->context; - struct bnxt_re_ucontext *cntx = container_of(context, - struct bnxt_re_ucontext, - ib_uctx); + struct bnxt_re_ucontext *cntx = rdma_udata_to_drv_context( + udata, struct bnxt_re_ucontext, ib_uctx); + if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) return -EFAULT; @@ -1359,10 +1358,9 @@ static int bnxt_re_init_user_srq(struct bnxt_re_dev *rdev, struct bnxt_qplib_srq *qplib_srq = &srq->qplib_srq; struct ib_umem *umem; int bytes = 0; - struct ib_ucontext *context = pd->ib_pd.uobject->context; - struct bnxt_re_ucontext *cntx = container_of(context, - struct bnxt_re_ucontext, - ib_uctx); + struct bnxt_re_ucontext *cntx = rdma_udata_to_drv_context( + udata, struct bnxt_re_ucontext, ib_uctx); + if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) return -EFAULT; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 80dff6804e48..b74fd90a22dc 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "cxio_hal.h" #include "iwch.h" @@ -825,7 +826,8 @@ static struct ib_qp *iwch_create_qp(struct ib_pd *pd, * Kernel users need more wq space for fastreg WRs which can take * 2 WR fragments. */ - ucontext = udata ? to_iwch_ucontext(pd->uobject->context) : NULL; + ucontext = rdma_udata_to_drv_context(udata, struct iwch_ucontext, + ibucontext); if (!ucontext && wqsize < (rqsize + (2 * sqsize))) wqsize = roundup_pow_of_two(rqsize + roundup_pow_of_two(attrs->cap.max_send_wr * 2)); diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 0fe87b9c1e10..2509f65f4420 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -31,6 +31,7 @@ */ #include +#include #include "iw_cxgb4.h" @@ -2136,7 +2137,8 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, struct c4iw_cq *rchp; struct c4iw_create_qp_resp uresp; unsigned int sqsize, rqsize = 0; - struct c4iw_ucontext *ucontext; + struct c4iw_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct c4iw_ucontext, ibucontext); int ret; struct c4iw_mm_entry *sq_key_mm, *rq_key_mm = NULL, *sq_db_key_mm; struct c4iw_mm_entry *rq_db_key_mm = NULL, *ma_sync_key_mm = NULL; @@ -2170,8 +2172,6 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, if (sqsize < 8) sqsize = 8; - ucontext = udata ? to_c4iw_ucontext(pd->uobject->context) : NULL; - qhp = kzalloc(sizeof(*qhp), GFP_KERNEL); if (!qhp) return ERR_PTR(-ENOMEM); @@ -2718,7 +2718,8 @@ struct ib_srq *c4iw_create_srq(struct ib_pd *pd, struct ib_srq_init_attr *attrs, rqsize = attrs->attr.max_wr + 1; rqsize = roundup_pow_of_two(max_t(u16, rqsize, 16)); - ucontext = udata ? to_c4iw_ucontext(pd->uobject->context) : NULL; + ucontext = rdma_udata_to_drv_context(udata, struct c4iw_ucontext, + ibucontext); srq = kzalloc(sizeof(*srq), GFP_KERNEL); if (!srq) diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 728ad8b6de2f..57c76eafef2f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "hns_roce_common.h" #include "hns_roce_device.h" #include "hns_roce_hem.h" @@ -560,6 +561,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, struct device *dev = hr_dev->dev; struct hns_roce_ib_create_qp ucmd; struct hns_roce_ib_create_qp_resp resp = {}; + struct hns_roce_ucontext *uctx = rdma_udata_to_drv_context( + udata, struct hns_roce_ucontext, ibucontext); unsigned long qpn = 0; int ret = 0; u32 page_shift; @@ -670,9 +673,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, (udata->inlen >= sizeof(ucmd)) && (udata->outlen >= sizeof(resp)) && hns_roce_qp_has_sq(init_attr)) { - ret = hns_roce_db_map_user( - to_hr_ucontext(ib_pd->uobject->context), udata, - ucmd.sdb_addr, &hr_qp->sdb); + ret = hns_roce_db_map_user(uctx, udata, ucmd.sdb_addr, + &hr_qp->sdb); if (ret) { dev_err(dev, "sq record doorbell map failed!\n"); goto err_mtt; @@ -686,9 +688,8 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) && (udata->outlen >= sizeof(resp)) && hns_roce_qp_has_rq(init_attr)) { - ret = hns_roce_db_map_user( - to_hr_ucontext(ib_pd->uobject->context), udata, - ucmd.db_addr, &hr_qp->rdb); + ret = hns_roce_db_map_user(uctx, udata, ucmd.db_addr, + &hr_qp->rdb); if (ret) { dev_err(dev, "rq record doorbell map failed!\n"); goto err_sq_dbmap; @@ -838,9 +839,7 @@ err_wrid: if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) && (udata->outlen >= sizeof(resp)) && hns_roce_qp_has_rq(init_attr)) - hns_roce_db_unmap_user( - to_hr_ucontext(ib_pd->uobject->context), - &hr_qp->rdb); + hns_roce_db_unmap_user(uctx, &hr_qp->rdb); } else { kfree(hr_qp->sq.wrid); kfree(hr_qp->rq.wrid); @@ -852,9 +851,7 @@ err_sq_dbmap: (udata->inlen >= sizeof(ucmd)) && (udata->outlen >= sizeof(resp)) && hns_roce_qp_has_sq(init_attr)) - hns_roce_db_unmap_user( - to_hr_ucontext(ib_pd->uobject->context), - &hr_qp->sdb); + hns_roce_db_unmap_user(uctx, &hr_qp->sdb); err_mtt: hns_roce_mtt_cleanup(hr_dev, &hr_qp->mtt); diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index d5fb2b927587..76b4d1218696 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "i40iw.h" /** @@ -556,7 +557,8 @@ static struct ib_qp *i40iw_create_qp(struct ib_pd *ibpd, struct i40iw_device *iwdev = to_iwdev(ibpd->device); struct i40iw_cqp *iwcqp = &iwdev->cqp; struct i40iw_qp *iwqp; - struct i40iw_ucontext *ucontext; + struct i40iw_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct i40iw_ucontext, ibucontext); struct i40iw_create_qp_req req; struct i40iw_create_qp_resp uresp; u32 qp_num = 0; @@ -665,7 +667,6 @@ static struct ib_qp *i40iw_create_qp(struct ib_pd *ibpd, } iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; iwqp->user_mode = 1; - ucontext = to_ucontext(ibpd->uobject->context); if (req.user_wqe_buffers) { struct i40iw_pbl *iwpbl; @@ -1819,7 +1820,8 @@ static struct ib_mr *i40iw_reg_user_mr(struct ib_pd *pd, { struct i40iw_pd *iwpd = to_iwpd(pd); struct i40iw_device *iwdev = to_iwdev(pd->device); - struct i40iw_ucontext *ucontext; + struct i40iw_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct i40iw_ucontext, ibucontext); struct i40iw_pble_alloc *palloc; struct i40iw_pbl *iwpbl; struct i40iw_mr *iwmr; @@ -1860,7 +1862,6 @@ static struct ib_mr *i40iw_reg_user_mr(struct ib_pd *pd, iwmr->region = region; iwmr->ibmr.pd = pd; iwmr->ibmr.device = pd->device; - ucontext = to_ucontext(pd->uobject->context); iwmr->page_size = PAGE_SIZE; iwmr->page_msk = PAGE_MASK; diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 56639ecd53ad..395379a480cb 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -367,8 +367,7 @@ end: return block_shift; } -static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, - struct ib_udata *udata, u64 start, +static struct ib_umem *mlx4_get_umem_mr(struct ib_udata *udata, u64 start, u64 length, u64 virt_addr, int access_flags) { @@ -416,8 +415,8 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (!mr) return ERR_PTR(-ENOMEM); - mr->umem = mlx4_get_umem_mr(pd->uobject->context, udata, start, length, - virt_addr, access_flags); + mr->umem = + mlx4_get_umem_mr(udata, start, length, virt_addr, access_flags); if (IS_ERR(mr->umem)) { err = PTR_ERR(mr->umem); goto err_free; @@ -506,9 +505,8 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, mlx4_mr_rereg_mem_cleanup(dev->dev, &mmr->mmr); ib_umem_release(mmr->umem); - mmr->umem = - mlx4_get_umem_mr(mr->uobject->context, udata, start, - length, virt_addr, mr_access_flags); + mmr->umem = mlx4_get_umem_mr(udata, start, length, virt_addr, + mr_access_flags); if (IS_ERR(mmr->umem)) { err = PTR_ERR(mmr->umem); /* Prevent mlx4_ib_dereg_mr from free'ing invalid pointer */ diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index e38bab50cecf..429a59c5801c 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,8 @@ static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv_cq); static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv_cq); -static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state); +static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state, + struct ib_udata *udata); enum { MLX4_IB_ACK_REQ_FREQ = 8, @@ -863,6 +865,8 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, int err; struct mlx4_ib_sqp *sqp = NULL; struct mlx4_ib_qp *qp; + struct mlx4_ib_ucontext *context = rdma_udata_to_drv_context( + udata, struct mlx4_ib_ucontext, ibucontext); enum mlx4_ib_qp_type qp_type = (enum mlx4_ib_qp_type) init_attr->qp_type; struct mlx4_ib_cq *mcq; unsigned long flags; @@ -1038,7 +1042,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, if (qp_has_rq(init_attr)) { err = mlx4_ib_db_map_user( - to_mucontext(pd->uobject->context), udata, + context, udata, (src == MLX4_IB_QP_SRC) ? ucmd.qp.db_addr : ucmd.wq.db_addr, &qp->db); @@ -1112,8 +1116,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, } } } else if (src == MLX4_IB_RWQ_SRC) { - err = mlx4_ib_alloc_wqn(to_mucontext(pd->uobject->context), qp, - range_size, &qpn); + err = mlx4_ib_alloc_wqn(context, qp, range_size, &qpn); if (err) goto err_wrid; } else { @@ -1184,8 +1187,7 @@ err_qpn: if (qp->flags & MLX4_IB_QP_NETIF) mlx4_ib_steer_qp_free(dev, qpn, 1); else if (src == MLX4_IB_RWQ_SRC) - mlx4_ib_release_wqn(to_mucontext(pd->uobject->context), - qp, 0); + mlx4_ib_release_wqn(context, qp, 0); else mlx4_qp_release_range(dev->dev, qpn, 1); } @@ -1195,7 +1197,7 @@ err_proxy: err_wrid: if (udata) { if (qp_has_rq(init_attr)) - mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db); + mlx4_ib_db_unmap_user(context, &qp->db); } else { kvfree(qp->sq.wrid); kvfree(qp->rq.wrid); @@ -1942,7 +1944,8 @@ static u8 gid_type_to_qpc(enum ib_gid_type gid_type) * Go over all RSS QP's childes (WQs) and apply their HW state according to * their logic state if the RSS QP is the first RSS QP associated for the WQ. */ -static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num) +static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num, + struct ib_udata *udata) { int err = 0; int i; @@ -1966,7 +1969,7 @@ static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num) } wq->port = port_num; if ((wq->rss_usecnt == 0) && (ibwq->state == IB_WQS_RDY)) { - err = _mlx4_ib_modify_wq(ibwq, IB_WQS_RDY); + err = _mlx4_ib_modify_wq(ibwq, IB_WQS_RDY, udata); if (err) { mutex_unlock(&wq->mutex); break; @@ -1988,7 +1991,8 @@ static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num) if ((wq->rss_usecnt == 1) && (ibwq->state == IB_WQS_RDY)) - if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET)) + if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET, + udata)) pr_warn("failed to reverse WQN=0x%06x\n", ibwq->wq_num); wq->rss_usecnt--; @@ -2000,7 +2004,8 @@ static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num) return err; } -static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl) +static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, + struct ib_udata *udata) { int i; @@ -2011,7 +2016,7 @@ static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl) mutex_lock(&wq->mutex); if ((wq->rss_usecnt == 1) && (ibwq->state == IB_WQS_RDY)) - if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET)) + if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET, udata)) pr_warn("failed to reverse WQN=%x\n", ibwq->wq_num); wq->rss_usecnt--; @@ -2043,9 +2048,10 @@ static void fill_qp_rss_context(struct mlx4_qp_context *context, static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, const struct ib_qp_attr *attr, int attr_mask, - enum ib_qp_state cur_state, enum ib_qp_state new_state) + enum ib_qp_state cur_state, + enum ib_qp_state new_state, + struct ib_udata *udata) { - struct ib_uobject *ibuobject; struct ib_srq *ibsrq; const struct ib_gid_attr *gid_attr = NULL; struct ib_rwq_ind_table *rwq_ind_tbl; @@ -2054,6 +2060,8 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, struct mlx4_ib_qp *qp; struct mlx4_ib_pd *pd; struct mlx4_ib_cq *send_cq, *recv_cq; + struct mlx4_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx4_ib_ucontext, ibucontext); struct mlx4_qp_context *context; enum mlx4_qp_optpar optpar = 0; int sqd_event; @@ -2065,7 +2073,6 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, struct ib_wq *ibwq; ibwq = (struct ib_wq *)src; - ibuobject = ibwq->uobject; ibsrq = NULL; rwq_ind_tbl = NULL; qp_type = IB_QPT_RAW_PACKET; @@ -2076,7 +2083,6 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, struct ib_qp *ibqp; ibqp = (struct ib_qp *)src; - ibuobject = ibqp->uobject; ibsrq = ibqp->srq; rwq_ind_tbl = ibqp->rwq_ind_tbl; qp_type = ibqp->qp_type; @@ -2161,11 +2167,9 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, context->param3 |= cpu_to_be32(1 << 30); } - if (ibuobject) + if (ucontext) context->usr_page = cpu_to_be32( - mlx4_to_hw_uar_index(dev->dev, - to_mucontext(ibuobject->context) - ->uar.index)); + mlx4_to_hw_uar_index(dev->dev, ucontext->uar.index)); else context->usr_page = cpu_to_be32( mlx4_to_hw_uar_index(dev->dev, dev->priv_uar.index)); @@ -2297,7 +2301,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, context->cqn_recv = cpu_to_be32(recv_cq->mcq.cqn); /* Set "fast registration enabled" for all kernel QPs */ - if (!ibuobject) + if (!ucontext) context->params1 |= cpu_to_be32(1 << 11); if (attr_mask & IB_QP_RNR_RETRY) { @@ -2434,7 +2438,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, else sqd_event = 0; - if (!ibuobject && + if (!ucontext && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) context->rlkey_roce_mode |= (1 << 4); @@ -2445,7 +2449,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, * headroom is stamped so that the hardware doesn't start * processing stale work requests. */ - if (!ibuobject && + if (!ucontext && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) { struct mlx4_wqe_ctrl_seg *ctrl; @@ -2509,7 +2513,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type, * entries and reinitialize the QP. */ if (new_state == IB_QPS_RESET) { - if (!ibuobject) { + if (!ucontext) { mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn, ibsrq ? to_msrq(ibsrq) : NULL); if (send_cq != recv_cq) @@ -2735,16 +2739,17 @@ static int _mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, } if (ibqp->rwq_ind_tbl && (new_state == IB_QPS_INIT)) { - err = bringup_rss_rwqs(ibqp->rwq_ind_tbl, attr->port_num); + err = bringup_rss_rwqs(ibqp->rwq_ind_tbl, attr->port_num, + udata); if (err) goto out; } err = __mlx4_ib_modify_qp(ibqp, MLX4_IB_QP_SRC, attr, attr_mask, - cur_state, new_state); + cur_state, new_state, udata); if (ibqp->rwq_ind_tbl && err) - bring_down_rss_rwqs(ibqp->rwq_ind_tbl); + bring_down_rss_rwqs(ibqp->rwq_ind_tbl, udata); if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT)) attr->port_num = 1; @@ -4122,7 +4127,8 @@ static int ib_wq2qp_state(enum ib_wq_state state) } } -static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state) +static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state, + struct ib_udata *udata) { struct mlx4_ib_qp *qp = to_mqp((struct ib_qp *)ibwq); enum ib_qp_state qp_cur_state; @@ -4146,7 +4152,8 @@ static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state) attr_mask = IB_QP_PORT; err = __mlx4_ib_modify_qp(ibwq, MLX4_IB_RWQ_SRC, &attr, - attr_mask, IB_QPS_RESET, IB_QPS_INIT); + attr_mask, IB_QPS_RESET, IB_QPS_INIT, + udata); if (err) { pr_debug("WQN=0x%06x failed to apply RST->INIT on the HW QP\n", ibwq->wq_num); @@ -4158,12 +4165,13 @@ static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state) attr_mask = 0; err = __mlx4_ib_modify_qp(ibwq, MLX4_IB_RWQ_SRC, NULL, attr_mask, - qp_cur_state, qp_new_state); + qp_cur_state, qp_new_state, udata); if (err && (qp_cur_state == IB_QPS_INIT)) { qp_new_state = IB_QPS_RESET; if (__mlx4_ib_modify_qp(ibwq, MLX4_IB_RWQ_SRC, NULL, - attr_mask, IB_QPS_INIT, IB_QPS_RESET)) { + attr_mask, IB_QPS_INIT, IB_QPS_RESET, + udata)) { pr_warn("WQN=0x%06x failed with reverting HW's resources failure\n", ibwq->wq_num); qp_new_state = IB_QPS_INIT; @@ -4226,7 +4234,7 @@ int mlx4_ib_modify_wq(struct ib_wq *ibwq, struct ib_wq_attr *wq_attr, * WQ, so we can apply its port on the WQ. */ if (qp->rss_usecnt) - err = _mlx4_ib_modify_wq(ibwq, new_state); + err = _mlx4_ib_modify_wq(ibwq, new_state, udata); if (!err) ibwq->state = new_state; diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c index 498588eac051..381cf899bcef 100644 --- a/drivers/infiniband/hw/mlx4/srq.c +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -37,6 +37,7 @@ #include "mlx4_ib.h" #include +#include static void *get_wqe(struct mlx4_ib_srq *srq, int n) { @@ -73,6 +74,8 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, struct ib_udata *udata) { struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx4_ib_ucontext, ibucontext); struct mlx4_ib_srq *srq; struct mlx4_wqe_srq_next_seg *next; struct mlx4_wqe_data_seg *scatter; @@ -128,8 +131,8 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, if (err) goto err_mtt; - err = mlx4_ib_db_map_user(to_mucontext(pd->uobject->context), - udata, ucmd.db_addr, &srq->db); + err = mlx4_ib_db_map_user(ucontext, udata, ucmd.db_addr, + &srq->db); if (err) goto err_mtt; } else { @@ -202,7 +205,7 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, err_wrid: if (udata) - mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &srq->db); + mlx4_ib_db_unmap_user(ucontext, &srq->db); else kvfree(srq->wrid); diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 8e6d23d6859f..eaa055007f28 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1151,7 +1151,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( void *cmd_out; struct ib_uobject *uobj = uverbs_attr_get_uobject( attrs, MLX5_IB_ATTR_DEVX_OBJ_CREATE_HANDLE); - struct mlx5_ib_ucontext *c = to_mucontext(uobj->context); + struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context( + &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_ib_dev *dev = to_mdev(c->ibucontext.device); u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; struct devx_obj *obj; @@ -1227,8 +1228,9 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_MODIFY)( MLX5_IB_ATTR_DEVX_OBJ_MODIFY_CMD_OUT); struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, MLX5_IB_ATTR_DEVX_OBJ_MODIFY_HANDLE); - struct mlx5_ib_ucontext *c = to_mucontext(uobj->context); - struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device); + struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context( + &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); + struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device); void *cmd_out; int err; int uid; @@ -1268,11 +1270,12 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_QUERY)( MLX5_IB_ATTR_DEVX_OBJ_QUERY_CMD_OUT); struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, MLX5_IB_ATTR_DEVX_OBJ_QUERY_HANDLE); - struct mlx5_ib_ucontext *c = to_mucontext(uobj->context); + struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context( + &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); void *cmd_out; int err; int uid; - struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device); + struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device); uid = devx_get_uid(c, cmd_in); if (uid < 0) @@ -1370,11 +1373,12 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)( attrs, MLX5_IB_ATTR_DEVX_OBJ_QUERY_ASYNC_HANDLE); u16 cmd_out_len; - struct mlx5_ib_ucontext *c = to_mucontext(uobj->context); + struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context( + &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); struct ib_uobject *fd_uobj; int err; int uid; - struct mlx5_ib_dev *mdev = to_mdev(uobj->context->device); + struct mlx5_ib_dev *mdev = to_mdev(c->ibucontext.device); struct devx_async_cmd_event_file *ev_file; struct devx_async_data *async_data; @@ -1530,7 +1534,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_UMEM_REG)( struct ib_uobject *uobj = uverbs_attr_get_uobject( attrs, MLX5_IB_ATTR_DEVX_UMEM_REG_HANDLE); u32 obj_id; - struct mlx5_ib_ucontext *c = to_mucontext(uobj->context); + struct mlx5_ib_ucontext *c = rdma_udata_to_drv_context( + &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_ib_dev *dev = to_mdev(c->ibucontext.device); int err; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 4f2bc101b061..6b1f0e76900b 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -794,6 +794,8 @@ static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd, struct ib_udata *udata, struct mlx5_ib_rwq *rwq, struct mlx5_ib_create_wq *ucmd) { + struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); int page_shift = 0; int npages; u32 offset = 0; @@ -828,8 +830,7 @@ static int create_user_rq(struct mlx5_ib_dev *dev, struct ib_pd *pd, (unsigned long long)ucmd->buf_addr, rwq->buf_size, npages, page_shift, ncont, offset); - err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context), udata, - ucmd->db_addr, &rwq->db); + err = mlx5_ib_db_map_user(ucontext, udata, ucmd->db_addr, &rwq->db); if (err) { mlx5_ib_dbg(dev, "map failed\n"); goto err_umem; @@ -877,7 +878,8 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, return err; } - context = to_mucontext(pd->uobject->context); + context = rdma_udata_to_drv_context(udata, struct mlx5_ib_ucontext, + ibucontext); if (ucmd.flags & MLX5_QP_FLAG_BFREG_INDEX) { uar_index = bfregn_to_uar_index(dev, &context->bfregi, ucmd.bfreg_index, true); @@ -1456,9 +1458,8 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, struct mlx5_ib_raw_packet_qp *raw_packet_qp = &qp->raw_packet_qp; struct mlx5_ib_sq *sq = &raw_packet_qp->sq; struct mlx5_ib_rq *rq = &raw_packet_qp->rq; - struct ib_uobject *uobj = pd->uobject; - struct ib_ucontext *ucontext = uobj->context; - struct mlx5_ib_ucontext *mucontext = to_mucontext(ucontext); + struct mlx5_ib_ucontext *mucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); int err; u32 tdn = mucontext->tdn; u16 uid = to_mpd(pd)->uid; @@ -1572,9 +1573,8 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, struct ib_qp_init_attr *init_attr, struct ib_udata *udata) { - struct ib_uobject *uobj = pd->uobject; - struct ib_ucontext *ucontext = uobj->context; - struct mlx5_ib_ucontext *mucontext = to_mucontext(ucontext); + struct mlx5_ib_ucontext *mucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_ib_create_qp_resp resp = {}; int inlen; int err; @@ -1916,6 +1916,8 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, int inlen = MLX5_ST_SZ_BYTES(create_qp_in); struct mlx5_core_dev *mdev = dev->mdev; struct mlx5_ib_create_qp_resp resp = {}; + struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_ib_cq *send_cq; struct mlx5_ib_cq *recv_cq; unsigned long flags; @@ -2018,8 +2020,7 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, MLX5_QP_FLAG_TYPE_DCT)) return -EINVAL; - err = get_qp_user_index(to_mucontext(pd->uobject->context), - &ucmd, udata->inlen, &uidx); + err = get_qp_user_index(ucontext, &ucmd, udata->inlen, &uidx); if (err) return err; @@ -2503,8 +2504,11 @@ static const char *ib_qp_type_str(enum ib_qp_type type) static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd, struct ib_qp_init_attr *attr, - struct mlx5_ib_create_qp *ucmd) + struct mlx5_ib_create_qp *ucmd, + struct ib_udata *udata) { + struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_ib_qp *qp; int err = 0; u32 uidx = MLX5_IB_DEFAULT_UIDX; @@ -2513,8 +2517,7 @@ static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd, if (!attr->srq || !attr->recv_cq) return ERR_PTR(-EINVAL); - err = get_qp_user_index(to_mucontext(pd->uobject->context), - ucmd, sizeof(*ucmd), &uidx); + err = get_qp_user_index(ucontext, ucmd, sizeof(*ucmd), &uidx); if (err) return ERR_PTR(err); @@ -2596,15 +2599,17 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd, int err; struct ib_qp_init_attr mlx_init_attr; struct ib_qp_init_attr *init_attr = verbs_init_attr; + struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); if (pd) { dev = to_mdev(pd->device); if (init_attr->qp_type == IB_QPT_RAW_PACKET) { - if (!udata) { + if (!ucontext) { mlx5_ib_dbg(dev, "Raw Packet QP is not supported for kernel consumers\n"); return ERR_PTR(-EINVAL); - } else if (!to_mucontext(pd->uobject->context)->cqe_version) { + } else if (!ucontext->cqe_version) { mlx5_ib_dbg(dev, "Raw Packet QP is only supported for CQE version > 0\n"); return ERR_PTR(-EINVAL); } @@ -2636,7 +2641,7 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd, return ERR_PTR(-EINVAL); } } else { - return mlx5_ib_create_dct(pd, init_attr, &ucmd); + return mlx5_ib_create_dct(pd, init_attr, &ucmd, udata); } } @@ -3274,14 +3279,12 @@ static int modify_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, static unsigned int get_tx_affinity(struct mlx5_ib_dev *dev, struct mlx5_ib_pd *pd, struct mlx5_ib_qp_base *qp_base, - u8 port_num) + u8 port_num, struct ib_udata *udata) { - struct mlx5_ib_ucontext *ucontext = NULL; + struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); unsigned int tx_port_affinity; - if (pd && pd->ibpd.uobject && pd->ibpd.uobject->context) - ucontext = to_mucontext(pd->ibpd.uobject->context); - if (ucontext) { tx_port_affinity = (unsigned int)atomic_add_return( 1, &ucontext->tx_port_affinity) % @@ -3304,8 +3307,10 @@ static unsigned int get_tx_affinity(struct mlx5_ib_dev *dev, static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr, int attr_mask, - enum ib_qp_state cur_state, enum ib_qp_state new_state, - const struct mlx5_ib_modify_qp *ucmd) + enum ib_qp_state cur_state, + enum ib_qp_state new_state, + const struct mlx5_ib_modify_qp *ucmd, + struct ib_udata *udata) { static const u16 optab[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE] = { [MLX5_QP_STATE_RST] = { @@ -3396,7 +3401,8 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, (ibqp->qp_type == IB_QPT_XRC_TGT)) { if (dev->lag_active) { u8 p = mlx5_core_native_port_num(dev->mdev); - tx_affinity = get_tx_affinity(dev, pd, base, p); + tx_affinity = get_tx_affinity(dev, pd, base, p, + udata); context->flags |= cpu_to_be32(tx_affinity << 24); } } @@ -3879,7 +3885,7 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, } err = __mlx5_ib_modify_qp(ibqp, attr, attr_mask, cur_state, - new_state, &ucmd); + new_state, &ucmd, udata); out: mutex_unlock(&qp->mutex); diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 22bd774e0b4e..1ec1beb1296b 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -47,6 +47,8 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, { struct mlx5_ib_dev *dev = to_mdev(pd->device); struct mlx5_ib_create_srq ucmd = {}; + struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mlx5_ib_ucontext, ibucontext); size_t ucmdlen; int err; int npages; @@ -71,8 +73,7 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, return -EINVAL; if (in->type != IB_SRQT_BASIC) { - err = get_srq_user_index(to_mucontext(pd->uobject->context), - &ucmd, udata->inlen, &uidx); + err = get_srq_user_index(ucontext, &ucmd, udata->inlen, &uidx); if (err) return err; } @@ -103,8 +104,7 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, mlx5_ib_populate_pas(dev, srq->umem, page_shift, in->pas, 0); - err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context), udata, - ucmd.db_addr, &srq->db); + err = mlx5_ib_db_map_user(ucontext, udata, ucmd.db_addr, &srq->db); if (err) { mlx5_ib_dbg(dev, "map doorbell failed\n"); goto err_in; diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 516c8cf9c0fd..80c3af217d96 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -435,7 +436,8 @@ static struct ib_srq *mthca_create_srq(struct ib_pd *pd, struct ib_udata *udata) { struct mthca_create_srq ucmd; - struct mthca_ucontext *context = NULL; + struct mthca_ucontext *context = rdma_udata_to_drv_context( + udata, struct mthca_ucontext, ibucontext); struct mthca_srq *srq; int err; @@ -447,8 +449,6 @@ static struct ib_srq *mthca_create_srq(struct ib_pd *pd, return ERR_PTR(-ENOMEM); if (udata) { - context = to_mucontext(pd->uobject->context); - if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { err = -EFAULT; goto err_free; @@ -510,6 +510,8 @@ static struct ib_qp *mthca_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *init_attr, struct ib_udata *udata) { + struct mthca_ucontext *context = rdma_udata_to_drv_context( + udata, struct mthca_ucontext, ibucontext); struct mthca_create_qp ucmd; struct mthca_qp *qp; int err; @@ -522,15 +524,11 @@ static struct ib_qp *mthca_create_qp(struct ib_pd *pd, case IB_QPT_UC: case IB_QPT_UD: { - struct mthca_ucontext *context; - qp = kzalloc(sizeof(*qp), GFP_KERNEL); if (!qp) return ERR_PTR(-ENOMEM); if (udata) { - context = to_mucontext(pd->uobject->context); - if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { kfree(qp); return ERR_PTR(-EFAULT); @@ -568,8 +566,6 @@ static struct ib_qp *mthca_create_qp(struct ib_pd *pd, &init_attr->cap, qp, udata); if (err && udata) { - context = to_mucontext(pd->uobject->context); - mthca_unmap_user_db(to_mdev(pd->device), &context->uar, context->db_tab, @@ -898,6 +894,8 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, { struct mthca_dev *dev = to_mdev(pd->device); struct sg_dma_page_iter sg_iter; + struct mthca_ucontext *context = rdma_udata_to_drv_context( + udata, struct mthca_ucontext, ibucontext); struct mthca_mr *mr; struct mthca_reg_mr ucmd; u64 *pages; @@ -906,12 +904,12 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, int write_mtt_size; if (udata->inlen < sizeof ucmd) { - if (!to_mucontext(pd->uobject->context)->reg_mr_warned) { + if (!context->reg_mr_warned) { mthca_warn(dev, "Process '%s' did not pass in MR attrs.\n", current->comm); mthca_warn(dev, " Update libmthca to fix this.\n"); } - ++to_mucontext(pd->uobject->context)->reg_mr_warned; + ++context->reg_mr_warned; ucmd.mr_attrs = 0; } else if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) return ERR_PTR(-EFAULT); diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index 4e5b5cc17f1d..7a5b25d13faa 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "mthca_dev.h" #include "mthca_cmd.h" @@ -554,10 +555,14 @@ static int mthca_path_set(struct mthca_dev *dev, const struct rdma_ah_attr *ah, static int __mthca_modify_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr, int attr_mask, - enum ib_qp_state cur_state, enum ib_qp_state new_state) + enum ib_qp_state cur_state, + enum ib_qp_state new_state, + struct ib_udata *udata) { struct mthca_dev *dev = to_mdev(ibqp->device); struct mthca_qp *qp = to_mqp(ibqp); + struct mthca_ucontext *context = rdma_udata_to_drv_context( + udata, struct mthca_ucontext, ibucontext); struct mthca_mailbox *mailbox; struct mthca_qp_param *qp_param; struct mthca_qp_context *qp_context; @@ -619,8 +624,7 @@ static int __mthca_modify_qp(struct ib_qp *ibqp, /* leave arbel_sched_queue as 0 */ if (qp->ibqp.uobject) - qp_context->usr_page = - cpu_to_be32(to_mucontext(qp->ibqp.uobject->context)->uar.index); + qp_context->usr_page = cpu_to_be32(context->uar.index); else qp_context->usr_page = cpu_to_be32(dev->driver_uar.index); qp_context->local_qpn = cpu_to_be32(qp->qpn); @@ -913,7 +917,8 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, goto out; } - err = __mthca_modify_qp(ibqp, attr, attr_mask, cur_state, new_state); + err = __mthca_modify_qp(ibqp, attr, attr_mask, cur_state, new_state, + udata); out: mutex_unlock(&qp->mutex); diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c index b8333c79e3fa..06b920385512 100644 --- a/drivers/infiniband/hw/mthca/mthca_srq.c +++ b/drivers/infiniband/hw/mthca/mthca_srq.c @@ -36,6 +36,8 @@ #include +#include + #include "mthca_dev.h" #include "mthca_cmd.h" #include "mthca_memfree.h" @@ -96,17 +98,19 @@ static void mthca_tavor_init_srq_context(struct mthca_dev *dev, struct mthca_pd *pd, struct mthca_srq *srq, struct mthca_tavor_srq_context *context, - bool is_user) + struct ib_udata *udata) { + struct mthca_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mthca_ucontext, ibucontext); + memset(context, 0, sizeof *context); context->wqe_base_ds = cpu_to_be64(1 << (srq->wqe_shift - 4)); context->state_pd = cpu_to_be32(pd->pd_num); context->lkey = cpu_to_be32(srq->mr.ibmr.lkey); - if (is_user) - context->uar = - cpu_to_be32(to_mucontext(pd->ibpd.uobject->context)->uar.index); + if (udata) + context->uar = cpu_to_be32(ucontext->uar.index); else context->uar = cpu_to_be32(dev->driver_uar.index); } @@ -115,8 +119,10 @@ static void mthca_arbel_init_srq_context(struct mthca_dev *dev, struct mthca_pd *pd, struct mthca_srq *srq, struct mthca_arbel_srq_context *context, - bool is_user) + struct ib_udata *udata) { + struct mthca_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct mthca_ucontext, ibucontext); int logsize, max; memset(context, 0, sizeof *context); @@ -131,9 +137,8 @@ static void mthca_arbel_init_srq_context(struct mthca_dev *dev, context->lkey = cpu_to_be32(srq->mr.ibmr.lkey); context->db_index = cpu_to_be32(srq->db_index); context->logstride_usrpage = cpu_to_be32((srq->wqe_shift - 4) << 29); - if (is_user) - context->logstride_usrpage |= - cpu_to_be32(to_mucontext(pd->ibpd.uobject->context)->uar.index); + if (udata) + context->logstride_usrpage |= cpu_to_be32(ucontext->uar.index); else context->logstride_usrpage |= cpu_to_be32(dev->driver_uar.index); context->eq_pd = cpu_to_be32(MTHCA_EQ_ASYNC << 24 | pd->pd_num); diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 138844299d73..38480b7708eb 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "nes.h" @@ -974,7 +975,8 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd, struct nes_adapter *nesadapter = nesdev->nesadapter; struct nes_qp *nesqp; struct nes_cq *nescq; - struct nes_ucontext *nes_ucontext; + struct nes_ucontext *nes_ucontext = rdma_udata_to_drv_context( + udata, struct nes_ucontext, ibucontext); struct nes_hw_cqp_wqe *cqp_wqe; struct nes_cqp_request *cqp_request; struct nes_create_qp_req req; @@ -1055,9 +1057,8 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd, } if (req.user_qp_buffer) nesqp->nesuqp_addr = req.user_qp_buffer; - if (udata && (ibpd->uobject->context)) { + if (udata) { nesqp->user_mode = 1; - nes_ucontext = to_nesucontext(ibpd->uobject->context); if (virt_wqs) { err = 1; list_for_each_entry(nespbl, &nes_ucontext->qp_reg_mem_list, list) { @@ -1078,7 +1079,6 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd, } } - nes_ucontext = to_nesucontext(ibpd->uobject->context); nesqp->mmap_sq_db_index = find_next_zero_bit(nes_ucontext->allocated_wqs, NES_MAX_USER_WQ_REGIONS, nes_ucontext->first_free_wq); @@ -2099,7 +2099,8 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, struct nes_adapter *nesadapter = nesdev->nesadapter; struct ib_mr *ibmr = ERR_PTR(-EINVAL); struct sg_dma_page_iter dma_iter; - struct nes_ucontext *nes_ucontext; + struct nes_ucontext *nes_ucontext = rdma_udata_to_drv_context( + udata, struct nes_ucontext, ibucontext); struct nes_pbl *nespbl; struct nes_mr *nesmr; struct ib_umem *region; @@ -2342,7 +2343,6 @@ reg_user_mr_err: return ERR_PTR(-ENOMEM); } nesmr->region = region; - nes_ucontext = to_nesucontext(pd->uobject->context); pbl_depth = region->length >> 12; pbl_depth += (region->length & (4096-1)) ? 1 : 0; nespbl->pbl_size = pbl_depth*sizeof(u64); diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index a613ebde322f..d51bc3ede9d1 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -1434,7 +1434,7 @@ struct ib_srq *qedr_create_srq(struct ib_pd *ibpd, hw_srq->max_wr = init_attr->attr.max_wr; hw_srq->max_sges = init_attr->attr.max_sge; - if (udata && ibpd->uobject && ibpd->uobject->context) { + if (udata) { if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) { DP_ERR(dev, "create srq: problem copying data from user space\n"); diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 0ced89b51448..b4575b1c2a62 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -37,6 +37,7 @@ #include #include +#include #include "usnic_abi.h" #include "usnic_ib.h" @@ -482,7 +483,8 @@ struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd, int err; struct usnic_ib_dev *us_ibdev; struct usnic_ib_qp_grp *qp_grp; - struct usnic_ib_ucontext *ucontext; + struct usnic_ib_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct usnic_ib_ucontext, ibucontext); int cq_cnt; struct usnic_vnic_res_spec res_spec; struct usnic_ib_create_qp_cmd cmd; @@ -490,7 +492,6 @@ struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd, usnic_dbg("\n"); - ucontext = to_uucontext(pd->uobject->context); us_ibdev = to_usdev(pd->device); if (init_attr->create_flags) diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 6b1b2b75ef60..72664f0a52ee 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "qp.h" #include "vt.h" #include "trace.h" @@ -956,6 +957,8 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, size_t sg_list_sz; struct ib_qp *ret = ERR_PTR(-ENOMEM); struct rvt_dev_info *rdi = ib_to_rvt(ibpd->device); + struct rvt_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct rvt_ucontext, ibucontext); void *priv = NULL; size_t sqsize; @@ -1129,7 +1132,7 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, u32 s = sizeof(struct rvt_rwq) + qp->r_rq.size * sz; qp->ip = rvt_create_mmap_info(rdi, s, - ibpd->uobject->context, + &ucontext->ibucontext, qp->r_rq.wq); if (!qp->ip) { ret = ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/sw/rdmavt/srq.c b/drivers/infiniband/sw/rdmavt/srq.c index 78e06fc456c5..895b3fabd0bf 100644 --- a/drivers/infiniband/sw/rdmavt/srq.c +++ b/drivers/infiniband/sw/rdmavt/srq.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "srq.h" #include "vt.h" @@ -77,6 +78,8 @@ struct ib_srq *rvt_create_srq(struct ib_pd *ibpd, struct ib_udata *udata) { struct rvt_dev_info *dev = ib_to_rvt(ibpd->device); + struct rvt_ucontext *ucontext = rdma_udata_to_drv_context( + udata, struct rvt_ucontext, ibucontext); struct rvt_srq *srq; u32 sz; struct ib_srq *ret; @@ -119,7 +122,7 @@ struct ib_srq *rvt_create_srq(struct ib_pd *ibpd, u32 s = sizeof(struct rvt_rwq) + srq->rq.size * sz; srq->ip = - rvt_create_mmap_info(dev, s, ibpd->uobject->context, + rvt_create_mmap_info(dev, s, &ucontext->ibucontext, srq->rq.wq); if (!srq->ip) { ret = ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index a19832c73d5a..ee9c82cb3b6b 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -284,10 +284,6 @@ static int rvt_query_gid(struct ib_device *ibdev, u8 port_num, &gid->global.interface_id); } -struct rvt_ucontext { - struct ib_ucontext ibucontext; -}; - static inline struct rvt_ucontext *to_iucontext(struct ib_ucontext *ibucontext) { diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index be5d76b2bcca..09ede70dc1e8 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "rxe.h" #include "rxe_loc.h" @@ -343,7 +344,8 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd, struct rxe_cq *rcq = to_rcq(init->recv_cq); struct rxe_cq *scq = to_rcq(init->send_cq); struct rxe_srq *srq = init->srq ? to_rsrq(init->srq) : NULL; - struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL; + struct rxe_ucontext *ucontext = + rdma_udata_to_drv_context(udata, struct rxe_ucontext, ibuc); rxe_add_ref(pd); rxe_add_ref(rcq); @@ -358,11 +360,11 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd, rxe_qp_init_misc(rxe, qp, init); - err = rxe_qp_init_req(rxe, qp, init, context, uresp); + err = rxe_qp_init_req(rxe, qp, init, &ucontext->ibuc, uresp); if (err) goto err1; - err = rxe_qp_init_resp(rxe, qp, init, context, uresp); + err = rxe_qp_init_resp(rxe, qp, init, &ucontext->ibuc, uresp); if (err) goto err2; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 051c3930e808..ffca654c8697 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -33,6 +33,7 @@ #include #include +#include #include "rxe.h" #include "rxe_loc.h" #include "rxe_queue.h" @@ -320,8 +321,9 @@ static struct ib_srq *rxe_create_srq(struct ib_pd *ibpd, int err; struct rxe_dev *rxe = to_rdev(ibpd->device); struct rxe_pd *pd = to_rpd(ibpd); + struct rxe_ucontext *ucontext = + rdma_udata_to_drv_context(udata, struct rxe_ucontext, ibuc); struct rxe_srq *srq; - struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL; struct rxe_create_srq_resp __user *uresp = NULL; if (udata) { @@ -344,7 +346,7 @@ static struct ib_srq *rxe_create_srq(struct ib_pd *ibpd, rxe_add_ref(pd); srq->pd = pd; - err = rxe_srq_from_init(rxe, srq, init, context, uresp); + err = rxe_srq_from_init(rxe, srq, init, &ucontext->ibuc, uresp); if (err) goto err2; diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index 87d66c9630d7..4c257aff7d32 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -186,6 +186,11 @@ struct rvt_driver_params { u8 reserved_operations; }; +/* User context */ +struct rvt_ucontext { + struct ib_ucontext ibucontext; +}; + /* Protection domain */ struct rvt_pd { struct ib_pd ibpd; From 9a7786784dcbec2f6ad52fd98a8d076f808e59f2 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 14 Feb 2019 20:13:31 +0000 Subject: [PATCH 1865/4277] RDMA/uverbs: Fix an error flow in ib_uverbs_poll_cq The new output_written block was wrongly placed before the ret=0, causing the error code to be lost. uverbs_output_written is not expected to fail, and even if it does fail it has no significant impact on the userspace flow. Reported-by: Bart Van Assche Fixes: d6f4a21f309d ("RDMA/uverbs: Mark ioctl responses with UVERBS_ATTR_F_VALID_OUTPUT") Signed-off-by: Jason Gunthorpe Reviewed-by: Leon Romanovsky --- drivers/infiniband/core/uverbs_cmd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index c2e7733a7fe0..4947d1480c6d 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -1186,12 +1186,11 @@ static int ib_uverbs_poll_cq(struct uverbs_attr_bundle *attrs) ret = -EFAULT; goto out_put; } + ret = 0; if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_CORE_OUT)) ret = uverbs_output_written(attrs, UVERBS_ATTR_CORE_OUT); - ret = 0; - out_put: uobj_put_obj_read(cq); return ret; From e50838c27ff7e1438ea2f9ab4bfcb227f90a107f Mon Sep 17 00:00:00 2001 From: Kaike Wan Date: Fri, 15 Feb 2019 13:45:30 -0800 Subject: [PATCH 1866/4277] IB/hfi1: Fix a build warning for TID RDMA READ The following build warning was produced for the TID RDMA READ patch ("IB/hfi1: Enable TID RDMA READ protocol"): drivers/infiniband/hw/hfi1/qp.c: In function 'hfi1_setup_wqe': drivers/infiniband/hw/hfi1/qp.c:328:3: warning: this statement may fall through [-Wimplicit-fallthrough=] hfi1_setup_tid_rdma_wqe(qp, wqe); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/infiniband/hw/hfi1/qp.c:329:2: note: here case IB_QPT_UC: ^~~~ This patch will fix the issue by adding the "fall through" comment. Fixes: f1ab4efa6d32 ("IB/hfi1: Enable TID RDMA READ protocol") Reviewed-by: Mike Marciniszyn Signed-off-by: Kaike Wan Signed-off-by: Dennis Dalessandro Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/hfi1/qp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index d8f7add935df..9b643c2409cf 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -326,6 +326,7 @@ int hfi1_setup_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe, bool *call_send) switch (qp->ibqp.qp_type) { case IB_QPT_RC: hfi1_setup_tid_rdma_wqe(qp, wqe); + /* fall through */ case IB_QPT_UC: if (wqe->length > 0x80000000U) return -EINVAL; From be56b07b4f3d0ab37abfb86a78f47bfcfeb6c51c Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 12 Feb 2019 20:39:14 +0200 Subject: [PATCH 1867/4277] RDMA/nes: Remove useless usecnt variable and redundant memset The internal design of RDMA/core ensures that there dealloc ucontext will be called only if alloc_ucontext succeeded, hence there is no need to manage internal variable to mark validity of ucontext. As part of this change, remove redundant memeset too. Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/nes/nes_verbs.c | 9 +-------- drivers/infiniband/hw/nes/nes_verbs.h | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 38480b7708eb..b23956aa45b8 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -536,7 +536,7 @@ static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev, struct nes_device *nesdev = nesvnic->nesdev; struct nes_adapter *nesadapter = nesdev->nesadapter; struct nes_alloc_ucontext_req req; - struct nes_alloc_ucontext_resp uresp; + struct nes_alloc_ucontext_resp uresp = {}; struct nes_ucontext *nes_ucontext; struct nes_ib_device *nesibdev = nesvnic->nesibdev; @@ -553,8 +553,6 @@ static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev, } - memset(&uresp, 0, sizeof uresp); - uresp.max_qps = nesibdev->max_qp; uresp.max_pds = nesibdev->max_pd; uresp.wq_size = nesdev->nesadapter->max_qp_wr * 2; @@ -579,7 +577,6 @@ static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev, INIT_LIST_HEAD(&nes_ucontext->cq_reg_mem_list); INIT_LIST_HEAD(&nes_ucontext->qp_reg_mem_list); - atomic_set(&nes_ucontext->usecnt, 1); return &nes_ucontext->ibucontext; } @@ -589,12 +586,8 @@ static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev, */ static int nes_dealloc_ucontext(struct ib_ucontext *context) { - /* struct nes_vnic *nesvnic = to_nesvnic(context->device); */ - /* struct nes_device *nesdev = nesvnic->nesdev; */ struct nes_ucontext *nes_ucontext = to_nesucontext(context); - if (!atomic_dec_and_test(&nes_ucontext->usecnt)) - return 0; kfree(nes_ucontext); return 0; } diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h index e02a5662dc20..114a9b59fefd 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.h +++ b/drivers/infiniband/hw/nes/nes_verbs.h @@ -59,7 +59,6 @@ struct nes_ucontext { struct list_head cq_reg_mem_list; struct list_head qp_reg_mem_list; u32 mcrqf; - atomic_t usecnt; }; struct nes_pd { From cfe876d8e6b0491170d44c8040c518b121957104 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 12 Feb 2019 20:39:15 +0200 Subject: [PATCH 1868/4277] RDMA/cxgb4: Remove kref accounting for sync operation Ucontext allocation and release aren't async events and don't need kref accounting. The common layer of RDMA subsystem ensures that dealloc ucontext will be called after all other objects are released. Signed-off-by: Leon Romanovsky Reviewed-by: Steve Wise Tested-by: Raju Rangoju Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 13 ------------- drivers/infiniband/hw/cxgb4/provider.c | 16 +++------------- drivers/infiniband/hw/cxgb4/qp.c | 3 --- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 3a0923f7c60e..5a5da41faef6 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -589,7 +589,6 @@ struct c4iw_ucontext { u32 key; spinlock_t mmap_lock; struct list_head mmaps; - struct kref kref; bool is_32b_cqe; }; @@ -598,18 +597,6 @@ static inline struct c4iw_ucontext *to_c4iw_ucontext(struct ib_ucontext *c) return container_of(c, struct c4iw_ucontext, ibucontext); } -void _c4iw_free_ucontext(struct kref *kref); - -static inline void c4iw_put_ucontext(struct c4iw_ucontext *ucontext) -{ - kref_put(&ucontext->kref, _c4iw_free_ucontext); -} - -static inline void c4iw_get_ucontext(struct c4iw_ucontext *ucontext) -{ - kref_get(&ucontext->kref); -} - struct c4iw_mm_entry { struct list_head entry; u64 addr; diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 680b5e98491d..81fcffb597ab 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -58,28 +58,19 @@ static int fastreg_support = 1; module_param(fastreg_support, int, 0644); MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=1)"); -void _c4iw_free_ucontext(struct kref *kref) +static int c4iw_dealloc_ucontext(struct ib_ucontext *context) { - struct c4iw_ucontext *ucontext; + struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context); struct c4iw_dev *rhp; struct c4iw_mm_entry *mm, *tmp; - ucontext = container_of(kref, struct c4iw_ucontext, kref); + pr_debug("context %p\n", context); rhp = to_c4iw_dev(ucontext->ibucontext.device); - pr_debug("ucontext %p\n", ucontext); list_for_each_entry_safe(mm, tmp, &ucontext->mmaps, entry) kfree(mm); c4iw_release_dev_ucontext(&rhp->rdev, &ucontext->uctx); kfree(ucontext); -} - -static int c4iw_dealloc_ucontext(struct ib_ucontext *context) -{ - struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context); - - pr_debug("context %p\n", context); - c4iw_put_ucontext(ucontext); return 0; } @@ -102,7 +93,6 @@ static struct ib_ucontext *c4iw_alloc_ucontext(struct ib_device *ibdev, c4iw_init_dev_ucontext(&rhp->rdev, &context->uctx); INIT_LIST_HEAD(&context->mmaps); spin_lock_init(&context->mmap_lock); - kref_init(&context->kref); if (udata->outlen < sizeof(uresp) - sizeof(uresp.reserved)) { pr_err_once("Warning - downlevel libcxgb4 (non-fatal), device status page disabled\n"); diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 2509f65f4420..d3a82839f5ea 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -904,8 +904,6 @@ static void free_qp_work(struct work_struct *work) destroy_qp(&rhp->rdev, &qhp->wq, ucontext ? &ucontext->uctx : &rhp->rdev.uctx, !qhp->srq); - if (ucontext) - c4iw_put_ucontext(ucontext); c4iw_put_wr_wait(qhp->wr_waitp); kfree(qhp); } @@ -2338,7 +2336,6 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, insert_mmap(ucontext, ma_sync_key_mm); } - c4iw_get_ucontext(ucontext); qhp->ucontext = ucontext; } if (!attrs->srq) { From e155755e53804e721c8ce99474cc9c65eb8e8bc2 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Wed, 13 Feb 2019 19:23:03 +0200 Subject: [PATCH 1869/4277] RDMA/core: Use simpler device_del() instead of device_unregister() Instead of holding extra reference using get_device() that device_unregister() releases, simplify it as below. device_add() balances with device_del(). device_initialize() balances with put_device(), always via ib_dealloc_device(). Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 1 + drivers/infiniband/core/sysfs.c | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 3325be4f91a5..46d237ce83de 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -369,6 +369,7 @@ void ib_dealloc_device(struct ib_device *device) WARN_ON(!xa_empty(&device->client_data)); WARN_ON(refcount_read(&device->refcount)); rdma_restrack_clean(device); + /* Balances with device_initialize */ put_device(&device->dev); } EXPORT_SYMBOL(ib_dealloc_device); diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index c75692802da8..e04f111fe406 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -1351,15 +1351,12 @@ err: void ib_device_unregister_sysfs(struct ib_device *device) { - /* Hold device until ib_dealloc_device() */ - get_device(&device->dev); - free_port_list_attributes(device); if (device->hw_stats) { kfree(device->hw_stats); free_hsag(&device->dev.kobj, device->hw_stats_ag); } - - device_unregister(&device->dev); + /* Balances with device_add */ + device_del(&device->dev); } From 5767198a14c796354e8a96fcdc7db12ee46a0623 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Wed, 13 Feb 2019 19:23:04 +0200 Subject: [PATCH 1870/4277] RDMA/core: Introduce and use ib_setup_port_attrs() Refactor code for device and port sysfs attributes for reuse. While at it, rename counter part free function to ib_free_port_attrs. Also attribute setup sequence is: (a) port specific init. (b) device stats alloc/init. So for cleanup, follow reverse sequence: (a) device stats dealloc (b) port specific cleanup Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/sysfs.c | 64 ++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index e04f111fe406..7e51b406e89a 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -1279,17 +1279,17 @@ static const struct attribute_group dev_attr_group = { .attrs = ib_dev_attrs, }; -static void free_port_list_attributes(struct ib_device *device) +static void ib_free_port_attrs(struct ib_device *device) { struct kobject *p, *t; list_for_each_entry_safe(p, t, &device->port_list, entry) { struct ib_port *port = container_of(p, struct ib_port, kobj); + list_del(&p->entry); - if (port->hw_stats) { - kfree(port->hw_stats); + if (port->hw_stats_ag) free_hsag(&port->kobj, port->hw_stats_ag); - } + kfree(port->hw_stats); if (port->pma_table) sysfs_remove_group(p, port->pma_table); @@ -1306,24 +1306,14 @@ static void free_port_list_attributes(struct ib_device *device) kobject_put(device->ports_kobj); } -int ib_device_register_sysfs(struct ib_device *device) +static int ib_setup_port_attrs(struct ib_device *device) { - struct device *class_dev = &device->dev; int ret; int i; - device->groups[0] = &dev_attr_group; - class_dev->groups = device->groups; - - ret = device_add(class_dev); - if (ret) - goto err; - - device->ports_kobj = kobject_create_and_add("ports", &class_dev->kobj); - if (!device->ports_kobj) { - ret = -ENOMEM; - goto err_put; - } + device->ports_kobj = kobject_create_and_add("ports", &device->dev.kobj); + if (!device->ports_kobj) + return -ENOMEM; if (rdma_cap_ib_switch(device)) { ret = add_port(device, 0); @@ -1337,26 +1327,42 @@ int ib_device_register_sysfs(struct ib_device *device) } } + return 0; + +err_put: + ib_free_port_attrs(device); + return ret; +} + +int ib_device_register_sysfs(struct ib_device *device) +{ + int ret; + + device->groups[0] = &dev_attr_group; + device->dev.groups = device->groups; + + ret = device_add(&device->dev); + if (ret) + return ret; + + ret = ib_setup_port_attrs(device); + if (ret) { + device_del(&device->dev); + return ret; + } if (device->ops.alloc_hw_stats) setup_hw_stats(device, NULL, 0); return 0; - -err_put: - free_port_list_attributes(device); - device_del(class_dev); -err: - return ret; } void ib_device_unregister_sysfs(struct ib_device *device) { - free_port_list_attributes(device); - - if (device->hw_stats) { - kfree(device->hw_stats); + if (device->hw_stats_ag) free_hsag(&device->dev.kobj, device->hw_stats_ag); - } + kfree(device->hw_stats); + + ib_free_port_attrs(device); /* Balances with device_add */ device_del(&device->dev); } From 5f8f5499005c51656645a011bca81ffb66fcaaca Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Wed, 13 Feb 2019 19:23:06 +0200 Subject: [PATCH 1871/4277] RDMA/core: Move device addition deletion to device.c Move core device addition and removal from sysfs.c to device.c as device.c is more appropriate place for device management. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/core_priv.h | 2 ++ drivers/infiniband/core/device.c | 11 ++++++++++- drivers/infiniband/core/sysfs.c | 14 ++------------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index a1826f4c2e23..eeabe9ca8427 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -54,6 +54,8 @@ struct pkey_index_qp_list { struct list_head qp_list; }; +extern const struct attribute_group ib_dev_attr_group; + int ib_device_register_sysfs(struct ib_device *device); void ib_device_unregister_sysfs(struct ib_device *device); int ib_device_rename(struct ib_device *ibdev, const char *name); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 46d237ce83de..3eddc6e67a16 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -341,6 +341,8 @@ struct ib_device *_ib_alloc_device(size_t size) rdma_restrack_init(device); device->dev.class = &ib_class; + device->groups[0] = &ib_dev_attr_group; + device->dev.groups = device->groups; device_initialize(&device->dev); INIT_LIST_HEAD(&device->event_handler_list); @@ -766,11 +768,15 @@ int ib_register_device(struct ib_device *device, const char *name) ib_device_register_rdmacg(device); + ret = device_add(&device->dev); + if (ret) + goto cg_cleanup; + ret = ib_device_register_sysfs(device); if (ret) { dev_warn(&device->dev, "Couldn't register device with driver model\n"); - goto cg_cleanup; + goto dev_cleanup; } ret = enable_device(device); @@ -781,6 +787,8 @@ int ib_register_device(struct ib_device *device, const char *name) sysfs_cleanup: ib_device_unregister_sysfs(device); +dev_cleanup: + device_del(&device->dev); cg_cleanup: ib_device_unregister_rdmacg(device); ib_cache_cleanup_one(device); @@ -800,6 +808,7 @@ void ib_unregister_device(struct ib_device *device) { disable_device(device); ib_device_unregister_sysfs(device); + device_del(&device->dev); ib_device_unregister_rdmacg(device); ib_cache_cleanup_one(device); release_name(device); diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 7e51b406e89a..9335b15c2e38 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -1275,7 +1275,7 @@ static struct attribute *ib_dev_attrs[] = { NULL, }; -static const struct attribute_group dev_attr_group = { +const struct attribute_group ib_dev_attr_group = { .attrs = ib_dev_attrs, }; @@ -1338,18 +1338,10 @@ int ib_device_register_sysfs(struct ib_device *device) { int ret; - device->groups[0] = &dev_attr_group; - device->dev.groups = device->groups; - - ret = device_add(&device->dev); + ret = ib_setup_port_attrs(device); if (ret) return ret; - ret = ib_setup_port_attrs(device); - if (ret) { - device_del(&device->dev); - return ret; - } if (device->ops.alloc_hw_stats) setup_hw_stats(device, NULL, 0); @@ -1363,6 +1355,4 @@ void ib_device_unregister_sysfs(struct ib_device *device) kfree(device->hw_stats); ib_free_port_attrs(device); - /* Balances with device_add */ - device_del(&device->dev); } From db610a640eeeb268c36a4558414f28e1c269433e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 24 Jan 2019 17:48:38 -0800 Subject: [PATCH 1872/4277] f2fs: add quick mode of checkpoint=disable for QA This mode returns mount() quickly with EAGAIN. We can trigger this by shutdown(F2FS_GOING_DOWN_NEED_FSCK). Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 5 +++++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 6 +++--- fs/f2fs/segment.c | 3 +++ fs/f2fs/super.c | 5 +++++ include/linux/f2fs_fs.h | 1 + 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index f955cd3e0677..622dca707752 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1259,6 +1259,11 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) else __clear_ckpt_flags(ckpt, CP_DISABLED_FLAG); + if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK)) + __set_ckpt_flags(ckpt, CP_DISABLED_QUICK_FLAG); + else + __clear_ckpt_flags(ckpt, CP_DISABLED_QUICK_FLAG); + if (is_sbi_flag_set(sbi, SBI_QUOTA_SKIP_FLUSH)) __set_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG); else diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6b6ec5600089..fe95abb05d40 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -191,6 +191,7 @@ enum { #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ #define DEF_DISABLE_INTERVAL 5 /* 5 secs */ +#define DEF_DISABLE_QUICK_INTERVAL 1 /* 1 secs */ #define DEF_UMOUNT_DISCARD_TIMEOUT 5 /* 5 secs */ struct cp_control { @@ -1101,6 +1102,7 @@ enum { SBI_IS_SHUTDOWN, /* shutdown by ioctl */ SBI_IS_RECOVERED, /* recovered orphan/data */ SBI_CP_DISABLED, /* CP was disabled last mount */ + SBI_CP_DISABLED_QUICK, /* CP was disabled quickly */ SBI_QUOTA_NEED_FLUSH, /* need to flush quota info in CP */ SBI_QUOTA_SKIP_FLUSH, /* skip flushing quota in current CP */ SBI_QUOTA_NEED_REPAIR, /* quota file may be corrupted */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0d461321edfc..fe6f92fbba38 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1972,11 +1972,11 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) break; case F2FS_GOING_DOWN_NEED_FSCK: set_sbi_flag(sbi, SBI_NEED_FSCK); + set_sbi_flag(sbi, SBI_CP_DISABLED_QUICK); + set_sbi_flag(sbi, SBI_IS_DIRTY); /* do checkpoint only */ ret = f2fs_sync_fs(sb, 1); - if (ret) - goto out; - break; + goto out; default: ret = -EINVAL; goto out; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5b2b9be6f28d..342b720fb4db 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -868,6 +868,9 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi) if (holes[DATA] > ovp || holes[NODE] > ovp) return -EAGAIN; + if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) && + dirty_segments(sbi) > overprovision_segments(sbi)) + return -EAGAIN; return 0; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 24efd76ca151..5e1f8573a17f 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3205,6 +3205,10 @@ try_onemore: if (__is_set_ckpt_flags(F2FS_CKPT(sbi), CP_QUOTA_NEED_FSCK_FLAG)) set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR); + if (__is_set_ckpt_flags(F2FS_CKPT(sbi), CP_DISABLED_QUICK_FLAG)) { + set_sbi_flag(sbi, SBI_CP_DISABLED_QUICK); + sbi->interval_time[DISABLE_TIME] = DEF_DISABLE_QUICK_INTERVAL; + } /* Initialize device list */ err = f2fs_scan_devices(sbi); @@ -3392,6 +3396,7 @@ skip_recovery: cur_cp_version(F2FS_CKPT(sbi))); f2fs_update_time(sbi, CP_TIME); f2fs_update_time(sbi, REQ_TIME); + clear_sbi_flag(sbi, SBI_CP_DISABLED_QUICK); return 0; free_meta: diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index d7711048ef93..d6befe1f9dc7 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -116,6 +116,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_DISABLED_QUICK_FLAG 0x00002000 #define CP_DISABLED_FLAG 0x00001000 #define CP_QUOTA_NEED_FSCK_FLAG 0x00000800 #define CP_LARGE_NAT_BITMAP_FLAG 0x00000400 From b86232536c3ec068b0960ffc62c61a872412b2b7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 25 Jan 2019 09:12:13 -0800 Subject: [PATCH 1873/4277] f2fs: try to keep CP_TRIMMED_FLAG after successful umount If every discard were issued successfully, we can avoid further discard. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 342b720fb4db..fdd8cd21522f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1063,6 +1063,8 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } else if (discard_type == DPOLICY_UMOUNT) { dpolicy->max_requests = UINT_MAX; dpolicy->io_aware = false; + /* we need to issue all to keep CP_TRIMMED_FLAG */ + dpolicy->granularity = 1; } } From b460866d27089ffaf915a5a3340b4d8c9f079d33 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 25 Jan 2019 10:26:39 -0800 Subject: [PATCH 1874/4277] f2fs: don't wake up too frequently, if there is lots of IOs Otherwise, it wakes up discard thread which will sleep again by busy IOs in a loop. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index a77f76f528b6..5c7ed0442d6e 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -865,7 +865,7 @@ static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force) } } mutex_unlock(&dcc->cmd_lock); - if (!wakeup) + if (!wakeup || !is_idle(sbi, DISCARD_TIME)) return; wake_up: dcc->discard_wake = 1; From 11ac8ef8d8c575409dcce3bcf1dda3c9f8c7f5e9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 25 Jan 2019 12:05:25 -0800 Subject: [PATCH 1875/4277] f2fs: avoid null pointer exception in dcc_info If dcc_info is not set yet, we can get null pointer panic. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fe95abb05d40..8c928cd72b61 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2161,10 +2161,17 @@ static inline bool is_idle(struct f2fs_sb_info *sbi, int type) get_pages(sbi, F2FS_RD_META) || get_pages(sbi, F2FS_WB_DATA) || get_pages(sbi, F2FS_WB_CP_DATA) || get_pages(sbi, F2FS_DIO_READ) || - get_pages(sbi, F2FS_DIO_WRITE) || - atomic_read(&SM_I(sbi)->dcc_info->queued_discard) || - atomic_read(&SM_I(sbi)->fcc_info->queued_flush)) + get_pages(sbi, F2FS_DIO_WRITE)) return false; + + if (SM_I(sbi) && SM_I(sbi)->dcc_info && + atomic_read(&SM_I(sbi)->dcc_info->queued_discard)) + return false; + + if (SM_I(sbi) && SM_I(sbi)->fcc_info && + atomic_read(&SM_I(sbi)->fcc_info->queued_flush)) + return false; + return f2fs_time_over(sbi, type); } From 0e0667b625cf64243df83171bff61f9d350b9ca5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 27 Jan 2019 17:59:53 -0800 Subject: [PATCH 1876/4277] f2fs: flush quota blocks after turnning it off After quota_off, we'll get some dirty blocks. If put_super don't have a chance to flush them by checkpoint, it causes NULL pointer exception in end_io after iput(node_inode). (e.g., by checkpoint=disable) Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5e1f8573a17f..7694cb350734 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2026,6 +2026,12 @@ void f2fs_quota_off_umount(struct super_block *sb) set_sbi_flag(F2FS_SB(sb), SBI_QUOTA_NEED_REPAIR); } } + /* + * In case of checkpoint=disable, we must flush quota blocks. + * This can cause NULL exception for node_inode in end_io, since + * put_super already dropped it. + */ + sync_filesystem(sb); } static void f2fs_truncate_quota_inode_pages(struct super_block *sb) From 812a95977fd2f0d1f220c716a98a7f22e22f488d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Jan 2019 14:04:33 -0800 Subject: [PATCH 1877/4277] f2fs: sync filesystem after roll-forward recovery Some works after roll-forward recovery can get an error which will release all the data structures. Let's flush them in order to make it clean. One possible corruption came from: [ 90.400500] list_del corruption. prev->next should be ffffffed1f566208, but was (null) [ 90.675349] Call trace: [ 90.677869] __list_del_entry_valid+0x94/0xb4 [ 90.682351] remove_dirty_inode+0xac/0x114 [ 90.686563] __f2fs_write_data_pages+0x6a8/0x6c8 [ 90.691302] f2fs_write_data_pages+0x40/0x4c [ 90.695695] do_writepages+0x80/0xf0 [ 90.699372] __writeback_single_inode+0xdc/0x4ac [ 90.704113] writeback_sb_inodes+0x280/0x440 [ 90.708501] wb_writeback+0x1b8/0x3d0 [ 90.712267] wb_workfn+0x1a8/0x4d4 [ 90.715765] process_one_work+0x1c0/0x3d4 [ 90.719883] worker_thread+0x224/0x344 [ 90.723739] kthread+0x120/0x130 [ 90.727055] ret_from_fork+0x10/0x18 Reported-by: Sahitya Tummala Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 5 +++-- fs/f2fs/node.c | 4 +++- fs/f2fs/super.c | 44 +++++++++++++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 622dca707752..03fea4efd64b 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -306,8 +306,9 @@ static int f2fs_write_meta_pages(struct address_space *mapping, goto skip_write; /* collect a number of dirty meta pages and write together */ - if (wbc->for_kupdate || - get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) + if (wbc->sync_mode != WB_SYNC_ALL && + get_pages(sbi, F2FS_DIRTY_META) < + nr_pages_to_skip(sbi, META)) goto skip_write; /* if locked failed, cp will flush dirty pages instead */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4f450e573312..f6ff84e29749 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1920,7 +1920,9 @@ static int f2fs_write_node_pages(struct address_space *mapping, f2fs_balance_fs_bg(sbi); /* collect a number of dirty node pages and write together */ - if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE)) + if (wbc->sync_mode != WB_SYNC_ALL && + get_pages(sbi, F2FS_DIRTY_NODES) < + nr_pages_to_skip(sbi, NODE)) goto skip_write; if (wbc->sync_mode == WB_SYNC_ALL) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7694cb350734..384d1c248857 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1458,9 +1458,16 @@ static int f2fs_enable_quotas(struct super_block *sb); static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) { + unsigned int s_flags = sbi->sb->s_flags; struct cp_control cpc; - int err; + int err = 0; + int ret; + if (s_flags & SB_RDONLY) { + f2fs_msg(sbi->sb, KERN_ERR, + "checkpoint=disable on readonly fs"); + return -EINVAL; + } sbi->sb->s_flags |= SB_ACTIVE; f2fs_update_time(sbi, DISABLE_TIME); @@ -1468,18 +1475,24 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) while (!f2fs_time_over(sbi, DISABLE_TIME)) { mutex_lock(&sbi->gc_mutex); err = f2fs_gc(sbi, true, false, NULL_SEGNO); - if (err == -ENODATA) + if (err == -ENODATA) { + err = 0; break; + } if (err && err != -EAGAIN) - return err; + break; } - err = sync_filesystem(sbi->sb); - if (err) - return err; + ret = sync_filesystem(sbi->sb); + if (ret || err) { + err = ret ? ret: err; + goto restore_flag; + } - if (f2fs_disable_cp_again(sbi)) - return -EAGAIN; + if (f2fs_disable_cp_again(sbi)) { + err = -EAGAIN; + goto restore_flag; + } mutex_lock(&sbi->gc_mutex); cpc.reason = CP_PAUSE; @@ -1488,7 +1501,9 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) sbi->unusable_block_count = 0; mutex_unlock(&sbi->gc_mutex); - return 0; +restore_flag: + sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ + return err; } static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi) @@ -3369,7 +3384,7 @@ skip_recovery: if (test_opt(sbi, DISABLE_CHECKPOINT)) { err = f2fs_disable_checkpoint(sbi); if (err) - goto free_meta; + goto sync_free_meta; } else if (is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)) { f2fs_enable_checkpoint(sbi); } @@ -3382,7 +3397,7 @@ skip_recovery: /* After POR, we can run background GC thread.*/ err = f2fs_start_gc_thread(sbi); if (err) - goto free_meta; + goto sync_free_meta; } kvfree(options); @@ -3405,6 +3420,11 @@ skip_recovery: clear_sbi_flag(sbi, SBI_CP_DISABLED_QUICK); return 0; +sync_free_meta: + /* safe to flush all the data */ + sync_filesystem(sbi->sb); + retry = false; + free_meta: #ifdef CONFIG_QUOTA f2fs_truncate_quota_inode_pages(sb); @@ -3418,6 +3438,8 @@ free_meta: * falls into an infinite loop in f2fs_sync_meta_pages(). */ truncate_inode_pages_final(META_MAPPING(sbi)); + /* evict some inodes being cached by GC */ + evict_inodes(sb); f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); From eecfa42716e63a99c4d1053b137aa10a3b94c0da Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Fri, 25 Jan 2019 20:11:39 +0800 Subject: [PATCH 1878/4277] f2fs: use xattr_prefix to wrap up Let's use xattr_prefix instead of open code. No logic changes. Signed-off-by: Gao Xiang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 18d5ffbc5e8c..fa620d31ea5f 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -538,7 +538,7 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) if (!handler || (handler->list && !handler->list(dentry))) continue; - prefix = handler->prefix ?: handler->name; + prefix = xattr_prefix(handler); prefix_len = strlen(prefix); size = prefix_len + entry->e_name_len + 1; if (buffer) { From 1ffdc3807589779626924eb600db784d3d943a08 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Fri, 25 Jan 2019 15:35:01 +0800 Subject: [PATCH 1879/4277] f2fs: fix typos in code comments lengh -> length Signed-off-by: Geliang Tang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- include/linux/f2fs_fs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index d6befe1f9dc7..8d57aaee8166 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -187,7 +187,7 @@ struct f2fs_orphan_block { struct f2fs_extent { __le32 fofs; /* start file offset of the extent */ __le32 blk; /* start block address of the extent */ - __le32 len; /* lengh of the extent */ + __le32 len; /* length of the extent */ } __packed; #define F2FS_NAME_LEN 255 @@ -512,7 +512,7 @@ typedef __le32 f2fs_hash_t; struct f2fs_dir_entry { __le32 hash_code; /* hash code of file name */ __le32 ino; /* inode number */ - __le16 name_len; /* lengh of file name */ + __le16 name_len; /* length of file name */ __u8 file_type; /* file type */ } __packed; From cc4b1242d7e3b42eed73881fc749944146493e4f Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Thu, 6 Dec 2018 20:05:34 +0100 Subject: [PATCH 1880/4277] vfs: fix preadv64v2 and pwritev64v2 compat syscalls with offset == -1 The preadv2 and pwritev2 syscalls are supposed to emulate the readv and writev syscalls when offset == -1. Therefore the compat code should check for offset before calling do_compat_preadv64 and do_compat_pwritev64. This is the case for the preadv2 and pwritev2 syscalls, but handling of offset == -1 is missing in their 64-bit equivalent. This patch fixes that, calling do_compat_readv and do_compat_writev when offset == -1. This fixes the following glibc tests on x32: - misc/tst-preadvwritev2 - misc/tst-preadvwritev64v2 Cc: Alexander Viro Cc: H.J. Lu Signed-off-by: Aurelien Jarno Signed-off-by: Al Viro --- fs/read_write.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/read_write.c b/fs/read_write.c index ff3c5e6f87cf..27b69b85d49f 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1238,6 +1238,9 @@ COMPAT_SYSCALL_DEFINE5(preadv64v2, unsigned long, fd, const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos, rwf_t, flags) { + if (pos == -1) + return do_compat_readv(fd, vec, vlen, flags); + return do_compat_preadv64(fd, vec, vlen, pos, flags); } #endif @@ -1344,6 +1347,9 @@ COMPAT_SYSCALL_DEFINE5(pwritev64v2, unsigned long, fd, const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos, rwf_t, flags) { + if (pos == -1) + return do_compat_writev(fd, vec, vlen, flags); + return do_compat_pwritev64(fd, vec, vlen, pos, flags); } #endif From 22cb45d7692ab502dd47dc5a607b3af240ee1e37 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 14 Feb 2019 09:04:08 +0000 Subject: [PATCH 1881/4277] swiotlb: drop pointless static qualifier in swiotlb_create_debugfs() There is no need to have the 'struct dentry *d_swiotlb_usage' variable static since new value always be assigned before use it. Signed-off-by: YueHaibing Signed-off-by: Konrad Rzeszutek Wilk --- kernel/dma/swiotlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index a01b83e95a2a..2b0c8fd9658e 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -682,7 +682,7 @@ swiotlb_dma_supported(struct device *hwdev, u64 mask) static int __init swiotlb_create_debugfs(void) { - static struct dentry *d_swiotlb_usage; + struct dentry *d_swiotlb_usage; struct dentry *ent; d_swiotlb_usage = debugfs_create_dir("swiotlb", NULL); From e3dd12f0eacc52ddf5cd4e6651e09daebcb25f0e Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Sat, 9 Feb 2019 08:53:13 -0800 Subject: [PATCH 1882/4277] Input: st1232 - switch to gpiod API Use devm_gpiod_get_optional() and gpiod_set_value_cansleep() instead of the old API. The st1232_ts_power() now passes on the inverted "poweron" value to reflect the correct logical value. Signed-off-by: Martin Kepplinger Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 634d6c243845..32819ee6ec4b 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -11,13 +11,12 @@ */ #include -#include +#include #include #include #include #include #include -#include #include #include #include @@ -45,7 +44,7 @@ struct st1232_ts_data { struct i2c_client *client; struct input_dev *input_dev; struct dev_pm_qos_request low_latency_req; - int reset_gpio; + struct gpio_desc *reset_gpio; const struct st_chip_info *chip_info; int read_buf_len; u8 *read_buf; @@ -142,8 +141,8 @@ end: static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) { - if (gpio_is_valid(ts->reset_gpio)) - gpio_direction_output(ts->reset_gpio, poweron); + if (ts->reset_gpio) + gpiod_set_value_cansleep(ts->reset_gpio, !poweron); } static const struct st_chip_info st1232_chip_info = { @@ -215,15 +214,13 @@ static int st1232_ts_probe(struct i2c_client *client, ts->client = client; ts->input_dev = input_dev; - ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); - if (gpio_is_valid(ts->reset_gpio)) { - error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); - if (error) { - dev_err(&client->dev, - "Unable to request GPIO pin %d.\n", - ts->reset_gpio); - return error; - } + ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL, + GPIOD_OUT_HIGH); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(&client->dev, "Unable to request GPIO pin: %d.\n", + error); + return error; } st1232_ts_power(ts, true); From 6b65189a2d09e0821c80581455b748c23c9342e9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 5 Feb 2019 14:21:45 -0800 Subject: [PATCH 1883/4277] Input: ims-pcu - switch to using brightness_set_blocking() Now that LEDs core allows "blocking" flavor of "set brightness" method we can use it and get rid of private work item. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 3d51175c4d72..74cf3b612f05 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -39,8 +39,6 @@ struct ims_pcu_gamepad { struct ims_pcu_backlight { struct led_classdev cdev; - struct work_struct work; - enum led_brightness desired_brightness; char name[32]; }; @@ -949,14 +947,14 @@ out: #define IMS_PCU_MAX_BRIGHTNESS 31998 -static void ims_pcu_backlight_work(struct work_struct *work) +static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev, + enum led_brightness value) { struct ims_pcu_backlight *backlight = - container_of(work, struct ims_pcu_backlight, work); + container_of(cdev, struct ims_pcu_backlight, cdev); struct ims_pcu *pcu = container_of(backlight, struct ims_pcu, backlight); - int desired_brightness = backlight->desired_brightness; - __le16 br_val = cpu_to_le16(desired_brightness); + __le16 br_val = cpu_to_le16(value); int error; mutex_lock(&pcu->cmd_mutex); @@ -966,19 +964,11 @@ static void ims_pcu_backlight_work(struct work_struct *work) if (error && error != -ENODEV) dev_warn(pcu->dev, "Failed to set desired brightness %u, error: %d\n", - desired_brightness, error); + value, error); mutex_unlock(&pcu->cmd_mutex); -} -static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev, - enum led_brightness value) -{ - struct ims_pcu_backlight *backlight = - container_of(cdev, struct ims_pcu_backlight, cdev); - - backlight->desired_brightness = value; - schedule_work(&backlight->work); + return error; } static enum led_brightness @@ -1015,14 +1005,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu) struct ims_pcu_backlight *backlight = &pcu->backlight; int error; - INIT_WORK(&backlight->work, ims_pcu_backlight_work); snprintf(backlight->name, sizeof(backlight->name), "pcu%d::kbd_backlight", pcu->device_no); backlight->cdev.name = backlight->name; backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS; backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness; - backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness; + backlight->cdev.brightness_set_blocking = + ims_pcu_backlight_set_brightness; error = led_classdev_register(pcu->dev, &backlight->cdev); if (error) { @@ -1040,7 +1030,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu) struct ims_pcu_backlight *backlight = &pcu->backlight; led_classdev_unregister(&backlight->cdev); - cancel_work_sync(&backlight->work); } From 16ab4f43eb953277e915115c1d811d2a518d6bf2 Mon Sep 17 00:00:00 2001 From: Matthias Fend Date: Mon, 11 Feb 2019 00:30:26 -0800 Subject: [PATCH 1884/4277] Input: st1232 - handle common DT bindings This is required to specify generic touchscreen properties via DT. Signed-off-by: Matthias Fend Reviewed-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/sitronix-st1232.txt | 2 ++ drivers/input/touchscreen/st1232.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt index e73e826e0f2a..019373253b28 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt @@ -10,6 +10,8 @@ Required properties: Optional properties: - gpios: a phandle to the reset GPIO +For additional optional properties see: touchscreen.txt + Example: i2c@00000000 { diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 32819ee6ec4b..34923399ece4 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -20,6 +20,7 @@ #include #include #include +#include #define ST1232_TS_NAME "st1232-ts" #define ST1633_TS_NAME "st1633-ts" @@ -43,6 +44,7 @@ struct st_chip_info { struct st1232_ts_data { struct i2c_client *client; struct input_dev *input_dev; + struct touchscreen_properties prop; struct dev_pm_qos_request low_latency_req; struct gpio_desc *reset_gpio; const struct st_chip_info *chip_info; @@ -112,8 +114,8 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); - input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); + touchscreen_report_pos(input_dev, &ts->prop, + finger[i].x, finger[i].y, true); input_mt_sync(input_dev); count++; } @@ -243,6 +245,8 @@ static int st1232_ts_probe(struct i2c_client *client, input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ts->chip_info->max_y, 0, 0); + touchscreen_parse_properties(input_dev, true, &ts->prop); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, st1232_ts_irq_handler, IRQF_ONESHOT, From bab4a6cca024dc202b1e893edbe34f58f1d43f3e Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Mon, 11 Feb 2019 14:15:54 -0800 Subject: [PATCH 1885/4277] Input: qt2160 - remove redundant spinlock Remove a spinlock which prevents schedule_delayed_work() and mod_delayed_work() from executing concurrently. This was required back when mod_delayed_work() did not exist, and had to be implemented with a cancel + schedule. See commit e7c2f967445d ("workqueue: use mod_delayed_work() instead of __cancel + queue") schedule_delayed_work() and mod_delayed_work() can now be used concurrently. So the spinlock is no longer needed. Signed-off-by: Sven Van Asbroeck Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/qt2160.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index 43b86482dda0..23dc7c0c7d78 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -69,7 +69,6 @@ struct qt2160_data { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; - spinlock_t lock; /* Protects canceling/rescheduling of dwork */ unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; u16 key_matrix; #ifdef CONFIG_LEDS_CLASS @@ -221,22 +220,15 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160) static irqreturn_t qt2160_irq(int irq, void *_qt2160) { struct qt2160_data *qt2160 = _qt2160; - unsigned long flags; - - spin_lock_irqsave(&qt2160->lock, flags); mod_delayed_work(system_wq, &qt2160->dwork, 0); - spin_unlock_irqrestore(&qt2160->lock, flags); - return IRQ_HANDLED; } static void qt2160_schedule_read(struct qt2160_data *qt2160) { - spin_lock_irq(&qt2160->lock); schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); - spin_unlock_irq(&qt2160->lock); } static void qt2160_worker(struct work_struct *work) @@ -406,7 +398,6 @@ static int qt2160_probe(struct i2c_client *client, qt2160->client = client; qt2160->input = input; INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); - spin_lock_init(&qt2160->lock); input->name = "AT42QT2160 Touch Sense Keyboard"; input->id.bustype = BUS_I2C; From 4e116e93f4fbd1b5f0e800d312d62d1917370d1a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Feb 2019 14:48:04 -0800 Subject: [PATCH 1886/4277] Input: db9 - mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: drivers/input/joystick/db9.c: In function ‘db9_saturn_read_packet’: drivers/input/joystick/db9.c:256:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (tmp == 0xff) { ^ drivers/input/joystick/db9.c:263:2: note: here default: ^~~~~~~ Notice that, in this particular case, the code comment is modified in accordance with what GCC is expecting to find. This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/db9.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 804b1b80a8be..5a52b65bef9a 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -259,7 +259,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char db9_saturn_write_sub(port, type, 3, powered, 0); return data[0] = 0xe3; } - /* else: fall through */ + /* fall through */ default: return data[0]; } From 43bcd820bd61859f5d4f6d9e7da4210ab4fcf448 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Sat, 16 Feb 2019 22:53:27 -0800 Subject: [PATCH 1887/4277] Input: synaptics_i2c - remove redundant spinlock Remove a leftover spinlock. This was required back when mod_delayed_work() did not exist, and had to be implemented with a cancel + queue. See commit e7c2f967445d ("workqueue: use mod_delayed_work() instead of __cancel + queue") schedule_delayed_work() and mod_delayed_work() can now be used concurrently. So the spinlock is no longer needed. Signed-off-by: Sven Van Asbroeck Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics_i2c.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 8538318d332c..fa304648d611 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -219,7 +219,6 @@ struct synaptics_i2c { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; - spinlock_t lock; int no_data_count; int no_decel_param; int reduce_report_param; @@ -369,23 +368,11 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) return xy_delta || gesture; } -static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch, - unsigned long delay) -{ - unsigned long flags; - - spin_lock_irqsave(&touch->lock, flags); - - mod_delayed_work(system_wq, &touch->dwork, delay); - - spin_unlock_irqrestore(&touch->lock, flags); -} - static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) { struct synaptics_i2c *touch = dev_id; - synaptics_i2c_reschedule_work(touch, 0); + mod_delayed_work(system_wq, &touch->dwork, 0); return IRQ_HANDLED; } @@ -461,7 +448,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work) * We poll the device once in THREAD_IRQ_SLEEP_SECS and * if error is detected, we try to reset and reconfigure the touchpad. */ - synaptics_i2c_reschedule_work(touch, delay); + mod_delayed_work(system_wq, &touch->dwork, delay); } static int synaptics_i2c_open(struct input_dev *input) @@ -474,7 +461,7 @@ static int synaptics_i2c_open(struct input_dev *input) return ret; if (polling_req) - synaptics_i2c_reschedule_work(touch, + mod_delayed_work(system_wq, &touch->dwork, msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; @@ -530,7 +517,6 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien touch->scan_rate_param = scan_rate; set_scan_rate(touch, scan_rate); INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); - spin_lock_init(&touch->lock); return touch; } @@ -637,7 +623,7 @@ static int __maybe_unused synaptics_i2c_resume(struct device *dev) if (ret) return ret; - synaptics_i2c_reschedule_work(touch, + mod_delayed_work(system_wq, &touch->dwork, msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; From 84ef1b339c2fa27e664657d2493601cc025cc9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 16 Feb 2019 23:03:13 -0800 Subject: [PATCH 1888/4277] Input: goodix - support Goodix gt5688 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From what I've seen in vendor trees it's fine to treat this as gt1x¹. Tested on the Purism Librem 5 Devkit (Rocktech JH057N00900 panel). [1]: https://github.com/TadiT7/android_kernel_mtk-4.4/tree/master/drivers/input/touchscreen/mediatek/GT5688 Signed-off-by: Guido Günther Reviewed-by: Bastien Nocera Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/touchscreen/goodix.txt | 1 + drivers/input/touchscreen/goodix.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt index f7e95c52f3c7..57d3d8870a09 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt @@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller Required properties: - compatible : Should be "goodix,gt1151" + or "goodix,gt5688" or "goodix,gt911" or "goodix,gt9110" or "goodix,gt912" diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f2d9c2c41885..47b1ced41576 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id) { switch (id) { case 1151: + case 5688: return >1x_chip_data; case 911: @@ -942,6 +943,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); #ifdef CONFIG_OF static const struct of_device_id goodix_of_match[] = { { .compatible = "goodix,gt1151" }, + { .compatible = "goodix,gt5688" }, { .compatible = "goodix,gt911" }, { .compatible = "goodix,gt9110" }, { .compatible = "goodix,gt912" }, From 1a3d1306ff3ea414309b1c282eba4ae2431ff4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 16 Feb 2019 23:03:41 -0800 Subject: [PATCH 1889/4277] Input: goodix - refer to touchscreen.txt in device tree bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refer to touchscreen.txt for generic touch properties. This avoids duplication and we're using the generic code to parse these in the driver. While at that add touchscreen-size-{x,y} which are respected by the driver as well. Signed-off-by: Guido Günther Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/goodix.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt index 57d3d8870a09..8cf0b4d38a7e 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt @@ -19,11 +19,14 @@ Optional properties: - irq-gpios : GPIO pin used for IRQ. The driver uses the interrupt gpio pin as output to reset the device. - reset-gpios : GPIO pin used for reset + - touchscreen-inverted-x + - touchscreen-inverted-y + - touchscreen-size-x + - touchscreen-size-y + - touchscreen-swapped-x-y - - touchscreen-inverted-x : X axis is inverted (boolean) - - touchscreen-inverted-y : Y axis is inverted (boolean) - - touchscreen-swapped-x-y : X and Y axis are swapped (boolean) - (swapping is done after inverting the axis) +The touchscreen-* properties are documented in touchscreen.txt in this +directory. Example: From c1c00aa53a636d635d9bfad15652fa0694b54f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 16 Feb 2019 23:04:43 -0800 Subject: [PATCH 1890/4277] Input: goodix - print values in case of inconsistencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Invalid config" gives little idea what's wrong. Print the values that must not be 0 so we know which ones are off. Signed-off-by: Guido Günther Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 47b1ced41576..f57d82220a88 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -693,7 +693,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) touchscreen_parse_properties(ts->input_dev, true, &ts->prop); if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) { - dev_err(&ts->client->dev, "Invalid config, using defaults\n"); + dev_err(&ts->client->dev, + "Invalid config (%d, %d, %d), using defaults\n", + ts->prop.max_x, ts->prop.max_y, ts->max_touch_num); ts->prop.max_x = GOODIX_MAX_WIDTH - 1; ts->prop.max_y = GOODIX_MAX_HEIGHT - 1; ts->max_touch_num = GOODIX_MAX_CONTACTS; From 5736610ab61622ca4ca09a5778a8b18fd3220b1b Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 11 Feb 2019 10:46:46 +0100 Subject: [PATCH 1891/4277] rtc: meson: remove useless rtc_nvmem_unregister call rtc_nvmem_unregister() is called on rtc_device release so it is not necessary to call it from the driver. Reviewed-by: Martin Blumenstingl Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-meson.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c index 6fc4b09e7ebd..e08b981dfc21 100644 --- a/drivers/rtc/rtc-meson.c +++ b/drivers/rtc/rtc-meson.c @@ -373,13 +373,10 @@ static int meson_rtc_probe(struct platform_device *pdev) ret = rtc_register_device(rtc->rtc); if (ret) - goto out_unregister_nvmem; + goto out_disable_vdd; return 0; -out_unregister_nvmem: - rtc_nvmem_unregister(rtc->rtc); - out_disable_vdd: regulator_disable(rtc->vdd); return ret; From ed3c52a075e9488cc672c5658414b2f157fc5688 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:03 +0000 Subject: [PATCH 1892/4277] rtc: isl1208: Introduce driver state struct This driver has no state of its own, depending entirely on what is in the generic rtc device. Intoduce a state struct. For now it only contains a pointer to the rtc device struct, but future patches will add more data. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 263af3d8cd9f..d8e0670e12fc 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -79,6 +79,11 @@ enum { TYPE_ISL1219, }; +/* Device state */ +struct isl1208_state { + struct rtc_device *rtc; +}; + /* block read */ static int isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], @@ -557,7 +562,7 @@ isl1208_rtc_interrupt(int irq, void *data) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct i2c_client *client = data; - struct rtc_device *rtc = i2c_get_clientdata(client); + struct isl1208_state *isl1208 = i2c_get_clientdata(client); int handled = 0, sr, err; /* @@ -580,7 +585,7 @@ isl1208_rtc_interrupt(int irq, void *data) if (sr & ISL1208_REG_SR_ALM) { dev_dbg(&client->dev, "alarm!\n"); - rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF); /* Clear the alarm */ sr &= ~ISL1208_REG_SR_ALM; @@ -598,7 +603,7 @@ isl1208_rtc_interrupt(int irq, void *data) } if (sr & ISL1208_REG_SR_EVT) { - sysfs_notify(&rtc->dev.kobj, NULL, + sysfs_notify(&isl1208->rtc->dev.kobj, NULL, dev_attr_timestamp0.attr.name); dev_warn(&client->dev, "event detected"); handled = 1; @@ -723,7 +728,7 @@ static int isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; - struct rtc_device *rtc; + struct isl1208_state *isl1208; int evdet_irq = -1; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) @@ -732,13 +737,17 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (isl1208_i2c_validate_client(client) < 0) return -ENODEV; - rtc = devm_rtc_allocate_device(&client->dev); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); + /* Allocate driver state, point i2c client data to it */ + isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL); + if (!isl1208) + return -ENOMEM; + i2c_set_clientdata(client, isl1208); - rtc->ops = &isl1208_rtc_ops; + isl1208->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(isl1208->rtc)) + return PTR_ERR(isl1208->rtc); - i2c_set_clientdata(client, rtc); + isl1208->rtc->ops = &isl1208_rtc_ops; rc = isl1208_i2c_get_sr(client); if (rc < 0) { @@ -771,13 +780,13 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_err(&client->dev, "could not enable tamper detection\n"); return rc; } - rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files); + rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); if (rc) return rc; evdet_irq = of_irq_get_byname(np, "evdet"); } - rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files); + rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); if (rc) return rc; @@ -791,7 +800,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rc) return rc; - return rtc_register_device(rtc); + return rtc_register_device(isl1208->rtc); } static const struct i2c_device_id isl1208_id[] = { From 5909b87db8ef0df4e11363afc53ed788b7ea17cb Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:04 +0000 Subject: [PATCH 1893/4277] rtc: isl1208: Support more chip variations Add more support in the driver for dealing with differences in is1208 compatible chips. Put the 1208, 1209, 1218, and 1219 in the list and encode information about nvram size, tamper, and timestamp features. This adds support for the isl1209, which has a tamper detect but no timestamp feature. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 77 ++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index d8e0670e12fc..036e5fc148d4 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -73,15 +74,49 @@ static struct i2c_driver isl1208_driver; /* ISL1208 various variants */ -enum { +enum isl1208_id { TYPE_ISL1208 = 0, + TYPE_ISL1209, TYPE_ISL1218, TYPE_ISL1219, + ISL_LAST_ID }; +/* Chip capabilities table */ +static const struct isl1208_config { + const char name[8]; + unsigned int nvmem_length; + unsigned has_tamper:1; + unsigned has_timestamp:1; +} isl1208_configs[] = { + [TYPE_ISL1208] = { "isl1208", 2, false, false }, + [TYPE_ISL1209] = { "isl1209", 2, true, false }, + [TYPE_ISL1218] = { "isl1218", 8, false, false }, + [TYPE_ISL1219] = { "isl1219", 2, true, true }, +}; + +static const struct i2c_device_id isl1208_id[] = { + { "isl1208", TYPE_ISL1208 }, + { "isl1209", TYPE_ISL1209 }, + { "isl1218", TYPE_ISL1218 }, + { "isl1219", TYPE_ISL1219 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl1208_id); + +static const struct of_device_id isl1208_of_match[] = { + { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] }, + { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] }, + { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] }, + { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] }, + { } +}; +MODULE_DEVICE_TABLE(of, isl1208_of_match); + /* Device state */ struct isl1208_state { struct rtc_device *rtc; + const struct isl1208_config *config; }; /* block read */ @@ -602,11 +637,12 @@ isl1208_rtc_interrupt(int irq, void *data) return err; } - if (sr & ISL1208_REG_SR_EVT) { - sysfs_notify(&isl1208->rtc->dev.kobj, NULL, - dev_attr_timestamp0.attr.name); + if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) { dev_warn(&client->dev, "event detected"); handled = 1; + if (isl1208->config->has_timestamp) + sysfs_notify(&isl1208->rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); } return handled ? IRQ_HANDLED : IRQ_NONE; @@ -743,6 +779,17 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENOMEM; i2c_set_clientdata(client, isl1208); + /* Determine which chip we have */ + if (client->dev.of_node) { + isl1208->config = of_device_get_match_data(&client->dev); + if (!isl1208->config) + return -ENODEV; + } else { + if (id->driver_data >= ISL_LAST_ID) + return -ENODEV; + isl1208->config = &isl1208_configs[id->driver_data]; + } + isl1208->rtc = devm_rtc_allocate_device(&client->dev); if (IS_ERR(isl1208->rtc)) return PTR_ERR(isl1208->rtc); @@ -759,7 +806,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_warn(&client->dev, "rtc power failure detected, " "please set clock.\n"); - if (id->driver_data == TYPE_ISL1219) { + if (isl1208->config->has_tamper) { struct device_node *np = client->dev.of_node; u32 evienb; @@ -780,10 +827,12 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_err(&client->dev, "could not enable tamper detection\n"); return rc; } + evdet_irq = of_irq_get_byname(np, "evdet"); + } + if (isl1208->config->has_timestamp) { rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); if (rc) return rc; - evdet_irq = of_irq_get_byname(np, "evdet"); } rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); @@ -803,22 +852,6 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) return rtc_register_device(isl1208->rtc); } -static const struct i2c_device_id isl1208_id[] = { - { "isl1208", TYPE_ISL1208 }, - { "isl1218", TYPE_ISL1218 }, - { "isl1219", TYPE_ISL1219 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, isl1208_id); - -static const struct of_device_id isl1208_of_match[] = { - { .compatible = "isil,isl1208" }, - { .compatible = "isil,isl1218" }, - { .compatible = "isil,isl1219" }, - { } -}; -MODULE_DEVICE_TABLE(of, isl1208_of_match); - static struct i2c_driver isl1208_driver = { .driver = { .name = "rtc-isl1208", From c3544f6f51edcac4040dd78b9e9d2ece48e2ac0b Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:05 +0000 Subject: [PATCH 1894/4277] rtc: isl1208: Add new style nvmem support to driver Add support for the RTC's NVRAM using the standard nvmem support that is part of the Linux RTC framework. This driver already has a sysfs attribute that provides access to the RTC's NVRAM as a single 16-bit value. Some chips have more than two bytes of NVRAM, so this will not work for them. It's also non-standard. This sysfs attribute is left in for backward compatibility, but will only be able to read the first two bytes of NVRAM. The nvmem interface will allow access to all NVRAM, e.g. eight bytes on the isl1218. Cc: Alessandro Zummo Cc: Alexandre Belloni Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 036e5fc148d4..471e395b20db 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -115,6 +115,7 @@ MODULE_DEVICE_TABLE(of, isl1208_of_match); /* Device state */ struct isl1208_state { + struct nvmem_config nvmem_config; struct rtc_device *rtc; const struct isl1208_config *config; }; @@ -742,6 +743,46 @@ static const struct attribute_group isl1219_rtc_sysfs_files = { .attrs = isl1219_rtc_attrs, }; +static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct isl1208_state *isl1208 = priv; + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); + int ret; + + /* nvmem sanitizes offset/count for us, but count==0 is possible */ + if (!count) + return count; + ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf, + count); + return ret == 0 ? count : ret; +} + +static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct isl1208_state *isl1208 = priv; + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); + int ret; + + /* nvmem sanitizes off/count for us, but count==0 is possible */ + if (!count) + return count; + ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf, + count); + + return ret == 0 ? count : ret; +} + +static const struct nvmem_config isl1208_nvmem_config = { + .name = "isl1208_nvram", + .word_size = 1, + .stride = 1, + /* .size from chip specific config */ + .reg_read = isl1208_nvmem_read, + .reg_write = isl1208_nvmem_write, +}; + static int isl1208_setup_irq(struct i2c_client *client, int irq) { int rc = devm_request_threaded_irq(&client->dev, irq, NULL, @@ -796,6 +837,11 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) isl1208->rtc->ops = &isl1208_rtc_ops; + /* Setup nvmem configuration in driver state struct */ + isl1208->nvmem_config = isl1208_nvmem_config; + isl1208->nvmem_config.size = isl1208->config->nvmem_length; + isl1208->nvmem_config.priv = isl1208; + rc = isl1208_i2c_get_sr(client); if (rc < 0) { dev_err(&client->dev, "reading status failed\n"); @@ -849,6 +895,10 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rc) return rc; + rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config); + if (rc) + return rc; + return rtc_register_device(isl1208->rtc); } From 10e7122107bd426a6ac33bcb17392978a72ca13b Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 12 Feb 2019 02:34:05 +0000 Subject: [PATCH 1895/4277] dt-bindings: rtc: Update for new chip in isl1208 series Since this documents multiple chips controlled by the isl1208 driver, name it isil,isl1208.txt, as the convention is to use the base driver name in the bindings document for drivers with multiple devices. Include all chips supported by this driver in the docs. Make it clear which properties apply to which chips. Expand documentation for existing isil,evienb property to explain operation in more detail. Existing docs just describe it in terms of setting a bit in a register. CC: Alessandro Zummo CC: Alexandre Belloni CC: Rob Herring CC: Mark Rutland CC: devicetree@vger.kernel.org Signed-off-by: Trent Piepho Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/isil,isl1208.txt | 38 +++++++++++++++++++ .../devicetree/bindings/rtc/isil,isl1219.txt | 29 -------------- 2 files changed, 38 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/rtc/isil,isl1208.txt delete mode 100644 Documentation/devicetree/bindings/rtc/isil,isl1219.txt diff --git a/Documentation/devicetree/bindings/rtc/isil,isl1208.txt b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt new file mode 100644 index 000000000000..51f003006f04 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/isil,isl1208.txt @@ -0,0 +1,38 @@ +Intersil ISL1209/19 I2C RTC/Alarm chip with event in + +ISL12X9 have additional pins EVIN and #EVDET for tamper detection, while the +ISL1208 and ISL1218 do not. They are all use the same driver with the bindings +described here, with chip specific properties as noted. + +Required properties supported by the device: + - "compatible": Should be one of the following: + - "isil,isl1208" + - "isil,isl1209" + - "isil,isl1218" + - "isil,isl1219" + - "reg": I2C bus address of the device + +Optional properties: + - "interrupt-names": list which may contains "irq" and "evdet" + evdet applies to isl1209 and isl1219 only + - "interrupts": list of interrupts for "irq" and "evdet" + evdet applies to isl1209 and isl1219 only + - "isil,ev-evienb": Enable or disable internal pull on EVIN pin + Applies to isl1209 and isl1219 only + Possible values are 0 and 1 + Value 0 enables internal pull-up on evin pin, 1 disables it. + Default will leave the non-volatile configuration of the pullup + as is. + +Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12 and #EVDET pin +connected to SoC gpio2 pin 24 and internal pull-up enabled in EVIN pin. + + isl1219: rtc@68 { + compatible = "isil,isl1219"; + reg = <0x68>; + interrupt-names = "irq", "evdet"; + interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>, + <&gpio2 24 IRQ_TYPE_EDGE_FALLING>; + isil,ev-evienb = <1>; + }; + diff --git a/Documentation/devicetree/bindings/rtc/isil,isl1219.txt b/Documentation/devicetree/bindings/rtc/isil,isl1219.txt deleted file mode 100644 index c3efd48e91c2..000000000000 --- a/Documentation/devicetree/bindings/rtc/isil,isl1219.txt +++ /dev/null @@ -1,29 +0,0 @@ -Intersil ISL1219 I2C RTC/Alarm chip with event in - -ISL1219 has additional pins EVIN and #EVDET for tamper detection. - -Required properties supported by the device: - - - "compatible": must be "isil,isl1219" - - "reg": I2C bus address of the device - -Optional properties: - - - "interrupt-names": list which may contains "irq" and "evdet" - - "interrupts": list of interrupts for "irq" and "evdet" - - "isil,ev-evienb": if present EV.EVIENB bit is set to the specified - value for proper operation. - - -Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12 - and #EVDET pin connected to SoC gpio2 pin 24: - - isl1219: rtc@68 { - compatible = "isil,isl1219"; - reg = <0x68>; - interrupt-names = "irq", "evdet"; - interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>, - <&gpio2 24 IRQ_TYPE_EDGE_FALLING>; - isil,ev-evienb = <1>; - }; - From e6e7376cfd7b3f9b63de3a22792f64d9bfb2ab53 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 13 Feb 2019 00:21:36 +0100 Subject: [PATCH 1896/4277] rtc: rv3028: add new driver Add a driver for the MicroCrystal RV-3028. It is a SMT Real-Time Clock Module that incorporates an integrated CMOS circuit together with an XTAL. It has an i2c interface. The driver handles date/time, alarms, trickle charging, timestamping, frequency offset correction, EEPROM and NVRAM. Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + drivers/rtc/Kconfig | 9 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rv3028.c | 732 ++++++++++++++++++ 4 files changed, 743 insertions(+) create mode 100644 drivers/rtc/rtc-rv3028.c diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index d86e2850fe1c..3e6a215e7304 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -52,6 +52,7 @@ emmicro,em3027 EM Microelectronic EM3027 Real-time Clock isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM isil,isl1218 Intersil ISL1218 Low Power RTC with Battery Backed SRAM isil,isl12022 Intersil ISL12022 Real-time Clock +microcrystal,rv3028 Real Time Clock Module with I2C-Bus microcrystal,rv3029 Real Time Clock Module with I2C-Bus microcrystal,rv8523 Real Time Clock nxp,pcf2127 Real-time clock diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 7d61bb382cf3..a08785ac8903 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -626,6 +626,15 @@ config RTC_DRV_EM3027 This driver can also be built as a module. If so, the module will be called rtc-em3027. +config RTC_DRV_RV3028 + tristate "Micro Crystal RV3028" + help + If you say yes here you get support for the Micro Crystal + RV3028. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3028. + config RTC_DRV_RV8803 tristate "Micro Crystal RV8803, Epson RX8900" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index b994323942bd..6dc1e561f533 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -139,6 +139,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o +obj-$(CONFIG_RTC_DRV_RV3028) += rtc-rv3028.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c new file mode 100644 index 000000000000..06884ebb7a61 --- /dev/null +++ b/drivers/rtc/rtc-rv3028.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the Micro Crystal RV3028 + * + * Copyright (C) 2019 Micro Crystal SA + * + * Alexandre Belloni + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RV3028_SEC 0x00 +#define RV3028_MIN 0x01 +#define RV3028_HOUR 0x02 +#define RV3028_WDAY 0x03 +#define RV3028_DAY 0x04 +#define RV3028_MONTH 0x05 +#define RV3028_YEAR 0x06 +#define RV3028_ALARM_MIN 0x07 +#define RV3028_ALARM_HOUR 0x08 +#define RV3028_ALARM_DAY 0x09 +#define RV3028_STATUS 0x0E +#define RV3028_CTRL1 0x0F +#define RV3028_CTRL2 0x10 +#define RV3028_EVT_CTRL 0x13 +#define RV3028_TS_COUNT 0x14 +#define RV3028_TS_SEC 0x15 +#define RV3028_RAM1 0x1F +#define RV3028_EEPROM_ADDR 0x25 +#define RV3028_EEPROM_DATA 0x26 +#define RV3028_EEPROM_CMD 0x27 +#define RV3028_CLKOUT 0x35 +#define RV3028_OFFSET 0x36 +#define RV3028_BACKUP 0x37 + +#define RV3028_STATUS_PORF BIT(0) +#define RV3028_STATUS_EVF BIT(1) +#define RV3028_STATUS_AF BIT(2) +#define RV3028_STATUS_TF BIT(3) +#define RV3028_STATUS_UF BIT(4) +#define RV3028_STATUS_BSF BIT(5) +#define RV3028_STATUS_CLKF BIT(6) +#define RV3028_STATUS_EEBUSY BIT(7) + +#define RV3028_CTRL1_EERD BIT(3) +#define RV3028_CTRL1_WADA BIT(5) + +#define RV3028_CTRL2_RESET BIT(0) +#define RV3028_CTRL2_12_24 BIT(1) +#define RV3028_CTRL2_EIE BIT(2) +#define RV3028_CTRL2_AIE BIT(3) +#define RV3028_CTRL2_TIE BIT(4) +#define RV3028_CTRL2_UIE BIT(5) +#define RV3028_CTRL2_TSE BIT(7) + +#define RV3028_EVT_CTRL_TSR BIT(2) + +#define RV3028_EEPROM_CMD_WRITE 0x21 +#define RV3028_EEPROM_CMD_READ 0x22 + +#define RV3028_EEBUSY_POLL 10000 +#define RV3028_EEBUSY_TIMEOUT 100000 + +#define RV3028_BACKUP_TCE BIT(5) +#define RV3028_BACKUP_TCR_MASK GENMASK(1,0) + +#define OFFSET_STEP_PPT 953674 + +enum rv3028_type { + rv_3028, +}; + +struct rv3028_data { + struct regmap *regmap; + struct rtc_device *rtc; + enum rv3028_type type; +}; + +static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000}; + +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + + regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR, + RV3028_EVT_CTRL_TSR); + + return count; +}; + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + struct rtc_time tm; + int ret, count; + u8 date[6]; + + ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); + if (ret) + return ret; + + if (!count) + return 0; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date, + sizeof(date)); + if (ret) + return ret; + + tm.tm_sec = bcd2bin(date[0]); + tm.tm_min = bcd2bin(date[1]); + tm.tm_hour = bcd2bin(date[2]); + tm.tm_mday = bcd2bin(date[3]); + tm.tm_mon = bcd2bin(date[4]) - 1; + tm.tm_year = bcd2bin(date[5]) + 100; + + ret = rtc_valid_tm(&tm); + if (ret) + return ret; + + return sprintf(buf, "%llu\n", + (unsigned long long)rtc_tm_to_time64(&tm)); +}; + +static DEVICE_ATTR_RW(timestamp0); + +static ssize_t timestamp0_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + int ret, count; + + ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); + if (ret) + return ret; + + return sprintf(buf, "%u\n", count); +}; + +static DEVICE_ATTR_RO(timestamp0_count); + +static struct attribute *rv3028_attrs[] = { + &dev_attr_timestamp0.attr, + &dev_attr_timestamp0_count.attr, + NULL +}; + +static const struct attribute_group rv3028_attr_group = { + .attrs = rv3028_attrs, +}; + +static irqreturn_t rv3028_handle_irq(int irq, void *dev_id) +{ + struct rv3028_data *rv3028 = dev_id; + unsigned long events = 0; + u32 status = 0, ctrl = 0; + + if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 || + status == 0) { + return IRQ_NONE; + } + + if (status & RV3028_STATUS_PORF) + dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n"); + + if (status & RV3028_STATUS_TF) { + status |= RV3028_STATUS_TF; + ctrl |= RV3028_CTRL2_TIE; + events |= RTC_PF; + } + + if (status & RV3028_STATUS_AF) { + status |= RV3028_STATUS_AF; + ctrl |= RV3028_CTRL2_AIE; + events |= RTC_AF; + } + + if (status & RV3028_STATUS_UF) { + status |= RV3028_STATUS_UF; + ctrl |= RV3028_CTRL2_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv3028->rtc, 1, events); + regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0); + regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0); + } + + if (status & RV3028_STATUS_EVF) { + sysfs_notify(&rv3028->rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); + dev_warn(&rv3028->rtc->dev, "event detected"); + } + + return IRQ_HANDLED; +} + +static int rv3028_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 date[7]; + int ret, status; + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[RV3028_SEC] & 0x7f); + tm->tm_min = bcd2bin(date[RV3028_MIN] & 0x7f); + tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f); + tm->tm_wday = ilog2(date[RV3028_WDAY] & 0x7f); + tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f); + tm->tm_mon = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100; + + return 0; +} + +static int rv3028_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + date[RV3028_SEC] = bin2bcd(tm->tm_sec); + date[RV3028_MIN] = bin2bcd(tm->tm_min); + date[RV3028_HOUR] = bin2bcd(tm->tm_hour); + date[RV3028_WDAY] = 1 << (tm->tm_wday); + date[RV3028_DAY] = bin2bcd(tm->tm_mday); + date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1); + date[RV3028_YEAR] = bin2bcd(tm->tm_year - 100); + + /* + * Writing to the Seconds register has the same effect as setting RESET + * bit to 1 + */ + ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date, + sizeof(date)); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_PORF, 0); + + return ret; +} + +static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int status, ctrl, ret; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl); + if (ret < 0) + return ret; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); + + alrm->enabled = !!(ctrl & RV3028_CTRL2_AIE); + alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled; + + return 0; +} + +static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl = 0; + int ret; + + /* The alarm has no seconds, round up to nearest minute */ + if (alrm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&alrm->time); + + alarm_time += 60 - alrm->time.tm_sec; + rtc_time64_to_tm(alarm_time, &alrm->time); + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0); + if (ret) + return ret; + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_AF, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + if (alrm->enabled) { + if (rv3028->rtc->uie_rtctimer.enabled) + ctrl |= RV3028_CTRL2_UIE; + if (rv3028->rtc->aie_timer.enabled) + ctrl |= RV3028_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); + + return ret; +} + +static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ctrl = 0, ret; + + if (enabled) { + if (rv3028->rtc->uie_rtctimer.enabled) + ctrl |= RV3028_CTRL2_UIE; + if (rv3028->rtc->aie_timer.enabled) + ctrl |= RV3028_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_AF | RV3028_STATUS_UF, 0); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); + if (ret) + return ret; + + return 0; +} + +static int rv3028_read_offset(struct device *dev, long *offset) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ret, value, steps; + + ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value); + if (ret < 0) + return ret; + + steps = sign_extend32(value << 1, 8); + + ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value); + if (ret < 0) + return ret; + + steps += value >> 7; + + *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000); + + return 0; +} + +static int rv3028_set_offset(struct device *dev, long offset) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ret; + + offset = clamp(offset, -244141L, 243187L) * 1000; + offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT); + + ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1); + if (ret < 0) + return ret; + + return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7), + offset << 7); +} + +static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int status, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) + dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n"); + + status &= RV3028_STATUS_PORF; + + if (copy_to_user((void __user *)arg, &status, sizeof(int))) + return -EFAULT; + + return 0; + + case RTC_VL_CLR: + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_PORF, 0); + + return ret; + + default: + return -ENOIOCTLCMD; + } +} + +static int rv3028_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes); +} + +static int rv3028_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes); +} + +static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + u32 status, ctrl1; + int i, ret, err; + u8 *buf = val; + + ret = regmap_read(priv, RV3028_CTRL1, &ctrl1); + if (ret) + return ret; + + if (!(ctrl1 & RV3028_CTRL1_EERD)) { + ret = regmap_update_bits(priv, RV3028_CTRL1, + RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + + for (i = 0; i < bytes; i++) { + ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, + RV3028_EEPROM_CMD_WRITE); + if (ret) + goto restore_eerd; + + usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + +restore_eerd: + if (!(ctrl1 & RV3028_CTRL1_EERD)) + { + err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD, + 0); + if (err && !ret) + ret = err; + } + + return ret; +} + +static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + u32 status, ctrl1, data; + int i, ret, err; + u8 *buf = val; + + ret = regmap_read(priv, RV3028_CTRL1, &ctrl1); + if (ret) + return ret; + + if (!(ctrl1 & RV3028_CTRL1_EERD)) { + ret = regmap_update_bits(priv, RV3028_CTRL1, + RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + + for (i = 0; i < bytes; i++) { + ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto restore_eerd; + + ret = regmap_write(priv, RV3028_EEPROM_CMD, + RV3028_EEPROM_CMD_READ); + if (ret) + goto restore_eerd; + + ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + + ret = regmap_read(priv, RV3028_EEPROM_DATA, &data); + if (ret) + goto restore_eerd; + buf[i] = data; + } + +restore_eerd: + if (!(ctrl1 & RV3028_CTRL1_EERD)) + { + err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD, + 0); + if (err && !ret) + ret = err; + } + + return ret; +} + +static struct rtc_class_ops rv3028_rtc_ops = { + .read_time = rv3028_get_time, + .set_time = rv3028_set_time, + .read_offset = rv3028_read_offset, + .set_offset = rv3028_set_offset, + .ioctl = rv3028_ioctl, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x37, +}; + +static int rv3028_probe(struct i2c_client *client) +{ + struct rv3028_data *rv3028; + int ret, status; + u32 ohms; + struct nvmem_config nvmem_cfg = { + .name = "rv3028_nvram", + .word_size = 1, + .stride = 1, + .size = 2, + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = rv3028_nvram_read, + .reg_write = rv3028_nvram_write, + }; + struct nvmem_config eeprom_cfg = { + .name = "rv3028_eeprom", + .word_size = 1, + .stride = 1, + .size = 43, + .type = NVMEM_TYPE_EEPROM, + .reg_read = rv3028_eeprom_read, + .reg_write = rv3028_eeprom_write, + }; + + rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data), + GFP_KERNEL); + if (!rv3028) + return -ENOMEM; + + rv3028->regmap = devm_regmap_init_i2c(client, ®map_config); + + i2c_set_clientdata(client, rv3028); + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (status & RV3028_STATUS_AF) + dev_warn(&client->dev, "An alarm may have been missed.\n"); + + rv3028->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv3028->rtc)) { + return PTR_ERR(rv3028->rtc); + } + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv3028_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rv3028", rv3028); + if (ret) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } else { + rv3028_rtc_ops.read_alarm = rv3028_get_alarm; + rv3028_rtc_ops.set_alarm = rv3028_set_alarm; + rv3028_rtc_ops.alarm_irq_enable = rv3028_alarm_irq_enable; + } + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1, + RV3028_CTRL1_WADA, RV3028_CTRL1_WADA); + if (ret) + return ret; + + /* setup timestamping */ + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_EIE | RV3028_CTRL2_TSE, + RV3028_CTRL2_EIE | RV3028_CTRL2_TSE); + if (ret) + return ret; + + /* setup trickle charger */ + if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms", + &ohms)) { + int i; + + for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++) + if (ohms == rv3028_trickle_resistors[i]) + break; + + if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { + ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, + RV3028_BACKUP_TCE | + RV3028_BACKUP_TCR_MASK, + RV3028_BACKUP_TCE | i); + if (ret) + return ret; + } else { + dev_warn(&client->dev, "invalid trickle resistor value\n"); + } + } + + ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group); + if (ret) + return ret; + + rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099; + rv3028->rtc->ops = &rv3028_rtc_ops; + ret = rtc_register_device(rv3028->rtc); + if (ret) + return ret; + + nvmem_cfg.priv = rv3028->regmap; + rtc_nvmem_register(rv3028->rtc, &nvmem_cfg); + eeprom_cfg.priv = rv3028->regmap; + rtc_nvmem_register(rv3028->rtc, &eeprom_cfg); + + rv3028->rtc->max_user_freq = 1; + + return 0; +} + +static const struct of_device_id rv3028_of_match[] = { + { .compatible = "microcrystal,rv3028", }, + { } +}; +MODULE_DEVICE_TABLE(of, rv3028_of_match); + +static struct i2c_driver rv3028_driver = { + .driver = { + .name = "rtc-rv3028", + .of_match_table = of_match_ptr(rv3028_of_match), + }, + .probe_new = rv3028_probe, +}; +module_i2c_driver(rv3028_driver); + +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver"); +MODULE_LICENSE("GPL v2"); From 3cbb7b638fd1b1adaf35911a8aa812290727df4f Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 13 Feb 2019 14:38:33 +0100 Subject: [PATCH 1897/4277] rtc: hid-sensor-time: stop selecting IIO Make RTC_DRV_HID_SENSOR_TIME depend on IIO and HID_SENSOR_HUB to remove possible circular dependencies as this is the only symbol selecting those. Suggested-by: Florian Lohoff Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a08785ac8903..5d155ce99517 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1835,8 +1835,7 @@ comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME tristate "HID Sensor Time" depends on USB_HID - select IIO - select HID_SENSOR_HUB + depends on HID_SENSOR_HUB && IIO select HID_SENSOR_IIO_COMMON help Say yes here to build support for the HID Sensors of type Time. From 243b44e157598b34cbc62f925cb4b8f725e69b68 Mon Sep 17 00:00:00 2001 From: Artem Panfilov Date: Sun, 17 Feb 2019 21:53:21 +0300 Subject: [PATCH 1898/4277] dt-bindings: rtc: add ABEOZ9 Add ABEOZ9 to the list of trivial RTCs Signed-off-by: Artem Panfilov Reviewed-by: Rob Herring Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/rtc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/rtc.txt b/Documentation/devicetree/bindings/rtc/rtc.txt index 3e6a215e7304..fc79d7133218 100644 --- a/Documentation/devicetree/bindings/rtc/rtc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc.txt @@ -43,6 +43,7 @@ possibly an interrupt line. Compatible Vendor / Chip ========== ============= abracon,abb5zes3 AB-RTCMC-32.768kHz-B5ZE-S3: Real Time Clock/Calendar Module with I2C Interface +abracon,abeoz9 AB-RTCMC-32.768kHz-EOZ9: Real Time Clock/Calendar Module with I2C Interface dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output dallas,ds1672 Dallas DS1672 Real-time Clock dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM From 0e7d6f94016407fd7e1ae472e254d64d4454e9c8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 16 Feb 2019 14:46:27 +0100 Subject: [PATCH 1899/4277] gpio: of: Apply regulator-gpio quirk only to enable-gpios Since commit d6cd33ad7102 ("regulator: gpio: Convert to use descriptors") the GPIO regulator had inverted the polarity of the control GPIO. This problem manifested itself on systems with DT containing the following description (snippet from salvator-common.dtsi): gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>; gpios-states = <1>; states = <3300000 1 1800000 0>; Prior to the aforementioned commit, the gpio-regulator code used gpio_request_array() to claim the GPIO(s) specified in the "gpios" DT node, while the commit changed that to devm_gpiod_get_index(). The legacy gpio_request_array() calls gpio_request_one() and then gpiod_request(), which parses the DT flags of the "gpios" node and populates the GPIO descriptor flags field accordingly. The new devm_gpiod_get_index() calls gpiod_get_index(), then of_find_gpio(), of_get_named_gpiod_flags() with flags != NULL, and then of_gpio_flags_quirks(). Since commit a603a2b8d86e ("gpio: of: Add special quirk to parse regulator flags"), of_gpio_flags_quirks() contains a quirk for regulator-gpio which was never triggered by the legacy gpio_request_array() code path, but is triggered by devm_gpiod_get_index() code path. This quirk checks whether a GPIO is associated with a fixed or gpio-regulator and if so, checks two additional conditions. First, whether such GPIO is active-low, and if so, ignores the active-low flag. Second, whether the regulator DT node does have an "enable-active-high" property and if the property is NOT present, sets the GPIO flags as active-low. The second check triggers a problem, since it is applied to all GPIOs associated with a gpio-regulator, rather than only on the "enable" GPIOs, as the old code did. This changes the way the gpio-regulator interprets the DT description of the control GPIOs. The old code using gpio_request_array() explicitly parsed the "enable-active-high" DT property and only applied it to the GPIOs described in the "enable-gpios" DT node, and only if those were present. This patch fixes the quirk code by only applying the quirk to "enable-gpios", thus restoring the old behavior. Signed-off-by: Marek Vasut Cc: Geert Uytterhoeven Cc: Jan Kotas Cc: Linus Walleij Cc: Mark Brown Cc: Wolfram Sang Cc: linux-renesas-soc@vger.kernel.org To: linux-gpio@vger.kernel.org Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 9a8b78477f79..4547dba19956 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -84,6 +84,7 @@ static void of_gpio_flags_quirks(struct device_node *np, * Note that active low is the default. */ if (IS_ENABLED(CONFIG_REGULATOR) && + !strcmp(propname, "enable-gpio") && (of_device_is_compatible(np, "regulator-fixed") || of_device_is_compatible(np, "reg-fixed-voltage") || of_device_is_compatible(np, "regulator-gpio"))) { From 32c8966e904bd12d002a113da442839503ff84be Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Mon, 11 Feb 2019 14:38:04 -0800 Subject: [PATCH 1900/4277] docs: kernel-doc: typo "if ... if" -> "if ... is" "If no *function* if specified" should instead be "If no *function* is specified". Reported-by: Matthew Wilcox Signed-off-by: Frank Rowand Signed-off-by: Jonathan Corbet --- Documentation/doc-guide/kernel-doc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst index 6513c16b7e4f..f96059767c8c 100644 --- a/Documentation/doc-guide/kernel-doc.rst +++ b/Documentation/doc-guide/kernel-doc.rst @@ -490,7 +490,7 @@ doc: *title* functions: *[ function ...]* Include documentation for each *function* in *source*. - If no *function* if specified, the documentation for all functions + If no *function* is specified, the documentation for all functions and types in the *source* will be included. Examples:: From 44a47f0e3ec213a670c0170bb8ee588551ff1549 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 15 Feb 2019 08:29:48 +0100 Subject: [PATCH 1901/4277] sysfs.txt: add note on available attribute macros The common cases of attributes wrappers should probably be using the __ATTR_XXX macros to make code more concise and readable but the current sysfs.txt does not point developers to those convenience macros. Further there is no note in sysfs.txt currently explaining why trying to set a sysfs file to mode 0666 will fail respectively revert to 0664. Signed-off-by: Nicholas Mc Guire Signed-off-by: Jonathan Corbet --- Documentation/filesystems/sysfs.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt index a1426cabcef1..d70c8f112662 100644 --- a/Documentation/filesystems/sysfs.txt +++ b/Documentation/filesystems/sysfs.txt @@ -116,6 +116,27 @@ static struct device_attribute dev_attr_foo = { .store = store_foo, }; +Note as stated in include/linux/kernel.h "OTHER_WRITABLE? Generally +considered a bad idea." so trying to set a sysfs file writable for +everyone will fail reverting to RO mode for "Others". + +For the common cases sysfs.h provides convenience macros to make +defining attributes easier as well as making code more concise and +readable. The above case could be shortened to: + +static struct device_attribute dev_attr_foo = __ATTR_RW(foo); + +the list of helpers available to define your wrapper function is: +__ATTR_RO(name): assumes default name_show and mode 0444 +__ATTR_WO(name): assumes a name_store only and is restricted to mode + 0200 that is root write access only. +__ATTR_RO_MODE(name, mode): fore more restrictive RO access currently + only use case is the EFI System Resource Table + (see drivers/firmware/efi/esrt.c) +__ATTR_RW(name): assumes default name_show, name_store and setting + mode to 0644. +__ATTR_NULL: which sets the name to NULL and is used as end of list + indicator (see: kernel/workqueue.c) Subsystem-Specific Callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 9d87bbae2d60ebbd1f28a4b5c65162051164c775 Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Mon, 11 Feb 2019 16:42:58 +0300 Subject: [PATCH 1902/4277] perf-security: document perf_events/Perf resource control Extend perf-security.rst file with perf_events/Perf resource control section describing RLIMIT_NOFILE and perf_event_mlock_kb settings for performance monitoring user processes. Signed-off-by: Alexey Budankov Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/perf-security.rst | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Documentation/admin-guide/perf-security.rst b/Documentation/admin-guide/perf-security.rst index f73ebfe9bfe2..bac599e3c55f 100644 --- a/Documentation/admin-guide/perf-security.rst +++ b/Documentation/admin-guide/perf-security.rst @@ -84,6 +84,46 @@ governed by perf_event_paranoid [2]_ setting: locking limit is imposed but ignored for unprivileged processes with CAP_IPC_LOCK capability. +perf_events/Perf resource control +--------------------------------- + +Open file descriptors ++++++++++++++++++++++ + +The perf_events system call API [2]_ allocates file descriptors for every configured +PMU event. Open file descriptors are a per-process accountable resource governed +by the RLIMIT_NOFILE [11]_ limit (ulimit -n), which is usually derived from the login +shell process. When configuring Perf collection for a long list of events on a +large server system, this limit can be easily hit preventing required monitoring +configuration. RLIMIT_NOFILE limit can be increased on per-user basis modifying +content of the limits.conf file [12]_ . Ordinarily, a Perf sampling session +(perf record) requires an amount of open perf_event file descriptors that is not +less than the number of monitored events multiplied by the number of monitored CPUs. + +Memory allocation ++++++++++++++++++ + +The amount of memory available to user processes for capturing performance monitoring +data is governed by the perf_event_mlock_kb [2]_ setting. This perf_event specific +resource setting defines overall per-cpu limits of memory allowed for mapping +by the user processes to execute performance monitoring. The setting essentially +extends the RLIMIT_MEMLOCK [11]_ limit, but only for memory regions mapped specifically +for capturing monitored performance events and related data. + +For example, if a machine has eight cores and perf_event_mlock_kb limit is set +to 516 KiB, then a user process is provided with 516 KiB * 8 = 4128 KiB of memory +above the RLIMIT_MEMLOCK limit (ulimit -l) for perf_event mmap buffers. In particular, +this means that, if the user wants to start two or more performance monitoring +processes, the user is required to manually distribute the available 4128 KiB between the +monitoring processes, for example, using the --mmap-pages Perf record mode option. +Otherwise, the first started performance monitoring process allocates all available +4128 KiB and the other processes will fail to proceed due to the lack of memory. + +RLIMIT_MEMLOCK and perf_event_mlock_kb resource constraints are ignored for +processes with the CAP_IPC_LOCK capability. Thus, perf_events/Perf privileged users +can be provided with memory above the constraints for perf_events/Perf performance +monitoring purpose by providing the Perf executable with CAP_IPC_LOCK capability. + Bibliography ------------ @@ -94,4 +134,6 @@ Bibliography .. [5] ``_ .. [6] ``_ .. [7] ``_ +.. [11] ``_ +.. [12] ``_ From 68570ca0b4b5194c7680c5caf9669b6bcbc6153a Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Mon, 11 Feb 2019 16:43:54 +0300 Subject: [PATCH 1903/4277] perf-security: document collected perf_events/Perf data categories Document and categorize system and performance data into groups that can be captured by perf_events/Perf and explicitly indicate the group that can contain process sensitive data. Signed-off-by: Alexey Budankov Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/perf-security.rst | 32 +++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/perf-security.rst b/Documentation/admin-guide/perf-security.rst index bac599e3c55f..8772d44a5912 100644 --- a/Documentation/admin-guide/perf-security.rst +++ b/Documentation/admin-guide/perf-security.rst @@ -11,8 +11,34 @@ impose a considerable risk of leaking sensitive data accessed by monitored processes. The data leakage is possible both in scenarios of direct usage of perf_events system call API [2]_ and over data files generated by Perf tool user mode utility (Perf) [3]_ , [4]_ . The risk depends on the nature of data that -perf_events performance monitoring units (PMU) [2]_ collect and expose for -performance analysis. Having that said perf_events/Perf performance monitoring +perf_events performance monitoring units (PMU) [2]_ and Perf collect and expose +for performance analysis. Collected system and performance data may be split into +several categories: + +1. System hardware and software configuration data, for example: a CPU model and + its cache configuration, an amount of available memory and its topology, used + kernel and Perf versions, performance monitoring setup including experiment + time, events configuration, Perf command line parameters, etc. + +2. User and kernel module paths and their load addresses with sizes, process and + thread names with their PIDs and TIDs, timestamps for captured hardware and + software events. + +3. Content of kernel software counters (e.g., for context switches, page faults, + CPU migrations), architectural hardware performance counters (PMC) [8]_ and + machine specific registers (MSR) [9]_ that provide execution metrics for + various monitored parts of the system (e.g., memory controller (IMC), interconnect + (QPI/UPI) or peripheral (PCIe) uncore counters) without direct attribution to any + execution context state. + +4. Content of architectural execution context registers (e.g., RIP, RSP, RBP on + x86_64), process user and kernel space memory addresses and data, content of + various architectural MSRs that capture data from this category. + +Data that belong to the fourth category can potentially contain sensitive process +data. If PMUs in some monitoring modes capture values of execution context registers +or data from process memory then access to such monitoring capabilities requires +to be ordered and secured properly. So, perf_events/Perf performance monitoring is the subject for security access control management [5]_ . perf_events/Perf access control @@ -134,6 +160,8 @@ Bibliography .. [5] ``_ .. [6] ``_ .. [7] ``_ +.. [8] ``_ +.. [9] ``_ .. [11] ``_ .. [12] ``_ From e152c7b7bf56f4b00262463dcedfd92dd5629b6f Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Mon, 11 Feb 2019 16:44:55 +0300 Subject: [PATCH 1904/4277] perf-security: elaborate on perf_events/Perf privileged users Elaborate on possible perf_event/Perf privileged users groups and document steps about creating such groups. Signed-off-by: Alexey Budankov Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/perf-security.rst | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Documentation/admin-guide/perf-security.rst b/Documentation/admin-guide/perf-security.rst index 8772d44a5912..dccbf2ec0c9e 100644 --- a/Documentation/admin-guide/perf-security.rst +++ b/Documentation/admin-guide/perf-security.rst @@ -73,6 +73,48 @@ enable capturing of additional data required for later performance analysis of monitored processes or a system. For example, CAP_SYSLOG capability permits reading kernel space memory addresses from /proc/kallsyms file. +perf_events/Perf privileged users +--------------------------------- + +Mechanisms of capabilities, privileged capability-dumb files [6]_ and file system +ACLs [10]_ can be used to create a dedicated group of perf_events/Perf privileged +users who are permitted to execute performance monitoring without scope limits. +The following steps can be taken to create such a group of privileged Perf users. + +1. Create perf_users group of privileged Perf users, assign perf_users group to + Perf tool executable and limit access to the executable for other users in the + system who are not in the perf_users group: + +:: + + # groupadd perf_users + # ls -alhF + -rwxr-xr-x 2 root root 11M Oct 19 15:12 perf + # chgrp perf_users perf + # ls -alhF + -rwxr-xr-x 2 root perf_users 11M Oct 19 15:12 perf + # chmod o-rwx perf + # ls -alhF + -rwxr-x--- 2 root perf_users 11M Oct 19 15:12 perf + +2. Assign the required capabilities to the Perf tool executable file and enable + members of perf_users group with performance monitoring privileges [6]_ : + +:: + + # setcap "cap_sys_admin,cap_sys_ptrace,cap_syslog=ep" perf + # setcap -v "cap_sys_admin,cap_sys_ptrace,cap_syslog=ep" perf + perf: OK + # getcap perf + perf = cap_sys_ptrace,cap_sys_admin,cap_syslog+ep + +As a result, members of perf_users group are capable of conducting performance +monitoring by using functionality of the configured Perf tool executable that, +when executes, passes perf_events subsystem scope checks. + +This specific access control management is only available to superuser or root +running processes with CAP_SETPCAP, CAP_SETFCAP [6]_ capabilities. + perf_events/Perf unprivileged users ----------------------------------- @@ -162,6 +204,7 @@ Bibliography .. [7] ``_ .. [8] ``_ .. [9] ``_ +.. [10] ``_ .. [11] ``_ .. [12] ``_ From e85a198e30e9ffbc8e12986c1a8d29e6f0bc0af9 Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Mon, 11 Feb 2019 17:58:24 +0300 Subject: [PATCH 1905/4277] perf-security: wrap paragraphs on 72 columns Implemented formatting of paragraphs to be not wider than 72 columns. Signed-off-by: Alexey Budankov Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/perf-security.rst | 254 +++++++++++--------- 1 file changed, 137 insertions(+), 117 deletions(-) diff --git a/Documentation/admin-guide/perf-security.rst b/Documentation/admin-guide/perf-security.rst index dccbf2ec0c9e..72effa7c23b9 100644 --- a/Documentation/admin-guide/perf-security.rst +++ b/Documentation/admin-guide/perf-security.rst @@ -6,84 +6,94 @@ Perf Events and tool security Overview -------- -Usage of Performance Counters for Linux (perf_events) [1]_ , [2]_ , [3]_ can -impose a considerable risk of leaking sensitive data accessed by monitored -processes. The data leakage is possible both in scenarios of direct usage of -perf_events system call API [2]_ and over data files generated by Perf tool user -mode utility (Perf) [3]_ , [4]_ . The risk depends on the nature of data that -perf_events performance monitoring units (PMU) [2]_ and Perf collect and expose -for performance analysis. Collected system and performance data may be split into -several categories: +Usage of Performance Counters for Linux (perf_events) [1]_ , [2]_ , [3]_ +can impose a considerable risk of leaking sensitive data accessed by +monitored processes. The data leakage is possible both in scenarios of +direct usage of perf_events system call API [2]_ and over data files +generated by Perf tool user mode utility (Perf) [3]_ , [4]_ . The risk +depends on the nature of data that perf_events performance monitoring +units (PMU) [2]_ and Perf collect and expose for performance analysis. +Collected system and performance data may be split into several +categories: -1. System hardware and software configuration data, for example: a CPU model and - its cache configuration, an amount of available memory and its topology, used - kernel and Perf versions, performance monitoring setup including experiment - time, events configuration, Perf command line parameters, etc. +1. System hardware and software configuration data, for example: a CPU + model and its cache configuration, an amount of available memory and + its topology, used kernel and Perf versions, performance monitoring + setup including experiment time, events configuration, Perf command + line parameters, etc. -2. User and kernel module paths and their load addresses with sizes, process and - thread names with their PIDs and TIDs, timestamps for captured hardware and - software events. +2. User and kernel module paths and their load addresses with sizes, + process and thread names with their PIDs and TIDs, timestamps for + captured hardware and software events. -3. Content of kernel software counters (e.g., for context switches, page faults, - CPU migrations), architectural hardware performance counters (PMC) [8]_ and - machine specific registers (MSR) [9]_ that provide execution metrics for - various monitored parts of the system (e.g., memory controller (IMC), interconnect - (QPI/UPI) or peripheral (PCIe) uncore counters) without direct attribution to any - execution context state. +3. Content of kernel software counters (e.g., for context switches, page + faults, CPU migrations), architectural hardware performance counters + (PMC) [8]_ and machine specific registers (MSR) [9]_ that provide + execution metrics for various monitored parts of the system (e.g., + memory controller (IMC), interconnect (QPI/UPI) or peripheral (PCIe) + uncore counters) without direct attribution to any execution context + state. -4. Content of architectural execution context registers (e.g., RIP, RSP, RBP on - x86_64), process user and kernel space memory addresses and data, content of - various architectural MSRs that capture data from this category. +4. Content of architectural execution context registers (e.g., RIP, RSP, + RBP on x86_64), process user and kernel space memory addresses and + data, content of various architectural MSRs that capture data from + this category. -Data that belong to the fourth category can potentially contain sensitive process -data. If PMUs in some monitoring modes capture values of execution context registers -or data from process memory then access to such monitoring capabilities requires -to be ordered and secured properly. So, perf_events/Perf performance monitoring -is the subject for security access control management [5]_ . +Data that belong to the fourth category can potentially contain +sensitive process data. If PMUs in some monitoring modes capture values +of execution context registers or data from process memory then access +to such monitoring capabilities requires to be ordered and secured +properly. So, perf_events/Perf performance monitoring is the subject for +security access control management [5]_ . perf_events/Perf access control ------------------------------- -To perform security checks, the Linux implementation splits processes into two -categories [6]_ : a) privileged processes (whose effective user ID is 0, referred -to as superuser or root), and b) unprivileged processes (whose effective UID is -nonzero). Privileged processes bypass all kernel security permission checks so -perf_events performance monitoring is fully available to privileged processes -without access, scope and resource restrictions. +To perform security checks, the Linux implementation splits processes +into two categories [6]_ : a) privileged processes (whose effective user +ID is 0, referred to as superuser or root), and b) unprivileged +processes (whose effective UID is nonzero). Privileged processes bypass +all kernel security permission checks so perf_events performance +monitoring is fully available to privileged processes without access, +scope and resource restrictions. -Unprivileged processes are subject to a full security permission check based on -the process's credentials [5]_ (usually: effective UID, effective GID, and -supplementary group list). +Unprivileged processes are subject to a full security permission check +based on the process's credentials [5]_ (usually: effective UID, +effective GID, and supplementary group list). -Linux divides the privileges traditionally associated with superuser into -distinct units, known as capabilities [6]_ , which can be independently enabled -and disabled on per-thread basis for processes and files of unprivileged users. +Linux divides the privileges traditionally associated with superuser +into distinct units, known as capabilities [6]_ , which can be +independently enabled and disabled on per-thread basis for processes and +files of unprivileged users. -Unprivileged processes with enabled CAP_SYS_ADMIN capability are treated as -privileged processes with respect to perf_events performance monitoring and -bypass *scope* permissions checks in the kernel. +Unprivileged processes with enabled CAP_SYS_ADMIN capability are treated +as privileged processes with respect to perf_events performance +monitoring and bypass *scope* permissions checks in the kernel. -Unprivileged processes using perf_events system call API is also subject for -PTRACE_MODE_READ_REALCREDS ptrace access mode check [7]_ , whose outcome -determines whether monitoring is permitted. So unprivileged processes provided -with CAP_SYS_PTRACE capability are effectively permitted to pass the check. +Unprivileged processes using perf_events system call API is also subject +for PTRACE_MODE_READ_REALCREDS ptrace access mode check [7]_ , whose +outcome determines whether monitoring is permitted. So unprivileged +processes provided with CAP_SYS_PTRACE capability are effectively +permitted to pass the check. -Other capabilities being granted to unprivileged processes can effectively -enable capturing of additional data required for later performance analysis of -monitored processes or a system. For example, CAP_SYSLOG capability permits -reading kernel space memory addresses from /proc/kallsyms file. +Other capabilities being granted to unprivileged processes can +effectively enable capturing of additional data required for later +performance analysis of monitored processes or a system. For example, +CAP_SYSLOG capability permits reading kernel space memory addresses from +/proc/kallsyms file. perf_events/Perf privileged users --------------------------------- -Mechanisms of capabilities, privileged capability-dumb files [6]_ and file system -ACLs [10]_ can be used to create a dedicated group of perf_events/Perf privileged -users who are permitted to execute performance monitoring without scope limits. -The following steps can be taken to create such a group of privileged Perf users. +Mechanisms of capabilities, privileged capability-dumb files [6]_ and +file system ACLs [10]_ can be used to create a dedicated group of +perf_events/Perf privileged users who are permitted to execute +performance monitoring without scope limits. The following steps can be +taken to create such a group of privileged Perf users. -1. Create perf_users group of privileged Perf users, assign perf_users group to - Perf tool executable and limit access to the executable for other users in the - system who are not in the perf_users group: +1. Create perf_users group of privileged Perf users, assign perf_users + group to Perf tool executable and limit access to the executable for + other users in the system who are not in the perf_users group: :: @@ -97,8 +107,9 @@ The following steps can be taken to create such a group of privileged Perf users # ls -alhF -rwxr-x--- 2 root perf_users 11M Oct 19 15:12 perf -2. Assign the required capabilities to the Perf tool executable file and enable - members of perf_users group with performance monitoring privileges [6]_ : +2. Assign the required capabilities to the Perf tool executable file and + enable members of perf_users group with performance monitoring + privileges [6]_ : :: @@ -108,49 +119,52 @@ The following steps can be taken to create such a group of privileged Perf users # getcap perf perf = cap_sys_ptrace,cap_sys_admin,cap_syslog+ep -As a result, members of perf_users group are capable of conducting performance -monitoring by using functionality of the configured Perf tool executable that, -when executes, passes perf_events subsystem scope checks. +As a result, members of perf_users group are capable of conducting +performance monitoring by using functionality of the configured Perf +tool executable that, when executes, passes perf_events subsystem scope +checks. -This specific access control management is only available to superuser or root -running processes with CAP_SETPCAP, CAP_SETFCAP [6]_ capabilities. +This specific access control management is only available to superuser +or root running processes with CAP_SETPCAP, CAP_SETFCAP [6]_ +capabilities. perf_events/Perf unprivileged users ----------------------------------- -perf_events/Perf *scope* and *access* control for unprivileged processes is -governed by perf_event_paranoid [2]_ setting: +perf_events/Perf *scope* and *access* control for unprivileged processes +is governed by perf_event_paranoid [2]_ setting: -1: - Impose no *scope* and *access* restrictions on using perf_events performance - monitoring. Per-user per-cpu perf_event_mlock_kb [2]_ locking limit is - ignored when allocating memory buffers for storing performance data. - This is the least secure mode since allowed monitored *scope* is - maximized and no perf_events specific limits are imposed on *resources* - allocated for performance monitoring. + Impose no *scope* and *access* restrictions on using perf_events + performance monitoring. Per-user per-cpu perf_event_mlock_kb [2]_ + locking limit is ignored when allocating memory buffers for storing + performance data. This is the least secure mode since allowed + monitored *scope* is maximized and no perf_events specific limits + are imposed on *resources* allocated for performance monitoring. >=0: *scope* includes per-process and system wide performance monitoring - but excludes raw tracepoints and ftrace function tracepoints monitoring. - CPU and system events happened when executing either in user or - in kernel space can be monitored and captured for later analysis. - Per-user per-cpu perf_event_mlock_kb locking limit is imposed but - ignored for unprivileged processes with CAP_IPC_LOCK [6]_ capability. + but excludes raw tracepoints and ftrace function tracepoints + monitoring. CPU and system events happened when executing either in + user or in kernel space can be monitored and captured for later + analysis. Per-user per-cpu perf_event_mlock_kb locking limit is + imposed but ignored for unprivileged processes with CAP_IPC_LOCK + [6]_ capability. >=1: - *scope* includes per-process performance monitoring only and excludes - system wide performance monitoring. CPU and system events happened when - executing either in user or in kernel space can be monitored and - captured for later analysis. Per-user per-cpu perf_event_mlock_kb - locking limit is imposed but ignored for unprivileged processes with - CAP_IPC_LOCK capability. + *scope* includes per-process performance monitoring only and + excludes system wide performance monitoring. CPU and system events + happened when executing either in user or in kernel space can be + monitored and captured for later analysis. Per-user per-cpu + perf_event_mlock_kb locking limit is imposed but ignored for + unprivileged processes with CAP_IPC_LOCK capability. >=2: - *scope* includes per-process performance monitoring only. CPU and system - events happened when executing in user space only can be monitored and - captured for later analysis. Per-user per-cpu perf_event_mlock_kb - locking limit is imposed but ignored for unprivileged processes with - CAP_IPC_LOCK capability. + *scope* includes per-process performance monitoring only. CPU and + system events happened when executing in user space only can be + monitored and captured for later analysis. Per-user per-cpu + perf_event_mlock_kb locking limit is imposed but ignored for + unprivileged processes with CAP_IPC_LOCK capability. perf_events/Perf resource control --------------------------------- @@ -158,39 +172,45 @@ perf_events/Perf resource control Open file descriptors +++++++++++++++++++++ -The perf_events system call API [2]_ allocates file descriptors for every configured -PMU event. Open file descriptors are a per-process accountable resource governed -by the RLIMIT_NOFILE [11]_ limit (ulimit -n), which is usually derived from the login -shell process. When configuring Perf collection for a long list of events on a -large server system, this limit can be easily hit preventing required monitoring -configuration. RLIMIT_NOFILE limit can be increased on per-user basis modifying -content of the limits.conf file [12]_ . Ordinarily, a Perf sampling session -(perf record) requires an amount of open perf_event file descriptors that is not -less than the number of monitored events multiplied by the number of monitored CPUs. +The perf_events system call API [2]_ allocates file descriptors for +every configured PMU event. Open file descriptors are a per-process +accountable resource governed by the RLIMIT_NOFILE [11]_ limit +(ulimit -n), which is usually derived from the login shell process. When +configuring Perf collection for a long list of events on a large server +system, this limit can be easily hit preventing required monitoring +configuration. RLIMIT_NOFILE limit can be increased on per-user basis +modifying content of the limits.conf file [12]_ . Ordinarily, a Perf +sampling session (perf record) requires an amount of open perf_event +file descriptors that is not less than the number of monitored events +multiplied by the number of monitored CPUs. Memory allocation +++++++++++++++++ -The amount of memory available to user processes for capturing performance monitoring -data is governed by the perf_event_mlock_kb [2]_ setting. This perf_event specific -resource setting defines overall per-cpu limits of memory allowed for mapping -by the user processes to execute performance monitoring. The setting essentially -extends the RLIMIT_MEMLOCK [11]_ limit, but only for memory regions mapped specifically -for capturing monitored performance events and related data. +The amount of memory available to user processes for capturing +performance monitoring data is governed by the perf_event_mlock_kb [2]_ +setting. This perf_event specific resource setting defines overall +per-cpu limits of memory allowed for mapping by the user processes to +execute performance monitoring. The setting essentially extends the +RLIMIT_MEMLOCK [11]_ limit, but only for memory regions mapped +specifically for capturing monitored performance events and related data. -For example, if a machine has eight cores and perf_event_mlock_kb limit is set -to 516 KiB, then a user process is provided with 516 KiB * 8 = 4128 KiB of memory -above the RLIMIT_MEMLOCK limit (ulimit -l) for perf_event mmap buffers. In particular, -this means that, if the user wants to start two or more performance monitoring -processes, the user is required to manually distribute the available 4128 KiB between the -monitoring processes, for example, using the --mmap-pages Perf record mode option. -Otherwise, the first started performance monitoring process allocates all available -4128 KiB and the other processes will fail to proceed due to the lack of memory. +For example, if a machine has eight cores and perf_event_mlock_kb limit +is set to 516 KiB, then a user process is provided with 516 KiB * 8 = +4128 KiB of memory above the RLIMIT_MEMLOCK limit (ulimit -l) for +perf_event mmap buffers. In particular, this means that, if the user +wants to start two or more performance monitoring processes, the user is +required to manually distribute the available 4128 KiB between the +monitoring processes, for example, using the --mmap-pages Perf record +mode option. Otherwise, the first started performance monitoring process +allocates all available 4128 KiB and the other processes will fail to +proceed due to the lack of memory. -RLIMIT_MEMLOCK and perf_event_mlock_kb resource constraints are ignored for -processes with the CAP_IPC_LOCK capability. Thus, perf_events/Perf privileged users -can be provided with memory above the constraints for perf_events/Perf performance -monitoring purpose by providing the Perf executable with CAP_IPC_LOCK capability. +RLIMIT_MEMLOCK and perf_event_mlock_kb resource constraints are ignored +for processes with the CAP_IPC_LOCK capability. Thus, perf_events/Perf +privileged users can be provided with memory above the constraints for +perf_events/Perf performance monitoring purpose by providing the Perf +executable with CAP_IPC_LOCK capability. Bibliography ------------ From c9f19b67f04c817ab83e80012fd2435fc516a44d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 12 Feb 2019 14:37:20 -0600 Subject: [PATCH 1906/4277] xen: mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: drivers/xen/xen-pciback/xenbus.c: In function ‘xen_pcibk_frontend_changed’: drivers/xen/xen-pciback/xenbus.c:545:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (xenbus_dev_is_online(xdev)) ^ drivers/xen/xen-pciback/xenbus.c:548:2: note: here case XenbusStateUnknown: ^~~~ Warning level 3 was used: -Wimplicit-fallthrough=3 Notice that, in this particular case, the code comment is modified in accordance with what GCC is expecting to find. This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Juergen Gross --- drivers/xen/xen-pciback/xenbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index 581c4e1a8b82..23f7f6ec7d1f 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -544,7 +544,7 @@ static void xen_pcibk_frontend_changed(struct xenbus_device *xdev, xenbus_switch_state(xdev, XenbusStateClosed); if (xenbus_dev_is_online(xdev)) break; - /* fall through if not online */ + /* fall through - if not online */ case XenbusStateUnknown: dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); device_unregister(&xdev->dev); From efac6c75dc4b4aac56c4a40e7f4d2e54fcd87834 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 12 Feb 2019 14:40:35 -0600 Subject: [PATCH 1907/4277] xen-scsiback: mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: drivers/xen/xen-scsiback.c: In function ‘scsiback_frontend_changed’: drivers/xen/xen-scsiback.c:1185:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (xenbus_dev_is_online(dev)) ^ drivers/xen/xen-scsiback.c:1188:2: note: here case XenbusStateUnknown: ^~~~ Warning level 3 was used: -Wimplicit-fallthrough=3 Notice that, in this particular case, the code comment is modified in accordance with what GCC is expecting to find. This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Juergen Gross --- drivers/xen/xen-scsiback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index c9e23a126218..cf0cb0898ae3 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -1184,7 +1184,7 @@ static void scsiback_frontend_changed(struct xenbus_device *dev, xenbus_switch_state(dev, XenbusStateClosed); if (xenbus_dev_is_online(dev)) break; - /* fall through if not online */ + /* fall through - if not online */ case XenbusStateUnknown: device_unregister(&dev->dev); break; From 7681f31ec9cdacab4fd10570be924f2cef6669ba Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 13 Feb 2019 18:21:31 -0500 Subject: [PATCH 1908/4277] xen/pciback: Don't disable PCI_COMMAND on PCI device reset. There is no need for this at all. Worst it means that if the guest tries to write to BARs it could lead (on certain platforms) to PCI SERR errors. Please note that with af6fc858a35b90e89ea7a7ee58e66628c55c776b "xen-pciback: limit guest control of command register" a guest is still allowed to enable those control bits (safely), but is not allowed to disable them and that therefore a well behaved frontend which enables things before using them will still function correctly. This is done via an write to the configuration register 0x4 which triggers on the backend side: command_write \- pci_enable_device \- pci_enable_device_flags \- do_pci_enable_device \- pcibios_enable_device \-pci_enable_resourcess [which enables the PCI_COMMAND_MEMORY|PCI_COMMAND_IO] However guests (and drivers) which don't do this could cause problems, including the security issues which XSA-120 sought to address. Reported-by: Jan Beulich Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Prarit Bhargava Signed-off-by: Juergen Gross --- drivers/xen/xen-pciback/pciback_ops.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index ea4a08b83fa0..787966f44589 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -127,8 +127,6 @@ void xen_pcibk_reset_device(struct pci_dev *dev) if (pci_is_enabled(dev)) pci_disable_device(dev); - pci_write_config_word(dev, PCI_COMMAND, 0); - dev->is_busmaster = 0; } else { pci_read_config_word(dev, PCI_COMMAND, &cmd); From fa13e665e02874c0a5f4d06d6967ae34a6cb3d6a Mon Sep 17 00:00:00 2001 From: Oleksandr Andrushchenko Date: Thu, 14 Feb 2019 16:23:20 +0200 Subject: [PATCH 1909/4277] xen/gntdev: Do not destroy context while dma-bufs are in use If there are exported DMA buffers which are still in use and grant device is closed by either normal user-space close or by a signal this leads to the grant device context to be destroyed, thus making it not possible to correctly destroy those exported buffers when they are returned back to gntdev and makes the module crash: [ 339.617540] [] dmabuf_exp_ops_release+0x40/0xa8 [ 339.617560] [] dma_buf_release+0x60/0x190 [ 339.617577] [] __fput+0x88/0x1d0 [ 339.617589] [] ____fput+0xc/0x18 [ 339.617607] [] task_work_run+0x9c/0xc0 [ 339.617622] [] do_notify_resume+0xfc/0x108 Fix this by referencing gntdev on each DMA buffer export and unreferencing on buffer release. Signed-off-by: Oleksandr Andrushchenko Reviewed-by: Boris Ostrovsky@oracle.com> Signed-off-by: Juergen Gross --- drivers/xen/gntdev-dmabuf.c | 12 +++++++++++- drivers/xen/gntdev-dmabuf.h | 2 +- drivers/xen/gntdev.c | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index cba6b586bfbd..d97fcfc5e558 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -80,6 +80,12 @@ struct gntdev_dmabuf_priv { struct list_head imp_list; /* This is the lock which protects dma_buf_xxx lists. */ struct mutex lock; + /* + * We reference this file while exporting dma-bufs, so + * the grant device context is not destroyed while there are + * external users alive. + */ + struct file *filp; }; /* DMA buffer export support. */ @@ -311,6 +317,7 @@ static void dmabuf_exp_release(struct kref *kref) dmabuf_exp_wait_obj_signal(gntdev_dmabuf->priv, gntdev_dmabuf); list_del(&gntdev_dmabuf->next); + fput(gntdev_dmabuf->priv->filp); kfree(gntdev_dmabuf); } @@ -423,6 +430,7 @@ static int dmabuf_exp_from_pages(struct gntdev_dmabuf_export_args *args) mutex_lock(&args->dmabuf_priv->lock); list_add(&gntdev_dmabuf->next, &args->dmabuf_priv->exp_list); mutex_unlock(&args->dmabuf_priv->lock); + get_file(gntdev_dmabuf->priv->filp); return 0; fail: @@ -834,7 +842,7 @@ long gntdev_ioctl_dmabuf_imp_release(struct gntdev_priv *priv, return dmabuf_imp_release(priv->dmabuf_priv, op.fd); } -struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void) +struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp) { struct gntdev_dmabuf_priv *priv; @@ -847,6 +855,8 @@ struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void) INIT_LIST_HEAD(&priv->exp_wait_list); INIT_LIST_HEAD(&priv->imp_list); + priv->filp = filp; + return priv; } diff --git a/drivers/xen/gntdev-dmabuf.h b/drivers/xen/gntdev-dmabuf.h index 7220a53d0fc5..3d9b9cf9d5a1 100644 --- a/drivers/xen/gntdev-dmabuf.h +++ b/drivers/xen/gntdev-dmabuf.h @@ -14,7 +14,7 @@ struct gntdev_dmabuf_priv; struct gntdev_priv; -struct gntdev_dmabuf_priv *gntdev_dmabuf_init(void); +struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp); void gntdev_dmabuf_fini(struct gntdev_dmabuf_priv *priv); diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 5efc5eee9544..7cf9c51318aa 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -600,7 +600,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) mutex_init(&priv->lock); #ifdef CONFIG_XEN_GNTDEV_DMABUF - priv->dmabuf_priv = gntdev_dmabuf_init(); + priv->dmabuf_priv = gntdev_dmabuf_init(flip); if (IS_ERR(priv->dmabuf_priv)) { ret = PTR_ERR(priv->dmabuf_priv); kfree(priv); From 068e79f4a9d613f4327cb4062a1e49c0eaca7149 Mon Sep 17 00:00:00 2001 From: Oleksandr Andrushchenko Date: Thu, 14 Feb 2019 16:23:21 +0200 Subject: [PATCH 1910/4277] xen/gntdev: Check and release imported dma-bufs on close Check if there are any imported dma-bufs left not released by user-space when grant device's release callback is called and free those if this is the case. This can happen if user-space leaks the buffers because of a bug or application has been terminated for any reason. Signed-off-by: Oleksandr Andrushchenko Reviewed-by: Boris Ostrovsky@oracle.com> Signed-off-by: Juergen Gross --- drivers/xen/gntdev-dmabuf.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index d97fcfc5e558..2c4f324f8626 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -745,6 +745,14 @@ static int dmabuf_imp_release(struct gntdev_dmabuf_priv *priv, u32 fd) return 0; } +static void dmabuf_imp_release_all(struct gntdev_dmabuf_priv *priv) +{ + struct gntdev_dmabuf *q, *gntdev_dmabuf; + + list_for_each_entry_safe(gntdev_dmabuf, q, &priv->imp_list, next) + dmabuf_imp_release(priv, gntdev_dmabuf->fd); +} + /* DMA buffer IOCTL support. */ long gntdev_ioctl_dmabuf_exp_from_refs(struct gntdev_priv *priv, int use_ptemod, @@ -862,5 +870,6 @@ struct gntdev_dmabuf_priv *gntdev_dmabuf_init(struct file *filp) void gntdev_dmabuf_fini(struct gntdev_dmabuf_priv *priv) { + dmabuf_imp_release_all(priv); kfree(priv); } From 357b4da50a62e2fd70eacee21cdbd22d4c7a7b60 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 14 Feb 2019 11:42:39 +0100 Subject: [PATCH 1911/4277] x86: respect memory size limiting via mem= parameter When limiting memory size via kernel parameter "mem=" this should be respected even in case of memory made accessible via a PCI card. Today this kind of memory won't be made usable in initial memory setup as the memory won't be visible in E820 map, but it might be added when adding PCI devices due to corresponding ACPI table entries. Not respecting "mem=" can be corrected by adding a global max_mem_size variable set by parse_memopt() which will result in rejecting adding memory areas resulting in a memory size above the allowed limit. Signed-off-by: Juergen Gross Acked-by: Ingo Molnar Reviewed-by: William Kucharski Signed-off-by: Juergen Gross --- arch/x86/kernel/e820.c | 5 +++++ include/linux/memory_hotplug.h | 2 ++ mm/memory_hotplug.c | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 50895c2f937d..e67513e2cbbb 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -881,6 +882,10 @@ static int __init parse_memopt(char *p) e820__range_remove(mem_size, ULLONG_MAX - mem_size, E820_TYPE_RAM, 1); +#ifdef CONFIG_MEMORY_HOTPLUG + max_mem_size = mem_size; +#endif + return 0; } early_param("mem", parse_memopt); diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 368267c1b71b..cfd12078172a 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -100,6 +100,8 @@ extern void __online_page_free(struct page *page); extern int try_online_node(int nid); +extern u64 max_mem_size; + extern bool memhp_auto_online; /* If movable_node boot option specified */ extern bool movable_node_enabled; diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 124e794867c5..519f9db063ff 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -96,10 +96,16 @@ void mem_hotplug_done(void) cpus_read_unlock(); } +u64 max_mem_size = U64_MAX; + /* add this memory to iomem resource */ static struct resource *register_memory_resource(u64 start, u64 size) { struct resource *res, *conflict; + + if (start + size > max_mem_size) + return ERR_PTR(-E2BIG); + res = kzalloc(sizeof(struct resource), GFP_KERNEL); if (!res) return ERR_PTR(-ENOMEM); From 1d988ed46543ca36c010634c97ac32114362ddb1 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 14 Feb 2019 11:42:40 +0100 Subject: [PATCH 1912/4277] x86/xen: dont add memory above max allowed allocation Don't allow memory to be added above the allowed maximum allocation limit set by Xen. Trying to do so would result in cases like the following: [ 584.559652] ------------[ cut here ]------------ [ 584.564897] WARNING: CPU: 2 PID: 1 at ../arch/x86/xen/multicalls.c:129 xen_alloc_pte+0x1c7/0x390() [ 584.575151] Modules linked in: [ 584.578643] Supported: Yes [ 584.581750] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 4.4.120-92.70-default #1 [ 584.590000] Hardware name: Cisco Systems Inc UCSC-C460-M4/UCSC-C460-M4, BIOS C460M4.4.0.1b.0.0629181419 06/29/2018 [ 584.601862] 0000000000000000 ffffffff813175a0 0000000000000000 ffffffff8184777c [ 584.610200] ffffffff8107f4e1 ffff880487eb7000 ffff8801862b79c0 ffff88048608d290 [ 584.618537] 0000000000487eb7 ffffea0000000201 ffffffff81009de7 ffffffff81068561 [ 584.626876] Call Trace: [ 584.629699] [] dump_trace+0x59/0x340 [ 584.635645] [] show_stack_log_lvl+0xea/0x170 [ 584.642391] [] show_stack+0x21/0x40 [ 584.648238] [] dump_stack+0x5c/0x7c [ 584.654085] [] warn_slowpath_common+0x81/0xb0 [ 584.660932] [] xen_alloc_pte+0x1c7/0x390 [ 584.667289] [] pmd_populate_kernel.constprop.6+0x40/0x80 [ 584.675241] [] phys_pmd_init+0x210/0x255 [ 584.681587] [] phys_pud_init+0x1da/0x247 [ 584.687931] [] kernel_physical_mapping_init+0xf5/0x1d4 [ 584.695682] [] init_memory_mapping+0x18d/0x380 [ 584.702631] [] arch_add_memory+0x59/0xf0 Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross --- arch/x86/xen/setup.c | 13 +++++++++++++ drivers/xen/xen-balloon.c | 11 +++++++++++ include/xen/xen.h | 4 ++++ 3 files changed, 28 insertions(+) diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index d5f303c0e656..548d1e0a5ba1 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -589,6 +590,14 @@ static void __init xen_align_and_add_e820_region(phys_addr_t start, if (type == E820_TYPE_RAM) { start = PAGE_ALIGN(start); end &= ~((phys_addr_t)PAGE_SIZE - 1); +#ifdef CONFIG_MEMORY_HOTPLUG + /* + * Don't allow adding memory not in E820 map while booting the + * system. Once the balloon driver is up it will remove that + * restriction again. + */ + max_mem_size = end; +#endif } e820__range_add(start, end - start, type); @@ -748,6 +757,10 @@ char * __init xen_memory_setup(void) memmap.nr_entries = ARRAY_SIZE(xen_e820_table.entries); set_xen_guest_handle(memmap.buffer, xen_e820_table.entries); +#if defined(CONFIG_MEMORY_HOTPLUG) && defined(CONFIG_XEN_BALLOON) + xen_saved_max_mem_size = max_mem_size; +#endif + op = xen_initial_domain() ? XENMEM_machine_memory_map : XENMEM_memory_map; diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index 2acbfe104e46..a67236b02452 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,10 @@ #define BALLOON_CLASS_NAME "xen_memory" +#ifdef CONFIG_MEMORY_HOTPLUG +u64 xen_saved_max_mem_size = 0; +#endif + static struct device balloon_dev; static int register_balloon(struct device *dev); @@ -63,6 +68,12 @@ static void watch_target(struct xenbus_watch *watch, static bool watch_fired; static long target_diff; +#ifdef CONFIG_MEMORY_HOTPLUG + /* The balloon driver will take care of adding memory now. */ + if (xen_saved_max_mem_size) + max_mem_size = xen_saved_max_mem_size; +#endif + err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target); if (err != 1) { /* This is ok (for domain0 at least) - so just return */ diff --git a/include/xen/xen.h b/include/xen/xen.h index 0e2156786ad2..19d032373de5 100644 --- a/include/xen/xen.h +++ b/include/xen/xen.h @@ -46,4 +46,8 @@ struct bio_vec; bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, const struct bio_vec *vec2); +#if defined(CONFIG_MEMORY_HOTPLUG) && defined(CONFIG_XEN_BALLOON) +extern u64 xen_saved_max_mem_size; +#endif + #endif /* _XEN_XEN_H */ From 376211557302c1d851452579e5dc7f87d297f572 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 30 Nov 2018 12:59:09 +0530 Subject: [PATCH 1913/4277] dt-binding: remoteproc: Remove lpass_aon clock from adsp pil clock list LPASS_Audio_Wrapper_AON clock is on by default. Remove it from lpass clock list to avoid voting for it. Reviewed-by: Rob Herring Signed-off-by: Rohit kumar Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/remoteproc/qcom,adsp-pil.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt index a842a782b557..66af2c30944f 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt @@ -35,7 +35,7 @@ on the Qualcomm Technology Inc. ADSP Hexagon core. Value type: Definition: List of clock input name strings sorted in the same order as the clocks property. Definition must have - "xo", "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", + "xo", "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep" and "qdsp6ss_core". @@ -100,13 +100,12 @@ ADSP, as it is found on SDM845 boards. clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_LPASS_SWAY_CLK>, - <&lpasscc LPASS_AUDIO_WRAPPER_AON_CLK>, <&lpasscc LPASS_Q6SS_AHBS_AON_CLK>, <&lpasscc LPASS_Q6SS_AHBM_AON_CLK>, <&lpasscc LPASS_QDSP6SS_XO_CLK>, <&lpasscc LPASS_QDSP6SS_SLEEP_CLK>, <&lpasscc LPASS_QDSP6SS_CORE_CLK>; - clock-names = "xo", "sway_cbcr", "lpass_aon", + clock-names = "xo", "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core"; From 621eafdb0f0dbb46b8ba9c12dbf566a49f07363d Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 30 Nov 2018 12:59:10 +0530 Subject: [PATCH 1914/4277] remoteproc: q6v5_adsp: Remove voting for lpass_aon clock Lpass_aon clock is on by default. Remove it from lpass clock list to avoid voting for it. Signed-off-by: Rohit kumar Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_adsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 91bb99668253..1f3ef9ee493c 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -48,7 +48,7 @@ /* list of clocks required by ADSP PIL */ static const char * const adsp_clk_id[] = { - "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", + "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", }; From 16b7a09bb6fdf5d83bd22fd930c7e3c1e4f32911 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Jan 2019 20:03:28 +0200 Subject: [PATCH 1915/4277] mfd: intel-lpss: Move linux/pm.h to the local header We now using a common macro for PM operations in Intel LPSS driver, and, since that macro relies on the definition and macro from linux/pm.h header file, it's logical to include it directly in intel-lpss.h. Otherwise it's a bit fragile and requires a proper ordering of header inclusion in C files. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-acpi.c | 1 - drivers/mfd/intel-lpss-pci.c | 1 - drivers/mfd/intel-lpss.h | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 7911b0a14a6d..6d9f03363ee7 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 0e5282fc1467..cba2eb166650 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h index 865bbeaaf00c..3a120fecd2dc 100644 --- a/drivers/mfd/intel-lpss.h +++ b/drivers/mfd/intel-lpss.h @@ -14,6 +14,8 @@ #ifndef __MFD_INTEL_LPSS_H #define __MFD_INTEL_LPSS_H +#include + struct device; struct resource; struct property_entry; From 2f17dd34ffed09823d6598903276b867bb238f95 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 9 Feb 2019 00:06:22 +0100 Subject: [PATCH 1916/4277] mfd: tqmx86: IO controller with I2C, Wachdog and GPIO The QMX86 is a PLD present on some TQ-Systems ComExpress modules. It provides 1 or 2 I2C bus masters, 8 GPIOs and a watchdog timer. Add an MFD which will instantiate the individual drivers. Signed-off-by: Andrew Lunn Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 8 ++ drivers/mfd/Makefile | 1 + drivers/mfd/tqmx86.c | 281 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 drivers/mfd/tqmx86.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f38f8741c68e..18ee80389237 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1677,6 +1677,14 @@ config MFD_TC6393XB help Support for Toshiba Mobile IO Controller TC6393XB +config MFD_TQMX86 + tristate "TQ-Systems IO controller TQMX86" + select MFD_CORE + help + Say yes here to enable support for various functions of the + TQ-Systems IO controller and watchdog device, found on their + ComExpress CPU modules. + config MFD_VX855 tristate "VIA VX855/VX875 integrated south bridge" depends on PCI diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a406fd3b8681..61a078a2b8f5 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_MFD_TC3589X) += tc3589x.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o +obj-$(CONFIG_MFD_TQMX86) += tqmx86.o obj-$(CONFIG_MFD_LOCHNAGAR) += lochnagar-i2c.o diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c new file mode 100644 index 000000000000..22d2f02d855c --- /dev/null +++ b/drivers/mfd/tqmx86.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TQ-Systems PLD MFD core driver, based on vendor driver by + * Vadim V.Vlasov + * + * Copyright (c) 2015 TQ-Systems GmbH + * Copyright (c) 2019 Andrew Lunn + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TQMX86_IOBASE 0x160 +#define TQMX86_IOSIZE 0x3f +#define TQMX86_IOBASE_I2C 0x1a0 +#define TQMX86_IOSIZE_I2C 0xa +#define TQMX86_IOBASE_WATCHDOG 0x18b +#define TQMX86_IOSIZE_WATCHDOG 0x2 +#define TQMX86_IOBASE_GPIO 0x18d +#define TQMX86_IOSIZE_GPIO 0x4 + +#define TQMX86_REG_BOARD_ID 0x20 +#define TQMX86_REG_BOARD_ID_E38M 1 +#define TQMX86_REG_BOARD_ID_50UC 2 +#define TQMX86_REG_BOARD_ID_E38C 3 +#define TQMX86_REG_BOARD_ID_60EB 4 +#define TQMX86_REG_BOARD_ID_E39M 5 +#define TQMX86_REG_BOARD_ID_E39C 6 +#define TQMX86_REG_BOARD_ID_E39x 7 +#define TQMX86_REG_BOARD_ID_70EB 8 +#define TQMX86_REG_BOARD_ID_80UC 9 +#define TQMX86_REG_BOARD_ID_90UC 10 +#define TQMX86_REG_BOARD_REV 0x21 +#define TQMX86_REG_IO_EXT_INT 0x26 +#define TQMX86_REG_IO_EXT_INT_NONE 0 +#define TQMX86_REG_IO_EXT_INT_7 1 +#define TQMX86_REG_IO_EXT_INT_9 2 +#define TQMX86_REG_IO_EXT_INT_12 3 +#define TQMX86_REG_IO_EXT_INT_MASK 0x3 +#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 4 + +#define TQMX86_REG_I2C_DETECT 0x47 +#define TQMX86_REG_I2C_DETECT_SOFT 0xa5 +#define TQMX86_REG_I2C_INT_EN 0x49 + +static uint gpio_irq; +module_param(gpio_irq, uint, 0); +MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)"); + +static const struct resource tqmx_i2c_soft_resources[] = { + DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C), +}; + +static const struct resource tqmx_watchdog_resources[] = { + DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG), +}; + +/* + * The IRQ resource must be first, since it is updated with the + * configured IRQ in the probe function. + */ +static struct resource tqmx_gpio_resources[] = { + DEFINE_RES_IRQ(0), + DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO), +}; + +static struct i2c_board_info tqmx86_i2c_devices[] = { + { + /* 4K EEPROM at 0x50 */ + I2C_BOARD_INFO("24c32", 0x50), + }, +}; + +static struct ocores_i2c_platform_data ocores_platfom_data = { + .num_devices = ARRAY_SIZE(tqmx86_i2c_devices), + .devices = tqmx86_i2c_devices, +}; + +static const struct mfd_cell tqmx86_i2c_soft_dev[] = { + { + .name = "ocores-i2c", + .platform_data = &ocores_platfom_data, + .pdata_size = sizeof(ocores_platfom_data), + .resources = tqmx_i2c_soft_resources, + .num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources), + }, +}; + +static const struct mfd_cell tqmx86_devs[] = { + { + .name = "tqmx86-wdt", + .resources = tqmx_watchdog_resources, + .num_resources = ARRAY_SIZE(tqmx_watchdog_resources), + .ignore_resource_conflicts = true, + }, + { + .name = "tqmx86-gpio", + .resources = tqmx_gpio_resources, + .num_resources = ARRAY_SIZE(tqmx_gpio_resources), + .ignore_resource_conflicts = true, + }, +}; + +static const char *tqmx86_board_id_to_name(u8 board_id) +{ + switch (board_id) { + case TQMX86_REG_BOARD_ID_E38M: + return "TQMxE38M"; + case TQMX86_REG_BOARD_ID_50UC: + return "TQMx50UC"; + case TQMX86_REG_BOARD_ID_E38C: + return "TQMxE38C"; + case TQMX86_REG_BOARD_ID_60EB: + return "TQMx60EB"; + case TQMX86_REG_BOARD_ID_E39M: + return "TQMxE39M"; + case TQMX86_REG_BOARD_ID_E39C: + return "TQMxE39C"; + case TQMX86_REG_BOARD_ID_E39x: + return "TQMxE39x"; + case TQMX86_REG_BOARD_ID_70EB: + return "TQMx70EB"; + case TQMX86_REG_BOARD_ID_80UC: + return "TQMx80UC"; + case TQMX86_REG_BOARD_ID_90UC: + return "TQMx90UC"; + default: + return "Unknown"; + } +} + +static int tqmx86_board_id_to_clk_rate(u8 board_id) +{ + switch (board_id) { + case TQMX86_REG_BOARD_ID_50UC: + case TQMX86_REG_BOARD_ID_60EB: + case TQMX86_REG_BOARD_ID_70EB: + case TQMX86_REG_BOARD_ID_80UC: + case TQMX86_REG_BOARD_ID_90UC: + return 24000; + case TQMX86_REG_BOARD_ID_E39M: + case TQMX86_REG_BOARD_ID_E39C: + case TQMX86_REG_BOARD_ID_E39x: + return 25000; + case TQMX86_REG_BOARD_ID_E38M: + case TQMX86_REG_BOARD_ID_E38C: + return 33000; + default: + return 0; + } +} + +static int tqmx86_probe(struct platform_device *pdev) +{ + u8 board_id, rev, i2c_det, i2c_ien, io_ext_int_val; + struct device *dev = &pdev->dev; + u8 gpio_irq_cfg, readback; + const char *board_name; + void __iomem *io_base; + int err; + + switch (gpio_irq) { + case 0: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE; + break; + case 7: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7; + break; + case 9: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9; + break; + case 12: + gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12; + break; + default: + pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq); + return -EINVAL; + } + + io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE); + if (!io_base) + return -ENOMEM; + + board_id = ioread8(io_base + TQMX86_REG_BOARD_ID); + board_name = tqmx86_board_id_to_name(board_id); + rev = ioread8(io_base + TQMX86_REG_BOARD_REV); + + dev_info(dev, + "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n", + board_name, board_id, rev >> 4, rev & 0xf); + + i2c_det = ioread8(io_base + TQMX86_REG_I2C_DETECT); + i2c_ien = ioread8(io_base + TQMX86_REG_I2C_INT_EN); + + if (gpio_irq_cfg) { + io_ext_int_val = + gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT; + iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT); + readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT); + if (readback != io_ext_int_val) { + dev_warn(dev, "GPIO interrupts not supported.\n"); + return -EINVAL; + } + + /* Assumes the IRQ resource is first. */ + tqmx_gpio_resources[0].start = gpio_irq; + } + + ocores_platfom_data.clock_khz = tqmx86_board_id_to_clk_rate(board_id); + + if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) { + err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + tqmx86_i2c_soft_dev, + ARRAY_SIZE(tqmx86_i2c_soft_dev), + NULL, 0, NULL); + if (err) + return err; + } + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + tqmx86_devs, + ARRAY_SIZE(tqmx86_devs), + NULL, 0, NULL); +} + +static int tqmx86_create_platform_device(const struct dmi_system_id *id) +{ + struct platform_device *pdev; + int err; + + pdev = platform_device_alloc("tqmx86", -1); + if (!pdev) + return -ENOMEM; + + err = platform_device_add(pdev); + if (err) + platform_device_put(pdev); + + return err; +} + +static const struct dmi_system_id tqmx86_dmi_table[] __initconst = { + { + .ident = "TQMX86", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"), + DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), + }, + .callback = tqmx86_create_platform_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); + +static struct platform_driver tqmx86_driver = { + .driver = { + .name = "tqmx86", + }, + .probe = tqmx86_probe, +}; + +static int __init tqmx86_init(void) +{ + if (!dmi_check_system(tqmx86_dmi_table)) + return -ENODEV; + + return platform_driver_register(&tqmx86_driver); +} + +module_init(tqmx86_init); + +MODULE_DESCRIPTION("TQx86 PLD Core Driver"); +MODULE_AUTHOR("Andrew Lunn "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tqmx86"); From 4720eb47529e456410470e6e2bed38d5d36cbb8a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 11 Feb 2019 14:20:23 +0100 Subject: [PATCH 1917/4277] mfd: sec-core: Cleanup formatting to a consistent style Cleanup the formatting to have consistent style across the file (only white-space issues). No expected difference in code. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 00321011de0a..521319086c81 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -114,7 +114,8 @@ static const struct mfd_cell s2mpu02_devs[] = { #ifdef CONFIG_OF static const struct of_device_id sec_dt_match[] = { - { .compatible = "samsung,s5m8767-pmic", + { + .compatible = "samsung,s5m8767-pmic", .data = (void *)S5M8767X, }, { .compatible = "samsung,s2mps11-pmic", @@ -309,8 +310,8 @@ static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic) * the sub-modules need not instantiate another instance while parsing their * platform data. */ -static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( - struct device *dev) +static struct sec_platform_data * +sec_pmic_i2c_parse_dt_pdata(struct device *dev) { struct sec_platform_data *pd; @@ -331,8 +332,8 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( return pd; } #else -static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( - struct device *dev) +static struct sec_platform_data * +sec_pmic_i2c_parse_dt_pdata(struct device *dev) { return NULL; } From d2d833e0bf2bad221a955626b942b38312630894 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Feb 2019 16:48:27 -0600 Subject: [PATCH 1918/4277] mfd: mxs-lradc: Mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: drivers/mfd/mxs-lradc.c: In function ‘mxs_lradc_probe’: drivers/mfd/mxs-lradc.c:179:7: warning: this statement may fall through [-Wimplicit-fallthrough=] if (lradc->soc == IMX28_LRADC) { ^ drivers/mfd/mxs-lradc.c:185:3: note: here default: ^~~~~~~ Warning level 3 was used: -Wimplicit-fallthrough=3 Notice that, in this particular case, the code comment is modified in accordance with what GCC is expecting to find. This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Lee Jones --- drivers/mfd/mxs-lradc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c index 98e732a7ae96..a09a8d06302b 100644 --- a/drivers/mfd/mxs-lradc.c +++ b/drivers/mfd/mxs-lradc.c @@ -181,7 +181,7 @@ static int mxs_lradc_probe(struct platform_device *pdev) MXS_LRADC_TOUCHSCREEN_5WIRE; break; } - /* fall through to an error message for i.MX23 */ + /* fall through - to an error message for i.MX23 */ default: dev_err(&pdev->dev, "Unsupported number of touchscreen wires (%d)\n" From 14c8e80e68695c2b9342112c39263a79e0963461 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Feb 2019 05:25:42 -0500 Subject: [PATCH 1919/4277] media: v4l: ioctl: Sanitize num_planes before using it The linked commit changed s_fmt/try_fmt to fail if num_planes is bogus. This, however, is against the spec, which mandates drivers to return a proper num_planes value, without an error. Replace the num_planes check and instead clamp it to a sane value, so we still make sure we don't overflow the planes array by accident. Fixes: 9048b2e15b11c5 ("media: v4l: ioctl: Validate num_planes before using it") Signed-off-by: Ezequiel Garcia Reviewed-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 90aad465f9ed..206b7348797e 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1017,6 +1017,12 @@ static void v4l_sanitize_format(struct v4l2_format *fmt) { unsigned int offset; + /* Make sure num_planes is not bogus */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + fmt->fmt.pix_mp.num_planes = min_t(u32, fmt->fmt.pix_mp.num_planes, + VIDEO_MAX_PLANES); + /* * The v4l2_pix_format structure has been extended with fields that were * not previously required to be set to zero by applications. The priv @@ -1553,8 +1559,6 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); @@ -1586,8 +1590,6 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); @@ -1656,8 +1658,6 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); @@ -1689,8 +1689,6 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); From a8566d79f8ffb09a9d046bc98eb7ab59c79c6b58 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 18 Feb 2019 10:29:31 -0500 Subject: [PATCH 1920/4277] media: vim2m: fix build breakage due to a merge conflict A merge conflict rised when merging from -rc7. Fix it. In this specific case, we don't need the if anymore, as the work_run was moved to its rightful place (struct vim2m_ctx). Fixes: b3e64e5b0778 ("media: vim2m: use per-file handler work queue") Fixes: 240809ef6630 ("media: vim2m: only cancel work if it is for right context") Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 04250adf58e0..a27d3052bb62 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -905,8 +905,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q) struct vb2_v4l2_buffer *vbuf; unsigned long flags; - if (v4l2_m2m_get_curr_priv(dev->m2m_dev) == ctx) - cancel_delayed_work_sync(&dev->work_run); + cancel_delayed_work_sync(&ctx->work_run); for (;;) { if (V4L2_TYPE_IS_OUTPUT(q->type)) From 09714569969cfe5e5b51fa5b0d9224ce38744e76 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 09:58:26 -0500 Subject: [PATCH 1921/4277] media: vimc: add USERPTR support Add VB2_USERPTR to the vimc capture device. Signed-off-by: Hans Verkuil Acked-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index a6f8715d2b44..ea869631a3f6 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -429,7 +429,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, /* Initialize the vb2 queue */ q = &vcap->queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; q->drv_priv = vcap; q->buf_struct_size = sizeof(struct vimc_cap_buffer); q->ops = &vimc_cap_qops; From 5964cbd8692252615370b77eb96764dd70c2f837 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Thu, 7 Feb 2019 18:42:55 -0500 Subject: [PATCH 1922/4277] media: imx: Set capture compose rectangle in capture_device_set_format The capture compose rectangle was not getting updated when setting the source subdevice's source pad format. This causes the compose window to be zero (or not updated) at stream start unless the capture device format was set explicitly at the capture device node. Fix by moving the calculation of the capture compose rectangle to imx_media_mbus_fmt_to_pix_fmt(), and pass the rectangle to imx_media_capture_device_set_format(). Fixes: 439d8186fb23 ("media: imx: add capture compose rectangle") Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 5 ++-- drivers/staging/media/imx/imx-media-capture.c | 24 +++++++++---------- drivers/staging/media/imx/imx-media-csi.c | 5 ++-- drivers/staging/media/imx/imx-media-utils.c | 20 ++++++++++++---- drivers/staging/media/imx/imx-media.h | 6 +++-- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 376b504e8a42..5c8e6ad8c025 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -912,6 +912,7 @@ static int prp_set_fmt(struct v4l2_subdev *sd, const struct imx_media_pixfmt *cc; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect vdev_compose; int ret = 0; if (sdformat->pad >= PRPENCVF_NUM_PADS) @@ -953,11 +954,11 @@ static int prp_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &priv->format_mbus[PRPENCVF_SRC_PAD], priv->cc[PRPENCVF_SRC_PAD]); mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index f92edee63aa6..9703c85b19c4 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -205,7 +205,8 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, static int __capture_try_fmt_vid_cap(struct capture_priv *priv, struct v4l2_subdev_format *fmt_src, - struct v4l2_format *f) + struct v4l2_format *f, + struct v4l2_rect *compose) { const struct imx_media_pixfmt *cc, *cc_src; @@ -245,7 +246,8 @@ static int __capture_try_fmt_vid_cap(struct capture_priv *priv, } } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src->format, cc); + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, compose, + &fmt_src->format, cc); return 0; } @@ -263,7 +265,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - return __capture_try_fmt_vid_cap(priv, &fmt_src, f); + return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL); } static int capture_s_fmt_vid_cap(struct file *file, void *fh, @@ -271,6 +273,7 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, { struct capture_priv *priv = video_drvdata(file); struct v4l2_subdev_format fmt_src; + struct v4l2_rect compose; int ret; if (vb2_is_busy(&priv->q)) { @@ -284,17 +287,14 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f); + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &compose); if (ret) return ret; priv->vdev.fmt.fmt.pix = f->fmt.pix; priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat, CS_SEL_ANY, true); - priv->vdev.compose.left = 0; - priv->vdev.compose.top = 0; - priv->vdev.compose.width = fmt_src.format.width; - priv->vdev.compose.height = fmt_src.format.height; + priv->vdev.compose = compose; return 0; } @@ -655,7 +655,8 @@ static struct video_device capture_videodev = { }; void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - struct v4l2_pix_format *pix) + const struct v4l2_pix_format *pix, + const struct v4l2_rect *compose) { struct capture_priv *priv = to_capture_priv(vdev); @@ -663,6 +664,7 @@ void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, priv->vdev.fmt.fmt.pix = *pix; priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY, true); + priv->vdev.compose = *compose; mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format); @@ -768,10 +770,8 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) } vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, + imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &vdev->compose, &fmt_src.format, NULL); - vdev->compose.width = fmt_src.format.width; - vdev->compose.height = fmt_src.format.height; vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat, CS_SEL_ANY, false); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index fb5307e2ca43..5265a2cc93bc 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1502,6 +1502,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; + struct v4l2_rect vdev_compose; int ret; if (sdformat->pad >= CSI_NUM_PADS) @@ -1557,11 +1558,11 @@ static int csi_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate IDMAC output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &priv->format_mbus[CSI_SRC_PAD_IDMAC], priv->cc[CSI_SRC_PAD_IDMAC]); mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5f110d90a4ef..e6f544528d31 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -577,7 +577,8 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - struct v4l2_mbus_framefmt *mbus, + struct v4l2_rect *compose, + const struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc) { u32 width; @@ -624,6 +625,17 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) : stride * pix->height; + /* + * set capture compose rectangle, which is fixed to the + * source subdevice mbus format. + */ + if (compose) { + compose->left = 0; + compose->top = 0; + compose->width = mbus->width; + compose->height = mbus->height; + } + return 0; } EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt); @@ -635,13 +647,11 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, memset(image, 0, sizeof(*image)); - ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL); + ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, &image->rect, + mbus, NULL); if (ret) return ret; - image->rect.width = mbus->width; - image->rect.height = mbus->height; - return 0; } EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 7a0e658753f0..195e858913a3 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -180,7 +180,8 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, struct v4l2_mbus_framefmt *fmt, bool ic_route); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - struct v4l2_mbus_framefmt *mbus, + struct v4l2_rect *compose, + const struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc); int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, struct v4l2_mbus_framefmt *mbus); @@ -261,7 +262,8 @@ void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev); struct imx_media_buffer * imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev); void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - struct v4l2_pix_format *pix); + const struct v4l2_pix_format *pix, + const struct v4l2_rect *compose); void imx_media_capture_device_error(struct imx_media_video_dev *vdev); /* subdev group ids */ From 5515e414f42bf2769caae15b634004d456658284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20A=2E=20M=2E=20Magalh=C3=A3es?= Date: Thu, 7 Feb 2019 18:59:41 -0500 Subject: [PATCH 1923/4277] media: vimc: Remove unused but set variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused but set variables to clean up the code and avoid warning. Signed-off-by: Lucas A. M. Magalhães Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-sensor.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 93961a1e694f..59195f262623 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -204,13 +204,6 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved, { struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); - const struct vimc_pix_map *vpix; - unsigned int frame_size; - - /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsen->mbus_format.code); - frame_size = vsen->mbus_format.width * vpix->bpp * - vsen->mbus_format.height; tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); return vsen->frame; From 3d769df5fc32dc3cb12d6ccb61690b09371b8b3f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 8 Feb 2019 03:49:23 -0500 Subject: [PATCH 1924/4277] media: v4l2-subdev.h: v4l2_subdev_call: use temp __sd variable The sd argument of this macro can be a more complex expression. Since it is used 5 times in the macro it can be evaluated that many times as well. So assign it to a temp variable in the beginning and use that instead. This also avoids any potential side-effects of evaluating sd. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 47af609dc8f1..34da094a3f40 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1093,13 +1093,14 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, */ #define v4l2_subdev_call(sd, o, f, args...) \ ({ \ + struct v4l2_subdev *__sd = (sd); \ int __result; \ - if (!(sd)) \ + if (!__sd) \ __result = -ENODEV; \ - else if (!((sd)->ops->o && (sd)->ops->o->f)) \ + else if (!(__sd->ops->o && __sd->ops->o->f)) \ __result = -ENOIOCTLCMD; \ else \ - __result = (sd)->ops->o->f((sd), ##args); \ + __result = __sd->ops->o->f(__sd, ##args); \ __result; \ }) From bc2dea9e51c8ffa4d1bdb5646438532189e63d9e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:08 -0500 Subject: [PATCH 1925/4277] media: adv748x: Add is_txb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add small is_txb() macro to the existing is_txa() and use it where appropriate. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-csi2.c | 2 +- drivers/media/i2c/adv748x/adv748x.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 6ce21542ed48..b6b5d8c7ea7c 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -82,7 +82,7 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, ADV748X_HDMI_SOURCE); - if (!is_txa(tx) && is_afe_enabled(state)) + if (is_txb(tx) && is_afe_enabled(state)) return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->afe.sd, ADV748X_AFE_SOURCE); diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index b482c7fe6957..ab0c84adbea9 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -89,8 +89,11 @@ struct adv748x_csi2 { #define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier) #define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd) + #define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL) #define is_txa(_tx) ((_tx) == &(_tx)->state->txa) +#define is_txb(_tx) ((_tx) == &(_tx)->state->txb) + #define is_afe_enabled(_state) \ ((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \ (_state)->endpoints[ADV748X_PORT_AIN1] != NULL || \ From 29166e0f8f426a9a3067efd9317d84e4db82f009 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:09 -0500 Subject: [PATCH 1926/4277] media: adv748x: Rename reset procedures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the chip reset procedure as they configure the CP (HDMI) and SD (AFE) cores. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 26 ++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index bd8a8c8d6f8e..ba91db45547a 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -353,9 +353,8 @@ static const struct adv748x_reg_value adv748x_sw_reset[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; -/* Supported Formats For Script Below */ -/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */ -static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { +/* Initialize CP Core with RGB888 format. */ +static const struct adv748x_reg_value adv748x_init_hdmi[] = { /* Disable chip powerdown & Enable HDMI Rx block */ {ADV748X_PAGE_IO, 0x00, 0x40}, @@ -399,10 +398,8 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; -/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */ -/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */ -static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { - +/* Initialize AFE core with YUV8 format. */ +static const struct adv748x_reg_value adv748x_init_afe[] = { {ADV748X_PAGE_IO, 0x00, 0x30}, /* Disable chip powerdown Rx */ {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */ @@ -445,19 +442,18 @@ static int adv748x_reset(struct adv748x_state *state) if (ret < 0) return ret; - /* Init and power down TXA */ - ret = adv748x_write_regs(state, adv748x_init_txa_4lane); + /* Initialize CP and AFE cores. */ + ret = adv748x_write_regs(state, adv748x_init_hdmi); if (ret) return ret; + ret = adv748x_write_regs(state, adv748x_init_afe); + if (ret) + return ret; + + /* Reset TXA and TXB */ adv748x_tx_power(&state->txa, 1); adv748x_tx_power(&state->txa, 0); - - /* Init and power down TXB */ - ret = adv748x_write_regs(state, adv748x_init_txb_1lane); - if (ret) - return ret; - adv748x_tx_power(&state->txb, 1); adv748x_tx_power(&state->txb, 0); From 21325e19cadec506bd48591d3944bd526f647d93 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:10 -0500 Subject: [PATCH 1927/4277] media: adv748x: csi2: Link AFE with TXA and TXB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV748x chip supports routing AFE output to either TXA or TXB. In order to support run-time configuration of video stream path, create an additional (not enabled) "AFE:8->TXA:0" link, and remove the IMMUTABLE flag from existing ones. Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-csi2.c | 44 +++++++++++++----------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index b6b5d8c7ea7c..8c3714495e11 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -27,6 +27,7 @@ static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, * @v4l2_dev: Video registration device * @src: Source subdevice to establish link * @src_pad: Pad number of source to link to this @tx + * @enable: Link enabled flag * * Ensure that the subdevice is registered against the v4l2_device, and link the * source pad to the sink pad of the CSI2 bus entity. @@ -34,17 +35,11 @@ static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, struct v4l2_device *v4l2_dev, struct v4l2_subdev *src, - unsigned int src_pad) + unsigned int src_pad, + bool enable) { - int enabled = MEDIA_LNK_FL_ENABLED; int ret; - /* - * Dynamic linking of the AFE is not supported. - * Register the links as immutable. - */ - enabled |= MEDIA_LNK_FL_IMMUTABLE; - if (!src->v4l2_dev) { ret = v4l2_device_register_subdev(v4l2_dev, src); if (ret) @@ -53,7 +48,7 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, return media_create_pad_link(&src->entity, src_pad, &tx->sd.entity, ADV748X_CSI2_SINK, - enabled); + enable ? MEDIA_LNK_FL_ENABLED : 0); } /* ----------------------------------------------------------------------------- @@ -68,25 +63,32 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); struct adv748x_state *state = tx->state; + int ret; adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB", sd->name); /* - * The adv748x hardware allows the AFE to route through the TXA, however - * this is not currently supported in this driver. + * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output + * HDMI. * - * Link HDMI->TXA, and AFE->TXB directly. + * The HDMI->TXA link is enabled by default, as is the AFE->TXB one. */ - if (is_txa(tx) && is_hdmi_enabled(state)) - return adv748x_csi2_register_link(tx, sd->v4l2_dev, - &state->hdmi.sd, - ADV748X_HDMI_SOURCE); - if (is_txb(tx) && is_afe_enabled(state)) - return adv748x_csi2_register_link(tx, sd->v4l2_dev, - &state->afe.sd, - ADV748X_AFE_SOURCE); - return 0; + if (is_afe_enabled(state)) { + ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, + &state->afe.sd, + ADV748X_AFE_SOURCE, + is_txb(tx)); + if (ret) + return ret; + } + + /* Register link to HDMI for TXA only. */ + if (is_txb(tx) || !is_hdmi_enabled(state)) + return 0; + + return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, + ADV748X_HDMI_SOURCE, true); } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { From 3361b9c4ed8790666875cbac1729d5a15e982947 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:11 -0500 Subject: [PATCH 1928/4277] media: adv748x: Store the source subdevice in TX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The power_up_tx() procedure needs to set a few registers conditionally to the selected video source, but it currently checks for the provided tx to be either TXA or TXB. With the introduction of dynamic routing between HDMI and AFE entities to TXA, checking which TX the function is operating on is not meaningful anymore. To fix this, store the subdevice of the source providing video data to the CSI-2 TX in the 'struct adv748x_csi2' representing the TX and check on it. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 2 +- drivers/media/i2c/adv748x/adv748x-csi2.c | 13 ++++++++++--- drivers/media/i2c/adv748x/adv748x.h | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index ba91db45547a..824bca3219b7 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -254,7 +254,7 @@ static int adv748x_power_up_tx(struct adv748x_csi2 *tx) adv748x_write_check(state, page, 0x00, 0xa0 | tx->num_lanes, &ret); /* ADI Required Write */ - if (is_txa(tx)) { + if (tx->src == &state->hdmi.sd) { adv748x_write_check(state, page, 0xdb, 0x10, &ret); adv748x_write_check(state, page, 0xd6, 0x07, &ret); } else { diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 8c3714495e11..353b6b9bf6a7 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -46,9 +46,16 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, return ret; } - return media_create_pad_link(&src->entity, src_pad, - &tx->sd.entity, ADV748X_CSI2_SINK, - enable ? MEDIA_LNK_FL_ENABLED : 0); + ret = media_create_pad_link(&src->entity, src_pad, + &tx->sd.entity, ADV748X_CSI2_SINK, + enable ? MEDIA_LNK_FL_ENABLED : 0); + if (ret) + return ret; + + if (enable) + tx->src = src; + + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index ab0c84adbea9..d22270f5e2c1 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -84,6 +84,7 @@ struct adv748x_csi2 { struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct v4l2_ctrl_handler ctrl_hdl; struct v4l2_ctrl *pixel_rate; + struct v4l2_subdev *src; struct v4l2_subdev sd; }; From a33df6ac31513c499e4b8dae9632e641ebe4a245 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:12 -0500 Subject: [PATCH 1929/4277] media: adv748x: Store the TX sink in HDMI/AFE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both the AFE and HDMI s_stream routines (adv748x_afe_s_stream() and adv748x_hdmi_s_stream()) have to enable the CSI-2 TX they are streaming video data to. With the introduction of dynamic routing between HDMI and AFE entities to TXA, the video stream sink needs to be set at run time, and not statically selected as the s_stream functions are currently doing. To fix this, store a reference to the active CSI-2 TX sink for both HDMI and AFE sources, and operate on it when starting/stopping the stream. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-afe.c | 2 +- drivers/media/i2c/adv748x/adv748x-csi2.c | 15 +++++++++++++-- drivers/media/i2c/adv748x/adv748x-hdmi.c | 2 +- drivers/media/i2c/adv748x/adv748x.h | 4 ++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 71714634efb0..dbbb1e4d6363 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -282,7 +282,7 @@ static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable) goto unlock; } - ret = adv748x_tx_power(&state->txb, enable); + ret = adv748x_tx_power(afe->tx, enable); if (ret) goto unlock; diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 353b6b9bf6a7..2091cda50935 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -88,14 +88,25 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) is_txb(tx)); if (ret) return ret; + + /* TXB can output AFE signals only. */ + if (is_txb(tx)) + state->afe.tx = tx; } /* Register link to HDMI for TXA only. */ if (is_txb(tx) || !is_hdmi_enabled(state)) return 0; - return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, - ADV748X_HDMI_SOURCE, true); + ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, + ADV748X_HDMI_SOURCE, true); + if (ret) + return ret; + + /* The default HDMI output is TXA. */ + state->hdmi.tx = tx; + + return 0; } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 35d027941482..c557f8fdf11a 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -358,7 +358,7 @@ static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&state->mutex); - ret = adv748x_tx_power(&state->txa, enable); + ret = adv748x_tx_power(hdmi->tx, enable); if (ret) goto done; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index d22270f5e2c1..934a9d9a75c8 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -121,6 +121,8 @@ struct adv748x_hdmi { struct v4l2_dv_timings timings; struct v4l2_fract aspect_ratio; + struct adv748x_csi2 *tx; + struct { u8 edid[512]; u32 present; @@ -151,6 +153,8 @@ struct adv748x_afe { struct v4l2_subdev sd; struct v4l2_mbus_framefmt format; + struct adv748x_csi2 *tx; + bool streaming; v4l2_std_id curr_norm; unsigned int input; From 9423ca350df71ff71f8db2b01127eecd2254f619 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:13 -0500 Subject: [PATCH 1930/4277] media: adv748x: Implement TX link_setup callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the adv748x driver is informed about a link being created from HDMI or AFE to a CSI-2 TX output, the 'link_setup()' callback is invoked. Make sure to implement proper routing management at link setup time, to route the selected video stream to the desired TX output. Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 48 +++++++++++++++++++++++- drivers/media/i2c/adv748x/adv748x.h | 2 + 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 824bca3219b7..02f9c440301c 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -335,6 +335,51 @@ int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) /* ----------------------------------------------------------------------------- * Media Operations */ +static int adv748x_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *rsd = media_entity_to_v4l2_subdev(remote->entity); + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct adv748x_state *state = v4l2_get_subdevdata(sd); + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + bool enable = flags & MEDIA_LNK_FL_ENABLED; + u8 io10_mask = ADV748X_IO_10_CSI1_EN | + ADV748X_IO_10_CSI4_EN | + ADV748X_IO_10_CSI4_IN_SEL_AFE; + u8 io10 = 0; + + /* Refuse to enable multiple links to the same TX at the same time. */ + if (enable && tx->src) + return -EINVAL; + + /* Set or clear the source (HDMI or AFE) and the current TX. */ + if (rsd == &state->afe.sd) + state->afe.tx = enable ? tx : NULL; + else + state->hdmi.tx = enable ? tx : NULL; + + tx->src = enable ? rsd : NULL; + + if (state->afe.tx) { + /* AFE Requires TXA enabled, even when output to TXB */ + io10 |= ADV748X_IO_10_CSI4_EN; + if (is_txa(tx)) + io10 |= ADV748X_IO_10_CSI4_IN_SEL_AFE; + else + io10 |= ADV748X_IO_10_CSI1_EN; + } + + if (state->hdmi.tx) + io10 |= ADV748X_IO_10_CSI4_EN; + + return io_clrset(state, ADV748X_IO_10, io10_mask, io10); +} + +static const struct media_entity_operations adv748x_tx_media_ops = { + .link_setup = adv748x_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; static const struct media_entity_operations adv748x_media_ops = { .link_validate = v4l2_subdev_link_validate, @@ -516,7 +561,8 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state, state->client->addr, ident); sd->entity.function = function; - sd->entity.ops = &adv748x_media_ops; + sd->entity.ops = is_tx(adv748x_sd_to_csi2(sd)) ? + &adv748x_tx_media_ops : &adv748x_media_ops; } static int adv748x_parse_csi2_lanes(struct adv748x_state *state, diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 934a9d9a75c8..b00c1995efb0 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -94,6 +94,7 @@ struct adv748x_csi2 { #define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL) #define is_txa(_tx) ((_tx) == &(_tx)->state->txa) #define is_txb(_tx) ((_tx) == &(_tx)->state->txb) +#define is_tx(_tx) (is_txa(_tx) || is_txb(_tx)) #define is_afe_enabled(_state) \ ((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \ @@ -223,6 +224,7 @@ struct adv748x_state { #define ADV748X_IO_10_CSI4_EN BIT(7) #define ADV748X_IO_10_CSI1_EN BIT(6) #define ADV748X_IO_10_PIX_OUT_EN BIT(5) +#define ADV748X_IO_10_CSI4_IN_SEL_AFE BIT(3) #define ADV748X_IO_CHIP_REV_ID_1 0xdf #define ADV748X_IO_CHIP_REV_ID_2 0xe0 From ac105ab2138f43b7acbc91f17453e0a0ba2f7f33 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 11 Jan 2019 12:41:40 -0500 Subject: [PATCH 1931/4277] media: i2c: adv748x: Convert SW reset routine to function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV748x is currently reset by writting a small table of registers to the device. The table lacks documentation and contains magic values to perform the actions, including using a fake register address to introduce a delay loop. Remove the table, and convert to code, documenting the purpose of the specific writes along the way. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 32 ++++++++++++++++-------- drivers/media/i2c/adv748x/adv748x.h | 16 ++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 02f9c440301c..252bdb28b18b 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -389,15 +389,6 @@ static const struct media_entity_operations adv748x_media_ops = { * HW setup */ -static const struct adv748x_reg_value adv748x_sw_reset[] = { - - {ADV748X_PAGE_IO, 0xff, 0xff}, /* SW reset */ - {ADV748X_PAGE_WAIT, 0x00, 0x05},/* delay 5 */ - {ADV748X_PAGE_IO, 0x01, 0x76}, /* ADI Required Write */ - {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */ - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; - /* Initialize CP Core with RGB888 format. */ static const struct adv748x_reg_value adv748x_init_hdmi[] = { /* Disable chip powerdown & Enable HDMI Rx block */ @@ -474,12 +465,33 @@ static const struct adv748x_reg_value adv748x_init_afe[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; +static int adv748x_sw_reset(struct adv748x_state *state) +{ + int ret; + + ret = io_write(state, ADV748X_IO_REG_FF, ADV748X_IO_REG_FF_MAIN_RESET); + if (ret) + return ret; + + usleep_range(5000, 6000); + + /* Disable CEC Wakeup from power-down mode */ + ret = io_clrset(state, ADV748X_IO_REG_01, ADV748X_IO_REG_01_PWRDN_MASK, + ADV748X_IO_REG_01_PWRDNB); + if (ret) + return ret; + + /* Enable I2C Read Auto-Increment for consecutive reads */ + return io_write(state, ADV748X_IO_REG_F2, + ADV748X_IO_REG_F2_READ_AUTO_INC); +} + static int adv748x_reset(struct adv748x_state *state) { int ret; u8 regval = 0; - ret = adv748x_write_regs(state, adv748x_sw_reset); + ret = adv748x_sw_reset(state); if (ret < 0) return ret; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index b00c1995efb0..2f8d751cfbb0 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -211,6 +211,11 @@ struct adv748x_state { #define ADV748X_IO_PD 0x00 /* power down controls */ #define ADV748X_IO_PD_RX_EN BIT(6) +#define ADV748X_IO_REG_01 0x01 /* pwrdn{2}b, prog_xtal_freq */ +#define ADV748X_IO_REG_01_PWRDN_MASK (BIT(7) | BIT(6)) +#define ADV748X_IO_REG_01_PWRDN2B BIT(7) /* CEC Wakeup Support */ +#define ADV748X_IO_REG_01_PWRDNB BIT(6) /* CEC Wakeup Support */ + #define ADV748X_IO_REG_04 0x04 #define ADV748X_IO_REG_04_FORCE_FR BIT(0) /* Force CP free-run */ @@ -229,8 +234,19 @@ struct adv748x_state { #define ADV748X_IO_CHIP_REV_ID_1 0xdf #define ADV748X_IO_CHIP_REV_ID_2 0xe0 +#define ADV748X_IO_REG_F2 0xf2 +#define ADV748X_IO_REG_F2_READ_AUTO_INC BIT(0) + +/* For PAGE slave address offsets */ #define ADV748X_IO_SLAVE_ADDR_BASE 0xf2 +/* + * The ADV748x_Recommended_Settings_PrA_2014-08-20.pdf details both 0x80 and + * 0xff as examples for performing a software reset. + */ +#define ADV748X_IO_REG_FF 0xff +#define ADV748X_IO_REG_FF_MAIN_RESET 0xff + /* HDMI RX Map */ #define ADV748X_HDMI_LW1 0x07 /* line width_1 */ #define ADV748X_HDMI_LW1_VERT_FILTER BIT(7) From 16597c2744f79aaf5f9ec0107be477639985bf44 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 15 Jan 2019 09:25:09 -0500 Subject: [PATCH 1932/4277] media: i2c: adv748x: Remove PAGE_WAIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV748X_PAGE_WAIT is a fake page to insert arbitrary delays in the register tables. Its only usage was removed, so we can remove the handling and simplify the code. Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Signed-off-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 19 ++++++------------- drivers/media/i2c/adv748x/adv748x.h | 1 - 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 252bdb28b18b..f57cd77a32fa 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -218,20 +218,13 @@ static int adv748x_write_regs(struct adv748x_state *state, { int ret; - while (regs->page != ADV748X_PAGE_EOR) { - if (regs->page == ADV748X_PAGE_WAIT) { - msleep(regs->value); - } else { - ret = adv748x_write(state, regs->page, regs->reg, - regs->value); - if (ret < 0) { - adv_err(state, - "Error regs page: 0x%02x reg: 0x%02x\n", - regs->page, regs->reg); - return ret; - } + for (; regs->page != ADV748X_PAGE_EOR; regs++) { + ret = adv748x_write(state, regs->page, regs->reg, regs->value); + if (ret < 0) { + adv_err(state, "Error regs page: 0x%02x reg: 0x%02x\n", + regs->page, regs->reg); + return ret; } - regs++; } return 0; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 2f8d751cfbb0..5042f9e94aee 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -39,7 +39,6 @@ enum adv748x_page { ADV748X_PAGE_MAX, /* Fake pages for register sequences */ - ADV748X_PAGE_WAIT, /* Wait x msec */ ADV748X_PAGE_EOR, /* End Mark */ }; From 9f6d7bacc726f4809170291072589dd9e62d4a3c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:01 -0500 Subject: [PATCH 1933/4277] media: ov5640: Move test_pattern_menu before ov5640_set_ctrl_test_pattern The OV5640 has many options for generating test patterns. Unfortunately there is only one V4L2 control for it. Thus the driver would need to list some or all combinations. Move the test_pattern_menu list before the ov5640_set_ctrl_test_pattern function that programs the hardware. This would allow us to add a matching list of values to program into the hardware, while keeping the two lists together for ease of maintenance. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 5a909abd0a2d..8e4e8fa3685f 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2441,6 +2441,11 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) return ret; } +static const char * const test_pattern_menu[] = { + "Disabled", + "Color bars", +}; + static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) { return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, @@ -2585,11 +2590,6 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = { .s_ctrl = ov5640_s_ctrl, }; -static const char * const test_pattern_menu[] = { - "Disabled", - "Color bars", -}; - static int ov5640_init_controls(struct ov5640_dev *sensor) { const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops; From a0c29afb506445e449713afd55e5f149b3d63b79 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:02 -0500 Subject: [PATCH 1934/4277] media: ov5640: Add register definition for test pattern register The OV5640 can generate many types of test patterns, some with additional modifiers, such as a rolling bar, or gamma gradients. Add the bit definitions for all bits in the test pattern register, and use them to compose the values to be written to the register. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 8e4e8fa3685f..22d07b3cc8a2 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2446,10 +2446,30 @@ static const char * const test_pattern_menu[] = { "Color bars", }; +#define OV5640_TEST_ENABLE BIT(7) +#define OV5640_TEST_ROLLING BIT(6) /* rolling horizontal bar */ +#define OV5640_TEST_TRANSPARENT BIT(5) +#define OV5640_TEST_SQUARE_BW BIT(4) /* black & white squares */ +#define OV5640_TEST_BAR_STANDARD (0 << 2) +#define OV5640_TEST_BAR_VERT_CHANGE_1 (1 << 2) +#define OV5640_TEST_BAR_HOR_CHANGE (2 << 2) +#define OV5640_TEST_BAR_VERT_CHANGE_2 (3 << 2) +#define OV5640_TEST_BAR (0 << 0) +#define OV5640_TEST_RANDOM (1 << 0) +#define OV5640_TEST_SQUARE (2 << 0) +#define OV5640_TEST_BLACK (3 << 0) + +static const u8 test_pattern_val[] = { + 0, + OV5640_TEST_ENABLE | OV5640_TEST_TRANSPARENT | + OV5640_TEST_BAR_VERT_CHANGE_1 | + OV5640_TEST_BAR, +}; + static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) { - return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, - 0xa4, value ? 0xa4 : 0); + return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, + test_pattern_val[value]); } static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) From 2aff1fc3653ade61d27e4355346934d71ae0fe59 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:03 -0500 Subject: [PATCH 1935/4277] media: ov5640: Disable transparent feature for test pattern The transparent feature for test patterns blends the test pattern with an actual captured image. This makes the result non-static, subject to changes in the sensor's field of view. Test patterns should be predictable and deterministic, even if they are dynamic patterns. Disable the transparent feature of the test pattern. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 22d07b3cc8a2..a1fd69a21df1 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2461,8 +2461,7 @@ static const char * const test_pattern_menu[] = { static const u8 test_pattern_val[] = { 0, - OV5640_TEST_ENABLE | OV5640_TEST_TRANSPARENT | - OV5640_TEST_BAR_VERT_CHANGE_1 | + OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR, }; From bddc5cdfc82d11cc9bd80c11ef681afbae11c1db Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:04 -0500 Subject: [PATCH 1936/4277] media: ov5640: Add three more test patterns The OV5640 driver currently supports a static color bar pattern with a small vertical gamma gradient. The hardware also supports a color square pattern, as well as having a rolling bar for dynamic sequences. Add three more test patterns: - color bars with a rolling bar (but without the gamma gradient) - static color squares - color squares with a rolling bar Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index a1fd69a21df1..13311483792c 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2444,6 +2444,9 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) static const char * const test_pattern_menu[] = { "Disabled", "Color bars", + "Color bars w/ rolling bar", + "Color squares", + "Color squares w/ rolling bar", }; #define OV5640_TEST_ENABLE BIT(7) @@ -2463,6 +2466,10 @@ static const u8 test_pattern_val[] = { 0, OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR, + OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | + OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR, + OV5640_TEST_ENABLE | OV5640_TEST_SQUARE, + OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | OV5640_TEST_SQUARE, }; static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) From 7cb013b1049a2a599c1fc344644528eb8bf985b8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:05 -0500 Subject: [PATCH 1937/4277] media: ov5640: Set JPEG output timings when outputting JPEG data When compression is turned on, the on-bus data is framed according to the compression mode, and the height and width set in VFIFO_VSIZE and VFIFO_HSIZE. If these are not updated correctly, the sensor will send data framed in a manner unexpected by the capture interface, such as having more bytes per line than expected, and having the extra data dropped. This ultimately results in corrupted data. Set the two values when the media bus is configured for JPEG data, meaning the sensor would be in JPEG mode. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 13311483792c..1c1dc401c678 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -83,6 +83,8 @@ #define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c #define OV5640_REG_FRAME_CTRL01 0x4202 #define OV5640_REG_FORMAT_CONTROL00 0x4300 +#define OV5640_REG_VFIFO_HSIZE 0x4602 +#define OV5640_REG_VFIFO_VSIZE 0x4604 #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 @@ -1043,12 +1045,31 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate) (ilog2(pclk_div) << 4)); } +/* set JPEG framing sizes */ +static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); + if (ret < 0) + return ret; + + return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact); +} + /* download ov5640 settings to sensor through i2c */ static int ov5640_set_timings(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) { int ret; + if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) { + ret = ov5640_set_jpeg_timings(sensor, mode); + if (ret < 0) + return ret; + } + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); if (ret < 0) return ret; From 2b5c18f964df7bb8b7c8319bdaf0548080bb9152 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:06 -0500 Subject: [PATCH 1938/4277] media: ov5640: Consolidate JPEG compression mode setting The register value lists for all the supported resolution settings all include a register address/value pair for setting the JPEG compression mode. With the exception of 1080p (which sets mode 2), all resolutions use mode 3. The only difference between mode 2 and mode 3 is that mode 2 may have padding data on the last line, while mode 3 does not add padding data. As these register values were from dumps of running systems, and the difference between the modes is quite small, using mode 3 for all configurations should be OK. [Sakari Ailus: Align OV5640_REG_JPG_MODE_SELECT register naming.] Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 1c1dc401c678..d2efb94a5720 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -85,6 +85,7 @@ #define OV5640_REG_FORMAT_CONTROL00 0x4300 #define OV5640_REG_VFIFO_HSIZE 0x4602 #define OV5640_REG_VFIFO_VSIZE 0x4604 +#define OV5640_REG_JPG_MODE_SELECT 0x4713 #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 @@ -303,7 +304,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, - {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0}, {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, @@ -372,7 +373,7 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -391,7 +392,7 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -410,7 +411,7 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -429,7 +430,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -448,7 +449,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -467,7 +468,7 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -486,7 +487,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = { {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, }; @@ -506,7 +507,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, @@ -518,7 +519,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, - {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, }; @@ -537,7 +538,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, }; @@ -1051,6 +1052,17 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, { int ret; + /* + * compression mode 3 timing + * + * Data is transmitted with programmable width (VFIFO_HSIZE). + * No padding done. Last line may have less data. Varying + * number of lines per frame, depending on amount of data. + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_JPG_MODE_SELECT, 0x7, 0x3); + if (ret < 0) + return ret; + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); if (ret < 0) return ret; From 6530a5eb99954699743885e800d88be88752d1ab Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Thu, 24 Jan 2019 12:58:01 -0500 Subject: [PATCH 1939/4277] media: ov5640: Fix set 15fps regression The ov5640_try_frame_interval operation updates the FPS as per user input based on default ov5640_frame_rate, OV5640_30_FPS which is failed to update when user trigger 15fps. So, initialize the default ov5640_frame_rate to OV5640_15_FPS so-that it can satisfy to update all fps. Fixes: 5a3ad937bc78 ("media: ov5640: Make the return rate type more explicit") Signed-off-by: Jagan Teki Acked-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index d2efb94a5720..2387c29b7fb0 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2105,7 +2105,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, u32 width, u32 height) { const struct ov5640_mode_info *mode; - enum ov5640_frame_rate rate = OV5640_30_FPS; + enum ov5640_frame_rate rate = OV5640_15_FPS; int minfps, maxfps, best_fps, fps; int i; From 1d4c41f3d887bcd66e82cb2fda124533dad8808a Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 30 Jan 2019 11:48:07 -0500 Subject: [PATCH 1940/4277] media: i2c: ov5640: Fix post-reset delay According to the ov5640 specification (2.7 power up sequence), host can access the sensor's registers 20ms after reset. Trying to access them before leads to undefined behavior and result in sporadic initialization errors. Signed-off-by: Loic Poulain Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 2387c29b7fb0..5278c8723fdd 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1939,7 +1939,7 @@ static void ov5640_reset(struct ov5640_dev *sensor) usleep_range(1000, 2000); gpiod_set_value_cansleep(sensor->reset_gpio, 0); - usleep_range(5000, 10000); + usleep_range(20000, 25000); } static int ov5640_set_power_on(struct ov5640_dev *sensor) From 27b795adb3c221eede4aaf0df4a3da694ca27ba3 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Thu, 7 Feb 2019 14:08:55 -0500 Subject: [PATCH 1941/4277] media: ipu3-imgu: Prefix functions with imgu_* instead of ipu3_* This addresses the below TODO item, no function related changes: - Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to imgu_v4l2_register(). (Sakari) The changes were obtained by applying the following perl script to driver code under drivers/staging/media/ipu3/. perl -pi.back -e 's/ipu3_(?!uapi)/imgu_/g;' Signed-off-by: Yong Zhi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/TODO | 3 - drivers/staging/media/ipu3/ipu3-css-fw.c | 18 +- drivers/staging/media/ipu3/ipu3-css-fw.h | 8 +- drivers/staging/media/ipu3/ipu3-css-params.c | 270 +++++------ drivers/staging/media/ipu3/ipu3-css-params.h | 8 +- drivers/staging/media/ipu3/ipu3-css-pool.c | 32 +- drivers/staging/media/ipu3/ipu3-css-pool.h | 30 +- drivers/staging/media/ipu3/ipu3-css.c | 454 +++++++++---------- drivers/staging/media/ipu3/ipu3-css.h | 92 ++-- drivers/staging/media/ipu3/ipu3-dmamap.c | 42 +- drivers/staging/media/ipu3/ipu3-dmamap.h | 14 +- drivers/staging/media/ipu3/ipu3-mmu.c | 120 ++--- drivers/staging/media/ipu3/ipu3-mmu.h | 18 +- drivers/staging/media/ipu3/ipu3-tables.c | 50 +- drivers/staging/media/ipu3/ipu3-tables.h | 54 +-- drivers/staging/media/ipu3/ipu3-v4l2.c | 290 ++++++------ drivers/staging/media/ipu3/ipu3.c | 86 ++-- drivers/staging/media/ipu3/ipu3.h | 18 +- 18 files changed, 802 insertions(+), 805 deletions(-) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index 0dc9a2e79978..8b95e74e43a0 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -8,9 +8,6 @@ staging directory. - Using ENABLED and IMMUTABLE link flags for the links where those are relevant. (Sakari) -- Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to - imgu_v4l2_register(). (Sakari) - - IPU3 driver documentation (Laurent) Add diagram in driver rst to describe output capability. Comments on configuring v4l2 subdevs for CIO2 and ImgU. diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c index 55861aa8fb03..4122d4e42db6 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.c +++ b/drivers/staging/media/ipu3/ipu3-css-fw.c @@ -10,7 +10,7 @@ #include "ipu3-css-fw.h" #include "ipu3-dmamap.h" -static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, +static void imgu_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, const char *name) { unsigned int i; @@ -54,7 +54,7 @@ static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, dev_dbg(dev, "\n"); } -unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) +unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi) { unsigned int width = DIV_ROUND_UP(bi->info.isp.sp.internal.max_width, IMGU_OBGRID_TILE_SIZE * 2) + 1; @@ -69,7 +69,7 @@ unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) return obgrid_size; } -void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, +void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, enum imgu_abi_param_class cls, enum imgu_abi_memories mem, struct imgu_fw_isp_parameter *par, @@ -91,7 +91,7 @@ void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, return binary_params + par->offset; } -void ipu3_css_fw_cleanup(struct ipu3_css *css) +void imgu_css_fw_cleanup(struct imgu_css *css) { struct imgu_device *imgu = dev_get_drvdata(css->dev); @@ -99,7 +99,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css) unsigned int i; for (i = 0; i < css->fwp->file_header.binary_nr; i++) - ipu3_dmamap_free(imgu, &css->binary[i]); + imgu_dmamap_free(imgu, &css->binary[i]); kfree(css->binary); } if (css->fw) @@ -109,7 +109,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css) css->fw = NULL; } -int ipu3_css_fw_init(struct ipu3_css *css) +int imgu_css_fw_init(struct imgu_css *css) { static const u32 BLOCK_MAX = 65536; struct imgu_device *imgu = dev_get_drvdata(css->dev); @@ -227,7 +227,7 @@ int ipu3_css_fw_init(struct ipu3_css *css) css->fw->size) goto bad_fw; - ipu3_css_fw_show_binary(dev, bi, name); + imgu_css_fw_show_binary(dev, bi, name); } if (css->fw_bl == -1 || css->fw_sp[0] == -1 || css->fw_sp[1] == -1) @@ -246,7 +246,7 @@ int ipu3_css_fw_init(struct ipu3_css *css) void *blob = (void *)css->fwp + bi->blob.offset; size_t size = bi->blob.size; - if (!ipu3_dmamap_alloc(imgu, &css->binary[i], size)) { + if (!imgu_dmamap_alloc(imgu, &css->binary[i], size)) { r = -ENOMEM; goto error_out; } @@ -260,6 +260,6 @@ bad_fw: r = -ENODEV; error_out: - ipu3_css_fw_cleanup(css); + imgu_css_fw_cleanup(css); return r; } diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h index 07d8bb8b25f3..79ffa7045139 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.h +++ b/drivers/staging/media/ipu3/ipu3-css-fw.h @@ -175,11 +175,11 @@ struct imgu_fw_header { /******************* Firmware functions *******************/ -int ipu3_css_fw_init(struct ipu3_css *css); -void ipu3_css_fw_cleanup(struct ipu3_css *css); +int imgu_css_fw_init(struct imgu_css *css); +void imgu_css_fw_cleanup(struct imgu_css *css); -unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi); -void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, +unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi); +void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, enum imgu_abi_param_class cls, enum imgu_abi_memories mem, struct imgu_fw_isp_parameter *par, diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 053edce54b71..4533dacad4be 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -14,7 +14,7 @@ #define IPU3_UAPI_ANR_MAX_RESET ((1 << 12) - 1) #define IPU3_UAPI_ANR_MIN_RESET (((-1) << 12) + 1) -struct ipu3_css_scaler_info { +struct imgu_css_scaler_info { unsigned int phase_step; /* Same for luma/chroma */ int exp_shift; @@ -25,7 +25,7 @@ struct ipu3_css_scaler_info { int crop_top; }; -static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, +static unsigned int imgu_css_scaler_get_exp(unsigned int counter, unsigned int divider) { int i = fls(divider) - fls(counter); @@ -41,13 +41,13 @@ static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, /* Set up the CSS scaler look up table */ static void -ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, +imgu_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, unsigned int output_width, int phase_step_correction, const int *coeffs, unsigned int coeffs_size, - s8 coeff_lut[], struct ipu3_css_scaler_info *info) + s8 coeff_lut[], struct imgu_css_scaler_info *info) { int tap, phase, phase_sum_left, phase_sum_right; - int exponent = ipu3_css_scaler_get_exp(output_width, input_width); + int exponent = imgu_css_scaler_get_exp(output_width, input_width); int mantissa = (1 << exponent) * output_width; unsigned int phase_step; @@ -114,8 +114,8 @@ ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, * (must be perfectly aligned with hardware). */ static unsigned int -ipu3_css_scaler_calc_scaled_output(unsigned int input, - struct ipu3_css_scaler_info *info) +imgu_css_scaler_calc_scaled_output(unsigned int input, + struct imgu_css_scaler_info *info) { unsigned int arg1 = input * info->phase_step + (1 - IMGU_SCALER_TAPS_Y / 2) * IMGU_SCALER_FIR_PHASES - @@ -133,10 +133,10 @@ ipu3_css_scaler_calc_scaled_output(unsigned int input, * and chroma details of a scaler */ static void -ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, +imgu_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, u32 target_height, struct imgu_abi_osys_config *cfg, - struct ipu3_css_scaler_info *info_luma, - struct ipu3_css_scaler_info *info_chroma, + struct imgu_css_scaler_info *info_luma, + struct imgu_css_scaler_info *info_chroma, unsigned int *output_width, unsigned int *output_height, unsigned int *procmode) { @@ -165,24 +165,24 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, do { phase_step_correction++; - ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, + imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, input_width, target_width, phase_step_correction, - ipu3_css_downscale_4taps, + imgu_css_downscale_4taps, IMGU_SCALER_DOWNSCALE_4TAPS_LEN, cfg->scaler_coeffs_luma, info_luma); - ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, + imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, input_width, target_width, phase_step_correction, - ipu3_css_downscale_2taps, + imgu_css_downscale_2taps, IMGU_SCALER_DOWNSCALE_2TAPS_LEN, cfg->scaler_coeffs_chroma, info_chroma); - out_width = ipu3_css_scaler_calc_scaled_output(input_width, + out_width = imgu_css_scaler_calc_scaled_output(input_width, info_luma); - out_height = ipu3_css_scaler_calc_scaled_output(input_height, + out_height = imgu_css_scaler_calc_scaled_output(input_height, info_luma); } while ((out_width < target_width || out_height < target_height || !IS_ALIGNED(out_height, height_alignment)) && @@ -194,7 +194,7 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, /********************** Osys routines for scaler****************************/ -static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, +static void imgu_css_osys_set_format(enum imgu_abi_frame_format host_format, unsigned int *osys_format, unsigned int *osys_tiling) { @@ -231,7 +231,7 @@ static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, * Function calculates input frame stripe offset, based * on output frame stripe offset and filter parameters. */ -static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, +static int imgu_css_osys_calc_stripe_offset(int stripe_offset_out, int fir_phases, int phase_init, int phase_step, int pad_left) { @@ -245,12 +245,12 @@ static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, * Calculate input frame phase, given the output frame * stripe offset and filter parameters */ -static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, +static int imgu_css_osys_calc_stripe_phase_init(int stripe_offset_out, int fir_phases, int phase_init, int phase_step, int pad_left) { int stripe_offset_inp = - ipu3_css_osys_calc_stripe_offset(stripe_offset_out, + imgu_css_osys_calc_stripe_offset(stripe_offset_out, fir_phases, phase_init, phase_step, pad_left); @@ -262,7 +262,7 @@ static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, * This function calculates input frame stripe width, * based on output frame stripe offset and filter parameters */ -static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, +static int imgu_css_osys_calc_inp_stripe_width(int stripe_width_out, int fir_phases, int phase_init, int phase_step, int fir_taps, int pad_left, int pad_right) @@ -279,7 +279,7 @@ static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, * This function calculates output frame stripe width, basedi * on output frame stripe offset and filter parameters */ -static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, +static int imgu_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, int phase_init, int phase_step, int fir_taps, int pad_left, int pad_right, int column_offset) @@ -292,7 +292,7 @@ static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, return stripe_width_out - (fir_taps - 1); } -struct ipu3_css_reso { +struct imgu_css_reso { unsigned int input_width; unsigned int input_height; enum imgu_abi_frame_format input_format; @@ -306,7 +306,7 @@ struct ipu3_css_reso { int block_width; }; -struct ipu3_css_frame_params { +struct imgu_css_frame_params { /* Output pins */ unsigned int enable; unsigned int format; @@ -322,7 +322,7 @@ struct ipu3_css_frame_params { unsigned int crop_top; }; -struct ipu3_css_stripe_params { +struct imgu_css_stripe_params { unsigned int processing_mode; unsigned int phase_step; unsigned int exp_shift; @@ -359,20 +359,20 @@ struct ipu3_css_stripe_params { * frame_params - size IMGU_ABI_OSYS_PINS * stripe_params - size IPU3_UAPI_MAX_STRIPES */ -static int ipu3_css_osys_calc_frame_and_stripe_params( - struct ipu3_css *css, unsigned int stripes, +static int imgu_css_osys_calc_frame_and_stripe_params( + struct imgu_css *css, unsigned int stripes, struct imgu_abi_osys_config *osys, - struct ipu3_css_scaler_info *scaler_luma, - struct ipu3_css_scaler_info *scaler_chroma, - struct ipu3_css_frame_params frame_params[], - struct ipu3_css_stripe_params stripe_params[], + struct imgu_css_scaler_info *scaler_luma, + struct imgu_css_scaler_info *scaler_chroma, + struct imgu_css_frame_params frame_params[], + struct imgu_css_stripe_params stripe_params[], unsigned int pipe) { - struct ipu3_css_reso reso; + struct imgu_css_reso reso; unsigned int output_width, pin, s; u32 input_width, input_height, target_width, target_height; unsigned int procmode = 0; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; input_width = css_pipe->rect[IPU3_CSS_RECT_GDC].width; input_height = css_pipe->rect[IPU3_CSS_RECT_GDC].height; @@ -464,7 +464,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( scaled = 1; } } - ipu3_css_osys_set_format(reso.pin_format[pin], &format, + imgu_css_osys_set_format(reso.pin_format[pin], &format, &tiling); } else { enable = 0; @@ -476,7 +476,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( frame_params[pin].scaled = scaled; } - ipu3_css_scaler_calc(input_width, input_height, target_width, + imgu_css_scaler_calc(input_width, input_height, target_width, target_height, osys, scaler_luma, scaler_chroma, &reso.pin_width[IMGU_ABI_OSYS_PIN_VF], &reso.pin_height[IMGU_ABI_OSYS_PIN_VF], &procmode); @@ -581,14 +581,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( stripe_offset_out_uv = stripe_offset_out_y / IMGU_LUMA_TO_CHROMA_RATIO; stripe_offset_inp_y = - ipu3_css_osys_calc_stripe_offset( + imgu_css_osys_calc_stripe_offset( stripe_offset_out_y, IMGU_OSYS_FIR_PHASES, scaler_luma->phase_init, scaler_luma->phase_step, scaler_luma->pad_left); stripe_offset_inp_uv = - ipu3_css_osys_calc_stripe_offset( + imgu_css_osys_calc_stripe_offset( stripe_offset_out_uv, IMGU_OSYS_FIR_PHASES, scaler_chroma->phase_init, @@ -597,14 +597,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( /* Calculate stripe phase init */ stripe_phase_init_y = - ipu3_css_osys_calc_stripe_phase_init( + imgu_css_osys_calc_stripe_phase_init( stripe_offset_out_y, IMGU_OSYS_FIR_PHASES, scaler_luma->phase_init, scaler_luma->phase_step, scaler_luma->pad_left); stripe_phase_init_uv = - ipu3_css_osys_calc_stripe_phase_init( + imgu_css_osys_calc_stripe_phase_init( stripe_offset_out_uv, IMGU_OSYS_FIR_PHASES, scaler_chroma->phase_init, @@ -708,7 +708,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( IMGU_LUMA_TO_CHROMA_RATIO; /* Calculate input stripe width */ stripe_input_width_y = stripe_offset_col_y + - ipu3_css_osys_calc_inp_stripe_width( + imgu_css_osys_calc_inp_stripe_width( stripe_output_width_y, IMGU_OSYS_FIR_PHASES, stripe_phase_init_y, @@ -718,7 +718,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( stripe_pad_right_y); stripe_input_width_uv = stripe_offset_col_uv + - ipu3_css_osys_calc_inp_stripe_width( + imgu_css_osys_calc_inp_stripe_width( stripe_output_width_uv, IMGU_OSYS_FIR_PHASES, stripe_phase_init_uv, @@ -753,7 +753,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( */ stripe_input_width_y = ALIGN(stripe_input_width_y, 8); stripe_output_width_y = - ipu3_css_osys_out_stripe_width( + imgu_css_osys_out_stripe_width( stripe_input_width_y, IMGU_OSYS_FIR_PHASES, stripe_phase_init_y, @@ -847,23 +847,23 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( * This function configures the Output Formatter System, given the number of * stripes, scaler luma and chrome parameters */ -static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_osys_calc(struct imgu_css *css, unsigned int pipe, unsigned int stripes, struct imgu_abi_osys_config *osys, - struct ipu3_css_scaler_info *scaler_luma, - struct ipu3_css_scaler_info *scaler_chroma, + struct imgu_css_scaler_info *scaler_luma, + struct imgu_css_scaler_info *scaler_chroma, struct imgu_abi_stripes block_stripes[]) { - struct ipu3_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; - struct ipu3_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; + struct imgu_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; + struct imgu_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; struct imgu_abi_osys_formatter_params *param; unsigned int pin, s; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; memset(osys, 0, sizeof(*osys)); /* Compute the frame and stripe params */ - if (ipu3_css_osys_calc_frame_and_stripe_params(css, stripes, osys, + if (imgu_css_osys_calc_frame_and_stripe_params(css, stripes, osys, scaler_luma, scaler_chroma, frame_params, @@ -1252,7 +1252,7 @@ static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, */ static int -ipu3_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, +imgu_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, const struct ipu3_uapi_shd_grid_config *grid, unsigned int image_height) { @@ -1496,7 +1496,7 @@ struct process_lines { /* Helper to config intra_frame_operations_data. */ static int -ipu3_css_acc_process_lines(const struct process_lines *pl, +imgu_css_acc_process_lines(const struct process_lines *pl, struct imgu_abi_acc_operation *p_op, struct imgu_abi_acc_process_lines_cmd_data *p_pl, struct imgu_abi_acc_transfer_op_data *p_tr) @@ -1633,12 +1633,12 @@ ipu3_css_acc_process_lines(const struct process_lines *pl, return 0; } -static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_af_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_af_config *af_config) { struct imgu_abi_af_intra_frame_operations_data *to = &af_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; @@ -1656,17 +1656,17 @@ static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.af, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, NULL); } static int -ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, +imgu_css_awb_fr_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_awb_fr_config *awb_fr_config) { struct imgu_abi_awb_fr_intra_frame_operations_data *to = &awb_fr_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct process_lines pl = { @@ -1683,16 +1683,16 @@ ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.awb_fr_acc, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, NULL); } -static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_awb_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_awb_config *awb_config) { struct imgu_abi_awb_intra_frame_operations_data *to = &awb_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; @@ -1709,33 +1709,33 @@ static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.awb_acc, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, to->transfer_data); } -static u16 ipu3_css_grid_end(u16 start, u8 width, u8 block_width_log2) +static u16 imgu_css_grid_end(u16 start, u8 width, u8 block_width_log2) { return (start & IPU3_UAPI_GRID_START_MASK) + (width << block_width_log2) - 1; } -static void ipu3_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) +static void imgu_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) { - grid_cfg->x_end = ipu3_css_grid_end(grid_cfg->x_start, grid_cfg->width, + grid_cfg->x_end = imgu_css_grid_end(grid_cfg->x_start, grid_cfg->width, grid_cfg->block_width_log2); - grid_cfg->y_end = ipu3_css_grid_end(grid_cfg->y_start, grid_cfg->height, + grid_cfg->y_end = imgu_css_grid_end(grid_cfg->y_start, grid_cfg->height, grid_cfg->block_height_log2); } /****************** config computation *****************************/ -static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, struct imgu_abi_acc_param *acc) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; - struct ipu3_css_scaler_info scaler_luma, scaler_chroma; + struct imgu_css_scaler_info scaler_luma, scaler_chroma; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; const unsigned int f = IPU3_UAPI_ISP_VEC_ELEMS * 2; unsigned int bds_ds, i; @@ -1744,7 +1744,7 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, /* acc_param: osys_config */ - if (ipu3_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, + if (imgu_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, &scaler_chroma, acc->stripe.block_stripes)) return -EINVAL; @@ -1901,12 +1901,12 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, return 0; } -static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, +static void imgu_css_cfg_acc_dvs(struct imgu_css *css, struct imgu_abi_acc_param *acc, unsigned int pipe) { unsigned int i; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; /* Disable DVS statistics */ acc->dvs_stat.operations_data.process_lines_data[0].lines = @@ -1920,11 +1920,11 @@ static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, acc->dvs_stat.cfg.grd_config[i].enable = 0; } -static void acc_bds_per_stripe_data(struct ipu3_css *css, +static void acc_bds_per_stripe_data(struct imgu_css *css, struct imgu_abi_acc_param *acc, const int i, unsigned int pipe) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_en = 0; acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_start = 0; @@ -1945,13 +1945,13 @@ static void acc_bds_per_stripe_data(struct ipu3_css *css, * telling which fields to take from the old values (or generate if it is NULL) * and which to take from the new user values. */ -int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, struct imgu_abi_acc_param *acc, struct imgu_abi_acc_param *acc_old, struct ipu3_uapi_acc_param *acc_user) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; @@ -1960,7 +1960,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, const unsigned int min_overlap = 10; const struct v4l2_pix_format_mplane *pixm = &css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix; - const struct ipu3_css_bds_config *cfg_bds; + const struct imgu_css_bds_config *cfg_bds; struct imgu_abi_input_feeder_data *feeder_data; unsigned int bds_ds, ofs_x, ofs_y, i, width, height; @@ -1968,7 +1968,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, /* Update stripe using chroma and luma */ - if (ipu3_css_cfg_acc_stripe(css, pipe, acc)) + if (imgu_css_cfg_acc_stripe(css, pipe, acc)) return -EINVAL; /* acc_param: input_feeder_config */ @@ -2022,7 +2022,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->bnr = acc_old->bnr; } else { /* Calculate from scratch */ - acc->bnr = ipu3_css_bnr_defaults; + acc->bnr = imgu_css_bnr_defaults; } acc->bnr.column_size = tnr_frame_width; @@ -2050,7 +2050,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->dm = acc_old->dm; } else { /* Calculate from scratch */ - acc->dm = ipu3_css_dm_defaults; + acc->dm = imgu_css_dm_defaults; } acc->dm.frame_width = tnr_frame_width; @@ -2065,7 +2065,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->ccm = acc_old->ccm; } else { /* Calculate from scratch */ - acc->ccm = ipu3_css_ccm_defaults; + acc->ccm = imgu_css_ccm_defaults; } /* acc_param: gamma_config */ @@ -2079,7 +2079,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, } else { /* Calculate from scratch */ acc->gamma.gc_ctrl.enable = 1; - acc->gamma.gc_lut = ipu3_css_gamma_lut; + acc->gamma.gc_lut = imgu_css_gamma_lut; } /* acc_param: csc_mat_config */ @@ -2092,7 +2092,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->csc = acc_old->csc; } else { /* Calculate from scratch */ - acc->csc = ipu3_css_csc_defaults; + acc->csc = imgu_css_csc_defaults; } /* acc_param: cds_params */ @@ -2105,7 +2105,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->cds = acc_old->cds; } else { /* Calculate from scratch */ - acc->cds = ipu3_css_cds_defaults; + acc->cds = imgu_css_cds_defaults; } /* acc_param: shd_config */ @@ -2120,7 +2120,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->shd.shd_lut = acc_old->shd.shd_lut; } else { /* Calculate from scratch */ - acc->shd.shd = ipu3_css_shd_defaults; + acc->shd.shd = imgu_css_shd_defaults; memset(&acc->shd.shd_lut, 0, sizeof(acc->shd.shd_lut)); } @@ -2138,12 +2138,12 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->shd.shd.grid.block_height_log2) % acc->shd.shd.grid.grid_height_per_slice; - if (ipu3_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, + if (imgu_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, css_pipe->rect[IPU3_CSS_RECT_BDS].height)) return -EINVAL; /* acc_param: dvs_stat_config */ - ipu3_css_cfg_acc_dvs(css, acc, pipe); + imgu_css_cfg_acc_dvs(css, acc, pipe); /* acc_param: yuvp1_iefd_config */ @@ -2155,7 +2155,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->iefd = acc_old->iefd; } else { /* Calculate from scratch */ - acc->iefd = ipu3_css_iefd_defaults; + acc->iefd = imgu_css_iefd_defaults; } /* acc_param: yuvp1_yds_config yds_c0 */ @@ -2168,7 +2168,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds_c0 = acc_old->yds_c0; } else { /* Calculate from scratch */ - acc->yds_c0 = ipu3_css_yds_defaults; + acc->yds_c0 = imgu_css_yds_defaults; } /* acc_param: yuvp1_chnr_config chnr_c0 */ @@ -2181,7 +2181,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->chnr_c0 = acc_old->chnr_c0; } else { /* Calculate from scratch */ - acc->chnr_c0 = ipu3_css_chnr_defaults; + acc->chnr_c0 = imgu_css_chnr_defaults; } /* acc_param: yuvp1_y_ee_nr_config */ @@ -2194,7 +2194,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->y_ee_nr = acc_old->y_ee_nr; } else { /* Calculate from scratch */ - acc->y_ee_nr = ipu3_css_y_ee_nr_defaults; + acc->y_ee_nr = imgu_css_y_ee_nr_defaults; } /* acc_param: yuvp1_yds_config yds */ @@ -2207,7 +2207,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds = acc_old->yds; } else { /* Calculate from scratch */ - acc->yds = ipu3_css_yds_defaults; + acc->yds = imgu_css_yds_defaults; } /* acc_param: yuvp1_chnr_config chnr */ @@ -2220,7 +2220,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->chnr = acc_old->chnr; } else { /* Calculate from scratch */ - acc->chnr = ipu3_css_chnr_defaults; + acc->chnr = imgu_css_chnr_defaults; } /* acc_param: yuvp2_y_tm_lut_static_config */ @@ -2239,7 +2239,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds2 = acc_old->yds2; } else { /* Calculate from scratch */ - acc->yds2 = ipu3_css_yds_defaults; + acc->yds2 = imgu_css_yds_defaults; } /* acc_param: yuvp2_tcc_static_config */ @@ -2271,8 +2271,8 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, for (i = 7; i < IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS; i++) acc->tcc.inv_y_lut.entries[i] = 1024 >> (i - 6); - acc->tcc.gain_pcwl = ipu3_css_tcc_gain_pcwl_lut; - acc->tcc.r_sqr_lut = ipu3_css_tcc_r_sqr_lut; + acc->tcc.gain_pcwl = imgu_css_tcc_gain_pcwl_lut; + acc->tcc.r_sqr_lut = imgu_css_tcc_r_sqr_lut; } /* acc_param: dpc_config */ @@ -2288,10 +2288,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, bds_ds = (css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height * IMGU_BDS_GRANULARITY) / css_pipe->rect[IPU3_CSS_RECT_BDS].height; if (bds_ds < IMGU_BDS_MIN_SF_INV || - bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(ipu3_css_bds_configs)) + bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(imgu_css_bds_configs)) return -EINVAL; - cfg_bds = &ipu3_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; + cfg_bds = &imgu_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; acc->bds.hor.hor_ctrl1.hor_crop_en = 0; acc->bds.hor.hor_ctrl1.hor_crop_start = 0; acc->bds.hor.hor_ctrl1.hor_crop_end = 0; @@ -2340,7 +2340,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, sizeof(acc->anr.stitch.pyramid)); } else { /* Calculate from scratch */ - acc->anr = ipu3_css_anr_defaults; + acc->anr = imgu_css_anr_defaults; } /* Always enabled */ @@ -2378,10 +2378,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.config = acc_old->awb_fr.config; } else { /* Set from scratch */ - acc->awb_fr.config = ipu3_css_awb_fr_defaults; + acc->awb_fr.config = imgu_css_awb_fr_defaults; } - ipu3_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); + imgu_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); if (acc->awb_fr.config.grid_cfg.width <= 0) return -EINVAL; @@ -2416,7 +2416,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.stripes[0].grid_cfg.width; b_w_log2 = acc->awb_fr.stripes[0].grid_cfg.block_width_log2; - end = ipu3_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, + end = imgu_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, acc->awb_fr.stripes[0].grid_cfg.width, b_w_log2); acc->awb_fr.stripes[0].grid_cfg.x_end = end; @@ -2426,7 +2426,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->stripe.down_scaled_stripes[1].offset) & IPU3_UAPI_GRID_START_MASK; b_w_log2 = acc->awb_fr.stripes[1].grid_cfg.block_width_log2; - end = ipu3_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, + end = imgu_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, acc->awb_fr.stripes[1].grid_cfg.width, b_w_log2); acc->awb_fr.stripes[1].grid_cfg.x_end = end; @@ -2440,7 +2440,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.stripes[i].grid_cfg.height_per_slice = 1; } - if (ipu3_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) + if (imgu_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) return -EINVAL; /* acc_param: ae_config */ @@ -2462,18 +2462,18 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, static const struct ipu3_uapi_ae_weight_elem weight_def = { 1, 1, 1, 1, 1, 1, 1, 1 }; - acc->ae.grid_cfg = ipu3_css_ae_grid_defaults; - acc->ae.ae_ccm = ipu3_css_ae_ccm_defaults; + acc->ae.grid_cfg = imgu_css_ae_grid_defaults; + acc->ae.ae_ccm = imgu_css_ae_ccm_defaults; for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++) acc->ae.weights[i] = weight_def; } b_w_log2 = acc->ae.grid_cfg.block_width_log2; - acc->ae.grid_cfg.x_end = ipu3_css_grid_end(acc->ae.grid_cfg.x_start, + acc->ae.grid_cfg.x_end = imgu_css_grid_end(acc->ae.grid_cfg.x_start, acc->ae.grid_cfg.width, b_w_log2); b_w_log2 = acc->ae.grid_cfg.block_height_log2; - acc->ae.grid_cfg.y_end = ipu3_css_grid_end(acc->ae.grid_cfg.y_start, + acc->ae.grid_cfg.y_end = imgu_css_grid_end(acc->ae.grid_cfg.y_start, acc->ae.grid_cfg.height, b_w_log2); @@ -2502,7 +2502,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->ae.stripes[0].grid.block_width_log2; acc->ae.stripes[0].grid.x_end = - ipu3_css_grid_end(acc->ae.stripes[0].grid.x_start, + imgu_css_grid_end(acc->ae.stripes[0].grid.x_start, acc->ae.stripes[0].grid.width, b_w_log2); @@ -2512,7 +2512,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, IPU3_UAPI_GRID_START_MASK; b_w_log2 = acc->ae.stripes[1].grid.block_width_log2; acc->ae.stripes[1].grid.x_end = - ipu3_css_grid_end(acc->ae.stripes[1].grid.x_start, + imgu_css_grid_end(acc->ae.stripes[1].grid.x_start, acc->ae.stripes[1].grid.width, b_w_log2); } @@ -2529,11 +2529,11 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, } else { /* Set from scratch */ acc->af.config.filter_config = - ipu3_css_af_defaults.filter_config; - acc->af.config.grid_cfg = ipu3_css_af_defaults.grid_cfg; + imgu_css_af_defaults.filter_config; + acc->af.config.grid_cfg = imgu_css_af_defaults.grid_cfg; } - ipu3_css_grid_end_calc(&acc->af.config.grid_cfg); + imgu_css_grid_end_calc(&acc->af.config.grid_cfg); if (acc->af.config.grid_cfg.width <= 0) return -EINVAL; @@ -2579,7 +2579,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->af.stripes[0].grid_cfg.block_width_log2; acc->af.stripes[0].grid_cfg.x_end = - ipu3_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, + imgu_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, acc->af.stripes[0].grid_cfg.width, b_w_log2); @@ -2590,7 +2590,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->af.stripes[1].grid_cfg.block_width_log2; acc->af.stripes[1].grid_cfg.x_end = - ipu3_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, + imgu_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, acc->af.stripes[1].grid_cfg.width, b_w_log2); @@ -2602,7 +2602,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->af.stripes[i].grid_cfg.height_per_slice = 1; } - if (ipu3_css_af_ops_calc(css, pipe, &acc->af)) + if (imgu_css_af_ops_calc(css, pipe, &acc->af)) return -EINVAL; /* acc_param: awb_config */ @@ -2615,7 +2615,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.config = acc_old->awb.config; } else { /* Set from scratch */ - acc->awb.config = ipu3_css_awb_defaults; + acc->awb.config = imgu_css_awb_defaults; } if (acc->awb.config.grid.width <= 0) @@ -2623,7 +2623,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.config.grid.height_per_slice = IMGU_ABI_AWB_MAX_CELLS_PER_SET / acc->awb.config.grid.width, - ipu3_css_grid_end_calc(&acc->awb.config.grid); + imgu_css_grid_end_calc(&acc->awb.config.grid); for (i = 0; i < stripes; i++) acc->awb.stripes[i] = acc->awb.config; @@ -2648,7 +2648,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->awb.stripes[0].grid.block_width_log2; acc->awb.stripes[0].grid.x_end = - ipu3_css_grid_end(acc->awb.stripes[0].grid.x_start, + imgu_css_grid_end(acc->awb.stripes[0].grid.x_start, acc->awb.stripes[0].grid.width, b_w_log2); @@ -2659,7 +2659,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->awb.stripes[1].grid.block_width_log2; acc->awb.stripes[1].grid.x_end = - ipu3_css_grid_end(acc->awb.stripes[1].grid.x_start, + imgu_css_grid_end(acc->awb.stripes[1].grid.x_start, acc->awb.stripes[1].grid.width, b_w_log2); @@ -2671,7 +2671,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.stripes[i].grid.height_per_slice = 1; } - if (ipu3_css_awb_ops_calc(css, pipe, &acc->awb)) + if (imgu_css_awb_ops_calc(css, pipe, &acc->awb)) return -EINVAL; return 0; @@ -2686,7 +2686,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, * to the structure inside `new_binary_params'. In that case the caller * should calculate and fill the structure from scratch. */ -static void *ipu3_css_cfg_copy(struct ipu3_css *css, +static void *imgu_css_cfg_copy(struct imgu_css *css, unsigned int pipe, bool use_user, void *user_setting, void *old_binary_params, void *new_binary_params, @@ -2697,7 +2697,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM; void *new_setting, *old_setting; - new_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + new_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par, par_size, new_binary_params); if (!new_setting) return ERR_PTR(-EPROTO); /* Corrupted firmware */ @@ -2707,7 +2707,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, memcpy(new_setting, user_setting, par_size); } else if (old_binary_params) { /* Take previous value */ - old_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + old_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par, par_size, old_binary_params); if (!old_setting) @@ -2723,7 +2723,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, /* * Configure VMEM0 parameters (late binding parameters). */ -int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *vmem0, void *vmem0_old, struct ipu3_uapi_params *user) @@ -2745,7 +2745,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure Linearization VMEM0 parameters */ - lin_vmem = ipu3_css_cfg_copy(css, pipe, use && use->lin_vmem_params, + lin_vmem = imgu_css_cfg_copy(css, pipe, use && use->lin_vmem_params, &user->lin_vmem_params, vmem0_old, vmem0, m, &pofs->vmem.lin, sizeof(*lin_vmem)); if (!IS_ERR_OR_NULL(lin_vmem)) { @@ -2765,7 +2765,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure TNR3 VMEM parameters */ if (css->pipes[pipe].pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - tnr_vmem = ipu3_css_cfg_copy(css, pipe, + tnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->tnr3_vmem_params, &user->tnr3_vmem_params, vmem0_old, vmem0, m, @@ -2781,17 +2781,17 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure XNR3 VMEM parameters */ - xnr_vmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, + xnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, &user->xnr3_vmem_params, vmem0_old, vmem0, m, &pofs->vmem.xnr3, sizeof(*xnr_vmem)); if (!IS_ERR_OR_NULL(xnr_vmem)) { - xnr_vmem->x[i] = ipu3_css_xnr3_vmem_defaults.x + xnr_vmem->x[i] = imgu_css_xnr3_vmem_defaults.x [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->a[i] = ipu3_css_xnr3_vmem_defaults.a + xnr_vmem->a[i] = imgu_css_xnr3_vmem_defaults.a [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->b[i] = ipu3_css_xnr3_vmem_defaults.b + xnr_vmem->b[i] = imgu_css_xnr3_vmem_defaults.b [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->c[i] = ipu3_css_xnr3_vmem_defaults.c + xnr_vmem->c[i] = imgu_css_xnr3_vmem_defaults.c [i % IMGU_XNR3_VMEM_LUT_LEN]; } @@ -2802,12 +2802,12 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* * Configure DMEM0 parameters (late binding parameters). */ -int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *dmem0, void *dmem0_old, struct ipu3_uapi_params *user) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct imgu_fw_param_memory_offsets *pofs = (void *)css->fwp + @@ -2825,7 +2825,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, /* Configure TNR3 DMEM0 parameters */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - tnr_dmem = ipu3_css_cfg_copy(css, pipe, + tnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->tnr3_dmem_params, &user->tnr3_dmem_params, dmem0_old, dmem0, m, @@ -2840,7 +2840,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, /* Configure XNR3 DMEM0 parameters */ - xnr_dmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, + xnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, &user->xnr3_dmem_params, dmem0_old, dmem0, m, &pofs->dmem.xnr3, sizeof(*xnr_dmem)); if (!IS_ERR_OR_NULL(xnr_dmem)) { @@ -2854,7 +2854,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, } /* Generate unity morphing table without morphing effect */ -void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, +void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, int frame_in_x, int frame_in_y, int frame_out_x, int frame_out_y, int env_w, int env_h) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.h b/drivers/staging/media/ipu3/ipu3-css-params.h index f3a0a47117a4..ffaec6b7d5cc 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.h +++ b/drivers/staging/media/ipu3/ipu3-css-params.h @@ -4,23 +4,23 @@ #ifndef __IPU3_PARAMS_H #define __IPU3_PARAMS_H -int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, struct imgu_abi_acc_param *acc, struct imgu_abi_acc_param *acc_old, struct ipu3_uapi_acc_param *acc_user); -int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *vmem0, void *vmem0_old, struct ipu3_uapi_params *user); -int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *dmem0, void *dmem0_old, struct ipu3_uapi_params *user); -void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, +void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, int frame_in_x, int frame_in_y, int frame_out_x, int frame_out_y, int env_w, int env_h); diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.c b/drivers/staging/media/ipu3/ipu3-css-pool.c index 6f271f81669b..fa5b7d3acef2 100644 --- a/drivers/staging/media/ipu3/ipu3-css-pool.c +++ b/drivers/staging/media/ipu3/ipu3-css-pool.c @@ -7,30 +7,30 @@ #include "ipu3-css-pool.h" #include "ipu3-dmamap.h" -int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, - struct ipu3_css_map *map, size_t size) +int imgu_css_dma_buffer_resize(struct imgu_device *imgu, + struct imgu_css_map *map, size_t size) { if (map->size < size && map->vaddr) { dev_warn(&imgu->pci_dev->dev, "dma buf resized from %zu to %zu", map->size, size); - ipu3_dmamap_free(imgu, map); - if (!ipu3_dmamap_alloc(imgu, map, size)) + imgu_dmamap_free(imgu, map); + if (!imgu_dmamap_alloc(imgu, map, size)) return -ENOMEM; } return 0; } -void ipu3_css_pool_cleanup(struct imgu_device *imgu, struct ipu3_css_pool *pool) +void imgu_css_pool_cleanup(struct imgu_device *imgu, struct imgu_css_pool *pool) { unsigned int i; for (i = 0; i < IPU3_CSS_POOL_SIZE; i++) - ipu3_dmamap_free(imgu, &pool->entry[i].param); + imgu_dmamap_free(imgu, &pool->entry[i].param); } -int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, +int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool, size_t size) { unsigned int i; @@ -42,7 +42,7 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, continue; } - if (!ipu3_dmamap_alloc(imgu, &pool->entry[i].param, size)) + if (!imgu_dmamap_alloc(imgu, &pool->entry[i].param, size)) goto fail; } @@ -51,14 +51,14 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, return 0; fail: - ipu3_css_pool_cleanup(imgu, pool); + imgu_css_pool_cleanup(imgu, pool); return -ENOMEM; } /* * Allocate a new parameter via recycling the oldest entry in the pool. */ -void ipu3_css_pool_get(struct ipu3_css_pool *pool) +void imgu_css_pool_get(struct imgu_css_pool *pool) { /* Get the oldest entry */ u32 n = (pool->last + 1) % IPU3_CSS_POOL_SIZE; @@ -70,25 +70,25 @@ void ipu3_css_pool_get(struct ipu3_css_pool *pool) /* * Undo, for all practical purposes, the effect of pool_get(). */ -void ipu3_css_pool_put(struct ipu3_css_pool *pool) +void imgu_css_pool_put(struct imgu_css_pool *pool) { pool->entry[pool->last].valid = false; pool->last = (pool->last + IPU3_CSS_POOL_SIZE - 1) % IPU3_CSS_POOL_SIZE; } /** - * ipu3_css_pool_last - Retrieve the nth pool entry from last + * imgu_css_pool_last - Retrieve the nth pool entry from last * - * @pool: a pointer to &struct ipu3_css_pool. + * @pool: a pointer to &struct imgu_css_pool. * @n: the distance to the last index. * * Returns: * The nth entry from last or null map to indicate no frame stored. */ -const struct ipu3_css_map * -ipu3_css_pool_last(struct ipu3_css_pool *pool, unsigned int n) +const struct imgu_css_map * +imgu_css_pool_last(struct imgu_css_pool *pool, unsigned int n) { - static const struct ipu3_css_map null_map = { 0 }; + static const struct imgu_css_map null_map = { 0 }; int i = (pool->last + IPU3_CSS_POOL_SIZE - n) % IPU3_CSS_POOL_SIZE; WARN_ON(n >= IPU3_CSS_POOL_SIZE); diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.h b/drivers/staging/media/ipu3/ipu3-css-pool.h index 2657c39a4d71..f4a60b41401b 100644 --- a/drivers/staging/media/ipu3/ipu3-css-pool.h +++ b/drivers/staging/media/ipu3/ipu3-css-pool.h @@ -10,15 +10,15 @@ struct imgu_device; #define IPU3_CSS_POOL_SIZE 4 /** - * ipu3_css_map - store DMA mapping info for buffer + * imgu_css_map - store DMA mapping info for buffer * * @size: size of the buffer in bytes. * @vaddr: kernel virtual address. * @daddr: iova dma address to access IPU3. * @vma: private, a pointer to &struct vm_struct, - * used for ipu3_dmamap_free. + * used for imgu_dmamap_free. */ -struct ipu3_css_map { +struct imgu_css_map { size_t size; void *vaddr; dma_addr_t daddr; @@ -26,30 +26,30 @@ struct ipu3_css_map { }; /** - * ipu3_css_pool - circular buffer pool definition + * imgu_css_pool - circular buffer pool definition * * @entry: array with IPU3_CSS_POOL_SIZE elements. - * @entry.param: a &struct ipu3_css_map for storing the mem mapping. + * @entry.param: a &struct imgu_css_map for storing the mem mapping. * @entry.valid: used to mark if the entry has valid data. * @last: write pointer, initialized to IPU3_CSS_POOL_SIZE. */ -struct ipu3_css_pool { +struct imgu_css_pool { struct { - struct ipu3_css_map param; + struct imgu_css_map param; bool valid; } entry[IPU3_CSS_POOL_SIZE]; u32 last; }; -int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, - struct ipu3_css_map *map, size_t size); -void ipu3_css_pool_cleanup(struct imgu_device *imgu, - struct ipu3_css_pool *pool); -int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, +int imgu_css_dma_buffer_resize(struct imgu_device *imgu, + struct imgu_css_map *map, size_t size); +void imgu_css_pool_cleanup(struct imgu_device *imgu, + struct imgu_css_pool *pool); +int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool, size_t size); -void ipu3_css_pool_get(struct ipu3_css_pool *pool); -void ipu3_css_pool_put(struct ipu3_css_pool *pool); -const struct ipu3_css_map *ipu3_css_pool_last(struct ipu3_css_pool *pool, +void imgu_css_pool_get(struct imgu_css_pool *pool); +void imgu_css_pool_put(struct imgu_css_pool *pool); +const struct imgu_css_map *imgu_css_pool_last(struct imgu_css_pool *pool, u32 last); #endif diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index b9354d2bb692..15ab77e4b766 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -46,7 +46,7 @@ IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_VF) /* Formats supported by IPU3 Camera Sub System */ -static const struct ipu3_css_format ipu3_css_formats[] = { +static const struct imgu_css_format imgu_css_formats[] = { { .pixelformat = V4L2_PIX_FMT_NV12, .colorspace = V4L2_COLORSPACE_SRGB, @@ -100,7 +100,7 @@ static const struct ipu3_css_format ipu3_css_formats[] = { static const struct { enum imgu_abi_queue_id qid; size_t ptr_ofs; -} ipu3_css_queues[IPU3_CSS_QUEUES] = { +} imgu_css_queues[IPU3_CSS_QUEUES] = { [IPU3_CSS_QUEUE_IN] = { IMGU_ABI_QUEUE_C_ID, offsetof(struct imgu_abi_buffer, payload.frame.frame_data) @@ -120,7 +120,7 @@ static const struct { }; /* Initialize queue based on given format, adjust format as needed */ -static int ipu3_css_queue_init(struct ipu3_css_queue *queue, +static int imgu_css_queue_init(struct imgu_css_queue *queue, struct v4l2_pix_format_mplane *fmt, u32 flags) { struct v4l2_pix_format_mplane *const f = &queue->fmt.mpix; @@ -133,11 +133,11 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue, if (!fmt) return 0; - for (i = 0; i < ARRAY_SIZE(ipu3_css_formats); i++) { - if (!(ipu3_css_formats[i].flags & flags)) + for (i = 0; i < ARRAY_SIZE(imgu_css_formats); i++) { + if (!(imgu_css_formats[i].flags & flags)) continue; - queue->css_fmt = &ipu3_css_formats[i]; - if (ipu3_css_formats[i].pixelformat == fmt->pixelformat) + queue->css_fmt = &imgu_css_formats[i]; + if (imgu_css_formats[i].pixelformat == fmt->pixelformat) break; } if (!queue->css_fmt) @@ -178,7 +178,7 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue, return 0; } -static bool ipu3_css_queue_enabled(struct ipu3_css_queue *q) +static bool imgu_css_queue_enabled(struct imgu_css_queue *q) { return q->css_fmt; } @@ -200,7 +200,7 @@ static inline void writes(const void *mem, ssize_t count, void __iomem *addr) } /* Wait until register `reg', masked with `mask', becomes `cmp' */ -static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) +static int imgu_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) { u32 val; @@ -210,7 +210,7 @@ static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) /* Initialize the IPU3 CSS hardware and associated h/w blocks */ -int ipu3_css_set_powerup(struct device *dev, void __iomem *base) +int imgu_css_set_powerup(struct device *dev, void __iomem *base) { static const unsigned int freq = 450; u32 pm_ctrl, state, val; @@ -221,7 +221,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(0, base + IMGU_REG_GP_BUSY); /* Wait for idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) { dev_err(dev, "failed to set CSS idle\n"); goto fail; @@ -245,7 +245,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) if (state & IMGU_STATE_POWER_DOWN) { writel(IMGU_PM_CTRL_RACE_TO_HALT | IMGU_PM_CTRL_START, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, + if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { dev_err(dev, "failed to power up CSS\n"); goto fail; @@ -263,7 +263,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) val = pm_ctrl & ~(IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF); writel(val, base + IMGU_REG_PM_CTRL); writel(0, base + IMGU_REG_GP_BUSY); - if (ipu3_hw_wait(base, IMGU_REG_STATE, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_PWRDNM_FSM_MASK, 0)) { dev_err(dev, "failed to pwrdn CSS\n"); goto fail; @@ -273,7 +273,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(1, base + IMGU_REG_GP_BUSY); writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_HALT, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, IMGU_STATE_HALT_STS)) { dev_err(dev, "failed to halt CSS\n"); goto fail; @@ -281,7 +281,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_START, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { + if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { dev_err(dev, "failed to start CSS\n"); goto fail; } @@ -296,26 +296,26 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) return 0; fail: - ipu3_css_set_powerdown(dev, base); + imgu_css_set_powerdown(dev, base); return -EIO; } -void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) +void imgu_css_set_powerdown(struct device *dev, void __iomem *base) { dev_dbg(dev, "%s\n", __func__); /* wait for cio idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, + if (imgu_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, IMGU_CIO_GATE_BURST_MASK, 0)) dev_warn(dev, "wait cio gate idle timeout"); /* wait for css idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) dev_warn(dev, "wait css idle timeout\n"); /* do halt-halted handshake with css */ writel(1, base + IMGU_REG_GP_HALT); - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, IMGU_STATE_HALT_STS)) dev_warn(dev, "failed to halt css"); @@ -323,7 +323,7 @@ void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) writel(0, base + IMGU_REG_GP_BUSY); } -static void ipu3_css_hw_enable_irq(struct ipu3_css *css) +static void imgu_css_hw_enable_irq(struct imgu_css *css) { void __iomem *const base = css->base; u32 val, i; @@ -371,7 +371,7 @@ static void ipu3_css_hw_enable_irq(struct ipu3_css *css) } } -static int ipu3_css_hw_init(struct ipu3_css *css) +static int imgu_css_hw_init(struct imgu_css *css) { /* For checking that streaming monitor statuses are valid */ static const struct { @@ -463,11 +463,11 @@ static int ipu3_css_hw_init(struct ipu3_css *css) /* Initialize GDC with default values */ - for (i = 0; i < ARRAY_SIZE(ipu3_css_gdc_lut[0]); i++) { - u32 val0 = ipu3_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; - u32 val1 = ipu3_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; - u32 val2 = ipu3_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; - u32 val3 = ipu3_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; + for (i = 0; i < ARRAY_SIZE(imgu_css_gdc_lut[0]); i++) { + u32 val0 = imgu_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; + u32 val1 = imgu_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; + u32 val2 = imgu_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; + u32 val3 = imgu_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; writel(val0 | (val1 << 16), base + IMGU_REG_GDC_LUT_BASE + i * 8); @@ -479,7 +479,7 @@ static int ipu3_css_hw_init(struct ipu3_css *css) } /* Boot the given IPU3 CSS SP */ -static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) +static int imgu_css_hw_start_sp(struct imgu_css *css, int sp) { void __iomem *const base = css->base; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; @@ -501,7 +501,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) writel(readl(base + IMGU_REG_SP_CTRL(sp)) | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_SP_CTRL(sp)); - if (ipu3_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + if (imgu_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + bi->info.sp.sw_state, ~0, IMGU_ABI_SP_SWSTATE_INITIALIZED)) return -EIO; @@ -510,7 +510,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) } /* Start the IPU3 CSS ImgU (Imaging Unit) and all the SPs */ -static int ipu3_css_hw_start(struct ipu3_css *css) +static int imgu_css_hw_start(struct imgu_css *css) { static const u32 event_mask = ((1 << IMGU_ABI_EVTTYPE_OUT_FRAME_DONE) | @@ -560,7 +560,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) writel(readl(base + IMGU_REG_ISP_CTRL) | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_ISP_CTRL); - if (ipu3_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + if (imgu_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + bl->info.bl.sw_state, ~0, IMGU_ABI_BL_SWSTATE_OK)) { dev_err(css->dev, "failed to start bootloader\n"); @@ -581,7 +581,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state); writel(1, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.invalidate_tlb); - if (ipu3_css_hw_start_sp(css, 0)) + if (imgu_css_hw_start_sp(css, 0)) return -EIO; writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.isp_started); @@ -608,7 +608,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) writel(IMGU_ABI_SP_SWSTATE_TERMINATED, base + IMGU_REG_SP_DMEM_BASE(1) + bi->info.sp.sw_state); - if (ipu3_css_hw_start_sp(css, 1)) + if (imgu_css_hw_start_sp(css, 1)) return -EIO; writel(IMGU_ABI_SP_COMM_COMMAND_READY, base + IMGU_REG_SP_DMEM_BASE(1) @@ -617,7 +617,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) return 0; } -static void ipu3_css_hw_stop(struct ipu3_css *css) +static void imgu_css_hw_stop(struct imgu_css *css) { void __iomem *const base = css->base; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; @@ -626,18 +626,18 @@ static void ipu3_css_hw_stop(struct ipu3_css *css) writel(IMGU_ABI_SP_COMM_COMMAND_TERMINATE, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND); - if (ipu3_hw_wait(css->base, IMGU_REG_SP_CTRL(0), + if (imgu_hw_wait(css->base, IMGU_REG_SP_CTRL(0), IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) dev_err(css->dev, "wait sp0 idle timeout.\n"); if (readl(base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state) != IMGU_ABI_SP_SWSTATE_TERMINATED) dev_err(css->dev, "sp0 is not terminated.\n"); - if (ipu3_hw_wait(css->base, IMGU_REG_ISP_CTRL, + if (imgu_hw_wait(css->base, IMGU_REG_ISP_CTRL, IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) dev_err(css->dev, "wait isp idle timeout\n"); } -static void ipu3_css_hw_cleanup(struct ipu3_css *css) +static void imgu_css_hw_cleanup(struct imgu_css *css) { void __iomem *const base = css->base; @@ -648,7 +648,7 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css) writel(0, base + IMGU_REG_GP_BUSY); /* Wait for idle signal */ - if (ipu3_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) dev_err(css->dev, "failed to shut down hw cleanly\n"); @@ -659,19 +659,19 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css) usleep_range(200, 300); } -static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_pipeline_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i; - ipu3_css_pool_cleanup(imgu, + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.parameter_set_info); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - ipu3_css_pool_cleanup(imgu, + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.binary_params_p[i]); } @@ -679,7 +679,7 @@ static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) * This function initializes various stages of the * IPU3 CSS ISP pipeline */ -static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_pipeline_init(struct imgu_css *css, unsigned int pipe) { static const int BYPC = 2; /* Bytes per component */ static const struct imgu_abi_buffer_sp buffer_sp_init = { @@ -697,7 +697,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) const int stage = 0; unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; @@ -725,7 +725,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure iterator */ - cfg_iter = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_iter = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.iterator, sizeof(*cfg_iter), vaddr); if (!cfg_iter) @@ -791,7 +791,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure reference (delay) frames */ - cfg_ref = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_ref = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.ref, sizeof(*cfg_ref), vaddr); if (!cfg_ref) @@ -821,7 +821,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure DVS (digital video stabilization) */ - cfg_dvs = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_dvs = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.dvs, sizeof(*cfg_dvs), vaddr); if (!cfg_dvs) @@ -837,7 +837,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure TNR (temporal noise reduction) */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - cfg_tnr = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_tnr = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.tnr3, sizeof(*cfg_tnr), vaddr); @@ -868,7 +868,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) cfg = IMGU_ABI_PARAM_CLASS_STATE; vaddr = css_pipe->binary_params_cs[cfg - 1][m0].vaddr; - cfg_ref_state = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_ref_state = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &sofs->dmem.ref, sizeof(*cfg_ref_state), vaddr); @@ -881,7 +881,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure tnr dmem state parameters */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { cfg_tnr_state = - ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &sofs->dmem.tnr3, sizeof(*cfg_tnr_state), vaddr); @@ -1068,21 +1068,21 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Initialize parameter pools */ - if (ipu3_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, + if (imgu_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, sizeof(struct imgu_abi_parameter_set_info)) || - ipu3_css_pool_init(imgu, &css_pipe->pool.acc, + imgu_css_pool_init(imgu, &css_pipe->pool.acc, sizeof(struct imgu_abi_acc_param)) || - ipu3_css_pool_init(imgu, &css_pipe->pool.gdc, + imgu_css_pool_init(imgu, &css_pipe->pool.gdc, sizeof(struct imgu_abi_gdc_warp_param) * 3 * cfg_dvs->num_horizontal_blocks / 2 * cfg_dvs->num_vertical_blocks) || - ipu3_css_pool_init(imgu, &css_pipe->pool.obgrid, - ipu3_css_fw_obgrid_size( + imgu_css_pool_init(imgu, &css_pipe->pool.obgrid, + imgu_css_fw_obgrid_size( &css->fwp->binary_header[css_pipe->bindex]))) goto out_of_memory; for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - if (ipu3_css_pool_init(imgu, + if (imgu_css_pool_init(imgu, &css_pipe->pool.binary_params_p[i], bi->info.isp.sp.mem_initializers.params [IMGU_ABI_PARAM_CLASS_PARAM][i].size)) @@ -1091,15 +1091,15 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) return 0; bad_firmware: - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); return -EPROTO; out_of_memory: - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); return -ENOMEM; } -static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) +static u8 imgu_css_queue_pos(struct imgu_css *css, int queue, int thread) { static const unsigned int sp; void __iomem *const base = css->base; @@ -1112,7 +1112,7 @@ static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) } /* Sent data to sp using given buffer queue, or if queue < 0, event queue. */ -static int ipu3_css_queue_data(struct ipu3_css *css, +static int imgu_css_queue_data(struct imgu_css *css, int queue, int thread, u32 data) { static const unsigned int sp; @@ -1151,7 +1151,7 @@ static int ipu3_css_queue_data(struct ipu3_css *css, } /* Receive data using given buffer queue, or if queue < 0, event queue. */ -static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) +static int imgu_css_dequeue_data(struct imgu_css *css, int queue, u32 *data) { static const unsigned int sp; void __iomem *const base = css->base; @@ -1188,7 +1188,7 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) writeb(start2, &q->sp2host_evtq_info.start); /* Acknowledge events dequeued from event queue */ - r = ipu3_css_queue_data(css, queue, 0, + r = imgu_css_queue_data(css, queue, 0, IMGU_ABI_EVENT_EVENT_DEQUEUED); if (r < 0) return r; @@ -1198,52 +1198,52 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) } /* Free binary-specific resources */ -static void ipu3_css_binary_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_binary_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (j = 0; j < IMGU_ABI_PARAM_CLASS_NUM - 1; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->binary_params_cs[j][i]); j = IPU3_CSS_AUX_FRAME_REF; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->aux_frames[j].mem[i]); j = IPU3_CSS_AUX_FRAME_TNR; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->aux_frames[j].mem[i]); } -static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_binary_preallocate(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->binary_params_cs[j - 1][i], CSS_ABI_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF]. mem[i], CSS_BDS_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR]. mem[i], CSS_GDC_SIZE)) goto out_of_memory; @@ -1251,14 +1251,14 @@ static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) return 0; out_of_memory: - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } /* allocate binary-specific resources */ -static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_binary_setup(struct imgu_css *css, unsigned int pipe) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct imgu_device *imgu = dev_get_drvdata(css->dev); int i, j, size; @@ -1269,7 +1269,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) { - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->binary_params_cs[j - 1][i], bi->info.isp.sp.mem_initializers.params[j][i].size)) @@ -1292,7 +1292,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel * w; size = w * h * BYPC + (w / 2) * (h / 2) * BYPC * 2; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i], size)) @@ -1313,7 +1313,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) h = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height; size = w * ALIGN(h * 3 / 2 + 3, 2); /* +3 for vf_pp prefetch */ for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i], size)) @@ -1322,11 +1322,11 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) return 0; out_of_memory: - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } -int ipu3_css_start_streaming(struct ipu3_css *css) +int imgu_css_start_streaming(struct imgu_css *css) { u32 data; int r, pipe; @@ -1335,48 +1335,48 @@ int ipu3_css_start_streaming(struct ipu3_css *css) return -EPROTO; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_binary_setup(css, pipe); + r = imgu_css_binary_setup(css, pipe); if (r < 0) return r; } - r = ipu3_css_hw_init(css); + r = imgu_css_hw_init(css); if (r < 0) return r; - r = ipu3_css_hw_start(css); + r = imgu_css_hw_start(css); if (r < 0) goto fail; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_pipeline_init(css, pipe); + r = imgu_css_pipeline_init(css, pipe); if (r < 0) goto fail; } css->streaming = true; - ipu3_css_hw_enable_irq(css); + imgu_css_hw_enable_irq(css); /* Initialize parameters to default */ for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_set_parameters(css, pipe, NULL); + r = imgu_css_set_parameters(css, pipe, NULL); if (r < 0) goto fail; } - while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) + while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) ; if (r != -EBUSY) goto fail; - while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) + while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) ; if (r != -EBUSY) goto fail; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_START_STREAM | pipe << 16); if (r < 0) @@ -1387,22 +1387,22 @@ int ipu3_css_start_streaming(struct ipu3_css *css) fail: css->streaming = false; - ipu3_css_hw_cleanup(css); + imgu_css_hw_cleanup(css); for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - ipu3_css_pipeline_cleanup(css, pipe); - ipu3_css_binary_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); } return r; } -void ipu3_css_stop_streaming(struct ipu3_css *css) +void imgu_css_stop_streaming(struct imgu_css *css) { - struct ipu3_css_buffer *b, *b0; + struct imgu_css_buffer *b, *b0; int q, r, pipe; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_STOP_STREAM); if (r < 0) dev_warn(css->dev, "failed on stop stream event\n"); @@ -1411,14 +1411,14 @@ void ipu3_css_stop_streaming(struct ipu3_css *css) if (!css->streaming) return; - ipu3_css_hw_stop(css); + imgu_css_hw_stop(css); - ipu3_css_hw_cleanup(css); + imgu_css_hw_cleanup(css); for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); spin_lock(&css_pipe->qlock); for (q = 0; q < IPU3_CSS_QUEUES; q++) @@ -1434,10 +1434,10 @@ void ipu3_css_stop_streaming(struct ipu3_css *css) css->streaming = false; } -bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) +bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe) { int q; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; spin_lock(&css_pipe->qlock); for (q = 0; q < IPU3_CSS_QUEUES; q++) @@ -1447,44 +1447,44 @@ bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) return (q == IPU3_CSS_QUEUES); } -bool ipu3_css_queue_empty(struct ipu3_css *css) +bool imgu_css_queue_empty(struct imgu_css *css) { unsigned int pipe; bool ret = 0; for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) - ret &= ipu3_css_pipe_queue_empty(css, pipe); + ret &= imgu_css_pipe_queue_empty(css, pipe); return ret; } -bool ipu3_css_is_streaming(struct ipu3_css *css) +bool imgu_css_is_streaming(struct imgu_css *css) { return css->streaming; } -static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_map_init(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; unsigned int p, q, i; /* Allocate and map common structures with imgu hardware */ for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe-> xmem_sp_stage_ptrs[p][i], sizeof(struct imgu_abi_sp_stage))) return -ENOMEM; - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe-> xmem_isp_stage_ptrs[p][i], sizeof(struct imgu_abi_isp_stage))) return -ENOMEM; } - if (!ipu3_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, + if (!imgu_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, ALIGN(sizeof(struct imgu_abi_ddr_address_map), IMGU_ABI_ISP_DDR_WORD_BYTES))) return -ENOMEM; @@ -1493,58 +1493,58 @@ static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) unsigned int abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); for (i = 0; i < abi_buf_num; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->abi_buffers[q][i], sizeof(struct imgu_abi_buffer))) return -ENOMEM; } - if (ipu3_css_binary_preallocate(css, pipe)) { - ipu3_css_binary_cleanup(css, pipe); + if (imgu_css_binary_preallocate(css, pipe)) { + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } return 0; } -static void ipu3_css_pipe_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_pipe_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; unsigned int p, q, i, abi_buf_num; - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); for (q = 0; q < IPU3_CSS_QUEUES; q++) { abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); for (i = 0; i < abi_buf_num; i++) - ipu3_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); + imgu_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); } for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->xmem_sp_stage_ptrs[p][i]); - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->xmem_isp_stage_ptrs[p][i]); } - ipu3_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); + imgu_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); } -void ipu3_css_cleanup(struct ipu3_css *css) +void imgu_css_cleanup(struct imgu_css *css) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int pipe; - ipu3_css_stop_streaming(css); + imgu_css_stop_streaming(css); for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) - ipu3_css_pipe_cleanup(css, pipe); - ipu3_dmamap_free(imgu, &css->xmem_sp_group_ptrs); - ipu3_css_fw_cleanup(css); + imgu_css_pipe_cleanup(css, pipe); + imgu_dmamap_free(imgu, &css->xmem_sp_group_ptrs); + imgu_css_fw_cleanup(css); } -int ipu3_css_init(struct device *dev, struct ipu3_css *css, +int imgu_css_init(struct device *dev, struct imgu_css *css, void __iomem *base, int length) { struct imgu_device *imgu = dev_get_drvdata(dev); @@ -1556,35 +1556,35 @@ int ipu3_css_init(struct device *dev, struct ipu3_css *css, css->iomem_length = length; for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; css_pipe->vf_output_en = false; spin_lock_init(&css_pipe->qlock); css_pipe->bindex = IPU3_CSS_DEFAULT_BINARY; css_pipe->pipe_id = IPU3_CSS_PIPE_ID_VIDEO; for (q = 0; q < IPU3_CSS_QUEUES; q++) { - r = ipu3_css_queue_init(&css_pipe->queue[q], NULL, 0); + r = imgu_css_queue_init(&css_pipe->queue[q], NULL, 0); if (r) return r; } - r = ipu3_css_map_init(css, pipe); + r = imgu_css_map_init(css, pipe); if (r) { - ipu3_css_cleanup(css); + imgu_css_cleanup(css); return r; } } - if (!ipu3_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, + if (!imgu_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, sizeof(struct imgu_abi_sp_group))) return -ENOMEM; - r = ipu3_css_fw_init(css); + r = imgu_css_fw_init(css); if (r) return r; return 0; } -static u32 ipu3_css_adjust(u32 res, u32 align) +static u32 imgu_css_adjust(u32 res, u32 align) { u32 val = max_t(u32, IPU3_CSS_MIN_RES, res); @@ -1592,9 +1592,9 @@ static u32 ipu3_css_adjust(u32 res, u32 align) } /* Select a binary matching the required resolutions and formats */ -static int ipu3_css_find_binary(struct ipu3_css *css, +static int imgu_css_find_binary(struct imgu_css *css, unsigned int pipe, - struct ipu3_css_queue queue[IPU3_CSS_QUEUES], + struct imgu_css_queue queue[IPU3_CSS_QUEUES], struct v4l2_rect rects[IPU3_CSS_RECTS]) { const int binary_nr = css->fwp->file_header.binary_nr; @@ -1611,7 +1611,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, const char *name; int i, j; - if (!ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) + if (!imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) return -EINVAL; /* Find out the strip size boundary */ @@ -1659,7 +1659,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, in->height > bi->info.isp.sp.input.max_height) continue; - if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { + if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { if (bi->info.isp.num_output_pins <= 0) continue; @@ -1681,7 +1681,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, continue; } - if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { + if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { if (bi->info.isp.num_output_pins <= 1) continue; @@ -1716,7 +1716,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, * found binary number. May modify the given parameters if not exact match * is found. */ -int ipu3_css_fmt_try(struct ipu3_css *css, +int imgu_css_fmt_try(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe) @@ -1744,7 +1744,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, struct v4l2_rect *const bds = &r[IPU3_CSS_RECT_BDS]; struct v4l2_rect *const env = &r[IPU3_CSS_RECT_ENVELOPE]; struct v4l2_rect *const gdc = &r[IPU3_CSS_RECT_GDC]; - struct ipu3_css_queue q[IPU3_CSS_QUEUES]; + struct imgu_css_queue q[IPU3_CSS_QUEUES]; struct v4l2_pix_format_mplane *const in = &q[IPU3_CSS_QUEUE_IN].fmt.mpix; struct v4l2_pix_format_mplane *const out = @@ -1762,7 +1762,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, else dev_dbg(css->dev, "%s %s: (not set)\n", __func__, qnames[i]); - if (ipu3_css_queue_init(&q[i], fmts[i], + if (imgu_css_queue_init(&q[i], fmts[i], IPU3_CSS_QUEUE_TO_FLAGS(i))) { dev_notice(css->dev, "can not initialize queue %s\n", qnames[i]); @@ -1785,13 +1785,13 @@ int ipu3_css_fmt_try(struct ipu3_css *css, } /* Always require one input and vf only if out is also enabled */ - if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || - !ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || + !imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { dev_warn(css->dev, "required queues are disabled\n"); return -EINVAL; } - if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { out->width = in->width; out->height = in->height; } @@ -1808,25 +1808,25 @@ int ipu3_css_fmt_try(struct ipu3_css *css, gdc->height = out->height; } - in->width = ipu3_css_adjust(in->width, 1); - in->height = ipu3_css_adjust(in->height, 1); - eff->width = ipu3_css_adjust(eff->width, EFF_ALIGN_W); - eff->height = ipu3_css_adjust(eff->height, 1); - bds->width = ipu3_css_adjust(bds->width, BDS_ALIGN_W); - bds->height = ipu3_css_adjust(bds->height, 1); - gdc->width = ipu3_css_adjust(gdc->width, OUT_ALIGN_W); - gdc->height = ipu3_css_adjust(gdc->height, OUT_ALIGN_H); - out->width = ipu3_css_adjust(out->width, OUT_ALIGN_W); - out->height = ipu3_css_adjust(out->height, OUT_ALIGN_H); - vf->width = ipu3_css_adjust(vf->width, VF_ALIGN_W); - vf->height = ipu3_css_adjust(vf->height, 1); + in->width = imgu_css_adjust(in->width, 1); + in->height = imgu_css_adjust(in->height, 1); + eff->width = imgu_css_adjust(eff->width, EFF_ALIGN_W); + eff->height = imgu_css_adjust(eff->height, 1); + bds->width = imgu_css_adjust(bds->width, BDS_ALIGN_W); + bds->height = imgu_css_adjust(bds->height, 1); + gdc->width = imgu_css_adjust(gdc->width, OUT_ALIGN_W); + gdc->height = imgu_css_adjust(gdc->height, OUT_ALIGN_H); + out->width = imgu_css_adjust(out->width, OUT_ALIGN_W); + out->height = imgu_css_adjust(out->height, OUT_ALIGN_H); + vf->width = imgu_css_adjust(vf->width, VF_ALIGN_W); + vf->height = imgu_css_adjust(vf->height, 1); s = (bds->width - gdc->width) / 2 - FILTER_SIZE; env->width = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; s = (bds->height - gdc->height) / 2 - FILTER_SIZE; env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; - ret = ipu3_css_find_binary(css, pipe, q, r); + ret = imgu_css_find_binary(css, pipe, q, r); if (ret < 0) { dev_err(css->dev, "failed to find suitable binary\n"); return -EINVAL; @@ -1839,7 +1839,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, /* Final adjustment and set back the queried formats */ for (i = 0; i < IPU3_CSS_QUEUES; i++) { if (fmts[i]) { - if (ipu3_css_queue_init(&q[i], &q[i].fmt.mpix, + if (imgu_css_queue_init(&q[i], &q[i].fmt.mpix, IPU3_CSS_QUEUE_TO_FLAGS(i))) { dev_err(css->dev, "final resolution adjustment failed\n"); @@ -1862,7 +1862,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, return 0; } -int ipu3_css_fmt_set(struct ipu3_css *css, +int imgu_css_fmt_set(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe) @@ -1870,7 +1870,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css, struct v4l2_rect rect_data[IPU3_CSS_RECTS]; struct v4l2_rect *all_rects[IPU3_CSS_RECTS]; int i, r; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (i = 0; i < IPU3_CSS_RECTS; i++) { if (rects[i]) @@ -1879,12 +1879,12 @@ int ipu3_css_fmt_set(struct ipu3_css *css, memset(&rect_data[i], 0, sizeof(rect_data[i])); all_rects[i] = &rect_data[i]; } - r = ipu3_css_fmt_try(css, fmts, all_rects, pipe); + r = imgu_css_fmt_try(css, fmts, all_rects, pipe); if (r < 0) return r; for (i = 0; i < IPU3_CSS_QUEUES; i++) - if (ipu3_css_queue_init(&css_pipe->queue[i], fmts[i], + if (imgu_css_queue_init(&css_pipe->queue[i], fmts[i], IPU3_CSS_QUEUE_TO_FLAGS(i))) return -EINVAL; for (i = 0; i < IPU3_CSS_RECTS; i++) { @@ -1896,7 +1896,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css, return 0; } -int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) +int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt) { switch (fmt->dataformat) { case V4L2_META_FMT_IPU3_PARAMS: @@ -1913,27 +1913,27 @@ int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) } /* - * Queue given buffer to CSS. ipu3_css_buf_prepare() must have been first + * Queue given buffer to CSS. imgu_css_buf_prepare() must have been first * called for the buffer. May be called from interrupt context. * Returns 0 on success, -EBUSY if the buffer queue is full, or some other * code on error conditions. */ -int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, - struct ipu3_css_buffer *b) +int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe, + struct imgu_css_buffer *b) { struct imgu_abi_buffer *abi_buf; struct imgu_addr_t *buf_addr; u32 data; int r; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; if (!css->streaming) return -EPROTO; /* CSS or buffer in wrong state */ - if (b->queue >= IPU3_CSS_QUEUES || !ipu3_css_queues[b->queue].qid) + if (b->queue >= IPU3_CSS_QUEUES || !imgu_css_queues[b->queue].qid) return -EINVAL; - b->queue_pos = ipu3_css_queue_pos(css, ipu3_css_queues[b->queue].qid, + b->queue_pos = imgu_css_queue_pos(css, imgu_css_queues[b->queue].qid, pipe); if (b->queue_pos >= ARRAY_SIZE(css->pipes[pipe].abi_buffers[b->queue])) @@ -1943,7 +1943,7 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, /* Fill struct abi_buffer for firmware */ memset(abi_buf, 0, sizeof(*abi_buf)); - buf_addr = (void *)abi_buf + ipu3_css_queues[b->queue].ptr_ofs; + buf_addr = (void *)abi_buf + imgu_css_queues[b->queue].ptr_ofs; *(imgu_addr_t *)buf_addr = b->daddr; if (b->queue == IPU3_CSS_QUEUE_STAT_3A) @@ -1963,14 +1963,14 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, b->state = IPU3_CSS_BUFFER_QUEUED; data = css->pipes[pipe].abi_buffers[b->queue][b->queue_pos].daddr; - r = ipu3_css_queue_data(css, ipu3_css_queues[b->queue].qid, + r = imgu_css_queue_data(css, imgu_css_queues[b->queue].qid, pipe, data); if (r < 0) goto queueing_failed; data = IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, - ipu3_css_queues[b->queue].qid); - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); + imgu_css_queues[b->queue].qid); + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); if (r < 0) goto queueing_failed; @@ -1992,7 +1992,7 @@ queueing_failed: * should be called again, or -EBUSY which means that there are no more * buffers available. May be called from interrupt context. */ -struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) +struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css) { static const unsigned char evtype_to_queue[] = { [IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE] = IPU3_CSS_QUEUE_IN, @@ -2000,15 +2000,15 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) [IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_VF, [IMGU_ABI_EVTTYPE_3A_STATS_DONE] = IPU3_CSS_QUEUE_STAT_3A, }; - struct ipu3_css_buffer *b = ERR_PTR(-EAGAIN); + struct imgu_css_buffer *b = ERR_PTR(-EAGAIN); u32 event, daddr; int evtype, pipe, pipeid, queue, qid, r; - struct ipu3_css_pipe *css_pipe; + struct imgu_css_pipe *css_pipe; if (!css->streaming) return ERR_PTR(-EPROTO); - r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); if (r < 0) return ERR_PTR(r); @@ -2025,7 +2025,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) pipeid = (event & IMGU_ABI_EVTTYPE_PIPEID_MASK) >> IMGU_ABI_EVTTYPE_PIPEID_SHIFT; queue = evtype_to_queue[evtype]; - qid = ipu3_css_queues[queue].qid; + qid = imgu_css_queues[queue].qid; if (pipe >= IMGU_MAX_PIPE_NUM) { dev_err(css->dev, "Invalid pipe: %i\n", pipe); @@ -2041,14 +2041,14 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) "event: buffer done 0x%x queue %i pipe %i pipeid %i\n", event, queue, pipe, pipeid); - r = ipu3_css_dequeue_data(css, qid, &daddr); + r = imgu_css_dequeue_data(css, qid, &daddr); if (r < 0) { dev_err(css->dev, "failed to dequeue buffer\n"); /* Force real error, not -EBUSY */ return ERR_PTR(-EIO); } - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_DEQUEUED(qid)); if (r < 0) { dev_err(css->dev, "failed to queue event\n"); @@ -2062,7 +2062,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) return ERR_PTR(-EIO); } b = list_first_entry(&css_pipe->queue[queue].bufs, - struct ipu3_css_buffer, list); + struct imgu_css_buffer, list); if (queue != b->queue || daddr != css_pipe->abi_buffers [b->queue][b->queue_pos].daddr) { @@ -2090,7 +2090,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) event, pipe); break; case IMGU_ABI_EVTTYPE_TIMER: - r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); if (r < 0) return ERR_PTR(r); @@ -2128,11 +2128,11 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) * Return index to css->parameter_set_info which has the newly created * parameters or negative value on error. */ -int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, +int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_params *set_params) { static const unsigned int queue_id = IMGU_ABI_QUEUE_A_ID; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const int stage = 0; const struct imgu_fw_info *bi; int obgrid_size; @@ -2144,7 +2144,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, struct imgu_abi_acc_param *acc = NULL; struct imgu_abi_gdc_warp_param *gdc = NULL; struct ipu3_uapi_obgrid_param *obgrid = NULL; - const struct ipu3_css_map *map; + const struct imgu_css_map *map; void *vmem0 = NULL; void *dmem0 = NULL; @@ -2157,7 +2157,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, dev_dbg(css->dev, "%s for pipe %d", __func__, pipe); bi = &css->fwp->binary_header[css_pipe->bindex]; - obgrid_size = ipu3_css_fw_obgrid_size(bi); + obgrid_size = imgu_css_fw_obgrid_size(bi); stripes = bi->info.isp.sp.iterator.num_stripes ? : 1; /* @@ -2165,45 +2165,45 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, * parameters from previous buffers will be overwritten. Fix the driver * not to allow this. */ - ipu3_css_pool_get(&css_pipe->pool.parameter_set_info); - param_set = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, + imgu_css_pool_get(&css_pipe->pool.parameter_set_info); + param_set = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0)->vaddr; /* Get a new acc only if new parameters given, or none yet */ - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); if (set_params || !map->vaddr) { - ipu3_css_pool_get(&css_pipe->pool.acc); - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + imgu_css_pool_get(&css_pipe->pool.acc); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); acc = map->vaddr; } /* Get new VMEM0 only if needed, or none yet */ m = IMGU_ABI_MEM_ISP_VMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); if (!map->vaddr || (set_params && (set_params->use.lin_vmem_params || set_params->use.tnr3_vmem_params || set_params->use.xnr3_vmem_params))) { - ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); vmem0 = map->vaddr; } /* Get new DMEM0 only if needed, or none yet */ m = IMGU_ABI_MEM_ISP_DMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); if (!map->vaddr || (set_params && (set_params->use.tnr3_dmem_params || set_params->use.xnr3_dmem_params))) { - ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); dmem0 = map->vaddr; } /* Configure acc parameter cluster */ if (acc) { /* get acc_old */ - map = ipu3_css_pool_last(&css_pipe->pool.acc, 1); + map = imgu_css_pool_last(&css_pipe->pool.acc, 1); /* user acc */ - r = ipu3_css_cfg_acc(css, pipe, use, acc, map->vaddr, + r = imgu_css_cfg_acc(css, pipe, use, acc, map->vaddr, set_params ? &set_params->acc_param : NULL); if (r < 0) goto fail; @@ -2212,8 +2212,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, /* Configure late binding parameters */ if (vmem0) { m = IMGU_ABI_MEM_ISP_VMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); - r = ipu3_css_cfg_vmem0(css, pipe, use, vmem0, + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = imgu_css_cfg_vmem0(css, pipe, use, vmem0, map->vaddr, set_params); if (r < 0) goto fail; @@ -2221,8 +2221,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, if (dmem0) { m = IMGU_ABI_MEM_ISP_DMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); - r = ipu3_css_cfg_dmem0(css, pipe, use, dmem0, + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = imgu_css_cfg_dmem0(css, pipe, use, dmem0, map->vaddr, set_params); if (r < 0) goto fail; @@ -2234,12 +2234,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, unsigned int g = IPU3_CSS_RECT_GDC; unsigned int e = IPU3_CSS_RECT_ENVELOPE; - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); if (!map->vaddr) { - ipu3_css_pool_get(&css_pipe->pool.gdc); - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + imgu_css_pool_get(&css_pipe->pool.gdc); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); gdc = map->vaddr; - ipu3_css_cfg_gdc_table(map->vaddr, + imgu_css_cfg_gdc_table(map->vaddr, css_pipe->aux_frames[a].bytesperline / css_pipe->aux_frames[a].bytesperpixel, css_pipe->aux_frames[a].height, @@ -2252,10 +2252,10 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, } /* Get a new obgrid only if a new obgrid is given, or none yet */ - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); if (!map->vaddr || (set_params && set_params->use.obgrid_param)) { - ipu3_css_pool_get(&css_pipe->pool.obgrid); - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + imgu_css_pool_get(&css_pipe->pool.obgrid); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); obgrid = map->vaddr; /* Configure optical black level grid (obgrid) */ @@ -2269,30 +2269,30 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, /* Configure parameter set info, queued to `queue_id' */ memset(param_set, 0, sizeof(*param_set)); - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); param_set->mem_map.acc_cluster_params_for_sp = map->daddr; - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); param_set->mem_map.dvs_6axis_params_y = map->daddr; for (i = 0; i < stripes; i++) { - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); param_set->mem_map.obgrid_tbl[i] = map->daddr + (obgrid_size / stripes) * i; } for (m = 0; m < IMGU_ABI_NUM_MEMORIES; m++) { - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); param_set->mem_map.isp_mem_param[stage][m] = map->daddr; } /* Then queue the new parameter buffer */ - map = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, 0); - r = ipu3_css_queue_data(css, queue_id, pipe, map->daddr); + map = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0); + r = imgu_css_queue_data(css, queue_id, pipe, map->daddr); if (r < 0) goto fail; - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, queue_id)); if (r < 0) @@ -2303,12 +2303,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, do { u32 daddr; - r = ipu3_css_dequeue_data(css, queue_id, &daddr); + r = imgu_css_dequeue_data(css, queue_id, &daddr); if (r == -EBUSY) break; if (r) goto fail_no_put; - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_DEQUEUED (queue_id)); if (r < 0) { @@ -2326,19 +2326,19 @@ fail: * parameters again later. */ - ipu3_css_pool_put(&css_pipe->pool.parameter_set_info); + imgu_css_pool_put(&css_pipe->pool.parameter_set_info); if (acc) - ipu3_css_pool_put(&css_pipe->pool.acc); + imgu_css_pool_put(&css_pipe->pool.acc); if (gdc) - ipu3_css_pool_put(&css_pipe->pool.gdc); + imgu_css_pool_put(&css_pipe->pool.gdc); if (obgrid) - ipu3_css_pool_put(&css_pipe->pool.obgrid); + imgu_css_pool_put(&css_pipe->pool.obgrid); if (vmem0) - ipu3_css_pool_put( + imgu_css_pool_put( &css_pipe->pool.binary_params_p [IMGU_ABI_MEM_ISP_VMEM0]); if (dmem0) - ipu3_css_pool_put( + imgu_css_pool_put( &css_pipe->pool.binary_params_p [IMGU_ABI_MEM_ISP_DMEM0]); @@ -2346,7 +2346,7 @@ fail_no_put: return r; } -int ipu3_css_irq_ack(struct ipu3_css *css) +int imgu_css_irq_ack(struct imgu_css *css) { static const int NUM_SWIRQS = 3; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h index e88d60f1a0c3..6b8bab27ab1f 100644 --- a/drivers/staging/media/ipu3/ipu3-css.h +++ b/drivers/staging/media/ipu3/ipu3-css.h @@ -43,7 +43,7 @@ * The pipe id type, distinguishes the kind of pipes that * can be run in parallel. */ -enum ipu3_css_pipe_id { +enum imgu_css_pipe_id { IPU3_CSS_PIPE_ID_PREVIEW, IPU3_CSS_PIPE_ID_COPY, IPU3_CSS_PIPE_ID_VIDEO, @@ -53,29 +53,29 @@ enum ipu3_css_pipe_id { IPU3_CSS_PIPE_ID_NUM }; -struct ipu3_css_resolution { +struct imgu_css_resolution { u32 w; u32 h; }; -enum ipu3_css_buffer_state { +enum imgu_css_buffer_state { IPU3_CSS_BUFFER_NEW, /* Not yet queued */ IPU3_CSS_BUFFER_QUEUED, /* Queued, waiting to be filled */ IPU3_CSS_BUFFER_DONE, /* Finished processing, removed from queue */ IPU3_CSS_BUFFER_FAILED, /* Was not processed, removed from queue */ }; -struct ipu3_css_buffer { +struct imgu_css_buffer { /* Private fields: user doesn't touch */ dma_addr_t daddr; unsigned int queue; - enum ipu3_css_buffer_state state; + enum imgu_css_buffer_state state; struct list_head list; u8 queue_pos; unsigned int pipe; }; -struct ipu3_css_format { +struct imgu_css_format { u32 pixelformat; enum v4l2_colorspace colorspace; enum imgu_abi_frame_format frame_format; @@ -89,22 +89,22 @@ struct ipu3_css_format { u8 flags; }; -struct ipu3_css_queue { +struct imgu_css_queue { union { struct v4l2_pix_format_mplane mpix; struct v4l2_meta_format meta; } fmt; - const struct ipu3_css_format *css_fmt; + const struct imgu_css_format *css_fmt; unsigned int width_pad; struct list_head bufs; }; -struct ipu3_css_pipe { - enum ipu3_css_pipe_id pipe_id; +struct imgu_css_pipe { + enum imgu_css_pipe_id pipe_id; unsigned int bindex; - struct ipu3_css_queue queue[IPU3_CSS_QUEUES]; + struct imgu_css_queue queue[IPU3_CSS_QUEUES]; struct v4l2_rect rect[IPU3_CSS_RECTS]; bool vf_output_en; @@ -112,21 +112,21 @@ struct ipu3_css_pipe { spinlock_t qlock; /* Data structures shared with IMGU and driver, always allocated */ - struct ipu3_css_map sp_ddr_ptrs; - struct ipu3_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + struct imgu_css_map sp_ddr_ptrs; + struct imgu_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] [IMGU_ABI_MAX_STAGES]; - struct ipu3_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + struct imgu_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] [IMGU_ABI_MAX_STAGES]; /* * Data structures shared with IMGU and driver, binary specific. * PARAM_CLASS_CONFIG and PARAM_CLASS_STATE parameters. */ - struct ipu3_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] + struct imgu_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] [IMGU_ABI_NUM_MEMORIES]; struct { - struct ipu3_css_map mem[IPU3_CSS_AUX_FRAMES]; + struct imgu_css_map mem[IPU3_CSS_AUX_FRAMES]; unsigned int width; unsigned int height; unsigned int bytesperline; @@ -134,76 +134,76 @@ struct ipu3_css_pipe { } aux_frames[IPU3_CSS_AUX_FRAME_TYPES]; struct { - struct ipu3_css_pool parameter_set_info; - struct ipu3_css_pool acc; - struct ipu3_css_pool gdc; - struct ipu3_css_pool obgrid; + struct imgu_css_pool parameter_set_info; + struct imgu_css_pool acc; + struct imgu_css_pool gdc; + struct imgu_css_pool obgrid; /* PARAM_CLASS_PARAM parameters for binding while streaming */ - struct ipu3_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; + struct imgu_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; } pool; - struct ipu3_css_map abi_buffers[IPU3_CSS_QUEUES] + struct imgu_css_map abi_buffers[IPU3_CSS_QUEUES] [IMGU_ABI_HOST2SP_BUFQ_SIZE]; }; /* IPU3 Camera Sub System structure */ -struct ipu3_css { +struct imgu_css { struct device *dev; void __iomem *base; const struct firmware *fw; struct imgu_fw_header *fwp; int iomem_length; int fw_bl, fw_sp[IMGU_NUM_SP]; /* Indices of bl and SP binaries */ - struct ipu3_css_map *binary; /* fw binaries mapped to device */ + struct imgu_css_map *binary; /* fw binaries mapped to device */ bool streaming; /* true when streaming is enabled */ - struct ipu3_css_pipe pipes[IMGU_MAX_PIPE_NUM]; - struct ipu3_css_map xmem_sp_group_ptrs; + struct imgu_css_pipe pipes[IMGU_MAX_PIPE_NUM]; + struct imgu_css_map xmem_sp_group_ptrs; /* enabled pipe(s) */ DECLARE_BITMAP(enabled_pipes, IMGU_MAX_PIPE_NUM); }; /******************* css v4l *******************/ -int ipu3_css_init(struct device *dev, struct ipu3_css *css, +int imgu_css_init(struct device *dev, struct imgu_css *css, void __iomem *base, int length); -void ipu3_css_cleanup(struct ipu3_css *css); -int ipu3_css_fmt_try(struct ipu3_css *css, +void imgu_css_cleanup(struct imgu_css *css); +int imgu_css_fmt_try(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe); -int ipu3_css_fmt_set(struct ipu3_css *css, +int imgu_css_fmt_set(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe); -int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt); -int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, - struct ipu3_css_buffer *b); -struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css); -int ipu3_css_start_streaming(struct ipu3_css *css); -void ipu3_css_stop_streaming(struct ipu3_css *css); -bool ipu3_css_queue_empty(struct ipu3_css *css); -bool ipu3_css_is_streaming(struct ipu3_css *css); -bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe); +int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt); +int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe, + struct imgu_css_buffer *b); +struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css); +int imgu_css_start_streaming(struct imgu_css *css); +void imgu_css_stop_streaming(struct imgu_css *css); +bool imgu_css_queue_empty(struct imgu_css *css); +bool imgu_css_is_streaming(struct imgu_css *css); +bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe); /******************* css hw *******************/ -int ipu3_css_set_powerup(struct device *dev, void __iomem *base); -void ipu3_css_set_powerdown(struct device *dev, void __iomem *base); -int ipu3_css_irq_ack(struct ipu3_css *css); +int imgu_css_set_powerup(struct device *dev, void __iomem *base); +void imgu_css_set_powerdown(struct device *dev, void __iomem *base); +int imgu_css_irq_ack(struct imgu_css *css); /******************* set parameters ************/ -int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, +int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_params *set_params); /******************* auxiliary helpers *******************/ -static inline enum ipu3_css_buffer_state -ipu3_css_buf_state(struct ipu3_css_buffer *b) +static inline enum imgu_css_buffer_state +imgu_css_buf_state(struct imgu_css_buffer *b) { return b->state; } /* Initialize given buffer. May be called several times. */ -static inline void ipu3_css_buf_init(struct ipu3_css_buffer *b, +static inline void imgu_css_buf_init(struct imgu_css_buffer *b, unsigned int queue, dma_addr_t daddr) { b->state = IPU3_CSS_BUFFER_NEW; diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c index 5bed01d5b8df..d978a00e1e0b 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.c +++ b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -15,9 +15,9 @@ #include "ipu3-dmamap.h" /* - * Free a buffer allocated by ipu3_dmamap_alloc_buffer() + * Free a buffer allocated by imgu_dmamap_alloc_buffer() */ -static void ipu3_dmamap_free_buffer(struct page **pages, +static void imgu_dmamap_free_buffer(struct page **pages, size_t size) { int count = size >> PAGE_SHIFT; @@ -31,7 +31,7 @@ static void ipu3_dmamap_free_buffer(struct page **pages, * Based on the implementation of __iommu_dma_alloc_pages() * defined in drivers/iommu/dma-iommu.c */ -static struct page **ipu3_dmamap_alloc_buffer(size_t size, +static struct page **imgu_dmamap_alloc_buffer(size_t size, unsigned long order_mask, gfp_t gfp) { @@ -74,7 +74,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, __free_pages(page, order); } if (!page) { - ipu3_dmamap_free_buffer(pages, i << PAGE_SHIFT); + imgu_dmamap_free_buffer(pages, i << PAGE_SHIFT); return NULL; } count -= order_size; @@ -86,7 +86,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, } /** - * ipu3_dmamap_alloc - allocate and map a buffer into KVA + * imgu_dmamap_alloc - allocate and map a buffer into KVA * @imgu: struct device pointer * @map: struct to store mapping variables * @len: size required @@ -95,7 +95,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, * KVA on success * %NULL on failure */ -void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, +void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len) { unsigned long shift = iova_shift(&imgu->iova_domain); @@ -114,7 +114,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, if (!iova) return NULL; - pages = ipu3_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, + pages = imgu_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, GFP_KERNEL); if (!pages) goto out_free_iova; @@ -122,7 +122,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, /* Call IOMMU driver to setup pgt */ iovaddr = iova_dma_addr(&imgu->iova_domain, iova); for (i = 0; i < size / PAGE_SIZE; ++i) { - rval = ipu3_mmu_map(imgu->mmu, iovaddr, + rval = imgu_mmu_map(imgu->mmu, iovaddr, page_to_phys(pages[i]), PAGE_SIZE); if (rval) goto out_unmap; @@ -153,8 +153,8 @@ out_vunmap: vunmap(map->vma->addr); out_unmap: - ipu3_dmamap_free_buffer(pages, size); - ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + imgu_dmamap_free_buffer(pages, size); + imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), i * PAGE_SIZE); map->vma = NULL; @@ -164,7 +164,7 @@ out_free_iova: return NULL; } -void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) +void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map) { struct iova *iova; @@ -173,16 +173,16 @@ void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) if (WARN_ON(!iova)) return; - ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), iova_size(iova) << iova_shift(&imgu->iova_domain)); __free_iova(&imgu->iova_domain, iova); } /* - * Counterpart of ipu3_dmamap_alloc + * Counterpart of imgu_dmamap_alloc */ -void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) +void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map) { struct vm_struct *area = map->vma; @@ -192,18 +192,18 @@ void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) if (!map->vaddr) return; - ipu3_dmamap_unmap(imgu, map); + imgu_dmamap_unmap(imgu, map); if (WARN_ON(!area) || WARN_ON(!area->pages)) return; - ipu3_dmamap_free_buffer(area->pages, map->size); + imgu_dmamap_free_buffer(area->pages, map->size); vunmap(map->vaddr); map->vaddr = NULL; } -int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, - int nents, struct ipu3_css_map *map) +int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct imgu_css_map *map) { unsigned long shift = iova_shift(&imgu->iova_domain); struct scatterlist *sg; @@ -233,7 +233,7 @@ int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, dev_dbg(&imgu->pci_dev->dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, iova->pfn_hi); - if (ipu3_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + if (imgu_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), sglist, nents) < size) goto out_fail; @@ -249,7 +249,7 @@ out_fail: return -EFAULT; } -int ipu3_dmamap_init(struct imgu_device *imgu) +int imgu_dmamap_init(struct imgu_device *imgu) { unsigned long order, base_pfn; int ret = iova_cache_get(); @@ -264,7 +264,7 @@ int ipu3_dmamap_init(struct imgu_device *imgu) return 0; } -void ipu3_dmamap_exit(struct imgu_device *imgu) +void imgu_dmamap_exit(struct imgu_device *imgu) { put_iova_domain(&imgu->iova_domain); iova_cache_put(); diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.h b/drivers/staging/media/ipu3/ipu3-dmamap.h index b9d224a33273..9db513b3d603 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.h +++ b/drivers/staging/media/ipu3/ipu3-dmamap.h @@ -8,15 +8,15 @@ struct imgu_device; struct scatterlist; -void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, +void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len); -void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map); +void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map); -int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, - int nents, struct ipu3_css_map *map); -void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map); +int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct imgu_css_map *map); +void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map); -int ipu3_dmamap_init(struct imgu_device *imgu); -void ipu3_dmamap_exit(struct imgu_device *imgu); +int imgu_dmamap_init(struct imgu_device *imgu); +void imgu_dmamap_exit(struct imgu_device *imgu); #endif diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c index b9f209541f78..cd2038b22b55 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.c +++ b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -48,7 +48,7 @@ #define REG_GP_HALT (IMGU_REG_BASE + 0x5dc) #define REG_GP_HALTED (IMGU_REG_BASE + 0x5e0) -struct ipu3_mmu { +struct imgu_mmu { struct device *dev; void __iomem *base; /* protect access to l2pts, l1pt */ @@ -63,28 +63,28 @@ struct ipu3_mmu { u32 **l2pts; u32 *l1pt; - struct ipu3_mmu_info geometry; + struct imgu_mmu_info geometry; }; -static inline struct ipu3_mmu *to_ipu3_mmu(struct ipu3_mmu_info *info) +static inline struct imgu_mmu *to_imgu_mmu(struct imgu_mmu_info *info) { - return container_of(info, struct ipu3_mmu, geometry); + return container_of(info, struct imgu_mmu, geometry); } /** - * ipu3_mmu_tlb_invalidate - invalidate translation look-aside buffer + * imgu_mmu_tlb_invalidate - invalidate translation look-aside buffer * @mmu: MMU to perform the invalidate operation on * * This function invalidates the whole TLB. Must be called when the hardware * is powered on. */ -static void ipu3_mmu_tlb_invalidate(struct ipu3_mmu *mmu) +static void imgu_mmu_tlb_invalidate(struct imgu_mmu *mmu) { writel(TLB_INVALIDATE, mmu->base + REG_TLB_INVALIDATE); } -static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, - void (*func)(struct ipu3_mmu *mmu)) +static void call_if_imgu_is_powered(struct imgu_mmu *mmu, + void (*func)(struct imgu_mmu *mmu)) { if (!pm_runtime_get_if_in_use(mmu->dev)) return; @@ -94,14 +94,14 @@ static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, } /** - * ipu3_mmu_set_halt - set CIO gate halt bit + * imgu_mmu_set_halt - set CIO gate halt bit * @mmu: MMU to set the CIO gate bit in. * @halt: Desired state of the gate bit. * * This function sets the CIO gate bit that controls whether external memory * accesses are allowed. Must be called when the hardware is powered on. */ -static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) +static void imgu_mmu_set_halt(struct imgu_mmu *mmu, bool halt) { int ret; u32 val; @@ -116,12 +116,12 @@ static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) } /** - * ipu3_mmu_alloc_page_table - allocate a pre-filled page table + * imgu_mmu_alloc_page_table - allocate a pre-filled page table * @pteval: Value to initialize for page table entries with. * * Return: Pointer to allocated page table or NULL on failure. */ -static u32 *ipu3_mmu_alloc_page_table(u32 pteval) +static u32 *imgu_mmu_alloc_page_table(u32 pteval) { u32 *pt; int pte; @@ -139,10 +139,10 @@ static u32 *ipu3_mmu_alloc_page_table(u32 pteval) } /** - * ipu3_mmu_free_page_table - free page table + * imgu_mmu_free_page_table - free page table * @pt: Page table to free. */ -static void ipu3_mmu_free_page_table(u32 *pt) +static void imgu_mmu_free_page_table(u32 *pt) { set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER); free_page((unsigned long)pt); @@ -168,7 +168,7 @@ static inline void address_to_pte_idx(unsigned long iova, u32 *l1pt_idx, *l1pt_idx = iova & IPU3_L1PT_MASK; } -static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) +static u32 *imgu_mmu_get_l2pt(struct imgu_mmu *mmu, u32 l1pt_idx) { unsigned long flags; u32 *l2pt, *new_l2pt; @@ -182,7 +182,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) spin_unlock_irqrestore(&mmu->lock, flags); - new_l2pt = ipu3_mmu_alloc_page_table(mmu->dummy_page_pteval); + new_l2pt = imgu_mmu_alloc_page_table(mmu->dummy_page_pteval); if (!new_l2pt) return NULL; @@ -193,7 +193,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) l2pt = mmu->l2pts[l1pt_idx]; if (l2pt) { - ipu3_mmu_free_page_table(new_l2pt); + imgu_mmu_free_page_table(new_l2pt); goto done; } @@ -208,7 +208,7 @@ done: return l2pt; } -static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, +static int __imgu_mmu_map(struct imgu_mmu *mmu, unsigned long iova, phys_addr_t paddr) { u32 l1pt_idx, l2pt_idx; @@ -220,7 +220,7 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, address_to_pte_idx(iova, &l1pt_idx, &l2pt_idx); - l2pt = ipu3_mmu_get_l2pt(mmu, l1pt_idx); + l2pt = imgu_mmu_get_l2pt(mmu, l1pt_idx); if (!l2pt) return -ENOMEM; @@ -242,7 +242,7 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, * The following four functions are implemented based on iommu.c * drivers/iommu/iommu.c/iommu_pgsize(). */ -static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap, +static size_t imgu_mmu_pgsize(unsigned long pgsize_bitmap, unsigned long addr_merge, size_t size) { unsigned int pgsize_idx; @@ -276,10 +276,10 @@ static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap, } /* drivers/iommu/iommu.c/iommu_map() */ -int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, +int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); unsigned int min_pagesz; int ret = 0; @@ -301,13 +301,13 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, iova, &paddr, size); while (size) { - size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap, + size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, iova | paddr, size); dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", iova, &paddr, pgsize); - ret = __ipu3_mmu_map(mmu, iova, paddr); + ret = __imgu_mmu_map(mmu, iova, paddr); if (ret) break; @@ -316,16 +316,16 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, size -= pgsize; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return ret; } /* drivers/iommu/iommu.c/default_iommu_map_sg() */ -size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); struct scatterlist *s; size_t s_length, mapped = 0; unsigned int i, min_pagesz; @@ -345,25 +345,25 @@ size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, if (i == nents - 1 && !IS_ALIGNED(s->length, min_pagesz)) s_length = PAGE_ALIGN(s->length); - ret = ipu3_mmu_map(info, iova + mapped, phys, s_length); + ret = imgu_mmu_map(info, iova + mapped, phys, s_length); if (ret) goto out_err; mapped += s_length; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return mapped; out_err: /* undo mappings already done */ - ipu3_mmu_unmap(info, iova, mapped); + imgu_mmu_unmap(info, iova, mapped); return 0; } -static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu, +static size_t __imgu_mmu_unmap(struct imgu_mmu *mmu, unsigned long iova, size_t size) { u32 l1pt_idx, l2pt_idx; @@ -395,10 +395,10 @@ static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu, } /* drivers/iommu/iommu.c/iommu_unmap() */ -size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); size_t unmapped_page, unmapped = 0; unsigned int min_pagesz; @@ -423,10 +423,10 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, * or we hit an area that isn't mapped. */ while (unmapped < size) { - size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap, + size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, iova, size - unmapped); - unmapped_page = __ipu3_mmu_unmap(mmu, iova, pgsize); + unmapped_page = __imgu_mmu_unmap(mmu, iova, pgsize); if (!unmapped_page) break; @@ -437,20 +437,20 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, unmapped += unmapped_page; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return unmapped; } /** - * ipu3_mmu_init() - initialize IPU3 MMU block + * imgu_mmu_init() - initialize IPU3 MMU block * @base: IOMEM base of hardware registers. * * Return: Pointer to IPU3 MMU private data pointer or ERR_PTR() on error. */ -struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) +struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base) { - struct ipu3_mmu *mmu; + struct imgu_mmu *mmu; u32 pteval; mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); @@ -462,7 +462,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) spin_lock_init(&mmu->lock); /* Disallow external memory access when having no valid page tables. */ - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); /* * The MMU does not have a "valid" bit, so we have to use a dummy @@ -478,7 +478,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) * Allocate a dummy L2 page table with all entries pointing to * the dummy page. */ - mmu->dummy_l2pt = ipu3_mmu_alloc_page_table(pteval); + mmu->dummy_l2pt = imgu_mmu_alloc_page_table(pteval); if (!mmu->dummy_l2pt) goto fail_dummy_page; pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->dummy_l2pt)); @@ -493,14 +493,14 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) goto fail_l2pt; /* Allocate the L1 page table. */ - mmu->l1pt = ipu3_mmu_alloc_page_table(mmu->dummy_l2pt_pteval); + mmu->l1pt = imgu_mmu_alloc_page_table(mmu->dummy_l2pt_pteval); if (!mmu->l1pt) goto fail_l2pts; pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt)); writel(pteval, mmu->base + REG_L1_PHYS); - ipu3_mmu_tlb_invalidate(mmu); - ipu3_mmu_set_halt(mmu, false); + imgu_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, false); mmu->geometry.aperture_start = 0; mmu->geometry.aperture_end = DMA_BIT_MASK(IPU3_MMU_ADDRESS_BITS); @@ -511,7 +511,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) fail_l2pts: vfree(mmu->l2pts); fail_l2pt: - ipu3_mmu_free_page_table(mmu->dummy_l2pt); + imgu_mmu_free_page_table(mmu->dummy_l2pt); fail_dummy_page: free_page((unsigned long)mmu->dummy_page); fail_group: @@ -521,41 +521,41 @@ fail_group: } /** - * ipu3_mmu_exit() - clean up IPU3 MMU block + * imgu_mmu_exit() - clean up IPU3 MMU block * @mmu: IPU3 MMU private data */ -void ipu3_mmu_exit(struct ipu3_mmu_info *info) +void imgu_mmu_exit(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); /* We are going to free our page tables, no more memory access. */ - ipu3_mmu_set_halt(mmu, true); - ipu3_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, true); + imgu_mmu_tlb_invalidate(mmu); - ipu3_mmu_free_page_table(mmu->l1pt); + imgu_mmu_free_page_table(mmu->l1pt); vfree(mmu->l2pts); - ipu3_mmu_free_page_table(mmu->dummy_l2pt); + imgu_mmu_free_page_table(mmu->dummy_l2pt); free_page((unsigned long)mmu->dummy_page); kfree(mmu); } -void ipu3_mmu_suspend(struct ipu3_mmu_info *info) +void imgu_mmu_suspend(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); } -void ipu3_mmu_resume(struct ipu3_mmu_info *info) +void imgu_mmu_resume(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); u32 pteval; - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt)); writel(pteval, mmu->base + REG_L1_PHYS); - ipu3_mmu_tlb_invalidate(mmu); - ipu3_mmu_set_halt(mmu, false); + imgu_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, false); } diff --git a/drivers/staging/media/ipu3/ipu3-mmu.h b/drivers/staging/media/ipu3/ipu3-mmu.h index 8fe63b4c6e1c..fa58827eb19c 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.h +++ b/drivers/staging/media/ipu3/ipu3-mmu.h @@ -6,13 +6,13 @@ #define __IPU3_MMU_H /** - * struct ipu3_mmu_info - Describes mmu geometry + * struct imgu_mmu_info - Describes mmu geometry * * @aperture_start: First address that can be mapped * @aperture_end: Last address that can be mapped * @pgsize_bitmap: Bitmap of page sizes in use */ -struct ipu3_mmu_info { +struct imgu_mmu_info { dma_addr_t aperture_start; dma_addr_t aperture_end; unsigned long pgsize_bitmap; @@ -21,15 +21,15 @@ struct ipu3_mmu_info { struct device; struct scatterlist; -struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base); -void ipu3_mmu_exit(struct ipu3_mmu_info *info); -void ipu3_mmu_suspend(struct ipu3_mmu_info *info); -void ipu3_mmu_resume(struct ipu3_mmu_info *info); +struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base); +void imgu_mmu_exit(struct imgu_mmu_info *info); +void imgu_mmu_suspend(struct imgu_mmu_info *info); +void imgu_mmu_resume(struct imgu_mmu_info *info); -int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, +int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size); -size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size); -size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents); #endif diff --git a/drivers/staging/media/ipu3/ipu3-tables.c b/drivers/staging/media/ipu3/ipu3-tables.c index 334517987eba..3a3730bd4395 100644 --- a/drivers/staging/media/ipu3/ipu3-tables.c +++ b/drivers/staging/media/ipu3/ipu3-tables.c @@ -5,8 +5,8 @@ #define X 0 /* Don't care value */ -const struct ipu3_css_bds_config - ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { { +const struct imgu_css_bds_config + imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { { /* Scale factor 32 / (32 + 0) = 1 */ .hor_phase_arr = { .even = { { 0, 0, 64, 6, 0, 0, 0 } }, @@ -9015,7 +9015,7 @@ const struct ipu3_css_bds_config .ver_ds_en = 1 } }; -const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { +const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { IMGU_SCALER_FP * -0.000000000000000, IMGU_SCALER_FP * -0.000249009327023, IMGU_SCALER_FP * -0.001022241683322, @@ -9146,7 +9146,7 @@ const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { IMGU_SCALER_FP * -0.000249009327023 }; -const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { +const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { IMGU_SCALER_FP * 0.074300676367033, IMGU_SCALER_FP * 0.094030234498392, IMGU_SCALER_FP * 0.115522859526596, @@ -9214,7 +9214,7 @@ const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { }; /* settings for Geometric Distortion Correction */ -const s16 ipu3_css_gdc_lut[4][256] = { { +const s16 imgu_css_gdc_lut[4][256] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -5, -5, -5, -6, -6, -7, -7, -7, -8, -8, -9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, @@ -9292,7 +9292,7 @@ const s16 ipu3_css_gdc_lut[4][256] = { { -1, 0, 1, 0, 0, 0, 0, 0, 0, 0 } }; -const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = { +const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults = { .x = { 1024, 1164, 1320, 1492, 1680, 1884, 2108, 2352, 2616, 2900, 3208, 3540, 3896, 4276, 4684, 5120 @@ -9311,7 +9311,7 @@ const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = { }; /* settings for Bayer Noise Reduction */ -const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = { +const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults = { { 16, 16, 16, 16 }, /* wb_gains */ { 16, 16, 16, 16 }, /* wb_gains_thr */ { 0, X, 8, 6, X, 14 }, /* thr_coeffs */ @@ -9327,18 +9327,18 @@ const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = { { 8, 4, 4, X, 8, X, 1, 1, 1, 1 }, /* dn_detect_ctrl */ }; -const struct ipu3_uapi_dm_config ipu3_css_dm_defaults = { +const struct ipu3_uapi_dm_config imgu_css_dm_defaults = { 1, 1, 1, X, X, 8, X, 7, X, 8, X, 8, X, 4, X }; -const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults = { +const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults = { 9775, -2671, 1087, 0, -1071, 8303, 815, 0, -23, -7887, 16103, 0 }; /* settings for Gamma correction */ -const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { { +const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut = { { 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287, 303, 319, 335, 351, 367, 383, 399, 415, 431, 447, 463, 479, 495, 511, 527, 543, 559, 575, 591, 607, 623, 639, 655, 671, 687, 703, 719, 735, @@ -9362,13 +9362,13 @@ const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { { 7807, 7871, 7935, 7999, 8063, 8127, 8191 } }; -const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults = { +const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults = { 4898, 9617, 1867, 0, -2410, -4732, 7143, 0, 10076, -8437, -1638, 0 }; -const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = { +const struct ipu3_uapi_cds_params imgu_css_cds_defaults = { 1, 3, 3, 1, 1, 3, 3, 1, 4, X, /* ds_nf */ @@ -9376,7 +9376,7 @@ const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = { 0, X /* uv_bin_output */ }; -const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = { +const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults = { .grid = { .width = 73, .height = 55, @@ -9397,7 +9397,7 @@ const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = { }, }; -const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = { +const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults = { .units = { .cu_1 = { 0, 150, 7, 0 }, .cu_ed = { 7, 110, 244, X, 307, 409, 511, X, @@ -9436,17 +9436,17 @@ const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = { { 1, X, 2, X, 8, X } }, }; -const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults = { +const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults = { 0, 1, 1, 0, 0, 1, 1, 0, 2, X, 0, X }; -const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults = { +const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults = { .coring = { 0, X, 0, X }, .sense_gain = { 6, 6, 6, X, 4, 4, 4, X }, .iir_fir = { 8, X, 12, X, 0, 256 - 127, X }, }; -const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = { +const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults = { .lpf = { 4, X, 8, X, 16, X, 0 }, .sense = { 8191, X, 0, X, 8191, X, 0, X }, .gain = { 8, X, 0, X, 8, X, 0, X }, @@ -9457,7 +9457,7 @@ const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = { }; const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - ipu3_css_tcc_gain_pcwl_lut = { { + imgu_css_tcc_gain_pcwl_lut = { { 1024, 1032, 1040, 1048, 1057, 1065, 1073, 1081, 1089, 1097, 1105, 1113, 1122, 1130, 1138, 1146, 1154, 1162, 1170, 1178, 1187, 1195, 1203, 1211, 1219, 1227, 1235, 1243, 1252, 1260, 1268, 1276, 1284, 1292, 1300, 1308, @@ -9483,12 +9483,12 @@ const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config } }; const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - ipu3_css_tcc_r_sqr_lut = { { + imgu_css_tcc_r_sqr_lut = { { 32, 44, 64, 92, 128, 180, 256, 364, 512, 628, 724, 808, 888, 956, 1024, 1088, 1144, 1200, 1256, 1304, 1356, 1404, 1448 } }; -const struct imgu_abi_anr_config ipu3_css_anr_defaults = { +const struct imgu_abi_anr_config imgu_css_anr_defaults = { .transform = { .adaptive_treshhold_en = 1, .alpha = { { 13, 13, 13, 13, 0, 0, 0, 0}, @@ -9545,7 +9545,7 @@ const struct imgu_abi_anr_config ipu3_css_anr_defaults = { }; /* frame settings for Auto White Balance */ -const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = { +const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults = { .grid_cfg = { .width = 16, .height = 16, @@ -9560,7 +9560,7 @@ const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = { }; /* settings for Auto Exposure */ -const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = { +const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults = { .width = 16, .height = 16, .block_width_log2 = 3, @@ -9571,13 +9571,13 @@ const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = { }; /* settings for Auto Exposure color correction matrix */ -const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults = { +const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults = { 256, 256, 256, 256, /* gain_gr/r/b/gb */ .mat = { 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128 }, }; /* settings for Auto Focus */ -const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = { +const struct ipu3_uapi_af_config_s imgu_css_af_defaults = { .filter_config = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0, @@ -9595,7 +9595,7 @@ const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = { }; /* settings for Auto White Balance */ -const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults = { +const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults = { 8191, 8191, 8191, 8191 | /* rgbs_thr_gr/r/gb/b */ IPU3_UAPI_AWB_RGBS_THR_B_EN | IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT, .grid = { diff --git a/drivers/staging/media/ipu3/ipu3-tables.h b/drivers/staging/media/ipu3/ipu3-tables.h index 6563782cbd22..a1bf3286f380 100644 --- a/drivers/staging/media/ipu3/ipu3-tables.h +++ b/drivers/staging/media/ipu3/ipu3-tables.h @@ -19,7 +19,7 @@ #define IMGU_GDC_LUT_UNIT 4 #define IMGU_GDC_LUT_LEN 256 -struct ipu3_css_bds_config { +struct imgu_css_bds_config { struct imgu_abi_bds_phase_arr hor_phase_arr; struct imgu_abi_bds_phase_arr ver_phase_arr; struct imgu_abi_bds_ptrn_arr ptrn_arr; @@ -28,39 +28,39 @@ struct ipu3_css_bds_config { u8 ver_ds_en; }; -struct ipu3_css_xnr3_vmem_defaults { +struct imgu_css_xnr3_vmem_defaults { s16 x[IMGU_XNR3_VMEM_LUT_LEN]; s16 a[IMGU_XNR3_VMEM_LUT_LEN]; s16 b[IMGU_XNR3_VMEM_LUT_LEN]; s16 c[IMGU_XNR3_VMEM_LUT_LEN]; }; -extern const struct ipu3_css_bds_config - ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN]; -extern const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN]; -extern const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN]; -extern const s16 ipu3_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN]; -extern const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults; -extern const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults; -extern const struct ipu3_uapi_dm_config ipu3_css_dm_defaults; -extern const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults; -extern const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut; -extern const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults; -extern const struct ipu3_uapi_cds_params ipu3_css_cds_defaults; -extern const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults; -extern const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults; -extern const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults; -extern const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults; -extern const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults; +extern const struct imgu_css_bds_config + imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN]; +extern const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN]; +extern const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN]; +extern const s16 imgu_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN]; +extern const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults; +extern const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults; +extern const struct ipu3_uapi_dm_config imgu_css_dm_defaults; +extern const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults; +extern const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut; +extern const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults; +extern const struct ipu3_uapi_cds_params imgu_css_cds_defaults; +extern const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults; +extern const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults; +extern const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults; +extern const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults; +extern const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults; extern const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - ipu3_css_tcc_gain_pcwl_lut; + imgu_css_tcc_gain_pcwl_lut; extern const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - ipu3_css_tcc_r_sqr_lut; -extern const struct imgu_abi_anr_config ipu3_css_anr_defaults; -extern const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults; -extern const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults; -extern const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults; -extern const struct ipu3_uapi_af_config_s ipu3_css_af_defaults; -extern const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults; + imgu_css_tcc_r_sqr_lut; +extern const struct imgu_abi_anr_config imgu_css_anr_defaults; +extern const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults; +extern const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults; +extern const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults; +extern const struct ipu3_uapi_af_config_s imgu_css_af_defaults; +extern const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults; #endif diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index e758a650ad2b..9c0352b193a7 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -15,7 +15,7 @@ #define IPU3_RUNNING_MODE_VIDEO 0 #define IPU3_RUNNING_MODE_STILL 1 -static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imgu_v4l2_subdev *imgu_sd = container_of(sd, struct imgu_v4l2_subdev, @@ -50,7 +50,7 @@ static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) +static int imgu_subdev_s_stream(struct v4l2_subdev *sd, int enable) { int i; unsigned int node; @@ -63,7 +63,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) struct device *dev = &imgu->pci_dev->dev; struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES] = { NULL }; struct v4l2_rect *rects[IPU3_CSS_RECTS] = { NULL }; - struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe]; + struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe]; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; dev_dbg(dev, "%s %d for pipe %d", __func__, enable, pipe); @@ -107,7 +107,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) rects[IPU3_CSS_RECT_BDS] = &imgu_sd->rect.bds; rects[IPU3_CSS_RECT_GDC] = &imgu_sd->rect.gdc; - r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe); if (r) { dev_err(dev, "failed to set initial formats pipe %d with (%d)", pipe, r); @@ -119,7 +119,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd, +static int imgu_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { @@ -143,7 +143,7 @@ static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd, +static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { @@ -189,7 +189,7 @@ static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_get_selection(struct v4l2_subdev *sd, +static int imgu_subdev_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { @@ -222,7 +222,7 @@ static int ipu3_subdev_get_selection(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_set_selection(struct v4l2_subdev *sd, +static int imgu_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { @@ -263,7 +263,7 @@ static int ipu3_subdev_set_selection(struct v4l2_subdev *sd, /******************** media_entity_operations ********************/ -static int ipu3_link_setup(struct media_entity *entity, +static int imgu_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { @@ -302,7 +302,7 @@ static int ipu3_link_setup(struct media_entity *entity, /******************** vb2_ops ********************/ -static int ipu3_vb2_buf_init(struct vb2_buffer *vb) +static int imgu_vb2_buf_init(struct vb2_buffer *vb) { struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); @@ -315,11 +315,11 @@ static int ipu3_vb2_buf_init(struct vb2_buffer *vb) if (queue == IPU3_CSS_QUEUE_PARAMS) return 0; - return ipu3_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map); + return imgu_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map); } /* Called when each buffer is freed */ -static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb) +static void imgu_vb2_buf_cleanup(struct vb2_buffer *vb) { struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); struct imgu_buffer *buf = container_of(vb, @@ -331,11 +331,11 @@ static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb) if (queue == IPU3_CSS_QUEUE_PARAMS) return; - ipu3_dmamap_unmap(imgu, &buf->map); + imgu_dmamap_unmap(imgu, &buf->map); } /* Transfer buffer ownership to me */ -static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) +static void imgu_vb2_buf_queue(struct vb2_buffer *vb) { struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); struct imgu_video_device *node = @@ -361,7 +361,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) vb2_set_plane_payload(vb, 0, payload); } if (payload >= need_bytes) - r = ipu3_css_set_parameters(&imgu->css, pipe, + r = imgu_css_set_parameters(&imgu->css, pipe, vb2_plane_vaddr(vb, 0)); buf->flags = V4L2_BUF_FLAG_DONE; vb2_buffer_done(vb, r == 0 ? VB2_BUF_STATE_DONE @@ -372,7 +372,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) vid_buf.vbb.vb2_buf); mutex_lock(&imgu->lock); - ipu3_css_buf_init(&buf->css_buf, queue, buf->map.daddr); + imgu_css_buf_init(&buf->css_buf, queue, buf->map.daddr); list_add_tail(&buf->vid_buf.list, &node->buffers); mutex_unlock(&imgu->lock); @@ -388,7 +388,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) } -static int ipu3_vb2_queue_setup(struct vb2_queue *vq, +static int imgu_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], @@ -425,7 +425,7 @@ static int ipu3_vb2_queue_setup(struct vb2_queue *vq, } /* Check if all enabled video nodes are streaming, exception ignored */ -static bool ipu3_all_nodes_streaming(struct imgu_device *imgu, +static bool imgu_all_nodes_streaming(struct imgu_device *imgu, struct imgu_video_device *except) { unsigned int i, pipe, p; @@ -454,11 +454,11 @@ static bool ipu3_all_nodes_streaming(struct imgu_device *imgu, return true; } -static void ipu3_return_all_buffers(struct imgu_device *imgu, +static void imgu_return_all_buffers(struct imgu_device *imgu, struct imgu_video_device *node, enum vb2_buffer_state state) { - struct ipu3_vb2_buffer *b, *b0; + struct imgu_vb2_buffer *b, *b0; /* Return all buffers */ mutex_lock(&imgu->lock); @@ -469,7 +469,7 @@ static void ipu3_return_all_buffers(struct imgu_device *imgu, mutex_unlock(&imgu->lock); } -static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +static int imgu_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) { struct imgu_media_pipe *imgu_pipe; struct imgu_device *imgu = vb2_get_drv_priv(vq); @@ -500,7 +500,7 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) goto fail_return_bufs; - if (!ipu3_all_nodes_streaming(imgu, node)) + if (!imgu_all_nodes_streaming(imgu, node)) return 0; for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) { @@ -521,12 +521,12 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) fail_stop_pipeline: media_pipeline_stop(&node->vdev.entity); fail_return_bufs: - ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); return r; } -static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) +static void imgu_vb2_stop_streaming(struct vb2_queue *vq) { struct imgu_media_pipe *imgu_pipe; struct imgu_device *imgu = vb2_get_drv_priv(vq); @@ -547,7 +547,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) "failed to stop subdev streaming\n"); /* Was this the first node with streaming disabled? */ - if (imgu->streaming && ipu3_all_nodes_streaming(imgu, node)) { + if (imgu->streaming && imgu_all_nodes_streaming(imgu, node)) { /* Yes, really stop streaming now */ dev_dbg(dev, "IMGU streaming is ready to stop"); r = imgu_s_stream(imgu, false); @@ -555,7 +555,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) imgu->streaming = false; } - ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); media_pipeline_stop(&node->vdev.entity); } @@ -566,13 +566,13 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) #define DEF_VID_CAPTURE 0 #define DEF_VID_OUTPUT 1 -struct ipu3_fmt { +struct imgu_fmt { u32 fourcc; u16 type; /* VID_CAPTURE or VID_OUTPUT not both */ }; /* format descriptions for capture and preview */ -static const struct ipu3_fmt formats[] = { +static const struct imgu_fmt formats[] = { { V4L2_PIX_FMT_NV12, VID_CAPTURE }, { V4L2_PIX_FMT_IPU3_SGRBG10, VID_OUTPUT }, { V4L2_PIX_FMT_IPU3_SBGGR10, VID_OUTPUT }, @@ -581,7 +581,7 @@ static const struct ipu3_fmt formats[] = { }; /* Find the first matched format, return default if not found */ -static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type) +static const struct imgu_fmt *find_format(struct v4l2_format *f, u32 type) { unsigned int i; @@ -595,10 +595,10 @@ static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type) &formats[DEF_VID_OUTPUT]; } -static int ipu3_vidioc_querycap(struct file *file, void *fh, +static int imgu_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); strscpy(cap->driver, IMGU_NAME, sizeof(cap->driver)); strscpy(cap->card, IMGU_NAME, sizeof(cap->card)); @@ -646,10 +646,10 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, } /* Propagate forward always the format from the CIO2 subdev */ -static int ipu3_vidioc_g_fmt(struct file *file, void *fh, +static int imgu_vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); f->fmt = node->vdev_fmt.fmt; @@ -670,7 +670,7 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, struct v4l2_mbus_framefmt pad_fmt; unsigned int i, css_q; int r; - struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe]; + struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe]; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; struct imgu_v4l2_subdev *imgu_sd = &imgu_pipe->imgu_sd; @@ -736,9 +736,9 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, return -EINVAL; if (try) - r = ipu3_css_fmt_try(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_try(&imgu->css, fmts, rects, pipe); else - r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe); /* r is the binary number in the firmware blob */ if (r < 0) @@ -752,10 +752,10 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, return 0; } -static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +static int imgu_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; - const struct ipu3_fmt *fmt; + const struct imgu_fmt *fmt; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fmt = find_format(f, VID_CAPTURE); @@ -772,58 +772,58 @@ static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f) return 0; } -static int ipu3_vidioc_try_fmt(struct file *file, void *fh, +static int imgu_vidioc_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct imgu_device *imgu = video_drvdata(file); struct device *dev = &imgu->pci_dev->dev; - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; int r; dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__, pix_mp->width, pix_mp->height, node->id); - r = ipu3_try_fmt(file, fh, f); + r = imgu_try_fmt(file, fh, f); if (r) return r; return imgu_fmt(imgu, node->pipe, node->id, f, true); } -static int ipu3_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +static int imgu_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct imgu_device *imgu = video_drvdata(file); struct device *dev = &imgu->pci_dev->dev; - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; int r; dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__, pix_mp->width, pix_mp->height, node->id); - r = ipu3_try_fmt(file, fh, f); + r = imgu_try_fmt(file, fh, f); if (r) return r; return imgu_fmt(imgu, node->pipe, node->id, f, false); } -struct ipu3_meta_fmt { +struct imgu_meta_fmt { __u32 fourcc; char *name; }; /* From drivers/media/v4l2-core/v4l2-ioctl.c */ -static const struct ipu3_meta_fmt meta_fmts[] = { +static const struct imgu_meta_fmt meta_fmts[] = { { V4L2_META_FMT_IPU3_PARAMS, "IPU3 processing parameters" }, { V4L2_META_FMT_IPU3_STAT_3A, "IPU3 3A statistics" }, }; -static int ipu3_meta_enum_format(struct file *file, void *fh, +static int imgu_meta_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); unsigned int i = fmt->type == V4L2_BUF_TYPE_META_OUTPUT ? 0 : 1; /* Each node is dedicated to only one meta format */ @@ -836,10 +836,10 @@ static int ipu3_meta_enum_format(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh, +static int imgu_vidioc_g_meta_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); if (f->type != node->vbq.type) return -EINVAL; @@ -849,7 +849,7 @@ static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_enum_input(struct file *file, void *fh, +static int imgu_vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *input) { if (input->index > 0) @@ -860,19 +860,19 @@ static int ipu3_vidioc_enum_input(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_input(struct file *file, void *fh, unsigned int *input) +static int imgu_vidioc_g_input(struct file *file, void *fh, unsigned int *input) { *input = 0; return 0; } -static int ipu3_vidioc_s_input(struct file *file, void *fh, unsigned int input) +static int imgu_vidioc_s_input(struct file *file, void *fh, unsigned int input) { return input == 0 ? 0 : -EINVAL; } -static int ipu3_vidioc_enum_output(struct file *file, void *fh, +static int imgu_vidioc_enum_output(struct file *file, void *fh, struct v4l2_output *output) { if (output->index > 0) @@ -883,7 +883,7 @@ static int ipu3_vidioc_enum_output(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_output(struct file *file, void *fh, +static int imgu_vidioc_g_output(struct file *file, void *fh, unsigned int *output) { *output = 0; @@ -891,7 +891,7 @@ static int ipu3_vidioc_g_output(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_s_output(struct file *file, void *fh, +static int imgu_vidioc_s_output(struct file *file, void *fh, unsigned int output) { return output == 0 ? 0 : -EINVAL; @@ -899,54 +899,54 @@ static int ipu3_vidioc_s_output(struct file *file, void *fh, /******************** function pointers ********************/ -static struct v4l2_subdev_internal_ops ipu3_subdev_internal_ops = { - .open = ipu3_subdev_open, +static struct v4l2_subdev_internal_ops imgu_subdev_internal_ops = { + .open = imgu_subdev_open, }; -static const struct v4l2_subdev_core_ops ipu3_subdev_core_ops = { +static const struct v4l2_subdev_core_ops imgu_subdev_core_ops = { .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops ipu3_subdev_video_ops = { - .s_stream = ipu3_subdev_s_stream, +static const struct v4l2_subdev_video_ops imgu_subdev_video_ops = { + .s_stream = imgu_subdev_s_stream, }; -static const struct v4l2_subdev_pad_ops ipu3_subdev_pad_ops = { +static const struct v4l2_subdev_pad_ops imgu_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .get_fmt = ipu3_subdev_get_fmt, - .set_fmt = ipu3_subdev_set_fmt, - .get_selection = ipu3_subdev_get_selection, - .set_selection = ipu3_subdev_set_selection, + .get_fmt = imgu_subdev_get_fmt, + .set_fmt = imgu_subdev_set_fmt, + .get_selection = imgu_subdev_get_selection, + .set_selection = imgu_subdev_set_selection, }; -static const struct v4l2_subdev_ops ipu3_subdev_ops = { - .core = &ipu3_subdev_core_ops, - .video = &ipu3_subdev_video_ops, - .pad = &ipu3_subdev_pad_ops, +static const struct v4l2_subdev_ops imgu_subdev_ops = { + .core = &imgu_subdev_core_ops, + .video = &imgu_subdev_video_ops, + .pad = &imgu_subdev_pad_ops, }; -static const struct media_entity_operations ipu3_media_ops = { - .link_setup = ipu3_link_setup, +static const struct media_entity_operations imgu_media_ops = { + .link_setup = imgu_link_setup, .link_validate = v4l2_subdev_link_validate, }; /****************** vb2_ops of the Q ********************/ -static const struct vb2_ops ipu3_vb2_ops = { - .buf_init = ipu3_vb2_buf_init, - .buf_cleanup = ipu3_vb2_buf_cleanup, - .buf_queue = ipu3_vb2_buf_queue, - .queue_setup = ipu3_vb2_queue_setup, - .start_streaming = ipu3_vb2_start_streaming, - .stop_streaming = ipu3_vb2_stop_streaming, +static const struct vb2_ops imgu_vb2_ops = { + .buf_init = imgu_vb2_buf_init, + .buf_cleanup = imgu_vb2_buf_cleanup, + .buf_queue = imgu_vb2_buf_queue, + .queue_setup = imgu_vb2_queue_setup, + .start_streaming = imgu_vb2_start_streaming, + .stop_streaming = imgu_vb2_stop_streaming, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, }; /****************** v4l2_file_operations *****************/ -static const struct v4l2_file_operations ipu3_v4l2_fops = { +static const struct v4l2_file_operations imgu_v4l2_fops = { .unlocked_ioctl = video_ioctl2, .open = v4l2_fh_open, .release = vb2_fop_release, @@ -956,26 +956,26 @@ static const struct v4l2_file_operations ipu3_v4l2_fops = { /******************** v4l2_ioctl_ops ********************/ -static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = { - .vidioc_querycap = ipu3_vidioc_querycap, +static const struct v4l2_ioctl_ops imgu_v4l2_ioctl_ops = { + .vidioc_querycap = imgu_vidioc_querycap, .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap_mplane = ipu3_vidioc_g_fmt, - .vidioc_s_fmt_vid_cap_mplane = ipu3_vidioc_s_fmt, - .vidioc_try_fmt_vid_cap_mplane = ipu3_vidioc_try_fmt, + .vidioc_g_fmt_vid_cap_mplane = imgu_vidioc_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = imgu_vidioc_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = imgu_vidioc_try_fmt, .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, - .vidioc_g_fmt_vid_out_mplane = ipu3_vidioc_g_fmt, - .vidioc_s_fmt_vid_out_mplane = ipu3_vidioc_s_fmt, - .vidioc_try_fmt_vid_out_mplane = ipu3_vidioc_try_fmt, + .vidioc_g_fmt_vid_out_mplane = imgu_vidioc_g_fmt, + .vidioc_s_fmt_vid_out_mplane = imgu_vidioc_s_fmt, + .vidioc_try_fmt_vid_out_mplane = imgu_vidioc_try_fmt, - .vidioc_enum_output = ipu3_vidioc_enum_output, - .vidioc_g_output = ipu3_vidioc_g_output, - .vidioc_s_output = ipu3_vidioc_s_output, + .vidioc_enum_output = imgu_vidioc_enum_output, + .vidioc_g_output = imgu_vidioc_g_output, + .vidioc_s_output = imgu_vidioc_s_output, - .vidioc_enum_input = ipu3_vidioc_enum_input, - .vidioc_g_input = ipu3_vidioc_g_input, - .vidioc_s_input = ipu3_vidioc_s_input, + .vidioc_enum_input = imgu_vidioc_enum_input, + .vidioc_g_input = imgu_vidioc_g_input, + .vidioc_s_input = imgu_vidioc_s_input, /* buffer queue management */ .vidioc_reqbufs = vb2_ioctl_reqbufs, @@ -989,20 +989,20 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, }; -static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = { - .vidioc_querycap = ipu3_vidioc_querycap, +static const struct v4l2_ioctl_ops imgu_v4l2_meta_ioctl_ops = { + .vidioc_querycap = imgu_vidioc_querycap, /* meta capture */ - .vidioc_enum_fmt_meta_cap = ipu3_meta_enum_format, - .vidioc_g_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, - .vidioc_s_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, - .vidioc_try_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, + .vidioc_enum_fmt_meta_cap = imgu_meta_enum_format, + .vidioc_g_fmt_meta_cap = imgu_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_cap = imgu_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_cap = imgu_vidioc_g_meta_fmt, /* meta output */ - .vidioc_enum_fmt_meta_out = ipu3_meta_enum_format, - .vidioc_g_fmt_meta_out = ipu3_vidioc_g_meta_fmt, - .vidioc_s_fmt_meta_out = ipu3_vidioc_g_meta_fmt, - .vidioc_try_fmt_meta_out = ipu3_vidioc_g_meta_fmt, + .vidioc_enum_fmt_meta_out = imgu_meta_enum_format, + .vidioc_g_fmt_meta_out = imgu_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_out = imgu_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_out = imgu_vidioc_g_meta_fmt, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -1015,7 +1015,7 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, }; -static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int imgu_sd_s_ctrl(struct v4l2_ctrl *ctrl) { struct imgu_v4l2_subdev *imgu_sd = container_of(ctrl->handler, struct imgu_v4l2_subdev, ctrl_handler); @@ -1034,29 +1034,29 @@ static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl) } } -static const struct v4l2_ctrl_ops ipu3_subdev_ctrl_ops = { - .s_ctrl = ipu3_sd_s_ctrl, +static const struct v4l2_ctrl_ops imgu_subdev_ctrl_ops = { + .s_ctrl = imgu_sd_s_ctrl, }; -static const char * const ipu3_ctrl_mode_strings[] = { +static const char * const imgu_ctrl_mode_strings[] = { "Video mode", "Still mode", }; -static const struct v4l2_ctrl_config ipu3_subdev_ctrl_mode = { - .ops = &ipu3_subdev_ctrl_ops, +static const struct v4l2_ctrl_config imgu_subdev_ctrl_mode = { + .ops = &imgu_subdev_ctrl_ops, .id = V4L2_CID_INTEL_IPU3_MODE, .name = "IPU3 Pipe Mode", .type = V4L2_CTRL_TYPE_MENU, - .max = ARRAY_SIZE(ipu3_ctrl_mode_strings) - 1, + .max = ARRAY_SIZE(imgu_ctrl_mode_strings) - 1, .def = IPU3_RUNNING_MODE_VIDEO, - .qmenu = ipu3_ctrl_mode_strings, + .qmenu = imgu_ctrl_mode_strings, }; /******************** Framework registration ********************/ /* helper function to config node's video properties */ -static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev, +static void imgu_node_to_v4l2(u32 node, struct video_device *vdev, struct v4l2_format *f) { u32 cap; @@ -1068,32 +1068,32 @@ static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev, case IMGU_NODE_IN: cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE; f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops; + vdev->ioctl_ops = &imgu_v4l2_ioctl_ops; break; case IMGU_NODE_PARAMS: cap = V4L2_CAP_META_OUTPUT; f->type = V4L2_BUF_TYPE_META_OUTPUT; f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_PARAMS; - vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops; - ipu3_css_meta_fmt_set(&f->fmt.meta); + vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops; + imgu_css_meta_fmt_set(&f->fmt.meta); break; case IMGU_NODE_STAT_3A: cap = V4L2_CAP_META_CAPTURE; f->type = V4L2_BUF_TYPE_META_CAPTURE; f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_STAT_3A; - vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops; - ipu3_css_meta_fmt_set(&f->fmt.meta); + vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops; + imgu_css_meta_fmt_set(&f->fmt.meta); break; default: cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops; + vdev->ioctl_ops = &imgu_v4l2_ioctl_ops; } vdev->device_caps = V4L2_CAP_STREAMING | cap; } -static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, +static int imgu_v4l2_subdev_register(struct imgu_device *imgu, struct imgu_v4l2_subdev *imgu_sd, unsigned int pipe) { @@ -1109,16 +1109,16 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, "failed initialize subdev media entity (%d)\n", r); return r; } - imgu_sd->subdev.entity.ops = &ipu3_media_ops; + imgu_sd->subdev.entity.ops = &imgu_media_ops; for (i = 0; i < IMGU_NODE_NUM; i++) { imgu_sd->subdev_pads[i].flags = imgu_pipe->nodes[i].output ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; } /* Initialize subdev */ - v4l2_subdev_init(&imgu_sd->subdev, &ipu3_subdev_ops); + v4l2_subdev_init(&imgu_sd->subdev, &imgu_subdev_ops); imgu_sd->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS; - imgu_sd->subdev.internal_ops = &ipu3_subdev_internal_ops; + imgu_sd->subdev.internal_ops = &imgu_subdev_internal_ops; imgu_sd->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(imgu_sd->subdev.name, sizeof(imgu_sd->subdev.name), @@ -1127,7 +1127,7 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, atomic_set(&imgu_sd->running_mode, IPU3_RUNNING_MODE_VIDEO); v4l2_ctrl_handler_init(hdl, 1); imgu_sd->subdev.ctrl_handler = hdl; - imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &ipu3_subdev_ctrl_mode, NULL); + imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &imgu_subdev_ctrl_mode, NULL); if (hdl->error) { r = hdl->error; dev_err(&imgu->pci_dev->dev, @@ -1151,7 +1151,7 @@ fail_subdev: return r; } -static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, +static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, int node_num) { int r; @@ -1196,7 +1196,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, node->pad_fmt = def_bus_fmt; node->id = node_num; node->pipe = pipe; - ipu3_node_to_v4l2(node_num, vdev, &node->vdev_fmt); + imgu_node_to_v4l2(node_num, vdev, &node->vdev_fmt); if (node->vdev_fmt.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || node->vdev_fmt.type == @@ -1221,11 +1221,11 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, /* Initialize vbq */ vbq->type = node->vdev_fmt.type; vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; - vbq->ops = &ipu3_vb2_ops; + vbq->ops = &imgu_vb2_ops; vbq->mem_ops = &vb2_dma_sg_memops; if (imgu->buf_struct_size <= 0) imgu->buf_struct_size = - sizeof(struct ipu3_vb2_buffer); + sizeof(struct imgu_vb2_buffer); vbq->buf_struct_size = imgu->buf_struct_size; vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; /* can streamon w/o buffers */ @@ -1243,7 +1243,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, snprintf(vdev->name, sizeof(vdev->name), "%s %d %s", IMGU_NAME, pipe, node->name); vdev->release = video_device_release_empty; - vdev->fops = &ipu3_v4l2_fops; + vdev->fops = &imgu_v4l2_fops; vdev->lock = &node->lock; vdev->v4l2_dev = &imgu->v4l2_dev; vdev->queue = &node->vbq; @@ -1276,7 +1276,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, return 0; } -static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, +static void imgu_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, unsigned int pipe, int node) { int i; @@ -1289,12 +1289,12 @@ static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, } } -static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) +static int imgu_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) { int i, r; for (i = 0; i < IMGU_NODE_NUM; i++) { - r = ipu3_v4l2_node_setup(imgu, pipe, i); + r = imgu_v4l2_node_setup(imgu, pipe, i); if (r) goto cleanup; } @@ -1302,11 +1302,11 @@ static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) return 0; cleanup: - ipu3_v4l2_nodes_cleanup_pipe(imgu, pipe, i); + imgu_v4l2_nodes_cleanup_pipe(imgu, pipe, i); return r; } -static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) +static void imgu_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) { struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[i]; @@ -1315,13 +1315,13 @@ static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) media_entity_cleanup(&imgu_pipe->imgu_sd.subdev.entity); } -static void ipu3_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe) +static void imgu_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe) { int i; for (i = 0; i < pipe; i++) { - ipu3_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM); - ipu3_v4l2_subdev_cleanup(imgu, i); + imgu_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM); + imgu_v4l2_subdev_cleanup(imgu, i); } } @@ -1332,15 +1332,15 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu) for (i = 0; i < IMGU_MAX_PIPE_NUM; i++) { imgu_pipe = &imgu->imgu_pipe[i]; - r = ipu3_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i); + r = imgu_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i); if (r) { dev_err(&imgu->pci_dev->dev, "failed to register subdev%d ret (%d)\n", i, r); goto pipes_cleanup; } - r = ipu3_v4l2_nodes_setup_pipe(imgu, i); + r = imgu_v4l2_nodes_setup_pipe(imgu, i); if (r) { - ipu3_v4l2_subdev_cleanup(imgu, i); + imgu_v4l2_subdev_cleanup(imgu, i); goto pipes_cleanup; } } @@ -1348,7 +1348,7 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu) return 0; pipes_cleanup: - ipu3_v4l2_cleanup_pipes(imgu, i); + imgu_v4l2_cleanup_pipes(imgu, i); return r; } @@ -1396,7 +1396,7 @@ int imgu_v4l2_register(struct imgu_device *imgu) return 0; fail_subdevs: - ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); + imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); fail_v4l2_pipes: v4l2_device_unregister(&imgu->v4l2_dev); fail_v4l2_dev: @@ -1408,7 +1408,7 @@ fail_v4l2_dev: int imgu_v4l2_unregister(struct imgu_device *imgu) { media_device_unregister(&imgu->media_dev); - ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); + imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); v4l2_device_unregister(&imgu->v4l2_dev); media_device_cleanup(&imgu->media_dev); @@ -1418,8 +1418,8 @@ int imgu_v4l2_unregister(struct imgu_device *imgu) void imgu_v4l2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { - struct ipu3_vb2_buffer *b = - container_of(vb, struct ipu3_vb2_buffer, vbb.vb2_buf); + struct imgu_vb2_buffer *b = + container_of(vb, struct imgu_vb2_buffer, vbb.vb2_buf); list_del(&b->list); vb2_buffer_done(&b->vbb.vb2_buf, state); diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index 839d9398f8e9..d575ac78c8f0 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -72,7 +72,7 @@ static void imgu_dummybufs_cleanup(struct imgu_device *imgu, unsigned int pipe) struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; for (i = 0; i < IPU3_CSS_QUEUES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &imgu_pipe->queues[i].dmap); } @@ -93,7 +93,7 @@ static int imgu_dummybufs_preallocate(struct imgu_device *imgu, if (i == IMGU_QUEUE_MASTER || size == 0) continue; - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &imgu_pipe->queues[i].dmap, size)) { imgu_dummybufs_cleanup(imgu, pipe); return -ENOMEM; @@ -133,7 +133,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) else size = mpix->plane_fmt[0].sizeimage; - if (ipu3_css_dma_buffer_resize(imgu, + if (imgu_css_dma_buffer_resize(imgu, &imgu_pipe->queues[i].dmap, size)) { imgu_dummybufs_cleanup(imgu, pipe); @@ -141,7 +141,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) } for (k = 0; k < IMGU_MAX_QUEUE_DEPTH; k++) - ipu3_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i, + imgu_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i, imgu_pipe->queues[i].dmap.daddr); } @@ -149,7 +149,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) } /* May be called from atomic context */ -static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, +static struct imgu_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, int queue, unsigned int pipe) { unsigned int i; @@ -164,14 +164,14 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, return NULL; for (i = 0; i < IMGU_MAX_QUEUE_DEPTH; i++) - if (ipu3_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) != + if (imgu_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) != IPU3_CSS_BUFFER_QUEUED) break; if (i == IMGU_MAX_QUEUE_DEPTH) return NULL; - ipu3_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue, + imgu_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue, imgu_pipe->queues[queue].dmap.daddr); return &imgu_pipe->queues[queue].dummybufs[i]; @@ -179,7 +179,7 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, /* Check if given buffer is a dummy buffer */ static bool imgu_dummybufs_check(struct imgu_device *imgu, - struct ipu3_css_buffer *buf, + struct imgu_css_buffer *buf, unsigned int pipe) { unsigned int i; @@ -200,7 +200,7 @@ static void imgu_buffer_done(struct imgu_device *imgu, struct vb2_buffer *vb, mutex_unlock(&imgu->lock); } -static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, +static struct imgu_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, unsigned int node, unsigned int pipe) { @@ -212,7 +212,7 @@ static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, /* Find first free buffer from the node */ list_for_each_entry(buf, &imgu_pipe->nodes[node].buffers, vid_buf.list) { - if (ipu3_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW) + if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW) return &buf->css_buf; } @@ -230,7 +230,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe int r = 0; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; - if (!ipu3_css_is_streaming(&imgu->css)) + if (!imgu_css_is_streaming(&imgu->css)) return 0; dev_dbg(&imgu->pci_dev->dev, "Queue buffers to pipe %d", pipe); @@ -247,7 +247,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe "Vf not enabled, ignore queue"); continue; } else if (imgu_pipe->queue_enabled[node]) { - struct ipu3_css_buffer *buf = + struct imgu_css_buffer *buf = imgu_queue_getbuf(imgu, node, pipe); struct imgu_buffer *ibuf = NULL; bool dummy; @@ -255,7 +255,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe if (!buf) break; - r = ipu3_css_buf_queue(&imgu->css, pipe, buf); + r = imgu_css_buf_queue(&imgu->css, pipe, buf); if (r) break; dummy = imgu_dummybufs_check(imgu, buf, pipe); @@ -300,7 +300,7 @@ failed: list_for_each_entry_safe(buf, buf0, &imgu_pipe->nodes[node].buffers, vid_buf.list) { - if (ipu3_css_buf_state(&buf->css_buf) == + if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_QUEUED) continue; /* Was already queued, skip */ @@ -317,18 +317,18 @@ static int imgu_powerup(struct imgu_device *imgu) { int r; - r = ipu3_css_set_powerup(&imgu->pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base); if (r) return r; - ipu3_mmu_resume(imgu->mmu); + imgu_mmu_resume(imgu->mmu); return 0; } static void imgu_powerdown(struct imgu_device *imgu) { - ipu3_mmu_suspend(imgu->mmu); - ipu3_css_set_powerdown(&imgu->pci_dev->dev, imgu->base); + imgu_mmu_suspend(imgu->mmu); + imgu_css_set_powerdown(&imgu->pci_dev->dev, imgu->base); } int imgu_s_stream(struct imgu_device *imgu, int enable) @@ -341,7 +341,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable) dev_dbg(dev, "stream off\n"); /* Block new buffers to be queued to CSS. */ atomic_set(&imgu->qbuf_barrier, 1); - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); synchronize_irq(imgu->pci_dev->irq); atomic_set(&imgu->qbuf_barrier, 0); imgu_powerdown(imgu); @@ -366,7 +366,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable) } /* Start CSS streaming */ - r = ipu3_css_start_streaming(&imgu->css); + r = imgu_css_start_streaming(&imgu->css); if (r) { dev_err(dev, "failed to start css streaming (%d)", r); goto fail_start_streaming; @@ -393,7 +393,7 @@ fail_queueing: for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) imgu_dummybufs_cleanup(imgu, pipe); fail_dummybufs: - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); fail_start_streaming: pm_runtime_put(dev); @@ -435,7 +435,7 @@ static int imgu_video_nodes_init(struct imgu_device *imgu) rects[IPU3_CSS_RECT_EFFECTIVE] = &imgu_pipe->imgu_sd.rect.eff; rects[IPU3_CSS_RECT_BDS] = &imgu_pipe->imgu_sd.rect.bds; - ipu3_css_fmt_set(&imgu->css, fmts, rects, j); + imgu_css_fmt_set(&imgu->css, fmts, rects, j); /* Pre-allocate dummy buffers */ r = imgu_dummybufs_preallocate(imgu, j); @@ -478,14 +478,14 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) /* Dequeue / queue buffers */ do { u64 ns = ktime_get_ns(); - struct ipu3_css_buffer *b; + struct imgu_css_buffer *b; struct imgu_buffer *buf = NULL; unsigned int node, pipe; bool dummy; do { mutex_lock(&imgu->lock); - b = ipu3_css_buf_dequeue(&imgu->css); + b = imgu_css_buf_dequeue(&imgu->css); mutex_unlock(&imgu->lock); } while (PTR_ERR(b) == -EAGAIN); @@ -525,12 +525,12 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) buf->vid_buf.vbb.sequence); } imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf, - ipu3_css_buf_state(&buf->css_buf) == + imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_DONE ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); mutex_lock(&imgu->lock); - if (ipu3_css_queue_empty(&imgu->css)) + if (imgu_css_queue_empty(&imgu->css)) wake_up_all(&imgu->buf_drain_wq); mutex_unlock(&imgu->lock); } while (1); @@ -552,7 +552,7 @@ static irqreturn_t imgu_isr(int irq, void *imgu_ptr) struct imgu_device *imgu = imgu_ptr; /* acknowledge interruption */ - if (ipu3_css_irq_ack(&imgu->css) < 0) + if (imgu_css_irq_ack(&imgu->css) < 0) return IRQ_NONE; return IRQ_WAKE_THREAD; @@ -637,21 +637,21 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, atomic_set(&imgu->qbuf_barrier, 0); init_waitqueue_head(&imgu->buf_drain_wq); - r = ipu3_css_set_powerup(&pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&pci_dev->dev, imgu->base); if (r) { dev_err(&pci_dev->dev, "failed to power up CSS (%d)\n", r); goto out_mutex_destroy; } - imgu->mmu = ipu3_mmu_init(&pci_dev->dev, imgu->base); + imgu->mmu = imgu_mmu_init(&pci_dev->dev, imgu->base); if (IS_ERR(imgu->mmu)) { r = PTR_ERR(imgu->mmu); dev_err(&pci_dev->dev, "failed to initialize MMU (%d)\n", r); goto out_css_powerdown; } - r = ipu3_dmamap_init(imgu); + r = imgu_dmamap_init(imgu); if (r) { dev_err(&pci_dev->dev, "failed to initialize DMA mapping (%d)\n", r); @@ -659,7 +659,7 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, } /* ISP programming */ - r = ipu3_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len); + r = imgu_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len); if (r) { dev_err(&pci_dev->dev, "failed to initialize CSS (%d)\n", r); goto out_dmamap_exit; @@ -689,13 +689,13 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, out_video_exit: imgu_video_nodes_exit(imgu); out_css_cleanup: - ipu3_css_cleanup(&imgu->css); + imgu_css_cleanup(&imgu->css); out_dmamap_exit: - ipu3_dmamap_exit(imgu); + imgu_dmamap_exit(imgu); out_mmu_exit: - ipu3_mmu_exit(imgu->mmu); + imgu_mmu_exit(imgu->mmu); out_css_powerdown: - ipu3_css_set_powerdown(&pci_dev->dev, imgu->base); + imgu_css_set_powerdown(&pci_dev->dev, imgu->base); out_mutex_destroy: mutex_destroy(&imgu->lock); @@ -710,10 +710,10 @@ static void imgu_pci_remove(struct pci_dev *pci_dev) pm_runtime_get_noresume(&pci_dev->dev); imgu_video_nodes_exit(imgu); - ipu3_css_cleanup(&imgu->css); - ipu3_css_set_powerdown(&pci_dev->dev, imgu->base); - ipu3_dmamap_exit(imgu); - ipu3_mmu_exit(imgu->mmu); + imgu_css_cleanup(&imgu->css); + imgu_css_set_powerdown(&pci_dev->dev, imgu->base); + imgu_dmamap_exit(imgu); + imgu_mmu_exit(imgu->mmu); mutex_destroy(&imgu->lock); } @@ -723,7 +723,7 @@ static int __maybe_unused imgu_suspend(struct device *dev) struct imgu_device *imgu = pci_get_drvdata(pci_dev); dev_dbg(dev, "enter %s\n", __func__); - imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css); + imgu->suspend_in_stream = imgu_css_is_streaming(&imgu->css); if (!imgu->suspend_in_stream) goto out; /* Block new buffers to be queued to CSS. */ @@ -735,10 +735,10 @@ static int __maybe_unused imgu_suspend(struct device *dev) synchronize_irq(pci_dev->irq); /* Wait until all buffers in CSS are done. */ if (!wait_event_timeout(imgu->buf_drain_wq, - ipu3_css_queue_empty(&imgu->css), msecs_to_jiffies(1000))) + imgu_css_queue_empty(&imgu->css), msecs_to_jiffies(1000))) dev_err(dev, "wait buffer drain timeout.\n"); - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); atomic_set(&imgu->qbuf_barrier, 0); imgu_powerdown(imgu); pm_runtime_force_suspend(dev); @@ -768,7 +768,7 @@ static int __maybe_unused imgu_resume(struct device *dev) } /* Start CSS streaming */ - r = ipu3_css_start_streaming(&imgu->css); + r = imgu_css_start_streaming(&imgu->css); if (r) { dev_err(dev, "failed to resume css streaming (%d)", r); goto out; diff --git a/drivers/staging/media/ipu3/ipu3.h b/drivers/staging/media/ipu3/ipu3.h index 04fc99f47ebb..6b408f726667 100644 --- a/drivers/staging/media/ipu3/ipu3.h +++ b/drivers/staging/media/ipu3/ipu3.h @@ -32,7 +32,7 @@ #define IMGU_NODE_STAT_3A 4 /* 3A statistics */ #define IMGU_NODE_NUM 5 -#define file_to_intel_ipu3_node(__file) \ +#define file_to_intel_imgu_node(__file) \ container_of(video_devdata(__file), struct imgu_video_device, vdev) #define IPU3_INPUT_MIN_WIDTH 0U @@ -44,7 +44,7 @@ #define IPU3_OUTPUT_MAX_WIDTH 4480U #define IPU3_OUTPUT_MAX_HEIGHT 34004U -struct ipu3_vb2_buffer { +struct imgu_vb2_buffer { /* Public fields */ struct vb2_v4l2_buffer vbb; /* Must be the first field */ @@ -53,9 +53,9 @@ struct ipu3_vb2_buffer { }; struct imgu_buffer { - struct ipu3_vb2_buffer vid_buf; /* Must be the first field */ - struct ipu3_css_buffer css_buf; - struct ipu3_css_map map; + struct imgu_vb2_buffer vid_buf; /* Must be the first field */ + struct imgu_css_buffer css_buf; + struct imgu_css_map map; }; struct imgu_node_mapping { @@ -107,8 +107,8 @@ struct imgu_media_pipe { /* Internally enabled queues */ struct { - struct ipu3_css_map dmap; - struct ipu3_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH]; + struct imgu_css_map dmap; + struct imgu_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH]; } queues[IPU3_CSS_QUEUES]; struct imgu_video_device nodes[IMGU_NODE_NUM]; bool queue_enabled[IMGU_NODE_NUM]; @@ -135,11 +135,11 @@ struct imgu_device { struct v4l2_file_operations v4l2_file_ops; /* MMU driver for css */ - struct ipu3_mmu_info *mmu; + struct imgu_mmu_info *mmu; struct iova_domain iova_domain; /* css - Camera Sub-System */ - struct ipu3_css css; + struct imgu_css css; /* * Coarse-grained lock to protect From f47c34be55f744ffd10b762e00366770048ce028 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 4 Feb 2019 04:07:25 -0500 Subject: [PATCH 1942/4277] media: soc_camera: Remove the mt9m001 SoC camera sensor driver There is a V4L2 sub-device sensor driver for the mt9m001. There is already a non-soc_camera driver. So, remove the SoC camera one. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 7 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_mt9m001.c | 757 --------------------- 3 files changed, 765 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_mt9m001.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index dea66ef1394d..5dcb93c0a902 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -1,12 +1,5 @@ comment "soc_camera sensor drivers" -config SOC_CAMERA_MT9M001 - tristate "mt9m001 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9M001 cameras from Micron, monochrome - and colour models. - config SOC_CAMERA_MT9M111 tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 94659f7aa195..a215d8b095d9 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o diff --git a/drivers/media/i2c/soc_camera/soc_mt9m001.c b/drivers/media/i2c/soc_camera/soc_mt9m001.c deleted file mode 100644 index a1a85ff838c5..000000000000 --- a/drivers/media/i2c/soc_camera/soc_mt9m001.c +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Driver for MT9M001 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* - * mt9m001 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9m001 selected register addresses */ -#define MT9M001_CHIP_VERSION 0x00 -#define MT9M001_ROW_START 0x01 -#define MT9M001_COLUMN_START 0x02 -#define MT9M001_WINDOW_HEIGHT 0x03 -#define MT9M001_WINDOW_WIDTH 0x04 -#define MT9M001_HORIZONTAL_BLANKING 0x05 -#define MT9M001_VERTICAL_BLANKING 0x06 -#define MT9M001_OUTPUT_CONTROL 0x07 -#define MT9M001_SHUTTER_WIDTH 0x09 -#define MT9M001_FRAME_RESTART 0x0b -#define MT9M001_SHUTTER_DELAY 0x0c -#define MT9M001_RESET 0x0d -#define MT9M001_READ_OPTIONS1 0x1e -#define MT9M001_READ_OPTIONS2 0x20 -#define MT9M001_GLOBAL_GAIN 0x35 -#define MT9M001_CHIP_ENABLE 0xF1 - -#define MT9M001_MAX_WIDTH 1280 -#define MT9M001_MAX_HEIGHT 1024 -#define MT9M001_MIN_WIDTH 48 -#define MT9M001_MIN_HEIGHT 32 -#define MT9M001_COLUMN_SKIP 20 -#define MT9M001_ROW_SKIP 12 - -/* MT9M001 has only one fixed colorspace per pixelcode */ -struct mt9m001_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct mt9m001_datafmt *mt9m001_find_datafmt( - u32 code, const struct mt9m001_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { - /* - * Order important: first natively supported, - * second supported with a GPIO extender - */ - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { - /* Order important - see above */ - {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, -}; - -struct mt9m001 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - const struct mt9m001_datafmt *fmt; - const struct mt9m001_datafmt *fmts; - int num_fmts; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9m001 *to_mt9m001(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int mt9m001_init(struct i2c_client *client) -{ - int ret; - - dev_dbg(&client->dev, "%s\n", __func__); - - /* - * We don't know, whether platform provides reset, issue a soft reset - * too. This returns all registers to their default values. - */ - ret = reg_write(client, MT9M001_RESET, 1); - if (!ret) - ret = reg_write(client, MT9M001_RESET, 0); - - /* Disable chip, synchronous option update */ - if (!ret) - ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); - - return ret; -} - -static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* Switch to master "normal" mode or stop sensor readout */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) - return -EIO; - return 0; -} - -static int mt9m001_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_rect rect = sel->r; - const u16 hblank = 9, vblank = 25; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - if (mt9m001->fmts == mt9m001_colour_fmts) - /* - * Bayer format - even number of rows for simplicity, - * but let the user play with the top row. - */ - rect.height = ALIGN(rect.height, 2); - - /* Datasheet requirement: see register description */ - rect.width = ALIGN(rect.width, 2); - rect.left = ALIGN(rect.left, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); - - mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); - if (!ret) - ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); - - /* - * The caller provides a supported format, as verified per - * call to .set_fmt(FORMAT_TRY). - */ - if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect.top); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect.height + mt9m001->y_skip_top - 1); - if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); - - if (!ret) - mt9m001->rect = rect; - - return ret; -} - -static int mt9m001_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9M001_COLUMN_SKIP; - sel->r.top = MT9M001_ROW_SKIP; - sel->r.width = MT9M001_MAX_WIDTH; - sel->r.height = MT9M001_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9m001->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9m001_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - mf->width = mt9m001->rect.width; - mf->height = mt9m001->rect.height; - mf->code = mt9m001->fmt->code; - mf->colorspace = mt9m001->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9m001_s_fmt(struct v4l2_subdev *sd, - const struct mt9m001_datafmt *fmt, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = mt9m001->rect.left, - .r.top = mt9m001->rect.top, - .r.width = mf->width, - .r.height = mf->height, - }; - int ret; - - /* No support for scaling so far, just crop. TODO: use skipping */ - ret = mt9m001_set_selection(sd, NULL, &sel); - if (!ret) { - mf->width = mt9m001->rect.width; - mf->height = mt9m001->rect.height; - mt9m001->fmt = fmt; - mf->colorspace = fmt->colorspace; - } - - return ret; -} - -static int mt9m001_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - const struct mt9m001_datafmt *fmt; - - if (format->pad) - return -EINVAL; - - v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH, - MT9M001_MAX_WIDTH, 1, - &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top, - MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0); - - if (mt9m001->fmts == mt9m001_colour_fmts) - mf->height = ALIGN(mf->height - 1, 2); - - fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts, - mt9m001->num_fmts); - if (!fmt) { - fmt = mt9m001->fmt; - mf->code = fmt->code; - } - - mf->colorspace = fmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9m001_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m001_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 2; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9m001_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9m001_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); -} - -static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m001 *mt9m001 = container_of(ctrl->handler, - struct mt9m001, hdl); - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9m001->exposure->minimum; - max = mt9m001->exposure->maximum; - mt9m001->exposure->val = - (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min; - break; - } - return 0; -} - -static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m001 *mt9m001 = container_of(ctrl->handler, - struct mt9m001, hdl); - struct v4l2_subdev *sd = &mt9m001->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9m001->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); - else - data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); - if (data < 0) - return -EIO; - return 0; - - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..15 variable step, register values 9..67 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 111 + range / 2) / range + 9; - - if (gain <= 32) - data = gain; - else if (gain <= 64) - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - data = ((gain - 64) * 7 + 28) / 56 + 96; - - dev_dbg(&client->dev, "Setting gain from %d to %d\n", - reg_read(client, MT9M001_GLOBAL_GAIN), data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - - dev_dbg(&client->dev, - "Setting shutter width from %d to %lu\n", - reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); - if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) - return -EIO; - } else { - const u16 vblank = 25; - - mt9m001->total_h = mt9m001->rect.height + - mt9m001->y_skip_top + vblank; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) - return -EIO; - } - return 0; - } - return -EINVAL; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, - struct i2c_client *client) -{ - struct mt9m001 *mt9m001 = to_mt9m001(client); - s32 data; - unsigned long flags; - int ret; - - ret = mt9m001_s_power(&mt9m001->subdev, 1); - if (ret < 0) - return ret; - - /* Enable the chip */ - data = reg_write(client, MT9M001_CHIP_ENABLE, 1); - dev_dbg(&client->dev, "write: %d\n", data); - - /* Read out the chip version register */ - data = reg_read(client, MT9M001_CHIP_VERSION); - - /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ - switch (data) { - case 0x8411: - case 0x8421: - mt9m001->fmts = mt9m001_colour_fmts; - break; - case 0x8431: - mt9m001->fmts = mt9m001_monochrome_fmts; - break; - default: - dev_err(&client->dev, - "No MT9M001 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - mt9m001->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9m001->num_fmts++; - else - mt9m001->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9m001->num_fmts++; - - mt9m001->fmt = &mt9m001->fmts[0]; - - dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, - data == 0x8431 ? "C12STM" : "C12ST"); - - ret = mt9m001_init(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* mt9m001_init() has reset the chip, returning registers to defaults */ - ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); - -done: - mt9m001_s_power(&mt9m001->subdev, 0); - return ret; -} - -static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) -{ - if (ssdd->free_bus) - ssdd->free_bus(ssdd); -} - -static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - *lines = mt9m001->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { - .g_volatile_ctrl = mt9m001_g_volatile_ctrl, - .s_ctrl = mt9m001_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9m001_g_register, - .s_register = mt9m001_s_register, -#endif - .s_power = mt9m001_s_power, -}; - -static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - if (code->pad || code->index >= mt9m001->num_fmts) - return -EINVAL; - - code->code = mt9m001->fmts[code->index].code; - return 0; -} - -static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - /* MT9M001 has all capture_format parameters fixed */ - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9m001 *mt9m001 = to_mt9m001(client); - unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - - if (ssdd->set_bus_param) - return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return bps == 10 ? 0 : -EINVAL; -} - -static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { - .s_stream = mt9m001_s_stream, - .g_mbus_config = mt9m001_g_mbus_config, - .s_mbus_config = mt9m001_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { - .g_skip_top_lines = mt9m001_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { - .enum_mbus_code = mt9m001_enum_mbus_code, - .get_selection = mt9m001_get_selection, - .set_selection = mt9m001_set_selection, - .get_fmt = mt9m001_get_fmt, - .set_fmt = mt9m001_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9m001_subdev_ops = { - .core = &mt9m001_subdev_core_ops, - .video = &mt9m001_subdev_video_ops, - .sensor = &mt9m001_subdev_sensor_ops, - .pad = &mt9m001_subdev_pad_ops, -}; - -static int mt9m001_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9m001 *mt9m001; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9M001 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); - if (!mt9m001) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); - v4l2_ctrl_handler_init(&mt9m001->hdl, 4); - v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl, - &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9m001->subdev.ctrl_handler = &mt9m001->hdl; - if (mt9m001->hdl.error) - return mt9m001->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - /* Second stage probe - when a capture adapter is there */ - mt9m001->y_skip_top = 0; - mt9m001->rect.left = MT9M001_COLUMN_SKIP; - mt9m001->rect.top = MT9M001_ROW_SKIP; - mt9m001->rect.width = MT9M001_MAX_WIDTH; - mt9m001->rect.height = MT9M001_MAX_HEIGHT; - - mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9m001->clk)) { - ret = PTR_ERR(mt9m001->clk); - goto eclkget; - } - - ret = mt9m001_video_probe(ssdd, client); - if (ret) { - v4l2_clk_put(mt9m001->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9m001->hdl); - } - - return ret; -} - -static int mt9m001_remove(struct i2c_client *client) -{ - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(mt9m001->clk); - v4l2_device_unregister_subdev(&mt9m001->subdev); - v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(ssdd); - - return 0; -} - -static const struct i2c_device_id mt9m001_id[] = { - { "mt9m001", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9m001_id); - -static struct i2c_driver mt9m001_i2c_driver = { - .driver = { - .name = "mt9m001", - }, - .probe = mt9m001_probe, - .remove = mt9m001_remove, - .id_table = mt9m001_id, -}; - -module_i2c_driver(mt9m001_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); From 3c32db82df60914271d87bf5f3dd5d5bc43ebf55 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 4 Feb 2019 05:18:17 -0500 Subject: [PATCH 1943/4277] media: soc_camera: Remove the rj45n1 SoC camera sensor driver There is a V4L2 sub-device sensor driver for the rj45n1. As there is already another driver, remove the SoC camera one. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c | 1415 ----------------- 3 files changed, 1422 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 5dcb93c0a902..bcd9ef86f40b 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -27,9 +27,3 @@ config SOC_CAMERA_OV9740 depends on SOC_CAMERA && I2C help This is a ov9740 camera driver - -config SOC_CAMERA_RJ54N1 - tristate "rj54n1cb0c support" - depends on SOC_CAMERA && I2C - help - This is a rj54n1cb0c video driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index a215d8b095d9..6d63eb31c3b7 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -2,4 +2,3 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o -obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o diff --git a/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c b/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c deleted file mode 100644 index f0cb49a6167b..000000000000 --- a/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c +++ /dev/null @@ -1,1415 +0,0 @@ -/* - * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp - * - * Copyright (C) 2009, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define RJ54N1_DEV_CODE 0x0400 -#define RJ54N1_DEV_CODE2 0x0401 -#define RJ54N1_OUT_SEL 0x0403 -#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 -#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 -#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 -#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 -#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 -#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 -#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a -#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b -#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c -#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d -#define RJ54N1_RESIZE_N 0x040e -#define RJ54N1_RESIZE_N_STEP 0x040f -#define RJ54N1_RESIZE_STEP 0x0410 -#define RJ54N1_RESIZE_HOLD_H 0x0411 -#define RJ54N1_RESIZE_HOLD_L 0x0412 -#define RJ54N1_H_OBEN_OFS 0x0413 -#define RJ54N1_V_OBEN_OFS 0x0414 -#define RJ54N1_RESIZE_CONTROL 0x0415 -#define RJ54N1_STILL_CONTROL 0x0417 -#define RJ54N1_INC_USE_SEL_H 0x0425 -#define RJ54N1_INC_USE_SEL_L 0x0426 -#define RJ54N1_MIRROR_STILL_MODE 0x0427 -#define RJ54N1_INIT_START 0x0428 -#define RJ54N1_SCALE_1_2_LEV 0x0429 -#define RJ54N1_SCALE_4_LEV 0x042a -#define RJ54N1_Y_GAIN 0x04d8 -#define RJ54N1_APT_GAIN_UP 0x04fa -#define RJ54N1_RA_SEL_UL 0x0530 -#define RJ54N1_BYTE_SWAP 0x0531 -#define RJ54N1_OUT_SIGPO 0x053b -#define RJ54N1_WB_SEL_WEIGHT_I 0x054e -#define RJ54N1_BIT8_WB 0x0569 -#define RJ54N1_HCAPS_WB 0x056a -#define RJ54N1_VCAPS_WB 0x056b -#define RJ54N1_HCAPE_WB 0x056c -#define RJ54N1_VCAPE_WB 0x056d -#define RJ54N1_EXPOSURE_CONTROL 0x058c -#define RJ54N1_FRAME_LENGTH_S_H 0x0595 -#define RJ54N1_FRAME_LENGTH_S_L 0x0596 -#define RJ54N1_FRAME_LENGTH_P_H 0x0597 -#define RJ54N1_FRAME_LENGTH_P_L 0x0598 -#define RJ54N1_PEAK_H 0x05b7 -#define RJ54N1_PEAK_50 0x05b8 -#define RJ54N1_PEAK_60 0x05b9 -#define RJ54N1_PEAK_DIFF 0x05ba -#define RJ54N1_IOC 0x05ef -#define RJ54N1_TG_BYPASS 0x0700 -#define RJ54N1_PLL_L 0x0701 -#define RJ54N1_PLL_N 0x0702 -#define RJ54N1_PLL_EN 0x0704 -#define RJ54N1_RATIO_TG 0x0706 -#define RJ54N1_RATIO_T 0x0707 -#define RJ54N1_RATIO_R 0x0708 -#define RJ54N1_RAMP_TGCLK_EN 0x0709 -#define RJ54N1_OCLK_DSP 0x0710 -#define RJ54N1_RATIO_OP 0x0711 -#define RJ54N1_RATIO_O 0x0712 -#define RJ54N1_OCLK_SEL_EN 0x0713 -#define RJ54N1_CLK_RST 0x0717 -#define RJ54N1_RESET_STANDBY 0x0718 -#define RJ54N1_FWFLG 0x07fe - -#define E_EXCLK (1 << 7) -#define SOFT_STDBY (1 << 4) -#define SEN_RSTX (1 << 2) -#define TG_RSTX (1 << 1) -#define DSP_RSTX (1 << 0) - -#define RESIZE_HOLD_SEL (1 << 2) -#define RESIZE_GO (1 << 1) - -/* - * When cropping, the camera automatically centers the cropped region, there - * doesn't seem to be a way to specify an explicit location of the rectangle. - */ -#define RJ54N1_COLUMN_SKIP 0 -#define RJ54N1_ROW_SKIP 0 -#define RJ54N1_MAX_WIDTH 1600 -#define RJ54N1_MAX_HEIGHT 1200 - -#define PLL_L 2 -#define PLL_N 0x31 - -/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ - -/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ -struct rj54n1_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct rj54n1_datafmt *rj54n1_find_datafmt( - u32 code, const struct rj54n1_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { - {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, -}; - -struct rj54n1_clock_div { - u8 ratio_tg; /* can be 0 or an odd number */ - u8 ratio_t; - u8 ratio_r; - u8 ratio_op; - u8 ratio_o; -}; - -struct rj54n1 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - struct rj54n1_clock_div clk_div; - const struct rj54n1_datafmt *fmt; - struct v4l2_rect rect; /* Sensor window */ - unsigned int tgclk_mhz; - bool auto_wb; - unsigned short width; /* Output window */ - unsigned short height; - unsigned short resize; /* Sensor * 1024 / resize = Output */ - unsigned short scale; - u8 bank; -}; - -struct rj54n1_reg_val { - u16 reg; - u8 val; -}; - -static const struct rj54n1_reg_val bank_4[] = { - {0x417, 0}, - {0x42c, 0}, - {0x42d, 0xf0}, - {0x42e, 0}, - {0x42f, 0x50}, - {0x430, 0xf5}, - {0x431, 0x16}, - {0x432, 0x20}, - {0x433, 0}, - {0x434, 0xc8}, - {0x43c, 8}, - {0x43e, 0x90}, - {0x445, 0x83}, - {0x4ba, 0x58}, - {0x4bb, 4}, - {0x4bc, 0x20}, - {0x4db, 4}, - {0x4fe, 2}, -}; - -static const struct rj54n1_reg_val bank_5[] = { - {0x514, 0}, - {0x516, 0}, - {0x518, 0}, - {0x51a, 0}, - {0x51d, 0xff}, - {0x56f, 0x28}, - {0x575, 0x40}, - {0x5bc, 0x48}, - {0x5c1, 6}, - {0x5e5, 0x11}, - {0x5e6, 0x43}, - {0x5e7, 0x33}, - {0x5e8, 0x21}, - {0x5e9, 0x30}, - {0x5ea, 0x0}, - {0x5eb, 0xa5}, - {0x5ec, 0xff}, - {0x5fe, 2}, -}; - -static const struct rj54n1_reg_val bank_7[] = { - {0x70a, 0}, - {0x714, 0xff}, - {0x715, 0xff}, - {0x716, 0x1f}, - {0x7FE, 2}, -}; - -static const struct rj54n1_reg_val bank_8[] = { - {0x800, 0x00}, - {0x801, 0x01}, - {0x802, 0x61}, - {0x805, 0x00}, - {0x806, 0x00}, - {0x807, 0x00}, - {0x808, 0x00}, - {0x809, 0x01}, - {0x80A, 0x61}, - {0x80B, 0x00}, - {0x80C, 0x01}, - {0x80D, 0x00}, - {0x80E, 0x00}, - {0x80F, 0x00}, - {0x810, 0x00}, - {0x811, 0x01}, - {0x812, 0x61}, - {0x813, 0x00}, - {0x814, 0x11}, - {0x815, 0x00}, - {0x816, 0x41}, - {0x817, 0x00}, - {0x818, 0x51}, - {0x819, 0x01}, - {0x81A, 0x1F}, - {0x81B, 0x00}, - {0x81C, 0x01}, - {0x81D, 0x00}, - {0x81E, 0x11}, - {0x81F, 0x00}, - {0x820, 0x41}, - {0x821, 0x00}, - {0x822, 0x51}, - {0x823, 0x00}, - {0x824, 0x00}, - {0x825, 0x00}, - {0x826, 0x47}, - {0x827, 0x01}, - {0x828, 0x4F}, - {0x829, 0x00}, - {0x82A, 0x00}, - {0x82B, 0x00}, - {0x82C, 0x30}, - {0x82D, 0x00}, - {0x82E, 0x40}, - {0x82F, 0x00}, - {0x830, 0xB3}, - {0x831, 0x00}, - {0x832, 0xE3}, - {0x833, 0x00}, - {0x834, 0x00}, - {0x835, 0x00}, - {0x836, 0x00}, - {0x837, 0x00}, - {0x838, 0x00}, - {0x839, 0x01}, - {0x83A, 0x61}, - {0x83B, 0x00}, - {0x83C, 0x01}, - {0x83D, 0x00}, - {0x83E, 0x00}, - {0x83F, 0x00}, - {0x840, 0x00}, - {0x841, 0x01}, - {0x842, 0x61}, - {0x843, 0x00}, - {0x844, 0x1D}, - {0x845, 0x00}, - {0x846, 0x00}, - {0x847, 0x00}, - {0x848, 0x00}, - {0x849, 0x01}, - {0x84A, 0x1F}, - {0x84B, 0x00}, - {0x84C, 0x05}, - {0x84D, 0x00}, - {0x84E, 0x19}, - {0x84F, 0x01}, - {0x850, 0x21}, - {0x851, 0x01}, - {0x852, 0x5D}, - {0x853, 0x00}, - {0x854, 0x00}, - {0x855, 0x00}, - {0x856, 0x19}, - {0x857, 0x01}, - {0x858, 0x21}, - {0x859, 0x00}, - {0x85A, 0x00}, - {0x85B, 0x00}, - {0x85C, 0x00}, - {0x85D, 0x00}, - {0x85E, 0x00}, - {0x85F, 0x00}, - {0x860, 0xB3}, - {0x861, 0x00}, - {0x862, 0xE3}, - {0x863, 0x00}, - {0x864, 0x00}, - {0x865, 0x00}, - {0x866, 0x00}, - {0x867, 0x00}, - {0x868, 0x00}, - {0x869, 0xE2}, - {0x86A, 0x00}, - {0x86B, 0x01}, - {0x86C, 0x06}, - {0x86D, 0x00}, - {0x86E, 0x00}, - {0x86F, 0x00}, - {0x870, 0x60}, - {0x871, 0x8C}, - {0x872, 0x10}, - {0x873, 0x00}, - {0x874, 0xE0}, - {0x875, 0x00}, - {0x876, 0x27}, - {0x877, 0x01}, - {0x878, 0x00}, - {0x879, 0x00}, - {0x87A, 0x00}, - {0x87B, 0x03}, - {0x87C, 0x00}, - {0x87D, 0x00}, - {0x87E, 0x00}, - {0x87F, 0x00}, - {0x880, 0x00}, - {0x881, 0x00}, - {0x882, 0x00}, - {0x883, 0x00}, - {0x884, 0x00}, - {0x885, 0x00}, - {0x886, 0xF8}, - {0x887, 0x00}, - {0x888, 0x03}, - {0x889, 0x00}, - {0x88A, 0x64}, - {0x88B, 0x00}, - {0x88C, 0x03}, - {0x88D, 0x00}, - {0x88E, 0xB1}, - {0x88F, 0x00}, - {0x890, 0x03}, - {0x891, 0x01}, - {0x892, 0x1D}, - {0x893, 0x00}, - {0x894, 0x03}, - {0x895, 0x01}, - {0x896, 0x4B}, - {0x897, 0x00}, - {0x898, 0xE5}, - {0x899, 0x00}, - {0x89A, 0x01}, - {0x89B, 0x00}, - {0x89C, 0x01}, - {0x89D, 0x04}, - {0x89E, 0xC8}, - {0x89F, 0x00}, - {0x8A0, 0x01}, - {0x8A1, 0x01}, - {0x8A2, 0x61}, - {0x8A3, 0x00}, - {0x8A4, 0x01}, - {0x8A5, 0x00}, - {0x8A6, 0x00}, - {0x8A7, 0x00}, - {0x8A8, 0x00}, - {0x8A9, 0x00}, - {0x8AA, 0x7F}, - {0x8AB, 0x03}, - {0x8AC, 0x00}, - {0x8AD, 0x00}, - {0x8AE, 0x00}, - {0x8AF, 0x00}, - {0x8B0, 0x00}, - {0x8B1, 0x00}, - {0x8B6, 0x00}, - {0x8B7, 0x01}, - {0x8B8, 0x00}, - {0x8B9, 0x00}, - {0x8BA, 0x02}, - {0x8BB, 0x00}, - {0x8BC, 0xFF}, - {0x8BD, 0x00}, - {0x8FE, 2}, -}; - -static const struct rj54n1_reg_val bank_10[] = { - {0x10bf, 0x69} -}; - -/* Clock dividers - these are default register values, divider = register + 1 */ -static const struct rj54n1_clock_div clk_div = { - .ratio_tg = 3 /* default: 5 */, - .ratio_t = 4 /* default: 1 */, - .ratio_r = 4 /* default: 0 */, - .ratio_op = 1 /* default: 5 */, - .ratio_o = 9 /* default: 0 */, -}; - -static struct rj54n1 *to_rj54n1(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); -} - -static int reg_read(struct i2c_client *client, const u16 reg) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret; - - /* set bank */ - if (rj54n1->bank != reg >> 8) { - dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); - ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); - if (ret < 0) - return ret; - rj54n1->bank = reg >> 8; - } - return i2c_smbus_read_byte_data(client, reg & 0xff); -} - -static int reg_write(struct i2c_client *client, const u16 reg, - const u8 data) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret; - - /* set bank */ - if (rj54n1->bank != reg >> 8) { - dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); - ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); - if (ret < 0) - return ret; - rj54n1->bank = reg >> 8; - } - dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); - return i2c_smbus_write_byte_data(client, reg & 0xff, data); -} - -static int reg_set(struct i2c_client *client, const u16 reg, - const u8 data, const u8 mask) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, (ret & ~mask) | (data & mask)); -} - -static int reg_write_multiple(struct i2c_client *client, - const struct rj54n1_reg_val *rv, const int n) -{ - int i, ret; - - for (i = 0; i < n; i++) { - ret = reg_write(client, rv->reg, rv->val); - if (ret < 0) - return ret; - rv++; - } - - return 0; -} - -static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) - return -EINVAL; - - code->code = rj54n1_colour_fmts[code->index].code; - return 0; -} - -static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* Switch between preview and still shot modes */ - return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); -} - -static int rj54n1_set_rect(struct i2c_client *client, - u16 reg_x, u16 reg_y, u16 reg_xy, - u32 width, u32 height) -{ - int ret; - - ret = reg_write(client, reg_xy, - ((width >> 4) & 0x70) | - ((height >> 8) & 7)); - - if (!ret) - ret = reg_write(client, reg_x, width & 0xff); - if (!ret) - ret = reg_write(client, reg_y, height & 0xff); - - return ret; -} - -/* - * Some commands, specifically certain initialisation sequences, require - * a commit operation. - */ -static int rj54n1_commit(struct i2c_client *client) -{ - int ret = reg_write(client, RJ54N1_INIT_START, 1); - msleep(10); - if (!ret) - ret = reg_write(client, RJ54N1_INIT_START, 0); - return ret; -} - -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, - s32 *out_w, s32 *out_h); - -static int rj54n1_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct v4l2_rect *rect = &sel->r; - int dummy = 0, output_w, output_h, - input_w = rect->width, input_h = rect->height; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* arbitrary minimum width and height, edges unimportant */ - soc_camera_limit_side(&dummy, &input_w, - RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH); - - soc_camera_limit_side(&dummy, &input_h, - RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT); - - output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; - output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; - - dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", - input_w, input_h, rj54n1->resize, output_w, output_h); - - ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); - if (ret < 0) - return ret; - - rj54n1->width = output_w; - rj54n1->height = output_h; - rj54n1->resize = ret; - rj54n1->rect.width = input_w; - rj54n1->rect.height = input_h; - - return 0; -} - -static int rj54n1_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = RJ54N1_COLUMN_SKIP; - sel->r.top = RJ54N1_ROW_SKIP; - sel->r.width = RJ54N1_MAX_WIDTH; - sel->r.height = RJ54N1_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = rj54n1->rect; - return 0; - default: - return -EINVAL; - } -} - -static int rj54n1_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - - if (format->pad) - return -EINVAL; - - mf->code = rj54n1->fmt->code; - mf->colorspace = rj54n1->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - mf->width = rj54n1->width; - mf->height = rj54n1->height; - - return 0; -} - -/* - * The actual geometry configuration routine. It scales the input window into - * the output one, updates the window sizes and returns an error or the resize - * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. - */ -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, - s32 *out_w, s32 *out_h) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - unsigned int skip, resize, input_w = *in_w, input_h = *in_h, - output_w = *out_w, output_h = *out_h; - u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; - unsigned int peak, peak_50, peak_60; - int ret; - - /* - * We have a problem with crops, where the window is larger than 512x384 - * and output window is larger than a half of the input one. In this - * case we have to either reduce the input window to equal or below - * 512x384 or the output window to equal or below 1/2 of the input. - */ - if (output_w > max(512U, input_w / 2)) { - if (2 * output_w > RJ54N1_MAX_WIDTH) { - input_w = RJ54N1_MAX_WIDTH; - output_w = RJ54N1_MAX_WIDTH / 2; - } else { - input_w = output_w * 2; - } - - dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n", - input_w, output_w); - } - - if (output_h > max(384U, input_h / 2)) { - if (2 * output_h > RJ54N1_MAX_HEIGHT) { - input_h = RJ54N1_MAX_HEIGHT; - output_h = RJ54N1_MAX_HEIGHT / 2; - } else { - input_h = output_h * 2; - } - - dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n", - input_h, output_h); - } - - /* Idea: use the read mode for snapshots, handle separate geometries */ - ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, - RJ54N1_Y_OUTPUT_SIZE_S_L, - RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); - if (!ret) - ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, - RJ54N1_Y_OUTPUT_SIZE_P_L, - RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); - - if (ret < 0) - return ret; - - if (output_w > input_w && output_h > input_h) { - input_w = output_w; - input_h = output_h; - - resize = 1024; - } else { - unsigned int resize_x, resize_y; - resize_x = (input_w * 1024 + output_w / 2) / output_w; - resize_y = (input_h * 1024 + output_h / 2) / output_h; - - /* We want max(resize_x, resize_y), check if it still fits */ - if (resize_x > resize_y && - (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT) - resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) / - output_h; - else if (resize_y > resize_x && - (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH) - resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) / - output_w; - else - resize = max(resize_x, resize_y); - - /* Prohibited value ranges */ - switch (resize) { - case 2040 ... 2047: - resize = 2039; - break; - case 4080 ... 4095: - resize = 4079; - break; - case 8160 ... 8191: - resize = 8159; - break; - case 16320 ... 16384: - resize = 16319; - } - } - - /* Set scaling */ - ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); - if (!ret) - ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); - - if (ret < 0) - return ret; - - /* - * Configure a skipping bitmask. The sensor will select a skipping value - * among set bits automatically. This is very unclear in the datasheet - * too. I was told, in this register one enables all skipping values, - * that are required for a specific resize, and the camera selects - * automatically, which ones to use. But it is unclear how to identify, - * which cropping values are needed. Secondly, why don't we just set all - * bits and let the camera choose? Would it increase processing time and - * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to - * improve the image quality or stability for larger frames (see comment - * above), but I didn't check the framerate. - */ - skip = min(resize / 1024, 15U); - - inc_sel = 1 << skip; - - if (inc_sel <= 2) - inc_sel = 0xc; - else if (resize & 1023 && skip < 15) - inc_sel |= 1 << (skip + 1); - - ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); - if (!ret) - ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); - - if (!rj54n1->auto_wb) { - /* Auto white balance window */ - wb_left = output_w / 16; - wb_right = (3 * output_w / 4 - 3) / 4; - wb_top = output_h / 16; - wb_bottom = (3 * output_h / 4 - 3) / 4; - wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) | - ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1); - - if (!ret) - ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); - if (!ret) - ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); - if (!ret) - ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); - if (!ret) - ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); - if (!ret) - ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom); - } - - /* Antiflicker */ - peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz / - 10000; - peak_50 = peak / 6; - peak_60 = peak / 5; - - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_H, - ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_50, peak_50); - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_60, peak_60); - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150); - - /* Start resizing */ - if (!ret) - ret = reg_write(client, RJ54N1_RESIZE_CONTROL, - RESIZE_HOLD_SEL | RESIZE_GO | 1); - - if (ret < 0) - return ret; - - /* Constant taken from manufacturer's example */ - msleep(230); - - ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); - if (ret < 0) - return ret; - - *in_w = (output_w * resize + 512) / 1024; - *in_h = (output_h * resize + 512) / 1024; - *out_w = output_w; - *out_h = output_h; - - dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", - *in_w, *in_h, resize, output_w, output_h, skip); - - return resize; -} - -static int rj54n1_set_clock(struct i2c_client *client) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret; - - /* Enable external clock */ - ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); - /* Leave stand-by. Note: use this when implementing suspend / resume */ - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); - - if (!ret) - ret = reg_write(client, RJ54N1_PLL_L, PLL_L); - if (!ret) - ret = reg_write(client, RJ54N1_PLL_N, PLL_N); - - /* TGCLK dividers */ - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_TG, - rj54n1->clk_div.ratio_tg); - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_T, - rj54n1->clk_div.ratio_t); - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_R, - rj54n1->clk_div.ratio_r); - - /* Enable TGCLK & RAMP */ - if (!ret) - ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); - - /* Disable clock output */ - if (!ret) - ret = reg_write(client, RJ54N1_OCLK_DSP, 0); - - /* Set divisors */ - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_OP, - rj54n1->clk_div.ratio_op); - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_O, - rj54n1->clk_div.ratio_o); - - /* Enable OCLK */ - if (!ret) - ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); - - /* Use PLL for Timing Generator, write 2 to reserved bits */ - if (!ret) - ret = reg_write(client, RJ54N1_TG_BYPASS, 2); - - /* Take sensor out of reset */ - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, - E_EXCLK | SEN_RSTX); - /* Enable PLL */ - if (!ret) - ret = reg_write(client, RJ54N1_PLL_EN, 1); - - /* Wait for PLL to stabilise */ - msleep(10); - - /* Enable clock to frequency divider */ - if (!ret) - ret = reg_write(client, RJ54N1_CLK_RST, 1); - - if (!ret) - ret = reg_read(client, RJ54N1_CLK_RST); - if (ret != 1) { - dev_err(&client->dev, - "Resetting RJ54N1CB0C clock failed: %d!\n", ret); - return -EIO; - } - - /* Start the PLL */ - ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); - - /* Enable OCLK */ - if (!ret) - ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); - - return ret; -} - -static int rj54n1_reg_init(struct i2c_client *client) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret = rj54n1_set_clock(client); - - if (!ret) - ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); - if (!ret) - ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); - - /* Set binning divisors */ - if (!ret) - ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); - if (!ret) - ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); - - /* Switch to fixed resize mode */ - if (!ret) - ret = reg_write(client, RJ54N1_RESIZE_CONTROL, - RESIZE_HOLD_SEL | 1); - - /* Set gain */ - if (!ret) - ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); - - /* - * Mirror the image back: default is upside down and left-to-right... - * Set manual preview / still shot switching - */ - if (!ret) - ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27); - - if (!ret) - ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); - - /* Auto exposure area */ - if (!ret) - ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); - /* Check current auto WB config */ - if (!ret) - ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); - if (ret >= 0) { - rj54n1->auto_wb = ret & 0x80; - ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); - } - if (!ret) - ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); - - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, - E_EXCLK | DSP_RSTX | SEN_RSTX); - - /* Commit init */ - if (!ret) - ret = rj54n1_commit(client); - - /* Take DSP, TG, sensor out of reset */ - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, - E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); - - /* Start register update? Same register as 0x?FE in many bank_* sets */ - if (!ret) - ret = reg_write(client, RJ54N1_FWFLG, 2); - - /* Constant taken from manufacturer's example */ - msleep(700); - - return ret; -} - -static int rj54n1_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct rj54n1_datafmt *fmt; - int output_w, output_h, max_w, max_h, - input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; - int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE; - int ret; - - if (format->pad) - return -EINVAL; - - dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", - __func__, mf->code, mf->width, mf->height); - - fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, - ARRAY_SIZE(rj54n1_colour_fmts)); - if (!fmt) { - fmt = rj54n1->fmt; - mf->code = fmt->code; - } - - mf->field = V4L2_FIELD_NONE; - mf->colorspace = fmt->colorspace; - - v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, - &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Verify if the sensor has just been powered on. TODO: replace this - * with proper PM, when a suitable API is available. - */ - ret = reg_read(client, RJ54N1_RESET_STANDBY); - if (ret < 0) - return ret; - - if (!(ret & E_EXCLK)) { - ret = rj54n1_reg_init(client); - if (ret < 0) - return ret; - } - - /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ - switch (mf->code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - ret = reg_write(client, RJ54N1_OUT_SEL, 0); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - ret = reg_write(client, RJ54N1_OUT_SEL, 0); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - break; - case MEDIA_BUS_FMT_RGB565_2X8_BE: - ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); - break; - case MEDIA_BUS_FMT_SBGGR10_1X10: - ret = reg_write(client, RJ54N1_OUT_SEL, 5); - break; - default: - ret = -EINVAL; - } - - /* Special case: a raw mode with 10 bits of data per clock tick */ - if (!ret) - ret = reg_set(client, RJ54N1_OCLK_SEL_EN, - (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2); - - if (ret < 0) - return ret; - - /* Supported scales 1:1 >= scale > 1:16 */ - max_w = mf->width * (16 * 1024 - 1) / 1024; - if (input_w > max_w) - input_w = max_w; - max_h = mf->height * (16 * 1024 - 1) / 1024; - if (input_h > max_h) - input_h = max_h; - - output_w = mf->width; - output_h = mf->height; - - ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); - if (ret < 0) - return ret; - - fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, - ARRAY_SIZE(rj54n1_colour_fmts)); - - rj54n1->fmt = fmt; - rj54n1->resize = ret; - rj54n1->rect.width = input_w; - rj54n1->rect.height = input_h; - rj54n1->width = output_w; - rj54n1->height = output_h; - - mf->width = output_w; - mf->height = output_h; - mf->field = V4L2_FIELD_NONE; - mf->colorspace = fmt->colorspace; - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int rj54n1_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg < 0x400 || reg->reg > 0x1fff) - /* Registers > 0x0800 are only available from Sharp support */ - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xff) - return -EIO; - - return 0; -} - -static int rj54n1_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg < 0x400 || reg->reg > 0x1fff) - /* Registers >= 0x0800 are only available from Sharp support */ - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int rj54n1_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct rj54n1 *rj54n1 = to_rj54n1(client); - - return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on); -} - -static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); - struct v4l2_subdev *sd = &rj54n1->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); - else - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); - else - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) - return -EIO; - return 0; - case V4L2_CID_AUTO_WHITE_BALANCE: - /* Auto WB area - whole image */ - if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, - 0x80) < 0) - return -EIO; - rj54n1->auto_wb = ctrl->val; - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { - .s_ctrl = rj54n1_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = rj54n1_g_register, - .s_register = rj54n1_s_register, -#endif - .s_power = rj54n1_s_power, -}; - -static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_RISING) - return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); - else - return reg_write(client, RJ54N1_OUT_SIGPO, 0); -} - -static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { - .s_stream = rj54n1_s_stream, - .g_mbus_config = rj54n1_g_mbus_config, - .s_mbus_config = rj54n1_s_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { - .enum_mbus_code = rj54n1_enum_mbus_code, - .get_selection = rj54n1_get_selection, - .set_selection = rj54n1_set_selection, - .get_fmt = rj54n1_get_fmt, - .set_fmt = rj54n1_set_fmt, -}; - -static const struct v4l2_subdev_ops rj54n1_subdev_ops = { - .core = &rj54n1_subdev_core_ops, - .video = &rj54n1_subdev_video_ops, - .pad = &rj54n1_subdev_pad_ops, -}; - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int rj54n1_video_probe(struct i2c_client *client, - struct rj54n1_pdata *priv) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int data1, data2; - int ret; - - ret = rj54n1_s_power(&rj54n1->subdev, 1); - if (ret < 0) - return ret; - - /* Read out the chip version register */ - data1 = reg_read(client, RJ54N1_DEV_CODE); - data2 = reg_read(client, RJ54N1_DEV_CODE2); - - if (data1 != 0x51 || data2 != 0x10) { - ret = -ENODEV; - dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", - data1, data2); - goto done; - } - - /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */ - ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); - if (ret < 0) - goto done; - - dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", - data1, data2); - - ret = v4l2_ctrl_handler_setup(&rj54n1->hdl); - -done: - rj54n1_s_power(&rj54n1->subdev, 0); - return ret; -} - -static int rj54n1_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct rj54n1 *rj54n1; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct rj54n1_pdata *rj54n1_priv; - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); - return -EINVAL; - } - - rj54n1_priv = ssdd->drv_priv; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); - return -EIO; - } - - rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); - if (!rj54n1) - return -ENOMEM; - - v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); - v4l2_ctrl_handler_init(&rj54n1->hdl, 4); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 66); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - rj54n1->subdev.ctrl_handler = &rj54n1->hdl; - if (rj54n1->hdl.error) - return rj54n1->hdl.error; - - rj54n1->clk_div = clk_div; - rj54n1->rect.left = RJ54N1_COLUMN_SKIP; - rj54n1->rect.top = RJ54N1_ROW_SKIP; - rj54n1->rect.width = RJ54N1_MAX_WIDTH; - rj54n1->rect.height = RJ54N1_MAX_HEIGHT; - rj54n1->width = RJ54N1_MAX_WIDTH; - rj54n1->height = RJ54N1_MAX_HEIGHT; - rj54n1->fmt = &rj54n1_colour_fmts[0]; - rj54n1->resize = 1024; - rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / - (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); - - rj54n1->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(rj54n1->clk)) { - ret = PTR_ERR(rj54n1->clk); - goto eclkget; - } - - ret = rj54n1_video_probe(client, rj54n1_priv); - if (ret < 0) { - v4l2_clk_put(rj54n1->clk); -eclkget: - v4l2_ctrl_handler_free(&rj54n1->hdl); - } - - return ret; -} - -static int rj54n1_remove(struct i2c_client *client) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(rj54n1->clk); - v4l2_device_unregister_subdev(&rj54n1->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - v4l2_ctrl_handler_free(&rj54n1->hdl); - - return 0; -} - -static const struct i2c_device_id rj54n1_id[] = { - { "rj54n1cb0c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, rj54n1_id); - -static struct i2c_driver rj54n1_i2c_driver = { - .driver = { - .name = "rj54n1cb0c", - }, - .probe = rj54n1_probe, - .remove = rj54n1_remove, - .id_table = rj54n1_id, -}; - -module_i2c_driver(rj54n1_i2c_driver); - -MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); From 280de94a651945905cb8337626c40025e4cea56d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:43:47 -0500 Subject: [PATCH 1944/4277] media: soc_camera: Move to the staging tree The SoC camera framework has no functional drivers left, something that has not changed for years. Move the leftovers to the staging tree. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 8 -------- drivers/media/i2c/Makefile | 1 - drivers/media/platform/Kconfig | 1 - drivers/media/platform/Makefile | 2 -- drivers/media/platform/soc_camera/Kconfig | 8 -------- drivers/media/platform/soc_camera/Makefile | 1 - drivers/staging/media/Kconfig | 2 ++ drivers/staging/media/Makefile | 1 + drivers/{media/i2c => staging/media}/soc_camera/Kconfig | 8 ++++++++ drivers/{media/i2c => staging/media}/soc_camera/Makefile | 1 + .../platform => staging/media}/soc_camera/soc_camera.c | 0 .../platform => staging/media}/soc_camera/soc_mediabus.c | 0 .../{media/i2c => staging/media}/soc_camera/soc_mt9v022.c | 0 .../{media/i2c => staging/media}/soc_camera/soc_ov5642.c | 0 .../{media/i2c => staging/media}/soc_camera/soc_ov9740.c | 0 15 files changed, 12 insertions(+), 21 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/Kconfig delete mode 100644 drivers/media/platform/soc_camera/Makefile rename drivers/{media/i2c => staging/media}/soc_camera/Kconfig (74%) rename drivers/{media/i2c => staging/media}/soc_camera/Makefile (76%) rename drivers/{media/platform => staging/media}/soc_camera/soc_camera.c (100%) rename drivers/{media/platform => staging/media}/soc_camera/soc_mediabus.c (100%) rename drivers/{media/i2c => staging/media}/soc_camera/soc_mt9v022.c (100%) rename drivers/{media/i2c => staging/media}/soc_camera/soc_ov5642.c (100%) rename drivers/{media/i2c => staging/media}/soc_camera/soc_ov9740.c (100%) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 19c112cda078..6d32f8dcf83b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1134,12 +1134,4 @@ config VIDEO_I2C endmenu -menu "Sensors used on soc_camera driver" - -if SOC_CAMERA - source "drivers/media/i2c/soc_camera/Kconfig" -endif - -endmenu - endif diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 2e5e4b0bf7f3..a64fca82e0c4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ -obj-y += soc_camera/ obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index b5ccb60cf664..6cff26b29a38 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -141,7 +141,6 @@ config VIDEO_RENESAS_CEU ---help--- This is a v4l2 driver for the Renesas CEU Interface -source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index e6deb2597738..7cbbd925124c 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -62,8 +62,6 @@ obj-y += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o -obj-$(CONFIG_SOC_CAMERA) += soc_camera/ - obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig deleted file mode 100644 index 8f9b3bac5450..000000000000 --- a/drivers/media/platform/soc_camera/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config SOC_CAMERA - tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C - select VIDEOBUF2_CORE - help - SoC Camera is a common API to several cameras, not connecting - over a bus like PCI or USB. For example some i2c camera connected - directly to the data bus of an SoC. diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile deleted file mode 100644 index 85d5e74f3b2b..000000000000 --- a/drivers/media/platform/soc_camera/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 19cadd17e542..7c3f443f2735 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -41,4 +41,6 @@ source "drivers/staging/media/zoran/Kconfig" source "drivers/staging/media/ipu3/Kconfig" +source "drivers/staging/media/soc_camera/Kconfig" + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index edde1960b030..9c1bb862f5c9 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip/vpu/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ +obj-$(CONFIG_SOC_CAMERA) += soc_camera/ diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig similarity index 74% rename from drivers/media/i2c/soc_camera/Kconfig rename to drivers/staging/media/soc_camera/Kconfig index bcd9ef86f40b..ebd78cebd4ec 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -1,3 +1,11 @@ +config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 && HAS_DMA && I2C + select VIDEOBUF2_CORE + help + SoC Camera is a common API to several cameras, not connecting + over a bus like PCI or USB. For example some i2c camera connected + directly to the data bus of an SoC. comment "soc_camera sensor drivers" config SOC_CAMERA_MT9M111 diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile similarity index 76% rename from drivers/media/i2c/soc_camera/Makefile rename to drivers/staging/media/soc_camera/Makefile index 6d63eb31c3b7..e03450cee524 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/staging/media/soc_camera/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c similarity index 100% rename from drivers/media/platform/soc_camera/soc_camera.c rename to drivers/staging/media/soc_camera/soc_camera.c diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c similarity index 100% rename from drivers/media/platform/soc_camera/soc_mediabus.c rename to drivers/staging/media/soc_camera/soc_mediabus.c diff --git a/drivers/media/i2c/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c similarity index 100% rename from drivers/media/i2c/soc_camera/soc_mt9v022.c rename to drivers/staging/media/soc_camera/soc_mt9v022.c diff --git a/drivers/media/i2c/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c similarity index 100% rename from drivers/media/i2c/soc_camera/soc_ov5642.c rename to drivers/staging/media/soc_camera/soc_ov5642.c diff --git a/drivers/media/i2c/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c similarity index 100% rename from drivers/media/i2c/soc_camera/soc_ov9740.c rename to drivers/staging/media/soc_camera/soc_ov9740.c From 045f95e773335f5b2bdd9e261ba5b28ba4083de0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:47:01 -0500 Subject: [PATCH 1945/4277] media: soc_camera: Move the imx074 under soc_camera directory Move the imx074 driver to the soc_camera directory in the media staging tree. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 -- drivers/staging/media/Makefile | 1 - drivers/staging/media/imx074/Kconfig | 5 ----- drivers/staging/media/imx074/Makefile | 1 - drivers/staging/media/imx074/TODO | 5 ----- drivers/staging/media/soc_camera/Kconfig | 7 +++++++ drivers/staging/media/soc_camera/Makefile | 1 + drivers/staging/media/{imx074 => soc_camera}/imx074.c | 0 8 files changed, 8 insertions(+), 14 deletions(-) delete mode 100644 drivers/staging/media/imx074/Kconfig delete mode 100644 drivers/staging/media/imx074/Makefile delete mode 100644 drivers/staging/media/imx074/TODO rename drivers/staging/media/{imx074 => soc_camera}/imx074.c (100%) diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 7c3f443f2735..fce893321624 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,8 +25,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/imx/Kconfig" -source "drivers/staging/media/imx074/Kconfig" - source "drivers/staging/media/mt9t031/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 9c1bb862f5c9..74920289b0d9 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074/ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ diff --git a/drivers/staging/media/imx074/Kconfig b/drivers/staging/media/imx074/Kconfig deleted file mode 100644 index 229cbeea580b..000000000000 --- a/drivers/staging/media/imx074/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config SOC_CAMERA_IMX074 - tristate "imx074 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony diff --git a/drivers/staging/media/imx074/Makefile b/drivers/staging/media/imx074/Makefile deleted file mode 100644 index 7d183574aa84..000000000000 --- a/drivers/staging/media/imx074/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o diff --git a/drivers/staging/media/imx074/TODO b/drivers/staging/media/imx074/TODO deleted file mode 100644 index 15580a4f950c..000000000000 --- a/drivers/staging/media/imx074/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This sensor driver needs to be converted to a regular -v4l2 subdev driver. The soc_camera framework is deprecated and -will be removed in the future. Unless someone does this work this -sensor driver will be deleted when the soc_camera framework is -deleted. diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig index ebd78cebd4ec..e6bd04840971 100644 --- a/drivers/staging/media/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -6,6 +6,7 @@ config SOC_CAMERA SoC Camera is a common API to several cameras, not connecting over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. + comment "soc_camera sensor drivers" config SOC_CAMERA_MT9M111 @@ -35,3 +36,9 @@ config SOC_CAMERA_OV9740 depends on SOC_CAMERA && I2C help This is a ov9740 camera driver + +config SOC_CAMERA_IMX074 + tristate "imx074 support (DEPRECATED)" + depends on SOC_CAMERA && I2C + help + This driver supports IMX074 cameras from Sony diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile index e03450cee524..09560dc32c4c 100644 --- a/drivers/staging/media/soc_camera/Makefile +++ b/drivers/staging/media/soc_camera/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o +obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o diff --git a/drivers/staging/media/imx074/imx074.c b/drivers/staging/media/soc_camera/imx074.c similarity index 100% rename from drivers/staging/media/imx074/imx074.c rename to drivers/staging/media/soc_camera/imx074.c From dc548ee0773bedf74a162e761b3bf487bb84270b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:48:28 -0500 Subject: [PATCH 1946/4277] media: soc_camera: Move the mt9t031 under soc_camera directory Move the mt9t031 driver to the soc_camera directory in the media staging tree. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 -- drivers/staging/media/Makefile | 1 - drivers/staging/media/soc_camera/Kconfig | 6 ++++++ drivers/staging/media/soc_camera/Makefile | 1 + drivers/staging/media/{mt9t031 => soc_camera}/mt9t031.c | 0 5 files changed, 7 insertions(+), 3 deletions(-) rename drivers/staging/media/{mt9t031 => soc_camera}/mt9t031.c (100%) diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index fce893321624..1da5c20d65c0 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,8 +25,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/imx/Kconfig" -source "drivers/staging/media/mt9t031/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rockchip/vpu/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 74920289b0d9..0355e3030504 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig index e6bd04840971..6a6aa6d2d150 100644 --- a/drivers/staging/media/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -42,3 +42,9 @@ config SOC_CAMERA_IMX074 depends on SOC_CAMERA && I2C help This driver supports IMX074 cameras from Sony + +config SOC_CAMERA_MT9T031 + tristate "mt9t031 support (DEPRECATED)" + depends on SOC_CAMERA && I2C + help + This driver supports MT9T031 cameras from Micron. diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile index 09560dc32c4c..3a351bd629f5 100644 --- a/drivers/staging/media/soc_camera/Makefile +++ b/drivers/staging/media/soc_camera/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o +obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/staging/media/mt9t031/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c similarity index 100% rename from drivers/staging/media/mt9t031/mt9t031.c rename to drivers/staging/media/soc_camera/mt9t031.c From dd1df35dd0f6172537271e533fe329a165e56265 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:50:48 -0500 Subject: [PATCH 1947/4277] media: soc_camera: Depend on BROKEN This patch makes the SoC camera framework as well as effectively the few remaining sensor drivers depend on BROKEN, rendering it uncompileable without changes in Kconfig. The purpose is to leave the code around if someone wishes to convert the drivers to the modern day V4L2 sub-device framework without having to go to see development history in git. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/soc_camera/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig index 6a6aa6d2d150..bacd30f0348d 100644 --- a/drivers/staging/media/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -1,6 +1,6 @@ config SOC_CAMERA tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C + depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN select VIDEOBUF2_CORE help SoC Camera is a common API to several cameras, not connecting From 959bba134238452b0a2f5c7d107d7d05b358b41f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 17 Feb 2019 09:12:41 -0500 Subject: [PATCH 1948/4277] media: vivid: two unregistration fixes When the media device registration fails, don't call media_device_unregister since the device was never actually registered. When removing the module also call media_device_cleanup() to avoid a memory leak. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 29e7b14fa704..342e0e6c103b 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -1478,9 +1478,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) return 0; unreg_dev: -#ifdef CONFIG_MEDIA_CONTROLLER - media_device_unregister(&dev->mdev); -#endif video_unregister_device(&dev->radio_tx_dev); video_unregister_device(&dev->radio_rx_dev); video_unregister_device(&dev->sdr_cap_dev); @@ -1553,6 +1550,7 @@ static int vivid_remove(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER media_device_unregister(&dev->mdev); + media_device_cleanup(&dev->mdev); #endif if (dev->has_vid_cap) { From cdabfa84025946b0eae4fefb5f6af3f9e99ce88b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 17 Feb 2019 09:38:19 -0500 Subject: [PATCH 1949/4277] media: vimc: fix memory leak media_device_cleanup() wasn't called, which caused a small memory leak. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index c2fdf3ea67ed..0fbb7914098f 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -220,6 +220,7 @@ static int vimc_comp_bind(struct device *master) err_mdev_unregister: media_device_unregister(&vimc->mdev); + media_device_cleanup(&vimc->mdev); err_comp_unbind_all: component_unbind_all(master, NULL); err_v4l2_unregister: @@ -236,6 +237,7 @@ static void vimc_comp_unbind(struct device *master) dev_dbg(master, "unbind"); media_device_unregister(&vimc->mdev); + media_device_cleanup(&vimc->mdev); component_unbind_all(master, NULL); v4l2_device_unregister(&vimc->v4l2_dev); } From e3e71be056df8309c80ed7457122b726172c45a1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 17 Feb 2019 17:26:05 -0500 Subject: [PATCH 1950/4277] media: wl128x: fix spelling mistake: "Swtich" -> "Switch" There is a spelling mistake in a fmdbg debug message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/wl128x/fmdrv_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 800d69c3f80b..e1c218b23d9e 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -908,7 +908,7 @@ static void fm_irq_afjump_setfreq(struct fmdev *fmdev) u16 frq_index; u16 payload; - fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); + fmdbg("Switch to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; From 46c039d06b6ecabb94bd16c3a999b28dc83b79ce Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Mon, 14 Jan 2019 19:14:24 -0500 Subject: [PATCH 1951/4277] media: cx25840: mark pad sig_types to fix cx231xx init Without this, we get failures like this when the kernel attempts to initialize a cx231xx device: [16046.153653] cx231xx 3-1.2:1.1: New device Hauppauge Hauppauge Device @ 480 Mbps (2040:c200) with 6 interfaces [16046.153900] cx231xx 3-1.2:1.1: can't change interface 3 alt no. to 3: Max. Pkt size = 0 [16046.153907] cx231xx 3-1.2:1.1: Identified as Hauppauge USB Live 2 (card=9) [16046.154350] i2c i2c-11: Added multiplexed i2c bus 13 [16046.154379] i2c i2c-11: Added multiplexed i2c bus 14 [16046.267194] cx25840 10-0044: cx23102 A/V decoder found @ 0x88 (cx231xx #0-0) [16048.424551] cx25840 10-0044: loaded v4l-cx231xx-avcore-01.fw firmware (16382 bytes) [16048.463224] cx231xx 3-1.2:1.1: v4l2 driver version 0.0.3 [16048.567878] cx231xx 3-1.2:1.1: Registered video device video2 [v4l2] [16048.568001] cx231xx 3-1.2:1.1: Registered VBI device vbi0 [16048.568419] cx231xx 3-1.2:1.1: audio EndPoint Addr 0x83, Alternate settings: 3 [16048.568425] cx231xx 3-1.2:1.1: video EndPoint Addr 0x84, Alternate settings: 5 [16048.568431] cx231xx 3-1.2:1.1: VBI EndPoint Addr 0x85, Alternate settings: 2 [16048.568436] cx231xx 3-1.2:1.1: sliced CC EndPoint Addr 0x86, Alternate settings: 2 [16048.568448] usb 3-1.2: couldn't get decoder output pad for V4L I/O [16048.568453] cx231xx 3-1.2:1.1: V4L2 device vbi0 deregistered [16048.568579] cx231xx 3-1.2:1.1: V4L2 device video2 deregistered [16048.569001] cx231xx: probe of 3-1.2:1.1 failed with error -22 Likely a regession since Commit 9d6d20e652c0 ("media: v4l2-mc: switch it to use the new approach to setup pipelines") (v4.19-rc1-100-g9d6d20e652c0), which introduced the use of PAD_SIGNAL_DV within v4l2_mc_create_media_graph(). This also modifies cx25840 to remove the VBI pad, matching the action taken in Commit 092a37875a22 ("media: v4l2: remove VBI output pad"). Fixes: 9d6d20e652c0 ("media: v4l2-mc: switch it to use the new approach to setup pipelines") Cc: stable@vger.kernel.org Signed-off-by: Cody P Schafer Tested-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-core.c | 3 ++- drivers/media/i2c/cx25840/cx25840-core.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index b168bf3635b6..8b0b8b5aa531 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5216,8 +5216,9 @@ static int cx25840_probe(struct i2c_client *client, * those extra inputs. So, let's add it only when needed. */ state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[CX25840_PAD_INPUT].sig_type = PAD_SIGNAL_ANALOG; state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; - state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[CX25840_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; sd->entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h index c323b1af1f83..9efefa15d090 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.h +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -40,7 +40,6 @@ enum cx25840_model { enum cx25840_media_pads { CX25840_PAD_INPUT, CX25840_PAD_VID_OUT, - CX25840_PAD_VBI_OUT, CX25840_NUM_PADS }; From a7fe4ca72b1fe7877de5672640d0b4e023d0fdca Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 7 Feb 2019 22:18:43 -0500 Subject: [PATCH 1952/4277] media: v4l: Add 32-bit packed YUV formats The formats added in this patch include: V4L2_PIX_FMT_AYUV32 V4L2_PIX_FMT_XYUV32 V4L2_PIX_FMT_VUYA32 V4L2_PIX_FMT_VUYX32 These formats enable the trasmission of alpha channel data to other drivers and userspace applications in addition to YUV data. For example, buffers generated by drivers in one of these formats can be used by the Weston compositor to display as a texture or flipped directly onto the overlay planes with the help of a DRM driver. Signed-off-by: Vivek Kasireddy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/uapi/v4l/pixfmt-packed-yuv.rst | 170 +++++++++++++++++- drivers/media/v4l2-core/v4l2-ioctl.c | 4 + include/uapi/linux/videodev2.h | 4 + 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst b/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst index f53e8f57a003..7fcee1c11ac4 100644 --- a/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst +++ b/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst @@ -190,6 +190,170 @@ component of each pixel in one 16 or 32 bit word. - Cr\ :sub:`2` - Cr\ :sub:`1` - Cr\ :sub:`0` + - + * .. _V4L2-PIX-FMT-AYUV32: + + - ``V4L2_PIX_FMT_AYUV32`` + - 'AYUV' + + - a\ :sub:`7` + - a\ :sub:`6` + - a\ :sub:`5` + - a\ :sub:`4` + - a\ :sub:`3` + - a\ :sub:`2` + - a\ :sub:`1` + - a\ :sub:`0` + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + - + * .. _V4L2-PIX-FMT-XYUV32: + + - ``V4L2_PIX_FMT_XYUV32`` + - 'XYUV' + + - + - + - + - + - + - + - + - + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + - + * .. _V4L2-PIX-FMT-VUYA32: + + - ``V4L2_PIX_FMT_VUYA32`` + - 'VUYA' + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - a\ :sub:`7` + - a\ :sub:`6` + - a\ :sub:`5` + - a\ :sub:`4` + - a\ :sub:`3` + - a\ :sub:`2` + - a\ :sub:`1` + - a\ :sub:`0` + - + * .. _V4L2-PIX-FMT-VUYX32: + + - ``V4L2_PIX_FMT_VUYX32`` + - 'VUYX' + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - + - + - + - + - + - + - + - .. raw:: latex @@ -202,4 +366,8 @@ component of each pixel in one 16 or 32 bit word. #) The value of a = alpha bits is undefined when reading from the driver, ignored when writing to the driver, except when alpha blending has been negotiated for a :ref:`Video Overlay ` or - :ref:`Video Output Overlay `. + :ref:`Video Output Overlay ` for the formats Y444, YUV555 and + YUV4. However, for formats AYUV32 and VUYA32, the alpha component is + expected to contain a meaningful value that can be used by drivers + and applications. And, the formats XYUV32 and VUYX32 contain undefined + alpha values that must be ignored by all applications and drivers. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 206b7348797e..6f707466b5d2 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1220,6 +1220,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_YUV555: descr = "16-bit A/XYUV 1-5-5-5"; break; case V4L2_PIX_FMT_YUV565: descr = "16-bit YUV 5-6-5"; break; case V4L2_PIX_FMT_YUV32: descr = "32-bit A/XYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_AYUV32: descr = "32-bit AYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_XYUV32: descr = "32-bit XYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_VUYA32: descr = "32-bit VUYA 8-8-8-8"; break; + case V4L2_PIX_FMT_VUYX32: descr = "32-bit VUYX 8-8-8-8"; break; case V4L2_PIX_FMT_YUV410: descr = "Planar YUV 4:1:0"; break; case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break; case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 9a920f071ff9..1db220da3bcc 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -562,6 +562,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */ #define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */ #define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */ +#define V4L2_PIX_FMT_AYUV32 v4l2_fourcc('A', 'Y', 'U', 'V') /* 32 AYUV-8-8-8-8 */ +#define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */ +#define V4L2_PIX_FMT_VUYA32 v4l2_fourcc('V', 'U', 'Y', 'A') /* 32 VUYA-8-8-8-8 */ +#define V4L2_PIX_FMT_VUYX32 v4l2_fourcc('V', 'U', 'Y', 'X') /* 32 VUYX-8-8-8-8 */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */ #define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ #define V4L2_PIX_FMT_M420 v4l2_fourcc('M', '4', '2', '0') /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */ From 10a2bc7e63f9fc0a7255a301130cad02fa521326 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 8 Feb 2019 20:29:32 -0500 Subject: [PATCH 1953/4277] media: v4l2-tpg-core: Add support for 32-bit packed YUV formats (v2) Add support for the following formats to tpg: V4L2_PIX_FMT_AYUV32 V4L2_PIX_FMT_XYUV32 V4L2_PIX_FMT_VUYA32 V4L2_PIX_FMT_VUYX32 Changes from v1: Remove the duplicate case value Signed-off-by: Vivek Kasireddy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index d9a590ae7545..07e0629af8ed 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -246,6 +246,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_YUV555: case V4L2_PIX_FMT_YUV565: case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_AYUV32: + case V4L2_PIX_FMT_XYUV32: + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: tpg->color_enc = TGP_COLOR_ENC_YCBCR; break; case V4L2_PIX_FMT_YUV420M: @@ -372,6 +376,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_ABGR32: case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_AYUV32: + case V4L2_PIX_FMT_XYUV32: + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: case V4L2_PIX_FMT_HSV32: tpg->twopixelsize[0] = 2 * 4; break; @@ -1267,10 +1275,12 @@ static void gen_twopix(struct tpg_data *tpg, case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_XRGB32: case V4L2_PIX_FMT_HSV32: + case V4L2_PIX_FMT_XYUV32: alpha = 0; /* fall through */ case V4L2_PIX_FMT_YUV32: case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_AYUV32: buf[0][offset] = alpha; buf[0][offset + 1] = r_y_h; buf[0][offset + 2] = g_u_s; @@ -1278,9 +1288,11 @@ static void gen_twopix(struct tpg_data *tpg, break; case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_VUYX32: alpha = 0; /* fall through */ case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_VUYA32: buf[0][offset] = b_v; buf[0][offset + 1] = g_u_s; buf[0][offset + 2] = r_y_h; From 6377bb7d18cc2d5892aa8f8f67b3226331847ec3 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 7 Feb 2019 22:18:45 -0500 Subject: [PATCH 1954/4277] media: vivid: Add definitions for the 32-bit packed YUV formats Enable vivid to make use of the following formats: V4L2_PIX_FMT_AYUV32 V4L2_PIX_FMT_XYUV32 V4L2_PIX_FMT_VUYA32 V4L2_PIX_FMT_VUYX32 Signed-off-by: Vivek Kasireddy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/vivid/vivid-vid-common.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index 661f4015fba1..74b83bcc6119 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -168,6 +168,36 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, .alpha_mask = 0x000000ff, }, + { + .fourcc = V4L2_PIX_FMT_AYUV32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0x000000ff, + }, + { + .fourcc = V4L2_PIX_FMT_XYUV32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_VUYA32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0xff000000, + }, + { + .fourcc = V4L2_PIX_FMT_VUYX32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + }, { .fourcc = V4L2_PIX_FMT_GREY, .vdownsampling = { 1 }, From 98617b45674113d241ec3264019217108d3a87a4 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 8 Feb 2019 20:38:18 -0500 Subject: [PATCH 1955/4277] media: imx-pxp: Start using the format VUYA32 instead of YUV32 (v2) Buffers generated with YUV32 format seems to be incorrect, hence use VUYA32 instead. Changes from v1: Add both formats VUYA32 and VUYX32 but associate only VUYX32 to the output queue as the alpha channel of buffers is ignored on this queue. Signed-off-by: Vivek Kasireddy Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/imx-pxp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c index f087dc4fc729..0bcfc5aa8f3d 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/imx-pxp.c @@ -90,7 +90,11 @@ static struct pxp_fmt formats[] = { .depth = 16, .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, }, { - .fourcc = V4L2_PIX_FMT_YUV32, + .fourcc = V4L2_PIX_FMT_VUYA32, + .depth = 32, + .types = MEM2MEM_CAPTURE, + }, { + .fourcc = V4L2_PIX_FMT_VUYX32, .depth = 32, .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, }, { @@ -236,7 +240,7 @@ static u32 pxp_v4l2_pix_fmt_to_ps_format(u32 v4l2_pix_fmt) case V4L2_PIX_FMT_RGB555: return BV_PXP_PS_CTRL_FORMAT__RGB555; case V4L2_PIX_FMT_RGB444: return BV_PXP_PS_CTRL_FORMAT__RGB444; case V4L2_PIX_FMT_RGB565: return BV_PXP_PS_CTRL_FORMAT__RGB565; - case V4L2_PIX_FMT_YUV32: return BV_PXP_PS_CTRL_FORMAT__YUV1P444; + case V4L2_PIX_FMT_VUYX32: return BV_PXP_PS_CTRL_FORMAT__YUV1P444; case V4L2_PIX_FMT_UYVY: return BV_PXP_PS_CTRL_FORMAT__UYVY1P422; case V4L2_PIX_FMT_YUYV: return BM_PXP_PS_CTRL_WB_SWAP | BV_PXP_PS_CTRL_FORMAT__UYVY1P422; @@ -265,7 +269,8 @@ static u32 pxp_v4l2_pix_fmt_to_out_format(u32 v4l2_pix_fmt) case V4L2_PIX_FMT_RGB555: return BV_PXP_OUT_CTRL_FORMAT__RGB555; case V4L2_PIX_FMT_RGB444: return BV_PXP_OUT_CTRL_FORMAT__RGB444; case V4L2_PIX_FMT_RGB565: return BV_PXP_OUT_CTRL_FORMAT__RGB565; - case V4L2_PIX_FMT_YUV32: return BV_PXP_OUT_CTRL_FORMAT__YUV1P444; + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: return BV_PXP_OUT_CTRL_FORMAT__YUV1P444; case V4L2_PIX_FMT_UYVY: return BV_PXP_OUT_CTRL_FORMAT__UYVY1P422; case V4L2_PIX_FMT_VYUY: return BV_PXP_OUT_CTRL_FORMAT__VYUY1P422; case V4L2_PIX_FMT_GREY: return BV_PXP_OUT_CTRL_FORMAT__Y8; @@ -281,7 +286,8 @@ static u32 pxp_v4l2_pix_fmt_to_out_format(u32 v4l2_pix_fmt) static bool pxp_v4l2_pix_fmt_is_yuv(u32 v4l2_pix_fmt) { switch (v4l2_pix_fmt) { - case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_VYUY: From cfc7740835d09b98a304a4793c93b6074c04379e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Feb 2019 05:11:32 -0500 Subject: [PATCH 1956/4277] media: vb2: replace bool by bitfield in vb2_buffer The bool type is not recommended for use in structs, so replace these by bitfields. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 12 ++++++------ include/media/videobuf2-core.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 34cc87ca8d59..b4457edc3245 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -934,7 +934,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = false; + vb->synced = 0; } spin_lock_irqsave(&q->done_lock, flags); @@ -1313,8 +1313,8 @@ static int __buf_prepare(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - vb->synced = true; - vb->prepared = true; + vb->synced = 1; + vb->prepared = 1; vb->state = orig_state; return 0; @@ -1802,7 +1802,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, } call_void_vb_qop(vb, buf_finish, vb); - vb->prepared = false; + vb->prepared = 0; if (pindex) *pindex = vb->index; @@ -1926,12 +1926,12 @@ static void __vb2_queue_cancel(struct vb2_queue *q) for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = false; + vb->synced = 0; } if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); - vb->prepared = false; + vb->prepared = 0; } __vb2_dqbuf(vb); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 06142c1469cc..6d5490bb91d3 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -269,8 +269,8 @@ struct vb2_buffer { * vb2_plane: per-plane information; do not change */ enum vb2_buffer_state state; - bool synced; - bool prepared; + unsigned int synced:1; + unsigned int prepared:1; struct vb2_plane planes[VB2_MAX_PLANES]; struct list_head queued_entry; From 7e4e71624491d8a8befe62b43138beb0ab696006 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Feb 2019 05:11:33 -0500 Subject: [PATCH 1957/4277] media: vb2: keep track of timestamp status If a stream is stopped, or if a USERPTR/DMABUF buffer is queued backed by a different user address or dmabuf fd, then the timestamp should be skipped by vb2_find_timestamp since the memory it refers to is no longer valid. So keep track of a 'copied_timestamp' state: it is set when the timestamp is copied from an output to a capture buffer, and is cleared when it is no longer valid. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 3 +++ drivers/media/common/videobuf2/videobuf2-v4l2.c | 3 ++- drivers/media/v4l2-core/v4l2-mem2mem.c | 1 + include/media/videobuf2-core.h | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index b4457edc3245..275cddd8808c 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1041,6 +1041,7 @@ static int __prepare_userptr(struct vb2_buffer *vb) if (vb->planes[plane].mem_priv) { if (!reacquired) { reacquired = true; + vb->copied_timestamp = 0; call_void_vb_qop(vb, buf_cleanup, vb); } call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv); @@ -1165,6 +1166,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) if (!reacquired) { reacquired = true; + vb->copied_timestamp = 0; call_void_vb_qop(vb, buf_cleanup, vb); } @@ -1942,6 +1944,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) if (vb->request) media_request_put(vb->request); vb->request = NULL; + vb->copied_timestamp = 0; } } diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 3aeaea3af42a..55277370c313 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -604,7 +604,8 @@ int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, unsigned int i; for (i = start_idx; i < q->num_buffers; i++) - if (q->bufs[i]->timestamp == timestamp) + if (q->bufs[i]->copied_timestamp && + q->bufs[i]->timestamp == timestamp) return i; return -1; } diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 1494d0d5951a..add228c98a9a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -992,6 +992,7 @@ void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb, cap_vb->field = out_vb->field; cap_vb->flags &= ~mask; cap_vb->flags |= out_vb->flags & mask; + cap_vb->vb2_buf.copied_timestamp = 1; } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_metadata); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 6d5490bb91d3..a844abcae71e 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -262,6 +262,8 @@ struct vb2_buffer { * prepared: this buffer has been prepared, i.e. the * buf_prepare op was called. It is cleared again * after the 'buf_finish' op is called. + * copied_timestamp: the timestamp of this capture buffer was copied + * from an output buffer. * queued_entry: entry on the queued buffers list, which holds * all buffers queued from userspace * done_entry: entry on the list that stores all buffers ready @@ -271,6 +273,7 @@ struct vb2_buffer { enum vb2_buffer_state state; unsigned int synced:1; unsigned int prepared:1; + unsigned int copied_timestamp:1; struct vb2_plane planes[VB2_MAX_PLANES]; struct list_head queued_entry; From b7990bcfb644c53511cca97dca6e39d1bd70732a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:41 -0500 Subject: [PATCH 1958/4277] media: cec: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in cec_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Reviewed-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 391b6fd483e1..156a0d76ab2a 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -38,6 +38,7 @@ static __poll_t cec_poll(struct file *filp, struct cec_adapter *adap = fh->adap; __poll_t res = 0; + poll_wait(filp, &fh->wait, poll); if (!cec_is_registered(adap)) return EPOLLERR | EPOLLHUP; mutex_lock(&adap->lock); @@ -48,7 +49,6 @@ static __poll_t cec_poll(struct file *filp, res |= EPOLLIN | EPOLLRDNORM; if (fh->total_queued_events) res |= EPOLLPRI; - poll_wait(filp, &fh->wait, poll); mutex_unlock(&adap->lock); return res; } From f4dd471b5c368c396e8e03a65fec64bced949639 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:42 -0500 Subject: [PATCH 1959/4277] media: media-request: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in media_request_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-request.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c index c71a34ae6383..eec2e2b2f6ec 100644 --- a/drivers/media/media-request.c +++ b/drivers/media/media-request.c @@ -100,6 +100,7 @@ static __poll_t media_request_poll(struct file *filp, if (!(poll_requested_events(wait) & EPOLLPRI)) return 0; + poll_wait(filp, &req->poll_wait, wait); spin_lock_irqsave(&req->lock, flags); if (req->state == MEDIA_REQUEST_STATE_COMPLETE) { ret = EPOLLPRI; @@ -110,8 +111,6 @@ static __poll_t media_request_poll(struct file *filp, goto unlock; } - poll_wait(filp, &req->poll_wait, wait); - unlock: spin_unlock_irqrestore(&req->lock, flags); return ret; From 914c68686441c93f6954267dabef6c936ebb479f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:43 -0500 Subject: [PATCH 1960/4277] media: vb2: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in vb2_core_poll() and vb2_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Reported-by: Yi Qingliang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 4 ++-- drivers/media/common/videobuf2/videobuf2-v4l2.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 275cddd8808c..15b6b9c0a2e4 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2291,6 +2291,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, if (q->is_output && !(req_events & (EPOLLOUT | EPOLLWRNORM))) return 0; + poll_wait(file, &q->done_wq, wait); + /* * Start file I/O emulator only if streaming API has not been used yet. */ @@ -2342,8 +2344,6 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, */ if (q->last_buffer_dequeued) return EPOLLIN | EPOLLRDNORM; - - poll_wait(file, &q->done_wq, wait); } /* diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 55277370c313..d09dee20e421 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -868,16 +868,14 @@ EXPORT_SYMBOL_GPL(vb2_queue_release); __poll_t vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { struct video_device *vfd = video_devdata(file); - __poll_t req_events = poll_requested_events(wait); __poll_t res = 0; if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { struct v4l2_fh *fh = file->private_data; + poll_wait(file, &fh->wait, wait); if (v4l2_event_pending(fh)) res = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->wait, wait); } return res | vb2_core_poll(q, file, wait); From 398d768025c171245918828b330697f9442d0fc1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:44 -0500 Subject: [PATCH 1961/4277] media: v4l2-ctrls.c: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in v4l2_ctrl_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 99308dac2daa..b79d3bbd8350 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -4166,9 +4166,9 @@ __poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) { struct v4l2_fh *fh = file->private_data; + poll_wait(file, &fh->wait, wait); if (v4l2_event_pending(fh)) return EPOLLPRI; - poll_wait(file, &fh->wait, wait); return 0; } EXPORT_SYMBOL(v4l2_ctrl_poll); From dd8695e4e1e39136968339ceed5680327c174a31 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:45 -0500 Subject: [PATCH 1962/4277] media: v4l2-mem2mem: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in v4l2_m2m_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index add228c98a9a..36ae6123dcef 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -617,20 +617,22 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, __poll_t rc = 0; unsigned long flags; + src_q = v4l2_m2m_get_src_vq(m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); + + poll_wait(file, &src_q->done_wq, wait); + poll_wait(file, &dst_q->done_wq, wait); + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { struct v4l2_fh *fh = file->private_data; + poll_wait(file, &fh->wait, wait); if (v4l2_event_pending(fh)) rc = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->wait, wait); if (!(req_events & (EPOLLOUT | EPOLLWRNORM | EPOLLIN | EPOLLRDNORM))) return rc; } - src_q = v4l2_m2m_get_src_vq(m2m_ctx); - dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); - /* * There has to be at least one buffer queued on each queued_list, which * means either in driver already or waiting for driver to claim it @@ -642,11 +644,6 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, goto end; } - spin_lock_irqsave(&src_q->done_lock, flags); - if (list_empty(&src_q->done_list)) - poll_wait(file, &src_q->done_wq, wait); - spin_unlock_irqrestore(&src_q->done_lock, flags); - spin_lock_irqsave(&dst_q->done_lock, flags); if (list_empty(&dst_q->done_list)) { /* @@ -657,8 +654,6 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, spin_unlock_irqrestore(&dst_q->done_lock, flags); return rc | EPOLLIN | EPOLLRDNORM; } - - poll_wait(file, &dst_q->done_wq, wait); } spin_unlock_irqrestore(&dst_q->done_lock, flags); From 96ebc0ca6f6d0978b1a016e6daa29817a812ce55 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:46 -0500 Subject: [PATCH 1963/4277] media: v4l2-mem2mem: add q->error check to v4l2_m2m_poll() The v4l2_m2m_poll function didn't check whether q->error was set for either of the two queues. Add support for this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 36ae6123dcef..57eccbaa52de 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -638,8 +638,10 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * means either in driver already or waiting for driver to claim it * and start processing. */ - if ((!src_q->streaming || list_empty(&src_q->queued_list)) - && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { + if ((!src_q->streaming || src_q->error || + list_empty(&src_q->queued_list)) && + (!dst_q->streaming || dst_q->error || + list_empty(&dst_q->queued_list))) { rc |= EPOLLERR; goto end; } From bb436cbeb918740638278217c241d81a306c1bc3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:47 -0500 Subject: [PATCH 1964/4277] media: videobuf: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in videobuf_poll_stream(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index d1bcfa91aaf8..ab849052b6dc 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -1119,13 +1119,14 @@ done: EXPORT_SYMBOL_GPL(videobuf_read_stream); __poll_t videobuf_poll_stream(struct file *file, - struct videobuf_queue *q, - poll_table *wait) + struct videobuf_queue *q, + poll_table *wait) { __poll_t req_events = poll_requested_events(wait); struct videobuf_buffer *buf = NULL; __poll_t rc = 0; + poll_wait(file, &buf->done, wait); videobuf_queue_lock(q); if (q->streaming) { if (!list_empty(&q->stream)) @@ -1149,7 +1150,6 @@ __poll_t videobuf_poll_stream(struct file *file, rc = EPOLLERR; if (0 == rc) { - poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { switch (q->type) { From c6f5c7c237fb7179f3652a46108086c9178eb419 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:48 -0500 Subject: [PATCH 1965/4277] media: dvb-core: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in dvb_demux_poll(), dvb_dvr_poll() and dvb_ca_en50221_io_poll(). Fix this, otherwise epoll() can timeout when it shouldn't. Signed-off-by: Hans Verkuil Reviewed-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dmxdev.c | 8 ++++---- drivers/media/dvb-core/dvb_ca_en50221.c | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 1544e8cef564..f14a872d1268 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -1195,13 +1195,13 @@ static __poll_t dvb_demux_poll(struct file *file, poll_table *wait) struct dmxdev_filter *dmxdevfilter = file->private_data; __poll_t mask = 0; + poll_wait(file, &dmxdevfilter->buffer.queue, wait); + if ((!dmxdevfilter) || dmxdevfilter->dev->exit) return EPOLLERR; if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait); - poll_wait(file, &dmxdevfilter->buffer.queue, wait); - if (dmxdevfilter->state != DMXDEV_STATE_GO && dmxdevfilter->state != DMXDEV_STATE_DONE && dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT) @@ -1346,13 +1346,13 @@ static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait) dprintk("%s\n", __func__); + poll_wait(file, &dmxdev->dvr_buffer.queue, wait); + if (dmxdev->exit) return EPOLLERR; if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx)) return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait); - poll_wait(file, &dmxdev->dvr_buffer.queue, wait); - if (((file->f_flags & O_ACCMODE) == O_RDONLY) || dmxdev->may_do_mmap) { if (dmxdev->dvr_buffer.error) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 4d371cea0d5d..ebf1e3b03819 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1797,6 +1797,8 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) dprintk("%s\n", __func__); + poll_wait(file, &ca->wait_queue, wait); + if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) mask |= EPOLLIN; @@ -1804,9 +1806,6 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) if (mask) return mask; - /* wait for something to happen */ - poll_wait(file, &ca->wait_queue, wait); - if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) mask |= EPOLLIN; From 4f14e3272f1ca1b317447cb77242cd382854fcca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 8 Feb 2019 10:53:50 +0100 Subject: [PATCH 1966/4277] extended-controls.rst: split up per control class The extended-controls.rst file had become too big. Split it up: each control class reference gets its own rst file, and this file just describes the Extended Control API. Each control class reference is also moved up one level into the table of contents to make it easier to find e.g. the codec control reference. Finally I rearranged the order so that all camera-related control classes are grouped together, ditto for codec/jpeg and fm-rx/tx. The ext-ctrls-codec.rst is still pretty big and it is a candidate to split up further in the future, possibly per codec. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/common.rst | 11 + .../media/uapi/v4l/ext-ctrls-camera.rst | 508 +++ .../media/uapi/v4l/ext-ctrls-codec.rst | 2451 +++++++++++ .../media/uapi/v4l/ext-ctrls-detect.rst | 71 + Documentation/media/uapi/v4l/ext-ctrls-dv.rst | 166 + .../media/uapi/v4l/ext-ctrls-flash.rst | 192 + .../media/uapi/v4l/ext-ctrls-fm-rx.rst | 95 + .../media/uapi/v4l/ext-ctrls-fm-tx.rst | 188 + .../uapi/v4l/ext-ctrls-image-process.rst | 63 + .../media/uapi/v4l/ext-ctrls-image-source.rst | 57 + .../media/uapi/v4l/ext-ctrls-jpeg.rst | 113 + .../media/uapi/v4l/ext-ctrls-rf-tuner.rst | 96 + .../media/uapi/v4l/extended-controls.rst | 3920 +---------------- 13 files changed, 4014 insertions(+), 3917 deletions(-) create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-camera.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-codec.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-detect.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-dv.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-flash.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-image-process.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-image-source.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst diff --git a/Documentation/media/uapi/v4l/common.rst b/Documentation/media/uapi/v4l/common.rst index 889f2f2632a1..5e87ae24e4b4 100644 --- a/Documentation/media/uapi/v4l/common.rst +++ b/Documentation/media/uapi/v4l/common.rst @@ -46,6 +46,17 @@ applicable to all devices. dv-timings control extended-controls + ext-ctrls-camera + ext-ctrls-flash + ext-ctrls-image-source + ext-ctrls-image-process + ext-ctrls-codec + ext-ctrls-jpeg + ext-ctrls-dv + ext-ctrls-rf-tuner + ext-ctrls-fm-tx + ext-ctrls-fm-rx + ext-ctrls-detect format planar-apis selection-api diff --git a/Documentation/media/uapi/v4l/ext-ctrls-camera.rst b/Documentation/media/uapi/v4l/ext-ctrls-camera.rst new file mode 100644 index 000000000000..d3a553cd86c9 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-camera.rst @@ -0,0 +1,508 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _camera-controls: + +************************ +Camera Control Reference +************************ + +The Camera class includes controls for mechanical (or equivalent +digital) features of a device such as controllable lenses or sensors. + + +.. _camera-control-id: + +Camera Control IDs +================== + +``V4L2_CID_CAMERA_CLASS (class)`` + The Camera class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +.. _v4l2-exposure-auto-type: + +``V4L2_CID_EXPOSURE_AUTO`` + (enum) + +enum v4l2_exposure_auto_type - + Enables automatic adjustments of the exposure time and/or iris + aperture. The effect of manual changes of the exposure time or iris + aperture while these features are enabled is undefined, drivers + should ignore such requests. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_EXPOSURE_AUTO`` + - Automatic exposure time, automatic iris aperture. + * - ``V4L2_EXPOSURE_MANUAL`` + - Manual exposure time, manual iris. + * - ``V4L2_EXPOSURE_SHUTTER_PRIORITY`` + - Manual exposure time, auto iris. + * - ``V4L2_EXPOSURE_APERTURE_PRIORITY`` + - Auto exposure time, manual iris. + + + +``V4L2_CID_EXPOSURE_ABSOLUTE (integer)`` + Determines the exposure time of the camera sensor. The exposure time + is limited by the frame interval. Drivers should interpret the + values as 100 µs units, where the value 1 stands for 1/10000th of a + second, 10000 for 1 second and 100000 for 10 seconds. + +``V4L2_CID_EXPOSURE_AUTO_PRIORITY (boolean)`` + When ``V4L2_CID_EXPOSURE_AUTO`` is set to ``AUTO`` or + ``APERTURE_PRIORITY``, this control determines if the device may + dynamically vary the frame rate. By default this feature is disabled + (0) and the frame rate must remain constant. + +``V4L2_CID_AUTO_EXPOSURE_BIAS (integer menu)`` + Determines the automatic exposure compensation, it is effective only + when ``V4L2_CID_EXPOSURE_AUTO`` control is set to ``AUTO``, + ``SHUTTER_PRIORITY`` or ``APERTURE_PRIORITY``. It is expressed in + terms of EV, drivers should interpret the values as 0.001 EV units, + where the value 1000 stands for +1 EV. + + Increasing the exposure compensation value is equivalent to + decreasing the exposure value (EV) and will increase the amount of + light at the image sensor. The camera performs the exposure + compensation by adjusting absolute exposure time and/or aperture. + +.. _v4l2-exposure-metering: + +``V4L2_CID_EXPOSURE_METERING`` + (enum) + +enum v4l2_exposure_metering - + Determines how the camera measures the amount of light available for + the frame exposure. Possible values are: + +.. tabularcolumns:: |p{8.5cm}|p{9.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_EXPOSURE_METERING_AVERAGE`` + - Use the light information coming from the entire frame and average + giving no weighting to any particular portion of the metered area. + * - ``V4L2_EXPOSURE_METERING_CENTER_WEIGHTED`` + - Average the light information coming from the entire frame giving + priority to the center of the metered area. + * - ``V4L2_EXPOSURE_METERING_SPOT`` + - Measure only very small area at the center of the frame. + * - ``V4L2_EXPOSURE_METERING_MATRIX`` + - A multi-zone metering. The light intensity is measured in several + points of the frame and the results are combined. The algorithm of + the zones selection and their significance in calculating the + final value is device dependent. + + + +``V4L2_CID_PAN_RELATIVE (integer)`` + This control turns the camera horizontally by the specified amount. + The unit is undefined. A positive value moves the camera to the + right (clockwise when viewed from above), a negative value to the + left. A value of zero does not cause motion. This is a write-only + control. + +``V4L2_CID_TILT_RELATIVE (integer)`` + This control turns the camera vertically by the specified amount. + The unit is undefined. A positive value moves the camera up, a + negative value down. A value of zero does not cause motion. This is + a write-only control. + +``V4L2_CID_PAN_RESET (button)`` + When this control is set, the camera moves horizontally to the + default position. + +``V4L2_CID_TILT_RESET (button)`` + When this control is set, the camera moves vertically to the default + position. + +``V4L2_CID_PAN_ABSOLUTE (integer)`` + This control turns the camera horizontally to the specified + position. Positive values move the camera to the right (clockwise + when viewed from above), negative values to the left. Drivers should + interpret the values as arc seconds, with valid values between -180 + * 3600 and +180 * 3600 inclusive. + +``V4L2_CID_TILT_ABSOLUTE (integer)`` + This control turns the camera vertically to the specified position. + Positive values move the camera up, negative values down. Drivers + should interpret the values as arc seconds, with valid values + between -180 * 3600 and +180 * 3600 inclusive. + +``V4L2_CID_FOCUS_ABSOLUTE (integer)`` + This control sets the focal point of the camera to the specified + position. The unit is undefined. Positive values set the focus + closer to the camera, negative values towards infinity. + +``V4L2_CID_FOCUS_RELATIVE (integer)`` + This control moves the focal point of the camera by the specified + amount. The unit is undefined. Positive values move the focus closer + to the camera, negative values towards infinity. This is a + write-only control. + +``V4L2_CID_FOCUS_AUTO (boolean)`` + Enables continuous automatic focus adjustments. The effect of manual + focus adjustments while this feature is enabled is undefined, + drivers should ignore such requests. + +``V4L2_CID_AUTO_FOCUS_START (button)`` + Starts single auto focus process. The effect of setting this control + when ``V4L2_CID_FOCUS_AUTO`` is set to ``TRUE`` (1) is undefined, + drivers should ignore such requests. + +``V4L2_CID_AUTO_FOCUS_STOP (button)`` + Aborts automatic focusing started with ``V4L2_CID_AUTO_FOCUS_START`` + control. It is effective only when the continuous autofocus is + disabled, that is when ``V4L2_CID_FOCUS_AUTO`` control is set to + ``FALSE`` (0). + +.. _v4l2-auto-focus-status: + +``V4L2_CID_AUTO_FOCUS_STATUS (bitmask)`` + The automatic focus status. This is a read-only control. + + Setting ``V4L2_LOCK_FOCUS`` lock bit of the ``V4L2_CID_3A_LOCK`` + control may stop updates of the ``V4L2_CID_AUTO_FOCUS_STATUS`` + control value. + +.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_AUTO_FOCUS_STATUS_IDLE`` + - Automatic focus is not active. + * - ``V4L2_AUTO_FOCUS_STATUS_BUSY`` + - Automatic focusing is in progress. + * - ``V4L2_AUTO_FOCUS_STATUS_REACHED`` + - Focus has been reached. + * - ``V4L2_AUTO_FOCUS_STATUS_FAILED`` + - Automatic focus has failed, the driver will not transition from + this state until another action is performed by an application. + + + +.. _v4l2-auto-focus-range: + +``V4L2_CID_AUTO_FOCUS_RANGE`` + (enum) + +enum v4l2_auto_focus_range - + Determines auto focus distance range for which lens may be adjusted. + +.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_AUTO_FOCUS_RANGE_AUTO`` + - The camera automatically selects the focus range. + * - ``V4L2_AUTO_FOCUS_RANGE_NORMAL`` + - Normal distance range, limited for best automatic focus + performance. + * - ``V4L2_AUTO_FOCUS_RANGE_MACRO`` + - Macro (close-up) auto focus. The camera will use its minimum + possible distance for auto focus. + * - ``V4L2_AUTO_FOCUS_RANGE_INFINITY`` + - The lens is set to focus on an object at infinite distance. + + + +``V4L2_CID_ZOOM_ABSOLUTE (integer)`` + Specify the objective lens focal length as an absolute value. The + zoom unit is driver-specific and its value should be a positive + integer. + +``V4L2_CID_ZOOM_RELATIVE (integer)`` + Specify the objective lens focal length relatively to the current + value. Positive values move the zoom lens group towards the + telephoto direction, negative values towards the wide-angle + direction. The zoom unit is driver-specific. This is a write-only + control. + +``V4L2_CID_ZOOM_CONTINUOUS (integer)`` + Move the objective lens group at the specified speed until it + reaches physical device limits or until an explicit request to stop + the movement. A positive value moves the zoom lens group towards the + telephoto direction. A value of zero stops the zoom lens group + movement. A negative value moves the zoom lens group towards the + wide-angle direction. The zoom speed unit is driver-specific. + +``V4L2_CID_IRIS_ABSOLUTE (integer)`` + This control sets the camera's aperture to the specified value. The + unit is undefined. Larger values open the iris wider, smaller values + close it. + +``V4L2_CID_IRIS_RELATIVE (integer)`` + This control modifies the camera's aperture by the specified amount. + The unit is undefined. Positive values open the iris one step + further, negative values close it one step further. This is a + write-only control. + +``V4L2_CID_PRIVACY (boolean)`` + Prevent video from being acquired by the camera. When this control + is set to ``TRUE`` (1), no image can be captured by the camera. + Common means to enforce privacy are mechanical obturation of the + sensor and firmware image processing, but the device is not + restricted to these methods. Devices that implement the privacy + control must support read access and may support write access. + +``V4L2_CID_BAND_STOP_FILTER (integer)`` + Switch the band-stop filter of a camera sensor on or off, or specify + its strength. Such band-stop filters can be used, for example, to + filter out the fluorescent light component. + +.. _v4l2-auto-n-preset-white-balance: + +``V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE`` + (enum) + +enum v4l2_auto_n_preset_white_balance - + Sets white balance to automatic, manual or a preset. The presets + determine color temperature of the light as a hint to the camera for + white balance adjustments resulting in most accurate color + representation. The following white balance presets are listed in + order of increasing color temperature. + +.. tabularcolumns:: |p{7.0 cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_WHITE_BALANCE_MANUAL`` + - Manual white balance. + * - ``V4L2_WHITE_BALANCE_AUTO`` + - Automatic white balance adjustments. + * - ``V4L2_WHITE_BALANCE_INCANDESCENT`` + - White balance setting for incandescent (tungsten) lighting. It + generally cools down the colors and corresponds approximately to + 2500...3500 K color temperature range. + * - ``V4L2_WHITE_BALANCE_FLUORESCENT`` + - White balance preset for fluorescent lighting. It corresponds + approximately to 4000...5000 K color temperature. + * - ``V4L2_WHITE_BALANCE_FLUORESCENT_H`` + - With this setting the camera will compensate for fluorescent H + lighting. + * - ``V4L2_WHITE_BALANCE_HORIZON`` + - White balance setting for horizon daylight. It corresponds + approximately to 5000 K color temperature. + * - ``V4L2_WHITE_BALANCE_DAYLIGHT`` + - White balance preset for daylight (with clear sky). It corresponds + approximately to 5000...6500 K color temperature. + * - ``V4L2_WHITE_BALANCE_FLASH`` + - With this setting the camera will compensate for the flash light. + It slightly warms up the colors and corresponds roughly to + 5000...5500 K color temperature. + * - ``V4L2_WHITE_BALANCE_CLOUDY`` + - White balance preset for moderately overcast sky. This option + corresponds approximately to 6500...8000 K color temperature + range. + * - ``V4L2_WHITE_BALANCE_SHADE`` + - White balance preset for shade or heavily overcast sky. It + corresponds approximately to 9000...10000 K color temperature. + + + +.. _v4l2-wide-dynamic-range: + +``V4L2_CID_WIDE_DYNAMIC_RANGE (boolean)`` + Enables or disables the camera's wide dynamic range feature. This + feature allows to obtain clear images in situations where intensity + of the illumination varies significantly throughout the scene, i.e. + there are simultaneously very dark and very bright areas. It is most + commonly realized in cameras by combining two subsequent frames with + different exposure times. [#f1]_ + +.. _v4l2-image-stabilization: + +``V4L2_CID_IMAGE_STABILIZATION (boolean)`` + Enables or disables image stabilization. + +``V4L2_CID_ISO_SENSITIVITY (integer menu)`` + Determines ISO equivalent of an image sensor indicating the sensor's + sensitivity to light. The numbers are expressed in arithmetic scale, + as per :ref:`iso12232` standard, where doubling the sensor + sensitivity is represented by doubling the numerical ISO value. + Applications should interpret the values as standard ISO values + multiplied by 1000, e.g. control value 800 stands for ISO 0.8. + Drivers will usually support only a subset of standard ISO values. + The effect of setting this control while the + ``V4L2_CID_ISO_SENSITIVITY_AUTO`` control is set to a value other + than ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` is undefined, drivers + should ignore such requests. + +.. _v4l2-iso-sensitivity-auto-type: + +``V4L2_CID_ISO_SENSITIVITY_AUTO`` + (enum) + +enum v4l2_iso_sensitivity_type - + Enables or disables automatic ISO sensitivity adjustments. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` + - Manual ISO sensitivity. + * - ``V4L2_CID_ISO_SENSITIVITY_AUTO`` + - Automatic ISO sensitivity adjustments. + + + +.. _v4l2-scene-mode: + +``V4L2_CID_SCENE_MODE`` + (enum) + +enum v4l2_scene_mode - + This control allows to select scene programs as the camera automatic + modes optimized for common shooting scenes. Within these modes the + camera determines best exposure, aperture, focusing, light metering, + white balance and equivalent sensitivity. The controls of those + parameters are influenced by the scene mode control. An exact + behavior in each mode is subject to the camera specification. + + When the scene mode feature is not used, this control should be set + to ``V4L2_SCENE_MODE_NONE`` to make sure the other possibly related + controls are accessible. The following scene programs are defined: + +.. tabularcolumns:: |p{6.0cm}|p{11.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_SCENE_MODE_NONE`` + - The scene mode feature is disabled. + * - ``V4L2_SCENE_MODE_BACKLIGHT`` + - Backlight. Compensates for dark shadows when light is coming from + behind a subject, also by automatically turning on the flash. + * - ``V4L2_SCENE_MODE_BEACH_SNOW`` + - Beach and snow. This mode compensates for all-white or bright + scenes, which tend to look gray and low contrast, when camera's + automatic exposure is based on an average scene brightness. To + compensate, this mode automatically slightly overexposes the + frames. The white balance may also be adjusted to compensate for + the fact that reflected snow looks bluish rather than white. + * - ``V4L2_SCENE_MODE_CANDLELIGHT`` + - Candle light. The camera generally raises the ISO sensitivity and + lowers the shutter speed. This mode compensates for relatively + close subject in the scene. The flash is disabled in order to + preserve the ambiance of the light. + * - ``V4L2_SCENE_MODE_DAWN_DUSK`` + - Dawn and dusk. Preserves the colors seen in low natural light + before dusk and after down. The camera may turn off the flash, and + automatically focus at infinity. It will usually boost saturation + and lower the shutter speed. + * - ``V4L2_SCENE_MODE_FALL_COLORS`` + - Fall colors. Increases saturation and adjusts white balance for + color enhancement. Pictures of autumn leaves get saturated reds + and yellows. + * - ``V4L2_SCENE_MODE_FIREWORKS`` + - Fireworks. Long exposure times are used to capture the expanding + burst of light from a firework. The camera may invoke image + stabilization. + * - ``V4L2_SCENE_MODE_LANDSCAPE`` + - Landscape. The camera may choose a small aperture to provide deep + depth of field and long exposure duration to help capture detail + in dim light conditions. The focus is fixed at infinity. Suitable + for distant and wide scenery. + * - ``V4L2_SCENE_MODE_NIGHT`` + - Night, also known as Night Landscape. Designed for low light + conditions, it preserves detail in the dark areas without blowing + out bright objects. The camera generally sets itself to a + medium-to-high ISO sensitivity, with a relatively long exposure + time, and turns flash off. As such, there will be increased image + noise and the possibility of blurred image. + * - ``V4L2_SCENE_MODE_PARTY_INDOOR`` + - Party and indoor. Designed to capture indoor scenes that are lit + by indoor background lighting as well as the flash. The camera + usually increases ISO sensitivity, and adjusts exposure for the + low light conditions. + * - ``V4L2_SCENE_MODE_PORTRAIT`` + - Portrait. The camera adjusts the aperture so that the depth of + field is reduced, which helps to isolate the subject against a + smooth background. Most cameras recognize the presence of faces in + the scene and focus on them. The color hue is adjusted to enhance + skin tones. The intensity of the flash is often reduced. + * - ``V4L2_SCENE_MODE_SPORTS`` + - Sports. Significantly increases ISO and uses a fast shutter speed + to freeze motion of rapidly-moving subjects. Increased image noise + may be seen in this mode. + * - ``V4L2_SCENE_MODE_SUNSET`` + - Sunset. Preserves deep hues seen in sunsets and sunrises. It bumps + up the saturation. + * - ``V4L2_SCENE_MODE_TEXT`` + - Text. It applies extra contrast and sharpness, it is typically a + black-and-white mode optimized for readability. Automatic focus + may be switched to close-up mode and this setting may also involve + some lens-distortion correction. + + + +``V4L2_CID_3A_LOCK (bitmask)`` + This control locks or unlocks the automatic focus, exposure and + white balance. The automatic adjustments can be paused independently + by setting the corresponding lock bit to 1. The camera then retains + the settings until the lock bit is cleared. The following lock bits + are defined: + + When a given algorithm is not enabled, drivers should ignore + requests to lock it and should return no error. An example might be + an application setting bit ``V4L2_LOCK_WHITE_BALANCE`` when the + ``V4L2_CID_AUTO_WHITE_BALANCE`` control is set to ``FALSE``. The + value of this control may be changed by exposure, white balance or + focus controls. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_LOCK_EXPOSURE`` + - Automatic exposure adjustments lock. + * - ``V4L2_LOCK_WHITE_BALANCE`` + - Automatic white balance adjustments lock. + * - ``V4L2_LOCK_FOCUS`` + - Automatic focus lock. + + + +``V4L2_CID_PAN_SPEED (integer)`` + This control turns the camera horizontally at the specific speed. + The unit is undefined. A positive value moves the camera to the + right (clockwise when viewed from above), a negative value to the + left. A value of zero stops the motion if one is in progress and has + no effect otherwise. + +``V4L2_CID_TILT_SPEED (integer)`` + This control turns the camera vertically at the specified speed. The + unit is undefined. A positive value moves the camera up, a negative + value down. A value of zero stops the motion if one is in progress + and has no effect otherwise. + +.. [#f1] + This control may be changed to a menu control in the future, if more + options are required. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst new file mode 100644 index 000000000000..54b3797b67dd --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst @@ -0,0 +1,2451 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _mpeg-controls: + +*********************** +Codec Control Reference +*********************** + +Below all controls within the Codec control class are described. First +the generic controls, then controls specific for certain hardware. + +.. note:: + + These controls are applicable to all codecs and not just MPEG. The + defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG as the controls + were originally made for MPEG codecs and later extended to cover all + encoding formats. + + +Generic Codec Controls +====================== + + +.. _mpeg-control-id: + +Codec Control IDs +----------------- + +``V4L2_CID_MPEG_CLASS (class)`` + The Codec class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. This description can be + used as the caption of a Tab page in a GUI, for example. + +.. _v4l2-mpeg-stream-type: + +``V4L2_CID_MPEG_STREAM_TYPE`` + (enum) + +enum v4l2_mpeg_stream_type - + The MPEG-1, -2 or -4 output stream type. One cannot assume anything + here. Each hardware MPEG encoder tends to support different subsets + of the available MPEG stream types. This control is specific to + multiplexed MPEG streams. The currently defined stream types are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_PS`` + - MPEG-2 program stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_TS`` + - MPEG-2 transport stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_SS`` + - MPEG-1 system stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_DVD`` + - MPEG-2 DVD-compatible stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_VCD`` + - MPEG-1 VCD-compatible stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD`` + - MPEG-2 SVCD-compatible stream + + + +``V4L2_CID_MPEG_STREAM_PID_PMT (integer)`` + Program Map Table Packet ID for the MPEG transport stream (default + 16) + +``V4L2_CID_MPEG_STREAM_PID_AUDIO (integer)`` + Audio Packet ID for the MPEG transport stream (default 256) + +``V4L2_CID_MPEG_STREAM_PID_VIDEO (integer)`` + Video Packet ID for the MPEG transport stream (default 260) + +``V4L2_CID_MPEG_STREAM_PID_PCR (integer)`` + Packet ID for the MPEG transport stream carrying PCR fields (default + 259) + +``V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (integer)`` + Audio ID for MPEG PES + +``V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (integer)`` + Video ID for MPEG PES + +.. _v4l2-mpeg-stream-vbi-fmt: + +``V4L2_CID_MPEG_STREAM_VBI_FMT`` + (enum) + +enum v4l2_mpeg_stream_vbi_fmt - + Some cards can embed VBI data (e. g. Closed Caption, Teletext) into + the MPEG stream. This control selects whether VBI data should be + embedded, and if so, what embedding method should be used. The list + of possible VBI formats depends on the driver. The currently defined + VBI format types are: + + + +.. tabularcolumns:: |p{6 cm}|p{11.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_STREAM_VBI_FMT_NONE`` + - No VBI in the MPEG stream + * - ``V4L2_MPEG_STREAM_VBI_FMT_IVTV`` + - VBI in private packets, IVTV format (documented in the kernel + sources in the file + ``Documentation/media/v4l-drivers/cx2341x.rst``) + + + +.. _v4l2-mpeg-audio-sampling-freq: + +``V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ`` + (enum) + +enum v4l2_mpeg_audio_sampling_freq - + MPEG Audio sampling frequency. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100`` + - 44.1 kHz + * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000`` + - 48 kHz + * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000`` + - 32 kHz + + + +.. _v4l2-mpeg-audio-encoding: + +``V4L2_CID_MPEG_AUDIO_ENCODING`` + (enum) + +enum v4l2_mpeg_audio_encoding - + MPEG Audio encoding. This control is specific to multiplexed MPEG + streams. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_1`` + - MPEG-1/2 Layer I encoding + * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_2`` + - MPEG-1/2 Layer II encoding + * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_3`` + - MPEG-1/2 Layer III encoding + * - ``V4L2_MPEG_AUDIO_ENCODING_AAC`` + - MPEG-2/4 AAC (Advanced Audio Coding) + * - ``V4L2_MPEG_AUDIO_ENCODING_AC3`` + - AC-3 aka ATSC A/52 encoding + + + +.. _v4l2-mpeg-audio-l1-bitrate: + +``V4L2_CID_MPEG_AUDIO_L1_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_l1_bitrate - + MPEG-1/2 Layer I bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_288K`` + - 288 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_320K`` + - 320 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_352K`` + - 352 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_384K`` + - 384 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_416K`` + - 416 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_448K`` + - 448 kbit/s + + + +.. _v4l2-mpeg-audio-l2-bitrate: + +``V4L2_CID_MPEG_AUDIO_L2_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_l2_bitrate - + MPEG-1/2 Layer II bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_48K`` + - 48 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_56K`` + - 56 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_80K`` + - 80 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_112K`` + - 112 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_320K`` + - 320 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_384K`` + - 384 kbit/s + + + +.. _v4l2-mpeg-audio-l3-bitrate: + +``V4L2_CID_MPEG_AUDIO_L3_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_l3_bitrate - + MPEG-1/2 Layer III bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_40K`` + - 40 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_48K`` + - 48 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_56K`` + - 56 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_80K`` + - 80 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_112K`` + - 112 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_320K`` + - 320 kbit/s + + + +``V4L2_CID_MPEG_AUDIO_AAC_BITRATE (integer)`` + AAC bitrate in bits per second. + +.. _v4l2-mpeg-audio-ac3-bitrate: + +``V4L2_CID_MPEG_AUDIO_AC3_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_ac3_bitrate - + AC-3 bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_40K`` + - 40 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_48K`` + - 48 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_56K`` + - 56 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_80K`` + - 80 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_112K`` + - 112 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_320K`` + - 320 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_384K`` + - 384 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_448K`` + - 448 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_512K`` + - 512 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_576K`` + - 576 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_640K`` + - 640 kbit/s + + + +.. _v4l2-mpeg-audio-mode: + +``V4L2_CID_MPEG_AUDIO_MODE`` + (enum) + +enum v4l2_mpeg_audio_mode - + MPEG Audio mode. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_MODE_STEREO`` + - Stereo + * - ``V4L2_MPEG_AUDIO_MODE_JOINT_STEREO`` + - Joint Stereo + * - ``V4L2_MPEG_AUDIO_MODE_DUAL`` + - Bilingual + * - ``V4L2_MPEG_AUDIO_MODE_MONO`` + - Mono + + + +.. _v4l2-mpeg-audio-mode-extension: + +``V4L2_CID_MPEG_AUDIO_MODE_EXTENSION`` + (enum) + +enum v4l2_mpeg_audio_mode_extension - + Joint Stereo audio mode extension. In Layer I and II they indicate + which subbands are in intensity stereo. All other subbands are coded + in stereo. Layer III is not (yet) supported. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4`` + - Subbands 4-31 in intensity stereo + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8`` + - Subbands 8-31 in intensity stereo + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12`` + - Subbands 12-31 in intensity stereo + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16`` + - Subbands 16-31 in intensity stereo + + + +.. _v4l2-mpeg-audio-emphasis: + +``V4L2_CID_MPEG_AUDIO_EMPHASIS`` + (enum) + +enum v4l2_mpeg_audio_emphasis - + Audio Emphasis. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_EMPHASIS_NONE`` + - None + * - ``V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS`` + - 50/15 microsecond emphasis + * - ``V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17`` + - CCITT J.17 + + + +.. _v4l2-mpeg-audio-crc: + +``V4L2_CID_MPEG_AUDIO_CRC`` + (enum) + +enum v4l2_mpeg_audio_crc - + CRC method. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_CRC_NONE`` + - None + * - ``V4L2_MPEG_AUDIO_CRC_CRC16`` + - 16 bit parity check + + + +``V4L2_CID_MPEG_AUDIO_MUTE (boolean)`` + Mutes the audio when capturing. This is not done by muting audio + hardware, which can still produce a slight hiss, but in the encoder + itself, guaranteeing a fixed and reproducible audio bitstream. 0 = + unmuted, 1 = muted. + +.. _v4l2-mpeg-audio-dec-playback: + +``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` + (enum) + +enum v4l2_mpeg_audio_dec_playback - + Determines how monolingual audio should be played back. Possible + values are: + + + +.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO`` + - Automatically determines the best playback mode. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO`` + - Stereo playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT`` + - Left channel playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT`` + - Right channel playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO`` + - Mono playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO`` + - Stereo playback with swapped left and right channels. + + + +.. _v4l2-mpeg-audio-dec-multilingual-playback: + +``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` + (enum) + +enum v4l2_mpeg_audio_dec_playback - + Determines how multilingual audio should be played back. + +.. _v4l2-mpeg-video-encoding: + +``V4L2_CID_MPEG_VIDEO_ENCODING`` + (enum) + +enum v4l2_mpeg_video_encoding - + MPEG Video encoding method. This control is specific to multiplexed + MPEG streams. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_1`` + - MPEG-1 Video encoding + * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_2`` + - MPEG-2 Video encoding + * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC`` + - MPEG-4 AVC (H.264) Video encoding + + + +.. _v4l2-mpeg-video-aspect: + +``V4L2_CID_MPEG_VIDEO_ASPECT`` + (enum) + +enum v4l2_mpeg_video_aspect - + Video aspect. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_ASPECT_1x1`` + * - ``V4L2_MPEG_VIDEO_ASPECT_4x3`` + * - ``V4L2_MPEG_VIDEO_ASPECT_16x9`` + * - ``V4L2_MPEG_VIDEO_ASPECT_221x100`` + + + +``V4L2_CID_MPEG_VIDEO_B_FRAMES (integer)`` + Number of B-Frames (default 2) + +``V4L2_CID_MPEG_VIDEO_GOP_SIZE (integer)`` + GOP size (default 12) + +``V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (boolean)`` + GOP closure (default 1) + +``V4L2_CID_MPEG_VIDEO_PULLDOWN (boolean)`` + Enable 3:2 pulldown (default 0) + +.. _v4l2-mpeg-video-bitrate-mode: + +``V4L2_CID_MPEG_VIDEO_BITRATE_MODE`` + (enum) + +enum v4l2_mpeg_video_bitrate_mode - + Video bitrate mode. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_VBR`` + - Variable bitrate + * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_CBR`` + - Constant bitrate + + + +``V4L2_CID_MPEG_VIDEO_BITRATE (integer)`` + Video bitrate in bits per second. + +``V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (integer)`` + Peak video bitrate in bits per second. Must be larger or equal to + the average video bitrate. It is ignored if the video bitrate mode + is set to constant bitrate. + +``V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (integer)`` + For every captured frame, skip this many subsequent frames (default + 0). + +``V4L2_CID_MPEG_VIDEO_MUTE (boolean)`` + "Mutes" the video to a fixed color when capturing. This is useful + for testing, to produce a fixed video bitstream. 0 = unmuted, 1 = + muted. + +``V4L2_CID_MPEG_VIDEO_MUTE_YUV (integer)`` + Sets the "mute" color of the video. The supplied 32-bit integer is + interpreted as follows (bit 0 = least significant bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:7 + - V chrominance information + * - Bit 8:15 + - U chrominance information + * - Bit 16:23 + - Y luminance information + * - Bit 24:31 + - Must be zero. + + + +.. _v4l2-mpeg-video-dec-pts: + +``V4L2_CID_MPEG_VIDEO_DEC_PTS (integer64)`` + This read-only control returns the 33-bit video Presentation Time + Stamp as defined in ITU T-REC-H.222.0 and ISO/IEC 13818-1 of the + currently displayed frame. This is the same PTS as is used in + :ref:`VIDIOC_DECODER_CMD`. + +.. _v4l2-mpeg-video-dec-frame: + +``V4L2_CID_MPEG_VIDEO_DEC_FRAME (integer64)`` + This read-only control returns the frame counter of the frame that + is currently displayed (decoded). This value is reset to 0 whenever + the decoder is started. + +``V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (boolean)`` + If enabled the decoder expects to receive a single slice per buffer, + otherwise the decoder expects a single frame in per buffer. + Applicable to the decoder, all codecs. + +``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (boolean)`` + Enable writing sample aspect ratio in the Video Usability + Information. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-vui-sar-idc: + +``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` + (enum) + +enum v4l2_mpeg_video_h264_vui_sar_idc - + VUI sample aspect ratio indicator for H.264 encoding. The value is + defined in the table E-1 in the standard. Applicable to the H264 + encoder. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED`` + - Unspecified + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1`` + - 1x1 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11`` + - 12x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11`` + - 10x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11`` + - 16x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33`` + - 40x33 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11`` + - 24x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11`` + - 20x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11`` + - 32x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33`` + - 80x33 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11`` + - 18x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11`` + - 15x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33`` + - 64x33 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99`` + - 160x99 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3`` + - 4x3 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2`` + - 3x2 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1`` + - 2x1 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED`` + - Extended SAR + + + +``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (integer)`` + Extended sample aspect ratio width for H.264 VUI encoding. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (integer)`` + Extended sample aspect ratio height for H.264 VUI encoding. + Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-level: + +``V4L2_CID_MPEG_VIDEO_H264_LEVEL`` + (enum) + +enum v4l2_mpeg_video_h264_level - + The level information for the H264 video elementary stream. + Applicable to the H264 encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_0`` + - Level 1.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1B`` + - Level 1B + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_1`` + - Level 1.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_2`` + - Level 1.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_3`` + - Level 1.3 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_0`` + - Level 2.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_1`` + - Level 2.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_2`` + - Level 2.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_0`` + - Level 3.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_1`` + - Level 3.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_2`` + - Level 3.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_0`` + - Level 4.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_1`` + - Level 4.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_2`` + - Level 4.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_0`` + - Level 5.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_1`` + - Level 5.1 + + + +.. _v4l2-mpeg-video-mpeg4-level: + +``V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL`` + (enum) + +enum v4l2_mpeg_video_mpeg4_level - + The level information for the MPEG4 elementary stream. Applicable to + the MPEG4 encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0`` + - Level 0 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B`` + - Level 0b + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_1`` + - Level 1 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_2`` + - Level 2 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3`` + - Level 3 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B`` + - Level 3b + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_4`` + - Level 4 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_5`` + - Level 5 + + + +.. _v4l2-mpeg-video-h264-profile: + +``V4L2_CID_MPEG_VIDEO_H264_PROFILE`` + (enum) + +enum v4l2_mpeg_video_h264_profile - + The profile information for H264. Applicable to the H264 encoder. + Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE`` + - Baseline profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE`` + - Constrained Baseline profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MAIN`` + - Main profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED`` + - Extended profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH`` + - High profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10`` + - High 10 profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422`` + - High 422 profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE`` + - High 444 Predictive profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA`` + - High 10 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA`` + - High 422 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA`` + - High 444 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA`` + - CAVLC 444 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE`` + - Scalable Baseline profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH`` + - Scalable High profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA`` + - Scalable High Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH`` + - Stereo High profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH`` + - Multiview High profile + + + +.. _v4l2-mpeg-video-mpeg4-profile: + +``V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE`` + (enum) + +enum v4l2_mpeg_video_mpeg4_profile - + The profile information for MPEG4. Applicable to the MPEG4 encoder. + Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE`` + - Simple profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE`` + - Advanced Simple profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE`` + - Core profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE`` + - Simple Scalable profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY`` + - + + + +``V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (integer)`` + The maximum number of reference pictures used for encoding. + Applicable to the encoder. + +.. _v4l2-mpeg-video-multi-slice-mode: + +``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` + (enum) + +enum v4l2_mpeg_video_multi_slice_mode - + Determines how the encoder should handle division of frame into + slices. Applicable to the encoder. Possible values are: + + + +.. tabularcolumns:: |p{8.7cm}|p{8.8cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE`` + - Single slice per frame. + * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB`` + - Multiple slices with set maximum number of macroblocks per slice. + * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES`` + - Multiple slice with set maximum size in bytes per slice. + + + +``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (integer)`` + The maximum number of macroblocks in a slice. Used when + ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to + ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB``. Applicable to the + encoder. + +``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (integer)`` + The maximum size of a slice in bytes. Used when + ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to + ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES``. Applicable to the + encoder. + +.. _v4l2-mpeg-video-h264-loop-filter-mode: + +``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_video_h264_loop_filter_mode - + Loop filter mode for H264 encoder. Possible values are: + + + +.. tabularcolumns:: |p{14.0cm}|p{3.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED`` + - Loop filter is enabled. + * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED`` + - Loop filter is disabled. + * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` + - Loop filter is disabled at the slice boundary. + + + +``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (integer)`` + Loop filter alpha coefficient, defined in the H264 standard. + This value corresponds to the slice_alpha_c0_offset_div2 slice header + field, and should be in the range of -6 to +6, inclusive. The actual alpha + offset FilterOffsetA is twice this value. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (integer)`` + Loop filter beta coefficient, defined in the H264 standard. + This corresponds to the slice_beta_offset_div2 slice header field, and + should be in the range of -6 to +6, inclusive. The actual beta offset + FilterOffsetB is twice this value. + Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-entropy-mode: + +``V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE`` + (enum) + +enum v4l2_mpeg_video_h264_entropy_mode - + Entropy coding mode for H264 - CABAC/CAVALC. Applicable to the H264 + encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC`` + - Use CAVLC entropy coding. + * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC`` + - Use CABAC entropy coding. + + + +``V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (boolean)`` + Enable 8X8 transform for H264. Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (boolean)`` + Enable constrained intra prediction for H264. Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (integer)`` + Specify the offset that should be added to the luma quantization + parameter to determine the chroma quantization parameter. Applicable + to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` + Cyclic intra macroblock refresh. This is the number of continuous + macroblocks refreshed every frame. Each frame a successive set of + macroblocks is refreshed until the cycle completes and starts from + the top of the frame. Applicable to H264, H263 and MPEG4 encoder. + +``V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (boolean)`` + Frame level rate control enable. If this control is disabled then + the quantization parameter for each frame type is constant and set + with appropriate controls (e.g. + ``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP``). If frame rate control is + enabled then quantization parameter is adjusted to meet the chosen + bitrate. Minimum and maximum value for the quantization parameter + can be set with appropriate controls (e.g. + ``V4L2_CID_MPEG_VIDEO_H263_MIN_QP``). Applicable to encoders. + +``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (boolean)`` + Macroblock level rate control enable. Applicable to the MPEG4 and + H264 encoders. + +``V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (boolean)`` + Quarter pixel motion estimation for MPEG4. Applicable to the MPEG4 + encoder. + +``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for H263. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_H263_MIN_QP (integer)`` + Minimum quantization parameter for H263. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_H263_MAX_QP (integer)`` + Maximum quantization parameter for H263. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (integer)`` + Quantization parameter for an P frame for H263. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (integer)`` + Quantization parameter for an B frame for H263. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for H264. Valid range: from 0 + to 51. + +``V4L2_CID_MPEG_VIDEO_H264_MIN_QP (integer)`` + Minimum quantization parameter for H264. Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_H264_MAX_QP (integer)`` + Maximum quantization parameter for H264. Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (integer)`` + Quantization parameter for an P frame for H264. Valid range: from 0 + to 51. + +``V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (integer)`` + Quantization parameter for an B frame for H264. Valid range: from 0 + to 51. + +``V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for MPEG4. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (integer)`` + Minimum quantization parameter for MPEG4. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (integer)`` + Maximum quantization parameter for MPEG4. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (integer)`` + Quantization parameter for an P frame for MPEG4. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (integer)`` + Quantization parameter for an B frame for MPEG4. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_VBV_SIZE (integer)`` + The Video Buffer Verifier size in kilobytes, it is used as a + limitation of frame skip. The VBV is defined in the standard as a + mean to verify that the produced stream will be successfully + decoded. The standard describes it as "Part of a hypothetical + decoder that is conceptually connected to the output of the encoder. + Its purpose is to provide a constraint on the variability of the + data rate that an encoder or editing process may produce.". + Applicable to the MPEG1, MPEG2, MPEG4 encoders. + +.. _v4l2-mpeg-video-vbv-delay: + +``V4L2_CID_MPEG_VIDEO_VBV_DELAY (integer)`` + Sets the initial delay in milliseconds for VBV buffer control. + +.. _v4l2-mpeg-video-hor-search-range: + +``V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (integer)`` + Horizontal search range defines maximum horizontal search area in + pixels to search and match for the present Macroblock (MB) in the + reference picture. This V4L2 control macro is used to set horizontal + search range for motion estimation module in video encoder. + +.. _v4l2-mpeg-video-vert-search-range: + +``V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (integer)`` + Vertical search range defines maximum vertical search area in pixels + to search and match for the present Macroblock (MB) in the reference + picture. This V4L2 control macro is used to set vertical search + range for motion estimation module in video encoder. + +.. _v4l2-mpeg-video-force-key-frame: + +``V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (button)`` + Force a key frame for the next queued buffer. Applicable to + encoders. This is a general, codec-agnostic keyframe control. + +``V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (integer)`` + The Coded Picture Buffer size in kilobytes, it is used as a + limitation of frame skip. The CPB is defined in the H264 standard as + a mean to verify that the produced stream will be successfully + decoded. Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (integer)`` + Period between I-frames in the open GOP for H264. In case of an open + GOP this is the period between two I-frames. The period between IDR + (Instantaneous Decoding Refresh) frames is taken from the GOP_SIZE + control. An IDR frame, which stands for Instantaneous Decoding + Refresh is an I-frame after which no prior frames are referenced. + This means that a stream can be restarted from an IDR frame without + the need to store or decode any previous frames. Applicable to the + H264 encoder. + +.. _v4l2-mpeg-video-header-mode: + +``V4L2_CID_MPEG_VIDEO_HEADER_MODE`` + (enum) + +enum v4l2_mpeg_video_header_mode - + Determines whether the header is returned as the first buffer or is + it returned together with the first frame. Applicable to encoders. + Possible values are: + + + +.. tabularcolumns:: |p{10.3cm}|p{7.2cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE`` + - The stream header is returned separately in the first buffer. + * - ``V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME`` + - The stream header is returned together with the first encoded + frame. + + + +``V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (boolean)`` + Repeat the video sequence headers. Repeating these headers makes + random access to the video stream easier. Applicable to the MPEG1, 2 + and 4 encoder. + +``V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (boolean)`` + Enabled the deblocking post processing filter for MPEG4 decoder. + Applicable to the MPEG4 decoder. + +``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_RES (integer)`` + vop_time_increment_resolution value for MPEG4. Applicable to the + MPEG4 encoder. + +``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_INC (integer)`` + vop_time_increment value for MPEG4. Applicable to the MPEG4 + encoder. + +``V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING (boolean)`` + Enable generation of frame packing supplemental enhancement + information in the encoded bitstream. The frame packing SEI message + contains the arrangement of L and R planes for 3D viewing. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0 (boolean)`` + Sets current frame as frame0 in frame packing SEI. Applicable to the + H264 encoder. + +.. _v4l2-mpeg-video-h264-sei-fp-arrangement-type: + +``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE`` + (enum) + +enum v4l2_mpeg_video_h264_sei_fp_arrangement_type - + Frame packing arrangement type for H264 SEI. Applicable to the H264 + encoder. Possible values are: + +.. tabularcolumns:: |p{12cm}|p{5.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_CHEKERBOARD`` + - Pixels are alternatively from L and R. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_COLUMN`` + - L and R are interlaced by column. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_ROW`` + - L and R are interlaced by row. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE`` + - L is on the left, R on the right. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM`` + - L is on top, R on bottom. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL`` + - One view per frame. + + + +``V4L2_CID_MPEG_VIDEO_H264_FMO (boolean)`` + Enables flexible macroblock ordering in the encoded bitstream. It is + a technique used for restructuring the ordering of macroblocks in + pictures. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-fmo-map-type: + +``V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE`` + (enum) + +enum v4l2_mpeg_video_h264_fmo_map_type - + When using FMO, the map type divides the image in different scan + patterns of macroblocks. Applicable to the H264 encoder. Possible + values are: + +.. tabularcolumns:: |p{12.5cm}|p{5.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES`` + - Slices are interleaved one after other with macroblocks in run + length order. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES`` + - Scatters the macroblocks based on a mathematical function known to + both encoder and decoder. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_FOREGROUND_WITH_LEFT_OVER`` + - Macroblocks arranged in rectangular areas or regions of interest. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_BOX_OUT`` + - Slice groups grow in a cyclic way from centre to outwards. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN`` + - Slice groups grow in raster scan pattern from left to right. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN`` + - Slice groups grow in wipe scan pattern from top to bottom. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_EXPLICIT`` + - User defined map type. + + + +``V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP (integer)`` + Number of slice groups in FMO. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-fmo-change-direction: + +``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION`` + (enum) + +enum v4l2_mpeg_video_h264_fmo_change_dir - + Specifies a direction of the slice group change for raster and wipe + maps. Applicable to the H264 encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT`` + - Raster scan or wipe right. + * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT`` + - Reverse raster scan or wipe left. + + + +``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE (integer)`` + Specifies the size of the first slice group for raster and wipe map. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH (integer)`` + Specifies the number of consecutive macroblocks for the interleaved + map. Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_ASO (boolean)`` + Enables arbitrary slice ordering in encoded bitstream. Applicable to + the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER (integer)`` + Specifies the slice order in ASO. Applicable to the H264 encoder. + The supplied 32-bit integer is interpreted as follows (bit 0 = least + significant bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:15 + - Slice ID + * - Bit 16:32 + - Slice position or order + + + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING (boolean)`` + Enables H264 hierarchical coding. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-hierarchical-coding-type: + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE`` + (enum) + +enum v4l2_mpeg_video_h264_hierarchical_coding_type - + Specifies the hierarchical coding type. Applicable to the H264 + encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B`` + - Hierarchical B coding. + * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P`` + - Hierarchical P coding. + + + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (integer)`` + Specifies the number of hierarchical coding layers. Applicable to + the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (integer)`` + Specifies a user defined QP for each layer. Applicable to the H264 + encoder. The supplied 32-bit integer is interpreted as follows (bit + 0 = least significant bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:15 + - QP value + * - Bit 16:32 + - Layer number + + + +.. _v4l2-mpeg-mpeg2: + +``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (struct)`` + Specifies the slice parameters (as extracted from the bitstream) for the + associated MPEG-2 slice data. This includes the necessary parameters for + configuring a stateless hardware decoding pipeline for MPEG-2. + The bitstream parameters are defined according to :ref:`mpeg2part2`. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_mpeg2_slice_params + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_mpeg2_slice_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``bit_size`` + - Size (in bits) of the current slice data. + * - __u32 + - ``data_bit_offset`` + - Offset (in bits) to the video data in the current slice data. + * - struct :c:type:`v4l2_mpeg2_sequence` + - ``sequence`` + - Structure with MPEG-2 sequence metadata, merging relevant fields from + the sequence header and sequence extension parts of the bitstream. + * - struct :c:type:`v4l2_mpeg2_picture` + - ``picture`` + - Structure with MPEG-2 picture metadata, merging relevant fields from + the picture header and picture coding extension parts of the bitstream. + * - __u64 + - ``backward_ref_ts`` + - Timestamp of the V4L2 capture buffer to use as backward reference, used + with B-coded and P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u64 + - ``forward_ref_ts`` + - Timestamp for the V4L2 capture buffer to use as forward reference, used + with B-coded frames. The timestamp refers to the ``timestamp`` field in + struct :c:type:`v4l2_buffer`. Use the :c:func:`v4l2_timeval_to_ns()` + function to convert the struct :c:type:`timeval` in struct + :c:type:`v4l2_buffer` to a __u64. + * - __u32 + - ``quantiser_scale_code`` + - Code used to determine the quantization scale to use for the IDCT. + +.. c:type:: v4l2_mpeg2_sequence + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_mpeg2_sequence + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u16 + - ``horizontal_size`` + - The width of the displayable part of the frame's luminance component. + * - __u16 + - ``vertical_size`` + - The height of the displayable part of the frame's luminance component. + * - __u32 + - ``vbv_buffer_size`` + - Used to calculate the required size of the video buffering verifier, + defined (in bits) as: 16 * 1024 * vbv_buffer_size. + * - __u16 + - ``profile_and_level_indication`` + - The current profile and level indication as extracted from the + bitstream. + * - __u8 + - ``progressive_sequence`` + - Indication that all the frames for the sequence are progressive instead + of interlaced. + * - __u8 + - ``chroma_format`` + - The chrominance sub-sampling format (1: 4:2:0, 2: 4:2:2, 3: 4:4:4). + +.. c:type:: v4l2_mpeg2_picture + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_mpeg2_picture + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``picture_coding_type`` + - Picture coding type for the frame covered by the current slice + (V4L2_MPEG2_PICTURE_CODING_TYPE_I, V4L2_MPEG2_PICTURE_CODING_TYPE_P or + V4L2_MPEG2_PICTURE_CODING_TYPE_B). + * - __u8 + - ``f_code[2][2]`` + - Motion vector codes. + * - __u8 + - ``intra_dc_precision`` + - Precision of Discrete Cosine transform (0: 8 bits precision, + 1: 9 bits precision, 2: 10 bits precision, 3: 11 bits precision). + * - __u8 + - ``picture_structure`` + - Picture structure (1: interlaced top field, 2: interlaced bottom field, + 3: progressive frame). + * - __u8 + - ``top_field_first`` + - If set to 1 and interlaced stream, top field is output first. + * - __u8 + - ``frame_pred_frame_dct`` + - If set to 1, only frame-DCT and frame prediction are used. + * - __u8 + - ``concealment_motion_vectors`` + - If set to 1, motion vectors are coded for intra macroblocks. + * - __u8 + - ``q_scale_type`` + - This flag affects the inverse quantization process. + * - __u8 + - ``intra_vlc_format`` + - This flag affects the decoding of transform coefficient data. + * - __u8 + - ``alternate_scan`` + - This flag affects the decoding of transform coefficient data. + * - __u8 + - ``repeat_first_field`` + - This flag affects the decoding process of progressive frames. + * - __u16 + - ``progressive_frame`` + - Indicates whether the current frame is progressive. + +``V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION (struct)`` + Specifies quantization matrices (as extracted from the bitstream) for the + associated MPEG-2 slice data. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_mpeg2_quantization + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_mpeg2_quantization + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``load_intra_quantiser_matrix`` + - One bit to indicate whether to load the ``intra_quantiser_matrix`` data. + * - __u8 + - ``load_non_intra_quantiser_matrix`` + - One bit to indicate whether to load the ``non_intra_quantiser_matrix`` + data. + * - __u8 + - ``load_chroma_intra_quantiser_matrix`` + - One bit to indicate whether to load the + ``chroma_intra_quantiser_matrix`` data, only relevant for non-4:2:0 YUV + formats. + * - __u8 + - ``load_chroma_non_intra_quantiser_matrix`` + - One bit to indicate whether to load the + ``chroma_non_intra_quantiser_matrix`` data, only relevant for non-4:2:0 + YUV formats. + * - __u8 + - ``intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for intra-coded frames, in zigzag + scanning order. It is relevant for both luma and chroma components, + although it can be superseded by the chroma-specific matrix for + non-4:2:0 YUV formats. + * - __u8 + - ``non_intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for non-intra-coded frames, in + zigzag scanning order. It is relevant for both luma and chroma + components, although it can be superseded by the chroma-specific matrix + for non-4:2:0 YUV formats. + * - __u8 + - ``chroma_intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for the chominance component of + intra-coded frames, in zigzag scanning order. Only relevant for + non-4:2:0 YUV formats. + * - __u8 + - ``chroma_non_intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for the chrominance component of + non-intra-coded frames, in zigzag scanning order. Only relevant for + non-4:2:0 YUV formats. + +MFC 5.1 MPEG Controls +===================== + +The following MPEG class controls deal with MPEG decoding and encoding +settings that are specific to the Multi Format Codec 5.1 device present +in the S5P family of SoCs by Samsung. + + +.. _mfc51-control-id: + +MFC 5.1 Control IDs +------------------- + +``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (boolean)`` + If the display delay is enabled then the decoder is forced to return + a CAPTURE buffer (decoded frame) after processing a certain number + of OUTPUT buffers. The delay can be set through + ``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY``. This + feature can be used for example for generating thumbnails of videos. + Applicable to the H264 decoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (integer)`` + Display delay value for H264 decoder. The decoder is forced to + return a decoded frame after the set 'display delay' number of + frames. If this number is low it may result in frames returned out + of dispaly order, in addition the hardware may still be using the + returned buffer as a reference picture for subsequent frames. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (integer)`` + The number of reference pictures used for encoding a P picture. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_PADDING (boolean)`` + Padding enable in the encoder - use a color instead of repeating + border pixels. Applicable to encoders. + +``V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (integer)`` + Padding color in the encoder. Applicable to encoders. The supplied + 32-bit integer is interpreted as follows (bit 0 = least significant + bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:7 + - V chrominance information + * - Bit 8:15 + - U chrominance information + * - Bit 16:23 + - Y luminance information + * - Bit 24:31 + - Must be zero. + + + +``V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (integer)`` + Reaction coefficient for MFC rate control. Applicable to encoders. + + .. note:: + + #. Valid only when the frame level RC is enabled. + + #. For tight CBR, this field must be small (ex. 2 ~ 10). For + VBR, this field must be large (ex. 100 ~ 1000). + + #. It is not recommended to use the greater number than + FRAME_RATE * (10^9 / BIT_RATE). + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (boolean)`` + Adaptive rate control for dark region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (boolean)`` + Adaptive rate control for smooth region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (boolean)`` + Adaptive rate control for static region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (boolean)`` + Adaptive rate control for activity region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +.. _v4l2-mpeg-mfc51-video-frame-skip-mode: + +``V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE`` + (enum) + +enum v4l2_mpeg_mfc51_video_frame_skip_mode - + Indicates in what conditions the encoder should skip frames. If + encoding a frame would cause the encoded stream to be larger then a + chosen data limit then the frame will be skipped. Possible values + are: + + +.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_DISABLED`` + - Frame skip mode is disabled. + * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_LEVEL_LIMIT`` + - Frame skip mode enabled and buffer limit is set by the chosen + level and is defined by the standard. + * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_BUF_LIMIT`` + - Frame skip mode enabled and buffer limit is set by the VBV + (MPEG1/2/4) or CPB (H264) buffer size control. + + + +``V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (integer)`` + Enable rate-control with fixed target bit. If this setting is + enabled, then the rate control logic of the encoder will calculate + the average bitrate for a GOP and keep it below or equal the set + bitrate target. Otherwise the rate control logic calculates the + overall average bitrate for the stream and keeps it below or equal + to the set bitrate. In the first case the average bitrate for the + whole stream will be smaller then the set bitrate. This is caused + because the average is calculated for smaller number of frames, on + the other hand enabling this setting will ensure that the stream + will meet tight bandwidth constraints. Applicable to encoders. + +.. _v4l2-mpeg-mfc51-video-force-frame-type: + +``V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE`` + (enum) + +enum v4l2_mpeg_mfc51_video_force_frame_type - + Force a frame type for the next queued buffer. Applicable to + encoders. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_DISABLED`` + - Forcing a specific frame type disabled. + * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_I_FRAME`` + - Force an I-frame. + * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_NOT_CODED`` + - Force a non-coded frame. + + + + +CX2341x MPEG Controls +===================== + +The following MPEG class controls deal with MPEG encoding settings that +are specific to the Conexant CX23415 and CX23416 MPEG encoding chips. + + +.. _cx2341x-control-id: + +CX2341x Control IDs +------------------- + +.. _v4l2-mpeg-cx2341x-video-spatial-filter-mode: + +``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_spatial_filter_mode - + Sets the Spatial Filter mode (default ``MANUAL``). Possible values + are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL`` + - Choose the filter manually + * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO`` + - Choose the filter automatically + + + +``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (integer (0-15))`` + The setting for the Spatial Filter. 0 = off, 15 = maximum. (Default + is 0.) + +.. _luma-spatial-filter-type: + +``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type - + Select the algorithm to use for the Luma Spatial Filter (default + ``1D_HOR``). Possible values: + + + +.. tabularcolumns:: |p{14.5cm}|p{3.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF`` + - No filter + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR`` + - One-dimensional horizontal + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT`` + - One-dimensional vertical + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE`` + - Two-dimensional separable + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE`` + - Two-dimensional symmetrical non-separable + + + +.. _chroma-spatial-filter-type: + +``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type - + Select the algorithm for the Chroma Spatial Filter (default + ``1D_HOR``). Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF`` + - No filter + * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR`` + - One-dimensional horizontal + + + +.. _v4l2-mpeg-cx2341x-video-temporal-filter-mode: + +``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_temporal_filter_mode - + Sets the Temporal Filter mode (default ``MANUAL``). Possible values + are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL`` + - Choose the filter manually + * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO`` + - Choose the filter automatically + + + +``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (integer (0-31))`` + The setting for the Temporal Filter. 0 = off, 31 = maximum. (Default + is 8 for full-scale capturing and 0 for scaled capturing.) + +.. _v4l2-mpeg-cx2341x-video-median-filter-type: + +``V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_median_filter_type - + Median Filter Type (default ``OFF``). Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF`` + - No filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR`` + - Horizontal filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT`` + - Vertical filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT`` + - Horizontal and vertical filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG`` + - Diagonal filter + + + +``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` + Threshold above which the luminance median filter is enabled + (default 0) + +``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (integer (0-255))`` + Threshold below which the luminance median filter is enabled + (default 255) + +``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` + Threshold above which the chroma median filter is enabled (default + 0) + +``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (integer (0-255))`` + Threshold below which the chroma median filter is enabled (default + 255) + +``V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (boolean)`` + The CX2341X MPEG encoder can insert one empty MPEG-2 PES packet into + the stream between every four video frames. The packet size is 2048 + bytes, including the packet_start_code_prefix and stream_id + fields. The stream_id is 0xBF (private stream 2). The payload + consists of 0x00 bytes, to be filled in by the application. 0 = do + not insert, 1 = insert packets. + + +VPX Control Reference +===================== + +The VPX controls include controls for encoding parameters of VPx video +codec. + + +.. _vpx-control-id: + +VPX Control IDs +--------------- + +.. _v4l2-vpx-num-partitions: + +``V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS`` + (enum) + +enum v4l2_vp8_num_partitions - + The number of token partitions to use in VP8 encoder. Possible + values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION`` + - 1 coefficient partition + * - ``V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS`` + - 2 coefficient partitions + * - ``V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS`` + - 4 coefficient partitions + * - ``V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS`` + - 8 coefficient partitions + + + +``V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (boolean)`` + Setting this prevents intra 4x4 mode in the intra mode decision. + +.. _v4l2-vpx-num-ref-frames: + +``V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES`` + (enum) + +enum v4l2_vp8_num_ref_frames - + The number of reference pictures for encoding P frames. Possible + values are: + +.. tabularcolumns:: |p{7.9cm}|p{9.6cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME`` + - Last encoded frame will be searched + * - ``V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME`` + - Two frames will be searched among the last encoded frame, the + golden frame and the alternate reference (altref) frame. The + encoder implementation will decide which two are chosen. + * - ``V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME`` + - The last encoded frame, the golden frame and the altref frame will + be searched. + + + +``V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (integer)`` + Indicates the loop filter level. The adjustment of the loop filter + level is done via a delta value against a baseline loop filter + value. + +``V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (integer)`` + This parameter affects the loop filter. Anything above zero weakens + the deblocking effect on the loop filter. + +``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (integer)`` + Sets the refresh period for the golden frame. The period is defined + in number of frames. For a value of 'n', every nth frame starting + from the first key frame will be taken as a golden frame. For eg. + for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden + frame refresh period is set as 4, the frames 0, 4, 8 etc will be + taken as the golden frames as frame 0 is always a key frame. + +.. _v4l2-vpx-golden-frame-sel: + +``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL`` + (enum) + +enum v4l2_vp8_golden_frame_sel - + Selects the golden frame for encoding. Possible values are: + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV`` + - Use the (n-2)th frame as a golden frame, current frame index being + 'n'. + * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD`` + - Use the previous specific frame indicated by + ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD`` as a + golden frame. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (integer)`` + Minimum quantization parameter for VP8. + +``V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (integer)`` + Maximum quantization parameter for VP8. + +``V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for VP8. + +``V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (integer)`` + Quantization parameter for a P frame for VP8. + +.. _v4l2-mpeg-video-vp8-profile: + +``V4L2_CID_MPEG_VIDEO_VP8_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp8_profile - + This control allows selecting the profile for VP8 encoder. + This is also used to enumerate supported profiles by VP8 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` + - Profile 3 + +.. _v4l2-mpeg-video-vp9-profile: + +``V4L2_CID_MPEG_VIDEO_VP9_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp9_profile - + This control allows selecting the profile for VP9 encoder. + This is also used to enumerate supported profiles by VP9 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_3`` + - Profile 3 + + +High Efficiency Video Coding (HEVC/H.265) Control Reference +=========================================================== + +The HEVC/H.265 controls include controls for encoding parameters of HEVC/H.265 +video codec. + + +.. _hevc-control-id: + +HEVC/H.265 Control IDs +---------------------- + +``V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (integer)`` + Minimum quantization parameter for HEVC. + Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (integer)`` + Maximum quantization parameter for HEVC. + Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for HEVC. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (integer)`` + Quantization parameter for a P frame for HEVC. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (integer)`` + Quantization parameter for a B frame for HEVC. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (boolean)`` + HIERARCHICAL_QP allows the host to specify the quantization parameter + values for each temporal layer through HIERARCHICAL_QP_LAYER. This is + valid only if HIERARCHICAL_CODING_LAYER is greater than 1. Setting the + control value to 1 enables setting of the QP values for the layers. + +.. _v4l2-hevc-hier-coding-type: + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE`` + (enum) + +enum v4l2_mpeg_video_hevc_hier_coding_type - + Selects the hierarchical coding type for encoding. Possible values are: + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B`` + - Use the B frame for hierarchical coding. + * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P`` + - Use the P frame for hierarchical coding. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (integer)`` + Selects the hierarchical coding layer. In normal encoding + (non-hierarchial coding), it should be zero. Possible values are [0, 6]. + 0 indicates HIERARCHICAL CODING LAYER 0, 1 indicates HIERARCHICAL CODING + LAYER 1 and so on. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 0. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 1. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 2. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 3. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 4. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 5. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 6. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +.. _v4l2-hevc-profile: + +``V4L2_CID_MPEG_VIDEO_HEVC_PROFILE`` + (enum) + +enum v4l2_mpeg_video_hevc_profile - + Select the desired profile for HEVC encoder. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN`` + - Main profile. + * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE`` + - Main still picture profile. + * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10`` + - Main 10 profile. + +.. raw:: latex + + \normalsize + + +.. _v4l2-hevc-level: + +``V4L2_CID_MPEG_VIDEO_HEVC_LEVEL`` + (enum) + +enum v4l2_mpeg_video_hevc_level - + Selects the desired level for HEVC encoder. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_1`` + - Level 1.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2`` + - Level 2.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1`` + - Level 2.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3`` + - Level 3.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1`` + - Level 3.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4`` + - Level 4.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1`` + - Level 4.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5`` + - Level 5.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1`` + - Level 5.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2`` + - Level 5.2 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6`` + - Level 6.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1`` + - Level 6.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2`` + - Level 6.2 + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (integer)`` + Indicates the number of evenly spaced subintervals, called ticks, within + one second. This is a 16 bit unsigned integer and has a maximum value up to + 0xffff and a minimum value of 1. + +.. _v4l2-hevc-tier: + +``V4L2_CID_MPEG_VIDEO_HEVC_TIER`` + (enum) + +enum v4l2_mpeg_video_hevc_tier - + TIER_FLAG specifies tiers information of the HEVC encoded picture. Tier + were made to deal with applications that differ in terms of maximum bit + rate. Setting the flag to 0 selects HEVC tier as Main tier and setting + this flag to 1 indicates High tier. High tier is for applications requiring + high bit rates. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_TIER_MAIN`` + - Main tier. + * - ``V4L2_MPEG_VIDEO_HEVC_TIER_HIGH`` + - High tier. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (integer)`` + Selects HEVC maximum coding unit depth. + +.. _v4l2-hevc-loop-filter-mode: + +``V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_video_hevc_loop_filter_mode - + Loop filter mode for HEVC encoder. Possible values are: + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{10.7cm}|p{6.3cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED`` + - Loop filter is disabled. + * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED`` + - Loop filter is enabled. + * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` + - Loop filter is disabled at the slice boundary. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (integer)`` + Selects HEVC loop filter beta offset. The valid range is [-6, +6]. + +``V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (integer)`` + Selects HEVC loop filter tc offset. The valid range is [-6, +6]. + +.. _v4l2-hevc-refresh-type: + +``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE`` + (enum) + +enum v4l2_mpeg_video_hevc_hier_refresh_type - + Selects refresh type for HEVC encoder. + Host has to specify the period into + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{8.0cm}|p{9.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE`` + - Use the B frame for hierarchical coding. + * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA`` + - Use CRA (Clean Random Access Unit) picture encoding. + * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR`` + - Use IDR (Instantaneous Decoding Refresh) picture encoding. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (integer)`` + Selects the refresh period for HEVC encoder. + This specifies the number of I pictures between two CRA/IDR pictures. + This is valid only if REFRESH_TYPE is not 0. + +``V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (boolean)`` + Indicates HEVC lossless encoding. Setting it to 0 disables lossless + encoding. Setting it to 1 enables lossless encoding. + +``V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (boolean)`` + Indicates constant intra prediction for HEVC encoder. Specifies the + constrained intra prediction in which intra largest coding unit (LCU) + prediction is performed by using residual data and decoded samples of + neighboring intra LCU only. Setting the value to 1 enables constant intra + prediction and setting the value to 0 disables constant intra prediction. + +``V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (boolean)`` + Indicates wavefront parallel processing for HEVC encoder. Setting it to 0 + disables the feature and setting it to 1 enables the wavefront parallel + processing. + +``V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (boolean)`` + Setting the value to 1 enables combination of P and B frame for HEVC + encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (boolean)`` + Indicates temporal identifier for HEVC encoder which is enabled by + setting the value to 1. + +``V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (boolean)`` + Indicates bi-linear interpolation is conditionally used in the intra + prediction filtering process in the CVS when set to 1. Indicates bi-linear + interpolation is not used in the CVS when set to 0. + +``V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (integer)`` + Indicates maximum number of merge candidate motion vectors. + Values are from 0 to 4. + +``V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (boolean)`` + Indicates temporal motion vector prediction for HEVC encoder. Setting it to + 1 enables the prediction. Setting it to 0 disables the prediction. + +``V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (boolean)`` + Specifies if HEVC generates a stream with a size of the length field + instead of start code pattern. The size of the length field is configurable + through the V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD control. Setting + the value to 0 disables encoding without startcode pattern. Setting the + value to 1 will enables encoding without startcode pattern. + +.. _v4l2-hevc-size-of-length-field: + +``V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD`` +(enum) + +enum v4l2_mpeg_video_hevc_size_of_length_field - + Indicates the size of length field. + This is valid when encoding WITHOUT_STARTCODE_ENABLE is enabled. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{6.0cm}|p{11.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_0`` + - Generate start code pattern (Normal). + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_1`` + - Generate size of length field instead of start code pattern and length is 1. + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_2`` + - Generate size of length field instead of start code pattern and length is 2. + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_4`` + - Generate size of length field instead of start code pattern and length is 4. + +.. raw:: latex + + \normalsize + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (integer)`` + Indicates bit rate for hierarchical coding layer 0 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (integer)`` + Indicates bit rate for hierarchical coding layer 1 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (integer)`` + Indicates bit rate for hierarchical coding layer 2 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (integer)`` + Indicates bit rate for hierarchical coding layer 3 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (integer)`` + Indicates bit rate for hierarchical coding layer 4 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (integer)`` + Indicates bit rate for hierarchical coding layer 5 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (integer)`` + Indicates bit rate for hierarchical coding layer 6 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (integer)`` + Selects number of P reference pictures required for HEVC encoder. + P-Frame can use 1 or 2 frames for reference. + +``V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (integer)`` + Indicates whether to generate SPS and PPS at every IDR. Setting it to 0 + disables generating SPS and PPS at every IDR. Setting it to one enables + generating SPS and PPS at every IDR. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-detect.rst b/Documentation/media/uapi/v4l/ext-ctrls-detect.rst new file mode 100644 index 000000000000..8a45ce642829 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-detect.rst @@ -0,0 +1,71 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _detect-controls: + +************************ +Detect Control Reference +************************ + +The Detect class includes controls for common features of various motion +or object detection capable devices. + + +.. _detect-control-id: + +Detect Control IDs +================== + +``V4L2_CID_DETECT_CLASS (class)`` + The Detect class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_DETECT_MD_MODE (menu)`` + Sets the motion detection mode. + +.. tabularcolumns:: |p{7.5cm}|p{10.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_DETECT_MD_MODE_DISABLED`` + - Disable motion detection. + * - ``V4L2_DETECT_MD_MODE_GLOBAL`` + - Use a single motion detection threshold. + * - ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` + - The image is divided into a grid, each cell with its own motion + detection threshold. These thresholds are set through the + ``V4L2_CID_DETECT_MD_THRESHOLD_GRID`` matrix control. + * - ``V4L2_DETECT_MD_MODE_REGION_GRID`` + - The image is divided into a grid, each cell with its own region + value that specifies which per-region motion detection thresholds + should be used. Each region has its own thresholds. How these + per-region thresholds are set up is driver-specific. The region + values for the grid are set through the + ``V4L2_CID_DETECT_MD_REGION_GRID`` matrix control. + + + +``V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD (integer)`` + Sets the global motion detection threshold to be used with the + ``V4L2_DETECT_MD_MODE_GLOBAL`` motion detection mode. + +``V4L2_CID_DETECT_MD_THRESHOLD_GRID (__u16 matrix)`` + Sets the motion detection thresholds for each cell in the grid. To + be used with the ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` motion + detection mode. Matrix element (0, 0) represents the cell at the + top-left of the grid. + +``V4L2_CID_DETECT_MD_REGION_GRID (__u8 matrix)`` + Sets the motion detection region value for each cell in the grid. To + be used with the ``V4L2_DETECT_MD_MODE_REGION_GRID`` motion + detection mode. Matrix element (0, 0) represents the cell at the + top-left of the grid. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-dv.rst b/Documentation/media/uapi/v4l/ext-ctrls-dv.rst new file mode 100644 index 000000000000..57edf211875c --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-dv.rst @@ -0,0 +1,166 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _dv-controls: + +******************************* +Digital Video Control Reference +******************************* + +The Digital Video control class is intended to control receivers and +transmitters for `VGA `__, +`DVI `__ +(Digital Visual Interface), HDMI (:ref:`hdmi`) and DisplayPort +(:ref:`dp`). These controls are generally expected to be private to +the receiver or transmitter subdevice that implements them, so they are +only exposed on the ``/dev/v4l-subdev*`` device node. + +.. note:: + + Note that these devices can have multiple input or output pads which are + hooked up to e.g. HDMI connectors. Even though the subdevice will + receive or transmit video from/to only one of those pads, the other pads + can still be active when it comes to EDID (Extended Display + Identification Data, :ref:`vesaedid`) and HDCP (High-bandwidth Digital + Content Protection System, :ref:`hdcp`) processing, allowing the + device to do the fairly slow EDID/HDCP handling in advance. This allows + for quick switching between connectors. + +These pads appear in several of the controls in this section as +bitmasks, one bit for each pad. Bit 0 corresponds to pad 0, bit 1 to pad +1, etc. The maximum value of the control is the set of valid pads. + + +.. _dv-control-id: + +Digital Video Control IDs +========================= + +``V4L2_CID_DV_CLASS (class)`` + The Digital Video class descriptor. + +``V4L2_CID_DV_TX_HOTPLUG (bitmask)`` + Many connectors have a hotplug pin which is high if EDID information + is available from the source. This control shows the state of the + hotplug pin as seen by the transmitter. Each bit corresponds to an + output pad on the transmitter. If an output pad does not have an + associated hotplug pin, then the bit for that pad will be 0. This + read-only control is applicable to DVI-D, HDMI and DisplayPort + connectors. + +``V4L2_CID_DV_TX_RXSENSE (bitmask)`` + Rx Sense is the detection of pull-ups on the TMDS clock lines. This + normally means that the sink has left/entered standby (i.e. the + transmitter can sense that the receiver is ready to receive video). + Each bit corresponds to an output pad on the transmitter. If an + output pad does not have an associated Rx Sense, then the bit for + that pad will be 0. This read-only control is applicable to DVI-D + and HDMI devices. + +``V4L2_CID_DV_TX_EDID_PRESENT (bitmask)`` + When the transmitter sees the hotplug signal from the receiver it + will attempt to read the EDID. If set, then the transmitter has read + at least the first block (= 128 bytes). Each bit corresponds to an + output pad on the transmitter. If an output pad does not support + EDIDs, then the bit for that pad will be 0. This read-only control + is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors. + +``V4L2_CID_DV_TX_MODE`` + (enum) + +enum v4l2_dv_tx_mode - + HDMI transmitters can transmit in DVI-D mode (just video) or in HDMI + mode (video + audio + auxiliary data). This control selects which + mode to use: V4L2_DV_TX_MODE_DVI_D or V4L2_DV_TX_MODE_HDMI. + This control is applicable to HDMI connectors. + +``V4L2_CID_DV_TX_RGB_RANGE`` + (enum) + +enum v4l2_dv_rgb_range - + Select the quantization range for RGB output. V4L2_DV_RANGE_AUTO + follows the RGB quantization range specified in the standard for the + video interface (ie. :ref:`cea861` for HDMI). + V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the + standard to be compatible with sinks that have not implemented the + standard correctly (unfortunately quite common for HDMI and DVI-D). + Full range allows all possible values to be used whereas limited + range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is + the number of bits per component. This control is applicable to VGA, + DVI-A/D, HDMI and DisplayPort connectors. + +``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` + (enum) + +enum v4l2_dv_it_content_type - + Configures the IT Content Type of the transmitted video. This + information is sent over HDMI and DisplayPort connectors as part of + the AVI InfoFrame. The term 'IT Content' is used for content that + originates from a computer as opposed to content from a TV broadcast + or an analog source. The enum v4l2_dv_it_content_type defines + the possible content types: + +.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_DV_IT_CONTENT_TYPE_GRAPHICS`` + - Graphics content. Pixel data should be passed unfiltered and + without analog reconstruction. + * - ``V4L2_DV_IT_CONTENT_TYPE_PHOTO`` + - Photo content. The content is derived from digital still pictures. + The content should be passed through with minimal scaling and + picture enhancements. + * - ``V4L2_DV_IT_CONTENT_TYPE_CINEMA`` + - Cinema content. + * - ``V4L2_DV_IT_CONTENT_TYPE_GAME`` + - Game content. Audio and video latency should be minimized. + * - ``V4L2_DV_IT_CONTENT_TYPE_NO_ITC`` + - No IT Content information is available and the ITC bit in the AVI + InfoFrame is set to 0. + + + +``V4L2_CID_DV_RX_POWER_PRESENT (bitmask)`` + Detects whether the receiver receives power from the source (e.g. + HDMI carries 5V on one of the pins). This is often used to power an + eeprom which contains EDID information, such that the source can + read the EDID even if the sink is in standby/power off. Each bit + corresponds to an input pad on the receiver. If an input pad + cannot detect whether power is present, then the bit for that pad + will be 0. This read-only control is applicable to DVI-D, HDMI and + DisplayPort connectors. + +``V4L2_CID_DV_RX_RGB_RANGE`` + (enum) + +enum v4l2_dv_rgb_range - + Select the quantization range for RGB input. V4L2_DV_RANGE_AUTO + follows the RGB quantization range specified in the standard for the + video interface (ie. :ref:`cea861` for HDMI). + V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the + standard to be compatible with sources that have not implemented the + standard correctly (unfortunately quite common for HDMI and DVI-D). + Full range allows all possible values to be used whereas limited + range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is + the number of bits per component. This control is applicable to VGA, + DVI-A/D, HDMI and DisplayPort connectors. + +``V4L2_CID_DV_RX_IT_CONTENT_TYPE`` + (enum) + +enum v4l2_dv_it_content_type - + Reads the IT Content Type of the received video. This information is + sent over HDMI and DisplayPort connectors as part of the AVI + InfoFrame. The term 'IT Content' is used for content that originates + from a computer as opposed to content from a TV broadcast or an + analog source. See ``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` for the + available content types. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-flash.rst b/Documentation/media/uapi/v4l/ext-ctrls-flash.rst new file mode 100644 index 000000000000..5f30791c35b5 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-flash.rst @@ -0,0 +1,192 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _flash-controls: + +*********************** +Flash Control Reference +*********************** + +The V4L2 flash controls are intended to provide generic access to flash +controller devices. Flash controller devices are typically used in +digital cameras. + +The interface can support both LED and xenon flash devices. As of +writing this, there is no xenon flash driver using this interface. + + +.. _flash-controls-use-cases: + +Supported use cases +=================== + + +Unsynchronised LED flash (software strobe) +------------------------------------------ + +Unsynchronised LED flash is controlled directly by the host as the +sensor. The flash must be enabled by the host before the exposure of the +image starts and disabled once it ends. The host is fully responsible +for the timing of the flash. + +Example of such device: Nokia N900. + + +Synchronised LED flash (hardware strobe) +---------------------------------------- + +The synchronised LED flash is pre-programmed by the host (power and +timeout) but controlled by the sensor through a strobe signal from the +sensor to the flash. + +The sensor controls the flash duration and timing. This information +typically must be made available to the sensor. + + +LED flash as torch +------------------ + +LED flash may be used as torch in conjunction with another use case +involving camera or individually. + + +.. _flash-control-id: + +Flash Control IDs +----------------- + +``V4L2_CID_FLASH_CLASS (class)`` + The FLASH class descriptor. + +``V4L2_CID_FLASH_LED_MODE (menu)`` + Defines the mode of the flash LED, the high-power white LED attached + to the flash controller. Setting this control may not be possible in + presence of some faults. See V4L2_CID_FLASH_FAULT. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_FLASH_LED_MODE_NONE`` + - Off. + * - ``V4L2_FLASH_LED_MODE_FLASH`` + - Flash mode. + * - ``V4L2_FLASH_LED_MODE_TORCH`` + - Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY. + + + +``V4L2_CID_FLASH_STROBE_SOURCE (menu)`` + Defines the source of the flash LED strobe. + +.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_FLASH_STROBE_SOURCE_SOFTWARE`` + - The flash strobe is triggered by using the + V4L2_CID_FLASH_STROBE control. + * - ``V4L2_FLASH_STROBE_SOURCE_EXTERNAL`` + - The flash strobe is triggered by an external source. Typically + this is a sensor, which makes it possible to synchronises the + flash strobe start to exposure start. + + + +``V4L2_CID_FLASH_STROBE (button)`` + Strobe flash. Valid when V4L2_CID_FLASH_LED_MODE is set to + V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE + is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this + control may not be possible in presence of some faults. See + V4L2_CID_FLASH_FAULT. + +``V4L2_CID_FLASH_STROBE_STOP (button)`` + Stop flash strobe immediately. + +``V4L2_CID_FLASH_STROBE_STATUS (boolean)`` + Strobe status: whether the flash is strobing at the moment or not. + This is a read-only control. + +``V4L2_CID_FLASH_TIMEOUT (integer)`` + Hardware timeout for flash. The flash strobe is stopped after this + period of time has passed from the start of the strobe. + +``V4L2_CID_FLASH_INTENSITY (integer)`` + Intensity of the flash strobe when the flash LED is in flash mode + (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps (mA) + if possible. + +``V4L2_CID_FLASH_TORCH_INTENSITY (integer)`` + Intensity of the flash LED in torch mode + (V4L2_FLASH_LED_MODE_TORCH). The unit should be milliamps (mA) + if possible. Setting this control may not be possible in presence of + some faults. See V4L2_CID_FLASH_FAULT. + +``V4L2_CID_FLASH_INDICATOR_INTENSITY (integer)`` + Intensity of the indicator LED. The indicator LED may be fully + independent of the flash LED. The unit should be microamps (uA) if + possible. + +``V4L2_CID_FLASH_FAULT (bitmask)`` + Faults related to the flash. The faults tell about specific problems + in the flash chip itself or the LEDs attached to it. Faults may + prevent further use of some of the flash controls. In particular, + V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE + if the fault affects the flash LED. Exactly which faults have such + an effect is chip dependent. Reading the faults resets the control + and returns the chip to a usable state if possible. + +.. tabularcolumns:: |p{8.0cm}|p{9.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_FLASH_FAULT_OVER_VOLTAGE`` + - Flash controller voltage to the flash LED has exceeded the limit + specific to the flash controller. + * - ``V4L2_FLASH_FAULT_TIMEOUT`` + - The flash strobe was still on when the timeout set by the user --- + V4L2_CID_FLASH_TIMEOUT control --- has expired. Not all flash + controllers may set this in all such conditions. + * - ``V4L2_FLASH_FAULT_OVER_TEMPERATURE`` + - The flash controller has overheated. + * - ``V4L2_FLASH_FAULT_SHORT_CIRCUIT`` + - The short circuit protection of the flash controller has been + triggered. + * - ``V4L2_FLASH_FAULT_OVER_CURRENT`` + - Current in the LED power supply has exceeded the limit specific to + the flash controller. + * - ``V4L2_FLASH_FAULT_INDICATOR`` + - The flash controller has detected a short or open circuit + condition on the indicator LED. + * - ``V4L2_FLASH_FAULT_UNDER_VOLTAGE`` + - Flash controller voltage to the flash LED has been below the + minimum limit specific to the flash controller. + * - ``V4L2_FLASH_FAULT_INPUT_VOLTAGE`` + - The input voltage of the flash controller is below the limit under + which strobing the flash at full current will not be possible.The + condition persists until this flag is no longer set. + * - ``V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE`` + - The temperature of the LED has exceeded its allowed upper limit. + + + +``V4L2_CID_FLASH_CHARGE (boolean)`` + Enable or disable charging of the xenon flash capacitor. + +``V4L2_CID_FLASH_READY (boolean)`` + Is the flash ready to strobe? Xenon flashes require their capacitors + charged before strobing. LED flashes often require a cooldown period + after strobe during which another strobe will not be possible. This + is a read-only control. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst b/Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst new file mode 100644 index 000000000000..3ed6dd7f586d --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst @@ -0,0 +1,95 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _fm-rx-controls: + +***************************** +FM Receiver Control Reference +***************************** + +The FM Receiver (FM_RX) class includes controls for common features of +FM Reception capable devices. + + +.. _fm-rx-control-id: + +FM_RX Control IDs +================= + +``V4L2_CID_FM_RX_CLASS (class)`` + The FM_RX class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_RDS_RECEPTION (boolean)`` + Enables/disables RDS reception by the radio tuner + +``V4L2_CID_RDS_RX_PTY (integer)`` + Gets RDS Programme Type field. This encodes up to 31 pre-defined + programme types. + +``V4L2_CID_RDS_RX_PS_NAME (string)`` + Gets the Programme Service name (PS_NAME). It is intended for + static display on a receiver. It is the primary aid to listeners in + programme service identification and selection. In Annex E of + :ref:`iec62106`, the RDS specification, there is a full + description of the correct character encoding for Programme Service + name strings. Also from RDS specification, PS is usually a single + eight character text. However, it is also possible to find receivers + which can scroll strings sized as 8 x N characters. So, this control + must be configured with steps of 8 characters. The result is it must + always contain a string with size multiple of 8. + +``V4L2_CID_RDS_RX_RADIO_TEXT (string)`` + Gets the Radio Text info. It is a textual description of what is + being broadcasted. RDS Radio Text can be applied when broadcaster + wishes to transmit longer PS names, programme-related information or + any other text. In these cases, RadioText can be used in addition to + ``V4L2_CID_RDS_RX_PS_NAME``. The encoding for Radio Text strings is + also fully described in Annex E of :ref:`iec62106`. The length of + Radio Text strings depends on which RDS Block is being used to + transmit it, either 32 (2A block) or 64 (2B block). However, it is + also possible to find receivers which can scroll strings sized as 32 + x N or 64 x N characters. So, this control must be configured with + steps of 32 or 64 characters. The result is it must always contain a + string with size multiple of 32 or 64. + +``V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (boolean)`` + If set, then a traffic announcement is in progress. + +``V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (boolean)`` + If set, then the tuned programme carries traffic announcements. + +``V4L2_CID_RDS_RX_MUSIC_SPEECH (boolean)`` + If set, then this channel broadcasts music. If cleared, then it + broadcasts speech. If the transmitter doesn't make this distinction, + then it will be set. + +``V4L2_CID_TUNE_DEEMPHASIS`` + (enum) + +enum v4l2_deemphasis - + Configures the de-emphasis value for reception. A de-emphasis filter + is applied to the broadcast to accentuate the high audio + frequencies. Depending on the region, a time constant of either 50 + or 75 useconds is used. The enum v4l2_deemphasis defines possible + values for de-emphasis. Here they are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_DEEMPHASIS_DISABLED`` + - No de-emphasis is applied. + * - ``V4L2_DEEMPHASIS_50_uS`` + - A de-emphasis of 50 uS is used. + * - ``V4L2_DEEMPHASIS_75_uS`` + - A de-emphasis of 75 uS is used. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst b/Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst new file mode 100644 index 000000000000..db88346d99fd --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst @@ -0,0 +1,188 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _fm-tx-controls: + +******************************** +FM Transmitter Control Reference +******************************** + +The FM Transmitter (FM_TX) class includes controls for common features +of FM transmissions capable devices. Currently this class includes +parameters for audio compression, pilot tone generation, audio deviation +limiter, RDS transmission and tuning power features. + + +.. _fm-tx-control-id: + +FM_TX Control IDs +================= + +``V4L2_CID_FM_TX_CLASS (class)`` + The FM_TX class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_RDS_TX_DEVIATION (integer)`` + Configures RDS signal frequency deviation level in Hz. The range and + step are driver-specific. + +``V4L2_CID_RDS_TX_PI (integer)`` + Sets the RDS Programme Identification field for transmission. + +``V4L2_CID_RDS_TX_PTY (integer)`` + Sets the RDS Programme Type field for transmission. This encodes up + to 31 pre-defined programme types. + +``V4L2_CID_RDS_TX_PS_NAME (string)`` + Sets the Programme Service name (PS_NAME) for transmission. It is + intended for static display on a receiver. It is the primary aid to + listeners in programme service identification and selection. In + Annex E of :ref:`iec62106`, the RDS specification, there is a full + description of the correct character encoding for Programme Service + name strings. Also from RDS specification, PS is usually a single + eight character text. However, it is also possible to find receivers + which can scroll strings sized as 8 x N characters. So, this control + must be configured with steps of 8 characters. The result is it must + always contain a string with size multiple of 8. + +``V4L2_CID_RDS_TX_RADIO_TEXT (string)`` + Sets the Radio Text info for transmission. It is a textual + description of what is being broadcasted. RDS Radio Text can be + applied when broadcaster wishes to transmit longer PS names, + programme-related information or any other text. In these cases, + RadioText should be used in addition to ``V4L2_CID_RDS_TX_PS_NAME``. + The encoding for Radio Text strings is also fully described in Annex + E of :ref:`iec62106`. The length of Radio Text strings depends on + which RDS Block is being used to transmit it, either 32 (2A block) + or 64 (2B block). However, it is also possible to find receivers + which can scroll strings sized as 32 x N or 64 x N characters. So, + this control must be configured with steps of 32 or 64 characters. + The result is it must always contain a string with size multiple of + 32 or 64. + +``V4L2_CID_RDS_TX_MONO_STEREO (boolean)`` + Sets the Mono/Stereo bit of the Decoder Identification code. If set, + then the audio was recorded as stereo. + +``V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (boolean)`` + Sets the + `Artificial Head `__ + bit of the Decoder Identification code. If set, then the audio was + recorded using an artificial head. + +``V4L2_CID_RDS_TX_COMPRESSED (boolean)`` + Sets the Compressed bit of the Decoder Identification code. If set, + then the audio is compressed. + +``V4L2_CID_RDS_TX_DYNAMIC_PTY (boolean)`` + Sets the Dynamic PTY bit of the Decoder Identification code. If set, + then the PTY code is dynamically switched. + +``V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (boolean)`` + If set, then a traffic announcement is in progress. + +``V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (boolean)`` + If set, then the tuned programme carries traffic announcements. + +``V4L2_CID_RDS_TX_MUSIC_SPEECH (boolean)`` + If set, then this channel broadcasts music. If cleared, then it + broadcasts speech. If the transmitter doesn't make this distinction, + then it should be set. + +``V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (boolean)`` + If set, then transmit alternate frequencies. + +``V4L2_CID_RDS_TX_ALT_FREQS (__u32 array)`` + The alternate frequencies in kHz units. The RDS standard allows for + up to 25 frequencies to be defined. Drivers may support fewer + frequencies so check the array size. + +``V4L2_CID_AUDIO_LIMITER_ENABLED (boolean)`` + Enables or disables the audio deviation limiter feature. The limiter + is useful when trying to maximize the audio volume, minimize + receiver-generated distortion and prevent overmodulation. + +``V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (integer)`` + Sets the audio deviation limiter feature release time. Unit is in + useconds. Step and range are driver-specific. + +``V4L2_CID_AUDIO_LIMITER_DEVIATION (integer)`` + Configures audio frequency deviation level in Hz. The range and step + are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_ENABLED (boolean)`` + Enables or disables the audio compression feature. This feature + amplifies signals below the threshold by a fixed gain and compresses + audio signals above the threshold by the ratio of Threshold/(Gain + + Threshold). + +``V4L2_CID_AUDIO_COMPRESSION_GAIN (integer)`` + Sets the gain for audio compression feature. It is a dB value. The + range and step are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (integer)`` + Sets the threshold level for audio compression freature. It is a dB + value. The range and step are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (integer)`` + Sets the attack time for audio compression feature. It is a useconds + value. The range and step are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (integer)`` + Sets the release time for audio compression feature. It is a + useconds value. The range and step are driver-specific. + +``V4L2_CID_PILOT_TONE_ENABLED (boolean)`` + Enables or disables the pilot tone generation feature. + +``V4L2_CID_PILOT_TONE_DEVIATION (integer)`` + Configures pilot tone frequency deviation level. Unit is in Hz. The + range and step are driver-specific. + +``V4L2_CID_PILOT_TONE_FREQUENCY (integer)`` + Configures pilot tone frequency value. Unit is in Hz. The range and + step are driver-specific. + +``V4L2_CID_TUNE_PREEMPHASIS`` + (enum) + +enum v4l2_preemphasis - + Configures the pre-emphasis value for broadcasting. A pre-emphasis + filter is applied to the broadcast to accentuate the high audio + frequencies. Depending on the region, a time constant of either 50 + or 75 useconds is used. The enum v4l2_preemphasis defines possible + values for pre-emphasis. Here they are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_PREEMPHASIS_DISABLED`` + - No pre-emphasis is applied. + * - ``V4L2_PREEMPHASIS_50_uS`` + - A pre-emphasis of 50 uS is used. + * - ``V4L2_PREEMPHASIS_75_uS`` + - A pre-emphasis of 75 uS is used. + + + +``V4L2_CID_TUNE_POWER_LEVEL (integer)`` + Sets the output power level for signal transmission. Unit is in + dBuV. Range and step are driver-specific. + +``V4L2_CID_TUNE_ANTENNA_CAPACITOR (integer)`` + This selects the value of antenna tuning capacitor manually or + automatically if set to zero. Unit, range and step are + driver-specific. + +For more details about RDS specification, refer to :ref:`iec62106` +document, from CENELEC. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-image-process.rst b/Documentation/media/uapi/v4l/ext-ctrls-image-process.rst new file mode 100644 index 000000000000..22fc2d3e433d --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-image-process.rst @@ -0,0 +1,63 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _image-process-controls: + +******************************* +Image Process Control Reference +******************************* + +The Image Process control class is intended for low-level control of +image processing functions. Unlike ``V4L2_CID_IMAGE_SOURCE_CLASS``, the +controls in this class affect processing the image, and do not control +capturing of it. + + +.. _image-process-control-id: + +Image Process Control IDs +========================= + +``V4L2_CID_IMAGE_PROC_CLASS (class)`` + The IMAGE_PROC class descriptor. + +``V4L2_CID_LINK_FREQ (integer menu)`` + Data bus frequency. Together with the media bus pixel code, bus type + (clock cycles per sample), the data bus frequency defines the pixel + rate (``V4L2_CID_PIXEL_RATE``) in the pixel array (or possibly + elsewhere, if the device is not an image sensor). The frame rate can + be calculated from the pixel clock, image width and height and + horizontal and vertical blanking. While the pixel rate control may + be defined elsewhere than in the subdev containing the pixel array, + the frame rate cannot be obtained from that information. This is + because only on the pixel array it can be assumed that the vertical + and horizontal blanking information is exact: no other blanking is + allowed in the pixel array. The selection of frame rate is performed + by selecting the desired horizontal and vertical blanking. The unit + of this control is Hz. + +``V4L2_CID_PIXEL_RATE (64-bit integer)`` + Pixel rate in the source pads of the subdev. This control is + read-only and its unit is pixels / second. + +``V4L2_CID_TEST_PATTERN (menu)`` + Some capture/display/sensor devices have the capability to generate + test pattern images. These hardware specific test patterns can be + used to test if a device is working properly. + +``V4L2_CID_DEINTERLACING_MODE (menu)`` + The video deinterlacing mode (such as Bob, Weave, ...). The menu items are + driver specific and are documented in :ref:`v4l-drivers`. + +``V4L2_CID_DIGITAL_GAIN (integer)`` + Digital gain is the value by which all colour components + are multiplied by. Typically the digital gain applied is the + control value divided by e.g. 0x100, meaning that to get no + digital gain the control value needs to be 0x100. The no-gain + configuration is also typically the default. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-image-source.rst b/Documentation/media/uapi/v4l/ext-ctrls-image-source.rst new file mode 100644 index 000000000000..2c3ab5796d76 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-image-source.rst @@ -0,0 +1,57 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _image-source-controls: + +****************************** +Image Source Control Reference +****************************** + +The Image Source control class is intended for low-level control of +image source devices such as image sensors. The devices feature an +analogue to digital converter and a bus transmitter to transmit the +image data out of the device. + + +.. _image-source-control-id: + +Image Source Control IDs +======================== + +``V4L2_CID_IMAGE_SOURCE_CLASS (class)`` + The IMAGE_SOURCE class descriptor. + +``V4L2_CID_VBLANK (integer)`` + Vertical blanking. The idle period after every frame during which no + image data is produced. The unit of vertical blanking is a line. + Every line has length of the image width plus horizontal blanking at + the pixel rate defined by ``V4L2_CID_PIXEL_RATE`` control in the + same sub-device. + +``V4L2_CID_HBLANK (integer)`` + Horizontal blanking. The idle period after every line of image data + during which no image data is produced. The unit of horizontal + blanking is pixels. + +``V4L2_CID_ANALOGUE_GAIN (integer)`` + Analogue gain is gain affecting all colour components in the pixel + matrix. The gain operation is performed in the analogue domain + before A/D conversion. + +``V4L2_CID_TEST_PATTERN_RED (integer)`` + Test pattern red colour component. + +``V4L2_CID_TEST_PATTERN_GREENR (integer)`` + Test pattern green (next to red) colour component. + +``V4L2_CID_TEST_PATTERN_BLUE (integer)`` + Test pattern blue colour component. + +``V4L2_CID_TEST_PATTERN_GREENB (integer)`` + Test pattern green (next to blue) colour component. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst b/Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst new file mode 100644 index 000000000000..cf9cd8a9f9b4 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst @@ -0,0 +1,113 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _jpeg-controls: + +********************** +JPEG Control Reference +********************** + +The JPEG class includes controls for common features of JPEG encoders +and decoders. Currently it includes features for codecs implementing +progressive baseline DCT compression process with Huffman entrophy +coding. + + +.. _jpeg-control-id: + +JPEG Control IDs +================ + +``V4L2_CID_JPEG_CLASS (class)`` + The JPEG class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_JPEG_CHROMA_SUBSAMPLING (menu)`` + The chroma subsampling factors describe how each component of an + input image is sampled, in respect to maximum sample rate in each + spatial dimension. See :ref:`itu-t81`, clause A.1.1. for more + details. The ``V4L2_CID_JPEG_CHROMA_SUBSAMPLING`` control determines + how Cb and Cr components are downsampled after converting an input + image from RGB to Y'CbCr color space. + +.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_444`` + - No chroma subsampling, each pixel has Y, Cr and Cb values. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_422`` + - Horizontally subsample Cr, Cb components by a factor of 2. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_420`` + - Subsample Cr, Cb components horizontally and vertically by 2. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_411`` + - Horizontally subsample Cr, Cb components by a factor of 4. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_410`` + - Subsample Cr, Cb components horizontally by 4 and vertically by 2. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY`` + - Use only luminance component. + + + +``V4L2_CID_JPEG_RESTART_INTERVAL (integer)`` + The restart interval determines an interval of inserting RSTm + markers (m = 0..7). The purpose of these markers is to additionally + reinitialize the encoder process, in order to process blocks of an + image independently. For the lossy compression processes the restart + interval unit is MCU (Minimum Coded Unit) and its value is contained + in DRI (Define Restart Interval) marker. If + ``V4L2_CID_JPEG_RESTART_INTERVAL`` control is set to 0, DRI and RSTm + markers will not be inserted. + +.. _jpeg-quality-control: + +``V4L2_CID_JPEG_COMPRESSION_QUALITY (integer)`` + ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control determines trade-off + between image quality and size. It provides simpler method for + applications to control image quality, without a need for direct + reconfiguration of luminance and chrominance quantization tables. In + cases where a driver uses quantization tables configured directly by + an application, using interfaces defined elsewhere, + ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control should be set by + driver to 0. + + The value range of this control is driver-specific. Only positive, + non-zero values are meaningful. The recommended range is 1 - 100, + where larger values correspond to better image quality. + +.. _jpeg-active-marker-control: + +``V4L2_CID_JPEG_ACTIVE_MARKER (bitmask)`` + Specify which JPEG markers are included in compressed stream. This + control is valid only for encoders. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_JPEG_ACTIVE_MARKER_APP0`` + - Application data segment APP\ :sub:`0`. + * - ``V4L2_JPEG_ACTIVE_MARKER_APP1`` + - Application data segment APP\ :sub:`1`. + * - ``V4L2_JPEG_ACTIVE_MARKER_COM`` + - Comment segment. + * - ``V4L2_JPEG_ACTIVE_MARKER_DQT`` + - Quantization tables segment. + * - ``V4L2_JPEG_ACTIVE_MARKER_DHT`` + - Huffman tables segment. + + + +For more details about JPEG specification, refer to :ref:`itu-t81`, +:ref:`jfif`, :ref:`w3c-jpeg-jfif`. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst b/Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst new file mode 100644 index 000000000000..0fb85ba878dd --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst @@ -0,0 +1,96 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _rf-tuner-controls: + +************************** +RF Tuner Control Reference +************************** + +The RF Tuner (RF_TUNER) class includes controls for common features of +devices having RF tuner. + +In this context, RF tuner is radio receiver circuit between antenna and +demodulator. It receives radio frequency (RF) from the antenna and +converts that received signal to lower intermediate frequency (IF) or +baseband frequency (BB). Tuners that could do baseband output are often +called Zero-IF tuners. Older tuners were typically simple PLL tuners +inside a metal box, while newer ones are highly integrated chips +without a metal box "silicon tuners". These controls are mostly +applicable for new feature rich silicon tuners, just because older +tuners does not have much adjustable features. + +For more information about RF tuners see +`Tuner (radio) `__ +and `RF front end `__ +from Wikipedia. + + +.. _rf-tuner-control-id: + +RF_TUNER Control IDs +==================== + +``V4L2_CID_RF_TUNER_CLASS (class)`` + The RF_TUNER class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_RF_TUNER_BANDWIDTH_AUTO (boolean)`` + Enables/disables tuner radio channel bandwidth configuration. In + automatic mode bandwidth configuration is performed by the driver. + +``V4L2_CID_RF_TUNER_BANDWIDTH (integer)`` + Filter(s) on tuner signal path are used to filter signal according + to receiving party needs. Driver configures filters to fulfill + desired bandwidth requirement. Used when + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO is not set. Unit is in Hz. The + range and step are driver-specific. + +``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO (boolean)`` + Enables/disables LNA automatic gain control (AGC) + +``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO (boolean)`` + Enables/disables mixer automatic gain control (AGC) + +``V4L2_CID_RF_TUNER_IF_GAIN_AUTO (boolean)`` + Enables/disables IF automatic gain control (AGC) + +``V4L2_CID_RF_TUNER_RF_GAIN (integer)`` + The RF amplifier is the very first amplifier on the receiver signal + path, just right after the antenna input. The difference between the + LNA gain and the RF gain in this document is that the LNA gain is + integrated in the tuner chip while the RF gain is a separate chip. + There may be both RF and LNA gain controls in the same device. The + range and step are driver-specific. + +``V4L2_CID_RF_TUNER_LNA_GAIN (integer)`` + LNA (low noise amplifier) gain is first gain stage on the RF tuner + signal path. It is located very close to tuner antenna input. Used + when ``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO`` is not set. See + ``V4L2_CID_RF_TUNER_RF_GAIN`` to understand how RF gain and LNA gain + differs from the each others. The range and step are + driver-specific. + +``V4L2_CID_RF_TUNER_MIXER_GAIN (integer)`` + Mixer gain is second gain stage on the RF tuner signal path. It is + located inside mixer block, where RF signal is down-converted by the + mixer. Used when ``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO`` is not set. + The range and step are driver-specific. + +``V4L2_CID_RF_TUNER_IF_GAIN (integer)`` + IF gain is last gain stage on the RF tuner signal path. It is + located on output of RF tuner. It controls signal level of + intermediate frequency output or baseband output. Used when + ``V4L2_CID_RF_TUNER_IF_GAIN_AUTO`` is not set. The range and step + are driver-specific. + +``V4L2_CID_RF_TUNER_PLL_LOCK (boolean)`` + Is synthesizer PLL locked? RF tuner is receiving given frequency + when that control is set. This is a read-only control. diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 00934efdc9e4..24274b398e63 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -9,9 +9,9 @@ .. _extended-controls: -***************** -Extended Controls -***************** +********************* +Extended Controls API +********************* Introduction @@ -181,3917 +181,3 @@ The flags field of struct :ref:`v4l2_queryctrl ` also contains hints on the behavior of the control. See the :ref:`VIDIOC_QUERYCTRL` documentation for more details. - - -.. _mpeg-controls: - -Codec Control Reference -======================= - -Below all controls within the Codec control class are described. First -the generic controls, then controls specific for certain hardware. - -.. note:: - - These controls are applicable to all codecs and not just MPEG. The - defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG as the controls - were originally made for MPEG codecs and later extended to cover all - encoding formats. - - -Generic Codec Controls ----------------------- - - -.. _mpeg-control-id: - -Codec Control IDs -^^^^^^^^^^^^^^^^^ - -``V4L2_CID_MPEG_CLASS (class)`` - The Codec class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. This description can be - used as the caption of a Tab page in a GUI, for example. - -.. _v4l2-mpeg-stream-type: - -``V4L2_CID_MPEG_STREAM_TYPE`` - (enum) - -enum v4l2_mpeg_stream_type - - The MPEG-1, -2 or -4 output stream type. One cannot assume anything - here. Each hardware MPEG encoder tends to support different subsets - of the available MPEG stream types. This control is specific to - multiplexed MPEG streams. The currently defined stream types are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_PS`` - - MPEG-2 program stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_TS`` - - MPEG-2 transport stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_SS`` - - MPEG-1 system stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_DVD`` - - MPEG-2 DVD-compatible stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_VCD`` - - MPEG-1 VCD-compatible stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD`` - - MPEG-2 SVCD-compatible stream - - - -``V4L2_CID_MPEG_STREAM_PID_PMT (integer)`` - Program Map Table Packet ID for the MPEG transport stream (default - 16) - -``V4L2_CID_MPEG_STREAM_PID_AUDIO (integer)`` - Audio Packet ID for the MPEG transport stream (default 256) - -``V4L2_CID_MPEG_STREAM_PID_VIDEO (integer)`` - Video Packet ID for the MPEG transport stream (default 260) - -``V4L2_CID_MPEG_STREAM_PID_PCR (integer)`` - Packet ID for the MPEG transport stream carrying PCR fields (default - 259) - -``V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (integer)`` - Audio ID for MPEG PES - -``V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (integer)`` - Video ID for MPEG PES - -.. _v4l2-mpeg-stream-vbi-fmt: - -``V4L2_CID_MPEG_STREAM_VBI_FMT`` - (enum) - -enum v4l2_mpeg_stream_vbi_fmt - - Some cards can embed VBI data (e. g. Closed Caption, Teletext) into - the MPEG stream. This control selects whether VBI data should be - embedded, and if so, what embedding method should be used. The list - of possible VBI formats depends on the driver. The currently defined - VBI format types are: - - - -.. tabularcolumns:: |p{6 cm}|p{11.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_STREAM_VBI_FMT_NONE`` - - No VBI in the MPEG stream - * - ``V4L2_MPEG_STREAM_VBI_FMT_IVTV`` - - VBI in private packets, IVTV format (documented in the kernel - sources in the file - ``Documentation/media/v4l-drivers/cx2341x.rst``) - - - -.. _v4l2-mpeg-audio-sampling-freq: - -``V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ`` - (enum) - -enum v4l2_mpeg_audio_sampling_freq - - MPEG Audio sampling frequency. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100`` - - 44.1 kHz - * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000`` - - 48 kHz - * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000`` - - 32 kHz - - - -.. _v4l2-mpeg-audio-encoding: - -``V4L2_CID_MPEG_AUDIO_ENCODING`` - (enum) - -enum v4l2_mpeg_audio_encoding - - MPEG Audio encoding. This control is specific to multiplexed MPEG - streams. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_1`` - - MPEG-1/2 Layer I encoding - * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_2`` - - MPEG-1/2 Layer II encoding - * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_3`` - - MPEG-1/2 Layer III encoding - * - ``V4L2_MPEG_AUDIO_ENCODING_AAC`` - - MPEG-2/4 AAC (Advanced Audio Coding) - * - ``V4L2_MPEG_AUDIO_ENCODING_AC3`` - - AC-3 aka ATSC A/52 encoding - - - -.. _v4l2-mpeg-audio-l1-bitrate: - -``V4L2_CID_MPEG_AUDIO_L1_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_l1_bitrate - - MPEG-1/2 Layer I bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_288K`` - - 288 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_320K`` - - 320 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_352K`` - - 352 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_384K`` - - 384 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_416K`` - - 416 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_448K`` - - 448 kbit/s - - - -.. _v4l2-mpeg-audio-l2-bitrate: - -``V4L2_CID_MPEG_AUDIO_L2_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_l2_bitrate - - MPEG-1/2 Layer II bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_48K`` - - 48 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_56K`` - - 56 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_80K`` - - 80 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_112K`` - - 112 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_320K`` - - 320 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_384K`` - - 384 kbit/s - - - -.. _v4l2-mpeg-audio-l3-bitrate: - -``V4L2_CID_MPEG_AUDIO_L3_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_l3_bitrate - - MPEG-1/2 Layer III bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_40K`` - - 40 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_48K`` - - 48 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_56K`` - - 56 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_80K`` - - 80 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_112K`` - - 112 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_320K`` - - 320 kbit/s - - - -``V4L2_CID_MPEG_AUDIO_AAC_BITRATE (integer)`` - AAC bitrate in bits per second. - -.. _v4l2-mpeg-audio-ac3-bitrate: - -``V4L2_CID_MPEG_AUDIO_AC3_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_ac3_bitrate - - AC-3 bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_40K`` - - 40 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_48K`` - - 48 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_56K`` - - 56 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_80K`` - - 80 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_112K`` - - 112 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_320K`` - - 320 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_384K`` - - 384 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_448K`` - - 448 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_512K`` - - 512 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_576K`` - - 576 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_640K`` - - 640 kbit/s - - - -.. _v4l2-mpeg-audio-mode: - -``V4L2_CID_MPEG_AUDIO_MODE`` - (enum) - -enum v4l2_mpeg_audio_mode - - MPEG Audio mode. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_MODE_STEREO`` - - Stereo - * - ``V4L2_MPEG_AUDIO_MODE_JOINT_STEREO`` - - Joint Stereo - * - ``V4L2_MPEG_AUDIO_MODE_DUAL`` - - Bilingual - * - ``V4L2_MPEG_AUDIO_MODE_MONO`` - - Mono - - - -.. _v4l2-mpeg-audio-mode-extension: - -``V4L2_CID_MPEG_AUDIO_MODE_EXTENSION`` - (enum) - -enum v4l2_mpeg_audio_mode_extension - - Joint Stereo audio mode extension. In Layer I and II they indicate - which subbands are in intensity stereo. All other subbands are coded - in stereo. Layer III is not (yet) supported. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4`` - - Subbands 4-31 in intensity stereo - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8`` - - Subbands 8-31 in intensity stereo - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12`` - - Subbands 12-31 in intensity stereo - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16`` - - Subbands 16-31 in intensity stereo - - - -.. _v4l2-mpeg-audio-emphasis: - -``V4L2_CID_MPEG_AUDIO_EMPHASIS`` - (enum) - -enum v4l2_mpeg_audio_emphasis - - Audio Emphasis. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_EMPHASIS_NONE`` - - None - * - ``V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS`` - - 50/15 microsecond emphasis - * - ``V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17`` - - CCITT J.17 - - - -.. _v4l2-mpeg-audio-crc: - -``V4L2_CID_MPEG_AUDIO_CRC`` - (enum) - -enum v4l2_mpeg_audio_crc - - CRC method. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_CRC_NONE`` - - None - * - ``V4L2_MPEG_AUDIO_CRC_CRC16`` - - 16 bit parity check - - - -``V4L2_CID_MPEG_AUDIO_MUTE (boolean)`` - Mutes the audio when capturing. This is not done by muting audio - hardware, which can still produce a slight hiss, but in the encoder - itself, guaranteeing a fixed and reproducible audio bitstream. 0 = - unmuted, 1 = muted. - -.. _v4l2-mpeg-audio-dec-playback: - -``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` - (enum) - -enum v4l2_mpeg_audio_dec_playback - - Determines how monolingual audio should be played back. Possible - values are: - - - -.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO`` - - Automatically determines the best playback mode. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO`` - - Stereo playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT`` - - Left channel playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT`` - - Right channel playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO`` - - Mono playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO`` - - Stereo playback with swapped left and right channels. - - - -.. _v4l2-mpeg-audio-dec-multilingual-playback: - -``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` - (enum) - -enum v4l2_mpeg_audio_dec_playback - - Determines how multilingual audio should be played back. - -.. _v4l2-mpeg-video-encoding: - -``V4L2_CID_MPEG_VIDEO_ENCODING`` - (enum) - -enum v4l2_mpeg_video_encoding - - MPEG Video encoding method. This control is specific to multiplexed - MPEG streams. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_1`` - - MPEG-1 Video encoding - * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_2`` - - MPEG-2 Video encoding - * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC`` - - MPEG-4 AVC (H.264) Video encoding - - - -.. _v4l2-mpeg-video-aspect: - -``V4L2_CID_MPEG_VIDEO_ASPECT`` - (enum) - -enum v4l2_mpeg_video_aspect - - Video aspect. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_ASPECT_1x1`` - * - ``V4L2_MPEG_VIDEO_ASPECT_4x3`` - * - ``V4L2_MPEG_VIDEO_ASPECT_16x9`` - * - ``V4L2_MPEG_VIDEO_ASPECT_221x100`` - - - -``V4L2_CID_MPEG_VIDEO_B_FRAMES (integer)`` - Number of B-Frames (default 2) - -``V4L2_CID_MPEG_VIDEO_GOP_SIZE (integer)`` - GOP size (default 12) - -``V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (boolean)`` - GOP closure (default 1) - -``V4L2_CID_MPEG_VIDEO_PULLDOWN (boolean)`` - Enable 3:2 pulldown (default 0) - -.. _v4l2-mpeg-video-bitrate-mode: - -``V4L2_CID_MPEG_VIDEO_BITRATE_MODE`` - (enum) - -enum v4l2_mpeg_video_bitrate_mode - - Video bitrate mode. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_VBR`` - - Variable bitrate - * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_CBR`` - - Constant bitrate - - - -``V4L2_CID_MPEG_VIDEO_BITRATE (integer)`` - Video bitrate in bits per second. - -``V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (integer)`` - Peak video bitrate in bits per second. Must be larger or equal to - the average video bitrate. It is ignored if the video bitrate mode - is set to constant bitrate. - -``V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (integer)`` - For every captured frame, skip this many subsequent frames (default - 0). - -``V4L2_CID_MPEG_VIDEO_MUTE (boolean)`` - "Mutes" the video to a fixed color when capturing. This is useful - for testing, to produce a fixed video bitstream. 0 = unmuted, 1 = - muted. - -``V4L2_CID_MPEG_VIDEO_MUTE_YUV (integer)`` - Sets the "mute" color of the video. The supplied 32-bit integer is - interpreted as follows (bit 0 = least significant bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:7 - - V chrominance information - * - Bit 8:15 - - U chrominance information - * - Bit 16:23 - - Y luminance information - * - Bit 24:31 - - Must be zero. - - - -.. _v4l2-mpeg-video-dec-pts: - -``V4L2_CID_MPEG_VIDEO_DEC_PTS (integer64)`` - This read-only control returns the 33-bit video Presentation Time - Stamp as defined in ITU T-REC-H.222.0 and ISO/IEC 13818-1 of the - currently displayed frame. This is the same PTS as is used in - :ref:`VIDIOC_DECODER_CMD`. - -.. _v4l2-mpeg-video-dec-frame: - -``V4L2_CID_MPEG_VIDEO_DEC_FRAME (integer64)`` - This read-only control returns the frame counter of the frame that - is currently displayed (decoded). This value is reset to 0 whenever - the decoder is started. - -``V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (boolean)`` - If enabled the decoder expects to receive a single slice per buffer, - otherwise the decoder expects a single frame in per buffer. - Applicable to the decoder, all codecs. - -``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (boolean)`` - Enable writing sample aspect ratio in the Video Usability - Information. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-vui-sar-idc: - -``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` - (enum) - -enum v4l2_mpeg_video_h264_vui_sar_idc - - VUI sample aspect ratio indicator for H.264 encoding. The value is - defined in the table E-1 in the standard. Applicable to the H264 - encoder. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED`` - - Unspecified - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1`` - - 1x1 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11`` - - 12x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11`` - - 10x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11`` - - 16x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33`` - - 40x33 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11`` - - 24x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11`` - - 20x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11`` - - 32x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33`` - - 80x33 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11`` - - 18x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11`` - - 15x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33`` - - 64x33 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99`` - - 160x99 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3`` - - 4x3 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2`` - - 3x2 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1`` - - 2x1 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED`` - - Extended SAR - - - -``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (integer)`` - Extended sample aspect ratio width for H.264 VUI encoding. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (integer)`` - Extended sample aspect ratio height for H.264 VUI encoding. - Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-level: - -``V4L2_CID_MPEG_VIDEO_H264_LEVEL`` - (enum) - -enum v4l2_mpeg_video_h264_level - - The level information for the H264 video elementary stream. - Applicable to the H264 encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_0`` - - Level 1.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1B`` - - Level 1B - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_1`` - - Level 1.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_2`` - - Level 1.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_3`` - - Level 1.3 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_0`` - - Level 2.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_1`` - - Level 2.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_2`` - - Level 2.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_0`` - - Level 3.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_1`` - - Level 3.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_2`` - - Level 3.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_0`` - - Level 4.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_1`` - - Level 4.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_2`` - - Level 4.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_0`` - - Level 5.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_1`` - - Level 5.1 - - - -.. _v4l2-mpeg-video-mpeg4-level: - -``V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL`` - (enum) - -enum v4l2_mpeg_video_mpeg4_level - - The level information for the MPEG4 elementary stream. Applicable to - the MPEG4 encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0`` - - Level 0 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B`` - - Level 0b - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_1`` - - Level 1 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_2`` - - Level 2 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3`` - - Level 3 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B`` - - Level 3b - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_4`` - - Level 4 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_5`` - - Level 5 - - - -.. _v4l2-mpeg-video-h264-profile: - -``V4L2_CID_MPEG_VIDEO_H264_PROFILE`` - (enum) - -enum v4l2_mpeg_video_h264_profile - - The profile information for H264. Applicable to the H264 encoder. - Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE`` - - Baseline profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE`` - - Constrained Baseline profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MAIN`` - - Main profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED`` - - Extended profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH`` - - High profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10`` - - High 10 profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422`` - - High 422 profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE`` - - High 444 Predictive profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA`` - - High 10 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA`` - - High 422 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA`` - - High 444 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA`` - - CAVLC 444 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE`` - - Scalable Baseline profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH`` - - Scalable High profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA`` - - Scalable High Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH`` - - Stereo High profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH`` - - Multiview High profile - - - -.. _v4l2-mpeg-video-mpeg4-profile: - -``V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE`` - (enum) - -enum v4l2_mpeg_video_mpeg4_profile - - The profile information for MPEG4. Applicable to the MPEG4 encoder. - Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE`` - - Simple profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE`` - - Advanced Simple profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE`` - - Core profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE`` - - Simple Scalable profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY`` - - - - - -``V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (integer)`` - The maximum number of reference pictures used for encoding. - Applicable to the encoder. - -.. _v4l2-mpeg-video-multi-slice-mode: - -``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` - (enum) - -enum v4l2_mpeg_video_multi_slice_mode - - Determines how the encoder should handle division of frame into - slices. Applicable to the encoder. Possible values are: - - - -.. tabularcolumns:: |p{8.7cm}|p{8.8cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE`` - - Single slice per frame. - * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB`` - - Multiple slices with set maximum number of macroblocks per slice. - * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES`` - - Multiple slice with set maximum size in bytes per slice. - - - -``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (integer)`` - The maximum number of macroblocks in a slice. Used when - ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB``. Applicable to the - encoder. - -``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (integer)`` - The maximum size of a slice in bytes. Used when - ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES``. Applicable to the - encoder. - -.. _v4l2-mpeg-video-h264-loop-filter-mode: - -``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_video_h264_loop_filter_mode - - Loop filter mode for H264 encoder. Possible values are: - - - -.. tabularcolumns:: |p{14.0cm}|p{3.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED`` - - Loop filter is enabled. - * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED`` - - Loop filter is disabled. - * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` - - Loop filter is disabled at the slice boundary. - - - -``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (integer)`` - Loop filter alpha coefficient, defined in the H264 standard. - This value corresponds to the slice_alpha_c0_offset_div2 slice header - field, and should be in the range of -6 to +6, inclusive. The actual alpha - offset FilterOffsetA is twice this value. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (integer)`` - Loop filter beta coefficient, defined in the H264 standard. - This corresponds to the slice_beta_offset_div2 slice header field, and - should be in the range of -6 to +6, inclusive. The actual beta offset - FilterOffsetB is twice this value. - Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-entropy-mode: - -``V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE`` - (enum) - -enum v4l2_mpeg_video_h264_entropy_mode - - Entropy coding mode for H264 - CABAC/CAVALC. Applicable to the H264 - encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC`` - - Use CAVLC entropy coding. - * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC`` - - Use CABAC entropy coding. - - - -``V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (boolean)`` - Enable 8X8 transform for H264. Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (boolean)`` - Enable constrained intra prediction for H264. Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (integer)`` - Specify the offset that should be added to the luma quantization - parameter to determine the chroma quantization parameter. Applicable - to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` - Cyclic intra macroblock refresh. This is the number of continuous - macroblocks refreshed every frame. Each frame a successive set of - macroblocks is refreshed until the cycle completes and starts from - the top of the frame. Applicable to H264, H263 and MPEG4 encoder. - -``V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (boolean)`` - Frame level rate control enable. If this control is disabled then - the quantization parameter for each frame type is constant and set - with appropriate controls (e.g. - ``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP``). If frame rate control is - enabled then quantization parameter is adjusted to meet the chosen - bitrate. Minimum and maximum value for the quantization parameter - can be set with appropriate controls (e.g. - ``V4L2_CID_MPEG_VIDEO_H263_MIN_QP``). Applicable to encoders. - -``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (boolean)`` - Macroblock level rate control enable. Applicable to the MPEG4 and - H264 encoders. - -``V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (boolean)`` - Quarter pixel motion estimation for MPEG4. Applicable to the MPEG4 - encoder. - -``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for H263. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_H263_MIN_QP (integer)`` - Minimum quantization parameter for H263. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_H263_MAX_QP (integer)`` - Maximum quantization parameter for H263. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (integer)`` - Quantization parameter for an P frame for H263. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (integer)`` - Quantization parameter for an B frame for H263. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for H264. Valid range: from 0 - to 51. - -``V4L2_CID_MPEG_VIDEO_H264_MIN_QP (integer)`` - Minimum quantization parameter for H264. Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_H264_MAX_QP (integer)`` - Maximum quantization parameter for H264. Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (integer)`` - Quantization parameter for an P frame for H264. Valid range: from 0 - to 51. - -``V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (integer)`` - Quantization parameter for an B frame for H264. Valid range: from 0 - to 51. - -``V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for MPEG4. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (integer)`` - Minimum quantization parameter for MPEG4. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (integer)`` - Maximum quantization parameter for MPEG4. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (integer)`` - Quantization parameter for an P frame for MPEG4. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (integer)`` - Quantization parameter for an B frame for MPEG4. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_VBV_SIZE (integer)`` - The Video Buffer Verifier size in kilobytes, it is used as a - limitation of frame skip. The VBV is defined in the standard as a - mean to verify that the produced stream will be successfully - decoded. The standard describes it as "Part of a hypothetical - decoder that is conceptually connected to the output of the encoder. - Its purpose is to provide a constraint on the variability of the - data rate that an encoder or editing process may produce.". - Applicable to the MPEG1, MPEG2, MPEG4 encoders. - -.. _v4l2-mpeg-video-vbv-delay: - -``V4L2_CID_MPEG_VIDEO_VBV_DELAY (integer)`` - Sets the initial delay in milliseconds for VBV buffer control. - -.. _v4l2-mpeg-video-hor-search-range: - -``V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (integer)`` - Horizontal search range defines maximum horizontal search area in - pixels to search and match for the present Macroblock (MB) in the - reference picture. This V4L2 control macro is used to set horizontal - search range for motion estimation module in video encoder. - -.. _v4l2-mpeg-video-vert-search-range: - -``V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (integer)`` - Vertical search range defines maximum vertical search area in pixels - to search and match for the present Macroblock (MB) in the reference - picture. This V4L2 control macro is used to set vertical search - range for motion estimation module in video encoder. - -.. _v4l2-mpeg-video-force-key-frame: - -``V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (button)`` - Force a key frame for the next queued buffer. Applicable to - encoders. This is a general, codec-agnostic keyframe control. - -``V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (integer)`` - The Coded Picture Buffer size in kilobytes, it is used as a - limitation of frame skip. The CPB is defined in the H264 standard as - a mean to verify that the produced stream will be successfully - decoded. Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (integer)`` - Period between I-frames in the open GOP for H264. In case of an open - GOP this is the period between two I-frames. The period between IDR - (Instantaneous Decoding Refresh) frames is taken from the GOP_SIZE - control. An IDR frame, which stands for Instantaneous Decoding - Refresh is an I-frame after which no prior frames are referenced. - This means that a stream can be restarted from an IDR frame without - the need to store or decode any previous frames. Applicable to the - H264 encoder. - -.. _v4l2-mpeg-video-header-mode: - -``V4L2_CID_MPEG_VIDEO_HEADER_MODE`` - (enum) - -enum v4l2_mpeg_video_header_mode - - Determines whether the header is returned as the first buffer or is - it returned together with the first frame. Applicable to encoders. - Possible values are: - - - -.. tabularcolumns:: |p{10.3cm}|p{7.2cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE`` - - The stream header is returned separately in the first buffer. - * - ``V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME`` - - The stream header is returned together with the first encoded - frame. - - - -``V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (boolean)`` - Repeat the video sequence headers. Repeating these headers makes - random access to the video stream easier. Applicable to the MPEG1, 2 - and 4 encoder. - -``V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (boolean)`` - Enabled the deblocking post processing filter for MPEG4 decoder. - Applicable to the MPEG4 decoder. - -``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_RES (integer)`` - vop_time_increment_resolution value for MPEG4. Applicable to the - MPEG4 encoder. - -``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_INC (integer)`` - vop_time_increment value for MPEG4. Applicable to the MPEG4 - encoder. - -``V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING (boolean)`` - Enable generation of frame packing supplemental enhancement - information in the encoded bitstream. The frame packing SEI message - contains the arrangement of L and R planes for 3D viewing. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0 (boolean)`` - Sets current frame as frame0 in frame packing SEI. Applicable to the - H264 encoder. - -.. _v4l2-mpeg-video-h264-sei-fp-arrangement-type: - -``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE`` - (enum) - -enum v4l2_mpeg_video_h264_sei_fp_arrangement_type - - Frame packing arrangement type for H264 SEI. Applicable to the H264 - encoder. Possible values are: - -.. tabularcolumns:: |p{12cm}|p{5.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_CHEKERBOARD`` - - Pixels are alternatively from L and R. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_COLUMN`` - - L and R are interlaced by column. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_ROW`` - - L and R are interlaced by row. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE`` - - L is on the left, R on the right. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM`` - - L is on top, R on bottom. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL`` - - One view per frame. - - - -``V4L2_CID_MPEG_VIDEO_H264_FMO (boolean)`` - Enables flexible macroblock ordering in the encoded bitstream. It is - a technique used for restructuring the ordering of macroblocks in - pictures. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-fmo-map-type: - -``V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE`` - (enum) - -enum v4l2_mpeg_video_h264_fmo_map_type - - When using FMO, the map type divides the image in different scan - patterns of macroblocks. Applicable to the H264 encoder. Possible - values are: - -.. tabularcolumns:: |p{12.5cm}|p{5.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES`` - - Slices are interleaved one after other with macroblocks in run - length order. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES`` - - Scatters the macroblocks based on a mathematical function known to - both encoder and decoder. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_FOREGROUND_WITH_LEFT_OVER`` - - Macroblocks arranged in rectangular areas or regions of interest. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_BOX_OUT`` - - Slice groups grow in a cyclic way from centre to outwards. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN`` - - Slice groups grow in raster scan pattern from left to right. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN`` - - Slice groups grow in wipe scan pattern from top to bottom. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_EXPLICIT`` - - User defined map type. - - - -``V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP (integer)`` - Number of slice groups in FMO. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-fmo-change-direction: - -``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION`` - (enum) - -enum v4l2_mpeg_video_h264_fmo_change_dir - - Specifies a direction of the slice group change for raster and wipe - maps. Applicable to the H264 encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT`` - - Raster scan or wipe right. - * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT`` - - Reverse raster scan or wipe left. - - - -``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE (integer)`` - Specifies the size of the first slice group for raster and wipe map. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH (integer)`` - Specifies the number of consecutive macroblocks for the interleaved - map. Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_ASO (boolean)`` - Enables arbitrary slice ordering in encoded bitstream. Applicable to - the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER (integer)`` - Specifies the slice order in ASO. Applicable to the H264 encoder. - The supplied 32-bit integer is interpreted as follows (bit 0 = least - significant bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:15 - - Slice ID - * - Bit 16:32 - - Slice position or order - - - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING (boolean)`` - Enables H264 hierarchical coding. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-hierarchical-coding-type: - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE`` - (enum) - -enum v4l2_mpeg_video_h264_hierarchical_coding_type - - Specifies the hierarchical coding type. Applicable to the H264 - encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B`` - - Hierarchical B coding. - * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P`` - - Hierarchical P coding. - - - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (integer)`` - Specifies the number of hierarchical coding layers. Applicable to - the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (integer)`` - Specifies a user defined QP for each layer. Applicable to the H264 - encoder. The supplied 32-bit integer is interpreted as follows (bit - 0 = least significant bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:15 - - QP value - * - Bit 16:32 - - Layer number - - - -.. _v4l2-mpeg-mpeg2: - -``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (struct)`` - Specifies the slice parameters (as extracted from the bitstream) for the - associated MPEG-2 slice data. This includes the necessary parameters for - configuring a stateless hardware decoding pipeline for MPEG-2. - The bitstream parameters are defined according to :ref:`mpeg2part2`. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_mpeg2_slice_params - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_mpeg2_slice_params - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u32 - - ``bit_size`` - - Size (in bits) of the current slice data. - * - __u32 - - ``data_bit_offset`` - - Offset (in bits) to the video data in the current slice data. - * - struct :c:type:`v4l2_mpeg2_sequence` - - ``sequence`` - - Structure with MPEG-2 sequence metadata, merging relevant fields from - the sequence header and sequence extension parts of the bitstream. - * - struct :c:type:`v4l2_mpeg2_picture` - - ``picture`` - - Structure with MPEG-2 picture metadata, merging relevant fields from - the picture header and picture coding extension parts of the bitstream. - * - __u64 - - ``backward_ref_ts`` - - Timestamp of the V4L2 capture buffer to use as backward reference, used - with B-coded and P-coded frames. The timestamp refers to the - ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the - :c:func:`v4l2_timeval_to_ns()` function to convert the struct - :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. - * - __u64 - - ``forward_ref_ts`` - - Timestamp for the V4L2 capture buffer to use as forward reference, used - with B-coded frames. The timestamp refers to the ``timestamp`` field in - struct :c:type:`v4l2_buffer`. Use the :c:func:`v4l2_timeval_to_ns()` - function to convert the struct :c:type:`timeval` in struct - :c:type:`v4l2_buffer` to a __u64. - * - __u32 - - ``quantiser_scale_code`` - - Code used to determine the quantization scale to use for the IDCT. - -.. c:type:: v4l2_mpeg2_sequence - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_mpeg2_sequence - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u16 - - ``horizontal_size`` - - The width of the displayable part of the frame's luminance component. - * - __u16 - - ``vertical_size`` - - The height of the displayable part of the frame's luminance component. - * - __u32 - - ``vbv_buffer_size`` - - Used to calculate the required size of the video buffering verifier, - defined (in bits) as: 16 * 1024 * vbv_buffer_size. - * - __u16 - - ``profile_and_level_indication`` - - The current profile and level indication as extracted from the - bitstream. - * - __u8 - - ``progressive_sequence`` - - Indication that all the frames for the sequence are progressive instead - of interlaced. - * - __u8 - - ``chroma_format`` - - The chrominance sub-sampling format (1: 4:2:0, 2: 4:2:2, 3: 4:4:4). - -.. c:type:: v4l2_mpeg2_picture - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_mpeg2_picture - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``picture_coding_type`` - - Picture coding type for the frame covered by the current slice - (V4L2_MPEG2_PICTURE_CODING_TYPE_I, V4L2_MPEG2_PICTURE_CODING_TYPE_P or - V4L2_MPEG2_PICTURE_CODING_TYPE_B). - * - __u8 - - ``f_code[2][2]`` - - Motion vector codes. - * - __u8 - - ``intra_dc_precision`` - - Precision of Discrete Cosine transform (0: 8 bits precision, - 1: 9 bits precision, 2: 10 bits precision, 3: 11 bits precision). - * - __u8 - - ``picture_structure`` - - Picture structure (1: interlaced top field, 2: interlaced bottom field, - 3: progressive frame). - * - __u8 - - ``top_field_first`` - - If set to 1 and interlaced stream, top field is output first. - * - __u8 - - ``frame_pred_frame_dct`` - - If set to 1, only frame-DCT and frame prediction are used. - * - __u8 - - ``concealment_motion_vectors`` - - If set to 1, motion vectors are coded for intra macroblocks. - * - __u8 - - ``q_scale_type`` - - This flag affects the inverse quantization process. - * - __u8 - - ``intra_vlc_format`` - - This flag affects the decoding of transform coefficient data. - * - __u8 - - ``alternate_scan`` - - This flag affects the decoding of transform coefficient data. - * - __u8 - - ``repeat_first_field`` - - This flag affects the decoding process of progressive frames. - * - __u16 - - ``progressive_frame`` - - Indicates whether the current frame is progressive. - -``V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION (struct)`` - Specifies quantization matrices (as extracted from the bitstream) for the - associated MPEG-2 slice data. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_mpeg2_quantization - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_mpeg2_quantization - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``load_intra_quantiser_matrix`` - - One bit to indicate whether to load the ``intra_quantiser_matrix`` data. - * - __u8 - - ``load_non_intra_quantiser_matrix`` - - One bit to indicate whether to load the ``non_intra_quantiser_matrix`` - data. - * - __u8 - - ``load_chroma_intra_quantiser_matrix`` - - One bit to indicate whether to load the - ``chroma_intra_quantiser_matrix`` data, only relevant for non-4:2:0 YUV - formats. - * - __u8 - - ``load_chroma_non_intra_quantiser_matrix`` - - One bit to indicate whether to load the - ``chroma_non_intra_quantiser_matrix`` data, only relevant for non-4:2:0 - YUV formats. - * - __u8 - - ``intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for intra-coded frames, in zigzag - scanning order. It is relevant for both luma and chroma components, - although it can be superseded by the chroma-specific matrix for - non-4:2:0 YUV formats. - * - __u8 - - ``non_intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for non-intra-coded frames, in - zigzag scanning order. It is relevant for both luma and chroma - components, although it can be superseded by the chroma-specific matrix - for non-4:2:0 YUV formats. - * - __u8 - - ``chroma_intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for the chominance component of - intra-coded frames, in zigzag scanning order. Only relevant for - non-4:2:0 YUV formats. - * - __u8 - - ``chroma_non_intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for the chrominance component of - non-intra-coded frames, in zigzag scanning order. Only relevant for - non-4:2:0 YUV formats. - -MFC 5.1 MPEG Controls ---------------------- - -The following MPEG class controls deal with MPEG decoding and encoding -settings that are specific to the Multi Format Codec 5.1 device present -in the S5P family of SoCs by Samsung. - - -.. _mfc51-control-id: - -MFC 5.1 Control IDs -^^^^^^^^^^^^^^^^^^^ - -``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (boolean)`` - If the display delay is enabled then the decoder is forced to return - a CAPTURE buffer (decoded frame) after processing a certain number - of OUTPUT buffers. The delay can be set through - ``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY``. This - feature can be used for example for generating thumbnails of videos. - Applicable to the H264 decoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (integer)`` - Display delay value for H264 decoder. The decoder is forced to - return a decoded frame after the set 'display delay' number of - frames. If this number is low it may result in frames returned out - of dispaly order, in addition the hardware may still be using the - returned buffer as a reference picture for subsequent frames. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (integer)`` - The number of reference pictures used for encoding a P picture. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_PADDING (boolean)`` - Padding enable in the encoder - use a color instead of repeating - border pixels. Applicable to encoders. - -``V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (integer)`` - Padding color in the encoder. Applicable to encoders. The supplied - 32-bit integer is interpreted as follows (bit 0 = least significant - bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:7 - - V chrominance information - * - Bit 8:15 - - U chrominance information - * - Bit 16:23 - - Y luminance information - * - Bit 24:31 - - Must be zero. - - - -``V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (integer)`` - Reaction coefficient for MFC rate control. Applicable to encoders. - - .. note:: - - #. Valid only when the frame level RC is enabled. - - #. For tight CBR, this field must be small (ex. 2 ~ 10). For - VBR, this field must be large (ex. 100 ~ 1000). - - #. It is not recommended to use the greater number than - FRAME_RATE * (10^9 / BIT_RATE). - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (boolean)`` - Adaptive rate control for dark region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (boolean)`` - Adaptive rate control for smooth region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (boolean)`` - Adaptive rate control for static region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (boolean)`` - Adaptive rate control for activity region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -.. _v4l2-mpeg-mfc51-video-frame-skip-mode: - -``V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE`` - (enum) - -enum v4l2_mpeg_mfc51_video_frame_skip_mode - - Indicates in what conditions the encoder should skip frames. If - encoding a frame would cause the encoded stream to be larger then a - chosen data limit then the frame will be skipped. Possible values - are: - - -.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_DISABLED`` - - Frame skip mode is disabled. - * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_LEVEL_LIMIT`` - - Frame skip mode enabled and buffer limit is set by the chosen - level and is defined by the standard. - * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_BUF_LIMIT`` - - Frame skip mode enabled and buffer limit is set by the VBV - (MPEG1/2/4) or CPB (H264) buffer size control. - - - -``V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (integer)`` - Enable rate-control with fixed target bit. If this setting is - enabled, then the rate control logic of the encoder will calculate - the average bitrate for a GOP and keep it below or equal the set - bitrate target. Otherwise the rate control logic calculates the - overall average bitrate for the stream and keeps it below or equal - to the set bitrate. In the first case the average bitrate for the - whole stream will be smaller then the set bitrate. This is caused - because the average is calculated for smaller number of frames, on - the other hand enabling this setting will ensure that the stream - will meet tight bandwidth constraints. Applicable to encoders. - -.. _v4l2-mpeg-mfc51-video-force-frame-type: - -``V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE`` - (enum) - -enum v4l2_mpeg_mfc51_video_force_frame_type - - Force a frame type for the next queued buffer. Applicable to - encoders. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_DISABLED`` - - Forcing a specific frame type disabled. - * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_I_FRAME`` - - Force an I-frame. - * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_NOT_CODED`` - - Force a non-coded frame. - - - - -CX2341x MPEG Controls ---------------------- - -The following MPEG class controls deal with MPEG encoding settings that -are specific to the Conexant CX23415 and CX23416 MPEG encoding chips. - - -.. _cx2341x-control-id: - -CX2341x Control IDs -^^^^^^^^^^^^^^^^^^^ - -.. _v4l2-mpeg-cx2341x-video-spatial-filter-mode: - -``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_spatial_filter_mode - - Sets the Spatial Filter mode (default ``MANUAL``). Possible values - are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL`` - - Choose the filter manually - * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO`` - - Choose the filter automatically - - - -``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (integer (0-15))`` - The setting for the Spatial Filter. 0 = off, 15 = maximum. (Default - is 0.) - -.. _luma-spatial-filter-type: - -``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type - - Select the algorithm to use for the Luma Spatial Filter (default - ``1D_HOR``). Possible values: - - - -.. tabularcolumns:: |p{14.5cm}|p{3.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF`` - - No filter - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR`` - - One-dimensional horizontal - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT`` - - One-dimensional vertical - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE`` - - Two-dimensional separable - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE`` - - Two-dimensional symmetrical non-separable - - - -.. _chroma-spatial-filter-type: - -``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type - - Select the algorithm for the Chroma Spatial Filter (default - ``1D_HOR``). Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF`` - - No filter - * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR`` - - One-dimensional horizontal - - - -.. _v4l2-mpeg-cx2341x-video-temporal-filter-mode: - -``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_temporal_filter_mode - - Sets the Temporal Filter mode (default ``MANUAL``). Possible values - are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL`` - - Choose the filter manually - * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO`` - - Choose the filter automatically - - - -``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (integer (0-31))`` - The setting for the Temporal Filter. 0 = off, 31 = maximum. (Default - is 8 for full-scale capturing and 0 for scaled capturing.) - -.. _v4l2-mpeg-cx2341x-video-median-filter-type: - -``V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_median_filter_type - - Median Filter Type (default ``OFF``). Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF`` - - No filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR`` - - Horizontal filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT`` - - Vertical filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT`` - - Horizontal and vertical filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG`` - - Diagonal filter - - - -``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` - Threshold above which the luminance median filter is enabled - (default 0) - -``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (integer (0-255))`` - Threshold below which the luminance median filter is enabled - (default 255) - -``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` - Threshold above which the chroma median filter is enabled (default - 0) - -``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (integer (0-255))`` - Threshold below which the chroma median filter is enabled (default - 255) - -``V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (boolean)`` - The CX2341X MPEG encoder can insert one empty MPEG-2 PES packet into - the stream between every four video frames. The packet size is 2048 - bytes, including the packet_start_code_prefix and stream_id - fields. The stream_id is 0xBF (private stream 2). The payload - consists of 0x00 bytes, to be filled in by the application. 0 = do - not insert, 1 = insert packets. - - -VPX Control Reference ---------------------- - -The VPX controls include controls for encoding parameters of VPx video -codec. - - -.. _vpx-control-id: - -VPX Control IDs -^^^^^^^^^^^^^^^ - -.. _v4l2-vpx-num-partitions: - -``V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS`` - (enum) - -enum v4l2_vp8_num_partitions - - The number of token partitions to use in VP8 encoder. Possible - values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION`` - - 1 coefficient partition - * - ``V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS`` - - 2 coefficient partitions - * - ``V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS`` - - 4 coefficient partitions - * - ``V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS`` - - 8 coefficient partitions - - - -``V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (boolean)`` - Setting this prevents intra 4x4 mode in the intra mode decision. - -.. _v4l2-vpx-num-ref-frames: - -``V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES`` - (enum) - -enum v4l2_vp8_num_ref_frames - - The number of reference pictures for encoding P frames. Possible - values are: - -.. tabularcolumns:: |p{7.9cm}|p{9.6cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME`` - - Last encoded frame will be searched - * - ``V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME`` - - Two frames will be searched among the last encoded frame, the - golden frame and the alternate reference (altref) frame. The - encoder implementation will decide which two are chosen. - * - ``V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME`` - - The last encoded frame, the golden frame and the altref frame will - be searched. - - - -``V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (integer)`` - Indicates the loop filter level. The adjustment of the loop filter - level is done via a delta value against a baseline loop filter - value. - -``V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (integer)`` - This parameter affects the loop filter. Anything above zero weakens - the deblocking effect on the loop filter. - -``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (integer)`` - Sets the refresh period for the golden frame. The period is defined - in number of frames. For a value of 'n', every nth frame starting - from the first key frame will be taken as a golden frame. For eg. - for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden - frame refresh period is set as 4, the frames 0, 4, 8 etc will be - taken as the golden frames as frame 0 is always a key frame. - -.. _v4l2-vpx-golden-frame-sel: - -``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL`` - (enum) - -enum v4l2_vp8_golden_frame_sel - - Selects the golden frame for encoding. Possible values are: - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV`` - - Use the (n-2)th frame as a golden frame, current frame index being - 'n'. - * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD`` - - Use the previous specific frame indicated by - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD`` as a - golden frame. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (integer)`` - Minimum quantization parameter for VP8. - -``V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (integer)`` - Maximum quantization parameter for VP8. - -``V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for VP8. - -``V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (integer)`` - Quantization parameter for a P frame for VP8. - -.. _v4l2-mpeg-video-vp8-profile: - -``V4L2_CID_MPEG_VIDEO_VP8_PROFILE`` - (enum) - -enum v4l2_mpeg_video_vp8_profile - - This control allows selecting the profile for VP8 encoder. - This is also used to enumerate supported profiles by VP8 encoder or decoder. - Possible values are: - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_0`` - - Profile 0 - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_1`` - - Profile 1 - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_2`` - - Profile 2 - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` - - Profile 3 - -.. _v4l2-mpeg-video-vp9-profile: - -``V4L2_CID_MPEG_VIDEO_VP9_PROFILE`` - (enum) - -enum v4l2_mpeg_video_vp9_profile - - This control allows selecting the profile for VP9 encoder. - This is also used to enumerate supported profiles by VP9 encoder or decoder. - Possible values are: - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_0`` - - Profile 0 - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_1`` - - Profile 1 - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_2`` - - Profile 2 - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_3`` - - Profile 3 - - -High Efficiency Video Coding (HEVC/H.265) Control Reference ------------------------------------------------------------ - -The HEVC/H.265 controls include controls for encoding parameters of HEVC/H.265 -video codec. - - -.. _hevc-control-id: - -HEVC/H.265 Control IDs -^^^^^^^^^^^^^^^^^^^^^^ - -``V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (integer)`` - Minimum quantization parameter for HEVC. - Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (integer)`` - Maximum quantization parameter for HEVC. - Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for HEVC. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (integer)`` - Quantization parameter for a P frame for HEVC. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (integer)`` - Quantization parameter for a B frame for HEVC. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (boolean)`` - HIERARCHICAL_QP allows the host to specify the quantization parameter - values for each temporal layer through HIERARCHICAL_QP_LAYER. This is - valid only if HIERARCHICAL_CODING_LAYER is greater than 1. Setting the - control value to 1 enables setting of the QP values for the layers. - -.. _v4l2-hevc-hier-coding-type: - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE`` - (enum) - -enum v4l2_mpeg_video_hevc_hier_coding_type - - Selects the hierarchical coding type for encoding. Possible values are: - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B`` - - Use the B frame for hierarchical coding. - * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P`` - - Use the P frame for hierarchical coding. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (integer)`` - Selects the hierarchical coding layer. In normal encoding - (non-hierarchial coding), it should be zero. Possible values are [0, 6]. - 0 indicates HIERARCHICAL CODING LAYER 0, 1 indicates HIERARCHICAL CODING - LAYER 1 and so on. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 0. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 1. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 2. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 3. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 4. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 5. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 6. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -.. _v4l2-hevc-profile: - -``V4L2_CID_MPEG_VIDEO_HEVC_PROFILE`` - (enum) - -enum v4l2_mpeg_video_hevc_profile - - Select the desired profile for HEVC encoder. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN`` - - Main profile. - * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE`` - - Main still picture profile. - * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10`` - - Main 10 profile. - -.. raw:: latex - - \normalsize - - -.. _v4l2-hevc-level: - -``V4L2_CID_MPEG_VIDEO_HEVC_LEVEL`` - (enum) - -enum v4l2_mpeg_video_hevc_level - - Selects the desired level for HEVC encoder. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_1`` - - Level 1.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2`` - - Level 2.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1`` - - Level 2.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3`` - - Level 3.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1`` - - Level 3.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4`` - - Level 4.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1`` - - Level 4.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5`` - - Level 5.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1`` - - Level 5.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2`` - - Level 5.2 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6`` - - Level 6.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1`` - - Level 6.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2`` - - Level 6.2 - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (integer)`` - Indicates the number of evenly spaced subintervals, called ticks, within - one second. This is a 16 bit unsigned integer and has a maximum value up to - 0xffff and a minimum value of 1. - -.. _v4l2-hevc-tier: - -``V4L2_CID_MPEG_VIDEO_HEVC_TIER`` - (enum) - -enum v4l2_mpeg_video_hevc_tier - - TIER_FLAG specifies tiers information of the HEVC encoded picture. Tier - were made to deal with applications that differ in terms of maximum bit - rate. Setting the flag to 0 selects HEVC tier as Main tier and setting - this flag to 1 indicates High tier. High tier is for applications requiring - high bit rates. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_TIER_MAIN`` - - Main tier. - * - ``V4L2_MPEG_VIDEO_HEVC_TIER_HIGH`` - - High tier. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (integer)`` - Selects HEVC maximum coding unit depth. - -.. _v4l2-hevc-loop-filter-mode: - -``V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_video_hevc_loop_filter_mode - - Loop filter mode for HEVC encoder. Possible values are: - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{10.7cm}|p{6.3cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED`` - - Loop filter is disabled. - * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED`` - - Loop filter is enabled. - * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` - - Loop filter is disabled at the slice boundary. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (integer)`` - Selects HEVC loop filter beta offset. The valid range is [-6, +6]. - -``V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (integer)`` - Selects HEVC loop filter tc offset. The valid range is [-6, +6]. - -.. _v4l2-hevc-refresh-type: - -``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE`` - (enum) - -enum v4l2_mpeg_video_hevc_hier_refresh_type - - Selects refresh type for HEVC encoder. - Host has to specify the period into - V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{8.0cm}|p{9.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE`` - - Use the B frame for hierarchical coding. - * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA`` - - Use CRA (Clean Random Access Unit) picture encoding. - * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR`` - - Use IDR (Instantaneous Decoding Refresh) picture encoding. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (integer)`` - Selects the refresh period for HEVC encoder. - This specifies the number of I pictures between two CRA/IDR pictures. - This is valid only if REFRESH_TYPE is not 0. - -``V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (boolean)`` - Indicates HEVC lossless encoding. Setting it to 0 disables lossless - encoding. Setting it to 1 enables lossless encoding. - -``V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (boolean)`` - Indicates constant intra prediction for HEVC encoder. Specifies the - constrained intra prediction in which intra largest coding unit (LCU) - prediction is performed by using residual data and decoded samples of - neighboring intra LCU only. Setting the value to 1 enables constant intra - prediction and setting the value to 0 disables constant intra prediction. - -``V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (boolean)`` - Indicates wavefront parallel processing for HEVC encoder. Setting it to 0 - disables the feature and setting it to 1 enables the wavefront parallel - processing. - -``V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (boolean)`` - Setting the value to 1 enables combination of P and B frame for HEVC - encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (boolean)`` - Indicates temporal identifier for HEVC encoder which is enabled by - setting the value to 1. - -``V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (boolean)`` - Indicates bi-linear interpolation is conditionally used in the intra - prediction filtering process in the CVS when set to 1. Indicates bi-linear - interpolation is not used in the CVS when set to 0. - -``V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (integer)`` - Indicates maximum number of merge candidate motion vectors. - Values are from 0 to 4. - -``V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (boolean)`` - Indicates temporal motion vector prediction for HEVC encoder. Setting it to - 1 enables the prediction. Setting it to 0 disables the prediction. - -``V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (boolean)`` - Specifies if HEVC generates a stream with a size of the length field - instead of start code pattern. The size of the length field is configurable - through the V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD control. Setting - the value to 0 disables encoding without startcode pattern. Setting the - value to 1 will enables encoding without startcode pattern. - -.. _v4l2-hevc-size-of-length-field: - -``V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD`` -(enum) - -enum v4l2_mpeg_video_hevc_size_of_length_field - - Indicates the size of length field. - This is valid when encoding WITHOUT_STARTCODE_ENABLE is enabled. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{6.0cm}|p{11.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_0`` - - Generate start code pattern (Normal). - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_1`` - - Generate size of length field instead of start code pattern and length is 1. - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_2`` - - Generate size of length field instead of start code pattern and length is 2. - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_4`` - - Generate size of length field instead of start code pattern and length is 4. - -.. raw:: latex - - \normalsize - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (integer)`` - Indicates bit rate for hierarchical coding layer 0 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (integer)`` - Indicates bit rate for hierarchical coding layer 1 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (integer)`` - Indicates bit rate for hierarchical coding layer 2 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (integer)`` - Indicates bit rate for hierarchical coding layer 3 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (integer)`` - Indicates bit rate for hierarchical coding layer 4 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (integer)`` - Indicates bit rate for hierarchical coding layer 5 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (integer)`` - Indicates bit rate for hierarchical coding layer 6 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (integer)`` - Selects number of P reference pictures required for HEVC encoder. - P-Frame can use 1 or 2 frames for reference. - -``V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (integer)`` - Indicates whether to generate SPS and PPS at every IDR. Setting it to 0 - disables generating SPS and PPS at every IDR. Setting it to one enables - generating SPS and PPS at every IDR. - - -.. _camera-controls: - -Camera Control Reference -======================== - -The Camera class includes controls for mechanical (or equivalent -digital) features of a device such as controllable lenses or sensors. - - -.. _camera-control-id: - -Camera Control IDs ------------------- - -``V4L2_CID_CAMERA_CLASS (class)`` - The Camera class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -.. _v4l2-exposure-auto-type: - -``V4L2_CID_EXPOSURE_AUTO`` - (enum) - -enum v4l2_exposure_auto_type - - Enables automatic adjustments of the exposure time and/or iris - aperture. The effect of manual changes of the exposure time or iris - aperture while these features are enabled is undefined, drivers - should ignore such requests. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_EXPOSURE_AUTO`` - - Automatic exposure time, automatic iris aperture. - * - ``V4L2_EXPOSURE_MANUAL`` - - Manual exposure time, manual iris. - * - ``V4L2_EXPOSURE_SHUTTER_PRIORITY`` - - Manual exposure time, auto iris. - * - ``V4L2_EXPOSURE_APERTURE_PRIORITY`` - - Auto exposure time, manual iris. - - - -``V4L2_CID_EXPOSURE_ABSOLUTE (integer)`` - Determines the exposure time of the camera sensor. The exposure time - is limited by the frame interval. Drivers should interpret the - values as 100 µs units, where the value 1 stands for 1/10000th of a - second, 10000 for 1 second and 100000 for 10 seconds. - -``V4L2_CID_EXPOSURE_AUTO_PRIORITY (boolean)`` - When ``V4L2_CID_EXPOSURE_AUTO`` is set to ``AUTO`` or - ``APERTURE_PRIORITY``, this control determines if the device may - dynamically vary the frame rate. By default this feature is disabled - (0) and the frame rate must remain constant. - -``V4L2_CID_AUTO_EXPOSURE_BIAS (integer menu)`` - Determines the automatic exposure compensation, it is effective only - when ``V4L2_CID_EXPOSURE_AUTO`` control is set to ``AUTO``, - ``SHUTTER_PRIORITY`` or ``APERTURE_PRIORITY``. It is expressed in - terms of EV, drivers should interpret the values as 0.001 EV units, - where the value 1000 stands for +1 EV. - - Increasing the exposure compensation value is equivalent to - decreasing the exposure value (EV) and will increase the amount of - light at the image sensor. The camera performs the exposure - compensation by adjusting absolute exposure time and/or aperture. - -.. _v4l2-exposure-metering: - -``V4L2_CID_EXPOSURE_METERING`` - (enum) - -enum v4l2_exposure_metering - - Determines how the camera measures the amount of light available for - the frame exposure. Possible values are: - -.. tabularcolumns:: |p{8.5cm}|p{9.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_EXPOSURE_METERING_AVERAGE`` - - Use the light information coming from the entire frame and average - giving no weighting to any particular portion of the metered area. - * - ``V4L2_EXPOSURE_METERING_CENTER_WEIGHTED`` - - Average the light information coming from the entire frame giving - priority to the center of the metered area. - * - ``V4L2_EXPOSURE_METERING_SPOT`` - - Measure only very small area at the center of the frame. - * - ``V4L2_EXPOSURE_METERING_MATRIX`` - - A multi-zone metering. The light intensity is measured in several - points of the frame and the results are combined. The algorithm of - the zones selection and their significance in calculating the - final value is device dependent. - - - -``V4L2_CID_PAN_RELATIVE (integer)`` - This control turns the camera horizontally by the specified amount. - The unit is undefined. A positive value moves the camera to the - right (clockwise when viewed from above), a negative value to the - left. A value of zero does not cause motion. This is a write-only - control. - -``V4L2_CID_TILT_RELATIVE (integer)`` - This control turns the camera vertically by the specified amount. - The unit is undefined. A positive value moves the camera up, a - negative value down. A value of zero does not cause motion. This is - a write-only control. - -``V4L2_CID_PAN_RESET (button)`` - When this control is set, the camera moves horizontally to the - default position. - -``V4L2_CID_TILT_RESET (button)`` - When this control is set, the camera moves vertically to the default - position. - -``V4L2_CID_PAN_ABSOLUTE (integer)`` - This control turns the camera horizontally to the specified - position. Positive values move the camera to the right (clockwise - when viewed from above), negative values to the left. Drivers should - interpret the values as arc seconds, with valid values between -180 - * 3600 and +180 * 3600 inclusive. - -``V4L2_CID_TILT_ABSOLUTE (integer)`` - This control turns the camera vertically to the specified position. - Positive values move the camera up, negative values down. Drivers - should interpret the values as arc seconds, with valid values - between -180 * 3600 and +180 * 3600 inclusive. - -``V4L2_CID_FOCUS_ABSOLUTE (integer)`` - This control sets the focal point of the camera to the specified - position. The unit is undefined. Positive values set the focus - closer to the camera, negative values towards infinity. - -``V4L2_CID_FOCUS_RELATIVE (integer)`` - This control moves the focal point of the camera by the specified - amount. The unit is undefined. Positive values move the focus closer - to the camera, negative values towards infinity. This is a - write-only control. - -``V4L2_CID_FOCUS_AUTO (boolean)`` - Enables continuous automatic focus adjustments. The effect of manual - focus adjustments while this feature is enabled is undefined, - drivers should ignore such requests. - -``V4L2_CID_AUTO_FOCUS_START (button)`` - Starts single auto focus process. The effect of setting this control - when ``V4L2_CID_FOCUS_AUTO`` is set to ``TRUE`` (1) is undefined, - drivers should ignore such requests. - -``V4L2_CID_AUTO_FOCUS_STOP (button)`` - Aborts automatic focusing started with ``V4L2_CID_AUTO_FOCUS_START`` - control. It is effective only when the continuous autofocus is - disabled, that is when ``V4L2_CID_FOCUS_AUTO`` control is set to - ``FALSE`` (0). - -.. _v4l2-auto-focus-status: - -``V4L2_CID_AUTO_FOCUS_STATUS (bitmask)`` - The automatic focus status. This is a read-only control. - - Setting ``V4L2_LOCK_FOCUS`` lock bit of the ``V4L2_CID_3A_LOCK`` - control may stop updates of the ``V4L2_CID_AUTO_FOCUS_STATUS`` - control value. - -.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_AUTO_FOCUS_STATUS_IDLE`` - - Automatic focus is not active. - * - ``V4L2_AUTO_FOCUS_STATUS_BUSY`` - - Automatic focusing is in progress. - * - ``V4L2_AUTO_FOCUS_STATUS_REACHED`` - - Focus has been reached. - * - ``V4L2_AUTO_FOCUS_STATUS_FAILED`` - - Automatic focus has failed, the driver will not transition from - this state until another action is performed by an application. - - - -.. _v4l2-auto-focus-range: - -``V4L2_CID_AUTO_FOCUS_RANGE`` - (enum) - -enum v4l2_auto_focus_range - - Determines auto focus distance range for which lens may be adjusted. - -.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_AUTO_FOCUS_RANGE_AUTO`` - - The camera automatically selects the focus range. - * - ``V4L2_AUTO_FOCUS_RANGE_NORMAL`` - - Normal distance range, limited for best automatic focus - performance. - * - ``V4L2_AUTO_FOCUS_RANGE_MACRO`` - - Macro (close-up) auto focus. The camera will use its minimum - possible distance for auto focus. - * - ``V4L2_AUTO_FOCUS_RANGE_INFINITY`` - - The lens is set to focus on an object at infinite distance. - - - -``V4L2_CID_ZOOM_ABSOLUTE (integer)`` - Specify the objective lens focal length as an absolute value. The - zoom unit is driver-specific and its value should be a positive - integer. - -``V4L2_CID_ZOOM_RELATIVE (integer)`` - Specify the objective lens focal length relatively to the current - value. Positive values move the zoom lens group towards the - telephoto direction, negative values towards the wide-angle - direction. The zoom unit is driver-specific. This is a write-only - control. - -``V4L2_CID_ZOOM_CONTINUOUS (integer)`` - Move the objective lens group at the specified speed until it - reaches physical device limits or until an explicit request to stop - the movement. A positive value moves the zoom lens group towards the - telephoto direction. A value of zero stops the zoom lens group - movement. A negative value moves the zoom lens group towards the - wide-angle direction. The zoom speed unit is driver-specific. - -``V4L2_CID_IRIS_ABSOLUTE (integer)`` - This control sets the camera's aperture to the specified value. The - unit is undefined. Larger values open the iris wider, smaller values - close it. - -``V4L2_CID_IRIS_RELATIVE (integer)`` - This control modifies the camera's aperture by the specified amount. - The unit is undefined. Positive values open the iris one step - further, negative values close it one step further. This is a - write-only control. - -``V4L2_CID_PRIVACY (boolean)`` - Prevent video from being acquired by the camera. When this control - is set to ``TRUE`` (1), no image can be captured by the camera. - Common means to enforce privacy are mechanical obturation of the - sensor and firmware image processing, but the device is not - restricted to these methods. Devices that implement the privacy - control must support read access and may support write access. - -``V4L2_CID_BAND_STOP_FILTER (integer)`` - Switch the band-stop filter of a camera sensor on or off, or specify - its strength. Such band-stop filters can be used, for example, to - filter out the fluorescent light component. - -.. _v4l2-auto-n-preset-white-balance: - -``V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE`` - (enum) - -enum v4l2_auto_n_preset_white_balance - - Sets white balance to automatic, manual or a preset. The presets - determine color temperature of the light as a hint to the camera for - white balance adjustments resulting in most accurate color - representation. The following white balance presets are listed in - order of increasing color temperature. - -.. tabularcolumns:: |p{7.0 cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_WHITE_BALANCE_MANUAL`` - - Manual white balance. - * - ``V4L2_WHITE_BALANCE_AUTO`` - - Automatic white balance adjustments. - * - ``V4L2_WHITE_BALANCE_INCANDESCENT`` - - White balance setting for incandescent (tungsten) lighting. It - generally cools down the colors and corresponds approximately to - 2500...3500 K color temperature range. - * - ``V4L2_WHITE_BALANCE_FLUORESCENT`` - - White balance preset for fluorescent lighting. It corresponds - approximately to 4000...5000 K color temperature. - * - ``V4L2_WHITE_BALANCE_FLUORESCENT_H`` - - With this setting the camera will compensate for fluorescent H - lighting. - * - ``V4L2_WHITE_BALANCE_HORIZON`` - - White balance setting for horizon daylight. It corresponds - approximately to 5000 K color temperature. - * - ``V4L2_WHITE_BALANCE_DAYLIGHT`` - - White balance preset for daylight (with clear sky). It corresponds - approximately to 5000...6500 K color temperature. - * - ``V4L2_WHITE_BALANCE_FLASH`` - - With this setting the camera will compensate for the flash light. - It slightly warms up the colors and corresponds roughly to - 5000...5500 K color temperature. - * - ``V4L2_WHITE_BALANCE_CLOUDY`` - - White balance preset for moderately overcast sky. This option - corresponds approximately to 6500...8000 K color temperature - range. - * - ``V4L2_WHITE_BALANCE_SHADE`` - - White balance preset for shade or heavily overcast sky. It - corresponds approximately to 9000...10000 K color temperature. - - - -.. _v4l2-wide-dynamic-range: - -``V4L2_CID_WIDE_DYNAMIC_RANGE (boolean)`` - Enables or disables the camera's wide dynamic range feature. This - feature allows to obtain clear images in situations where intensity - of the illumination varies significantly throughout the scene, i.e. - there are simultaneously very dark and very bright areas. It is most - commonly realized in cameras by combining two subsequent frames with - different exposure times. [#f1]_ - -.. _v4l2-image-stabilization: - -``V4L2_CID_IMAGE_STABILIZATION (boolean)`` - Enables or disables image stabilization. - -``V4L2_CID_ISO_SENSITIVITY (integer menu)`` - Determines ISO equivalent of an image sensor indicating the sensor's - sensitivity to light. The numbers are expressed in arithmetic scale, - as per :ref:`iso12232` standard, where doubling the sensor - sensitivity is represented by doubling the numerical ISO value. - Applications should interpret the values as standard ISO values - multiplied by 1000, e.g. control value 800 stands for ISO 0.8. - Drivers will usually support only a subset of standard ISO values. - The effect of setting this control while the - ``V4L2_CID_ISO_SENSITIVITY_AUTO`` control is set to a value other - than ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` is undefined, drivers - should ignore such requests. - -.. _v4l2-iso-sensitivity-auto-type: - -``V4L2_CID_ISO_SENSITIVITY_AUTO`` - (enum) - -enum v4l2_iso_sensitivity_type - - Enables or disables automatic ISO sensitivity adjustments. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` - - Manual ISO sensitivity. - * - ``V4L2_CID_ISO_SENSITIVITY_AUTO`` - - Automatic ISO sensitivity adjustments. - - - -.. _v4l2-scene-mode: - -``V4L2_CID_SCENE_MODE`` - (enum) - -enum v4l2_scene_mode - - This control allows to select scene programs as the camera automatic - modes optimized for common shooting scenes. Within these modes the - camera determines best exposure, aperture, focusing, light metering, - white balance and equivalent sensitivity. The controls of those - parameters are influenced by the scene mode control. An exact - behavior in each mode is subject to the camera specification. - - When the scene mode feature is not used, this control should be set - to ``V4L2_SCENE_MODE_NONE`` to make sure the other possibly related - controls are accessible. The following scene programs are defined: - -.. tabularcolumns:: |p{6.0cm}|p{11.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_SCENE_MODE_NONE`` - - The scene mode feature is disabled. - * - ``V4L2_SCENE_MODE_BACKLIGHT`` - - Backlight. Compensates for dark shadows when light is coming from - behind a subject, also by automatically turning on the flash. - * - ``V4L2_SCENE_MODE_BEACH_SNOW`` - - Beach and snow. This mode compensates for all-white or bright - scenes, which tend to look gray and low contrast, when camera's - automatic exposure is based on an average scene brightness. To - compensate, this mode automatically slightly overexposes the - frames. The white balance may also be adjusted to compensate for - the fact that reflected snow looks bluish rather than white. - * - ``V4L2_SCENE_MODE_CANDLELIGHT`` - - Candle light. The camera generally raises the ISO sensitivity and - lowers the shutter speed. This mode compensates for relatively - close subject in the scene. The flash is disabled in order to - preserve the ambiance of the light. - * - ``V4L2_SCENE_MODE_DAWN_DUSK`` - - Dawn and dusk. Preserves the colors seen in low natural light - before dusk and after down. The camera may turn off the flash, and - automatically focus at infinity. It will usually boost saturation - and lower the shutter speed. - * - ``V4L2_SCENE_MODE_FALL_COLORS`` - - Fall colors. Increases saturation and adjusts white balance for - color enhancement. Pictures of autumn leaves get saturated reds - and yellows. - * - ``V4L2_SCENE_MODE_FIREWORKS`` - - Fireworks. Long exposure times are used to capture the expanding - burst of light from a firework. The camera may invoke image - stabilization. - * - ``V4L2_SCENE_MODE_LANDSCAPE`` - - Landscape. The camera may choose a small aperture to provide deep - depth of field and long exposure duration to help capture detail - in dim light conditions. The focus is fixed at infinity. Suitable - for distant and wide scenery. - * - ``V4L2_SCENE_MODE_NIGHT`` - - Night, also known as Night Landscape. Designed for low light - conditions, it preserves detail in the dark areas without blowing - out bright objects. The camera generally sets itself to a - medium-to-high ISO sensitivity, with a relatively long exposure - time, and turns flash off. As such, there will be increased image - noise and the possibility of blurred image. - * - ``V4L2_SCENE_MODE_PARTY_INDOOR`` - - Party and indoor. Designed to capture indoor scenes that are lit - by indoor background lighting as well as the flash. The camera - usually increases ISO sensitivity, and adjusts exposure for the - low light conditions. - * - ``V4L2_SCENE_MODE_PORTRAIT`` - - Portrait. The camera adjusts the aperture so that the depth of - field is reduced, which helps to isolate the subject against a - smooth background. Most cameras recognize the presence of faces in - the scene and focus on them. The color hue is adjusted to enhance - skin tones. The intensity of the flash is often reduced. - * - ``V4L2_SCENE_MODE_SPORTS`` - - Sports. Significantly increases ISO and uses a fast shutter speed - to freeze motion of rapidly-moving subjects. Increased image noise - may be seen in this mode. - * - ``V4L2_SCENE_MODE_SUNSET`` - - Sunset. Preserves deep hues seen in sunsets and sunrises. It bumps - up the saturation. - * - ``V4L2_SCENE_MODE_TEXT`` - - Text. It applies extra contrast and sharpness, it is typically a - black-and-white mode optimized for readability. Automatic focus - may be switched to close-up mode and this setting may also involve - some lens-distortion correction. - - - -``V4L2_CID_3A_LOCK (bitmask)`` - This control locks or unlocks the automatic focus, exposure and - white balance. The automatic adjustments can be paused independently - by setting the corresponding lock bit to 1. The camera then retains - the settings until the lock bit is cleared. The following lock bits - are defined: - - When a given algorithm is not enabled, drivers should ignore - requests to lock it and should return no error. An example might be - an application setting bit ``V4L2_LOCK_WHITE_BALANCE`` when the - ``V4L2_CID_AUTO_WHITE_BALANCE`` control is set to ``FALSE``. The - value of this control may be changed by exposure, white balance or - focus controls. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_LOCK_EXPOSURE`` - - Automatic exposure adjustments lock. - * - ``V4L2_LOCK_WHITE_BALANCE`` - - Automatic white balance adjustments lock. - * - ``V4L2_LOCK_FOCUS`` - - Automatic focus lock. - - - -``V4L2_CID_PAN_SPEED (integer)`` - This control turns the camera horizontally at the specific speed. - The unit is undefined. A positive value moves the camera to the - right (clockwise when viewed from above), a negative value to the - left. A value of zero stops the motion if one is in progress and has - no effect otherwise. - -``V4L2_CID_TILT_SPEED (integer)`` - This control turns the camera vertically at the specified speed. The - unit is undefined. A positive value moves the camera up, a negative - value down. A value of zero stops the motion if one is in progress - and has no effect otherwise. - - -.. _fm-tx-controls: - -FM Transmitter Control Reference -================================ - -The FM Transmitter (FM_TX) class includes controls for common features -of FM transmissions capable devices. Currently this class includes -parameters for audio compression, pilot tone generation, audio deviation -limiter, RDS transmission and tuning power features. - - -.. _fm-tx-control-id: - -FM_TX Control IDs ------------------ - -``V4L2_CID_FM_TX_CLASS (class)`` - The FM_TX class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_RDS_TX_DEVIATION (integer)`` - Configures RDS signal frequency deviation level in Hz. The range and - step are driver-specific. - -``V4L2_CID_RDS_TX_PI (integer)`` - Sets the RDS Programme Identification field for transmission. - -``V4L2_CID_RDS_TX_PTY (integer)`` - Sets the RDS Programme Type field for transmission. This encodes up - to 31 pre-defined programme types. - -``V4L2_CID_RDS_TX_PS_NAME (string)`` - Sets the Programme Service name (PS_NAME) for transmission. It is - intended for static display on a receiver. It is the primary aid to - listeners in programme service identification and selection. In - Annex E of :ref:`iec62106`, the RDS specification, there is a full - description of the correct character encoding for Programme Service - name strings. Also from RDS specification, PS is usually a single - eight character text. However, it is also possible to find receivers - which can scroll strings sized as 8 x N characters. So, this control - must be configured with steps of 8 characters. The result is it must - always contain a string with size multiple of 8. - -``V4L2_CID_RDS_TX_RADIO_TEXT (string)`` - Sets the Radio Text info for transmission. It is a textual - description of what is being broadcasted. RDS Radio Text can be - applied when broadcaster wishes to transmit longer PS names, - programme-related information or any other text. In these cases, - RadioText should be used in addition to ``V4L2_CID_RDS_TX_PS_NAME``. - The encoding for Radio Text strings is also fully described in Annex - E of :ref:`iec62106`. The length of Radio Text strings depends on - which RDS Block is being used to transmit it, either 32 (2A block) - or 64 (2B block). However, it is also possible to find receivers - which can scroll strings sized as 32 x N or 64 x N characters. So, - this control must be configured with steps of 32 or 64 characters. - The result is it must always contain a string with size multiple of - 32 or 64. - -``V4L2_CID_RDS_TX_MONO_STEREO (boolean)`` - Sets the Mono/Stereo bit of the Decoder Identification code. If set, - then the audio was recorded as stereo. - -``V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (boolean)`` - Sets the - `Artificial Head `__ - bit of the Decoder Identification code. If set, then the audio was - recorded using an artificial head. - -``V4L2_CID_RDS_TX_COMPRESSED (boolean)`` - Sets the Compressed bit of the Decoder Identification code. If set, - then the audio is compressed. - -``V4L2_CID_RDS_TX_DYNAMIC_PTY (boolean)`` - Sets the Dynamic PTY bit of the Decoder Identification code. If set, - then the PTY code is dynamically switched. - -``V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (boolean)`` - If set, then a traffic announcement is in progress. - -``V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (boolean)`` - If set, then the tuned programme carries traffic announcements. - -``V4L2_CID_RDS_TX_MUSIC_SPEECH (boolean)`` - If set, then this channel broadcasts music. If cleared, then it - broadcasts speech. If the transmitter doesn't make this distinction, - then it should be set. - -``V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (boolean)`` - If set, then transmit alternate frequencies. - -``V4L2_CID_RDS_TX_ALT_FREQS (__u32 array)`` - The alternate frequencies in kHz units. The RDS standard allows for - up to 25 frequencies to be defined. Drivers may support fewer - frequencies so check the array size. - -``V4L2_CID_AUDIO_LIMITER_ENABLED (boolean)`` - Enables or disables the audio deviation limiter feature. The limiter - is useful when trying to maximize the audio volume, minimize - receiver-generated distortion and prevent overmodulation. - -``V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (integer)`` - Sets the audio deviation limiter feature release time. Unit is in - useconds. Step and range are driver-specific. - -``V4L2_CID_AUDIO_LIMITER_DEVIATION (integer)`` - Configures audio frequency deviation level in Hz. The range and step - are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_ENABLED (boolean)`` - Enables or disables the audio compression feature. This feature - amplifies signals below the threshold by a fixed gain and compresses - audio signals above the threshold by the ratio of Threshold/(Gain + - Threshold). - -``V4L2_CID_AUDIO_COMPRESSION_GAIN (integer)`` - Sets the gain for audio compression feature. It is a dB value. The - range and step are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (integer)`` - Sets the threshold level for audio compression freature. It is a dB - value. The range and step are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (integer)`` - Sets the attack time for audio compression feature. It is a useconds - value. The range and step are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (integer)`` - Sets the release time for audio compression feature. It is a - useconds value. The range and step are driver-specific. - -``V4L2_CID_PILOT_TONE_ENABLED (boolean)`` - Enables or disables the pilot tone generation feature. - -``V4L2_CID_PILOT_TONE_DEVIATION (integer)`` - Configures pilot tone frequency deviation level. Unit is in Hz. The - range and step are driver-specific. - -``V4L2_CID_PILOT_TONE_FREQUENCY (integer)`` - Configures pilot tone frequency value. Unit is in Hz. The range and - step are driver-specific. - -``V4L2_CID_TUNE_PREEMPHASIS`` - (enum) - -enum v4l2_preemphasis - - Configures the pre-emphasis value for broadcasting. A pre-emphasis - filter is applied to the broadcast to accentuate the high audio - frequencies. Depending on the region, a time constant of either 50 - or 75 useconds is used. The enum v4l2_preemphasis defines possible - values for pre-emphasis. Here they are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_PREEMPHASIS_DISABLED`` - - No pre-emphasis is applied. - * - ``V4L2_PREEMPHASIS_50_uS`` - - A pre-emphasis of 50 uS is used. - * - ``V4L2_PREEMPHASIS_75_uS`` - - A pre-emphasis of 75 uS is used. - - - -``V4L2_CID_TUNE_POWER_LEVEL (integer)`` - Sets the output power level for signal transmission. Unit is in - dBuV. Range and step are driver-specific. - -``V4L2_CID_TUNE_ANTENNA_CAPACITOR (integer)`` - This selects the value of antenna tuning capacitor manually or - automatically if set to zero. Unit, range and step are - driver-specific. - -For more details about RDS specification, refer to :ref:`iec62106` -document, from CENELEC. - - -.. _flash-controls: - -Flash Control Reference -======================= - -The V4L2 flash controls are intended to provide generic access to flash -controller devices. Flash controller devices are typically used in -digital cameras. - -The interface can support both LED and xenon flash devices. As of -writing this, there is no xenon flash driver using this interface. - - -.. _flash-controls-use-cases: - -Supported use cases -------------------- - - -Unsynchronised LED flash (software strobe) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Unsynchronised LED flash is controlled directly by the host as the -sensor. The flash must be enabled by the host before the exposure of the -image starts and disabled once it ends. The host is fully responsible -for the timing of the flash. - -Example of such device: Nokia N900. - - -Synchronised LED flash (hardware strobe) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The synchronised LED flash is pre-programmed by the host (power and -timeout) but controlled by the sensor through a strobe signal from the -sensor to the flash. - -The sensor controls the flash duration and timing. This information -typically must be made available to the sensor. - - -LED flash as torch -^^^^^^^^^^^^^^^^^^ - -LED flash may be used as torch in conjunction with another use case -involving camera or individually. - - -.. _flash-control-id: - -Flash Control IDs -""""""""""""""""" - -``V4L2_CID_FLASH_CLASS (class)`` - The FLASH class descriptor. - -``V4L2_CID_FLASH_LED_MODE (menu)`` - Defines the mode of the flash LED, the high-power white LED attached - to the flash controller. Setting this control may not be possible in - presence of some faults. See V4L2_CID_FLASH_FAULT. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_FLASH_LED_MODE_NONE`` - - Off. - * - ``V4L2_FLASH_LED_MODE_FLASH`` - - Flash mode. - * - ``V4L2_FLASH_LED_MODE_TORCH`` - - Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY. - - - -``V4L2_CID_FLASH_STROBE_SOURCE (menu)`` - Defines the source of the flash LED strobe. - -.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_FLASH_STROBE_SOURCE_SOFTWARE`` - - The flash strobe is triggered by using the - V4L2_CID_FLASH_STROBE control. - * - ``V4L2_FLASH_STROBE_SOURCE_EXTERNAL`` - - The flash strobe is triggered by an external source. Typically - this is a sensor, which makes it possible to synchronises the - flash strobe start to exposure start. - - - -``V4L2_CID_FLASH_STROBE (button)`` - Strobe flash. Valid when V4L2_CID_FLASH_LED_MODE is set to - V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE - is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this - control may not be possible in presence of some faults. See - V4L2_CID_FLASH_FAULT. - -``V4L2_CID_FLASH_STROBE_STOP (button)`` - Stop flash strobe immediately. - -``V4L2_CID_FLASH_STROBE_STATUS (boolean)`` - Strobe status: whether the flash is strobing at the moment or not. - This is a read-only control. - -``V4L2_CID_FLASH_TIMEOUT (integer)`` - Hardware timeout for flash. The flash strobe is stopped after this - period of time has passed from the start of the strobe. - -``V4L2_CID_FLASH_INTENSITY (integer)`` - Intensity of the flash strobe when the flash LED is in flash mode - (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps (mA) - if possible. - -``V4L2_CID_FLASH_TORCH_INTENSITY (integer)`` - Intensity of the flash LED in torch mode - (V4L2_FLASH_LED_MODE_TORCH). The unit should be milliamps (mA) - if possible. Setting this control may not be possible in presence of - some faults. See V4L2_CID_FLASH_FAULT. - -``V4L2_CID_FLASH_INDICATOR_INTENSITY (integer)`` - Intensity of the indicator LED. The indicator LED may be fully - independent of the flash LED. The unit should be microamps (uA) if - possible. - -``V4L2_CID_FLASH_FAULT (bitmask)`` - Faults related to the flash. The faults tell about specific problems - in the flash chip itself or the LEDs attached to it. Faults may - prevent further use of some of the flash controls. In particular, - V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE - if the fault affects the flash LED. Exactly which faults have such - an effect is chip dependent. Reading the faults resets the control - and returns the chip to a usable state if possible. - -.. tabularcolumns:: |p{8.0cm}|p{9.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_FLASH_FAULT_OVER_VOLTAGE`` - - Flash controller voltage to the flash LED has exceeded the limit - specific to the flash controller. - * - ``V4L2_FLASH_FAULT_TIMEOUT`` - - The flash strobe was still on when the timeout set by the user --- - V4L2_CID_FLASH_TIMEOUT control --- has expired. Not all flash - controllers may set this in all such conditions. - * - ``V4L2_FLASH_FAULT_OVER_TEMPERATURE`` - - The flash controller has overheated. - * - ``V4L2_FLASH_FAULT_SHORT_CIRCUIT`` - - The short circuit protection of the flash controller has been - triggered. - * - ``V4L2_FLASH_FAULT_OVER_CURRENT`` - - Current in the LED power supply has exceeded the limit specific to - the flash controller. - * - ``V4L2_FLASH_FAULT_INDICATOR`` - - The flash controller has detected a short or open circuit - condition on the indicator LED. - * - ``V4L2_FLASH_FAULT_UNDER_VOLTAGE`` - - Flash controller voltage to the flash LED has been below the - minimum limit specific to the flash controller. - * - ``V4L2_FLASH_FAULT_INPUT_VOLTAGE`` - - The input voltage of the flash controller is below the limit under - which strobing the flash at full current will not be possible.The - condition persists until this flag is no longer set. - * - ``V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE`` - - The temperature of the LED has exceeded its allowed upper limit. - - - -``V4L2_CID_FLASH_CHARGE (boolean)`` - Enable or disable charging of the xenon flash capacitor. - -``V4L2_CID_FLASH_READY (boolean)`` - Is the flash ready to strobe? Xenon flashes require their capacitors - charged before strobing. LED flashes often require a cooldown period - after strobe during which another strobe will not be possible. This - is a read-only control. - - -.. _jpeg-controls: - -JPEG Control Reference -====================== - -The JPEG class includes controls for common features of JPEG encoders -and decoders. Currently it includes features for codecs implementing -progressive baseline DCT compression process with Huffman entrophy -coding. - - -.. _jpeg-control-id: - -JPEG Control IDs ----------------- - -``V4L2_CID_JPEG_CLASS (class)`` - The JPEG class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_JPEG_CHROMA_SUBSAMPLING (menu)`` - The chroma subsampling factors describe how each component of an - input image is sampled, in respect to maximum sample rate in each - spatial dimension. See :ref:`itu-t81`, clause A.1.1. for more - details. The ``V4L2_CID_JPEG_CHROMA_SUBSAMPLING`` control determines - how Cb and Cr components are downsampled after converting an input - image from RGB to Y'CbCr color space. - -.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_444`` - - No chroma subsampling, each pixel has Y, Cr and Cb values. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_422`` - - Horizontally subsample Cr, Cb components by a factor of 2. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_420`` - - Subsample Cr, Cb components horizontally and vertically by 2. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_411`` - - Horizontally subsample Cr, Cb components by a factor of 4. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_410`` - - Subsample Cr, Cb components horizontally by 4 and vertically by 2. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY`` - - Use only luminance component. - - - -``V4L2_CID_JPEG_RESTART_INTERVAL (integer)`` - The restart interval determines an interval of inserting RSTm - markers (m = 0..7). The purpose of these markers is to additionally - reinitialize the encoder process, in order to process blocks of an - image independently. For the lossy compression processes the restart - interval unit is MCU (Minimum Coded Unit) and its value is contained - in DRI (Define Restart Interval) marker. If - ``V4L2_CID_JPEG_RESTART_INTERVAL`` control is set to 0, DRI and RSTm - markers will not be inserted. - -.. _jpeg-quality-control: - -``V4L2_CID_JPEG_COMPRESSION_QUALITY (integer)`` - ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control determines trade-off - between image quality and size. It provides simpler method for - applications to control image quality, without a need for direct - reconfiguration of luminance and chrominance quantization tables. In - cases where a driver uses quantization tables configured directly by - an application, using interfaces defined elsewhere, - ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control should be set by - driver to 0. - - The value range of this control is driver-specific. Only positive, - non-zero values are meaningful. The recommended range is 1 - 100, - where larger values correspond to better image quality. - -.. _jpeg-active-marker-control: - -``V4L2_CID_JPEG_ACTIVE_MARKER (bitmask)`` - Specify which JPEG markers are included in compressed stream. This - control is valid only for encoders. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_JPEG_ACTIVE_MARKER_APP0`` - - Application data segment APP\ :sub:`0`. - * - ``V4L2_JPEG_ACTIVE_MARKER_APP1`` - - Application data segment APP\ :sub:`1`. - * - ``V4L2_JPEG_ACTIVE_MARKER_COM`` - - Comment segment. - * - ``V4L2_JPEG_ACTIVE_MARKER_DQT`` - - Quantization tables segment. - * - ``V4L2_JPEG_ACTIVE_MARKER_DHT`` - - Huffman tables segment. - - - -For more details about JPEG specification, refer to :ref:`itu-t81`, -:ref:`jfif`, :ref:`w3c-jpeg-jfif`. - - -.. _image-source-controls: - -Image Source Control Reference -============================== - -The Image Source control class is intended for low-level control of -image source devices such as image sensors. The devices feature an -analogue to digital converter and a bus transmitter to transmit the -image data out of the device. - - -.. _image-source-control-id: - -Image Source Control IDs ------------------------- - -``V4L2_CID_IMAGE_SOURCE_CLASS (class)`` - The IMAGE_SOURCE class descriptor. - -``V4L2_CID_VBLANK (integer)`` - Vertical blanking. The idle period after every frame during which no - image data is produced. The unit of vertical blanking is a line. - Every line has length of the image width plus horizontal blanking at - the pixel rate defined by ``V4L2_CID_PIXEL_RATE`` control in the - same sub-device. - -``V4L2_CID_HBLANK (integer)`` - Horizontal blanking. The idle period after every line of image data - during which no image data is produced. The unit of horizontal - blanking is pixels. - -``V4L2_CID_ANALOGUE_GAIN (integer)`` - Analogue gain is gain affecting all colour components in the pixel - matrix. The gain operation is performed in the analogue domain - before A/D conversion. - -``V4L2_CID_TEST_PATTERN_RED (integer)`` - Test pattern red colour component. - -``V4L2_CID_TEST_PATTERN_GREENR (integer)`` - Test pattern green (next to red) colour component. - -``V4L2_CID_TEST_PATTERN_BLUE (integer)`` - Test pattern blue colour component. - -``V4L2_CID_TEST_PATTERN_GREENB (integer)`` - Test pattern green (next to blue) colour component. - - -.. _image-process-controls: - -Image Process Control Reference -=============================== - -The Image Process control class is intended for low-level control of -image processing functions. Unlike ``V4L2_CID_IMAGE_SOURCE_CLASS``, the -controls in this class affect processing the image, and do not control -capturing of it. - - -.. _image-process-control-id: - -Image Process Control IDs -------------------------- - -``V4L2_CID_IMAGE_PROC_CLASS (class)`` - The IMAGE_PROC class descriptor. - -``V4L2_CID_LINK_FREQ (integer menu)`` - Data bus frequency. Together with the media bus pixel code, bus type - (clock cycles per sample), the data bus frequency defines the pixel - rate (``V4L2_CID_PIXEL_RATE``) in the pixel array (or possibly - elsewhere, if the device is not an image sensor). The frame rate can - be calculated from the pixel clock, image width and height and - horizontal and vertical blanking. While the pixel rate control may - be defined elsewhere than in the subdev containing the pixel array, - the frame rate cannot be obtained from that information. This is - because only on the pixel array it can be assumed that the vertical - and horizontal blanking information is exact: no other blanking is - allowed in the pixel array. The selection of frame rate is performed - by selecting the desired horizontal and vertical blanking. The unit - of this control is Hz. - -``V4L2_CID_PIXEL_RATE (64-bit integer)`` - Pixel rate in the source pads of the subdev. This control is - read-only and its unit is pixels / second. - -``V4L2_CID_TEST_PATTERN (menu)`` - Some capture/display/sensor devices have the capability to generate - test pattern images. These hardware specific test patterns can be - used to test if a device is working properly. - -``V4L2_CID_DEINTERLACING_MODE (menu)`` - The video deinterlacing mode (such as Bob, Weave, ...). The menu items are - driver specific and are documented in :ref:`v4l-drivers`. - -``V4L2_CID_DIGITAL_GAIN (integer)`` - Digital gain is the value by which all colour components - are multiplied by. Typically the digital gain applied is the - control value divided by e.g. 0x100, meaning that to get no - digital gain the control value needs to be 0x100. The no-gain - configuration is also typically the default. - - -.. _dv-controls: - -Digital Video Control Reference -=============================== - -The Digital Video control class is intended to control receivers and -transmitters for `VGA `__, -`DVI `__ -(Digital Visual Interface), HDMI (:ref:`hdmi`) and DisplayPort -(:ref:`dp`). These controls are generally expected to be private to -the receiver or transmitter subdevice that implements them, so they are -only exposed on the ``/dev/v4l-subdev*`` device node. - -.. note:: - - Note that these devices can have multiple input or output pads which are - hooked up to e.g. HDMI connectors. Even though the subdevice will - receive or transmit video from/to only one of those pads, the other pads - can still be active when it comes to EDID (Extended Display - Identification Data, :ref:`vesaedid`) and HDCP (High-bandwidth Digital - Content Protection System, :ref:`hdcp`) processing, allowing the - device to do the fairly slow EDID/HDCP handling in advance. This allows - for quick switching between connectors. - -These pads appear in several of the controls in this section as -bitmasks, one bit for each pad. Bit 0 corresponds to pad 0, bit 1 to pad -1, etc. The maximum value of the control is the set of valid pads. - - -.. _dv-control-id: - -Digital Video Control IDs -------------------------- - -``V4L2_CID_DV_CLASS (class)`` - The Digital Video class descriptor. - -``V4L2_CID_DV_TX_HOTPLUG (bitmask)`` - Many connectors have a hotplug pin which is high if EDID information - is available from the source. This control shows the state of the - hotplug pin as seen by the transmitter. Each bit corresponds to an - output pad on the transmitter. If an output pad does not have an - associated hotplug pin, then the bit for that pad will be 0. This - read-only control is applicable to DVI-D, HDMI and DisplayPort - connectors. - -``V4L2_CID_DV_TX_RXSENSE (bitmask)`` - Rx Sense is the detection of pull-ups on the TMDS clock lines. This - normally means that the sink has left/entered standby (i.e. the - transmitter can sense that the receiver is ready to receive video). - Each bit corresponds to an output pad on the transmitter. If an - output pad does not have an associated Rx Sense, then the bit for - that pad will be 0. This read-only control is applicable to DVI-D - and HDMI devices. - -``V4L2_CID_DV_TX_EDID_PRESENT (bitmask)`` - When the transmitter sees the hotplug signal from the receiver it - will attempt to read the EDID. If set, then the transmitter has read - at least the first block (= 128 bytes). Each bit corresponds to an - output pad on the transmitter. If an output pad does not support - EDIDs, then the bit for that pad will be 0. This read-only control - is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors. - -``V4L2_CID_DV_TX_MODE`` - (enum) - -enum v4l2_dv_tx_mode - - HDMI transmitters can transmit in DVI-D mode (just video) or in HDMI - mode (video + audio + auxiliary data). This control selects which - mode to use: V4L2_DV_TX_MODE_DVI_D or V4L2_DV_TX_MODE_HDMI. - This control is applicable to HDMI connectors. - -``V4L2_CID_DV_TX_RGB_RANGE`` - (enum) - -enum v4l2_dv_rgb_range - - Select the quantization range for RGB output. V4L2_DV_RANGE_AUTO - follows the RGB quantization range specified in the standard for the - video interface (ie. :ref:`cea861` for HDMI). - V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the - standard to be compatible with sinks that have not implemented the - standard correctly (unfortunately quite common for HDMI and DVI-D). - Full range allows all possible values to be used whereas limited - range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is - the number of bits per component. This control is applicable to VGA, - DVI-A/D, HDMI and DisplayPort connectors. - -``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` - (enum) - -enum v4l2_dv_it_content_type - - Configures the IT Content Type of the transmitted video. This - information is sent over HDMI and DisplayPort connectors as part of - the AVI InfoFrame. The term 'IT Content' is used for content that - originates from a computer as opposed to content from a TV broadcast - or an analog source. The enum v4l2_dv_it_content_type defines - the possible content types: - -.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_DV_IT_CONTENT_TYPE_GRAPHICS`` - - Graphics content. Pixel data should be passed unfiltered and - without analog reconstruction. - * - ``V4L2_DV_IT_CONTENT_TYPE_PHOTO`` - - Photo content. The content is derived from digital still pictures. - The content should be passed through with minimal scaling and - picture enhancements. - * - ``V4L2_DV_IT_CONTENT_TYPE_CINEMA`` - - Cinema content. - * - ``V4L2_DV_IT_CONTENT_TYPE_GAME`` - - Game content. Audio and video latency should be minimized. - * - ``V4L2_DV_IT_CONTENT_TYPE_NO_ITC`` - - No IT Content information is available and the ITC bit in the AVI - InfoFrame is set to 0. - - - -``V4L2_CID_DV_RX_POWER_PRESENT (bitmask)`` - Detects whether the receiver receives power from the source (e.g. - HDMI carries 5V on one of the pins). This is often used to power an - eeprom which contains EDID information, such that the source can - read the EDID even if the sink is in standby/power off. Each bit - corresponds to an input pad on the receiver. If an input pad - cannot detect whether power is present, then the bit for that pad - will be 0. This read-only control is applicable to DVI-D, HDMI and - DisplayPort connectors. - -``V4L2_CID_DV_RX_RGB_RANGE`` - (enum) - -enum v4l2_dv_rgb_range - - Select the quantization range for RGB input. V4L2_DV_RANGE_AUTO - follows the RGB quantization range specified in the standard for the - video interface (ie. :ref:`cea861` for HDMI). - V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the - standard to be compatible with sources that have not implemented the - standard correctly (unfortunately quite common for HDMI and DVI-D). - Full range allows all possible values to be used whereas limited - range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is - the number of bits per component. This control is applicable to VGA, - DVI-A/D, HDMI and DisplayPort connectors. - -``V4L2_CID_DV_RX_IT_CONTENT_TYPE`` - (enum) - -enum v4l2_dv_it_content_type - - Reads the IT Content Type of the received video. This information is - sent over HDMI and DisplayPort connectors as part of the AVI - InfoFrame. The term 'IT Content' is used for content that originates - from a computer as opposed to content from a TV broadcast or an - analog source. See ``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` for the - available content types. - - -.. _fm-rx-controls: - -FM Receiver Control Reference -============================= - -The FM Receiver (FM_RX) class includes controls for common features of -FM Reception capable devices. - - -.. _fm-rx-control-id: - -FM_RX Control IDs ------------------ - -``V4L2_CID_FM_RX_CLASS (class)`` - The FM_RX class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_RDS_RECEPTION (boolean)`` - Enables/disables RDS reception by the radio tuner - -``V4L2_CID_RDS_RX_PTY (integer)`` - Gets RDS Programme Type field. This encodes up to 31 pre-defined - programme types. - -``V4L2_CID_RDS_RX_PS_NAME (string)`` - Gets the Programme Service name (PS_NAME). It is intended for - static display on a receiver. It is the primary aid to listeners in - programme service identification and selection. In Annex E of - :ref:`iec62106`, the RDS specification, there is a full - description of the correct character encoding for Programme Service - name strings. Also from RDS specification, PS is usually a single - eight character text. However, it is also possible to find receivers - which can scroll strings sized as 8 x N characters. So, this control - must be configured with steps of 8 characters. The result is it must - always contain a string with size multiple of 8. - -``V4L2_CID_RDS_RX_RADIO_TEXT (string)`` - Gets the Radio Text info. It is a textual description of what is - being broadcasted. RDS Radio Text can be applied when broadcaster - wishes to transmit longer PS names, programme-related information or - any other text. In these cases, RadioText can be used in addition to - ``V4L2_CID_RDS_RX_PS_NAME``. The encoding for Radio Text strings is - also fully described in Annex E of :ref:`iec62106`. The length of - Radio Text strings depends on which RDS Block is being used to - transmit it, either 32 (2A block) or 64 (2B block). However, it is - also possible to find receivers which can scroll strings sized as 32 - x N or 64 x N characters. So, this control must be configured with - steps of 32 or 64 characters. The result is it must always contain a - string with size multiple of 32 or 64. - -``V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (boolean)`` - If set, then a traffic announcement is in progress. - -``V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (boolean)`` - If set, then the tuned programme carries traffic announcements. - -``V4L2_CID_RDS_RX_MUSIC_SPEECH (boolean)`` - If set, then this channel broadcasts music. If cleared, then it - broadcasts speech. If the transmitter doesn't make this distinction, - then it will be set. - -``V4L2_CID_TUNE_DEEMPHASIS`` - (enum) - -enum v4l2_deemphasis - - Configures the de-emphasis value for reception. A de-emphasis filter - is applied to the broadcast to accentuate the high audio - frequencies. Depending on the region, a time constant of either 50 - or 75 useconds is used. The enum v4l2_deemphasis defines possible - values for de-emphasis. Here they are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_DEEMPHASIS_DISABLED`` - - No de-emphasis is applied. - * - ``V4L2_DEEMPHASIS_50_uS`` - - A de-emphasis of 50 uS is used. - * - ``V4L2_DEEMPHASIS_75_uS`` - - A de-emphasis of 75 uS is used. - - - - -.. _detect-controls: - -Detect Control Reference -======================== - -The Detect class includes controls for common features of various motion -or object detection capable devices. - - -.. _detect-control-id: - -Detect Control IDs ------------------- - -``V4L2_CID_DETECT_CLASS (class)`` - The Detect class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_DETECT_MD_MODE (menu)`` - Sets the motion detection mode. - -.. tabularcolumns:: |p{7.5cm}|p{10.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_DETECT_MD_MODE_DISABLED`` - - Disable motion detection. - * - ``V4L2_DETECT_MD_MODE_GLOBAL`` - - Use a single motion detection threshold. - * - ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` - - The image is divided into a grid, each cell with its own motion - detection threshold. These thresholds are set through the - ``V4L2_CID_DETECT_MD_THRESHOLD_GRID`` matrix control. - * - ``V4L2_DETECT_MD_MODE_REGION_GRID`` - - The image is divided into a grid, each cell with its own region - value that specifies which per-region motion detection thresholds - should be used. Each region has its own thresholds. How these - per-region thresholds are set up is driver-specific. The region - values for the grid are set through the - ``V4L2_CID_DETECT_MD_REGION_GRID`` matrix control. - - - -``V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD (integer)`` - Sets the global motion detection threshold to be used with the - ``V4L2_DETECT_MD_MODE_GLOBAL`` motion detection mode. - -``V4L2_CID_DETECT_MD_THRESHOLD_GRID (__u16 matrix)`` - Sets the motion detection thresholds for each cell in the grid. To - be used with the ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` motion - detection mode. Matrix element (0, 0) represents the cell at the - top-left of the grid. - -``V4L2_CID_DETECT_MD_REGION_GRID (__u8 matrix)`` - Sets the motion detection region value for each cell in the grid. To - be used with the ``V4L2_DETECT_MD_MODE_REGION_GRID`` motion - detection mode. Matrix element (0, 0) represents the cell at the - top-left of the grid. - - -.. _rf-tuner-controls: - -RF Tuner Control Reference -========================== - -The RF Tuner (RF_TUNER) class includes controls for common features of -devices having RF tuner. - -In this context, RF tuner is radio receiver circuit between antenna and -demodulator. It receives radio frequency (RF) from the antenna and -converts that received signal to lower intermediate frequency (IF) or -baseband frequency (BB). Tuners that could do baseband output are often -called Zero-IF tuners. Older tuners were typically simple PLL tuners -inside a metal box, while newer ones are highly integrated chips -without a metal box "silicon tuners". These controls are mostly -applicable for new feature rich silicon tuners, just because older -tuners does not have much adjustable features. - -For more information about RF tuners see -`Tuner (radio) `__ -and `RF front end `__ -from Wikipedia. - - -.. _rf-tuner-control-id: - -RF_TUNER Control IDs --------------------- - -``V4L2_CID_RF_TUNER_CLASS (class)`` - The RF_TUNER class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_RF_TUNER_BANDWIDTH_AUTO (boolean)`` - Enables/disables tuner radio channel bandwidth configuration. In - automatic mode bandwidth configuration is performed by the driver. - -``V4L2_CID_RF_TUNER_BANDWIDTH (integer)`` - Filter(s) on tuner signal path are used to filter signal according - to receiving party needs. Driver configures filters to fulfill - desired bandwidth requirement. Used when - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO is not set. Unit is in Hz. The - range and step are driver-specific. - -``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO (boolean)`` - Enables/disables LNA automatic gain control (AGC) - -``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO (boolean)`` - Enables/disables mixer automatic gain control (AGC) - -``V4L2_CID_RF_TUNER_IF_GAIN_AUTO (boolean)`` - Enables/disables IF automatic gain control (AGC) - -``V4L2_CID_RF_TUNER_RF_GAIN (integer)`` - The RF amplifier is the very first amplifier on the receiver signal - path, just right after the antenna input. The difference between the - LNA gain and the RF gain in this document is that the LNA gain is - integrated in the tuner chip while the RF gain is a separate chip. - There may be both RF and LNA gain controls in the same device. The - range and step are driver-specific. - -``V4L2_CID_RF_TUNER_LNA_GAIN (integer)`` - LNA (low noise amplifier) gain is first gain stage on the RF tuner - signal path. It is located very close to tuner antenna input. Used - when ``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO`` is not set. See - ``V4L2_CID_RF_TUNER_RF_GAIN`` to understand how RF gain and LNA gain - differs from the each others. The range and step are - driver-specific. - -``V4L2_CID_RF_TUNER_MIXER_GAIN (integer)`` - Mixer gain is second gain stage on the RF tuner signal path. It is - located inside mixer block, where RF signal is down-converted by the - mixer. Used when ``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO`` is not set. - The range and step are driver-specific. - -``V4L2_CID_RF_TUNER_IF_GAIN (integer)`` - IF gain is last gain stage on the RF tuner signal path. It is - located on output of RF tuner. It controls signal level of - intermediate frequency output or baseband output. Used when - ``V4L2_CID_RF_TUNER_IF_GAIN_AUTO`` is not set. The range and step - are driver-specific. - -``V4L2_CID_RF_TUNER_PLL_LOCK (boolean)`` - Is synthesizer PLL locked? RF tuner is receiving given frequency - when that control is set. This is a read-only control. - -.. [#f1] - This control may be changed to a menu control in the future, if more - options are required. From 0b8f452c9698d48aa92de17294032fac03d47229 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 17 Feb 2019 23:14:25 -0800 Subject: [PATCH 1967/4277] Input: i8042 - rework DT node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. For the root node on SUN DT, we need to retrieve the 'name' property as it is the rare case where the 'name' property and node name differ. With both changes, it removes direct access to the node name pointer. While at it, convert the open coded loop to use for_each_child_of_node(). Signed-off-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-sparcio.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index 796289846204..fce76812843b 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -53,12 +53,11 @@ static struct resource *kbd_res; static int sparc_i8042_probe(struct platform_device *op) { - struct device_node *dp = op->dev.of_node; + struct device_node *dp; - dp = dp->child; - while (dp) { - if (!strcmp(dp->name, OBP_PS2KBD_NAME1) || - !strcmp(dp->name, OBP_PS2KBD_NAME2)) { + for_each_child_of_node(op->dev.of_node, dp) { + if (of_node_name_eq(dp, OBP_PS2KBD_NAME1) || + of_node_name_eq(dp, OBP_PS2KBD_NAME2)) { struct platform_device *kbd = of_find_device_by_node(dp); unsigned int irq = kbd->archdata.irqs[0]; if (irq == 0xffffffff) @@ -67,16 +66,14 @@ static int sparc_i8042_probe(struct platform_device *op) kbd_iobase = of_ioremap(&kbd->resource[0], 0, 8, "kbd"); kbd_res = &kbd->resource[0]; - } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) || - !strcmp(dp->name, OBP_PS2MS_NAME2)) { + } else if (of_node_name_eq(dp, OBP_PS2MS_NAME1) || + of_node_name_eq(dp, OBP_PS2MS_NAME2)) { struct platform_device *ms = of_find_device_by_node(dp); unsigned int irq = ms->archdata.irqs[0]; if (irq == 0xffffffff) irq = op->archdata.irqs[0]; i8042_aux_irq = irq; } - - dp = dp->sibling; } return 0; @@ -109,8 +106,9 @@ static struct platform_driver sparc_i8042_driver = { static int __init i8042_platform_init(void) { struct device_node *root = of_find_node_by_path("/"); + const char *name = of_get_property(root, "name", NULL); - if (!strcmp(root->name, "SUNW,JavaStation-1")) { + if (name && !strcmp(name, "SUNW,JavaStation-1")) { /* Hardcoded values for MrCoffee. */ i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; kbd_iobase = ioremap(0x71300060, 8); @@ -139,8 +137,9 @@ static int __init i8042_platform_init(void) static inline void i8042_platform_exit(void) { struct device_node *root = of_find_node_by_path("/"); + const char *name = of_get_property(root, "name", NULL); - if (strcmp(root->name, "SUNW,JavaStation-1")) + if (!name || strcmp(name, "SUNW,JavaStation-1")) platform_driver_unregister(&sparc_i8042_driver); } From 076d792b17468632cb104b6de8d2cf6cd426fed1 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 28 Jan 2019 03:58:43 -0500 Subject: [PATCH 1968/4277] media: dt-bindings: media: sun6i: Add A64 CSI compatible Allwinner A64 CSI is a single channel time-multiplexed BT.656 protocol interface. Add separate compatible string for A64 since it require explicit change in sun6i_csi driver to update default CSI_SCLK rate. Signed-off-by: Jagan Teki Reviewed-by: Rob Herring Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/sun6i-csi.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt index cc37cf7fd051..0dd540bb03db 100644 --- a/Documentation/devicetree/bindings/media/sun6i-csi.txt +++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt @@ -8,6 +8,7 @@ Required properties: * "allwinner,sun6i-a31-csi" * "allwinner,sun8i-h3-csi" * "allwinner,sun8i-v3s-csi" + * "allwinner,sun50i-a64-csi" - reg: base address and size of the memory-mapped region. - interrupts: interrupt associated to this IP - clocks: phandles to the clocks feeding the CSI From 27e2add8ae8fcae07e2e8d3ea5b3699572290ef3 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 28 Jan 2019 03:58:44 -0500 Subject: [PATCH 1969/4277] media: sun6i: Add A64 CSI block support CSI block in Allwinner A64 has similar features as like in H3, but the default CSI_SCLK rate cannot work properly to drive the connected sensor interface. The tested mod cock rate is 300 MHz and BSP vfe media driver is also using the same rate. Unfortunately there is no valid information about clock rate in manual or any other sources except the BSP driver. so more faith on BSP code, because same has tested in mainline. So, add support for A64 CSI block by setting updated mod clock rate. Signed-off-by: Jagan Teki Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index ee882b66a5ea..5ecdfbf9f6ae 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,7 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) { struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); + struct device *dev = sdev->dev; struct regmap *regmap = sdev->regmap; int ret; @@ -161,6 +163,9 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0); clk_disable_unprepare(sdev->clk_ram); + if (of_device_is_compatible(dev->of_node, + "allwinner,sun50i-a64-csi")) + clk_rate_exclusive_put(sdev->clk_mod); clk_disable_unprepare(sdev->clk_mod); reset_control_assert(sdev->rstc_bus); return 0; @@ -172,6 +177,9 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) return ret; } + if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) + clk_set_rate_exclusive(sdev->clk_mod, 300000000); + ret = clk_prepare_enable(sdev->clk_ram); if (ret) { dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret); @@ -191,6 +199,8 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) clk_ram_disable: clk_disable_unprepare(sdev->clk_ram); clk_mod_disable: + if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) + clk_rate_exclusive_put(sdev->clk_mod); clk_disable_unprepare(sdev->clk_mod); return ret; } @@ -895,6 +905,7 @@ static const struct of_device_id sun6i_csi_of_match[] = { { .compatible = "allwinner,sun6i-a31-csi", }, { .compatible = "allwinner,sun8i-h3-csi", }, { .compatible = "allwinner,sun8i-v3s-csi", }, + { .compatible = "allwinner,sun50i-a64-csi", }, {}, }; MODULE_DEVICE_TABLE(of, sun6i_csi_of_match); From b8726aea59de780a2eb95897f133284d742d6c83 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Thu, 24 Jan 2019 19:05:31 -0500 Subject: [PATCH 1970/4277] media: ipu3: update meta format documentation Language improvements, fix entity naming, make pipeline a graph and move device usage documentation to device documentation ipu3.rst. Signed-off-by: Yong Zhi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/meta-formats.rst | 2 +- .../media/uapi/v4l/pixfmt-meta-intel-ipu3.rst | 119 ++------------ Documentation/media/v4l-drivers/ipu3.rst | 147 ++++++++++++++++++ 3 files changed, 159 insertions(+), 109 deletions(-) diff --git a/Documentation/media/uapi/v4l/meta-formats.rst b/Documentation/media/uapi/v4l/meta-formats.rst index 5f956fa784b7..b10ca9ee3968 100644 --- a/Documentation/media/uapi/v4l/meta-formats.rst +++ b/Documentation/media/uapi/v4l/meta-formats.rst @@ -19,8 +19,8 @@ These formats are used for the :ref:`metadata` interface only. .. toctree:: :maxdepth: 1 - pixfmt-meta-intel-ipu3 pixfmt-meta-d4xx + pixfmt-meta-intel-ipu3 pixfmt-meta-uvc pixfmt-meta-vsp1-hgo pixfmt-meta-vsp1-hgt diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst index 659e58aa9c93..7fb54339f4a7 100644 --- a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst +++ b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst @@ -30,21 +30,22 @@ V4L2_META_FMT_IPU3_PARAMS ('ip3p'), V4L2_META_FMT_IPU3_3A ('ip3s') ****************************************************************** -.. c:type:: ipu3_uapi_stats_3a +.. ipu3_uapi_stats_3a 3A statistics ============= -For IPU3 ImgU, the 3A statistics accelerators collect different statistics over -an input bayer frame. Those statistics, defined in data struct :c:type:`ipu3_uapi_stats_3a`, -are obtained from "ipu3-imgu 3a stat" metadata capture video node, which are then -passed to user space for statistics analysis using :c:type:`v4l2_meta_format` interface. +The IPU3 ImgU 3A statistics accelerators collect different statistics over +an input Bayer frame. Those statistics are obtained from the "ipu3-imgu [01] 3a +stat" metadata capture video nodes, using the :c:type:`v4l2_meta_format` +interface. They are formatted as described by the :c:type:`ipu3_uapi_stats_3a` +structure. The statistics collected are AWB (Auto-white balance) RGBS (Red, Green, Blue and Saturation measure) cells, AWB filter response, AF (Auto-focus) filter response, and AE (Auto-exposure) histogram. -struct :c:type:`ipu3_uapi_4a_config` saves configurable parameters for all above. +The struct :c:type:`ipu3_uapi_4a_config` saves all configurable parameters. .. code-block:: c @@ -60,105 +61,14 @@ struct :c:type:`ipu3_uapi_4a_config` saves configurable parameters for all above struct ipu3_uapi_ff_status stats_3a_status; }; -.. c:type:: ipu3_uapi_params +.. ipu3_uapi_params Pipeline parameters =================== -IPU3 pipeline has a number of image processing stages, each of which takes a -set of parameters as input. The major stages of pipelines are shown here: - -Raw pixels -> Bayer Downscaling -> Optical Black Correction -> - -Linearization -> Lens Shading Correction -> White Balance / Exposure / - -Focus Apply -> Bayer Noise Reduction -> ANR -> Demosaicing -> Color - -Correction Matrix -> Gamma correction -> Color Space Conversion -> - -Chroma Down Scaling -> Chromatic Noise Reduction -> Total Color - -Correction -> XNR3 -> TNR -> DDR - -The table below presents a description of the above algorithms. - -======================== ======================================================= -Name Description -======================== ======================================================= -Optical Black Correction Optical Black Correction block subtracts a pre-defined - value from the respective pixel values to obtain better - image quality. - Defined in :c:type:`ipu3_uapi_obgrid_param`. -Linearization This algo block uses linearization parameters to - address non-linearity sensor effects. The Lookup table - table is defined in - :c:type:`ipu3_uapi_isp_lin_vmem_params`. -SHD Lens shading correction is used to correct spatial - non-uniformity of the pixel response due to optical - lens shading. This is done by applying a different gain - for each pixel. The gain, black level etc are - configured in :c:type:`ipu3_uapi_shd_config_static`. -BNR Bayer noise reduction block removes image noise by - applying a bilateral filter. - See :c:type:`ipu3_uapi_bnr_static_config` for details. -ANR Advanced Noise Reduction is a block based algorithm - that performs noise reduction in the Bayer domain. The - convolution matrix etc can be found in - :c:type:`ipu3_uapi_anr_config`. -Demosaicing Demosaicing converts raw sensor data in Bayer format - into RGB (Red, Green, Blue) presentation. Then add - outputs of estimation of Y channel for following stream - processing by Firmware. The struct is defined as - :c:type:`ipu3_uapi_dm_config`. (TODO) -Color Correction Color Correction algo transforms sensor specific color - space to the standard "sRGB" color space. This is done - by applying 3x3 matrix defined in - :c:type:`ipu3_uapi_ccm_mat_config`. -Gamma correction Gamma correction :c:type:`ipu3_uapi_gamma_config` is a - basic non-linear tone mapping correction that is - applied per pixel for each pixel component. -CSC Color space conversion transforms each pixel from the - RGB primary presentation to YUV (Y: brightness, - UV: Luminance) presentation. This is done by applying - a 3x3 matrix defined in - :c:type:`ipu3_uapi_csc_mat_config` -CDS Chroma down sampling - After the CSC is performed, the Chroma Down Sampling - is applied for a UV plane down sampling by a factor - of 2 in each direction for YUV 4:2:0 using a 4x2 - configurable filter :c:type:`ipu3_uapi_cds_params`. -CHNR Chroma noise reduction - This block processes only the chrominance pixels and - performs noise reduction by cleaning the high - frequency noise. - See struct :c:type:`ipu3_uapi_yuvp1_chnr_config`. -TCC Total color correction as defined in struct - :c:type:`ipu3_uapi_yuvp2_tcc_static_config`. -XNR3 eXtreme Noise Reduction V3 is the third revision of - noise reduction algorithm used to improve image - quality. This removes the low frequency noise in the - captured image. Two related structs are being defined, - :c:type:`ipu3_uapi_isp_xnr3_params` for ISP data memory - and :c:type:`ipu3_uapi_isp_xnr3_vmem_params` for vector - memory. -TNR Temporal Noise Reduction block compares successive - frames in time to remove anomalies / noise in pixel - values. :c:type:`ipu3_uapi_isp_tnr3_vmem_params` and - :c:type:`ipu3_uapi_isp_tnr3_params` are defined for ISP - vector and data memory respectively. -======================== ======================================================= - -A few stages of the pipeline will be executed by firmware running on the ISP -processor, while many others will use a set of fixed hardware blocks also -called accelerator cluster (ACC) to crunch pixel data and produce statistics. - -ACC parameters of individual algorithms, as defined by -:c:type:`ipu3_uapi_acc_param`, can be chosen to be applied by the user -space through struct :c:type:`ipu3_uapi_flags` embedded in -:c:type:`ipu3_uapi_params` structure. For parameters that are configured as -not enabled by the user space, the corresponding structs are ignored by the -driver, in which case the existing configuration of the algorithm will be -preserved. +The pipeline parameters are passed to the "ipu3-imgu [01] parameters" metadata +output video nodes, using the :c:type:`v4l2_meta_format` interface. They are +formatted as described by the :c:type:`ipu3_uapi_params` structure. Both 3A statistics and pipeline parameters described here are closely tied to the underlying camera sub-system (CSS) APIs. They are usually consumed and @@ -166,13 +76,6 @@ produced by dedicated user space libraries that comprise the important tuning tools, thus freeing the developers from being bothered with the low level hardware and algorithm details. -It should be noted that IPU3 DMA operations require the addresses of all data -structures (that includes both input and output) to be aligned on 32 byte -boundaries. - -The meta data :c:type:`ipu3_uapi_params` will be sent to "ipu3-imgu parameters" -video node in ``V4L2_BUF_TYPE_META_CAPTURE`` format. - .. code-block:: c struct ipu3_uapi_params { diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index 804f37300623..c9f780404eee 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -357,6 +357,153 @@ https://chromium.googlesource.com/chromiumos/platform/arc-camera/+/master/ The source can be located under hal/intel directory. +Overview of IPU3 pipeline +========================= + +IPU3 pipeline has a number of image processing stages, each of which takes a +set of parameters as input. The major stages of pipelines are shown here: + +.. kernel-render:: DOT + :alt: IPU3 ImgU Pipeline + :caption: IPU3 ImgU Pipeline Diagram + + digraph "IPU3 ImgU" { + node [shape=box] + splines="ortho" + rankdir="LR" + + a [label="Raw pixels"] + b [label="Bayer Downscaling"] + c [label="Optical Black Correction"] + d [label="Linearization"] + e [label="Lens Shading Correction"] + f [label="White Balance / Exposure / Focus Apply"] + g [label="Bayer Noise Reduction"] + h [label="ANR"] + i [label="Demosaicing"] + j [label="Color Correction Matrix"] + k [label="Gamma correction"] + l [label="Color Space Conversion"] + m [label="Chroma Down Scaling"] + n [label="Chromatic Noise Reduction"] + o [label="Total Color Correction"] + p [label="XNR3"] + q [label="TNR"] + r [label="DDR"] + + { rank=same; a -> b -> c -> d -> e -> f } + { rank=same; g -> h -> i -> j -> k -> l } + { rank=same; m -> n -> o -> p -> q -> r } + + a -> g -> m [style=invis, weight=10] + + f -> g + l -> m + } + +The table below presents a description of the above algorithms. + +======================== ======================================================= +Name Description +======================== ======================================================= +Optical Black Correction Optical Black Correction block subtracts a pre-defined + value from the respective pixel values to obtain better + image quality. + Defined in :c:type:`ipu3_uapi_obgrid_param`. +Linearization This algo block uses linearization parameters to + address non-linearity sensor effects. The Lookup table + table is defined in + :c:type:`ipu3_uapi_isp_lin_vmem_params`. +SHD Lens shading correction is used to correct spatial + non-uniformity of the pixel response due to optical + lens shading. This is done by applying a different gain + for each pixel. The gain, black level etc are + configured in :c:type:`ipu3_uapi_shd_config_static`. +BNR Bayer noise reduction block removes image noise by + applying a bilateral filter. + See :c:type:`ipu3_uapi_bnr_static_config` for details. +ANR Advanced Noise Reduction is a block based algorithm + that performs noise reduction in the Bayer domain. The + convolution matrix etc can be found in + :c:type:`ipu3_uapi_anr_config`. +DM Demosaicing converts raw sensor data in Bayer format + into RGB (Red, Green, Blue) presentation. Then add + outputs of estimation of Y channel for following stream + processing by Firmware. The struct is defined as + :c:type:`ipu3_uapi_dm_config`. +Color Correction Color Correction algo transforms sensor specific color + space to the standard "sRGB" color space. This is done + by applying 3x3 matrix defined in + :c:type:`ipu3_uapi_ccm_mat_config`. +Gamma correction Gamma correction :c:type:`ipu3_uapi_gamma_config` is a + basic non-linear tone mapping correction that is + applied per pixel for each pixel component. +CSC Color space conversion transforms each pixel from the + RGB primary presentation to YUV (Y: brightness, + UV: Luminance) presentation. This is done by applying + a 3x3 matrix defined in + :c:type:`ipu3_uapi_csc_mat_config` +CDS Chroma down sampling + After the CSC is performed, the Chroma Down Sampling + is applied for a UV plane down sampling by a factor + of 2 in each direction for YUV 4:2:0 using a 4x2 + configurable filter :c:type:`ipu3_uapi_cds_params`. +CHNR Chroma noise reduction + This block processes only the chrominance pixels and + performs noise reduction by cleaning the high + frequency noise. + See struct :c:type:`ipu3_uapi_yuvp1_chnr_config`. +TCC Total color correction as defined in struct + :c:type:`ipu3_uapi_yuvp2_tcc_static_config`. +XNR3 eXtreme Noise Reduction V3 is the third revision of + noise reduction algorithm used to improve image + quality. This removes the low frequency noise in the + captured image. Two related structs are being defined, + :c:type:`ipu3_uapi_isp_xnr3_params` for ISP data memory + and :c:type:`ipu3_uapi_isp_xnr3_vmem_params` for vector + memory. +TNR Temporal Noise Reduction block compares successive + frames in time to remove anomalies / noise in pixel + values. :c:type:`ipu3_uapi_isp_tnr3_vmem_params` and + :c:type:`ipu3_uapi_isp_tnr3_params` are defined for ISP + vector and data memory respectively. +======================== ======================================================= + +Other often encountered acronyms not listed in above table: + + ACC + Accelerator cluster + AWB_FR + Auto white balance filter response statistics + BDS + Bayer downscaler parameters + CCM + Color correction matrix coefficients + IEFd + Image enhancement filter directed + Obgrid + Optical black level compensation + OSYS + Output system configuration + ROI + Region of interest + YDS + Y down sampling + YTM + Y-tone mapping + +A few stages of the pipeline will be executed by firmware running on the ISP +processor, while many others will use a set of fixed hardware blocks also +called accelerator cluster (ACC) to crunch pixel data and produce statistics. + +ACC parameters of individual algorithms, as defined by +:c:type:`ipu3_uapi_acc_param`, can be chosen to be applied by the user +space through struct :c:type:`ipu3_uapi_flags` embedded in +:c:type:`ipu3_uapi_params` structure. For parameters that are configured as +not enabled by the user space, the corresponding structs are ignored by the +driver, in which case the existing configuration of the algorithm will be +preserved. + References ========== From 45602f7110fe8fa2d109641d6f80fb7d6ccf9b5a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:35 -0500 Subject: [PATCH 1971/4277] media: uvcvideo: Fix smatch warning drivers/media/usb/uvc/uvc_video.c: drivers/media/usb/uvc/uvc_video.c:1893 uvc_video_start_transfer() warn: argument 2 to %u specifier is cast from pointer Signed-off-by: Hans Verkuil Reviewed-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvcvideo.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 9b41b14ce076..c7c1baa90dea 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -620,8 +620,10 @@ struct uvc_streaming { (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \ ++(uvc_urb)) -#define uvc_urb_index(uvc_urb) \ - (unsigned int)((uvc_urb) - (&(uvc_urb)->stream->uvc_urb[0])) +static inline u32 uvc_urb_index(const struct uvc_urb *uvc_urb) +{ + return uvc_urb - &uvc_urb->stream->uvc_urb[0]; +} struct uvc_device_info { u32 quirks; From 0654cbcc8dfbf3bc5e6f93916b67fa57ed826a4b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 1 Feb 2019 04:57:31 -0500 Subject: [PATCH 1972/4277] media: uvcvideo: Use usb_make_path to fill in usb_info The uvc driver uses this function to fill in bus_info for VIDIOC_QUERYCAP, so use the same function when filling in the bus_info for the media device. The current implementation only fills in part of the info. E.g. if the full bus_info is usb-0000:01:00.0-1.4.2, then the media bus_info only has 1.4.2. Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 33a22c016456..10cfe8e51626 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2183,7 +2183,7 @@ static int uvc_probe(struct usb_interface *intf, if (udev->serial) strscpy(dev->mdev.serial, udev->serial, sizeof(dev->mdev.serial)); - strscpy(dev->mdev.bus_info, udev->devpath, sizeof(dev->mdev.bus_info)); + usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); media_device_init(&dev->mdev); From 5b6326b7aedd08a4d8108f44af9b7af77a0dfd4e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:36 -0500 Subject: [PATCH 1973/4277] media: vsp1: Fix smatch warning drivers/media/platform/vsp1/vsp1_drm.c: drivers/media/platform/vsp1/vsp1_drm.c:336 vsp1_du_pipeline_setup_brx() error: we previously assumed 'pipe->brx' could be null (see line 244) smatch missed that if pipe->brx was NULL, then later on it will be set with a non-NULL value. But it is easier to just use the brx pointer so smatch doesn't get confused. Tested-on: Salvator-XS-ES2.0, Salvator-XS-M3N Signed-off-by: Hans Verkuil Reviewed-by: Kieran Bingham Tested-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 8d86f618ec77..84895385d2e5 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -333,19 +333,19 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, * on the BRx sink pad 0 and propagated inside the entity, not on the * source pad. */ - format.pad = pipe->brx->source_pad; + format.pad = brx->source_pad; format.format.width = drm_pipe->width; format.format.height = drm_pipe->height; format.format.field = V4L2_FIELD_NONE; - ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, + ret = v4l2_subdev_call(&brx->subdev, pad, set_fmt, NULL, &format); if (ret < 0) return ret; dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", __func__, format.format.width, format.format.height, - format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad); + format.format.code, BRX_NAME(brx), brx->source_pad); if (format.format.width != drm_pipe->width || format.format.height != drm_pipe->height) { From e260d78736db0c74240df2243c299be7b0d0b182 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:20:24 -0500 Subject: [PATCH 1974/4277] media: vsp1: Add RZ/G support Document RZ/G1 and RZ/G2 support. Signed-off-by: Fabrizio Castro Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,vsp1.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt index 16427017cb45..cd5a955b2ea0 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.txt @@ -2,13 +2,13 @@ The VSP is a video processing engine that supports up-/down-scaling, alpha blending, color space conversion and various other image processing features. -It can be found in the Renesas R-Car second generation SoCs. +It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. Required properties: - compatible: Must contain one of the following values - - "renesas,vsp1" for the R-Car Gen2 VSP1 - - "renesas,vsp2" for the R-Car Gen3 VSP2 + - "renesas,vsp1" for the R-Car Gen2 and RZ/G1 VSP1 + - "renesas,vsp2" for the R-Car Gen3 and RZ/G2 VSP2 - reg: Base address and length of the registers block for the VSP. - interrupts: VSP interrupt specifier. From b8eb83457e79fc745abea3abcd6c95b1428a05ee Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:20:33 -0500 Subject: [PATCH 1975/4277] media: dt-bindings: media: renesas-fcp: Add RZ/G2 support Document RZ/G2 support. Signed-off-by: Fabrizio Castro Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,fcp.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt index 3ec91803ba58..79c37395b396 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.txt +++ b/Documentation/devicetree/bindings/media/renesas,fcp.txt @@ -2,8 +2,9 @@ Renesas R-Car Frame Compression Processor (FCP) ----------------------------------------------- The FCP is a companion module of video processing modules in the Renesas R-Car -Gen3 SoCs. It provides data compression and decompression, data caching, and -conversion of AXI transactions in order to reduce the memory bandwidth. +Gen3 and RZ/G2 SoCs. It provides data compression and decompression, data +caching, and conversion of AXI transactions in order to reduce the memory +bandwidth. There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP for FDP (FCPF). Their configuration and behaviour depend on the module they From d31b282e2c0de9c7fb113516820340251f03a625 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 3 Feb 2019 11:03:56 -0500 Subject: [PATCH 1976/4277] media: sun6i: Fix CSI regmap's max_register max_register is currently set to 0x1000. This is beyond the mapped address range of the hardware, so attempts to dump the regmap from debugfs would trigger a kernel exception. Furthermore, the useful registers only occupy a small section at the beginning of the full range. Change the value to 0x9c, the last known register on the V3s and H3. On the A31, the register range is extended to support additional capture channels. Since this is not yet supported, ignore it for now. Fixes: 5cc7522d8965 ("media: sun6i: Add support for Allwinner CSI V3s") Cc: Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 5ecdfbf9f6ae..457131c7f74a 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -803,7 +803,7 @@ static const struct regmap_config sun6i_csi_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x1000, + .max_register = 0x9c, }; static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev, From 19b18e78b327c8e0a5964ea7d08de5cb718f472d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 3 Feb 2019 11:03:57 -0500 Subject: [PATCH 1977/4277] media: sun6i: Add support for RGB565 formats The CSI controller can take raw data from the data bus and output RGB565 format. The controller does not distinguish between RGB565 LE and BE. Instead this is determined by the media bus format, i.e. the format or order the sensor is sending data in. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../platform/sunxi/sun6i-csi/sun6i_csi.c | 19 +++++++++++++++++-- .../platform/sunxi/sun6i-csi/sun6i_csi.h | 2 ++ .../platform/sunxi/sun6i-csi/sun6i_video.c | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 457131c7f74a..ff739466f189 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -144,6 +144,12 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, break; } break; + + case V4L2_PIX_FMT_RGB565: + return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE); + case V4L2_PIX_FMT_RGB565X: + return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE); + default: dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; @@ -208,8 +214,8 @@ clk_mod_disable: static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev, u32 mbus_code, u32 pixformat) { - /* bayer */ - if ((mbus_code & 0xF000) == 0x3000) + /* non-YUV */ + if ((mbus_code & 0xF000) != 0x2000) return CSI_INPUT_FORMAT_RAW; switch (pixformat) { @@ -278,6 +284,11 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev, case V4L2_PIX_FMT_YUV422P: return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 : CSI_FIELD_PLANAR_YUV422; + + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565; + default: dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; @@ -289,6 +300,10 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev, static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev, u32 mbus_code, u32 pixformat) { + /* Input sequence does not apply to non-YUV formats */ + if ((mbus_code & 0xF000) != 0x2000) + return 0; + switch (pixformat) { case V4L2_PIX_FMT_HM12: case V4L2_PIX_FMT_NV12: diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index 0bb000712c33..585cd9669417 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -117,6 +117,8 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat) case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: return 16; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index b04300c3811f..bff6fe832803 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -56,6 +56,8 @@ static const u32 supported_pixformats[] = { V4L2_PIX_FMT_NV16, V4L2_PIX_FMT_NV61, V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_RGB565, + V4L2_PIX_FMT_RGB565X, }; static bool is_pixformat_valid(unsigned int pixformat) From 35deee14183457754f77e34dc92c588d93d40052 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 3 Feb 2019 11:03:58 -0500 Subject: [PATCH 1978/4277] media: sun6i: Add support for JPEG media bus format The CSI controller can take raw data from the data bus and output it directly to capture buffers. This can be used to support the JPEG media bus format. While the controller can report minimum and maximum bytes per line, it has no way to report how many lines were captured in the last frame. Thus, even when the on-bus data is framed correctly, we have no way to accertain the actual amount of data captured, unless we scan the buffer for JPEG EOI markers, or sequential zeros. For now we leave bytesused alone, and leave it up to userspace applications to parse the data. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 6 ++++++ drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 1 + drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c | 1 + 3 files changed, 8 insertions(+) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index ff739466f189..4c79eb64a7a7 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -150,6 +150,9 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, case V4L2_PIX_FMT_RGB565X: return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE); + case V4L2_PIX_FMT_JPEG: + return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8); + default: dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; @@ -289,6 +292,9 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev, case V4L2_PIX_FMT_RGB565X: return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565; + case V4L2_PIX_FMT_JPEG: + return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8; + default: dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index 585cd9669417..f30a210e9caa 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -94,6 +94,7 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat) case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_JPEG: return 8; case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index bff6fe832803..1fd16861f111 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -58,6 +58,7 @@ static const u32 supported_pixformats[] = { V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_RGB565X, + V4L2_PIX_FMT_JPEG, }; static bool is_pixformat_valid(unsigned int pixformat) From 0806bc0afbb4157b6e1c8d91cbe8e536b292996c Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 5 Feb 2019 11:23:36 -0500 Subject: [PATCH 1979/4277] media: tda1997x: fix get_edid set_edid never wrote the new EDID to state->edid.edid, it was only written to the hardware. Since get_edid returned state->edid.edid, it was never returning the right EDID. Signed-off-by: Tim Harvey Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: added missing commit log] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tda1997x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index e8613e364403..a62ede096636 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1884,6 +1884,10 @@ static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) for (i = 0; i < 128; i++) io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]); + /* store state */ + memcpy(state->edid.edid, edid->edid, 256); + state->edid.blocks = edid->blocks; + tda1997x_enable_edid(sd); return 0; From 5c88ee02932a964096cbbcc7c9f38b78d230bacb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 5 Feb 2019 14:37:42 -0500 Subject: [PATCH 1980/4277] media: sh: migor: Include missing dma-mapping header Since the removal of the stale soc_camera headers, Migo-R board fails to build due to missing dma-mapping include directive. Include missing dma-mapping.h header in Migo-R board file to fix the build error. Fixes: a50c7738e8ae ("media: sh: migor: Remove stale soc_camera include") Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- arch/sh/boards/mach-migor/setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index f4ad33c6d2aa..1d2993bdd231 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 Magnus Damm */ #include +#include #include #include #include From 1b275e4e8b70dbff9850874b30831c1bd8d3c504 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:39 -0500 Subject: [PATCH 1981/4277] media: mtk-jpeg: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/mtk-jpeg/mtk_jpeg_core.c | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 2a5d5002c27e..f761e4d8bf2a 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -702,7 +702,7 @@ end: v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb)); } -static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, +static struct vb2_v4l2_buffer *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -714,7 +714,7 @@ static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; int ret = 0; ret = pm_runtime_get_sync(ctx->jpeg->dev); @@ -724,14 +724,14 @@ static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) return 0; err: while ((vb = mtk_jpeg_buf_remove(ctx, q->type))) - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_QUEUED); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_QUEUED); return ret; } static void mtk_jpeg_stop_streaming(struct vb2_queue *q) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; /* * STREAMOFF is an acknowledgment for source change event. @@ -743,7 +743,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) struct mtk_jpeg_src_buf *src_buf; vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - src_buf = mtk_jpeg_vb2_to_srcbuf(vb); + src_buf = mtk_jpeg_vb2_to_srcbuf(&vb->vb2_buf); mtk_jpeg_set_queue_data(ctx, &src_buf->dec_param); ctx->state = MTK_JPEG_RUNNING; } else if (V4L2_TYPE_IS_OUTPUT(q->type)) { @@ -751,7 +751,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) } while ((vb = mtk_jpeg_buf_remove(ctx, q->type))) - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); pm_runtime_put_sync(ctx->jpeg->dev); } @@ -807,7 +807,7 @@ static void mtk_jpeg_device_run(void *priv) { struct mtk_jpeg_ctx *ctx = priv; struct mtk_jpeg_dev *jpeg = ctx->jpeg; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; unsigned long flags; struct mtk_jpeg_src_buf *jpeg_src_buf; @@ -817,11 +817,11 @@ static void mtk_jpeg_device_run(void *priv) src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf); + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) { - for (i = 0; i < dst_buf->num_planes; i++) - vb2_set_plane_payload(dst_buf, i, 0); + for (i = 0; i < dst_buf->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&dst_buf->vb2_buf, i, 0); buf_state = VB2_BUF_STATE_DONE; goto dec_end; } @@ -833,8 +833,8 @@ static void mtk_jpeg_device_run(void *priv) return; } - mtk_jpeg_set_dec_src(ctx, src_buf, &bs); - if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, dst_buf, &fb)) + mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs); + if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb)) goto dec_end; spin_lock_irqsave(&jpeg->hw_lock, flags); @@ -849,8 +849,8 @@ static void mtk_jpeg_device_run(void *priv) dec_end: v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state); + v4l2_m2m_buf_done(src_buf, buf_state); + v4l2_m2m_buf_done(dst_buf, buf_state); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } @@ -921,7 +921,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) { struct mtk_jpeg_dev *jpeg = priv; struct mtk_jpeg_ctx *ctx; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_jpeg_src_buf *jpeg_src_buf; enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; u32 dec_irq_ret; @@ -938,7 +938,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf); + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW) mtk_jpeg_dec_reset(jpeg->dec_reg_base); @@ -948,15 +948,15 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) goto dec_end; } - for (i = 0; i < dst_buf->num_planes; i++) - vb2_set_plane_payload(dst_buf, i, + for (i = 0; i < dst_buf->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&dst_buf->vb2_buf, i, jpeg_src_buf->dec_param.comp_size[i]); buf_state = VB2_BUF_STATE_DONE; dec_end: - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state); + v4l2_m2m_buf_done(src_buf, buf_state); + v4l2_m2m_buf_done(dst_buf, buf_state); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); return IRQ_HANDLED; } From 9af469c5be3b031e651ca90ea193f09bae0792d3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:40 -0500 Subject: [PATCH 1982/4277] media: mtk-mdp: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 51a13466261e..7d15c06e9db9 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -473,20 +473,17 @@ static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx, static void mtk_mdp_m2m_get_bufs(struct mtk_mdp_ctx *ctx) { struct mtk_mdp_frame *s_frame, *d_frame; - struct vb2_buffer *src_vb, *dst_vb; struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; - src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - mtk_mdp_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); + src_vbuf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + mtk_mdp_prepare_addr(ctx, &src_vbuf->vb2_buf, s_frame, &s_frame->addr); - dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - mtk_mdp_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); + dst_vbuf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + mtk_mdp_prepare_addr(ctx, &dst_vbuf->vb2_buf, d_frame, &d_frame->addr); - src_vbuf = to_vb2_v4l2_buffer(src_vb); - dst_vbuf = to_vb2_v4l2_buffer(dst_vb); dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp; } @@ -494,17 +491,14 @@ static void mtk_mdp_process_done(void *priv, int vb_state) { struct mtk_mdp_dev *mdp = priv; struct mtk_mdp_ctx *ctx; - struct vb2_buffer *src_vb, *dst_vb; - struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL; + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; ctx = v4l2_m2m_get_curr_priv(mdp->m2m_dev); if (!ctx) return; - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - src_vbuf = to_vb2_v4l2_buffer(src_vb); - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - dst_vbuf = to_vb2_v4l2_buffer(dst_vb); + src_vbuf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp; dst_vbuf->timecode = src_vbuf->timecode; From 0650a91499e0102ff658c48895bcc881cbfdf3b5 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:41 -0500 Subject: [PATCH 1983/4277] media: mtk-vcodec: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../platform/mtk-vcodec/mtk_vcodec_dec.c | 62 +++++++--------- .../platform/mtk-vcodec/mtk_vcodec_enc.c | 72 ++++++++----------- 2 files changed, 54 insertions(+), 80 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index ba619647bc10..2a36f0ce6974 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -325,13 +325,12 @@ static void mtk_vdec_worker(struct work_struct *work) struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, decode_work); struct mtk_vcodec_dev *dev = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem buf; struct vdec_fb *pfb; bool res_chg = false; int ret; struct mtk_video_dec_buf *dst_buf_info, *src_buf_info; - struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2; src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (src_buf == NULL) { @@ -347,26 +346,23 @@ static void mtk_vdec_worker(struct work_struct *work) return; } - src_vb2_v4l2 = container_of(src_buf, struct vb2_v4l2_buffer, vb2_buf); - src_buf_info = container_of(src_vb2_v4l2, struct mtk_video_dec_buf, vb); - - dst_vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf); - dst_buf_info = container_of(dst_vb2_v4l2, struct mtk_video_dec_buf, vb); + src_buf_info = container_of(src_buf, struct mtk_video_dec_buf, vb); + dst_buf_info = container_of(dst_buf, struct mtk_video_dec_buf, vb); pfb = &dst_buf_info->frame_buffer; - pfb->base_y.va = vb2_plane_vaddr(dst_buf, 0); - pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + pfb->base_y.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); pfb->base_y.size = ctx->picinfo.y_bs_sz + ctx->picinfo.y_len_sz; - pfb->base_c.va = vb2_plane_vaddr(dst_buf, 1); - pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 1); + pfb->base_c.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 1); + pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1); pfb->base_c.size = ctx->picinfo.c_bs_sz + ctx->picinfo.c_len_sz; pfb->status = 0; mtk_v4l2_debug(3, "===>[%d] vdec_if_decode() ===>", ctx->id); mtk_v4l2_debug(3, "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx", - dst_buf->index, pfb, + dst_buf->vb2_buf.index, pfb, pfb->base_y.va, &pfb->base_y.dma_addr, &pfb->base_c.dma_addr, pfb->base_y.size); @@ -384,19 +380,19 @@ static void mtk_vdec_worker(struct work_struct *work) clean_display_buffer(ctx); vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 0, 0); vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0); - dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST; + dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_m2m_buf_done(&dst_buf_info->vb, VB2_BUF_STATE_DONE); clean_free_buffer(ctx); v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); return; } - buf.va = vb2_plane_vaddr(src_buf, 0); - buf.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + buf.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + buf.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); buf.size = (size_t)src_buf->planes[0].bytesused; if (!buf.va) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); mtk_v4l2_err("[%d] id=%d src_addr is NULL!!", - ctx->id, src_buf->index); + ctx->id, src_buf->vb2_buf.index); return; } mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", @@ -416,10 +412,10 @@ static void mtk_vdec_worker(struct work_struct *work) mtk_v4l2_err( " <===[%d], src_buf[%d] sz=0x%zx pts=%llu dst_buf[%d] vdec_if_decode() ret=%d res_chg=%d===>", ctx->id, - src_buf->index, + src_buf->vb2_buf.index, buf.size, src_buf_info->vb.vb2_buf.timestamp, - dst_buf->index, + dst_buf->vb2_buf.index, ret, res_chg); src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); if (ret == -EIO) { @@ -1103,7 +1099,7 @@ static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) { - struct vb2_buffer *src_buf; + struct vb2_v4l2_buffer *src_buf; struct mtk_vcodec_mem src_mem; bool res_chg = false; int ret = 0; @@ -1149,8 +1145,7 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) mtk_v4l2_err("No src buffer"); return; } - vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); - buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, vb); + buf = container_of(src_buf, struct mtk_video_dec_buf, vb); if (buf->lastframe) { /* This shouldn't happen. Just in case. */ mtk_v4l2_err("Invalid flush buffer."); @@ -1158,8 +1153,8 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) return; } - src_mem.va = vb2_plane_vaddr(src_buf, 0); - src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + src_mem.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); src_mem.size = (size_t)src_buf->planes[0].bytesused; mtk_v4l2_debug(2, "[%d] buf id=%d va=%p dma=%pad size=%zx", @@ -1181,11 +1176,9 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) mtk_v4l2_err("[%d] Unrecoverable error in vdec_if_decode.", ctx->id); ctx->state = MTK_STATE_ABORT; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } else { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); } mtk_v4l2_debug(ret ? 0 : 1, "[%d] vdec_if_decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d", @@ -1281,7 +1274,7 @@ static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) { - struct vb2_buffer *src_buf = NULL, *dst_buf = NULL; + struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL; struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); mtk_v4l2_debug(3, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d", @@ -1289,12 +1282,10 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { - struct vb2_v4l2_buffer *vb2_v4l2 = - to_vb2_v4l2_buffer(src_buf); struct mtk_video_dec_buf *buf_info = container_of( - vb2_v4l2, struct mtk_video_dec_buf, vb); + src_buf, struct mtk_video_dec_buf, vb); if (!buf_info->lastframe) - v4l2_m2m_buf_done(vb2_v4l2, + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } return; @@ -1323,10 +1314,9 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) ctx->state = MTK_STATE_FLUSH; while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { - vb2_set_plane_payload(dst_buf, 0, 0); - vb2_set_plane_payload(dst_buf, 1, 0); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); } } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index d1f12257bf66..c627202fcffd 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -887,7 +887,7 @@ err_set_param: static void vb2ops_venc_stop_streaming(struct vb2_queue *q) { struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; int ret; mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); @@ -895,13 +895,11 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { dst_buf->planes[0].bytesused = 0; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); } } else { while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && @@ -937,8 +935,7 @@ static int mtk_venc_encode_header(void *priv) { struct mtk_vcodec_ctx *ctx = priv; int ret; - struct vb2_buffer *src_buf, *dst_buf; - struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem bs_buf; struct venc_done_result enc_result; @@ -948,14 +945,14 @@ static int mtk_venc_encode_header(void *priv) return -EINVAL; } - bs_buf.va = vb2_plane_vaddr(dst_buf, 0); - bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); bs_buf.size = (size_t)dst_buf->planes[0].length; mtk_v4l2_debug(1, "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", ctx->id, - dst_buf->index, bs_buf.va, + dst_buf->vb2_buf.index, bs_buf.va, (u64)bs_buf.dma_addr, bs_buf.size); @@ -964,26 +961,23 @@ static int mtk_venc_encode_header(void *priv) NULL, &bs_buf, &enc_result); if (ret) { - dst_buf->planes[0].bytesused = 0; + dst_buf->vb2_buf.planes[0].bytesused = 0; ctx->state = MTK_STATE_ABORT; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); mtk_v4l2_err("venc_if_encode failed=%d", ret); return -EINVAL; } src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (src_buf) { - src_vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); - dst_vb2_v4l2 = to_vb2_v4l2_buffer(dst_buf); - dst_buf->timestamp = src_buf->timestamp; - dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode; + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; } else { mtk_v4l2_err("No timestamp for the header buffer."); } ctx->state = MTK_STATE_HEADER; dst_buf->planes[0].bytesused = enc_result.bs_size; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); return 0; } @@ -991,9 +985,7 @@ static int mtk_venc_encode_header(void *priv) static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) { struct venc_enc_param enc_prm; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - struct vb2_v4l2_buffer *vb2_v4l2 = - container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx); struct mtk_video_enc_buf *mtk_buf = container_of(vb2_v4l2, struct mtk_video_enc_buf, vb); @@ -1067,12 +1059,11 @@ static void mtk_venc_worker(struct work_struct *work) { struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, encode_work); - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct venc_frm_buf frm_buf; struct mtk_vcodec_mem bs_buf; struct venc_done_result enc_result; int ret, i; - struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2; /* check dst_buf, dst_buf may be removed in device_run * to stored encdoe header so we need check dst_buf and @@ -1086,15 +1077,15 @@ static void mtk_venc_worker(struct work_struct *work) src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); memset(&frm_buf, 0, sizeof(frm_buf)); - for (i = 0; i < src_buf->num_planes ; i++) { + for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) { frm_buf.fb_addr[i].dma_addr = - vb2_dma_contig_plane_dma_addr(src_buf, i); + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i); frm_buf.fb_addr[i].size = - (size_t)src_buf->planes[i].length; + (size_t)src_buf->vb2_buf.planes[i].length; } - bs_buf.va = vb2_plane_vaddr(dst_buf, 0); - bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - bs_buf.size = (size_t)dst_buf->planes[0].length; + bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; mtk_v4l2_debug(2, "Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu", @@ -1108,28 +1099,21 @@ static void mtk_venc_worker(struct work_struct *work) ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, &frm_buf, &bs_buf, &enc_result); - src_vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); - dst_vb2_v4l2 = to_vb2_v4l2_buffer(dst_buf); - - dst_buf->timestamp = src_buf->timestamp; - dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode; + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; if (enc_result.is_key_frm) - dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; if (ret) { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); dst_buf->planes[0].bytesused = 0; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); mtk_v4l2_err("venc_if_encode failed=%d", ret); } else { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); dst_buf->planes[0].bytesused = enc_result.bs_size; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); mtk_v4l2_debug(2, "venc_if_encode bs size=%d", enc_result.bs_size); } @@ -1137,7 +1121,7 @@ static void mtk_venc_worker(struct work_struct *work) v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", - src_buf->index, dst_buf->index, ret, + src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret, enc_result.bs_size); } From 8d20dcefe471763f23ad538369ec65b51993ffff Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:42 -0500 Subject: [PATCH 1984/4277] media: mx2_emmaprp: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mx2_emmaprp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 27b078cf98e3..f60f499c596b 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -274,7 +274,7 @@ static void emmaprp_device_run(void *priv) { struct emmaprp_ctx *ctx = priv; struct emmaprp_q_data *s_q_data, *d_q_data; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct emmaprp_dev *pcdev = ctx->dev; unsigned int s_width, s_height; unsigned int d_width, d_height; @@ -294,8 +294,8 @@ static void emmaprp_device_run(void *priv) d_height = d_q_data->height; d_size = d_width * d_height; - p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0); - p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + p_in = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + p_out = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); if (!p_in || !p_out) { v4l2_err(&pcdev->v4l2_dev, "Acquiring kernel pointers to buffers failed\n"); From da2d3a4e4adabc6ccfb100bc9abd58ee9cd6c4b7 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:43 -0500 Subject: [PATCH 1985/4277] media: rockchip/rga: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 5c653287185f..b096227a9722 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -43,7 +43,7 @@ static void device_run(void *prv) { struct rga_ctx *ctx = prv; struct rockchip_rga *rga = ctx->rga; - struct vb2_buffer *src, *dst; + struct vb2_v4l2_buffer *src, *dst; unsigned long flags; spin_lock_irqsave(&rga->ctrl_lock, flags); @@ -53,8 +53,8 @@ static void device_run(void *prv) src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_buf_map(src); - rga_buf_map(dst); + rga_buf_map(&src->vb2_buf); + rga_buf_map(&dst->vb2_buf); rga_hw_start(rga); From 30fa627b32230737bc3f678067e2adfecf956987 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:44 -0500 Subject: [PATCH 1986/4277] media: s5p-g2d: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 57ab1d1085d1..971c47165010 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -513,7 +513,7 @@ static void device_run(void *prv) { struct g2d_ctx *ctx = prv; struct g2d_dev *dev = ctx->dev; - struct vb2_buffer *src, *dst; + struct vb2_v4l2_buffer *src, *dst; unsigned long flags; u32 cmd = 0; @@ -528,10 +528,10 @@ static void device_run(void *prv) spin_lock_irqsave(&dev->ctrl_lock, flags); g2d_set_src_size(dev, &ctx->in); - g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0)); + g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0)); g2d_set_dst_size(dev, &ctx->out); - g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0)); + g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0)); g2d_set_rop4(dev, ctx->rop); g2d_set_flip(dev, ctx->flip); From 4a88f89885c7cf65c62793f385261a6e3315178a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:45 -0500 Subject: [PATCH 1987/4277] media: s5p-jpeg: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 0a23b2d19e14..8cc730eccb6c 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -793,14 +793,14 @@ static void skip(struct s5p_jpeg_buffer *buf, long len); static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; unsigned int word; int c, x, components; jpeg_buffer.size = 2; /* Ls */ jpeg_buffer.data = - (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sos + 2; + (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2; jpeg_buffer.curr = 0; word = 0; @@ -830,14 +830,14 @@ static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; unsigned int word; int c, i, n, j; for (j = 0; j < ctx->out_q.dht.n; ++j) { jpeg_buffer.size = ctx->out_q.dht.len[j]; - jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) + + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.dht.marker[j]; jpeg_buffer.curr = 0; @@ -889,13 +889,13 @@ static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; int c, x, components; jpeg_buffer.size = ctx->out_q.sof_len; jpeg_buffer.data = - (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sof; + (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sof; jpeg_buffer.curr = 0; skip(&jpeg_buffer, 5); /* P, Y, X */ @@ -920,14 +920,14 @@ static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; unsigned int word; int c, i, j; for (j = 0; j < ctx->out_q.dqt.n; ++j) { jpeg_buffer.size = ctx->out_q.dqt.len[j]; - jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) + + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.dqt.marker[j]; jpeg_buffer.curr = 0; @@ -2075,15 +2075,15 @@ static void s5p_jpeg_device_run(void *priv) { struct s5p_jpeg_ctx *ctx = priv; struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; unsigned long src_addr, dst_addr, flags; spin_lock_irqsave(&ctx->jpeg->slock, flags); src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); s5p_jpeg_reset(jpeg->regs); s5p_jpeg_poweron(jpeg->regs); @@ -2156,7 +2156,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; struct s5p_jpeg_fmt *fmt; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; struct s5p_jpeg_addr jpeg_addr = {}; u32 pix_size, padding_bytes = 0; @@ -2175,7 +2175,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); } - jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); if (fmt->colplanes == 2) { jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes; @@ -2193,7 +2193,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; unsigned int jpeg_addr = 0; if (ctx->mode == S5P_JPEG_ENCODE) @@ -2201,7 +2201,7 @@ static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) else vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); if (jpeg->variant->version == SJPEG_EXYNOS5433 && ctx->mode == S5P_JPEG_DECODE) jpeg_addr += ctx->out_q.sos; @@ -2317,7 +2317,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; struct s5p_jpeg_fmt *fmt; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; struct s5p_jpeg_addr jpeg_addr = {}; u32 pix_size; @@ -2331,7 +2331,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) fmt = ctx->cap_q.fmt; } - jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); if (fmt->colplanes == 2) { jpeg_addr.cb = jpeg_addr.y + pix_size; @@ -2349,7 +2349,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; unsigned int jpeg_addr = 0; if (ctx->mode == S5P_JPEG_ENCODE) @@ -2357,7 +2357,7 @@ static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) else vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); exynos3250_jpeg_jpgadr(jpeg->regs, jpeg_addr); } From 43c145195c7fc3025ee7ecfc67112ac1c82af7c2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:46 -0500 Subject: [PATCH 1988/4277] media: sh_veu: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 09ae64a0004c..d277cc674349 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -273,13 +273,13 @@ static void sh_veu_process(struct sh_veu_dev *veu, static void sh_veu_device_run(void *priv) { struct sh_veu_dev *veu = priv; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); if (src_buf && dst_buf) - sh_veu_process(veu, src_buf, dst_buf); + sh_veu_process(veu, &src_buf->vb2_buf, &dst_buf->vb2_buf); } /* ========== video ioctls ========== */ From 29701c3612fa025d5e8dc64c7a4ae8dc4763912e Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:47 -0500 Subject: [PATCH 1989/4277] media: rockchip/vpu: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c | 6 +++--- drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c index 5282236d1bb1..06daea66fb49 100644 --- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c @@ -80,7 +80,7 @@ rk3288_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu, void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) { struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; u32 reg; @@ -88,7 +88,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -99,7 +99,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) VEPU_REG_ENC_CTRL); rk3288_vpu_set_src_img_ctrl(vpu, ctx); - rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf); + rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3288_vpu_jpeg_enc_set_qtable(vpu, rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0), rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1)); diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c index dbc86d95fe3b..3d438797692e 100644 --- a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c @@ -111,7 +111,7 @@ rk3399_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu, void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) { struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; u32 reg; @@ -119,7 +119,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -130,7 +130,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) VEPU_REG_ENCODE_START); rk3399_vpu_set_src_img_ctrl(vpu, ctx); - rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf); + rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3399_vpu_jpeg_enc_set_qtable(vpu, rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0), rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1)); From 8dd22b289c8fab1dcb13ab038c0228aa74f134ca Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:48 -0500 Subject: [PATCH 1990/4277] media: v4l2-mem2mem: Correct return type for mem2mem buffer helpers This commit changes the return type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove which currently return void pointer. In every case, the actual return type is a struct vb2_v4l2_buffer pointer. Change the return type of the listed functions, so type checking can be properly used. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: clean up line-too-long checkpatch warnings] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- include/media/v4l2-mem2mem.h | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 57eccbaa52de..3392833d9541 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -131,7 +131,7 @@ struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL(v4l2_m2m_get_vq); -void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) +struct vb2_v4l2_buffer *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; unsigned long flags; @@ -149,7 +149,7 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); -void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) +struct vb2_v4l2_buffer *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; unsigned long flags; @@ -167,7 +167,7 @@ void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_last_buf); -void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) +struct vb2_v4l2_buffer *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; unsigned long flags; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 47c6d9aa0bf4..bb3e63d6bd1a 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -425,7 +425,7 @@ unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx) * * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx */ -void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); +struct vb2_v4l2_buffer *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_next_src_buf() - return next source buffer from the list of ready @@ -433,7 +433,8 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_next_buf(&m2m_ctx->out_q_ctx); } @@ -444,7 +445,8 @@ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_next_buf(&m2m_ctx->cap_q_ctx); } @@ -454,7 +456,7 @@ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) * * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx */ -void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); +struct vb2_v4l2_buffer *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_last_src_buf() - return last destination buffer from the list of @@ -462,7 +464,8 @@ void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_last_buf(&m2m_ctx->out_q_ctx); } @@ -473,7 +476,8 @@ static inline void *v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_last_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_last_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_last_buf(&m2m_ctx->cap_q_ctx); } @@ -547,7 +551,7 @@ struct vb2_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx) * * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx */ -void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); +struct vb2_v4l2_buffer *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_src_buf_remove() - take off a source buffer from the list of ready @@ -555,7 +559,8 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_buf_remove(&m2m_ctx->out_q_ctx); } @@ -566,7 +571,8 @@ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx); } From ffaec3b21abcce537be166bd023f55986a5b2002 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 13 Feb 2019 08:25:59 -0500 Subject: [PATCH 1991/4277] media: exynos4-is: remove redundant check on type The check to see if type is V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE is redundant as this has been already checked at the start of the function and if it's not that value then -ENOSYS is returned. Hence the sprintf can be replaced by a simpler string copy. Detected by CoverityScan, CID#1309450 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-isp-video.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index de6bd28f7e31..bb35a2017f21 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -606,9 +606,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, vdev = &iv->ve.vdev; memset(vdev, 0, sizeof(*vdev)); - snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s", - type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? - "capture" : "output"); + strscpy(vdev->name, "fimc-is-isp.capture", sizeof(vdev->name)); vdev->queue = q; vdev->fops = &isp_video_fops; vdev->ioctl_ops = &isp_video_ioctl_ops; From 9dacde5ed0645a8db2dbfd0951c24b9eed87cc60 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 14 Feb 2019 04:25:11 -0500 Subject: [PATCH 1992/4277] media: cedrus: Forbid setting new formats on busy queues Check that our queues are not busy before setting the format or return EBUSY if that's the case. This ensures that our format can't change once buffers are allocated for the queue. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_video.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index b5cc79389d67..b47854b3bce4 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -282,8 +282,13 @@ static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, { struct cedrus_ctx *ctx = cedrus_file2ctx(file); struct cedrus_dev *dev = ctx->dev; + struct vb2_queue *vq; int ret; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + ret = cedrus_try_fmt_vid_cap(file, priv, f); if (ret) return ret; @@ -299,8 +304,13 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct vb2_queue *vq; int ret; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + ret = cedrus_try_fmt_vid_out(file, priv, f); if (ret) return ret; From 73a1d43293069eb19642cf7c110f229846a52213 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 14 Feb 2019 04:44:03 -0500 Subject: [PATCH 1993/4277] media: cedrus: mpeg2: Use v4l2_m2m_get_vq helper for capture queue Since there's a v4l2_m2m_get_vq helper, use it instead of accessing the queue directly, which slightly increases readability. Also reformat the code using the queue a bit while at it. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index cb45fda9aaeb..13c34927bad5 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -82,7 +82,7 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t fwd_luma_addr, fwd_chroma_addr; dma_addr_t bwd_luma_addr, bwd_chroma_addr; struct cedrus_dev *dev = ctx->dev; - struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; + struct vb2_queue *vq; const u8 *matrix; int forward_idx; int backward_idx; @@ -159,17 +159,17 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - forward_idx = vb2_find_timestamp(cap_q, - slice_params->forward_ref_ts, 0); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + forward_idx = vb2_find_timestamp(vq, slice_params->forward_ref_ts, 0); fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - backward_idx = vb2_find_timestamp(cap_q, - slice_params->backward_ref_ts, 0); + backward_idx = vb2_find_timestamp(vq, slice_params->backward_ref_ts, 0); bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); From 75e3e5b85da199e0c9a885c800933d74ee53ce3c Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 15 Feb 2019 15:58:53 -0500 Subject: [PATCH 1994/4277] media: vicodec: Add a flag for I-frames in fwht header Add a flag 'FWHT_FL_I_FRAME' that indicates that this is an I-frame. This requires incrementing to version 3 This flag is needed for the upcoming stateless FWHT decoder since it has to know if an encoded frame is an I or a P frame. Signed-off-by: Dafna Hirschfeld [hverkuil-cisco@xs4all.nl: added the last paragraph of the commit msg] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.h | 3 ++- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 4 +++- drivers/media/platform/vicodec/vicodec-core.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 60d71d9dacb3..c410512d47c5 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -56,7 +56,7 @@ #define FWHT_MAGIC1 0x4f4f4f4f #define FWHT_MAGIC2 0xffffffff -#define FWHT_VERSION 2 +#define FWHT_VERSION 3 /* Set if this is an interlaced format */ #define FWHT_FL_IS_INTERLACED BIT(0) @@ -76,6 +76,7 @@ #define FWHT_FL_CHROMA_FULL_HEIGHT BIT(7) #define FWHT_FL_CHROMA_FULL_WIDTH BIT(8) #define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9) +#define FWHT_FL_I_FRAME BIT(10) /* A 4-values flag - the number of components - 1 */ #define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index c15034849133..6573a471c5ca 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -218,6 +218,8 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags |= FWHT_FL_CR_IS_UNCOMPRESSED; if (encoding & FWHT_ALPHA_UNENCODED) flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED; + if (!(encoding & FWHT_FRAME_PCODED)) + flags |= FWHT_FL_I_FRAME; if (rf.height_div == 1) flags |= FWHT_FL_CHROMA_FULL_HEIGHT; if (rf.width_div == 1) @@ -265,7 +267,7 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags = ntohl(state->header.flags); - if (version == FWHT_VERSION) { + if (version >= 2) { if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) return -EINVAL; components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 9d739ea5542d..d7636fe9e174 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -339,7 +339,7 @@ info_from_header(const struct fwht_cframe_hdr *p_hdr) unsigned int pixenc = 0; unsigned int version = ntohl(p_hdr->version); - if (version == FWHT_VERSION) { + if (version >= 2) { components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> FWHT_FL_COMPONENTS_NUM_OFFSET); pixenc = (flags & FWHT_FL_PIXENC_MSK); @@ -362,7 +362,7 @@ static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr) if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) return false; - if (version == FWHT_VERSION) { + if (version >= 2) { unsigned int components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> FWHT_FL_COMPONENTS_NUM_OFFSET); From 12aceee1f412c3ddc7750155fec06c906f14ab51 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 17 Feb 2019 10:17:47 -0500 Subject: [PATCH 1995/4277] media: ov7740: fix runtime pm initialization The runtime PM of this device is enabled after v4l2_ctrl_handler_setup(), and this makes this device's runtime PM usage count a negative value. The ov7740_set_ctrl() tries to do something only if the device's runtime PM usage counter is nonzero. ov7740_set_ctrl() { if (!pm_runtime_get_if_in_use(&client->dev)) return 0; ; pm_runtime_put(&client->dev); return ret; } However, the ov7740_set_ctrl() is called by v4l2_ctrl_handler_setup() while the runtime PM of this device is not yet enabled. In this case, the pm_runtime_get_if_in_use() returns -EINVAL (!= 0). Therefore we can't bail out of this function and the usage count is decreased by pm_runtime_put() without increment. This fixes this problem by enabling the runtime PM of this device before v4l2_ctrl_handler_setup() so that the ov7740_set_ctrl() is always called when the runtime PM is enabled. Cc: Wenyou Yang Signed-off-by: Akinobu Mita Tested-by: Eugen Hristev Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7740.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 177688afd9a6..8835b831cdc0 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1101,6 +1101,9 @@ static int ov7740_probe(struct i2c_client *client, if (ret) return ret; + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + ret = ov7740_detect(ov7740); if (ret) goto error_detect; @@ -1123,8 +1126,6 @@ static int ov7740_probe(struct i2c_client *client, if (ret) goto error_async_register; - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); return 0; @@ -1134,6 +1135,8 @@ error_async_register: error_init_controls: ov7740_free_controls(ov7740); error_detect: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); ov7740_set_power(ov7740, 0); media_entity_cleanup(&ov7740->subdev.entity); From 721074b03411327e7bf41555d4cc7c18f49313f7 Mon Sep 17 00:00:00 2001 From: Patrick Lerda Date: Thu, 17 Jan 2019 03:50:13 -0500 Subject: [PATCH 1996/4277] media: rc: rcmm decoder and encoder media: add support for RCMM infrared remote controls. Signed-off-by: Patrick Lerda Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/lirc.h.rst.exceptions | 3 + MAINTAINERS | 5 + drivers/media/rc/Kconfig | 13 ++ drivers/media/rc/Makefile | 1 + drivers/media/rc/ir-rcmm-decoder.c | 254 ++++++++++++++++++++++ drivers/media/rc/rc-core-priv.h | 5 + drivers/media/rc/rc-main.c | 9 + include/media/rc-map.h | 14 +- include/uapi/linux/lirc.h | 6 + tools/include/uapi/linux/lirc.h | 12 + tools/testing/selftests/ir/ir_loopback.c | 9 + 11 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 drivers/media/rc/ir-rcmm-decoder.c diff --git a/Documentation/media/lirc.h.rst.exceptions b/Documentation/media/lirc.h.rst.exceptions index 379b9e7df5d0..7a8b8ff4f076 100644 --- a/Documentation/media/lirc.h.rst.exceptions +++ b/Documentation/media/lirc.h.rst.exceptions @@ -60,6 +60,9 @@ ignore symbol RC_PROTO_SHARP ignore symbol RC_PROTO_XMP ignore symbol RC_PROTO_CEC ignore symbol RC_PROTO_IMON +ignore symbol RC_PROTO_RCMM12 +ignore symbol RC_PROTO_RCMM24 +ignore symbol RC_PROTO_RCMM32 # Undocumented macros diff --git a/MAINTAINERS b/MAINTAINERS index 914d333ee73d..9662ad6c58e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16536,6 +16536,11 @@ M: David Härdeman S: Maintained F: drivers/media/rc/winbond-cir.c +RCMM REMOTE CONTROLS DECODER +M: Patrick Lerda +S: Maintained +F: drivers/media/rc/ir-rcmm-decoder.c + WINSYSTEMS EBC-C384 WATCHDOG DRIVER M: William Breathitt Gray L: linux-watchdog@vger.kernel.org diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 8a216068a35a..8e95f6eac89d 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -133,6 +133,19 @@ config IR_IMON_DECODER remote control and you would like to use it with a raw IR receiver, or if you wish to use an encoder to transmit this IR. +config IR_RCMM_DECODER + tristate "Enable IR raw decoder for the RC-MM protocol" + depends on RC_CORE + help + Enable this option when you have IR with RC-MM protocol, and + you need the software decoder. The driver supports 12, + 24 and 32 bits RC-MM variants. You can enable or disable the + different modes using the following RC protocol keywords: + 'rc-mm-12', 'rc-mm-24' and 'rc-mm-32'. + + To compile this driver as a module, choose M here: the module + will be called ir-rcmm-decoder. + endif #RC_DECODERS menuconfig RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 92c163816849..48d23433b3c0 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o +obj-$(CONFIG_IR_RCMM_DECODER) += ir-rcmm-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o diff --git a/drivers/media/rc/ir-rcmm-decoder.c b/drivers/media/rc/ir-rcmm-decoder.c new file mode 100644 index 000000000000..f1096ac1e5c5 --- /dev/null +++ b/drivers/media/rc/ir-rcmm-decoder.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ +// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol +// +// Copyright (C) 2018 by Patrick Lerda + +#include "rc-core-priv.h" +#include +#include + +#define RCMM_UNIT 166667 /* nanosecs */ +#define RCMM_PREFIX_PULSE 416666 /* 166666.666666666*2.5 */ +#define RCMM_PULSE_0 277777 /* 166666.666666666*(1+2/3) */ +#define RCMM_PULSE_1 444444 /* 166666.666666666*(2+2/3) */ +#define RCMM_PULSE_2 611111 /* 166666.666666666*(3+2/3) */ +#define RCMM_PULSE_3 777778 /* 166666.666666666*(4+2/3) */ + +enum rcmm_state { + STATE_INACTIVE, + STATE_LOW, + STATE_BUMP, + STATE_VALUE, + STATE_FINISHED, +}; + +static bool rcmm_mode(const struct rcmm_dec *data) +{ + return !((0x000c0000 & data->bits) == 0x000c0000); +} + +static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data) +{ + switch (data->count) { + case 24: + if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) { + rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0); + data->state = STATE_INACTIVE; + return 0; + } + return -1; + + case 12: + if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) { + rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0); + data->state = STATE_INACTIVE; + return 0; + } + return -1; + } + + return -1; +} + +/** + * ir_rcmm_decode() - Decode one RCMM pulse or space + * @dev: the struct rc_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct rcmm_dec *data = &dev->raw->rcmm; + u32 scancode; + u8 toggle; + int value; + + if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 | + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM12))) + return 0; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + switch (data->state) { + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT / 2)) + break; + + data->state = STATE_LOW; + data->count = 0; + data->bits = 0; + return 0; + + case STATE_LOW: + if (ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2)) + break; + + data->state = STATE_BUMP; + return 0; + + case STATE_BUMP: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2)) + break; + + data->state = STATE_VALUE; + return 0; + + case STATE_VALUE: + if (ev.pulse) + break; + + if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2)) + value = 0; + else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2)) + value = 1; + else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2)) + value = 2; + else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2)) + value = 3; + else + value = -1; + + if (value == -1) { + if (!rcmm_miscmode(dev, data)) + return 0; + break; + } + + data->bits <<= 2; + data->bits |= value; + + data->count += 2; + + if (data->count < 32) + data->state = STATE_BUMP; + else + data->state = STATE_FINISHED; + + return 0; + + case STATE_FINISHED: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2)) + break; + + if (rcmm_mode(data)) { + toggle = !!(0x8000 & data->bits); + scancode = data->bits & ~0x8000; + } else { + toggle = 0; + scancode = data->bits; + } + + if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) { + rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle); + data->state = STATE_INACTIVE; + return 0; + } + + break; + } + + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static const int rcmmspace[] = { + RCMM_PULSE_0, + RCMM_PULSE_1, + RCMM_PULSE_2, + RCMM_PULSE_3, +}; + +static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max, + unsigned int n, u32 data) +{ + int i; + int ret; + + ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0); + if (ret) + return ret; + + for (i = n - 2; i >= 0; i -= 2) { + const unsigned int space = rcmmspace[(data >> i) & 3]; + + ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space); + if (ret) + return ret; + } + + return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2); +} + +static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode, + struct ir_raw_event *events, unsigned int max) +{ + struct ir_raw_event *e = events; + int ret; + + switch (protocol) { + case RC_PROTO_RCMM32: + ret = ir_rcmm_rawencoder(&e, max, 32, scancode); + break; + case RC_PROTO_RCMM24: + ret = ir_rcmm_rawencoder(&e, max, 24, scancode); + break; + case RC_PROTO_RCMM12: + ret = ir_rcmm_rawencoder(&e, max, 12, scancode); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + return e - events; +} + +static struct ir_raw_handler rcmm_handler = { + .protocols = RC_PROTO_BIT_RCMM32 | + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM12, + .decode = ir_rcmm_decode, + .encode = ir_rcmm_encode, + .carrier = 36000, + .min_timeout = RCMM_PULSE_3 + RCMM_UNIT, +}; + +static int __init ir_rcmm_decode_init(void) +{ + ir_raw_handler_register(&rcmm_handler); + + pr_info("IR RCMM protocol handler initialized\n"); + return 0; +} + +static void __exit ir_rcmm_decode_exit(void) +{ + ir_raw_handler_unregister(&rcmm_handler); +} + +module_init(ir_rcmm_decode_init); +module_exit(ir_rcmm_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick Lerda"); +MODULE_DESCRIPTION("RCMM IR protocol decoder"); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index c2cbe7f6266c..9f21b3e8b377 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -131,6 +131,11 @@ struct ir_raw_event_ctrl { unsigned int bits; bool stick_keyboard; } imon; + struct rcmm_dec { + int state; + unsigned int count; + u32 bits; + } rcmm; }; /* Mutex for locking raw IR processing and handler change */ diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 66a174979b3c..dc38e9c0a2ff 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -70,6 +70,12 @@ static const struct { [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 0 }, [RC_PROTO_IMON] = { .name = "imon", .scancode_bits = 0x7fffffff, .repeat_period = 114 }, + [RC_PROTO_RCMM12] = { .name = "rc-mm-12", + .scancode_bits = 0x00000fff, .repeat_period = 114 }, + [RC_PROTO_RCMM24] = { .name = "rc-mm-24", + .scancode_bits = 0x00ffffff, .repeat_period = 114 }, + [RC_PROTO_RCMM32] = { .name = "rc-mm-32", + .scancode_bits = 0xffffffff, .repeat_period = 114 }, }; /* Used to keep track of known keymaps */ @@ -1006,6 +1012,9 @@ static const struct { { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, { RC_PROTO_BIT_CEC, "cec", NULL }, { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" }, + { RC_PROTO_BIT_RCMM12 | + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM32, "rc-mm", "ir-rcmm-decoder" }, }; /** diff --git a/include/media/rc-map.h b/include/media/rc-map.h index d621acadfbf3..e5e86d595645 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -37,6 +37,9 @@ #define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP) #define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC) #define RC_PROTO_BIT_IMON BIT_ULL(RC_PROTO_IMON) +#define RC_PROTO_BIT_RCMM12 BIT_ULL(RC_PROTO_RCMM12) +#define RC_PROTO_BIT_RCMM24 BIT_ULL(RC_PROTO_RCMM24) +#define RC_PROTO_BIT_RCMM32 BIT_ULL(RC_PROTO_RCMM32) #define RC_PROTO_BIT_ALL \ (RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \ @@ -51,7 +54,8 @@ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC | \ - RC_PROTO_BIT_IMON) + RC_PROTO_BIT_IMON | RC_PROTO_BIT_RCMM12 | \ + RC_PROTO_BIT_RCMM24 | RC_PROTO_BIT_RCMM32) /* All rc protocols for which we have decoders */ #define RC_PROTO_BIT_ALL_IR_DECODER \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ @@ -64,7 +68,9 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ - RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON) + RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON | \ + RC_PROTO_BIT_RCMM12 | RC_PROTO_BIT_RCMM24 | \ + RC_PROTO_BIT_RCMM32) #define RC_PROTO_BIT_ALL_IR_ENCODER \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ @@ -77,7 +83,9 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | \ RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \ - RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON) + RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON | \ + RC_PROTO_BIT_RCMM12 | RC_PROTO_BIT_RCMM24 | \ + RC_PROTO_BIT_RCMM32) #define RC_SCANCODE_UNKNOWN(x) (x) #define RC_SCANCODE_OTHER(x) (x) diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h index 6b319581882f..45fcbf99d72e 100644 --- a/include/uapi/linux/lirc.h +++ b/include/uapi/linux/lirc.h @@ -192,6 +192,9 @@ struct lirc_scancode { * @RC_PROTO_XMP: XMP protocol * @RC_PROTO_CEC: CEC protocol * @RC_PROTO_IMON: iMon Pad protocol + * @RC_PROTO_RCMM12: RC-MM protocol 12 bits + * @RC_PROTO_RCMM24: RC-MM protocol 24 bits + * @RC_PROTO_RCMM32: RC-MM protocol 32 bits */ enum rc_proto { RC_PROTO_UNKNOWN = 0, @@ -218,6 +221,9 @@ enum rc_proto { RC_PROTO_XMP = 21, RC_PROTO_CEC = 22, RC_PROTO_IMON = 23, + RC_PROTO_RCMM12 = 24, + RC_PROTO_RCMM24 = 25, + RC_PROTO_RCMM32 = 26, }; #endif diff --git a/tools/include/uapi/linux/lirc.h b/tools/include/uapi/linux/lirc.h index f189931042a7..45fcbf99d72e 100644 --- a/tools/include/uapi/linux/lirc.h +++ b/tools/include/uapi/linux/lirc.h @@ -133,6 +133,12 @@ #define LIRC_SET_WIDEBAND_RECEIVER _IOW('i', 0x00000023, __u32) +/* + * Return the recording timeout, which is either set by + * the ioctl LIRC_SET_REC_TIMEOUT or by the kernel after setting the protocols. + */ +#define LIRC_GET_REC_TIMEOUT _IOR('i', 0x00000024, __u32) + /* * struct lirc_scancode - decoded scancode with protocol for use with * LIRC_MODE_SCANCODE @@ -186,6 +192,9 @@ struct lirc_scancode { * @RC_PROTO_XMP: XMP protocol * @RC_PROTO_CEC: CEC protocol * @RC_PROTO_IMON: iMon Pad protocol + * @RC_PROTO_RCMM12: RC-MM protocol 12 bits + * @RC_PROTO_RCMM24: RC-MM protocol 24 bits + * @RC_PROTO_RCMM32: RC-MM protocol 32 bits */ enum rc_proto { RC_PROTO_UNKNOWN = 0, @@ -212,6 +221,9 @@ enum rc_proto { RC_PROTO_XMP = 21, RC_PROTO_CEC = 22, RC_PROTO_IMON = 23, + RC_PROTO_RCMM12 = 24, + RC_PROTO_RCMM24 = 25, + RC_PROTO_RCMM32 = 26, }; #endif diff --git a/tools/testing/selftests/ir/ir_loopback.c b/tools/testing/selftests/ir/ir_loopback.c index 858c19caf224..570a7358942c 100644 --- a/tools/testing/selftests/ir/ir_loopback.c +++ b/tools/testing/selftests/ir/ir_loopback.c @@ -51,6 +51,10 @@ static const struct { { RC_PROTO_RC6_6A_32, "rc-6-6a-32", 0xffffffff, "rc-6" }, { RC_PROTO_RC6_MCE, "rc-6-mce", 0x00007fff, "rc-6" }, { RC_PROTO_SHARP, "sharp", 0x1fff, "sharp" }, + { RC_PROTO_IMON, "imon", 0x7fffffff, "imon" }, + { RC_PROTO_RCMM12, "rcmm-12", 0x00000fff, "rcmm" }, + { RC_PROTO_RCMM24, "rcmm-24", 0x00ffffff, "rcmm" }, + { RC_PROTO_RCMM32, "rcmm-32", 0xffffffff, "rcmm" }, }; int lirc_open(const char *rc) @@ -139,6 +143,11 @@ int main(int argc, char **argv) (((scancode >> 8) ^ ~scancode) & 0xff) == 0) continue; + if (rc_proto == RC_PROTO_RCMM32 && + (scancode & 0x000c0000) != 0x000c0000 && + scancode & 0x00008000) + continue; + struct lirc_scancode lsc = { .rc_proto = rc_proto, .scancode = scancode From b1f37757dc3befef88fba3961410627e43795f13 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Thu, 7 Feb 2019 04:29:12 -0500 Subject: [PATCH 1997/4277] media: rc: ir-rc6-decoder: enable toggle bit for Zotac remotes The Zotac RC2604323/01G and RC2604329/02BG remotes use the 32-bit rc6 protocol and toggle bit 15 (0x8000) on repeated button presses, like MCE remotes. Add the customer code 0x80340000 to the 32-bit rc6 toggle handling code to get proper scancodes and toggle reports. Signed-off-by: Matthias Reichl Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-rc6-decoder.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index d96aed1343e4..5cc302fa4daa 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -40,6 +40,7 @@ #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ #define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ #define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ +#define RC6_6A_ZOTAC_CC 0x80340000 /* Zotac customer code */ #define RC6_6A_KATHREIN_CC 0x80460000 /* Kathrein RCU-676 customer code */ #ifndef CHAR_BIT #define CHAR_BIT 8 /* Normally in */ @@ -246,6 +247,7 @@ again: switch (scancode & RC6_6A_LCC_MASK) { case RC6_6A_MCE_CC: case RC6_6A_KATHREIN_CC: + case RC6_6A_ZOTAC_CC: protocol = RC_PROTO_RC6_MCE; toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); scancode &= ~RC6_6A_MCE_TOGGLE_MASK; From a49a7a4635dea5b799a8f77e227ef5c648fa29ec Mon Sep 17 00:00:00 2001 From: Patrick Lerda Date: Wed, 23 Jan 2019 19:04:20 -0500 Subject: [PATCH 1998/4277] media: smipcie: add universal ir capability smipcie: switch to RC_DRIVER_IR_RAW. Signed-off-by: Patrick Lerda Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-ir.c | 130 +++++++++---------------- drivers/media/pci/smipcie/smipcie.h | 1 - 2 files changed, 44 insertions(+), 87 deletions(-) diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index c5595af6b976..d292cdfb3671 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -16,6 +16,9 @@ #include "smipcie.h" +#define SMI_SAMPLE_PERIOD 83 +#define SMI_SAMPLE_IDLEMIN (10000 / SMI_SAMPLE_PERIOD) + static void smi_ir_enableInterrupt(struct smi_rc *ir) { struct smi_dev *dev = ir->dev; @@ -42,114 +45,64 @@ static void smi_ir_stop(struct smi_rc *ir) struct smi_dev *dev = ir->dev; smi_ir_disableInterrupt(ir); - smi_clear(IR_Init_Reg, 0x80); + smi_clear(IR_Init_Reg, rbIRen); } -#define BITS_PER_COMMAND 14 -#define GROUPS_PER_BIT 2 -#define IR_RC5_MIN_BIT 36 -#define IR_RC5_MAX_BIT 52 -static u32 smi_decode_rc5(u8 *pData, u8 size) +static void smi_raw_process(struct rc_dev *rc_dev, const u8 *buffer, + const u8 length) { - u8 index, current_bit, bit_count; - u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4]; - u8 group_index = 0; - u32 command = 0xFFFFFFFF; + struct ir_raw_event rawir = {}; + int cnt; - group_array[group_index++] = 1; - - for (index = 0; index < size; index++) { - - current_bit = (pData[index] & 0x80) ? 1 : 0; - bit_count = pData[index] & 0x7f; - - if ((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) { - goto process_code; - } else if ((bit_count >= IR_RC5_MIN_BIT) && - (bit_count <= IR_RC5_MAX_BIT)) { - group_array[group_index++] = current_bit; - } else if ((bit_count > IR_RC5_MAX_BIT) && - (bit_count <= 2*IR_RC5_MAX_BIT)) { - group_array[group_index++] = current_bit; - group_array[group_index++] = current_bit; - } else { - goto invalid_timing; - } - if (group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT) - goto process_code; - - if ((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1) - && (group_array[group_index-1] == 0)) { - group_array[group_index++] = 1; - goto process_code; + for (cnt = 0; cnt < length; cnt++) { + if (buffer[cnt] & 0x7f) { + rawir.pulse = (buffer[cnt] & 0x80) == 0; + rawir.duration = ((buffer[cnt] & 0x7f) + + (rawir.pulse ? 0 : -1)) * + rc_dev->rx_resolution; + ir_raw_event_store_with_filter(rc_dev, &rawir); } } - -process_code: - if (group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1)) - group_array[group_index++] = 1; - - if (group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) { - command = 0; - for (index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT); - index = index + 2) { - if ((group_array[index] == 1) && - (group_array[index+1] == 0)) { - command |= (1 << (BITS_PER_COMMAND - - (index/2) - 1)); - } else if ((group_array[index] == 0) && - (group_array[index+1] == 1)) { - /* */ - } else { - command = 0xFFFFFFFF; - goto invalid_timing; - } - } - } - -invalid_timing: - return command; } -static void smi_ir_decode(struct work_struct *work) +static void smi_ir_decode(struct smi_rc *ir) { - struct smi_rc *ir = container_of(work, struct smi_rc, work); struct smi_dev *dev = ir->dev; struct rc_dev *rc_dev = ir->rc_dev; - u32 dwIRControl, dwIRData, dwIRCode, scancode; - u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle; + u32 dwIRControl, dwIRData; + u8 index, ucIRCount, readLoop; dwIRControl = smi_read(IR_Init_Reg); + if (dwIRControl & rbIRVld) { ucIRCount = (u8) smi_read(IR_Data_Cnt); - if (ucIRCount < 4) - goto end_ir_decode; - readLoop = ucIRCount/4; if (ucIRCount % 4) readLoop += 1; for (index = 0; index < readLoop; index++) { - dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4)); + dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index * 4)); ir->irData[index*4 + 0] = (u8)(dwIRData); ir->irData[index*4 + 1] = (u8)(dwIRData >> 8); ir->irData[index*4 + 2] = (u8)(dwIRData >> 16); ir->irData[index*4 + 3] = (u8)(dwIRData >> 24); } - dwIRCode = smi_decode_rc5(ir->irData, ucIRCount); - - if (dwIRCode != 0xFFFFFFFF) { - rc5_command = dwIRCode & 0x3F; - rc5_system = (dwIRCode & 0x7C0) >> 6; - toggle = (dwIRCode & 0x800) ? 1 : 0; - scancode = rc5_system << 8 | rc5_command; - rc_keydown(rc_dev, RC_PROTO_RC5, scancode, toggle); - } + smi_raw_process(rc_dev, ir->irData, ucIRCount); + smi_set(IR_Init_Reg, rbIRVld); } -end_ir_decode: - smi_set(IR_Init_Reg, 0x04); - smi_ir_enableInterrupt(ir); + + if (dwIRControl & rbIRhighidle) { + struct ir_raw_event rawir = {}; + + rawir.pulse = 0; + rawir.duration = US_TO_NS(SMI_SAMPLE_PERIOD * + SMI_SAMPLE_IDLEMIN); + ir_raw_event_store_with_filter(rc_dev, &rawir); + smi_set(IR_Init_Reg, rbIRhighidle); + } + + ir_raw_event_handle(rc_dev); } /* ir functions call by main driver.*/ @@ -160,7 +113,8 @@ int smi_ir_irq(struct smi_rc *ir, u32 int_status) if (int_status & IR_X_INT) { smi_ir_disableInterrupt(ir); smi_ir_clearInterrupt(ir); - schedule_work(&ir->work); + smi_ir_decode(ir); + smi_ir_enableInterrupt(ir); handled = 1; } return handled; @@ -170,9 +124,11 @@ void smi_ir_start(struct smi_rc *ir) { struct smi_dev *dev = ir->dev; - smi_write(IR_Idle_Cnt_Low, 0x00140070); + smi_write(IR_Idle_Cnt_Low, + (((SMI_SAMPLE_PERIOD - 1) & 0xFFFF) << 16) | + (SMI_SAMPLE_IDLEMIN & 0xFFFF)); msleep(20); - smi_set(IR_Init_Reg, 0x90); + smi_set(IR_Init_Reg, rbIRen | rbIRhighidle); smi_ir_enableInterrupt(ir); } @@ -183,7 +139,7 @@ int smi_ir_init(struct smi_dev *dev) struct rc_dev *rc_dev; struct smi_rc *ir = &dev->ir; - rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); + rc_dev = rc_allocate_device(RC_DRIVER_IR_RAW); if (!rc_dev) return -ENOMEM; @@ -193,6 +149,7 @@ int smi_ir_init(struct smi_dev *dev) snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0", pci_name(dev->pci_dev)); + rc_dev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; rc_dev->driver_name = "SMI_PCIe"; rc_dev->input_phys = ir->input_phys; rc_dev->device_name = ir->device_name; @@ -203,11 +160,12 @@ int smi_ir_init(struct smi_dev *dev) rc_dev->dev.parent = &dev->pci_dev->dev; rc_dev->map_name = dev->info->rc_map; + rc_dev->timeout = MS_TO_NS(100); + rc_dev->rx_resolution = US_TO_NS(SMI_SAMPLE_PERIOD); ir->rc_dev = rc_dev; ir->dev = dev; - INIT_WORK(&ir->work, smi_ir_decode); smi_ir_disableInterrupt(ir); ret = rc_register_device(rc_dev); diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index a6c5b1bd7edb..e52229a87b84 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -241,7 +241,6 @@ struct smi_rc { struct rc_dev *rc_dev; char input_phys[64]; char device_name[64]; - struct work_struct work; u8 irData[256]; int users; From 174bcae1a4e98e1d5c35cd878bdf282e56287e8f Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 6 Feb 2019 11:11:37 -0500 Subject: [PATCH 1999/4277] media: staging/imx: refactor imx media device probe Refactor and move media device initialization code to a new common module, so it can be used by other devices, this will allow for example a near to introduce imx7 CSI driver, to use this media device. Signed-off-by: Rui Miguel Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/Makefile | 1 + .../staging/media/imx/imx-media-dev-common.c | 90 +++++++++++++++++++ drivers/staging/media/imx/imx-media-dev.c | 86 ++++-------------- drivers/staging/media/imx/imx-media-of.c | 6 +- drivers/staging/media/imx/imx-media.h | 14 +++ 5 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 drivers/staging/media/imx/imx-media-dev-common.c diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 698a4210316e..a30b3033f9a3 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o +imx-media-objs += imx-media-dev-common.o imx-media-common-objs := imx-media-utils.o imx-media-fim.o imx-media-ic-objs := imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c new file mode 100644 index 000000000000..910594125889 --- /dev/null +++ b/drivers/staging/media/imx/imx-media-dev-common.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Media Controller Driver for Freescale common i.MX5/6/7 SOC + * + * Copyright (c) 2019 Linaro Ltd + * Copyright (c) 2016 Mentor Graphics Inc. + */ + +#include +#include +#include "imx-media.h" + +static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { + .bound = imx_media_subdev_bound, + .complete = imx_media_probe_complete, +}; + +static const struct media_device_ops imx_media_md_ops = { + .link_notify = imx_media_link_notify, +}; + +struct imx_media_dev *imx_media_dev_init(struct device *dev) +{ + struct imx_media_dev *imxmd; + int ret; + + imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); + if (!imxmd) + return ERR_PTR(-ENOMEM); + + dev_set_drvdata(dev, imxmd); + + strlcpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); + imxmd->md.ops = &imx_media_md_ops; + imxmd->md.dev = dev; + + mutex_init(&imxmd->mutex); + + imxmd->v4l2_dev.mdev = &imxmd->md; + imxmd->v4l2_dev.notify = imx_media_notify; + strlcpy(imxmd->v4l2_dev.name, "imx-media", + sizeof(imxmd->v4l2_dev.name)); + + media_device_init(&imxmd->md); + + ret = v4l2_device_register(dev, &imxmd->v4l2_dev); + if (ret < 0) { + v4l2_err(&imxmd->v4l2_dev, + "Failed to register v4l2_device: %d\n", ret); + goto cleanup; + } + + dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); + + INIT_LIST_HEAD(&imxmd->vdev_list); + + v4l2_async_notifier_init(&imxmd->notifier); + + return imxmd; + +cleanup: + media_device_cleanup(&imxmd->md); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(imx_media_dev_init); + +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) +{ + int ret; + + /* no subdevs? just bail */ + if (list_empty(&imxmd->notifier.asd_list)) { + v4l2_err(&imxmd->v4l2_dev, "no subdevs\n"); + return -ENODEV; + } + + /* prepare the async subdev notifier and register it */ + imxmd->notifier.ops = &imx_media_subdev_ops; + ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, + &imxmd->notifier); + if (ret) { + v4l2_err(&imxmd->v4l2_dev, + "v4l2_async_notifier_register failed with %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(imx_media_dev_notifier_register); diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 25e916562c66..c42bddd78906 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -116,9 +116,9 @@ static int imx_media_get_ipu(struct imx_media_dev *imxmd, } /* async subdev bound notifier */ -static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret = 0; @@ -302,7 +302,7 @@ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) } /* async subdev complete notifier */ -static int imx_media_probe_complete(struct v4l2_async_notifier *notifier) +int imx_media_probe_complete(struct v4l2_async_notifier *notifier) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret; @@ -326,11 +326,6 @@ unlock: return media_device_register(&imxmd->md); } -static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { - .bound = imx_media_subdev_bound, - .complete = imx_media_probe_complete, -}; - /* * adds controls to a video device from an entity subdevice. * Continues upstream from the entity's sink pads. @@ -374,8 +369,8 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd, return ret; } -static int imx_media_link_notify(struct media_link *link, u32 flags, - unsigned int notification) +int imx_media_link_notify(struct media_link *link, u32 flags, + unsigned int notification) { struct media_entity *source = link->source->entity; struct imx_media_pad_vdev *pad_vdev; @@ -438,13 +433,8 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, return ret; } -static const struct media_device_ops imx_media_md_ops = { - .link_notify = imx_media_link_notify, -}; - -static void imx_media_notify(struct v4l2_subdev *sd, - unsigned int notification, - void *arg) +void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) { struct media_entity *entity = &sd->entity; int i; @@ -472,77 +462,37 @@ static int imx_media_probe(struct platform_device *pdev) struct imx_media_dev *imxmd; int ret; - imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); - if (!imxmd) - return -ENOMEM; - - dev_set_drvdata(dev, imxmd); - - strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); - imxmd->md.ops = &imx_media_md_ops; - imxmd->md.dev = dev; - - mutex_init(&imxmd->mutex); - - imxmd->v4l2_dev.mdev = &imxmd->md; - imxmd->v4l2_dev.notify = imx_media_notify; - strscpy(imxmd->v4l2_dev.name, "imx-media", - sizeof(imxmd->v4l2_dev.name)); - - media_device_init(&imxmd->md); - - ret = v4l2_device_register(dev, &imxmd->v4l2_dev); - if (ret < 0) { - v4l2_err(&imxmd->v4l2_dev, - "Failed to register v4l2_device: %d\n", ret); - goto cleanup; - } - - dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); - - INIT_LIST_HEAD(&imxmd->vdev_list); - - v4l2_async_notifier_init(&imxmd->notifier); + imxmd = imx_media_dev_init(dev); + if (IS_ERR(imxmd)) + return PTR_ERR(imxmd); ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_of_subdevs failed with %d\n", ret); - goto notifier_cleanup; + goto cleanup; } ret = imx_media_add_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_internal_subdevs failed with %d\n", ret); - goto notifier_cleanup; + goto cleanup; } - /* no subdevs? just bail */ - if (list_empty(&imxmd->notifier.asd_list)) { - ret = -ENODEV; - goto notifier_cleanup; - } - - /* prepare the async subdev notifier and register it */ - imxmd->notifier.ops = &imx_media_subdev_ops; - ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, - &imxmd->notifier); - if (ret) { - v4l2_err(&imxmd->v4l2_dev, - "v4l2_async_notifier_register failed with %d\n", ret); + ret = imx_media_dev_notifier_register(imxmd); + if (ret) goto del_int; - } return 0; del_int: imx_media_remove_internal_subdevs(imxmd); -notifier_cleanup: +cleanup: v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); -cleanup: media_device_cleanup(&imxmd->md); + return ret; } @@ -556,8 +506,8 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&imxmd->notifier); imx_media_remove_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); - v4l2_device_unregister(&imxmd->v4l2_dev); media_device_unregister(&imxmd->md); + v4l2_device_unregister(&imxmd->v4l2_dev); media_device_cleanup(&imxmd->md); return 0; diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index a01327f6e045..03446335ac03 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,7 +20,8 @@ #include